From 7832e8aee17a7305f19a064a668dd75fc43ebffc Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Fri, 11 Dec 2020 12:38:14 +0100
Subject: [PATCH 01/20] Enabled tech ENABLE_GCODE_VIEWER_STATISTICS

---
 src/libslic3r/Technologies.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 75b228155..b650809bb 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -24,7 +24,7 @@
 // Use wxDataViewRender instead of wxDataViewCustomRenderer
 #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
 // Enable G-Code viewer statistics imgui dialog
-#define ENABLE_GCODE_VIEWER_STATISTICS 0
+#define ENABLE_GCODE_VIEWER_STATISTICS 1
 // Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation 
 #define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
 

From 27bd79e409aebc5a5910da999d981a85ba65aa02 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Thu, 7 Jan 2021 09:06:15 +0100
Subject: [PATCH 02/20] Fixed merging error after merge with master

---
 src/slic3r/GUI/GCodeViewer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 0af10270e..097511565 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1964,7 +1964,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
 
 #if ENABLE_GCODE_VIEWER_STATISTICS
     for (const TBuffer& buffer : m_buffers) {
-        m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.render_paths, RenderPath);
+        m_statistics.render_paths_size += SLIC3R_STDUNORDEREDSET_MEMSIZE(buffer.render_paths, RenderPath);
         for (const RenderPath& path : buffer.render_paths) {
             m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.sizes, unsigned int);
             m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t);

From ebed29708de1d544c8ae31af5b31d8e52465af53 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Thu, 7 Jan 2021 09:57:37 +0100
Subject: [PATCH 03/20] Refactoring of GCodeViewer initialization

---
 src/slic3r/GUI/GCodeViewer.cpp | 88 +++++++++++++++-------------------
 src/slic3r/GUI/GCodeViewer.hpp |  8 ++--
 2 files changed, 42 insertions(+), 54 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 097511565..1ae184447 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -306,10 +306,47 @@ const std::vector<GCodeViewer::Color> GCodeViewer::Range_Colors {{
     { 0.581f, 0.149f, 0.087f }  // reddish
 }};
 
+GCodeViewer::GCodeViewer()
+{
+    // initializes non opengl data of TBuffers
+    for (size_t i = 0; i < m_buffers.size(); ++i) {
+        TBuffer& buffer = m_buffers[i];
+        switch (buffer_type(i))
+        {
+        default: { break; }
+        case EMoveType::Tool_change:
+        case EMoveType::Color_change:
+        case EMoveType::Pause_Print:
+        case EMoveType::Custom_GCode:
+        case EMoveType::Retract:
+        case EMoveType::Unretract:
+        {
+            buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
+            buffer.vertices.format = VBuffer::EFormat::Position;
+            break;
+        }
+        case EMoveType::Wipe:
+        case EMoveType::Extrude:
+        {
+            buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
+            buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
+            break;
+        }
+        case EMoveType::Travel:
+        {
+            buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
+            buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
+            break;
+        }
+        }
+    }
+
+    set_toolpath_move_type_visible(EMoveType::Extrude, true);
+//    m_sequential_view.skip_invisible_moves = true;
+}
+
 void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized)
 {
-    init();
-
     // avoid processing if called with the same gcode_result
     if (m_last_result_id == gcode_result.id)
         return;
@@ -460,9 +497,6 @@ void GCodeViewer::update_shells_color_by_extruder(const DynamicPrintConfig* conf
 
 void GCodeViewer::reset()
 {
-    m_initialized = false;
-    m_gl_data_initialized = false;
-
     m_moves_count = 0;
     for (TBuffer& buffer : m_buffers) {
         buffer.reset();
@@ -955,50 +989,6 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     fclose(fp);
 }
 
-void GCodeViewer::init()
-{
-    if (m_initialized)
-        return;
-
-    // initializes non opengl data of TBuffers
-    for (size_t i = 0; i < m_buffers.size(); ++i) {
-        TBuffer& buffer = m_buffers[i];
-        switch (buffer_type(i))
-        {
-        default: { break; }
-        case EMoveType::Tool_change:
-        case EMoveType::Color_change:
-        case EMoveType::Pause_Print:
-        case EMoveType::Custom_GCode:
-        case EMoveType::Retract:
-        case EMoveType::Unretract:
-        {
-            buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
-            buffer.vertices.format = VBuffer::EFormat::Position;
-            break;
-        }
-        case EMoveType::Wipe:
-        case EMoveType::Extrude:
-        {
-            buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
-            buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
-            break;
-        }
-        case EMoveType::Travel:
-        {
-            buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
-            buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
-            break;
-        }
-        }
-    }
-
-    set_toolpath_move_type_visible(EMoveType::Extrude, true);
-//    m_sequential_view.skip_invisible_moves = true;
-
-    m_initialized = true;
-}
-
 void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
 #if ENABLE_GCODE_VIEWER_STATISTICS
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index 31092e30d..a929fcc6b 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -195,7 +195,7 @@ class GCodeViewer
 
         std::string shader;
         std::vector<Path> paths;
-        // std::set seems to perform singificantly better, at least on Windows.
+        // std::set seems to perform significantly better, at least on Windows.
 //        std::unordered_set<RenderPath, RenderPathPropertyHash, RenderPathPropertyEqual> render_paths;
         std::set<RenderPath, RenderPathPropertyLower> render_paths;
         bool visible{ false };
@@ -228,7 +228,7 @@ class GCodeViewer
             {
             case ERenderPrimitiveType::Point:    { return 0; }
             case ERenderPrimitiveType::Line:     { return 1; }
-            case ERenderPrimitiveType::Triangle: { return 36; } // 1 vertex of 13th triangle
+            case ERenderPrimitiveType::Triangle: { return 36; } // 1st vertex of 13th triangle
             default:                             { return 0; }
             }
         }
@@ -461,7 +461,6 @@ public:
     };
 
 private:
-    bool m_initialized{ false };
     mutable bool m_gl_data_initialized{ false };
     unsigned int m_last_result_id{ 0 };
     size_t m_moves_count{ 0 };
@@ -490,7 +489,7 @@ private:
     GCodeProcessor::Result::SettingsIds m_settings_ids;
 
 public:
-    GCodeViewer() = default;
+    GCodeViewer();
     ~GCodeViewer() { reset(); }
 
     // extract rendering data from the given parameters
@@ -536,7 +535,6 @@ public:
     void export_toolpaths_to_obj(const char* filename) const;
 
 private:
-    void init();
     void load_toolpaths(const GCodeProcessor::Result& gcode_result);
     void load_shells(const Print& print, bool initialized);
     void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;

From 83f60a9dd118d526fa4819c3cda999ca6cde48ea Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Thu, 7 Jan 2021 10:11:50 +0100
Subject: [PATCH 04/20] Small refactoring in GCodeViewer::load_toolpaths()

---
 src/slic3r/GUI/GCodeViewer.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 1ae184447..6ecbcf280 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1053,7 +1053,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
     // format data into the buffers to be rendered as lines
     auto add_vertices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr,
-        TBuffer& buffer, std::vector<float>& buffer_vertices) {
+        std::vector<float>& buffer_vertices) {
             // x component of the normal to the current segment (the normal is parallel to the XY plane)
             float normal_x = (curr.position - prev.position).normalized()[1];
 
@@ -1413,7 +1413,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             break;
         }
         case TBuffer::ERenderPrimitiveType::Line: {
-            add_vertices_as_line(prev, curr, buffer, buffer_vertices);
+            add_vertices_as_line(prev, curr, buffer_vertices);
             break;
         }
         case TBuffer::ERenderPrimitiveType::Triangle: {
@@ -1422,11 +1422,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         }
         }
 
-        EMoveType type = buffer_type(id);
-        if (type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) {
+        if (curr.type == EMoveType::Pause_Print || curr.type == EMoveType::Custom_GCode) {
             const float* const last_z = options_zs.empty() ? nullptr : &options_zs.back();
-            float z = static_cast<double>(curr.position[2]);
-            if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z)
+            if (last_z == nullptr || curr.position[2] < *last_z - EPSILON || *last_z + EPSILON < curr.position[2])
                 options_zs.emplace_back(curr.position[2]);
         }
     }

From 77f6606771215047fc723a3b3267b34ae5a54dd9 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Thu, 7 Jan 2021 11:13:07 +0100
Subject: [PATCH 05/20] Another refactoring in GCodeViewer::load_toolpaths()

---
 src/libslic3r/Technologies.hpp |  10 ++
 src/slic3r/GUI/GCodeViewer.cpp | 204 ++++++++++++++++-----------------
 src/slic3r/GUI/GCodeViewer.hpp |   1 +
 3 files changed, 113 insertions(+), 102 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index d09f1c6ee..eabd711d5 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -107,4 +107,14 @@
 
 #define ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING (1 && ENABLE_2_3_0_RC1)
 
+
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//=======
+// 2.3.1 
+//=======
+#define ENABLE_2_3_1 1
+
+#define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1)
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
 #endif // _prusaslicer_technologies_h_
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 6ecbcf280..103c1f777 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -748,7 +748,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 
     // get vertices data from vertex buffer on gpu
     size_t floats_per_vertex = buffer.vertices.vertex_size_floats();
-    std::vector<float> vertices = std::vector<float>(buffer.vertices.count * floats_per_vertex);
+    VertexBuffer vertices = VertexBuffer(buffer.vertices.count * floats_per_vertex);
     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id));
     glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer.vertices.data_size_bytes(), vertices.data()));
     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
@@ -1025,7 +1025,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     m_max_bounding_box = m_paths_bounding_box;
     m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ());
 
-    auto log_memory_usage = [this](const std::string& label, const std::vector<std::vector<float>>& vertices, const std::vector<MultiIndexBuffer>& indices) {
+    auto log_memory_usage = [this](const std::string& label, const std::vector<VertexBuffer>& vertices, const std::vector<MultiIndexBuffer>& indices) {
         int64_t vertices_size = 0;
         for (size_t i = 0; i < vertices.size(); ++i) {
             vertices_size += SLIC3R_STDVEC_MEMSIZE(vertices[i], float);
@@ -1040,30 +1040,30 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     };
 
     // format data into the buffers to be rendered as points
-    auto add_vertices_as_point = [](const GCodeProcessor::MoveVertex& curr, std::vector<float>& buffer_vertices) {
-        for (int j = 0; j < 3; ++j) {
-            buffer_vertices.push_back(curr.position[j]);
-        }
+    auto add_vertices_as_point = [](const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) {
+        vertices.push_back(curr.position[0]);
+        vertices.push_back(curr.position[1]);
+        vertices.push_back(curr.position[2]);
     };
     auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
-        unsigned int index_buffer_id, IndexBuffer& buffer_indices, size_t move_id) {
-            buffer.add_path(curr, index_buffer_id, buffer_indices.size(), move_id);
-            buffer_indices.push_back(static_cast<unsigned int>(buffer_indices.size()));
+            unsigned int index_buffer_id, IndexBuffer& indices, size_t move_id) {
+            buffer.add_path(curr, index_buffer_id, indices.size(), move_id);
+            indices.push_back(static_cast<unsigned int>(indices.size()));
     };
 
     // format data into the buffers to be rendered as lines
     auto add_vertices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr,
-        std::vector<float>& buffer_vertices) {
+        VertexBuffer& vertices) {
             // x component of the normal to the current segment (the normal is parallel to the XY plane)
             float normal_x = (curr.position - prev.position).normalized()[1];
 
-            auto add_vertex = [&buffer_vertices, normal_x](const GCodeProcessor::MoveVertex& vertex) {
+            auto add_vertex = [&vertices, normal_x](const GCodeProcessor::MoveVertex& vertex) {
                 // add position
-                for (int j = 0; j < 3; ++j) {
-                    buffer_vertices.push_back(vertex.position[j]);
-                }
+                vertices.push_back(vertex.position[0]);
+                vertices.push_back(vertex.position[1]);
+                vertices.push_back(vertex.position[2]);
                 // add normal x component
-                buffer_vertices.push_back(normal_x);
+                vertices.push_back(normal_x);
             };
 
             // add previous vertex
@@ -1072,48 +1072,48 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             add_vertex(curr);
     };
     auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
-        unsigned int index_buffer_id, IndexBuffer& buffer_indices, size_t move_id) {
+        unsigned int index_buffer_id, IndexBuffer& indices, size_t move_id) {
             // x component of the normal to the current segment (the normal is parallel to the XY plane)
             float normal_x = (curr.position - prev.position).normalized()[1];
 
             if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
                 // add starting index
-                buffer_indices.push_back(static_cast<unsigned int>(buffer_indices.size()));
-                buffer.add_path(curr, index_buffer_id, buffer_indices.size() - 1, move_id - 1);
+                indices.push_back(static_cast<unsigned int>(indices.size()));
+                buffer.add_path(curr, index_buffer_id, indices.size() - 1, move_id - 1);
                 buffer.paths.back().first.position = prev.position;
             }
 
             Path& last_path = buffer.paths.back();
             if (last_path.first.i_id != last_path.last.i_id) {
                 // add previous index
-                buffer_indices.push_back(static_cast<unsigned int>(buffer_indices.size()));
+                indices.push_back(static_cast<unsigned int>(indices.size()));
             }
 
             // add current index
-            buffer_indices.push_back(static_cast<unsigned int>(buffer_indices.size()));
-            last_path.last = { index_buffer_id, buffer_indices.size() - 1, move_id, curr.position };
+            indices.push_back(static_cast<unsigned int>(indices.size()));
+            last_path.last = { index_buffer_id, indices.size() - 1, move_id, curr.position };
     };
 
     // format data into the buffers to be rendered as solid
     auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
-        std::vector<float>& buffer_vertices, size_t move_id) {
+        VertexBuffer& vertices, size_t move_id) {
             static Vec3f prev_dir;
             static Vec3f prev_up;
             static float prev_length;
-            auto store_vertex = [](std::vector<float>& buffer_vertices, const Vec3f& position, const Vec3f& normal) {
+            auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
                 // append position
-                for (int j = 0; j < 3; ++j) {
-                    buffer_vertices.push_back(position[j]);
-                }
+                vertices.push_back(position[0]);
+                vertices.push_back(position[1]);
+                vertices.push_back(position[2]);
                 // append normal
-                for (int j = 0; j < 3; ++j) {
-                    buffer_vertices.push_back(normal[j]);
-                }
+                vertices.push_back(normal[0]);
+                vertices.push_back(normal[1]);
+                vertices.push_back(normal[2]);
             };
-            auto extract_position_at = [](const std::vector<float>& vertices, size_t id) {
+            auto extract_position_at = [](const VertexBuffer& vertices, size_t id) {
                 return Vec3f(vertices[id + 0], vertices[id + 1], vertices[id + 2]);
             };
-            auto update_position_at = [](std::vector<float>& vertices, size_t id, const Vec3f& position) {
+            auto update_position_at = [](VertexBuffer& vertices, size_t id, const Vec3f& position) {
                 vertices[id + 0] = position[0];
                 vertices[id + 1] = position[1];
                 vertices[id + 2] = position[2];
@@ -1124,7 +1124,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 buffer.paths.back().first.position = prev.position;
             }
 
-            unsigned int starting_vertices_size = static_cast<unsigned int>(buffer_vertices.size() / buffer.vertices.vertex_size_floats());
+            unsigned int starting_vertices_size = static_cast<unsigned int>(vertices.size() / buffer.vertices.vertex_size_floats());
 
             Vec3f dir = (curr.position - prev.position).normalized();
             Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized();
@@ -1145,16 +1145,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 // 1st segment
 
                 // vertices 1st endpoint
-                store_vertex(buffer_vertices, prev_pos + half_height * up, up);
-                store_vertex(buffer_vertices, prev_pos + half_width * right, right);
-                store_vertex(buffer_vertices, prev_pos + half_height * down, down);
-                store_vertex(buffer_vertices, prev_pos + half_width * left, left);
+                store_vertex(vertices, prev_pos + half_height * up, up);
+                store_vertex(vertices, prev_pos + half_width * right, right);
+                store_vertex(vertices, prev_pos + half_height * down, down);
+                store_vertex(vertices, prev_pos + half_width * left, left);
 
                 // vertices 2nd endpoint
-                store_vertex(buffer_vertices, curr_pos + half_height * up, up);
-                store_vertex(buffer_vertices, curr_pos + half_width * right, right);
-                store_vertex(buffer_vertices, curr_pos + half_height * down, down);
-                store_vertex(buffer_vertices, curr_pos + half_width * left, left);
+                store_vertex(vertices, curr_pos + half_height * up, up);
+                store_vertex(vertices, curr_pos + half_width * right, right);
+                store_vertex(vertices, curr_pos + half_height * down, down);
+                store_vertex(vertices, curr_pos + half_width * left, left);
             }
             else {
                 // any other segment
@@ -1171,8 +1171,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
                 size_t prev_right_id = (starting_vertices_size - 3) * buffer.vertices.vertex_size_floats();
                 size_t prev_left_id = (starting_vertices_size - 1) * buffer.vertices.vertex_size_floats();
-                Vec3f prev_right_pos = extract_position_at(buffer_vertices, prev_right_id);
-                Vec3f prev_left_pos = extract_position_at(buffer_vertices, prev_left_id);
+                Vec3f prev_right_pos = extract_position_at(vertices, prev_right_id);
+                Vec3f prev_left_pos = extract_position_at(vertices, prev_left_id);
 
                 bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f;
                 // whether the angle between adjacent segments is greater than 45 degrees
@@ -1185,12 +1185,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 if (can_displace) {
                     if (is_right_turn) {
                         prev_right_pos -= displacement_vec;
-                        update_position_at(buffer_vertices, prev_right_id, prev_right_pos);
+                        update_position_at(vertices, prev_right_id, prev_right_pos);
                         right_displaced = true;
                     }
                     else {
                         prev_left_pos -= displacement_vec;
-                        update_position_at(buffer_vertices, prev_left_id, prev_left_pos);
+                        update_position_at(vertices, prev_left_id, prev_left_pos);
                         left_displaced = true;
                     }
                 }
@@ -1200,39 +1200,39 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                     if (can_displace) {
                         if (is_right_turn) {
                             prev_left_pos += displacement_vec;
-                            update_position_at(buffer_vertices, prev_left_id, prev_left_pos);
+                            update_position_at(vertices, prev_left_id, prev_left_pos);
                             left_displaced = true;
                         }
                         else {
                             prev_right_pos += displacement_vec;
-                            update_position_at(buffer_vertices, prev_right_id, prev_right_pos);
+                            update_position_at(vertices, prev_right_id, prev_right_pos);
                             right_displaced = true;
                         }
                     }
 
                     // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint)
                     // vertices position matches that of the previous segment 2nd endpoint, if displaced
-                    store_vertex(buffer_vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right);
-                    store_vertex(buffer_vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left);
+                    store_vertex(vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right);
+                    store_vertex(vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left);
                 }
                 else {
                     // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint)
                     // the inner corner vertex position matches that of the previous segment 2nd endpoint, if displaced
                     if (is_right_turn) {
-                        store_vertex(buffer_vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right);
-                        store_vertex(buffer_vertices, prev_pos + half_width * left, left);
+                        store_vertex(vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right);
+                        store_vertex(vertices, prev_pos + half_width * left, left);
                     }
                     else {
-                        store_vertex(buffer_vertices, prev_pos + half_width * right, right);
-                        store_vertex(buffer_vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left);
+                        store_vertex(vertices, prev_pos + half_width * right, right);
+                        store_vertex(vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left);
                     }
                 }
 
                 // vertices 2nd endpoint
-                store_vertex(buffer_vertices, curr_pos + half_height * up, up);
-                store_vertex(buffer_vertices, curr_pos + half_width * right, right);
-                store_vertex(buffer_vertices, curr_pos + half_height * down, down);
-                store_vertex(buffer_vertices, curr_pos + half_width * left, left);
+                store_vertex(vertices, curr_pos + half_height * up, up);
+                store_vertex(vertices, curr_pos + half_width * right, right);
+                store_vertex(vertices, curr_pos + half_height * down, down);
+                store_vertex(vertices, curr_pos + half_width * left, left);
             }
 
             last_path.last = { 0, 0, move_id, curr.position };
@@ -1242,22 +1242,22 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     };
 
     auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
-        size_t& buffer_vertices_size, unsigned int index_buffer_id, IndexBuffer& buffer_indices, size_t move_id) {
+        size_t& buffer_vertices_size, unsigned int index_buffer_id, IndexBuffer& indices, size_t move_id) {
             static Vec3f prev_dir;
             static Vec3f prev_up;
             static float prev_length;
-            auto store_triangle = [](IndexBuffer& buffer_indices, unsigned int i1, unsigned int i2, unsigned int i3) {
-                buffer_indices.push_back(i1);
-                buffer_indices.push_back(i2);
-                buffer_indices.push_back(i3);
+            auto store_triangle = [](IndexBuffer& indices, unsigned int i1, unsigned int i2, unsigned int i3) {
+                indices.push_back(i1);
+                indices.push_back(i2);
+                indices.push_back(i3);
             };
-            auto append_dummy_cap = [store_triangle](IndexBuffer& buffer_indices, unsigned int id) {
-                store_triangle(buffer_indices, id, id, id);
-                store_triangle(buffer_indices, id, id, id);
+            auto append_dummy_cap = [store_triangle](IndexBuffer& indices, unsigned int id) {
+                store_triangle(indices, id, id, id);
+                store_triangle(indices, id, id, id);
             };
 
             if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
-                buffer.add_path(curr, index_buffer_id, buffer_indices.size(), move_id - 1);
+                buffer.add_path(curr, index_buffer_id, indices.size(), move_id - 1);
                 buffer.paths.back().first.position = prev.position;
             }
 
@@ -1281,25 +1281,25 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 buffer_vertices_size += 8;
 
                 // triangles starting cap
-                store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1);
-                store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
+                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1);
+                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
 
                 // dummy triangles outer corner cap
-                append_dummy_cap(buffer_indices, starting_vertices_size);
+                append_dummy_cap(indices, starting_vertices_size);
 
                 // triangles sides
-                store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4);
-                store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
-                store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 2, starting_vertices_size + 5);
-                store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 6, starting_vertices_size + 5);
-                store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 6);
-                store_triangle(buffer_indices, starting_vertices_size + 3, starting_vertices_size + 7, starting_vertices_size + 6);
-                store_triangle(buffer_indices, starting_vertices_size + 3, starting_vertices_size + 0, starting_vertices_size + 7);
-                store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 4, starting_vertices_size + 7);
+                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4);
+                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
+                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 2, starting_vertices_size + 5);
+                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 6, starting_vertices_size + 5);
+                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 6);
+                store_triangle(indices, starting_vertices_size + 3, starting_vertices_size + 7, starting_vertices_size + 6);
+                store_triangle(indices, starting_vertices_size + 3, starting_vertices_size + 0, starting_vertices_size + 7);
+                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 4, starting_vertices_size + 7);
 
                 // triangles ending cap
-                store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 6, starting_vertices_size + 7);
-                store_triangle(buffer_indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6);
+                store_triangle(indices, starting_vertices_size + 4, starting_vertices_size + 6, starting_vertices_size + 7);
+                store_triangle(indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6);
             }
             else {
                 // any other segment
@@ -1333,45 +1333,45 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 buffer_vertices_size += 6;
 
                 // triangles starting cap
-                store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size - 2, starting_vertices_size + 0);
-                store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 2);
+                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size - 2, starting_vertices_size + 0);
+                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 2);
 
                 // triangles outer corner cap
                 if (is_right_turn) {
                     if (left_displaced)
                         // dummy triangles
-                        append_dummy_cap(buffer_indices, starting_vertices_size);
+                        append_dummy_cap(indices, starting_vertices_size);
                     else {
-                        store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 1);
-                        store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size - 2, starting_vertices_size - 1);
+                        store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 1);
+                        store_triangle(indices, starting_vertices_size + 1, starting_vertices_size - 2, starting_vertices_size - 1);
                     }
                 }
                 else {
                     if (right_displaced)
                         // dummy triangles
-                        append_dummy_cap(buffer_indices, starting_vertices_size);
+                        append_dummy_cap(indices, starting_vertices_size);
                     else {
-                        store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size - 3, starting_vertices_size + 0);
-                        store_triangle(buffer_indices, starting_vertices_size - 3, starting_vertices_size - 2, starting_vertices_size + 0);
+                        store_triangle(indices, starting_vertices_size - 4, starting_vertices_size - 3, starting_vertices_size + 0);
+                        store_triangle(indices, starting_vertices_size - 3, starting_vertices_size - 2, starting_vertices_size + 0);
                     }
                 }
 
                 // triangles sides
-                store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 0, starting_vertices_size + 2);
-                store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
-                store_triangle(buffer_indices, starting_vertices_size + 0, starting_vertices_size - 2, starting_vertices_size + 3);
-                store_triangle(buffer_indices, starting_vertices_size - 2, starting_vertices_size + 4, starting_vertices_size + 3);
-                store_triangle(buffer_indices, starting_vertices_size - 2, starting_vertices_size + 1, starting_vertices_size + 4);
-                store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
-                store_triangle(buffer_indices, starting_vertices_size + 1, starting_vertices_size - 4, starting_vertices_size + 5);
-                store_triangle(buffer_indices, starting_vertices_size - 4, starting_vertices_size + 2, starting_vertices_size + 5);
+                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 0, starting_vertices_size + 2);
+                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
+                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size - 2, starting_vertices_size + 3);
+                store_triangle(indices, starting_vertices_size - 2, starting_vertices_size + 4, starting_vertices_size + 3);
+                store_triangle(indices, starting_vertices_size - 2, starting_vertices_size + 1, starting_vertices_size + 4);
+                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
+                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size - 4, starting_vertices_size + 5);
+                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 2, starting_vertices_size + 5);
 
                 // triangles ending cap
-                store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 4, starting_vertices_size + 5);
-                store_triangle(buffer_indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 4);
+                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 4, starting_vertices_size + 5);
+                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 4);
             }
 
-            last_path.last = { index_buffer_id, buffer_indices.size() - 1, move_id, curr.position };
+            last_path.last = { index_buffer_id, indices.size() - 1, move_id, curr.position };
             prev_dir = dir;
             prev_up = up;
             prev_length = length;
@@ -1381,7 +1381,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
     // to reduce the peak in memory usage, we split the generation of the vertex and index buffers in two steps.
     // the data are deleted as soon as they are sent to the gpu.
-    std::vector<std::vector<float>> vertices(m_buffers.size());
+    std::vector<VertexBuffer> vertices(m_buffers.size());
     std::vector<MultiIndexBuffer> indices(m_buffers.size());
     std::vector<float> options_zs;
 
@@ -1404,7 +1404,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         unsigned char id = buffer_id(curr.type);
         TBuffer& buffer = m_buffers[id];
-        std::vector<float>& buffer_vertices = vertices[id];
+        VertexBuffer& buffer_vertices = vertices[id];
 
         switch (buffer.render_primitive_type)
         {
@@ -1430,7 +1430,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     }
 
     // move the wipe toolpaths half height up to render them on proper position
-    std::vector<float>& wipe_vertices = vertices[buffer_id(EMoveType::Wipe)];
+    VertexBuffer& wipe_vertices = vertices[buffer_id(EMoveType::Wipe)];
     for (size_t i = 2; i < wipe_vertices.size(); i += 3) {
         wipe_vertices[i] += 0.5f * GCodeProcessor::Wipe_Height;
     }
@@ -1441,7 +1441,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     for (size_t i = 0; i < m_buffers.size(); ++i) {
         TBuffer& buffer = m_buffers[i];
 
-        const std::vector<float>& buffer_vertices = vertices[i];
+        const VertexBuffer& buffer_vertices = vertices[i];
         buffer.vertices.count = buffer_vertices.size() / buffer.vertices.vertex_size_floats();
 #if ENABLE_GCODE_VIEWER_STATISTICS
         m_statistics.total_vertices_gpu_size += buffer_vertices.size() * sizeof(float);
@@ -1461,7 +1461,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     }
 
     // dismiss vertices data, no more needed
-    std::vector<std::vector<float>>().swap(vertices);
+    std::vector<VertexBuffer>().swap(vertices);
 
     // toolpaths data -> extract indices from result
     // paths may have been filled while extracting vertices,
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index a929fcc6b..5f276b09f 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -20,6 +20,7 @@ namespace GUI {
 class GCodeViewer
 {
     using Color = std::array<float, 3>;
+    using VertexBuffer = std::vector<float>;
     using IndexBuffer = std::vector<unsigned int>;
     using MultiIndexBuffer = std::vector<IndexBuffer>;
 

From fb7dada01dfe45f5f5c822bdd0a0684f135aa4eb Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 12 Jan 2021 09:03:35 +0100
Subject: [PATCH 06/20] 1st installment of tech ENABLE_SPLITTED_VERTEX_BUFFER -
 split vertex buffer when greater than a fixed max size

---
 src/slic3r/GUI/GCodeViewer.cpp | 1415 ++++++++++++++++++++++++++++++--
 src/slic3r/GUI/GCodeViewer.hpp |  182 +++-
 2 files changed, 1518 insertions(+), 79 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 103c1f777..aaafd333d 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -79,6 +79,20 @@ static float round_to_nearest(float value, unsigned int decimals)
     return res;
 }
 
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+void GCodeViewer::VBuffer::reset()
+{
+    // release gpu memory
+    if (!ids.empty()) {
+        glsafe(::glDeleteBuffers(static_cast<GLsizei>(ids.size()), static_cast<const GLuint*>(ids.data())));
+        ids.clear();
+    }
+
+    count = 0;
+}
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 void GCodeViewer::VBuffer::reset()
 {
     // release gpu memory
@@ -89,15 +103,35 @@ void GCodeViewer::VBuffer::reset()
 
     count = 0;
 }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
 void GCodeViewer::IBuffer::reset()
 {
     // release gpu memory
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+    // release gpu memory
+    if (ibo > 0) {
+        glsafe(::glDeleteBuffers(1, &ibo));
+        ibo = 0;
+    }
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     if (id > 0) {
         glsafe(::glDeleteBuffers(1, &id));
         id = 0;
     }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+    vbo = 0;
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     count = 0;
 }
 
@@ -119,6 +153,21 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
     case EMoveType::Unretract:
     case EMoveType::Extrude: {
         // use rounding to reduce the number of generated paths
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+#if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
+        return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
+            move.position[2] <= sub_paths.front().first.position[2] && feedrate == move.feedrate && fan_speed == move.fan_speed &&
+            height == round_to_nearest(move.height, 2) && width == round_to_nearest(move.width, 2) &&
+            matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f);
+#else
+        return type == move.type && move.position[2] <= sub_paths.front().position[2] && role == move.extrusion_role && height == round_to_nearest(move.height, 2) &&
+            width == round_to_nearest(move.width, 2) && feedrate == move.feedrate && fan_speed == move.fan_speed &&
+            volumetric_rate == round_to_nearest(move.volumetric_rate(), 2) && extruder_id == move.extruder_id &&
+            cp_color_id == move.cp_color_id;
+#endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
         return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
             move.position[2] <= first.position[2] && feedrate == move.feedrate && fan_speed == move.fan_speed &&
@@ -130,6 +179,9 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
             volumetric_rate == round_to_nearest(move.volumetric_rate(), 2) && extruder_id == move.extruder_id &&
             cp_color_id == move.cp_color_id;
 #endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     case EMoveType::Travel: {
         return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id;
@@ -156,6 +208,19 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi
 {
     Path::Endpoint endpoint = { b_id, i_id, s_id, move.position };
     // use rounding to reduce the number of generated paths
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+#if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
+    paths.push_back({ move.type, move.extrusion_role, move.delta_extruder,
+        round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed,
+        move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } });
+#else
+    paths.push_back({ move.type, move.extrusion_role, move.delta_extruder,
+        round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed,
+        round_to_nearest(move.volumetric_rate(), 2), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } });
+#endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
     paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder,
         round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed,
@@ -165,6 +230,9 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi
         round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed,
         round_to_nearest(move.volumetric_rate(), 2), move.extruder_id, move.cp_color_id });
 #endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 }
 
 GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) const
@@ -319,21 +387,18 @@ GCodeViewer::GCodeViewer()
         case EMoveType::Pause_Print:
         case EMoveType::Custom_GCode:
         case EMoveType::Retract:
-        case EMoveType::Unretract:
-        {
+        case EMoveType::Unretract: {
             buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
             buffer.vertices.format = VBuffer::EFormat::Position;
             break;
         }
         case EMoveType::Wipe:
-        case EMoveType::Extrude:
-        {
+        case EMoveType::Extrude: {
             buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
             buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
             break;
         }
-        case EMoveType::Travel:
-        {
+        case EMoveType::Travel: {
             buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
             buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
             break;
@@ -357,6 +422,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
     reset();
 
     load_toolpaths(gcode_result);
+
     if (m_layers.empty())
         return;
 
@@ -526,7 +592,8 @@ void GCodeViewer::render() const
         // initializes opengl data of TBuffers
         for (size_t i = 0; i < m_buffers.size(); ++i) {
             TBuffer& buffer = m_buffers[i];
-            switch (buffer_type(i)) {
+            switch (buffer_type(i))
+            {
             default: { break; }
             case EMoveType::Tool_change:
             case EMoveType::Color_change:
@@ -590,8 +657,16 @@ void GCodeViewer::update_sequential_view_current(unsigned int first, unsigned in
         for (const TBuffer& buffer : m_buffers) {
             if (buffer.visible) {
                 for (const Path& path : buffer.paths) {
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+                    if (path.sub_paths.front().first.s_id <= id && id <= path.sub_paths.back().last.s_id)
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                     if (path.first.s_id <= id && id <= path.last.s_id)
-                        return true;
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+                    return true;
                 }
             }
         }
@@ -694,6 +769,10 @@ void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z
 
 void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 {
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if !ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
     if (filename == nullptr)
         return;
 
@@ -820,6 +899,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
         // get paths segments from buffer paths
         const IndexBuffer& ibuffer = indices[render_path.index_buffer_id];
         const Path& path = buffer.paths[render_path.path_id];
+
         float half_width = 0.5f * path.width;
         // clamp height to avoid artifacts due to z-fighting when importing the obj file into blender and similar
         float half_height = std::max(0.5f * path.height, 0.005f);
@@ -987,8 +1067,720 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     }
 
     fclose(fp);
+
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // !ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 }
 
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
+{
+    auto log_memory_usage = [this](const std::string& label, const std::vector<MultiVertexBuffer>& vertices, const std::vector<MultiIndexBuffer>& indices) {
+        int64_t vertices_size = 0;
+        for (const MultiVertexBuffer& v_multibuffer : vertices) {
+            for (const VertexBuffer& v_buffer : v_multibuffer) {
+                vertices_size += SLIC3R_STDVEC_MEMSIZE(v_buffer, float);
+            }
+        }
+        int64_t indices_size = 0;
+        for (const MultiIndexBuffer& i_multibuffer : indices) {
+            for (const IndexBuffer& i_buffer : i_multibuffer) {
+                indices_size += SLIC3R_STDVEC_MEMSIZE(i_buffer, unsigned int);
+            }
+        }
+        log_memory_used(label, vertices_size + indices_size);
+    };
+
+    // format data into the buffers to be rendered as points
+    auto add_vertices_as_point = [](const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) {
+        vertices.push_back(curr.position[0]);
+        vertices.push_back(curr.position[1]);
+        vertices.push_back(curr.position[2]);
+    };
+    auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
+        unsigned int i_buffer_id, IndexBuffer& indices, size_t move_id) {
+            buffer.add_path(curr, i_buffer_id, indices.size(), move_id);
+            indices.push_back(static_cast<unsigned int>(indices.size()));
+    };
+
+    // format data into the buffers to be rendered as lines
+    auto add_vertices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, VertexBuffer& vertices) {
+        // x component of the normal to the current segment (the normal is parallel to the XY plane)
+        float normal_x = (curr.position - prev.position).normalized()[1];
+
+        auto add_vertex = [&vertices, normal_x](const GCodeProcessor::MoveVertex& vertex) {
+            // add position
+            vertices.push_back(vertex.position[0]);
+            vertices.push_back(vertex.position[1]);
+            vertices.push_back(vertex.position[2]);
+            // add normal x component
+            vertices.push_back(normal_x);
+        };
+
+        // add previous vertex
+        add_vertex(prev);
+        // add current vertex
+        add_vertex(curr);
+    };
+    auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
+        unsigned int i_buffer_id, IndexBuffer& indices, size_t move_id) {
+            if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
+                // add starting index
+                indices.push_back(static_cast<unsigned int>(indices.size()));
+                buffer.add_path(curr, i_buffer_id, indices.size() - 1, move_id - 1);
+                buffer.paths.back().sub_paths.front().first.position = prev.position;
+            }
+
+            Path& last_path = buffer.paths.back();
+            if (last_path.sub_paths.front().first.i_id != last_path.sub_paths.back().last.i_id) {
+                // add previous index
+                indices.push_back(static_cast<unsigned int>(indices.size()));
+            }
+
+            // add current index
+            indices.push_back(static_cast<unsigned int>(indices.size()));
+            last_path.sub_paths.back().last = { i_buffer_id, indices.size() - 1, move_id, curr.position };
+    };
+
+    // format data into the buffers to be rendered as solid
+    auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, VertexBuffer& vertices, size_t move_id) {
+        static Vec3f prev_dir;
+        static Vec3f prev_up;
+        static float prev_length;
+        auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
+            // append position
+            vertices.push_back(position[0]);
+            vertices.push_back(position[1]);
+            vertices.push_back(position[2]);
+            // append normal
+            vertices.push_back(normal[0]);
+            vertices.push_back(normal[1]);
+            vertices.push_back(normal[2]);
+        };
+        auto extract_position_at = [](const VertexBuffer& vertices, size_t id) {
+            return Vec3f(vertices[id + 0], vertices[id + 1], vertices[id + 2]);
+        };
+        auto update_position_at = [](VertexBuffer& vertices, size_t id, const Vec3f& position) {
+            vertices[id + 0] = position[0];
+            vertices[id + 1] = position[1];
+            vertices[id + 2] = position[2];
+        };
+
+        if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
+            buffer.add_path(curr, 0, 0, move_id - 1);
+            buffer.paths.back().sub_paths.back().first.position = prev.position;
+        }
+
+        unsigned int starting_vertices_size = static_cast<unsigned int>(vertices.size() / buffer.vertices.vertex_size_floats());
+
+        Vec3f dir = (curr.position - prev.position).normalized();
+        Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized();
+        Vec3f left = -right;
+        Vec3f up = right.cross(dir);
+        Vec3f down = -up;
+
+        Path& last_path = buffer.paths.back();
+
+        float half_width = 0.5f * last_path.width;
+        float half_height = 0.5f * last_path.height;
+
+        Vec3f prev_pos = prev.position - half_height * up;
+        Vec3f curr_pos = curr.position - half_height * up;
+
+        float length = (curr_pos - prev_pos).norm();
+        if (last_path.vertices_count() == 1 || vertices.empty()) {
+            // 1st segment or restart into a new vertex buffer
+            // ===============================================
+
+            // vertices 1st endpoint
+            store_vertex(vertices, prev_pos + half_height * up, up);
+            store_vertex(vertices, prev_pos + half_width * right, right);
+            store_vertex(vertices, prev_pos + half_height * down, down);
+            store_vertex(vertices, prev_pos + half_width * left, left);
+
+            // vertices 2nd endpoint
+            store_vertex(vertices, curr_pos + half_height * up, up);
+            store_vertex(vertices, curr_pos + half_width * right, right);
+            store_vertex(vertices, curr_pos + half_height * down, down);
+            store_vertex(vertices, curr_pos + half_width * left, left);
+        }
+        else {
+            // any other segment
+            // =================
+
+            float displacement = 0.0f;
+            float cos_dir = prev_dir.dot(dir);
+            if (cos_dir > -0.9998477f) {
+                // if the angle between adjacent segments is smaller than 179 degrees
+                Vec3f med_dir = (prev_dir + dir).normalized();
+                displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f)));
+            }
+
+            Vec3f displacement_vec = displacement * prev_dir;
+            bool can_displace = displacement > 0.0f && displacement < prev_length&& displacement < length;
+
+            size_t prev_right_id = (starting_vertices_size - 3) * buffer.vertices.vertex_size_floats();
+            size_t prev_left_id = (starting_vertices_size - 1) * buffer.vertices.vertex_size_floats();
+            Vec3f prev_right_pos = extract_position_at(vertices, prev_right_id);
+            Vec3f prev_left_pos = extract_position_at(vertices, prev_left_id);
+
+            bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f;
+            // whether the angle between adjacent segments is greater than 45 degrees
+            bool is_sharp = cos_dir < 0.7071068f;
+
+            bool right_displaced = false;
+            bool left_displaced = false;
+
+            // displace the vertex (inner with respect to the corner) of the previous segment 2nd endpoint, if possible
+            if (can_displace) {
+                if (is_right_turn) {
+                    prev_right_pos -= displacement_vec;
+                    update_position_at(vertices, prev_right_id, prev_right_pos);
+                    right_displaced = true;
+                }
+                else {
+                    prev_left_pos -= displacement_vec;
+                    update_position_at(vertices, prev_left_id, prev_left_pos);
+                    left_displaced = true;
+                }
+            }
+
+            if (!is_sharp) {
+                // displace the vertex (outer with respect to the corner) of the previous segment 2nd endpoint, if possible
+                if (can_displace) {
+                    if (is_right_turn) {
+                        prev_left_pos += displacement_vec;
+                        update_position_at(vertices, prev_left_id, prev_left_pos);
+                        left_displaced = true;
+                    }
+                    else {
+                        prev_right_pos += displacement_vec;
+                        update_position_at(vertices, prev_right_id, prev_right_pos);
+                        right_displaced = true;
+                    }
+                }
+
+                // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint)
+                // vertices position matches that of the previous segment 2nd endpoint, if displaced
+                store_vertex(vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right);
+                store_vertex(vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left);
+            }
+            else {
+                // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint)
+                // the inner corner vertex position matches that of the previous segment 2nd endpoint, if displaced
+                if (is_right_turn) {
+                    store_vertex(vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right);
+                    store_vertex(vertices, prev_pos + half_width * left, left);
+                }
+                else {
+                    store_vertex(vertices, prev_pos + half_width * right, right);
+                    store_vertex(vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left);
+                }
+            }
+
+            // vertices 2nd endpoint
+            store_vertex(vertices, curr_pos + half_height * up, up);
+            store_vertex(vertices, curr_pos + half_width * right, right);
+            store_vertex(vertices, curr_pos + half_height * down, down);
+            store_vertex(vertices, curr_pos + half_width * left, left);
+        }
+
+        last_path.sub_paths.back().last = { 0, 0, move_id, curr.position };
+        prev_dir = dir;
+        prev_up = up;
+        prev_length = length;
+    };
+    auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
+        size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
+            static Vec3f prev_dir;
+            static Vec3f prev_up;
+            static float prev_length;
+            auto store_triangle = [](IndexBuffer& indices, unsigned int i1, unsigned int i2, unsigned int i3) {
+                indices.push_back(i1);
+                indices.push_back(i2);
+                indices.push_back(i3);
+            };
+            auto append_dummy_cap = [store_triangle](IndexBuffer& indices, unsigned int id) {
+                store_triangle(indices, id, id, id);
+                store_triangle(indices, id, id, id);
+            };
+
+            if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
+                buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1);
+                buffer.paths.back().sub_paths.back().first.position = prev.position;
+            }
+
+            Vec3f dir = (curr.position - prev.position).normalized();
+            Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized();
+            Vec3f up = right.cross(dir);
+
+            Path& last_path = buffer.paths.back();
+
+            float half_width = 0.5f * last_path.width;
+            float half_height = 0.5f * last_path.height;
+
+            Vec3f prev_pos = prev.position - half_height * up;
+            Vec3f curr_pos = curr.position - half_height * up;
+
+            float length = (curr_pos - prev_pos).norm();
+            if (last_path.vertices_count() == 1 || vbuffer_size == 0) {
+                // 1st segment or restart into a new vertex buffer
+                // ===============================================
+
+                // triangles starting cap
+                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 2, vbuffer_size + 1);
+                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 3, vbuffer_size + 2);
+
+                // dummy triangles outer corner cap
+                append_dummy_cap(indices, vbuffer_size);
+
+                // triangles sides
+                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 1, vbuffer_size + 4);
+                store_triangle(indices, vbuffer_size + 1, vbuffer_size + 5, vbuffer_size + 4);
+                store_triangle(indices, vbuffer_size + 1, vbuffer_size + 2, vbuffer_size + 5);
+                store_triangle(indices, vbuffer_size + 2, vbuffer_size + 6, vbuffer_size + 5);
+                store_triangle(indices, vbuffer_size + 2, vbuffer_size + 3, vbuffer_size + 6);
+                store_triangle(indices, vbuffer_size + 3, vbuffer_size + 7, vbuffer_size + 6);
+                store_triangle(indices, vbuffer_size + 3, vbuffer_size + 0, vbuffer_size + 7);
+                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 4, vbuffer_size + 7);
+
+                // triangles ending cap
+                store_triangle(indices, vbuffer_size + 4, vbuffer_size + 6, vbuffer_size + 7);
+                store_triangle(indices, vbuffer_size + 4, vbuffer_size + 5, vbuffer_size + 6);
+
+                vbuffer_size += 8;
+            }
+            else {
+                // any other segment
+                // =================
+
+                float displacement = 0.0f;
+                float cos_dir = prev_dir.dot(dir);
+                if (cos_dir > -0.9998477f) {
+                    // if the angle between adjacent segments is smaller than 179 degrees
+                    Vec3f med_dir = (prev_dir + dir).normalized();
+                    displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f)));
+                }
+
+                Vec3f displacement_vec = displacement * prev_dir;
+                bool can_displace = displacement > 0.0f && displacement < prev_length&& displacement < length;
+
+                bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f;
+                // whether the angle between adjacent segments is greater than 45 degrees
+                bool is_sharp = cos_dir < 0.7071068f;
+
+                bool right_displaced = false;
+                bool left_displaced = false;
+
+                if (!is_sharp) {
+                    if (can_displace) {
+                        if (is_right_turn)
+                            left_displaced = true;
+                        else
+                            right_displaced = true;
+                    }
+                }
+
+                // triangles starting cap
+                store_triangle(indices, vbuffer_size - 4, vbuffer_size - 2, vbuffer_size + 0);
+                store_triangle(indices, vbuffer_size - 4, vbuffer_size + 1, vbuffer_size - 2);
+
+                // triangles outer corner cap
+                if (is_right_turn) {
+                    if (left_displaced)
+                        // dummy triangles
+                        append_dummy_cap(indices, vbuffer_size);
+                    else {
+                        store_triangle(indices, vbuffer_size - 4, vbuffer_size + 1, vbuffer_size - 1);
+                        store_triangle(indices, vbuffer_size + 1, vbuffer_size - 2, vbuffer_size - 1);
+                    }
+                }
+                else {
+                    if (right_displaced)
+                        // dummy triangles
+                        append_dummy_cap(indices, vbuffer_size);
+                    else {
+                        store_triangle(indices, vbuffer_size - 4, vbuffer_size - 3, vbuffer_size + 0);
+                        store_triangle(indices, vbuffer_size - 3, vbuffer_size - 2, vbuffer_size + 0);
+                    }
+                }
+
+                // triangles sides
+                store_triangle(indices, vbuffer_size - 4, vbuffer_size + 0, vbuffer_size + 2);
+                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 3, vbuffer_size + 2);
+                store_triangle(indices, vbuffer_size + 0, vbuffer_size - 2, vbuffer_size + 3);
+                store_triangle(indices, vbuffer_size - 2, vbuffer_size + 4, vbuffer_size + 3);
+                store_triangle(indices, vbuffer_size - 2, vbuffer_size + 1, vbuffer_size + 4);
+                store_triangle(indices, vbuffer_size + 1, vbuffer_size + 5, vbuffer_size + 4);
+                store_triangle(indices, vbuffer_size + 1, vbuffer_size - 4, vbuffer_size + 5);
+                store_triangle(indices, vbuffer_size - 4, vbuffer_size + 2, vbuffer_size + 5);
+
+                // triangles ending cap
+                store_triangle(indices, vbuffer_size + 2, vbuffer_size + 4, vbuffer_size + 5);
+                store_triangle(indices, vbuffer_size + 2, vbuffer_size + 3, vbuffer_size + 4);
+
+                vbuffer_size += 6;
+            }
+
+            last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position };
+            prev_dir = dir;
+            prev_up = up;
+            prev_length = length;
+    };
+
+#if ENABLE_GCODE_VIEWER_STATISTICS
+    auto start_time = std::chrono::high_resolution_clock::now();
+    m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessor::MoveVertex);
+    m_statistics.results_time = gcode_result.time;
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+
+    m_moves_count = gcode_result.moves.size();
+    if (m_moves_count == 0)
+        return;
+
+    unsigned int progress_count = 0;
+    static const unsigned int progress_threshold = 1000;
+    wxProgressDialog* progress_dialog = wxGetApp().is_gcode_viewer() ?
+        new wxProgressDialog(_L("Generating toolpaths"), "...",
+            100, wxGetApp().plater(), wxPD_AUTO_HIDE | wxPD_APP_MODAL) : nullptr;
+
+    wxBusyCursor busy;
+
+    // extract approximate paths bounding box from result
+    for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
+        if (wxGetApp().is_gcode_viewer())
+            // for the gcode viewer we need to take in account all moves to correctly size the printbed
+            m_paths_bounding_box.merge(move.position.cast<double>());
+        else {
+            if (move.type == EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f)
+                m_paths_bounding_box.merge(move.position.cast<double>());
+        }
+    }
+
+    // set approximate max bounding box (take in account also the tool marker)
+    m_max_bounding_box = m_paths_bounding_box;
+    m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ());
+
+    std::vector<MultiVertexBuffer> vertices(m_buffers.size());
+    std::vector<MultiIndexBuffer> indices(m_buffers.size());
+    std::vector<float> options_zs;
+
+    // max vertex buffer size, in bytes
+    const size_t VBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
+
+    // toolpaths data -> extract vertices from result
+    for (size_t i = 0; i < m_moves_count; ++i) {
+        const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
+
+        // skip first vertex
+        if (i == 0)
+            continue;
+
+        const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
+
+        // update progress dialog
+        ++progress_count;
+        if (progress_dialog != nullptr && progress_count % progress_threshold == 0) {
+            progress_dialog->Update(int(100.0f * float(i) / (2.0f * float(m_moves_count))),
+                _L("Generating vertex buffer") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%");
+            progress_dialog->Fit();
+            progress_count = 0;
+        }
+
+        unsigned char id = buffer_id(curr.type);
+        TBuffer& t_buffer = m_buffers[id];
+        MultiVertexBuffer& v_multibuffer = vertices[id];
+
+        // ensure there is at least one vertex buffer
+        if (v_multibuffer.empty())
+            v_multibuffer.push_back(VertexBuffer());
+
+        // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
+        // add another vertex buffer
+        if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes())
+            v_multibuffer.push_back(VertexBuffer());
+
+        VertexBuffer& v_buffer = v_multibuffer.back();
+
+        switch (t_buffer.render_primitive_type)
+        {
+        case TBuffer::ERenderPrimitiveType::Point:    { add_vertices_as_point(curr, v_buffer); break; }
+        case TBuffer::ERenderPrimitiveType::Line:     { add_vertices_as_line(prev, curr, v_buffer); break; }
+        case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, v_buffer, i); break; }
+        }
+
+        // collect options zs for later use
+        if (curr.type == EMoveType::Pause_Print || curr.type == EMoveType::Custom_GCode) {
+            const float* const last_z = options_zs.empty() ? nullptr : &options_zs.back();
+            if (last_z == nullptr || curr.position[2] < *last_z - EPSILON || *last_z + EPSILON < curr.position[2])
+                options_zs.emplace_back(curr.position[2]);
+        }
+    }
+
+    for (MultiVertexBuffer& v_multibuffer : vertices) {
+        for (VertexBuffer& v_buffer : v_multibuffer) {
+            v_buffer.shrink_to_fit();
+        }
+    }
+
+    // move the wipe toolpaths half height up to render them on proper position
+    MultiVertexBuffer& wipe_vertices = vertices[buffer_id(EMoveType::Wipe)];
+    for (VertexBuffer& v_buffer : wipe_vertices) {
+        for (size_t i = 2; i < v_buffer.size(); i += 3) {
+            v_buffer[i] += 0.5f * GCodeProcessor::Wipe_Height;
+        }
+    }
+
+    // send vertices data to gpu
+    for (size_t i = 0; i < m_buffers.size(); ++i) {
+        TBuffer& t_buffer = m_buffers[i];
+
+        const MultiVertexBuffer& v_multibuffer = vertices[i];
+        for (const VertexBuffer& v_buffer : v_multibuffer) {
+            size_t size_elements = v_buffer.size();
+            size_t size_bytes = size_elements * sizeof(float);
+            size_t vertices_count = size_elements / t_buffer.vertices.vertex_size_floats();
+            t_buffer.vertices.count += vertices_count;
+
+#if ENABLE_GCODE_VIEWER_STATISTICS
+            m_statistics.total_vertices_gpu_size += static_cast<int64_t>(size_bytes);
+            m_statistics.max_vbuffer_gpu_size = std::max(m_statistics.max_vbuffer_gpu_size, static_cast<int64_t>(size_bytes));
+            ++m_statistics.vbuffers_count;
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+
+            GLuint id = 0;
+            glsafe(::glGenBuffers(1, &id));
+            t_buffer.vertices.ids.push_back(static_cast<unsigned int>(id));
+            glsafe(::glBindBuffer(GL_ARRAY_BUFFER, id));
+            glsafe(::glBufferData(GL_ARRAY_BUFFER, size_bytes, v_buffer.data(), GL_STATIC_DRAW));
+            glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+        }
+    }
+
+    log_memory_usage("Loaded G-code generated vertex buffers ", vertices, indices);
+
+    // dismiss vertices data, no more needed
+    std::vector<MultiVertexBuffer>().swap(vertices);
+
+    // toolpaths data -> extract indices from result
+    // paths may have been filled while extracting vertices,
+    // so reset them, they will be filled again while extracting indices
+    for (TBuffer& buffer : m_buffers) {
+        buffer.paths.clear();
+    }
+
+    // max index buffer size, in bytes
+    const size_t IBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
+
+    // variable used to keep track of the current vertex buffers index and size
+    using CurrVertexBuffer = std::pair<unsigned int, size_t>;
+    std::vector<CurrVertexBuffer> curr_vertex_buffers(m_buffers.size(), { 0, 0 });
+
+    // variable used to keep track of the vertex buffers ids
+    using VboIndexList = std::vector<unsigned int>;
+    std::vector<VboIndexList> vbo_indices(m_buffers.size());
+
+    for (size_t i = 0; i < m_moves_count; ++i) {
+        const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
+
+        // skip first vertex
+        if (i == 0)
+            continue;
+
+        const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
+
+        ++progress_count;
+        if (progress_dialog != nullptr && progress_count % progress_threshold == 0) {
+            progress_dialog->Update(int(100.0f * float(m_moves_count + i) / (2.0f * float(m_moves_count))),
+                _L("Generating index buffers") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%");
+            progress_dialog->Fit();
+            progress_count = 0;
+        }
+
+        unsigned char id = buffer_id(curr.type);
+        TBuffer& t_buffer = m_buffers[id];
+        MultiIndexBuffer& i_multibuffer = indices[id];
+        CurrVertexBuffer& curr_vertex_buffer = curr_vertex_buffers[id];
+        VboIndexList& vbo_index_list = vbo_indices[id];
+
+        // ensure there is at least one index buffer
+        if (i_multibuffer.empty()) {
+            i_multibuffer.push_back(IndexBuffer());
+            vbo_index_list.push_back(t_buffer.vertices.ids[curr_vertex_buffer.first]);
+        }
+
+        // if adding the indices for the current segment exceeds the threshold size of the current index buffer
+        // create another index buffer
+        if (i_multibuffer.back().size() * sizeof(unsigned int) >= IBUFFER_THRESHOLD_BYTES - t_buffer.indices_per_segment_size_bytes()) {
+            i_multibuffer.push_back(IndexBuffer());
+            vbo_index_list.push_back(t_buffer.vertices.ids[curr_vertex_buffer.first]);
+        }
+
+        // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
+        // create another index buffer
+        if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes()) {
+            i_multibuffer.push_back(IndexBuffer());
+
+            ++curr_vertex_buffer.first;
+            curr_vertex_buffer.second = 0;
+            vbo_index_list.push_back(t_buffer.vertices.ids[curr_vertex_buffer.first]);
+
+            if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
+                Path& last_path = t_buffer.paths.back();
+                last_path.add_sub_path(curr, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i);
+            }
+        }
+
+        IndexBuffer& i_buffer = i_multibuffer.back();
+
+        switch (t_buffer.render_primitive_type)
+        {
+        case TBuffer::ERenderPrimitiveType::Point: {
+            add_indices_as_point(curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
+            curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
+            break;
+        }
+        case TBuffer::ERenderPrimitiveType::Line: {
+            add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
+            curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
+            break;
+        }
+        case TBuffer::ERenderPrimitiveType::Triangle: {
+            add_indices_as_solid(prev, curr, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
+            break;
+        }
+        }
+    }
+
+    for (MultiIndexBuffer& i_multibuffer : indices) {
+        for (IndexBuffer& i_buffer : i_multibuffer) {
+            i_buffer.shrink_to_fit();
+        }
+    }
+
+    // toolpaths data -> send indices data to gpu
+    for (size_t i = 0; i < m_buffers.size(); ++i) {
+        TBuffer& t_buffer = m_buffers[i];
+        const MultiIndexBuffer& i_multibuffer = indices[i];
+        for (const IndexBuffer& i_buffer : i_multibuffer) {
+            size_t size_elements = i_buffer.size();
+            size_t size_bytes = size_elements * sizeof(unsigned int);
+
+            if (size_elements == 0) {
+                continue;
+            }
+
+            // stores index buffer informations into TBuffer
+            t_buffer.indices.push_back(IBuffer());
+            IBuffer& ibuf = t_buffer.indices.back();
+            ibuf.count = size_elements;
+            ibuf.vbo = vbo_indices[i][t_buffer.indices.size() - 1];
+
+#if ENABLE_GCODE_VIEWER_STATISTICS
+            m_statistics.total_indices_gpu_size += static_cast<int64_t>(size_bytes);
+            m_statistics.max_ibuffer_gpu_size = std::max(m_statistics.max_ibuffer_gpu_size, static_cast<int64_t>(size_bytes));
+            ++m_statistics.ibuffers_count;
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+
+            glsafe(::glGenBuffers(1, &ibuf.ibo));
+            glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf.ibo));
+            glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, size_bytes, i_buffer.data(), GL_STATIC_DRAW));
+            glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+        }
+    }
+
+    if (progress_dialog != nullptr) {
+        progress_dialog->Update(100, "");
+        progress_dialog->Fit();
+    }
+
+#if ENABLE_GCODE_VIEWER_STATISTICS
+    for (const TBuffer& buffer : m_buffers) {
+        m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path);
+    }
+    unsigned int travel_buffer_id = buffer_id(EMoveType::Travel);
+    const MultiIndexBuffer& travel_buffers = indices[travel_buffer_id];
+    for (const IndexBuffer& buffer : travel_buffers) {
+        m_statistics.travel_segments_count += buffer.size() / m_buffers[travel_buffer_id].indices_per_segment();
+    }
+    unsigned int wipe_buffer_id = buffer_id(EMoveType::Wipe);
+    const MultiIndexBuffer& wipe_buffers = indices[wipe_buffer_id];
+    for (const IndexBuffer& buffer : wipe_buffers) {
+        m_statistics.wipe_segments_count += buffer.size() / m_buffers[wipe_buffer_id].indices_per_segment();
+    }
+    unsigned int extrude_buffer_id = buffer_id(EMoveType::Extrude);
+    const MultiIndexBuffer& extrude_buffers = indices[extrude_buffer_id];
+    for (const IndexBuffer& buffer : extrude_buffers) {
+        m_statistics.extrude_segments_count += buffer.size() / m_buffers[extrude_buffer_id].indices_per_segment();
+    }
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+
+    log_memory_usage("Loaded G-code generated indices buffers ", vertices, indices);
+
+    // dismiss indices data, no more needed
+    std::vector<MultiIndexBuffer>().swap(indices);
+
+    // layers zs / roles / extruder ids -> extract from result
+    size_t last_travel_s_id = 0;
+    for (size_t i = 0; i < m_moves_count; ++i) {
+        const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
+        if (move.type == EMoveType::Extrude) {
+            // layers zs
+            const double* const last_z = m_layers.empty() ? nullptr : &m_layers.get_zs().back();
+            double z = static_cast<double>(move.position[2]);
+            if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z)
+                m_layers.append(z, { last_travel_s_id, i });
+            else
+                m_layers.get_endpoints().back().last = i;
+            // extruder ids
+            m_extruder_ids.emplace_back(move.extruder_id);
+            // roles
+            if (i > 0)
+                m_roles.emplace_back(move.extrusion_role);
+        }
+        else if (move.type == EMoveType::Travel) {
+            if (i - last_travel_s_id > 1 && !m_layers.empty())
+                m_layers.get_endpoints().back().last = i;
+
+            last_travel_s_id = i;
+        }
+    }
+
+    // roles -> remove duplicates
+    std::sort(m_roles.begin(), m_roles.end());
+    m_roles.erase(std::unique(m_roles.begin(), m_roles.end()), m_roles.end());
+    m_roles.shrink_to_fit();
+
+    // extruder ids -> remove duplicates
+    std::sort(m_extruder_ids.begin(), m_extruder_ids.end());
+    m_extruder_ids.erase(std::unique(m_extruder_ids.begin(), m_extruder_ids.end()), m_extruder_ids.end());
+    m_extruder_ids.shrink_to_fit();
+
+    // set layers z range
+    if (!m_layers.empty())
+        m_layers_z_range = { 0, static_cast<unsigned int>(m_layers.size() - 1) };
+
+    // change color of paths whose layer contains option points
+    if (!options_zs.empty()) {
+        TBuffer& extrude_buffer = m_buffers[buffer_id(EMoveType::Extrude)];
+        for (Path& path : extrude_buffer.paths) {
+            float z = path.sub_paths.front().first.position[2];
+            if (std::find_if(options_zs.begin(), options_zs.end(), [z](float f) { return f - EPSILON <= z && z <= f + EPSILON; }) != options_zs.end())
+                path.cp_color_id = 255 - path.cp_color_id;
+        }
+    }
+
+#if ENABLE_GCODE_VIEWER_STATISTICS
+    m_statistics.load_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time).count();
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+
+    if (progress_dialog != nullptr)
+        progress_dialog->Destroy();
+}
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
 #if ENABLE_GCODE_VIEWER_STATISTICS
@@ -1073,8 +1865,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     };
     auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
         unsigned int index_buffer_id, IndexBuffer& indices, size_t move_id) {
-            // x component of the normal to the current segment (the normal is parallel to the XY plane)
-            float normal_x = (curr.position - prev.position).normalized()[1];
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//            // x component of the normal to the current segment (the normal is parallel to the XY plane)
+//            float normal_x = (curr.position - prev.position).normalized()[1];
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
             if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
                 // add starting index
@@ -1240,7 +2034,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             prev_up = up;
             prev_length = length;
     };
-
     auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
         size_t& buffer_vertices_size, unsigned int index_buffer_id, IndexBuffer& indices, size_t move_id) {
             static Vec3f prev_dir;
@@ -1261,7 +2054,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 buffer.paths.back().first.position = prev.position;
             }
 
-            unsigned int starting_vertices_size = static_cast<unsigned int>(buffer_vertices_size);
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//            unsigned int starting_vertices_size = static_cast<unsigned int>(buffer_vertices_size);
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
             Vec3f dir = (curr.position - prev.position).normalized();
             Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized();
@@ -1277,29 +2072,56 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
             float length = (curr_pos - prev_pos).norm();
             if (last_path.vertices_count() == 1) {
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                 // 1st segment
-                buffer_vertices_size += 8;
 
                 // triangles starting cap
-                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1);
-                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
+                store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 2, buffer_vertices_size + 1);
+                store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 3, buffer_vertices_size + 2);
 
                 // dummy triangles outer corner cap
-                append_dummy_cap(indices, starting_vertices_size);
+                append_dummy_cap(indices, buffer_vertices_size);
 
                 // triangles sides
-                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4);
-                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
-                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 2, starting_vertices_size + 5);
-                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 6, starting_vertices_size + 5);
-                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 6);
-                store_triangle(indices, starting_vertices_size + 3, starting_vertices_size + 7, starting_vertices_size + 6);
-                store_triangle(indices, starting_vertices_size + 3, starting_vertices_size + 0, starting_vertices_size + 7);
-                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 4, starting_vertices_size + 7);
+                store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 1, buffer_vertices_size + 4);
+                store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size + 5, buffer_vertices_size + 4);
+                store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size + 2, buffer_vertices_size + 5);
+                store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 6, buffer_vertices_size + 5);
+                store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 3, buffer_vertices_size + 6);
+                store_triangle(indices, buffer_vertices_size + 3, buffer_vertices_size + 7, buffer_vertices_size + 6);
+                store_triangle(indices, buffer_vertices_size + 3, buffer_vertices_size + 0, buffer_vertices_size + 7);
+                store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 4, buffer_vertices_size + 7);
 
                 // triangles ending cap
-                store_triangle(indices, starting_vertices_size + 4, starting_vertices_size + 6, starting_vertices_size + 7);
-                store_triangle(indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6);
+                store_triangle(indices, buffer_vertices_size + 4, buffer_vertices_size + 6, buffer_vertices_size + 7);
+                store_triangle(indices, buffer_vertices_size + 4, buffer_vertices_size + 5, buffer_vertices_size + 6);
+
+                buffer_vertices_size += 8;
+
+//                // 1st segment
+//                buffer_vertices_size += 8;
+//
+//                // triangles starting cap
+//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1);
+//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
+//
+//                // dummy triangles outer corner cap
+//                append_dummy_cap(indices, starting_vertices_size);
+//
+//                // triangles sides
+//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4);
+//                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
+//                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 2, starting_vertices_size + 5);
+//                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 6, starting_vertices_size + 5);
+//                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 6);
+//                store_triangle(indices, starting_vertices_size + 3, starting_vertices_size + 7, starting_vertices_size + 6);
+//                store_triangle(indices, starting_vertices_size + 3, starting_vertices_size + 0, starting_vertices_size + 7);
+//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 4, starting_vertices_size + 7);
+//
+//                // triangles ending cap
+//                store_triangle(indices, starting_vertices_size + 4, starting_vertices_size + 6, starting_vertices_size + 7);
+//                store_triangle(indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6);
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
             }
             else {
                 // any other segment
@@ -1330,45 +2152,87 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                     }
                 }
 
-                buffer_vertices_size += 6;
-
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                 // triangles starting cap
-                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size - 2, starting_vertices_size + 0);
-                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 2);
+                store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size - 2, buffer_vertices_size + 0);
+                store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 1, buffer_vertices_size - 2);
 
                 // triangles outer corner cap
                 if (is_right_turn) {
                     if (left_displaced)
                         // dummy triangles
-                        append_dummy_cap(indices, starting_vertices_size);
+                        append_dummy_cap(indices, buffer_vertices_size);
                     else {
-                        store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 1);
-                        store_triangle(indices, starting_vertices_size + 1, starting_vertices_size - 2, starting_vertices_size - 1);
+                        store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 1, buffer_vertices_size - 1);
+                        store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size - 2, buffer_vertices_size - 1);
                     }
                 }
                 else {
                     if (right_displaced)
                         // dummy triangles
-                        append_dummy_cap(indices, starting_vertices_size);
+                        append_dummy_cap(indices, buffer_vertices_size);
                     else {
-                        store_triangle(indices, starting_vertices_size - 4, starting_vertices_size - 3, starting_vertices_size + 0);
-                        store_triangle(indices, starting_vertices_size - 3, starting_vertices_size - 2, starting_vertices_size + 0);
+                        store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size - 3, buffer_vertices_size + 0);
+                        store_triangle(indices, buffer_vertices_size - 3, buffer_vertices_size - 2, buffer_vertices_size + 0);
                     }
                 }
 
                 // triangles sides
-                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 0, starting_vertices_size + 2);
-                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
-                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size - 2, starting_vertices_size + 3);
-                store_triangle(indices, starting_vertices_size - 2, starting_vertices_size + 4, starting_vertices_size + 3);
-                store_triangle(indices, starting_vertices_size - 2, starting_vertices_size + 1, starting_vertices_size + 4);
-                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
-                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size - 4, starting_vertices_size + 5);
-                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 2, starting_vertices_size + 5);
+                store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 0, buffer_vertices_size + 2);
+                store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size + 3, buffer_vertices_size + 2);
+                store_triangle(indices, buffer_vertices_size + 0, buffer_vertices_size - 2, buffer_vertices_size + 3);
+                store_triangle(indices, buffer_vertices_size - 2, buffer_vertices_size + 4, buffer_vertices_size + 3);
+                store_triangle(indices, buffer_vertices_size - 2, buffer_vertices_size + 1, buffer_vertices_size + 4);
+                store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size + 5, buffer_vertices_size + 4);
+                store_triangle(indices, buffer_vertices_size + 1, buffer_vertices_size - 4, buffer_vertices_size + 5);
+                store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 2, buffer_vertices_size + 5);
 
                 // triangles ending cap
-                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 4, starting_vertices_size + 5);
-                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 4);
+                store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 4, buffer_vertices_size + 5);
+                store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 3, buffer_vertices_size + 4);
+
+                buffer_vertices_size += 6;
+
+//                buffer_vertices_size += 6;
+//
+//                // triangles starting cap
+//                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size - 2, starting_vertices_size + 0);
+//                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 2);
+//
+//                // triangles outer corner cap
+//                if (is_right_turn) {
+//                    if (left_displaced)
+//                        // dummy triangles
+//                        append_dummy_cap(indices, starting_vertices_size);
+//                    else {
+//                        store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 1);
+//                        store_triangle(indices, starting_vertices_size + 1, starting_vertices_size - 2, starting_vertices_size - 1);
+//                    }
+//                }
+//                else {
+//                    if (right_displaced)
+//                        // dummy triangles
+//                        append_dummy_cap(indices, starting_vertices_size);
+//                    else {
+//                        store_triangle(indices, starting_vertices_size - 4, starting_vertices_size - 3, starting_vertices_size + 0);
+//                        store_triangle(indices, starting_vertices_size - 3, starting_vertices_size - 2, starting_vertices_size + 0);
+//                    }
+//                }
+//
+//                // triangles sides
+//                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 0, starting_vertices_size + 2);
+//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
+//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size - 2, starting_vertices_size + 3);
+//                store_triangle(indices, starting_vertices_size - 2, starting_vertices_size + 4, starting_vertices_size + 3);
+//                store_triangle(indices, starting_vertices_size - 2, starting_vertices_size + 1, starting_vertices_size + 4);
+//                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
+//                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size - 4, starting_vertices_size + 5);
+//                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 2, starting_vertices_size + 5);
+//
+//                // triangles ending cap
+//                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 4, starting_vertices_size + 5);
+//                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 4);
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
             }
 
             last_path.last = { index_buffer_id, indices.size() - 1, move_id, curr.position };
@@ -1446,7 +2310,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 #if ENABLE_GCODE_VIEWER_STATISTICS
         m_statistics.total_vertices_gpu_size += buffer_vertices.size() * sizeof(float);
         m_statistics.max_vbuffer_gpu_size = std::max(m_statistics.max_vbuffer_gpu_size, static_cast<int64_t>(buffer_vertices.size() * sizeof(float)));
-        m_statistics.max_vertices_in_vertex_buffer = std::max(m_statistics.max_vertices_in_vertex_buffer, static_cast<int64_t>(buffer.vertices.count));
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//        m_statistics.max_vertices_in_vertex_buffer = std::max(m_statistics.max_vertices_in_vertex_buffer, static_cast<int64_t>(buffer.vertices.count));
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 
         if (buffer.vertices.count > 0) {
@@ -1557,7 +2423,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 #if ENABLE_GCODE_VIEWER_STATISTICS
             m_statistics.total_indices_gpu_size += ibuffer.count * sizeof(unsigned int);
             m_statistics.max_ibuffer_gpu_size = std::max(m_statistics.max_ibuffer_gpu_size, static_cast<int64_t>(ibuffer.count * sizeof(unsigned int)));
-            m_statistics.max_indices_in_index_buffer = std::max(m_statistics.max_indices_in_index_buffer, static_cast<int64_t>(ibuffer.count));
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//            m_statistics.max_indices_in_index_buffer = std::max(m_statistics.max_indices_in_index_buffer, static_cast<int64_t>(ibuffer.count));
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 
             if (ibuffer.count > 0) {
@@ -1579,17 +2447,26 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     unsigned int travel_buffer_id = buffer_id(EMoveType::Travel);
     const MultiIndexBuffer& travel_buffer_indices = indices[travel_buffer_id];
     for (size_t i = 0; i < travel_buffer_indices.size(); ++i) {
-        m_statistics.travel_segments_count = travel_buffer_indices[i].size() / m_buffers[travel_buffer_id].indices_per_segment();
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+        m_statistics.travel_segments_count += travel_buffer_indices[i].size() / m_buffers[travel_buffer_id].indices_per_segment();
+//        m_statistics.travel_segments_count = travel_buffer_indices[i].size() / m_buffers[travel_buffer_id].indices_per_segment();
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     unsigned int wipe_buffer_id = buffer_id(EMoveType::Wipe);
     const MultiIndexBuffer& wipe_buffer_indices = indices[wipe_buffer_id];
     for (size_t i = 0; i < wipe_buffer_indices.size(); ++i) {
-        m_statistics.wipe_segments_count = wipe_buffer_indices[i].size() / m_buffers[wipe_buffer_id].indices_per_segment();
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+        m_statistics.wipe_segments_count += wipe_buffer_indices[i].size() / m_buffers[wipe_buffer_id].indices_per_segment();
+//        m_statistics.wipe_segments_count = wipe_buffer_indices[i].size() / m_buffers[wipe_buffer_id].indices_per_segment();
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     unsigned int extrude_buffer_id = buffer_id(EMoveType::Extrude);
     const MultiIndexBuffer& extrude_buffer_indices = indices[extrude_buffer_id];
     for (size_t i = 0; i < extrude_buffer_indices.size(); ++i) {
-        m_statistics.extrude_segments_count = extrude_buffer_indices[i].size() / m_buffers[extrude_buffer_id].indices_per_segment();
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+        m_statistics.extrude_segments_count += extrude_buffer_indices[i].size() / m_buffers[extrude_buffer_id].indices_per_segment();
+//        m_statistics.extrude_segments_count = extrude_buffer_indices[i].size() / m_buffers[extrude_buffer_id].indices_per_segment();
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 
@@ -1655,6 +2532,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     if (progress_dialog != nullptr)
         progress_dialog->Destroy();
 }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
 void GCodeViewer::load_shells(const Print& print, bool initialized)
 {
@@ -1714,6 +2594,264 @@ void GCodeViewer::load_shells(const Print& print, bool initialized)
     }
 }
 
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const
+{
+#if ENABLE_GCODE_VIEWER_STATISTICS
+    auto start_time = std::chrono::high_resolution_clock::now();
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+
+    auto extrusion_color = [this](const Path& path) {
+        Color color;
+        switch (m_view_type)
+        {
+        case EViewType::FeatureType:    { color = Extrusion_Role_Colors[static_cast<unsigned int>(path.role)]; break; }
+        case EViewType::Height:         { color = m_extrusions.ranges.height.get_color_at(path.height); break; }
+        case EViewType::Width:          { color = m_extrusions.ranges.width.get_color_at(path.width); break; }
+        case EViewType::Feedrate:       { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; }
+        case EViewType::FanSpeed:       { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; }
+        case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; }
+        case EViewType::Tool:           { color = m_tool_colors[path.extruder_id]; break; }
+        case EViewType::ColorPrint:     {
+            if (path.cp_color_id >= static_cast<unsigned char>(m_tool_colors.size())) {
+                color = { 0.5f, 0.5f, 0.5f };
+//                // complementary color
+//                color = m_tool_colors[255 - path.cp_color_id];
+//                color = { 1.0f - color[0], 1.0f - color[1], 1.0f - color[2] };
+            }
+            else
+                color = m_tool_colors[path.cp_color_id];
+
+            break;
+        }
+        default: { color = { 1.0f, 1.0f, 1.0f }; break; }
+        }
+
+        return color;
+    };
+
+    auto travel_color = [this](const Path& path) {
+        return (path.delta_extruder < 0.0f) ? Travel_Colors[2] /* Retract */ :
+            ((path.delta_extruder > 0.0f) ? Travel_Colors[1] /* Extrude */ :
+                Travel_Colors[0] /* Move */);
+    };
+
+    auto is_in_layers_range = [this](const Path& path, size_t min_id, size_t max_id) {
+        auto in_layers_range = [this, min_id, max_id](size_t id) {
+            return m_layers.get_endpoints_at(min_id).first <= id && id <= m_layers.get_endpoints_at(max_id).last;
+        };
+
+        return in_layers_range(path.sub_paths.front().first.s_id) || in_layers_range(path.sub_paths.back().last.s_id);
+    };
+
+    auto is_travel_in_layers_range = [this](size_t path_id, size_t min_id, size_t max_id) {
+        auto is_in_z_range = [](const Path& path, double min_z, double max_z) {
+            auto in_z_range = [min_z, max_z](double z) {
+                return min_z - EPSILON < z && z < max_z + EPSILON;
+            };
+
+            return in_z_range(path.sub_paths.front().first.position[2]) || in_z_range(path.sub_paths.back().last.position[2]);
+        };
+
+        const TBuffer& buffer = m_buffers[buffer_id(EMoveType::Travel)];
+        if (path_id >= buffer.paths.size())
+            return false;
+
+        Path path = buffer.paths[path_id];
+        size_t first = path_id;
+        size_t last = path_id;
+
+        // check adjacent paths
+        while (first > 0 && path.sub_paths.front().first.position.isApprox(buffer.paths[first - 1].sub_paths.back().last.position)) {
+            --first;
+            path.sub_paths.front().first = buffer.paths[first].sub_paths.front().first;
+        }
+        while (last < buffer.paths.size() - 1 && path.sub_paths.back().last.position.isApprox(buffer.paths[last + 1].sub_paths.front().first.position)) {
+            ++last;
+            path.sub_paths.back().last = buffer.paths[last].sub_paths.back().last;
+        }
+
+        size_t min_s_id = m_layers.get_endpoints_at(min_id).first;
+        size_t max_s_id = m_layers.get_endpoints_at(max_id).last;
+
+        return (min_s_id <= path.sub_paths.front().first.s_id && path.sub_paths.front().first.s_id <= max_s_id) ||
+            (min_s_id <= path.sub_paths.back().last.s_id && path.sub_paths.back().last.s_id <= max_s_id);
+    };
+
+#if ENABLE_GCODE_VIEWER_STATISTICS
+    m_statistics.render_paths_size = 0;
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+
+    bool top_layer_only = get_app_config()->get("seq_top_layer_only") == "1";
+
+    SequentialView::Endpoints global_endpoints = { m_moves_count , 0 };
+    SequentialView::Endpoints top_layer_endpoints = global_endpoints;
+    if (top_layer_only || !keep_sequential_current_first) m_sequential_view.current.first = 0;
+    if (!keep_sequential_current_last) m_sequential_view.current.last = m_moves_count;
+
+    // first pass: collect visible paths and update sequential view data
+    std::vector<std::tuple<TBuffer*, unsigned int, unsigned int, unsigned int>> paths;
+    for (TBuffer& buffer : m_buffers) {
+        // reset render paths
+        buffer.render_paths.clear();
+
+        if (!buffer.visible)
+            continue;
+
+        for (size_t i = 0; i < buffer.paths.size(); ++i) {
+            const Path& path = buffer.paths[i];
+            if (path.type == EMoveType::Travel) {
+                if (!is_travel_in_layers_range(i, m_layers_z_range[0], m_layers_z_range[1]))
+                    continue;
+            }
+            else if (!is_in_layers_range(path, m_layers_z_range[0], m_layers_z_range[1]))
+                continue;
+
+            if (path.type == EMoveType::Extrude && !is_visible(path))
+                continue;
+
+            // store valid path
+            for (size_t j = 0; j < path.sub_paths.size(); ++j) {
+                paths.push_back({ &buffer, path.sub_paths[j].first.b_id, static_cast<unsigned int>(i), static_cast<unsigned int>(j) });
+            }
+
+            global_endpoints.first = std::min(global_endpoints.first, path.sub_paths.front().first.s_id);
+            global_endpoints.last = std::max(global_endpoints.last, path.sub_paths.back().last.s_id);
+
+            if (top_layer_only) {
+                if (path.type == EMoveType::Travel) {
+                    if (is_travel_in_layers_range(i, m_layers_z_range[1], m_layers_z_range[1])) {
+                        top_layer_endpoints.first = std::min(top_layer_endpoints.first, path.sub_paths.front().first.s_id);
+                        top_layer_endpoints.last = std::max(top_layer_endpoints.last, path.sub_paths.back().last.s_id);
+                    }
+                }
+                else if (is_in_layers_range(path, m_layers_z_range[1], m_layers_z_range[1])) {
+                    top_layer_endpoints.first = std::min(top_layer_endpoints.first, path.sub_paths.front().first.s_id);
+                    top_layer_endpoints.last = std::max(top_layer_endpoints.last, path.sub_paths.back().last.s_id);
+                }
+            }
+        }
+    }
+
+    // update current sequential position
+    m_sequential_view.current.first = !top_layer_only && keep_sequential_current_first ? std::clamp(m_sequential_view.current.first, global_endpoints.first, global_endpoints.last) : global_endpoints.first;
+    m_sequential_view.current.last = keep_sequential_current_last ? std::clamp(m_sequential_view.current.last, global_endpoints.first, global_endpoints.last) : global_endpoints.last;
+
+    // get the world position from gpu
+    bool found = false;
+    for (const TBuffer& buffer : m_buffers) {
+        // searches the path containing the current position
+        for (const Path& path : buffer.paths) {
+            int sub_path_id = path.get_id_of_sub_path_containing(m_sequential_view.current.last);
+            if (sub_path_id != -1) {
+                const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id];
+                unsigned int offset = static_cast<unsigned int>(m_sequential_view.current.last - sub_path.first.s_id);
+                if (offset > 0) {
+                    if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Line)
+                        offset = 2 * offset - 1;
+                    else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
+                        unsigned int indices_count = buffer.indices_per_segment();
+                        offset = indices_count * (offset - 1) + (indices_count - 6);
+                    }
+                }
+                offset += static_cast<unsigned int>(sub_path.first.i_id);
+
+                // gets the index from the index buffer on gpu
+                const IBuffer& i_buffer = buffer.indices[sub_path.first.b_id];
+                unsigned int index = 0;
+                glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
+                glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(offset * sizeof(unsigned int)), static_cast<GLsizeiptr>(sizeof(unsigned int)), static_cast<void*>(&index)));
+                glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+
+                // gets the position from the vertices buffer on gpu
+                glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo));
+                glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(index * buffer.vertices.vertex_size_bytes()), static_cast<GLsizeiptr>(3 * sizeof(float)), static_cast<void*>(m_sequential_view.current_position.data())));
+                glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+                found = true;
+                break;
+            }
+        }
+
+        if (found)
+            break;
+    }
+
+    // second pass: filter paths by sequential data and collect them by color
+    RenderPath* render_path = nullptr;
+    for (const auto& [buffer, index_buffer_id, path_id, sub_path_id] : paths) {
+        const Path& path = buffer->paths[path_id];
+        const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id];
+        if (m_sequential_view.current.last <= sub_path.first.s_id || sub_path.last.s_id <= m_sequential_view.current.first)
+            continue;
+
+        Color color;
+        switch (path.type)
+        {
+        case EMoveType::Extrude: {
+            if (!top_layer_only ||
+                m_sequential_view.current.last == global_endpoints.last ||
+                is_in_layers_range(path, m_layers_z_range[1], m_layers_z_range[1]))
+                color = extrusion_color(path);
+            else
+                color = { 0.25f, 0.25f, 0.25f };
+
+            break;
+        }
+        case EMoveType::Travel: {
+            if (!top_layer_only || m_sequential_view.current.last == global_endpoints.last || is_travel_in_layers_range(path_id, m_layers_z_range[1], m_layers_z_range[1]))
+                color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path);
+            else
+                color = { 0.25f, 0.25f, 0.25f };
+
+            break;
+        }
+        case EMoveType::Wipe: { color = Wipe_Color; break; }
+        default: { color = { 0.0f, 0.0f, 0.0f }; break; }
+        }
+
+        RenderPath key{ color, static_cast<unsigned int>(index_buffer_id), path_id };
+        if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key))
+            render_path = const_cast<RenderPath*>(&(*buffer->render_paths.emplace(key).first));
+        unsigned int segments_count = std::min(m_sequential_view.current.last, sub_path.last.s_id) - std::max(m_sequential_view.current.first, sub_path.first.s_id) + 1;
+        unsigned int size_in_indices = 0;
+        switch (buffer->render_primitive_type)
+        {
+        case TBuffer::ERenderPrimitiveType::Point:    { size_in_indices = segments_count; break; }
+        case TBuffer::ERenderPrimitiveType::Line:
+        case TBuffer::ERenderPrimitiveType::Triangle: { size_in_indices = buffer->indices_per_segment() * (segments_count - 1); break; }
+        }
+        render_path->sizes.push_back(size_in_indices);
+
+        unsigned int delta_1st = 0;
+        if (sub_path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= sub_path.last.s_id)
+            delta_1st = m_sequential_view.current.first - sub_path.first.s_id;
+
+        if (buffer->render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle)
+            delta_1st *= buffer->indices_per_segment();
+
+        render_path->offsets.push_back(static_cast<size_t>((sub_path.first.i_id + delta_1st) * sizeof(unsigned int)));
+    }
+
+    // set sequential data to their final value
+    m_sequential_view.endpoints = top_layer_only ? top_layer_endpoints : global_endpoints;
+    m_sequential_view.current.first = !top_layer_only && keep_sequential_current_first ? std::clamp(m_sequential_view.current.first, m_sequential_view.endpoints.first, m_sequential_view.endpoints.last) : m_sequential_view.endpoints.first;
+
+    wxGetApp().plater()->enable_preview_moves_slider(!paths.empty());
+
+#if ENABLE_GCODE_VIEWER_STATISTICS
+    for (const TBuffer& buffer : m_buffers) {
+        m_statistics.render_paths_size += SLIC3R_STDUNORDEREDSET_MEMSIZE(buffer.render_paths, RenderPath);
+        for (const RenderPath& path : buffer.render_paths) {
+            m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.sizes, unsigned int);
+            m_statistics.render_paths_size += SLIC3R_STDVEC_MEMSIZE(path.offsets, size_t);
+        }
+    }
+    m_statistics.refresh_paths_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time).count();
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+}
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const
 {
 #if ENABLE_GCODE_VIEWER_STATISTICS
@@ -1961,7 +3099,159 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
     m_statistics.refresh_paths_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time).count();
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+void GCodeViewer::render_toolpaths() const
+{
+#if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
+    float point_size = 20.0f;
+#else
+    float point_size = 0.8f;
+#endif // ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
+    std::array<float, 4> light_intensity = { 0.25f, 0.70f, 0.75f, 0.75f };
+    const Camera& camera = wxGetApp().plater()->get_camera();
+    double zoom = camera.get_zoom();
+    const std::array<int, 4>& viewport = camera.get_viewport();
+    float near_plane_height = camera.get_type() == Camera::Perspective ? static_cast<float>(viewport[3]) / (2.0f * static_cast<float>(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) :
+        static_cast<float>(viewport[3]) * 0.0005;
+
+    auto set_uniform_color = [](const std::array<float, 3>& color, GLShaderProgram& shader) {
+        std::array<float, 4> color4 = { color[0], color[1], color[2], 1.0f };
+        shader.set_uniform("uniform_color", color4);
+    };
+
+    auto render_as_points = [this, zoom, point_size, near_plane_height, set_uniform_color]
+    (const TBuffer& buffer, unsigned int i_buffer_id, EOptionsColors color_id, GLShaderProgram& shader) {
+        set_uniform_color(Options_Colors[static_cast<unsigned int>(color_id)], shader);
+#if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
+        shader.set_uniform("use_fixed_screen_size", 1);
+#else
+        shader.set_uniform("use_fixed_screen_size", 0);
+#endif // ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
+        shader.set_uniform("zoom", zoom);
+        shader.set_uniform("percent_outline_radius", 0.0f);
+        shader.set_uniform("percent_center_radius", 0.33f);
+        shader.set_uniform("point_size", point_size);
+        shader.set_uniform("near_plane_height", near_plane_height);
+
+        glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE));
+        glsafe(::glEnable(GL_POINT_SPRITE));
+
+        for (const RenderPath& path : buffer.render_paths) {
+            if (path.index_buffer_id == i_buffer_id) {
+                glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#if ENABLE_GCODE_VIEWER_STATISTICS
+                ++m_statistics.gl_multi_points_calls_count;
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+            }
+        }
+
+        glsafe(::glDisable(GL_POINT_SPRITE));
+        glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE));
+    };
+
+    auto render_as_lines = [this, light_intensity, set_uniform_color](const TBuffer& buffer, unsigned int index_buffer_id, GLShaderProgram& shader) {
+        shader.set_uniform("light_intensity", light_intensity);
+        for (const RenderPath& path : buffer.render_paths) {
+            if (path.index_buffer_id == index_buffer_id) {
+                set_uniform_color(path.color, shader);
+                glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#if ENABLE_GCODE_VIEWER_STATISTICS
+                ++m_statistics.gl_multi_lines_calls_count;
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+            }
+        }
+    };
+
+    auto render_as_triangles = [this, set_uniform_color](const TBuffer& buffer, unsigned int index_buffer_id, GLShaderProgram& shader) {
+        for (const RenderPath& path : buffer.render_paths) {
+            if (path.index_buffer_id == index_buffer_id) {
+                set_uniform_color(path.color, shader);
+                glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#if ENABLE_GCODE_VIEWER_STATISTICS
+                ++m_statistics.gl_multi_triangles_calls_count;
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+            }
+        }
+    };
+
+    auto line_width = [](double zoom) {
+        return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0));
+    };
+
+    glsafe(::glLineWidth(static_cast<GLfloat>(line_width(zoom))));
+
+    unsigned char begin_id = buffer_id(EMoveType::Retract);
+    unsigned char end_id = buffer_id(EMoveType::Count);
+
+    for (unsigned char i = begin_id; i < end_id; ++i) {
+        const TBuffer& buffer = m_buffers[i];
+        if (!buffer.visible || !buffer.has_data())
+            continue;
+
+        GLShaderProgram* shader = wxGetApp().get_shader(buffer.shader.c_str());
+        if (shader != nullptr) {
+            shader->start_using();
+
+            for (size_t j = 0; j < buffer.indices.size(); ++j) {
+                const IBuffer& i_buffer = buffer.indices[j];
+
+                glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo));
+                glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_size()));
+                glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
+                bool has_normals = buffer.vertices.normal_size_floats() > 0;
+                if (has_normals) {
+                    glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_size()));
+                    glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
+                }
+
+                glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
+
+                switch (buffer.render_primitive_type)
+                {
+                case TBuffer::ERenderPrimitiveType::Point: {
+                    EOptionsColors color;
+                    switch (buffer_type(i))
+                    {
+                    case EMoveType::Tool_change:  { color = EOptionsColors::ToolChanges; break; }
+                    case EMoveType::Color_change: { color = EOptionsColors::ColorChanges; break; }
+                    case EMoveType::Pause_Print:  { color = EOptionsColors::PausePrints; break; }
+                    case EMoveType::Custom_GCode: { color = EOptionsColors::CustomGCodes; break; }
+                    case EMoveType::Retract:      { color = EOptionsColors::Retractions; break; }
+                    case EMoveType::Unretract:    { color = EOptionsColors::Unretractions; break; }
+                    }
+                    render_as_points(buffer, static_cast<unsigned int>(j), color, *shader);
+                    break;
+                }
+                case TBuffer::ERenderPrimitiveType::Line: {
+                    render_as_lines(buffer, static_cast<unsigned int>(j), *shader);
+                    break;
+                }
+                case TBuffer::ERenderPrimitiveType::Triangle: {
+                    render_as_triangles(buffer, static_cast<unsigned int>(j), *shader);
+                    break;
+                }
+                }
+
+                glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+
+                if (has_normals)
+                    glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
+
+                glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
+                glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+            }
+
+            shader->stop_using();
+        }
+    }
+}
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 void GCodeViewer::render_toolpaths() const
 {
 #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
@@ -2108,6 +3398,9 @@ void GCodeViewer::render_toolpaths() const
         }
     }
 }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
 void GCodeViewer::render_shells() const
 {
@@ -2908,10 +4201,12 @@ void GCodeViewer::render_statistics() const
         add_counter(std::string("Extrude segments count:"), m_statistics.extrude_segments_count);
         ImGui::Separator();
         add_counter(std::string("VBuffers count:"), m_statistics.vbuffers_count);
-        add_counter(std::string("IBuffers  count:"), m_statistics.ibuffers_count);
-        ImGui::Separator();
-        add_counter(std::string("Max vertices in VBuffer:"), m_statistics.max_vertices_in_vertex_buffer);
-        add_counter(std::string("Max indices in IBuffer:"), m_statistics.max_indices_in_index_buffer);
+        add_counter(std::string("IBuffers count:"), m_statistics.ibuffers_count);
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//        ImGui::Separator();
+//        add_counter(std::string("Max vertices in VBuffer:"), m_statistics.max_vertices_in_vertex_buffer);
+//        add_counter(std::string("Max indices in IBuffer:"), m_statistics.max_indices_in_index_buffer);
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
         wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
     }
@@ -2935,9 +4230,19 @@ void GCodeViewer::log_memory_used(const std::string& label, int64_t additional)
         }
         int64_t layers_size = SLIC3R_STDVEC_MEMSIZE(m_layers.get_zs(), double);
         layers_size += SLIC3R_STDVEC_MEMSIZE(m_layers.get_endpoints(), Layers::Endpoints);
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        BOOST_LOG_TRIVIAL(trace) << label
+            << "(" << format_memsize_MB(additional + paths_size + render_paths_size + layers_size) << ");"
+            << log_memory_info();
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         BOOST_LOG_TRIVIAL(trace) << label
             << format_memsize_MB(additional + paths_size + render_paths_size + layers_size)
             << log_memory_info();
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
 }
 
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index 5f276b09f..51fd560a3 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -21,6 +21,11 @@ class GCodeViewer
 {
     using Color = std::array<float, 3>;
     using VertexBuffer = std::vector<float>;
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+    using MultiVertexBuffer = std::vector<VertexBuffer>;
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     using IndexBuffer = std::vector<unsigned int>;
     using MultiIndexBuffer = std::vector<IndexBuffer>;
 
@@ -40,7 +45,7 @@ class GCodeViewer
         CustomGCodes
     };
 
-    // vbo buffer containing vertices data used to rendder a specific toolpath type
+    // vbo buffer containing vertices data used to render a specific toolpath type
     struct VBuffer
     {
         enum class EFormat : unsigned char
@@ -54,8 +59,17 @@ class GCodeViewer
         };
 
         EFormat format{ EFormat::Position };
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        // vbos id
+        std::vector<unsigned int> ids;
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         // vbo id
         unsigned int id{ 0 };
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         // count of vertices, updated after data are sent to gpu
         size_t count{ 0 };
 
@@ -66,26 +80,24 @@ class GCodeViewer
 
         size_t position_offset_floats() const { return 0; }
         size_t position_offset_size() const { return position_offset_floats() * sizeof(float); }
-        size_t position_size_floats() const
-        {
+        size_t position_size_floats() const {
             switch (format)
             {
             case EFormat::Position:
             case EFormat::PositionNormal3: { return 3; }
             case EFormat::PositionNormal1: { return 4; }
-            default: { return 0; }
+            default:                       { return 0; }
             }
         }
         size_t position_size_bytes() const { return position_size_floats() * sizeof(float); }
 
-        size_t normal_offset_floats() const
-        {
+        size_t normal_offset_floats() const {
             switch (format)
             {
             case EFormat::Position:
             case EFormat::PositionNormal1: { return 0; }
             case EFormat::PositionNormal3: { return 3; }
-            default: { return 0; }
+            default:                       { return 0; }
             }
         }
         size_t normal_offset_size() const { return normal_offset_floats() * sizeof(float); }
@@ -103,11 +115,22 @@ class GCodeViewer
         void reset();
     };
 
-    // ibo buffer containing indices data (lines/triangles) used to render a specific toolpath type
+    // ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type
     struct IBuffer
     {
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        // id of the associated vertex buffer
+        unsigned int vbo{ 0 };
+        // ibo id
+        unsigned int ibo{ 0 };
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         // ibo id
         unsigned int id{ 0 };
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         // count of indices, updated after data are sent to gpu
         size_t count{ 0 };
 
@@ -128,10 +151,30 @@ class GCodeViewer
             Vec3f position{ Vec3f::Zero() };
         };
 
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        struct Sub_Path
+        {
+            Endpoint first;
+            Endpoint last;
+
+            bool contains(size_t s_id) const {
+                return first.s_id <= s_id && s_id <= last.s_id;
+            }
+        };
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
         EMoveType type{ EMoveType::Noop };
         ExtrusionRole role{ erNone };
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if !ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         Endpoint first;
         Endpoint last;
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // !ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         float delta_extruder{ 0.0f };
         float height{ 0.0f };
         float width{ 0.0f };
@@ -140,13 +183,46 @@ class GCodeViewer
         float volumetric_rate{ 0.0f };
         unsigned char extruder_id{ 0 };
         unsigned char cp_color_id{ 0 };
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        std::vector<Sub_Path> sub_paths;
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
         bool matches(const GCodeProcessor::MoveVertex& move) const;
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        size_t vertices_count() const {
+            return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
+        }
+        bool contains(size_t s_id) const {
+            return sub_paths.empty() ? false : sub_paths.front().first.s_id <= s_id && s_id <= sub_paths.back().last.s_id;
+        }
+        int get_id_of_sub_path_containing(size_t s_id) const {
+            if (sub_paths.empty())
+                return -1;
+            else {
+                for (int i = 0; i < static_cast<int>(sub_paths.size()); ++i) {
+                    if (sub_paths[i].contains(s_id))
+                        return i;
+                }
+                return -1;
+            }
+        }
+        void add_sub_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) {
+            Endpoint endpoint = { b_id, i_id, s_id, move.position };
+            sub_paths.push_back({ endpoint , endpoint });
+        }
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         size_t vertices_count() const { return last.s_id - first.s_id + 1; }
         bool contains(size_t id) const { return first.s_id <= id && id <= last.s_id; }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     };
 
-    // Used to batch the indices needed to render paths
+    // Used to batch the indices needed to render the paths
     struct RenderPath
     {
         // Render path property
@@ -202,10 +278,28 @@ class GCodeViewer
         bool visible{ false };
 
         void reset();
+
         // b_id index of buffer contained in this->indices
         // i_id index of first index contained in this->indices[b_id]
         // s_id index of first vertex contained in this->vertices
         void add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
+
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        unsigned int max_vertices_per_segment() const {
+            switch (render_primitive_type)
+            {
+            case ERenderPrimitiveType::Point:    { return 1; }
+            case ERenderPrimitiveType::Line:     { return 2; }
+            case ERenderPrimitiveType::Triangle: { return 8; }
+            default:                             { return 0; }
+            }
+        }
+
+        size_t max_vertices_per_segment_size_floats() const { return vertices.vertex_size_floats() * static_cast<size_t>(max_vertices_per_segment()); }
+        size_t max_vertices_per_segment_size_bytes() const { return max_vertices_per_segment_size_floats() * sizeof(float); }
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         unsigned int indices_per_segment() const {
             switch (render_primitive_type)
             {
@@ -215,14 +309,22 @@ class GCodeViewer
             default:                             { return 0; }
             }
         }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(unsigned int)); }
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         unsigned int start_segment_vertex_offset() const {
-            switch (render_primitive_type)
-            {
-            case ERenderPrimitiveType::Point:
-            case ERenderPrimitiveType::Line: 
-            case ERenderPrimitiveType::Triangle:
-            default: { return 0; }
-            }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+            return 0;
+//            switch (render_primitive_type)
+//            {
+//            case ERenderPrimitiveType::Point:
+//            case ERenderPrimitiveType::Line: 
+//            case ERenderPrimitiveType::Triangle:
+//            default: { return 0; }
+//            }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         }
         unsigned int end_segment_vertex_offset() const {
             switch (render_primitive_type)
@@ -234,7 +336,17 @@ class GCodeViewer
             }
         }
 
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        bool has_data() const {
+            return !vertices.ids.empty() && vertices.ids.front() != 0 && !indices.empty() && indices.front().ibo != 0;
+        }
+#else
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         bool has_data() const { return vertices.id != 0 && !indices.empty() && indices.front().id != 0; }
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     };
 
     // helper to render shells
@@ -309,6 +421,13 @@ class GCodeViewer
         {
             size_t first{ 0 };
             size_t last{ 0 };
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+            bool operator == (const Endpoints& other) const {
+                return first == other.first && last == other.last;
+            }
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         };
 
     private:
@@ -316,14 +435,12 @@ class GCodeViewer
         std::vector<Endpoints> m_endpoints;
 
     public:
-        void append(double z, Endpoints endpoints)
-        {
+        void append(double z, Endpoints endpoints) {
             m_zs.emplace_back(z);
             m_endpoints.emplace_back(endpoints);
         }
 
-        void reset()
-        {
+        void reset() {
             m_zs = std::vector<double>();
             m_endpoints = std::vector<Endpoints>();
         }
@@ -335,6 +452,19 @@ class GCodeViewer
         std::vector<Endpoints>& get_endpoints() { return m_endpoints; }
         double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; }
         Endpoints get_endpoints_at(unsigned int id) const { return (id < m_endpoints.size()) ? m_endpoints[id] : Endpoints(); }
+
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+        bool operator != (const Layers& other) const {
+            if (m_zs != other.m_zs)
+                return true;
+            if (!(m_endpoints == other.m_endpoints))
+                return true;
+
+            return false;
+        }
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     };
 
 #if ENABLE_GCODE_VIEWER_STATISTICS
@@ -363,8 +493,10 @@ class GCodeViewer
         int64_t extrude_segments_count{ 0 };
         int64_t vbuffers_count{ 0 };
         int64_t ibuffers_count{ 0 };
-        int64_t max_vertices_in_vertex_buffer{ 0 };
-        int64_t max_indices_in_index_buffer{ 0 };
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//        int64_t max_vertices_in_vertex_buffer{ 0 };
+//        int64_t max_indices_in_index_buffer{ 0 };
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
         void reset_all() {
             reset_times();
@@ -402,8 +534,10 @@ class GCodeViewer
             extrude_segments_count =  0;
             vbuffers_count = 0;
             ibuffers_count = 0;
-            max_vertices_in_vertex_buffer = 0;
-            max_indices_in_index_buffer = 0;
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//            max_vertices_in_vertex_buffer = 0;
+//            max_indices_in_index_buffer = 0;
+//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         }
     };
 #endif // ENABLE_GCODE_VIEWER_STATISTICS

From e443fe49ad6de3dbc36225d638f095fcad30d303 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 12 Jan 2021 14:03:58 +0100
Subject: [PATCH 07/20] ENABLE_SPLITTED_VERTEX_BUFFER - fixed visualization of
 last segment for paths whose vertex buffer has been splitted

---
 src/slic3r/GUI/GCodeViewer.cpp | 220 ++++++++++-----------------------
 src/slic3r/GUI/GCodeViewer.hpp |  23 +---
 2 files changed, 67 insertions(+), 176 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index aaafd333d..64d064a33 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1079,15 +1079,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
     auto log_memory_usage = [this](const std::string& label, const std::vector<MultiVertexBuffer>& vertices, const std::vector<MultiIndexBuffer>& indices) {
         int64_t vertices_size = 0;
-        for (const MultiVertexBuffer& v_multibuffer : vertices) {
-            for (const VertexBuffer& v_buffer : v_multibuffer) {
-                vertices_size += SLIC3R_STDVEC_MEMSIZE(v_buffer, float);
+        for (const MultiVertexBuffer& buffers : vertices) {
+            for (const VertexBuffer& buffer : buffers) {
+                vertices_size += SLIC3R_STDVEC_MEMSIZE(buffer, float);
             }
         }
         int64_t indices_size = 0;
-        for (const MultiIndexBuffer& i_multibuffer : indices) {
-            for (const IndexBuffer& i_buffer : i_multibuffer) {
-                indices_size += SLIC3R_STDVEC_MEMSIZE(i_buffer, unsigned int);
+        for (const MultiIndexBuffer& buffers : indices) {
+            for (const IndexBuffer& buffer : buffers) {
+                indices_size += SLIC3R_STDVEC_MEMSIZE(buffer, unsigned int);
             }
         }
         log_memory_used(label, vertices_size + indices_size);
@@ -1100,8 +1100,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         vertices.push_back(curr.position[2]);
     };
     auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
-        unsigned int i_buffer_id, IndexBuffer& indices, size_t move_id) {
-            buffer.add_path(curr, i_buffer_id, indices.size(), move_id);
+        unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
+            buffer.add_path(curr, ibuffer_id, indices.size(), move_id);
             indices.push_back(static_cast<unsigned int>(indices.size()));
     };
 
@@ -1125,11 +1125,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         add_vertex(curr);
     };
     auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
-        unsigned int i_buffer_id, IndexBuffer& indices, size_t move_id) {
+        unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
             if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
                 // add starting index
                 indices.push_back(static_cast<unsigned int>(indices.size()));
-                buffer.add_path(curr, i_buffer_id, indices.size() - 1, move_id - 1);
+                buffer.add_path(curr, ibuffer_id, indices.size() - 1, move_id - 1);
                 buffer.paths.back().sub_paths.front().first.position = prev.position;
             }
 
@@ -1141,7 +1141,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
             // add current index
             indices.push_back(static_cast<unsigned int>(indices.size()));
-            last_path.sub_paths.back().last = { i_buffer_id, indices.size() - 1, move_id, curr.position };
+            last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position };
     };
 
     // format data into the buffers to be rendered as solid
@@ -1629,7 +1629,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
             if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
                 Path& last_path = t_buffer.paths.back();
-                last_path.add_sub_path(curr, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i);
+                last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
             }
         }
 
@@ -1700,21 +1700,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     for (const TBuffer& buffer : m_buffers) {
         m_statistics.paths_size += SLIC3R_STDVEC_MEMSIZE(buffer.paths, Path);
     }
-    unsigned int travel_buffer_id = buffer_id(EMoveType::Travel);
-    const MultiIndexBuffer& travel_buffers = indices[travel_buffer_id];
-    for (const IndexBuffer& buffer : travel_buffers) {
-        m_statistics.travel_segments_count += buffer.size() / m_buffers[travel_buffer_id].indices_per_segment();
-    }
-    unsigned int wipe_buffer_id = buffer_id(EMoveType::Wipe);
-    const MultiIndexBuffer& wipe_buffers = indices[wipe_buffer_id];
-    for (const IndexBuffer& buffer : wipe_buffers) {
-        m_statistics.wipe_segments_count += buffer.size() / m_buffers[wipe_buffer_id].indices_per_segment();
-    }
-    unsigned int extrude_buffer_id = buffer_id(EMoveType::Extrude);
-    const MultiIndexBuffer& extrude_buffers = indices[extrude_buffer_id];
-    for (const IndexBuffer& buffer : extrude_buffers) {
-        m_statistics.extrude_segments_count += buffer.size() / m_buffers[extrude_buffer_id].indices_per_segment();
-    }
+
+    auto update_segments_count = [&](EMoveType type, int64_t& count) {
+        unsigned int id = buffer_id(type);
+        const MultiIndexBuffer& buffers = indices[id];
+        for (const IndexBuffer& buffer : buffers) {
+            count += buffer.size() / m_buffers[id].indices_per_segment();
+        }
+    };
+
+    update_segments_count(EMoveType::Travel, m_statistics.travel_segments_count);
+    update_segments_count(EMoveType::Wipe, m_statistics.wipe_segments_count);
+    update_segments_count(EMoveType::Extrude, m_statistics.extrude_segments_count);
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 
     log_memory_usage("Loaded G-code generated indices buffers ", vertices, indices);
@@ -1865,11 +1862,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     };
     auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
         unsigned int index_buffer_id, IndexBuffer& indices, size_t move_id) {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-//            // x component of the normal to the current segment (the normal is parallel to the XY plane)
-//            float normal_x = (curr.position - prev.position).normalized()[1];
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-
             if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
                 // add starting index
                 indices.push_back(static_cast<unsigned int>(indices.size()));
@@ -2054,10 +2046,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 buffer.paths.back().first.position = prev.position;
             }
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-//            unsigned int starting_vertices_size = static_cast<unsigned int>(buffer_vertices_size);
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-
             Vec3f dir = (curr.position - prev.position).normalized();
             Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized();
             Vec3f up = right.cross(dir);
@@ -2072,7 +2060,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
             float length = (curr_pos - prev_pos).norm();
             if (last_path.vertices_count() == 1) {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                 // 1st segment
 
                 // triangles starting cap
@@ -2097,31 +2084,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 store_triangle(indices, buffer_vertices_size + 4, buffer_vertices_size + 5, buffer_vertices_size + 6);
 
                 buffer_vertices_size += 8;
-
-//                // 1st segment
-//                buffer_vertices_size += 8;
-//
-//                // triangles starting cap
-//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 2, starting_vertices_size + 1);
-//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
-//
-//                // dummy triangles outer corner cap
-//                append_dummy_cap(indices, starting_vertices_size);
-//
-//                // triangles sides
-//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 1, starting_vertices_size + 4);
-//                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
-//                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 2, starting_vertices_size + 5);
-//                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 6, starting_vertices_size + 5);
-//                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 6);
-//                store_triangle(indices, starting_vertices_size + 3, starting_vertices_size + 7, starting_vertices_size + 6);
-//                store_triangle(indices, starting_vertices_size + 3, starting_vertices_size + 0, starting_vertices_size + 7);
-//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 4, starting_vertices_size + 7);
-//
-//                // triangles ending cap
-//                store_triangle(indices, starting_vertices_size + 4, starting_vertices_size + 6, starting_vertices_size + 7);
-//                store_triangle(indices, starting_vertices_size + 4, starting_vertices_size + 5, starting_vertices_size + 6);
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
             }
             else {
                 // any other segment
@@ -2152,7 +2114,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                     }
                 }
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                 // triangles starting cap
                 store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size - 2, buffer_vertices_size + 0);
                 store_triangle(indices, buffer_vertices_size - 4, buffer_vertices_size + 1, buffer_vertices_size - 2);
@@ -2192,47 +2153,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 store_triangle(indices, buffer_vertices_size + 2, buffer_vertices_size + 3, buffer_vertices_size + 4);
 
                 buffer_vertices_size += 6;
-
-//                buffer_vertices_size += 6;
-//
-//                // triangles starting cap
-//                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size - 2, starting_vertices_size + 0);
-//                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 2);
-//
-//                // triangles outer corner cap
-//                if (is_right_turn) {
-//                    if (left_displaced)
-//                        // dummy triangles
-//                        append_dummy_cap(indices, starting_vertices_size);
-//                    else {
-//                        store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 1, starting_vertices_size - 1);
-//                        store_triangle(indices, starting_vertices_size + 1, starting_vertices_size - 2, starting_vertices_size - 1);
-//                    }
-//                }
-//                else {
-//                    if (right_displaced)
-//                        // dummy triangles
-//                        append_dummy_cap(indices, starting_vertices_size);
-//                    else {
-//                        store_triangle(indices, starting_vertices_size - 4, starting_vertices_size - 3, starting_vertices_size + 0);
-//                        store_triangle(indices, starting_vertices_size - 3, starting_vertices_size - 2, starting_vertices_size + 0);
-//                    }
-//                }
-//
-//                // triangles sides
-//                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 0, starting_vertices_size + 2);
-//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size + 3, starting_vertices_size + 2);
-//                store_triangle(indices, starting_vertices_size + 0, starting_vertices_size - 2, starting_vertices_size + 3);
-//                store_triangle(indices, starting_vertices_size - 2, starting_vertices_size + 4, starting_vertices_size + 3);
-//                store_triangle(indices, starting_vertices_size - 2, starting_vertices_size + 1, starting_vertices_size + 4);
-//                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size + 5, starting_vertices_size + 4);
-//                store_triangle(indices, starting_vertices_size + 1, starting_vertices_size - 4, starting_vertices_size + 5);
-//                store_triangle(indices, starting_vertices_size - 4, starting_vertices_size + 2, starting_vertices_size + 5);
-//
-//                // triangles ending cap
-//                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 4, starting_vertices_size + 5);
-//                store_triangle(indices, starting_vertices_size + 2, starting_vertices_size + 3, starting_vertices_size + 4);
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
             }
 
             last_path.last = { index_buffer_id, indices.size() - 1, move_id, curr.position };
@@ -2310,9 +2230,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 #if ENABLE_GCODE_VIEWER_STATISTICS
         m_statistics.total_vertices_gpu_size += buffer_vertices.size() * sizeof(float);
         m_statistics.max_vbuffer_gpu_size = std::max(m_statistics.max_vbuffer_gpu_size, static_cast<int64_t>(buffer_vertices.size() * sizeof(float)));
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-//        m_statistics.max_vertices_in_vertex_buffer = std::max(m_statistics.max_vertices_in_vertex_buffer, static_cast<int64_t>(buffer.vertices.count));
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 
         if (buffer.vertices.count > 0) {
@@ -2423,9 +2340,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 #if ENABLE_GCODE_VIEWER_STATISTICS
             m_statistics.total_indices_gpu_size += ibuffer.count * sizeof(unsigned int);
             m_statistics.max_ibuffer_gpu_size = std::max(m_statistics.max_ibuffer_gpu_size, static_cast<int64_t>(ibuffer.count * sizeof(unsigned int)));
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-//            m_statistics.max_indices_in_index_buffer = std::max(m_statistics.max_indices_in_index_buffer, static_cast<int64_t>(ibuffer.count));
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 
             if (ibuffer.count > 0) {
@@ -2447,26 +2361,17 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     unsigned int travel_buffer_id = buffer_id(EMoveType::Travel);
     const MultiIndexBuffer& travel_buffer_indices = indices[travel_buffer_id];
     for (size_t i = 0; i < travel_buffer_indices.size(); ++i) {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         m_statistics.travel_segments_count += travel_buffer_indices[i].size() / m_buffers[travel_buffer_id].indices_per_segment();
-//        m_statistics.travel_segments_count = travel_buffer_indices[i].size() / m_buffers[travel_buffer_id].indices_per_segment();
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     unsigned int wipe_buffer_id = buffer_id(EMoveType::Wipe);
     const MultiIndexBuffer& wipe_buffer_indices = indices[wipe_buffer_id];
     for (size_t i = 0; i < wipe_buffer_indices.size(); ++i) {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         m_statistics.wipe_segments_count += wipe_buffer_indices[i].size() / m_buffers[wipe_buffer_id].indices_per_segment();
-//        m_statistics.wipe_segments_count = wipe_buffer_indices[i].size() / m_buffers[wipe_buffer_id].indices_per_segment();
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     unsigned int extrude_buffer_id = buffer_id(EMoveType::Extrude);
     const MultiIndexBuffer& extrude_buffer_indices = indices[extrude_buffer_id];
     for (size_t i = 0; i < extrude_buffer_indices.size(); ++i) {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         m_statistics.extrude_segments_count += extrude_buffer_indices[i].size() / m_buffers[extrude_buffer_id].indices_per_segment();
-//        m_statistics.extrude_segments_count = extrude_buffer_indices[i].size() / m_buffers[extrude_buffer_id].indices_per_segment();
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 
@@ -2743,33 +2648,36 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
     for (const TBuffer& buffer : m_buffers) {
         // searches the path containing the current position
         for (const Path& path : buffer.paths) {
-            int sub_path_id = path.get_id_of_sub_path_containing(m_sequential_view.current.last);
-            if (sub_path_id != -1) {
-                const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id];
-                unsigned int offset = static_cast<unsigned int>(m_sequential_view.current.last - sub_path.first.s_id);
-                if (offset > 0) {
-                    if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Line)
-                        offset = 2 * offset - 1;
-                    else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
-                        unsigned int indices_count = buffer.indices_per_segment();
-                        offset = indices_count * (offset - 1) + (indices_count - 6);
+            if (path.contains(m_sequential_view.current.last)) {
+                int sub_path_id = path.get_id_of_sub_path_containing(m_sequential_view.current.last);
+                if (sub_path_id != -1) {
+                    const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id];
+                    unsigned int offset = static_cast<unsigned int>(m_sequential_view.current.last - sub_path.first.s_id);
+                    if (offset > 0) {
+                        if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Line)
+                            offset = 2 * offset - 1;
+                        else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
+                            unsigned int indices_count = buffer.indices_per_segment();
+                            offset = indices_count * (offset - 1) + (indices_count - 6);
+                        }
                     }
+                    offset += static_cast<unsigned int>(sub_path.first.i_id);
+
+                    // gets the vertex index from the index buffer on gpu
+                    const IBuffer& i_buffer = buffer.indices[sub_path.first.b_id];
+                    unsigned int index = 0;
+                    glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
+                    glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(offset * sizeof(unsigned int)), static_cast<GLsizeiptr>(sizeof(unsigned int)), static_cast<void*>(&index)));
+                    glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+
+                    // gets the position from the vertices buffer on gpu
+                    glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo));
+                    glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(index * buffer.vertices.vertex_size_bytes()), static_cast<GLsizeiptr>(3 * sizeof(float)), static_cast<void*>(m_sequential_view.current_position.data())));
+                    glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+
+                    found = true;
+                    break;
                 }
-                offset += static_cast<unsigned int>(sub_path.first.i_id);
-
-                // gets the index from the index buffer on gpu
-                const IBuffer& i_buffer = buffer.indices[sub_path.first.b_id];
-                unsigned int index = 0;
-                glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
-                glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(offset * sizeof(unsigned int)), static_cast<GLsizeiptr>(sizeof(unsigned int)), static_cast<void*>(&index)));
-                glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
-
-                // gets the position from the vertices buffer on gpu
-                glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo));
-                glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(index * buffer.vertices.vertex_size_bytes()), static_cast<GLsizeiptr>(3 * sizeof(float)), static_cast<void*>(m_sequential_view.current_position.data())));
-                glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
-                found = true;
-                break;
             }
         }
 
@@ -2779,7 +2687,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
 
     // second pass: filter paths by sequential data and collect them by color
     RenderPath* render_path = nullptr;
-    for (const auto& [buffer, index_buffer_id, path_id, sub_path_id] : paths) {
+    for (const auto& [buffer, ibuffer_id, path_id, sub_path_id] : paths) {
         const Path& path = buffer->paths[path_id];
         const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id];
         if (m_sequential_view.current.last <= sub_path.first.s_id || sub_path.last.s_id <= m_sequential_view.current.first)
@@ -2810,17 +2718,24 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
         default: { color = { 0.0f, 0.0f, 0.0f }; break; }
         }
 
-        RenderPath key{ color, static_cast<unsigned int>(index_buffer_id), path_id };
+        RenderPath key{ color, static_cast<unsigned int>(ibuffer_id), path_id };
         if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key))
             render_path = const_cast<RenderPath*>(&(*buffer->render_paths.emplace(key).first));
-        unsigned int segments_count = std::min(m_sequential_view.current.last, sub_path.last.s_id) - std::max(m_sequential_view.current.first, sub_path.first.s_id) + 1;
         unsigned int size_in_indices = 0;
         switch (buffer->render_primitive_type)
         {
-        case TBuffer::ERenderPrimitiveType::Point:    { size_in_indices = segments_count; break; }
-        case TBuffer::ERenderPrimitiveType::Line:
-        case TBuffer::ERenderPrimitiveType::Triangle: { size_in_indices = buffer->indices_per_segment() * (segments_count - 1); break; }
+        case TBuffer::ERenderPrimitiveType::Point: {
+            size_in_indices = buffer->indices_per_segment();
+            break;
         }
+        case TBuffer::ERenderPrimitiveType::Line:
+        case TBuffer::ERenderPrimitiveType::Triangle: {
+            unsigned int segments_count = std::min(m_sequential_view.current.last, sub_path.last.s_id) - std::max(m_sequential_view.current.first, sub_path.first.s_id);
+            size_in_indices = buffer->indices_per_segment() * segments_count;
+            break;
+        }
+        }
+
         render_path->sizes.push_back(size_in_indices);
 
         unsigned int delta_1st = 0;
@@ -4202,11 +4117,6 @@ void GCodeViewer::render_statistics() const
         ImGui::Separator();
         add_counter(std::string("VBuffers count:"), m_statistics.vbuffers_count);
         add_counter(std::string("IBuffers count:"), m_statistics.ibuffers_count);
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-//        ImGui::Separator();
-//        add_counter(std::string("Max vertices in VBuffer:"), m_statistics.max_vertices_in_vertex_buffer);
-//        add_counter(std::string("Max indices in IBuffer:"), m_statistics.max_indices_in_index_buffer);
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
         wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
     }
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index 51fd560a3..0aecf7eba 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -146,7 +146,7 @@ class GCodeViewer
             unsigned int b_id{ 0 };
             // index into the index buffer
             size_t i_id{ 0 };
-            // sequential id (index into the vertex buffer)
+            // move id
             size_t s_id{ 0 };
             Vec3f position{ Vec3f::Zero() };
         };
@@ -314,18 +314,7 @@ class GCodeViewer
         size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(unsigned int)); }
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
 //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-        unsigned int start_segment_vertex_offset() const {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-            return 0;
-//            switch (render_primitive_type)
-//            {
-//            case ERenderPrimitiveType::Point:
-//            case ERenderPrimitiveType::Line: 
-//            case ERenderPrimitiveType::Triangle:
-//            default: { return 0; }
-//            }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-        }
+        unsigned int start_segment_vertex_offset() const { return 0; }
         unsigned int end_segment_vertex_offset() const {
             switch (render_primitive_type)
             {
@@ -493,10 +482,6 @@ class GCodeViewer
         int64_t extrude_segments_count{ 0 };
         int64_t vbuffers_count{ 0 };
         int64_t ibuffers_count{ 0 };
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-//        int64_t max_vertices_in_vertex_buffer{ 0 };
-//        int64_t max_indices_in_index_buffer{ 0 };
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
         void reset_all() {
             reset_times();
@@ -534,10 +519,6 @@ class GCodeViewer
             extrude_segments_count =  0;
             vbuffers_count = 0;
             ibuffers_count = 0;
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-//            max_vertices_in_vertex_buffer = 0;
-//            max_indices_in_index_buffer = 0;
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         }
     };
 #endif // ENABLE_GCODE_VIEWER_STATISTICS

From dc781535b2067a019d2bd72603fcef4acdbd02d9 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 12 Jan 2021 14:34:52 +0100
Subject: [PATCH 08/20] Code cleanup

---
 src/libslic3r/Technologies.hpp |  3 +--
 src/slic3r/GUI/GCodeViewer.cpp | 44 +---------------------------------
 src/slic3r/GUI/GCodeViewer.hpp | 35 +--------------------------
 3 files changed, 3 insertions(+), 79 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index eabd711d5..010f10972 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -108,13 +108,12 @@
 #define ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING (1 && ENABLE_2_3_0_RC1)
 
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 //=======
 // 2.3.1 
 //=======
 #define ENABLE_2_3_1 1
 
 #define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1)
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
 
 #endif // _prusaslicer_technologies_h_
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 64d064a33..c67e1c7a2 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -79,7 +79,6 @@ static float round_to_nearest(float value, unsigned int decimals)
     return res;
 }
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 void GCodeViewer::VBuffer::reset()
 {
@@ -92,7 +91,6 @@ void GCodeViewer::VBuffer::reset()
     count = 0;
 }
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 void GCodeViewer::VBuffer::reset()
 {
     // release gpu memory
@@ -103,14 +101,10 @@ void GCodeViewer::VBuffer::reset()
 
     count = 0;
 }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
 void GCodeViewer::IBuffer::reset()
 {
-    // release gpu memory
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
     // release gpu memory
     if (ibo > 0) {
@@ -118,20 +112,16 @@ void GCodeViewer::IBuffer::reset()
         ibo = 0;
     }
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+    // release gpu memory
     if (id > 0) {
         glsafe(::glDeleteBuffers(1, &id));
         id = 0;
     }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
     vbo = 0;
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     count = 0;
 }
 
@@ -153,7 +143,6 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
     case EMoveType::Unretract:
     case EMoveType::Extrude: {
         // use rounding to reduce the number of generated paths
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 #if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
         return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
@@ -167,7 +156,6 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
             cp_color_id == move.cp_color_id;
 #endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
         return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
             move.position[2] <= first.position[2] && feedrate == move.feedrate && fan_speed == move.fan_speed &&
@@ -179,9 +167,7 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
             volumetric_rate == round_to_nearest(move.volumetric_rate(), 2) && extruder_id == move.extruder_id &&
             cp_color_id == move.cp_color_id;
 #endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
     case EMoveType::Travel: {
         return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id;
@@ -208,7 +194,6 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi
 {
     Path::Endpoint endpoint = { b_id, i_id, s_id, move.position };
     // use rounding to reduce the number of generated paths
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 #if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
     paths.push_back({ move.type, move.extrusion_role, move.delta_extruder,
@@ -220,7 +205,6 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi
         round_to_nearest(move.volumetric_rate(), 2), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } });
 #endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
     paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder,
         round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed,
@@ -230,9 +214,7 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi
         round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed,
         round_to_nearest(move.volumetric_rate(), 2), move.extruder_id, move.cp_color_id });
 #endif // ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 }
 
 GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) const
@@ -657,15 +639,11 @@ void GCodeViewer::update_sequential_view_current(unsigned int first, unsigned in
         for (const TBuffer& buffer : m_buffers) {
             if (buffer.visible) {
                 for (const Path& path : buffer.paths) {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
                     if (path.sub_paths.front().first.s_id <= id && id <= path.sub_paths.back().last.s_id)
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                     if (path.first.s_id <= id && id <= path.last.s_id)
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                     return true;
                 }
             }
@@ -769,9 +747,7 @@ void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z
 
 void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if !ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
     if (filename == nullptr)
         return;
@@ -1068,12 +1044,9 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 
     fclose(fp);
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // !ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 }
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
@@ -1777,7 +1750,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         progress_dialog->Destroy();
 }
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
 #if ENABLE_GCODE_VIEWER_STATISTICS
@@ -2437,9 +2409,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     if (progress_dialog != nullptr)
         progress_dialog->Destroy();
 }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
 void GCodeViewer::load_shells(const Print& print, bool initialized)
 {
@@ -2499,7 +2469,6 @@ void GCodeViewer::load_shells(const Print& print, bool initialized)
     }
 }
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const
 {
@@ -2766,7 +2735,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 }
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const
 {
 #if ENABLE_GCODE_VIEWER_STATISTICS
@@ -3014,11 +2982,8 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
     m_statistics.refresh_paths_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time).count();
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 void GCodeViewer::render_toolpaths() const
 {
@@ -3166,7 +3131,6 @@ void GCodeViewer::render_toolpaths() const
     }
 }
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 void GCodeViewer::render_toolpaths() const
 {
 #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
@@ -3313,9 +3277,7 @@ void GCodeViewer::render_toolpaths() const
         }
     }
 }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
 void GCodeViewer::render_shells() const
 {
@@ -4140,19 +4102,15 @@ void GCodeViewer::log_memory_used(const std::string& label, int64_t additional)
         }
         int64_t layers_size = SLIC3R_STDVEC_MEMSIZE(m_layers.get_zs(), double);
         layers_size += SLIC3R_STDVEC_MEMSIZE(m_layers.get_endpoints(), Layers::Endpoints);
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         BOOST_LOG_TRIVIAL(trace) << label
             << "(" << format_memsize_MB(additional + paths_size + render_paths_size + layers_size) << ");"
             << log_memory_info();
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         BOOST_LOG_TRIVIAL(trace) << label
             << format_memsize_MB(additional + paths_size + render_paths_size + layers_size)
             << log_memory_info();
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     }
 }
 
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index 0aecf7eba..a334f08e0 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -21,11 +21,9 @@ class GCodeViewer
 {
     using Color = std::array<float, 3>;
     using VertexBuffer = std::vector<float>;
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
     using MultiVertexBuffer = std::vector<VertexBuffer>;
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     using IndexBuffer = std::vector<unsigned int>;
     using MultiIndexBuffer = std::vector<IndexBuffer>;
 
@@ -59,17 +57,13 @@ class GCodeViewer
         };
 
         EFormat format{ EFormat::Position };
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         // vbos id
         std::vector<unsigned int> ids;
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         // vbo id
         unsigned int id{ 0 };
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         // count of vertices, updated after data are sent to gpu
         size_t count{ 0 };
 
@@ -118,19 +112,15 @@ class GCodeViewer
     // ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type
     struct IBuffer
     {
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         // id of the associated vertex buffer
         unsigned int vbo{ 0 };
         // ibo id
         unsigned int ibo{ 0 };
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         // ibo id
         unsigned int id{ 0 };
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         // count of indices, updated after data are sent to gpu
         size_t count{ 0 };
 
@@ -151,7 +141,6 @@ class GCodeViewer
             Vec3f position{ Vec3f::Zero() };
         };
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         struct Sub_Path
         {
@@ -163,18 +152,13 @@ class GCodeViewer
             }
         };
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
         EMoveType type{ EMoveType::Noop };
         ExtrusionRole role{ erNone };
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if !ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         Endpoint first;
         Endpoint last;
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // !ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         float delta_extruder{ 0.0f };
         float height{ 0.0f };
         float width{ 0.0f };
@@ -183,14 +167,11 @@ class GCodeViewer
         float volumetric_rate{ 0.0f };
         unsigned char extruder_id{ 0 };
         unsigned char cp_color_id{ 0 };
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         std::vector<Sub_Path> sub_paths;
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
         bool matches(const GCodeProcessor::MoveVertex& move) const;
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         size_t vertices_count() const {
             return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
@@ -214,12 +195,9 @@ class GCodeViewer
             sub_paths.push_back({ endpoint , endpoint });
         }
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         size_t vertices_count() const { return last.s_id - first.s_id + 1; }
         bool contains(size_t id) const { return first.s_id <= id && id <= last.s_id; }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     };
 
     // Used to batch the indices needed to render the paths
@@ -284,7 +262,6 @@ class GCodeViewer
         // s_id index of first vertex contained in this->vertices
         void add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         unsigned int max_vertices_per_segment() const {
             switch (render_primitive_type)
@@ -299,7 +276,6 @@ class GCodeViewer
         size_t max_vertices_per_segment_size_floats() const { return vertices.vertex_size_floats() * static_cast<size_t>(max_vertices_per_segment()); }
         size_t max_vertices_per_segment_size_bytes() const { return max_vertices_per_segment_size_floats() * sizeof(float); }
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         unsigned int indices_per_segment() const {
             switch (render_primitive_type)
             {
@@ -309,11 +285,9 @@ class GCodeViewer
             default:                             { return 0; }
             }
         }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(unsigned int)); }
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         unsigned int start_segment_vertex_offset() const { return 0; }
         unsigned int end_segment_vertex_offset() const {
             switch (render_primitive_type)
@@ -325,17 +299,13 @@ class GCodeViewer
             }
         }
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         bool has_data() const {
             return !vertices.ids.empty() && vertices.ids.front() != 0 && !indices.empty() && indices.front().ibo != 0;
         }
 #else
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         bool has_data() const { return vertices.id != 0 && !indices.empty() && indices.front().id != 0; }
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     };
 
     // helper to render shells
@@ -410,13 +380,12 @@ class GCodeViewer
         {
             size_t first{ 0 };
             size_t last{ 0 };
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
 #if ENABLE_SPLITTED_VERTEX_BUFFER
             bool operator == (const Endpoints& other) const {
                 return first == other.first && last == other.last;
             }
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         };
 
     private:
@@ -442,7 +411,6 @@ class GCodeViewer
         double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; }
         Endpoints get_endpoints_at(unsigned int id) const { return (id < m_endpoints.size()) ? m_endpoints[id] : Endpoints(); }
 
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         bool operator != (const Layers& other) const {
             if (m_zs != other.m_zs)
@@ -453,7 +421,6 @@ class GCodeViewer
             return false;
         }
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
-//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     };
 
 #if ENABLE_GCODE_VIEWER_STATISTICS

From 67e144f32910c59a499d6671e292ee298cba4291 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Wed, 13 Jan 2021 10:45:52 +0100
Subject: [PATCH 09/20] ENABLE_SPLITTED_VERTEX_BUFFER - fixed export of
 toolpaths to obj files

---
 src/slic3r/GUI/GCodeViewer.cpp | 119 ++++++++++++++++++++++++++++-----
 src/slic3r/GUI/GCodeViewer.hpp |   9 ++-
 src/slic3r/GUI/GLCanvas3D.cpp  |   4 ++
 3 files changed, 114 insertions(+), 18 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index c67e1c7a2..882ac97ea 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -83,11 +83,11 @@ static float round_to_nearest(float value, unsigned int decimals)
 void GCodeViewer::VBuffer::reset()
 {
     // release gpu memory
-    if (!ids.empty()) {
-        glsafe(::glDeleteBuffers(static_cast<GLsizei>(ids.size()), static_cast<const GLuint*>(ids.data())));
-        ids.clear();
+    if (!vbos.empty()) {
+        glsafe(::glDeleteBuffers(static_cast<GLsizei>(vbos.size()), static_cast<const GLuint*>(vbos.data())));
+        vbos.clear();
     }
-
+    sizes.clear();
     count = 0;
 }
 #else
@@ -358,7 +358,8 @@ const std::vector<GCodeViewer::Color> GCodeViewer::Range_Colors {{
 
 GCodeViewer::GCodeViewer()
 {
-    // initializes non opengl data of TBuffers
+    // initializes non OpenGL data of TBuffers
+    // OpenGL data are initialized into render().init_gl_data()
     for (size_t i = 0; i < m_buffers.size(); ++i) {
         TBuffer& buffer = m_buffers[i];
         switch (buffer_type(i))
@@ -633,6 +634,13 @@ void GCodeViewer::render() const
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 }
 
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+bool GCodeViewer::can_export_toolpaths() const
+{
+    return has_data() && m_buffers[buffer_id(EMoveType::Extrude)].render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle;
+}
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+
 void GCodeViewer::update_sequential_view_current(unsigned int first, unsigned int last)
 {
     auto is_visible = [this](unsigned int id) {
@@ -747,8 +755,6 @@ void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z
 
 void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 {
-#if !ENABLE_SPLITTED_VERTEX_BUFFER
-
     if (filename == nullptr)
         return;
 
@@ -758,15 +764,24 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     wxBusyCursor busy;
 
     // the data needed is contained into the Extrude TBuffer
-    const TBuffer& buffer = m_buffers[buffer_id(EMoveType::Extrude)];
-    if (!buffer.has_data())
+    const TBuffer& t_buffer = m_buffers[buffer_id(EMoveType::Extrude)];
+    if (!t_buffer.has_data())
         return;
 
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+    if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Triangle)
+        return;
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
+
     // collect color information to generate materials
     std::vector<Color> colors;
-    for (const RenderPath& path : buffer.render_paths) {
+    for (const RenderPath& path : t_buffer.render_paths) {
         colors.push_back(path.color);
     }
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+    std::sort(colors.begin(), colors.end());
+    colors.erase(std::unique(colors.begin(), colors.end()), colors.end());
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
 
     // save materials file
     boost::filesystem::path mat_filename(filename);
@@ -801,6 +816,78 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID);
     fprintf(fp, "\nmtllib ./%s\n", mat_filename.filename().string().c_str());
 
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+    const size_t floats_per_vertex = t_buffer.vertices.vertex_size_floats();
+
+    std::vector<Vec3f> out_vertices;
+    std::vector<Vec3f> out_normals;
+    std::vector<size_t> vertices_offsets;
+    vertices_offsets.push_back(0);
+
+    // get vertices/normals data from vertex buffers on gpu
+    for (size_t i = 0; i < t_buffer.vertices.vbos.size(); ++i) {
+        const size_t floats_count = t_buffer.vertices.sizes[i] / sizeof(float);
+        VertexBuffer vertices(floats_count);
+        glsafe(::glBindBuffer(GL_ARRAY_BUFFER, t_buffer.vertices.vbos[i]));
+        glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(t_buffer.vertices.sizes[i]), static_cast<void*>(vertices.data())));
+        glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+        const size_t vertices_count = floats_count / floats_per_vertex;
+        for (size_t j = 0; j < vertices_count; ++j) {
+            const size_t base = j * floats_per_vertex;
+            out_vertices.push_back({ vertices[base + 0], vertices[base + 1], vertices[base + 2] });
+            out_normals.push_back({ vertices[base + 3], vertices[base + 4], vertices[base + 5] });
+        }
+        vertices_offsets.push_back(vertices_offsets.back() + vertices_count);
+    }
+
+    // save vertices to file
+    fprintf(fp, "\n# vertices\n");
+    for (const Vec3f& v : out_vertices) {
+        fprintf(fp, "v %g %g %g\n", v[0], v[1], v[2]);
+    }
+
+    // save normals to file
+    fprintf(fp, "\n# normals\n");
+    for (const Vec3f& n : out_normals) {
+        fprintf(fp, "vn %g %g %g\n", n[0], n[1], n[2]);
+    }
+
+    size_t i = 0;
+    for (const Color& color : colors) {
+        // save material triangles to file
+        fprintf(fp, "\nusemtl material_%zu\n", i + 1);
+        fprintf(fp, "# triangles material %zu\n", i + 1);
+
+        for (const RenderPath& render_path : t_buffer.render_paths) {
+            if (render_path.color != color)
+                continue;
+
+            const IBuffer& ibuffer = t_buffer.indices[render_path.index_buffer_id];
+            const size_t vertices_offset = vertices_offsets[render_path.index_buffer_id];
+
+            // get indices data from index buffer on gpu
+            glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer.ibo));
+            for (size_t j = 0; j < render_path.sizes.size(); ++j) {
+                const size_t triangles_count = render_path.sizes[j] / 3;
+                IndexBuffer indices(render_path.sizes[j]);
+                glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(render_path.offsets[j]),
+                    static_cast<GLsizeiptr>(render_path.sizes[j] * sizeof(unsigned int)), static_cast<void*>(indices.data())));
+
+                for (size_t k = 0; k < triangles_count; ++k) {
+                    const size_t base = k * 3;
+                    const size_t v1 = 1 + static_cast<size_t>(indices[base + 0]) + vertices_offset;
+                    const size_t v2 = 1 + static_cast<size_t>(indices[base + 1]) + vertices_offset;
+                    const size_t v3 = 1 + static_cast<size_t>(indices[base + 2]) + vertices_offset;
+                    if (v1 != v2)
+                        // do not export dummy triangles
+                        fprintf(fp, "f %zu//%zu %zu//%zu %zu//%zu\n", v1, v1, v2, v2, v3, v3);
+                }
+            }
+            glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+        }
+        ++i;
+    }
+#else
     // get vertices data from vertex buffer on gpu
     size_t floats_per_vertex = buffer.vertices.vertex_size_floats();
     VertexBuffer vertices = VertexBuffer(buffer.vertices.count * floats_per_vertex);
@@ -1041,10 +1128,9 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 
         ++ i;
     }
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
 
     fclose(fp);
-
-#endif // !ENABLE_SPLITTED_VERTEX_BUFFER
 }
 
 #if ENABLE_SPLITTED_VERTEX_BUFFER
@@ -1525,7 +1611,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
             GLuint id = 0;
             glsafe(::glGenBuffers(1, &id));
-            t_buffer.vertices.ids.push_back(static_cast<unsigned int>(id));
+            t_buffer.vertices.vbos.push_back(static_cast<unsigned int>(id));
+            t_buffer.vertices.sizes.push_back(size_bytes);
             glsafe(::glBindBuffer(GL_ARRAY_BUFFER, id));
             glsafe(::glBufferData(GL_ARRAY_BUFFER, size_bytes, v_buffer.data(), GL_STATIC_DRAW));
             glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
@@ -1581,14 +1668,14 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         // ensure there is at least one index buffer
         if (i_multibuffer.empty()) {
             i_multibuffer.push_back(IndexBuffer());
-            vbo_index_list.push_back(t_buffer.vertices.ids[curr_vertex_buffer.first]);
+            vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
         }
 
         // if adding the indices for the current segment exceeds the threshold size of the current index buffer
         // create another index buffer
         if (i_multibuffer.back().size() * sizeof(unsigned int) >= IBUFFER_THRESHOLD_BYTES - t_buffer.indices_per_segment_size_bytes()) {
             i_multibuffer.push_back(IndexBuffer());
-            vbo_index_list.push_back(t_buffer.vertices.ids[curr_vertex_buffer.first]);
+            vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
         }
 
         // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
@@ -1598,7 +1685,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
             ++curr_vertex_buffer.first;
             curr_vertex_buffer.second = 0;
-            vbo_index_list.push_back(t_buffer.vertices.ids[curr_vertex_buffer.first]);
+            vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
 
             if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
                 Path& last_path = t_buffer.paths.back();
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index a334f08e0..6602bbcf8 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -59,7 +59,9 @@ class GCodeViewer
         EFormat format{ EFormat::Position };
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         // vbos id
-        std::vector<unsigned int> ids;
+        std::vector<unsigned int> vbos;
+        // sizes of the buffers, in bytes, used in export to obj
+        std::vector<size_t> sizes;
 #else
         // vbo id
         unsigned int id{ 0 };
@@ -301,7 +303,7 @@ class GCodeViewer
 
 #if ENABLE_SPLITTED_VERTEX_BUFFER
         bool has_data() const {
-            return !vertices.ids.empty() && vertices.ids.front() != 0 && !indices.empty() && indices.front().ibo != 0;
+            return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
         }
 #else
         bool has_data() const { return vertices.id != 0 && !indices.empty() && indices.front().id != 0; }
@@ -588,6 +590,9 @@ public:
     void render() const;
 
     bool has_data() const { return !m_roles.empty(); }
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+    bool can_export_toolpaths() const;
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
 
     const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; }
     const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; }
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 03d6b94ce..388db4a27 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3866,7 +3866,11 @@ void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar()
 
 bool GLCanvas3D::has_toolpaths_to_export() const
 {
+#if ENABLE_SPLITTED_VERTEX_BUFFER
+    return m_gcode_viewer.can_export_toolpaths();
+#else
     return m_gcode_viewer.has_data();
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
 }
 
 void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const

From 8bdfb6bbce6fce6c08d180fb2ec25b57576b0d93 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Thu, 14 Jan 2021 11:38:03 +0100
Subject: [PATCH 10/20] ENABLE_SPLITTED_VERTEX_BUFFER - fixed bugs in export of
 toolpaths to obj files and in index buffer splitting

---
 src/slic3r/GUI/GCodeViewer.cpp | 43 ++++++++++++++++++++++++----------
 1 file changed, 30 insertions(+), 13 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 882ac97ea..b3794175e 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -793,13 +793,13 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     }
 
     fprintf(fp, "# G-Code Toolpaths Materials\n");
-    fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID);
+    fprintf(fp, "# Generated by %s-%s based on Slic3r\n", SLIC3R_APP_NAME, SLIC3R_VERSION);
 
     unsigned int colors_count = 1;
     for (const Color& color : colors) {
         fprintf(fp, "\nnewmtl material_%d\n", colors_count++);
         fprintf(fp, "Ka 1 1 1\n");
-        fprintf(fp, "Kd %f %f %f\n", color[0], color[1], color[2]);
+        fprintf(fp, "Kd %g %g %g\n", color[0], color[1], color[2]);
         fprintf(fp, "Ks 0 0 0\n");
     }
 
@@ -813,7 +813,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     }
 
     fprintf(fp, "# G-Code Toolpaths\n");
-    fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID);
+    fprintf(fp, "# Generated by %s-%s based on Slic3r\n", SLIC3R_APP_NAME, SLIC3R_VERSION);
     fprintf(fp, "\nmtllib ./%s\n", mat_filename.filename().string().c_str());
 
 #if ENABLE_SPLITTED_VERTEX_BUFFER
@@ -821,8 +821,14 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 
     std::vector<Vec3f> out_vertices;
     std::vector<Vec3f> out_normals;
-    std::vector<size_t> vertices_offsets;
-    vertices_offsets.push_back(0);
+
+    struct VerticesOffset
+    {
+        unsigned int vbo;
+        size_t offset;
+    };
+    std::vector<VerticesOffset> vertices_offsets;
+    vertices_offsets.push_back({ t_buffer.vertices.vbos.front(), 0 });
 
     // get vertices/normals data from vertex buffers on gpu
     for (size_t i = 0; i < t_buffer.vertices.vbos.size(); ++i) {
@@ -837,7 +843,9 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
             out_vertices.push_back({ vertices[base + 0], vertices[base + 1], vertices[base + 2] });
             out_normals.push_back({ vertices[base + 3], vertices[base + 4], vertices[base + 5] });
         }
-        vertices_offsets.push_back(vertices_offsets.back() + vertices_count);
+
+        if (i < t_buffer.vertices.vbos.size() - 1)
+            vertices_offsets.push_back({ t_buffer.vertices.vbos[i + 1], vertices_offsets.back().offset + vertices_count });
     }
 
     // save vertices to file
@@ -863,16 +871,23 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
                 continue;
 
             const IBuffer& ibuffer = t_buffer.indices[render_path.index_buffer_id];
-            const size_t vertices_offset = vertices_offsets[render_path.index_buffer_id];
+            size_t vertices_offset = 0;
+            for (size_t j = 0; j < vertices_offsets.size(); ++j) {
+                const VerticesOffset& offset = vertices_offsets[j];
+                if (offset.vbo == ibuffer.vbo) {
+                    vertices_offset = offset.offset;
+                    break;
+                }
+            }
 
             // get indices data from index buffer on gpu
             glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer.ibo));
             for (size_t j = 0; j < render_path.sizes.size(); ++j) {
-                const size_t triangles_count = render_path.sizes[j] / 3;
                 IndexBuffer indices(render_path.sizes[j]);
                 glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(render_path.offsets[j]),
                     static_cast<GLsizeiptr>(render_path.sizes[j] * sizeof(unsigned int)), static_cast<void*>(indices.data())));
 
+                const size_t triangles_count = render_path.sizes[j] / 3;
                 for (size_t k = 0; k < triangles_count; ++k) {
                     const size_t base = k * 3;
                     const size_t v1 = 1 + static_cast<size_t>(indices[base + 0]) + vertices_offset;
@@ -1676,6 +1691,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         if (i_multibuffer.back().size() * sizeof(unsigned int) >= IBUFFER_THRESHOLD_BYTES - t_buffer.indices_per_segment_size_bytes()) {
             i_multibuffer.push_back(IndexBuffer());
             vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
+            if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
+                Path& last_path = t_buffer.paths.back();
+                --last_path.sub_paths.back().last.s_id;
+                last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
+            }
         }
 
         // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
@@ -1689,6 +1709,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
             if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
                 Path& last_path = t_buffer.paths.back();
+                --last_path.sub_paths.back().last.s_id;
                 last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
             }
         }
@@ -1728,10 +1749,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             size_t size_elements = i_buffer.size();
             size_t size_bytes = size_elements * sizeof(unsigned int);
 
-            if (size_elements == 0) {
-                continue;
-            }
-
             // stores index buffer informations into TBuffer
             t_buffer.indices.push_back(IBuffer());
             IBuffer& ibuf = t_buffer.indices.back();
@@ -2777,6 +2794,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
         RenderPath key{ color, static_cast<unsigned int>(ibuffer_id), path_id };
         if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key))
             render_path = const_cast<RenderPath*>(&(*buffer->render_paths.emplace(key).first));
+
         unsigned int size_in_indices = 0;
         switch (buffer->render_primitive_type)
         {
@@ -2791,7 +2809,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
             break;
         }
         }
-
         render_path->sizes.push_back(size_in_indices);
 
         unsigned int delta_1st = 0;

From 8f1bc02287a2cd4e9f7046e372da952fdffb8985 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Mon, 18 Jan 2021 09:59:05 +0100
Subject: [PATCH 11/20] ENABLE_SPLITTED_VERTEX_BUFFER - fixed cracks in
 toolpaths when splitting vertex and index buffers

---
 src/libslic3r/Technologies.hpp |   1 +
 src/slic3r/GUI/GCodeViewer.cpp | 221 ++++++++++++++++++++++++++++++++-
 2 files changed, 219 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 010f10972..ad6de77ed 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -114,6 +114,7 @@
 #define ENABLE_2_3_1 1
 
 #define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1)
+#define ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
 
 
 #endif // _prusaslicer_technologies_h_
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index b3794175e..510aa2529 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1219,6 +1219,65 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     };
 
     // format data into the buffers to be rendered as solid
+#if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
+    auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) {
+        auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
+            // append position
+            vertices.push_back(position[0]);
+            vertices.push_back(position[1]);
+            vertices.push_back(position[2]);
+            // append normal
+            vertices.push_back(normal[0]);
+            vertices.push_back(normal[1]);
+            vertices.push_back(normal[2]);
+        };
+
+        if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
+            buffer.add_path(curr, vbuffer_id, vertices.size(), move_id - 1);
+            buffer.paths.back().sub_paths.back().first.position = prev.position;
+        }
+
+        Path& last_path = buffer.paths.back();
+
+        Vec3f dir = (curr.position - prev.position).normalized();
+        Vec3f right = Vec3f(dir[1], -dir[0], 0.0f).normalized();
+        Vec3f left = -right;
+        Vec3f up = right.cross(dir);
+        Vec3f down = -up;
+        float half_width = 0.5f * last_path.width;
+        float half_height = 0.5f * last_path.height;
+        Vec3f prev_pos = prev.position - half_height * up;
+        Vec3f curr_pos = curr.position - half_height * up;
+        Vec3f d_up = half_height * up;
+        Vec3f d_down = -half_height * up;
+        Vec3f d_right = half_width * right;
+        Vec3f d_left = -half_width * right;
+
+        // vertices 1st endpoint
+        if (last_path.vertices_count() == 1 || vertices.empty()) {
+            // 1st segment or restart into a new vertex buffer
+            // ===============================================
+            store_vertex(vertices, prev_pos + d_up, up);
+            store_vertex(vertices, prev_pos + d_right, right);
+            store_vertex(vertices, prev_pos + d_down, down);
+            store_vertex(vertices, prev_pos + d_left, left);
+        }
+        else {
+            // any other segment
+            // =================
+            store_vertex(vertices, prev_pos + d_right, right);
+            store_vertex(vertices, prev_pos + d_left, left);
+        }
+
+        // vertices 2nd endpoint
+        store_vertex(vertices, curr_pos + d_up, up);
+        store_vertex(vertices, curr_pos + d_right, right);
+        store_vertex(vertices, curr_pos + d_down, down);
+        store_vertex(vertices, curr_pos + d_left, left);
+
+        last_path.sub_paths.back().last = { vbuffer_id, vertices.size(), move_id, curr.position };
+    };
+#else
     auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, VertexBuffer& vertices, size_t move_id) {
         static Vec3f prev_dir;
         static Vec3f prev_up;
@@ -1293,7 +1352,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             }
 
             Vec3f displacement_vec = displacement * prev_dir;
-            bool can_displace = displacement > 0.0f && displacement < prev_length&& displacement < length;
+            bool can_displace = displacement > 0.0f && displacement < prev_length && displacement < length;
 
             size_t prev_right_id = (starting_vertices_size - 3) * buffer.vertices.vertex_size_floats();
             size_t prev_left_id = (starting_vertices_size - 1) * buffer.vertices.vertex_size_floats();
@@ -1366,6 +1425,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         prev_up = up;
         prev_length = length;
     };
+#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
     auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
         size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
             static Vec3f prev_dir;
@@ -1573,8 +1633,19 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
         // add another vertex buffer
+#if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
+        if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes()) {
+            v_multibuffer.push_back(VertexBuffer());
+            if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
+                Path& last_path = t_buffer.paths.back();
+                if (prev.type == curr.type && last_path.matches(curr))
+                    last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, i - 1);
+            }
+        }
+#else
         if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes())
             v_multibuffer.push_back(VertexBuffer());
+#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
 
         VertexBuffer& v_buffer = v_multibuffer.back();
 
@@ -1582,7 +1653,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         {
         case TBuffer::ERenderPrimitiveType::Point:    { add_vertices_as_point(curr, v_buffer); break; }
         case TBuffer::ERenderPrimitiveType::Line:     { add_vertices_as_line(prev, curr, v_buffer); break; }
+#if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
+        case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, i); break; }
+#else
         case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, v_buffer, i); break; }
+#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
         }
 
         // collect options zs for later use
@@ -1593,6 +1668,138 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         }
     }
 
+#if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
+    // smooth toolpaths corners for the given TBuffer using triangles
+    auto smooth_triangle_toolpaths_corners = [&gcode_result](const TBuffer& t_buffer, MultiVertexBuffer& v_multibuffer) {
+        auto extract_position_at = [](const VertexBuffer& vertices, size_t offset) {
+            return Vec3f(vertices[offset + 0], vertices[offset + 1], vertices[offset + 2]);
+        };
+        auto update_position_at = [](VertexBuffer& vertices, size_t offset, const Vec3f& position) {
+            vertices[offset + 0] = position[0];
+            vertices[offset + 1] = position[1];
+            vertices[offset + 2] = position[2];
+        };
+
+        size_t vertex_size_floats = t_buffer.vertices.vertex_size_floats();
+        for (const Path& path : t_buffer.paths) {
+            // the two segments of the path sharing the current vertex may belong
+            // to two different vertex buffers
+            size_t prev_sub_path_id = 0;
+            size_t next_sub_path_id = 0;
+            size_t path_vertices_count = path.vertices_count();
+            float half_width = 0.5f * path.width;
+            for (size_t j = 1; j < path_vertices_count - 1; ++j) {
+                size_t curr_s_id = path.sub_paths.front().first.s_id + j;
+                const Vec3f& prev = gcode_result.moves[curr_s_id - 1].position;
+                const Vec3f& curr = gcode_result.moves[curr_s_id].position;
+                const Vec3f& next = gcode_result.moves[curr_s_id + 1].position;
+
+                // select the subpaths which contains the previous/next segments
+                if (!path.sub_paths[prev_sub_path_id].contains(curr_s_id))
+                    ++prev_sub_path_id;
+                if (!path.sub_paths[next_sub_path_id].contains(curr_s_id + 1))
+                    ++next_sub_path_id;
+                const Path::Sub_Path& prev_sub_path = path.sub_paths[prev_sub_path_id];
+                const Path::Sub_Path& next_sub_path = path.sub_paths[next_sub_path_id];
+
+                Vec3f prev_dir = (curr - prev).normalized();
+                Vec3f prev_right = Vec3f(prev_dir[1], -prev_dir[0], 0.0f).normalized();
+                Vec3f prev_up = prev_right.cross(prev_dir);
+
+                Vec3f next_dir = (next - curr).normalized();
+                Vec3f next_right = Vec3f(next_dir[1], -next_dir[0], 0.0f).normalized();
+                Vec3f next_up = next_right.cross(next_dir);
+
+                bool is_right_turn = prev_up.dot(prev_dir.cross(next_dir)) <= 0.0f;
+                float cos_dir = prev_dir.dot(next_dir);
+                // whether the angle between adjacent segments is greater than 45 degrees
+                bool is_sharp = cos_dir < 0.7071068f;
+
+                float displacement = 0.0f;
+                if (cos_dir > -0.9998477f) {
+                    // if the angle between adjacent segments is smaller than 179 degrees
+                    Vec3f med_dir = (prev_dir + next_dir).normalized();
+                    displacement = half_width * ::tan(::acos(std::clamp(next_dir.dot(med_dir), -1.0f, 1.0f)));
+                }
+
+                float sq_prev_length = (curr - prev).squaredNorm();
+                float sq_next_length = (next - curr).squaredNorm();
+                float sq_displacement = sqr(displacement);
+                bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length&& sq_displacement < sq_next_length;
+
+                if (can_displace) {
+                    Vec3f displacement_vec = displacement * prev_dir;
+                    // matches inner corner vertices
+                    if (is_right_turn) {
+                        // offset into the vertex buffer of the right vertex of the previous segment 
+                        size_t prev_right_offset = prev_sub_path.last.i_id - ((prev_sub_path.last.s_id - curr_s_id) * 6 + 3) * vertex_size_floats;
+                        // new position of the right vertices
+                        Vec3f shared_right = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset) - displacement_vec;
+                        // update previous segment
+                        update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset, shared_right);
+                        // offset into the vertex buffer of the right vertex of the next segment
+                        size_t r_offset = (curr_s_id == next_sub_path.first.i_id) ? 1 : 0;
+                        size_t next_right_offset = next_sub_path.last.i_id - ((next_sub_path.last.s_id - curr_s_id) * 6 - r_offset) * vertex_size_floats;
+                        // update next segment
+                        update_position_at(v_multibuffer[next_sub_path.first.b_id], next_right_offset, shared_right);
+                    }
+                    else {
+                        // offset into the vertex buffer of the left vertex of the previous segment 
+                        size_t prev_left_offset = prev_sub_path.last.i_id - ((prev_sub_path.last.s_id - curr_s_id) * 6 + 1) * vertex_size_floats;
+                        // new position of the left vertices
+                        Vec3f shared_left = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset) - displacement_vec;
+                        // update previous segment
+                        update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset, shared_left);
+                        // offset into the vertex buffer of the left vertex of the next segment 
+                        size_t l_offset = (curr_s_id == next_sub_path.first.i_id) ? 3 : 1;
+                        size_t next_left_offset = next_sub_path.last.i_id - ((next_sub_path.last.s_id - curr_s_id) * 6 - l_offset) * vertex_size_floats;
+                        // update next segment
+                        update_position_at(v_multibuffer[next_sub_path.first.b_id], next_left_offset, shared_left);
+                    }
+
+                    if (!is_sharp) {
+                        // matches outer corner vertices
+                        if (is_right_turn) {
+                            // offset into the vertex buffer of the left vertex of the previous segment 
+                            size_t prev_left_offset = prev_sub_path.last.i_id - ((prev_sub_path.last.s_id - curr_s_id) * 6 + 1) * vertex_size_floats;
+                            // new position of the left vertices
+                            Vec3f shared_left = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset) + displacement_vec;
+                            // update previous segment
+                            update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset, shared_left);
+                            // offset into the vertex buffer of the left vertex of the next segment 
+                            size_t l_offset = (curr_s_id == next_sub_path.first.i_id) ? 3 : 1;
+                            size_t next_left_offset = next_sub_path.last.i_id - ((next_sub_path.last.s_id - curr_s_id) * 6 - l_offset) * vertex_size_floats;
+                            // update next segment
+                            update_position_at(v_multibuffer[next_sub_path.first.b_id], next_left_offset, shared_left);
+                        }
+                        else {
+                            // offset into the vertex buffer of the right vertex of the previous segment 
+                            size_t prev_right_offset = prev_sub_path.last.i_id - ((prev_sub_path.last.s_id - curr_s_id) * 6 + 3) * vertex_size_floats;
+                            // new position of the right vertices
+                            Vec3f shared_right = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset) + displacement_vec;
+                            // update previous segment
+                            update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset, shared_right);
+                            // offset into the vertex buffer of the right vertex of the next segment 
+                            size_t r_offset = (curr_s_id == next_sub_path.first.i_id) ? 1 : 0;
+                            size_t next_right_offset = next_sub_path.last.i_id - ((next_sub_path.last.s_id - curr_s_id) * 6 - r_offset) * vertex_size_floats;
+                            // update next segment
+                            update_position_at(v_multibuffer[next_sub_path.first.b_id], next_right_offset, shared_right);
+                        }
+                    }
+                }
+            }
+        }
+    };
+
+    // smooth toolpaths corners for TBuffers using triangles
+    for (size_t i = 0; i < m_buffers.size(); ++i) {
+        const TBuffer& t_buffer = m_buffers[i];
+        if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
+            smooth_triangle_toolpaths_corners(t_buffer, vertices[i]);
+        }
+    }
+#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
+
     for (MultiVertexBuffer& v_multibuffer : vertices) {
         for (VertexBuffer& v_buffer : v_multibuffer) {
             v_buffer.shrink_to_fit();
@@ -1693,7 +1900,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
             if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
                 Path& last_path = t_buffer.paths.back();
-                --last_path.sub_paths.back().last.s_id;
                 last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
             }
         }
@@ -1709,7 +1915,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
             if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
                 Path& last_path = t_buffer.paths.back();
-                --last_path.sub_paths.back().last.s_id;
                 last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
             }
         }
@@ -2819,6 +3024,16 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
             delta_1st *= buffer->indices_per_segment();
 
         render_path->offsets.push_back(static_cast<size_t>((sub_path.first.i_id + delta_1st) * sizeof(unsigned int)));
+
+#if 0
+        // check sizes and offsets against index buffer size on gpu
+        GLint buffer_size;
+        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->indices[render_path->index_buffer_id].ibo));
+        glsafe(::glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &buffer_size));
+        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+        if (render_path->offsets.back() + render_path->sizes.back() * sizeof(unsigned int) > buffer_size)
+            BOOST_LOG_TRIVIAL(error) << "GCodeViewer::refresh_render_paths: Invalid render path data";
+#endif 
     }
 
     // set sequential data to their final value

From 3a0aabb92455152b6f206106f7ffb9c5d38336bb Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Mon, 18 Jan 2021 11:37:23 +0100
Subject: [PATCH 12/20] ENABLE_SPLITTED_VERTEX_BUFFER - small refactoring in
 GCodeViewer::load_toolpaths()

---
 src/slic3r/GUI/GCodeViewer.cpp | 104 +++++++++++++++------------------
 src/slic3r/GUI/GCodeViewer.hpp |   7 ++-
 2 files changed, 51 insertions(+), 60 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 510aa2529..006572235 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1151,6 +1151,11 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
+    // max vertex buffer size, in bytes
+    static const size_t VBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
+    // max index buffer size, in bytes
+    static const size_t IBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
+
     auto log_memory_usage = [this](const std::string& label, const std::vector<MultiVertexBuffer>& vertices, const std::vector<MultiIndexBuffer>& indices) {
         int64_t vertices_size = 0;
         for (const MultiVertexBuffer& buffers : vertices) {
@@ -1601,9 +1606,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     std::vector<MultiIndexBuffer> indices(m_buffers.size());
     std::vector<float> options_zs;
 
-    // max vertex buffer size, in bytes
-    const size_t VBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
-
     // toolpaths data -> extract vertices from result
     for (size_t i = 0; i < m_moves_count; ++i) {
         const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
@@ -1679,6 +1681,38 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             vertices[offset + 1] = position[1];
             vertices[offset + 2] = position[2];
         };
+        auto match_right_vertices = [&](const Path::Sub_Path& prev_sub_path, const Path::Sub_Path& next_sub_path,
+            size_t curr_s_id, size_t vertex_size_floats, const Vec3f& displacement_vec) {
+                // offset into the vertex buffer of the next segment 1st vertex
+                size_t next_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats;
+                // offset into the vertex buffer of the right vertex of the previous segment 
+                size_t prev_right_offset = prev_sub_path.last.i_id - next_offset - 3 * vertex_size_floats;
+                // new position of the right vertices
+                Vec3f shared_vertex = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset) + displacement_vec;
+                // update previous segment
+                update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset, shared_vertex);
+                // offset into the vertex buffer of the right vertex of the next segment
+                size_t r_offset = (curr_s_id == next_sub_path.first.i_id) ? 1 : 0;
+                size_t next_right_offset = next_sub_path.last.i_id - next_offset + r_offset * vertex_size_floats;
+                // update next segment
+                update_position_at(v_multibuffer[next_sub_path.first.b_id], next_right_offset, shared_vertex);
+        };
+        auto match_left_vertices = [&](const Path::Sub_Path& prev_sub_path, const Path::Sub_Path& next_sub_path,
+            size_t curr_s_id, size_t vertex_size_floats, const Vec3f& displacement_vec) {
+                // offset into the vertex buffer of the next segment 1st vertex
+                size_t next_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats;
+                // offset into the vertex buffer of the left vertex of the previous segment 
+                size_t prev_left_offset = prev_sub_path.last.i_id - next_offset - 1 * vertex_size_floats;
+                // new position of the left vertices
+                Vec3f shared_vertex = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset) + displacement_vec;
+                // update previous segment
+                update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset, shared_vertex);
+                // offset into the vertex buffer of the left vertex of the next segment 
+                size_t l_offset = (curr_s_id == next_sub_path.first.i_id) ? 3 : 1;
+                size_t next_left_offset = next_sub_path.last.i_id - next_offset + l_offset * vertex_size_floats;
+                // update next segment
+                update_position_at(v_multibuffer[next_sub_path.first.b_id], next_left_offset, shared_vertex);
+        };
 
         size_t vertex_size_floats = t_buffer.vertices.vertex_size_floats();
         for (const Path& path : t_buffer.paths) {
@@ -1728,63 +1762,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length&& sq_displacement < sq_next_length;
 
                 if (can_displace) {
+                    // displacement to apply to the vertices to match
                     Vec3f displacement_vec = displacement * prev_dir;
                     // matches inner corner vertices
-                    if (is_right_turn) {
-                        // offset into the vertex buffer of the right vertex of the previous segment 
-                        size_t prev_right_offset = prev_sub_path.last.i_id - ((prev_sub_path.last.s_id - curr_s_id) * 6 + 3) * vertex_size_floats;
-                        // new position of the right vertices
-                        Vec3f shared_right = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset) - displacement_vec;
-                        // update previous segment
-                        update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset, shared_right);
-                        // offset into the vertex buffer of the right vertex of the next segment
-                        size_t r_offset = (curr_s_id == next_sub_path.first.i_id) ? 1 : 0;
-                        size_t next_right_offset = next_sub_path.last.i_id - ((next_sub_path.last.s_id - curr_s_id) * 6 - r_offset) * vertex_size_floats;
-                        // update next segment
-                        update_position_at(v_multibuffer[next_sub_path.first.b_id], next_right_offset, shared_right);
-                    }
-                    else {
-                        // offset into the vertex buffer of the left vertex of the previous segment 
-                        size_t prev_left_offset = prev_sub_path.last.i_id - ((prev_sub_path.last.s_id - curr_s_id) * 6 + 1) * vertex_size_floats;
-                        // new position of the left vertices
-                        Vec3f shared_left = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset) - displacement_vec;
-                        // update previous segment
-                        update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset, shared_left);
-                        // offset into the vertex buffer of the left vertex of the next segment 
-                        size_t l_offset = (curr_s_id == next_sub_path.first.i_id) ? 3 : 1;
-                        size_t next_left_offset = next_sub_path.last.i_id - ((next_sub_path.last.s_id - curr_s_id) * 6 - l_offset) * vertex_size_floats;
-                        // update next segment
-                        update_position_at(v_multibuffer[next_sub_path.first.b_id], next_left_offset, shared_left);
-                    }
+                    if (is_right_turn)
+                        match_right_vertices(prev_sub_path, next_sub_path, curr_s_id, vertex_size_floats, -displacement_vec);
+                    else
+                        match_left_vertices(prev_sub_path, next_sub_path, curr_s_id, vertex_size_floats, -displacement_vec);
 
                     if (!is_sharp) {
                         // matches outer corner vertices
-                        if (is_right_turn) {
-                            // offset into the vertex buffer of the left vertex of the previous segment 
-                            size_t prev_left_offset = prev_sub_path.last.i_id - ((prev_sub_path.last.s_id - curr_s_id) * 6 + 1) * vertex_size_floats;
-                            // new position of the left vertices
-                            Vec3f shared_left = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset) + displacement_vec;
-                            // update previous segment
-                            update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset, shared_left);
-                            // offset into the vertex buffer of the left vertex of the next segment 
-                            size_t l_offset = (curr_s_id == next_sub_path.first.i_id) ? 3 : 1;
-                            size_t next_left_offset = next_sub_path.last.i_id - ((next_sub_path.last.s_id - curr_s_id) * 6 - l_offset) * vertex_size_floats;
-                            // update next segment
-                            update_position_at(v_multibuffer[next_sub_path.first.b_id], next_left_offset, shared_left);
-                        }
-                        else {
-                            // offset into the vertex buffer of the right vertex of the previous segment 
-                            size_t prev_right_offset = prev_sub_path.last.i_id - ((prev_sub_path.last.s_id - curr_s_id) * 6 + 3) * vertex_size_floats;
-                            // new position of the right vertices
-                            Vec3f shared_right = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset) + displacement_vec;
-                            // update previous segment
-                            update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset, shared_right);
-                            // offset into the vertex buffer of the right vertex of the next segment 
-                            size_t r_offset = (curr_s_id == next_sub_path.first.i_id) ? 1 : 0;
-                            size_t next_right_offset = next_sub_path.last.i_id - ((next_sub_path.last.s_id - curr_s_id) * 6 - r_offset) * vertex_size_floats;
-                            // update next segment
-                            update_position_at(v_multibuffer[next_sub_path.first.b_id], next_right_offset, shared_right);
-                        }
+                        if (is_right_turn)
+                            match_left_vertices(prev_sub_path, next_sub_path, curr_s_id, vertex_size_floats, displacement_vec);
+                        else
+                            match_right_vertices(prev_sub_path, next_sub_path, curr_s_id, vertex_size_floats, displacement_vec);
                     }
                 }
             }
@@ -1853,9 +1844,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         buffer.paths.clear();
     }
 
-    // max index buffer size, in bytes
-    const size_t IBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
-
     // variable used to keep track of the current vertex buffers index and size
     using CurrVertexBuffer = std::pair<unsigned int, size_t>;
     std::vector<CurrVertexBuffer> curr_vertex_buffers(m_buffers.size(), { 0, 0 });
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index 6602bbcf8..f33a718dd 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -134,9 +134,12 @@ class GCodeViewer
     {
         struct Endpoint
         {
-            // index of the index buffer
+            // index of the buffer in the multibuffer vector
+            // the buffer type may change:
+            // it is the vertex buffer while extracting vertices data,
+            // the index buffer while extracting indices data
             unsigned int b_id{ 0 };
-            // index into the index buffer
+            // index into the buffer
             size_t i_id{ 0 };
             // move id
             size_t s_id{ 0 };

From 56d26ad491d93d6637deea1665c048ccb9bf1e20 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Mon, 18 Jan 2021 13:55:45 +0100
Subject: [PATCH 13/20] ENABLE_SPLITTED_VERTEX_BUFFER - slightly faster indices
 buffers generation in GCodeViewer::load_toolpaths()

---
 src/slic3r/GUI/GCodeViewer.cpp | 124 ++++++++++++++++++++++++++++++++-
 1 file changed, 123 insertions(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 006572235..61c34d25c 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1282,6 +1282,128 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         last_path.sub_paths.back().last = { vbuffer_id, vertices.size(), move_id, curr.position };
     };
+    auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
+        size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
+            static Vec3f prev_dir;
+            static Vec3f prev_up;
+            static float sq_prev_length;
+            auto store_triangle = [](IndexBuffer& indices, unsigned int i1, unsigned int i2, unsigned int i3) {
+                indices.push_back(i1);
+                indices.push_back(i2);
+                indices.push_back(i3);
+            };
+            auto append_dummy_cap = [store_triangle](IndexBuffer& indices, unsigned int id) {
+                store_triangle(indices, id, id, id);
+                store_triangle(indices, id, id, id);
+            };
+            auto store_main_triangles = [&](IndexBuffer& indices, size_t vbuffer_size, const std::array<int, 8>& v_offsets) {
+                std::array<unsigned int, 8> v_ids;
+                for (size_t i = 0; i < v_ids.size(); ++i) {
+                    v_ids[i] = static_cast<unsigned int>(static_cast<int>(vbuffer_size) + v_offsets[i]);
+                }
+
+                // triangles starting cap
+                store_triangle(indices, v_ids[0], v_ids[2], v_ids[1]);
+                store_triangle(indices, v_ids[0], v_ids[3], v_ids[2]);
+
+                // triangles sides
+                store_triangle(indices, v_ids[0], v_ids[1], v_ids[4]);
+                store_triangle(indices, v_ids[1], v_ids[5], v_ids[4]);
+                store_triangle(indices, v_ids[1], v_ids[2], v_ids[5]);
+                store_triangle(indices, v_ids[2], v_ids[6], v_ids[5]);
+                store_triangle(indices, v_ids[2], v_ids[3], v_ids[6]);
+                store_triangle(indices, v_ids[3], v_ids[7], v_ids[6]);
+                store_triangle(indices, v_ids[3], v_ids[0], v_ids[7]);
+                store_triangle(indices, v_ids[0], v_ids[4], v_ids[7]);
+
+                // triangles ending cap
+                store_triangle(indices, v_ids[4], v_ids[6], v_ids[7]);
+                store_triangle(indices, v_ids[4], v_ids[5], v_ids[6]);
+            };
+
+            if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
+                buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1);
+                buffer.paths.back().sub_paths.back().first.position = prev.position;
+            }
+
+            Path& last_path = buffer.paths.back();
+
+            Vec3f dir = (curr.position - prev.position).normalized();
+            Vec3f right = Vec3f(dir[1], -dir[0], 0.0f).normalized();
+            Vec3f up = right.cross(dir);
+            float sq_length = (curr.position - prev.position).squaredNorm();
+
+            if (last_path.vertices_count() == 1 || vbuffer_size == 0) {
+                // 1st segment or restart into a new vertex buffer
+                // ===============================================
+                // dummy triangles outer corner cap
+                append_dummy_cap(indices, vbuffer_size);
+
+                // stem triangles
+                store_main_triangles(indices, vbuffer_size, { 0, 1, 2, 3, 4, 5, 6, 7 });
+
+                vbuffer_size += 8;
+            }
+            else {
+                // any other segment
+                // =================
+                float displacement = 0.0f;
+                float cos_dir = prev_dir.dot(dir);
+                if (cos_dir > -0.9998477f) {
+                    // if the angle between adjacent segments is smaller than 179 degrees
+                    Vec3f med_dir = (prev_dir + dir).normalized();
+                    float half_width = 0.5f * last_path.width;
+                    displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f)));
+                }
+
+                float sq_displacement = sqr(displacement);
+                bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length && sq_displacement < sq_length;
+
+                bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f;
+                // whether the angle between adjacent segments is greater than 45 degrees
+                bool is_sharp = cos_dir < 0.7071068f;
+
+                bool right_displaced = false;
+                bool left_displaced = false;
+
+                if (!is_sharp && can_displace) {
+                    if (is_right_turn)
+                        left_displaced = true;
+                    else
+                        right_displaced = true;
+                }
+
+                // triangles outer corner cap
+                if (is_right_turn) {
+                    if (left_displaced)
+                        // dummy triangles
+                        append_dummy_cap(indices, vbuffer_size);
+                    else {
+                        store_triangle(indices, vbuffer_size - 4, vbuffer_size + 1, vbuffer_size - 1);
+                        store_triangle(indices, vbuffer_size + 1, vbuffer_size - 2, vbuffer_size - 1);
+                    }
+                }
+                else {
+                    if (right_displaced)
+                        // dummy triangles
+                        append_dummy_cap(indices, vbuffer_size);
+                    else {
+                        store_triangle(indices, vbuffer_size - 4, vbuffer_size - 3, vbuffer_size + 0);
+                        store_triangle(indices, vbuffer_size - 3, vbuffer_size - 2, vbuffer_size + 0);
+                    }
+                }
+
+                // stem triangles
+                store_main_triangles(indices, vbuffer_size, { -4, 0, -2, 1, 2, 3, 4, 5 });
+
+                vbuffer_size += 6;
+            }
+
+            last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position };
+            prev_dir = dir;
+            prev_up = up;
+            sq_prev_length = sq_length;
+    };
 #else
     auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, VertexBuffer& vertices, size_t move_id) {
         static Vec3f prev_dir;
@@ -1430,7 +1552,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         prev_up = up;
         prev_length = length;
     };
-#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
     auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
         size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
             static Vec3f prev_dir;
@@ -1568,6 +1689,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             prev_up = up;
             prev_length = length;
     };
+#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
 
 #if ENABLE_GCODE_VIEWER_STATISTICS
     auto start_time = std::chrono::high_resolution_clock::now();

From ee40ab421a3270b42dfadd08f47259a071177b93 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Wed, 20 Jan 2021 13:47:48 +0100
Subject: [PATCH 14/20] ENABLE_SPLITTED_VERTEX_BUFFER - Use unsigned short
 indices buffers to render toolpaths and temporary disable tech
 ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING which is causing artifacts

---
 src/libslic3r/Technologies.hpp |   3 +-
 src/slic3r/GUI/GCodeViewer.cpp | 107 +++++++++++++++++++++++++++++----
 src/slic3r/GUI/GCodeViewer.hpp |  16 +++++
 3 files changed, 113 insertions(+), 13 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index ad6de77ed..8552484c1 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -114,7 +114,8 @@
 #define ENABLE_2_3_1 1
 
 #define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1)
-#define ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
+#define ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING (0 && ENABLE_SPLITTED_VERTEX_BUFFER)
+#define ENABLE_UNSIGNED_SHORT_INDEX_BUFFER (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
 
 
 #endif // _prusaslicer_technologies_h_
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 61c34d25c..a0cdbcf05 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -884,8 +884,13 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
             glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer.ibo));
             for (size_t j = 0; j < render_path.sizes.size(); ++j) {
                 IndexBuffer indices(render_path.sizes[j]);
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+                glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(render_path.offsets[j]),
+                    static_cast<GLsizeiptr>(render_path.sizes[j] * sizeof(IBufferType)), static_cast<void*>(indices.data())));
+#else
                 glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(render_path.offsets[j]),
                     static_cast<GLsizeiptr>(render_path.sizes[j] * sizeof(unsigned int)), static_cast<void*>(indices.data())));
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
                 const size_t triangles_count = render_path.sizes[j] / 3;
                 for (size_t k = 0; k < triangles_count; ++k) {
@@ -904,17 +909,17 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     }
 #else
     // get vertices data from vertex buffer on gpu
-    size_t floats_per_vertex = buffer.vertices.vertex_size_floats();
-    VertexBuffer vertices = VertexBuffer(buffer.vertices.count * floats_per_vertex);
-    glsafe(::glBindBuffer(GL_ARRAY_BUFFER, buffer.vertices.id));
-    glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer.vertices.data_size_bytes(), vertices.data()));
+    size_t floats_per_vertex = t_buffer.vertices.vertex_size_floats();
+    VertexBuffer vertices = VertexBuffer(t_buffer.vertices.count * floats_per_vertex);
+    glsafe(::glBindBuffer(GL_ARRAY_BUFFER, t_buffer.vertices.id));
+    glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, t_buffer.vertices.data_size_bytes(), vertices.data()));
     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 
     // get indices data from index buffer on gpu
     MultiIndexBuffer indices;
-    for (size_t i = 0; i < buffer.indices.size(); ++i) {
-        indices.push_back(IndexBuffer(buffer.indices[i].count));
-        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.indices[i].id));
+    for (size_t i = 0; i < t_buffer.indices.size(); ++i) {
+        indices.push_back(IndexBuffer(t_buffer.indices[i].count));
+        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, t_buffer.indices[i].id));
         glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(indices.back().size() * sizeof(unsigned int)), indices.back().data()));
         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
     }
@@ -968,15 +973,15 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
     };
 
     size_t out_vertices_count = 0;
-    unsigned int indices_per_segment = buffer.indices_per_segment();
-    unsigned int start_vertex_offset = buffer.start_segment_vertex_offset();
-    unsigned int end_vertex_offset = buffer.end_segment_vertex_offset();
+    unsigned int indices_per_segment = t_buffer.indices_per_segment();
+    unsigned int start_vertex_offset = t_buffer.start_segment_vertex_offset();
+    unsigned int end_vertex_offset = t_buffer.end_segment_vertex_offset();
 
     size_t i = 0;
-    for (const RenderPath& render_path : buffer.render_paths) {
+    for (const RenderPath& render_path : t_buffer.render_paths) {
         // get paths segments from buffer paths
         const IndexBuffer& ibuffer = indices[render_path.index_buffer_id];
-        const Path& path = buffer.paths[render_path.path_id];
+        const Path& path = t_buffer.paths[render_path.path_id];
 
         float half_width = 0.5f * path.width;
         // clamp height to avoid artifacts due to z-fighting when importing the obj file into blender and similar
@@ -1151,8 +1156,10 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
+#if !ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
     // max vertex buffer size, in bytes
     static const size_t VBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
+#endif // !ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
     // max index buffer size, in bytes
     static const size_t IBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
 
@@ -1166,7 +1173,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         int64_t indices_size = 0;
         for (const MultiIndexBuffer& buffers : indices) {
             for (const IndexBuffer& buffer : buffers) {
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+                indices_size += SLIC3R_STDVEC_MEMSIZE(buffer, IBufferType);
+#else
                 indices_size += SLIC3R_STDVEC_MEMSIZE(buffer, unsigned int);
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             }
         }
         log_memory_used(label, vertices_size + indices_size);
@@ -1181,7 +1192,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
         unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
             buffer.add_path(curr, ibuffer_id, indices.size(), move_id);
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+            indices.push_back(static_cast<IBufferType>(indices.size()));
+#else
             indices.push_back(static_cast<unsigned int>(indices.size()));
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
     };
 
     // format data into the buffers to be rendered as lines
@@ -1287,20 +1302,35 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             static Vec3f prev_dir;
             static Vec3f prev_up;
             static float sq_prev_length;
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+            auto store_triangle = [](IndexBuffer& indices, IBufferType i1, IBufferType i2, IBufferType i3) {
+#else
             auto store_triangle = [](IndexBuffer& indices, unsigned int i1, unsigned int i2, unsigned int i3) {
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 indices.push_back(i1);
                 indices.push_back(i2);
                 indices.push_back(i3);
             };
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+            auto append_dummy_cap = [store_triangle](IndexBuffer& indices, IBufferType id) {
+#else
             auto append_dummy_cap = [store_triangle](IndexBuffer& indices, unsigned int id) {
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 store_triangle(indices, id, id, id);
                 store_triangle(indices, id, id, id);
             };
             auto store_main_triangles = [&](IndexBuffer& indices, size_t vbuffer_size, const std::array<int, 8>& v_offsets) {
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+                std::array<IBufferType, 8> v_ids;
+                for (size_t i = 0; i < v_ids.size(); ++i) {
+                    v_ids[i] = static_cast<IBufferType>(static_cast<int>(vbuffer_size) + v_offsets[i]);
+                }
+#else
                 std::array<unsigned int, 8> v_ids;
                 for (size_t i = 0; i < v_ids.size(); ++i) {
                     v_ids[i] = static_cast<unsigned int>(static_cast<int>(vbuffer_size) + v_offsets[i]);
                 }
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
                 // triangles starting cap
                 store_triangle(indices, v_ids[0], v_ids[2], v_ids[1]);
@@ -1557,7 +1587,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             static Vec3f prev_dir;
             static Vec3f prev_up;
             static float prev_length;
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+            auto store_triangle = [](IndexBuffer& indices, IBufferType i1, IBufferType i2, IBufferType i3) {
+#else
             auto store_triangle = [](IndexBuffer& indices, unsigned int i1, unsigned int i2, unsigned int i3) {
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 indices.push_back(i1);
                 indices.push_back(i2);
                 indices.push_back(i3);
@@ -1758,7 +1792,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
         // add another vertex buffer
 #if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+        if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
+            std::cout << "Splitted v buffer at " << i << "\n";
+#else
         if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes()) {
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             v_multibuffer.push_back(VertexBuffer());
             if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
                 Path& last_path = t_buffer.paths.back();
@@ -1766,9 +1805,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                     last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, i - 1);
             }
         }
+#else
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+        if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
+//            std::cout << "Splitted v buffer at " << i << "\n";
+            v_multibuffer.push_back(VertexBuffer());
+        }
 #else
         if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes())
             v_multibuffer.push_back(VertexBuffer());
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
 
         VertexBuffer& v_buffer = v_multibuffer.back();
@@ -2005,7 +2051,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         // if adding the indices for the current segment exceeds the threshold size of the current index buffer
         // create another index buffer
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+        if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - t_buffer.indices_per_segment_size_bytes()) {
+#else
         if (i_multibuffer.back().size() * sizeof(unsigned int) >= IBUFFER_THRESHOLD_BYTES - t_buffer.indices_per_segment_size_bytes()) {
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             i_multibuffer.push_back(IndexBuffer());
             vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
             if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
@@ -2016,7 +2066,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
         // create another index buffer
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+        if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
+//            std::cout << "Splitted i buffer at " << i << "\n";
+#else
         if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes()) {
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             i_multibuffer.push_back(IndexBuffer());
 
             ++curr_vertex_buffer.first;
@@ -2062,7 +2117,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         const MultiIndexBuffer& i_multibuffer = indices[i];
         for (const IndexBuffer& i_buffer : i_multibuffer) {
             size_t size_elements = i_buffer.size();
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+            size_t size_bytes = size_elements * sizeof(IBufferType);
+#else
             size_t size_bytes = size_elements * sizeof(unsigned int);
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
             // stores index buffer informations into TBuffer
             t_buffer.indices.push_back(IBuffer());
@@ -3055,7 +3114,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
                     const IBuffer& i_buffer = buffer.indices[sub_path.first.b_id];
                     unsigned int index = 0;
                     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+                    glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(offset * sizeof(IBufferType)), static_cast<GLsizeiptr>(sizeof(IBufferType)), static_cast<void*>(&index)));
+#else
                     glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(offset * sizeof(unsigned int)), static_cast<GLsizeiptr>(sizeof(unsigned int)), static_cast<void*>(&index)));
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 
                     // gets the position from the vertices buffer on gpu
@@ -3133,7 +3196,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
         if (buffer->render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle)
             delta_1st *= buffer->indices_per_segment();
 
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+        render_path->offsets.push_back(static_cast<size_t>((sub_path.first.i_id + delta_1st) * sizeof(IBufferType)));
+#else
         render_path->offsets.push_back(static_cast<size_t>((sub_path.first.i_id + delta_1st) * sizeof(unsigned int)));
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
 #if 0
         // check sizes and offsets against index buffer size on gpu
@@ -3141,7 +3208,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->indices[render_path->index_buffer_id].ibo));
         glsafe(::glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &buffer_size));
         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+        if (render_path->offsets.back() + render_path->sizes.back() * sizeof(IBufferType) > buffer_size)
+#else
         if (render_path->offsets.back() + render_path->sizes.back() * sizeof(unsigned int) > buffer_size)
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             BOOST_LOG_TRIVIAL(error) << "GCodeViewer::refresh_render_paths: Invalid render path data";
 #endif 
     }
@@ -3452,7 +3523,11 @@ void GCodeViewer::render_toolpaths() const
 
         for (const RenderPath& path : buffer.render_paths) {
             if (path.index_buffer_id == i_buffer_id) {
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+                glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#else
                 glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #if ENABLE_GCODE_VIEWER_STATISTICS
                 ++m_statistics.gl_multi_points_calls_count;
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -3468,7 +3543,11 @@ void GCodeViewer::render_toolpaths() const
         for (const RenderPath& path : buffer.render_paths) {
             if (path.index_buffer_id == index_buffer_id) {
                 set_uniform_color(path.color, shader);
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+                glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#else
                 glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #if ENABLE_GCODE_VIEWER_STATISTICS
                 ++m_statistics.gl_multi_lines_calls_count;
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -3480,7 +3559,11 @@ void GCodeViewer::render_toolpaths() const
         for (const RenderPath& path : buffer.render_paths) {
             if (path.index_buffer_id == index_buffer_id) {
                 set_uniform_color(path.color, shader);
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+                glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#else
                 glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #if ENABLE_GCODE_VIEWER_STATISTICS
                 ++m_statistics.gl_multi_triangles_calls_count;
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index f33a718dd..d80cc9dfc 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -19,12 +19,19 @@ namespace GUI {
 
 class GCodeViewer
 {
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+    using IBufferType = unsigned short;
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
     using Color = std::array<float, 3>;
     using VertexBuffer = std::vector<float>;
 #if ENABLE_SPLITTED_VERTEX_BUFFER
     using MultiVertexBuffer = std::vector<VertexBuffer>;
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+    using IndexBuffer = std::vector<IBufferType>;
+#else
     using IndexBuffer = std::vector<unsigned int>;
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
     using MultiIndexBuffer = std::vector<IndexBuffer>;
 
     static const std::vector<Color> Extrusion_Role_Colors;
@@ -70,6 +77,11 @@ class GCodeViewer
         size_t count{ 0 };
 
         size_t data_size_bytes() const { return count * vertex_size_bytes(); }
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+        // We set 65536 as max count of vertices inside a vertex buffer to allow
+        // to use unsigned short in place of unsigned int for indices in the index buffer, to save memory
+        size_t max_size_bytes() const { return 65536 * vertex_size_bytes(); }
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
         size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); }
         size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); }
@@ -291,7 +303,11 @@ class GCodeViewer
             }
         }
 #if ENABLE_SPLITTED_VERTEX_BUFFER
+#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+        size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(IBufferType)); }
+#else
         size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(unsigned int)); }
+#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
         unsigned int start_segment_vertex_offset() const { return 0; }
         unsigned int end_segment_vertex_offset() const {

From 56b378d766a1c05b6ac5439a4490f4accf4e9d45 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Thu, 21 Jan 2021 09:44:02 +0100
Subject: [PATCH 15/20] Fixed visualization of point toolpaths, broken with
 daa0bbdb0cfad984565bbd4a17940a65ef28650e

---
 src/slic3r/GUI/GCodeViewer.cpp | 34 +++++++++++++++-------------------
 src/slic3r/GUI/GCodeViewer.hpp |  1 +
 2 files changed, 16 insertions(+), 19 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index a0cdbcf05..150f65acf 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -3147,6 +3147,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
         Color color;
         switch (path.type)
         {
+        case EMoveType::Tool_change:  { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::ToolChanges)]; break; }
+        case EMoveType::Color_change: { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::ColorChanges)]; break; }
+        case EMoveType::Pause_Print:  { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::PausePrints)]; break; }
+        case EMoveType::Custom_GCode: { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::CustomGCodes)]; break; }
+        case EMoveType::Retract:      { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::Retractions)]; break; }
+        case EMoveType::Unretract:    { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::Unretractions)]; break; }
         case EMoveType::Extrude: {
             if (!top_layer_only ||
                 m_sequential_view.current.last == global_endpoints.last ||
@@ -3166,7 +3172,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
             break;
         }
         case EMoveType::Wipe: { color = Wipe_Color; break; }
-        default: { color = { 0.0f, 0.0f, 0.0f }; break; }
+        default:              { color = { 0.0f, 0.0f, 0.0f }; break; }
         }
 
         RenderPath key{ color, static_cast<unsigned int>(ibuffer_id), path_id };
@@ -3505,8 +3511,7 @@ void GCodeViewer::render_toolpaths() const
     };
 
     auto render_as_points = [this, zoom, point_size, near_plane_height, set_uniform_color]
-    (const TBuffer& buffer, unsigned int i_buffer_id, EOptionsColors color_id, GLShaderProgram& shader) {
-        set_uniform_color(Options_Colors[static_cast<unsigned int>(color_id)], shader);
+        (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) {
 #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
         shader.set_uniform("use_fixed_screen_size", 1);
 #else
@@ -3522,7 +3527,8 @@ void GCodeViewer::render_toolpaths() const
         glsafe(::glEnable(GL_POINT_SPRITE));
 
         for (const RenderPath& path : buffer.render_paths) {
-            if (path.index_buffer_id == i_buffer_id) {
+            if (path.index_buffer_id == ibuffer_id) {
+                set_uniform_color(path.color, shader);
 #if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
 #else
@@ -3538,10 +3544,10 @@ void GCodeViewer::render_toolpaths() const
         glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE));
     };
 
-    auto render_as_lines = [this, light_intensity, set_uniform_color](const TBuffer& buffer, unsigned int index_buffer_id, GLShaderProgram& shader) {
+    auto render_as_lines = [this, light_intensity, set_uniform_color](const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) {
         shader.set_uniform("light_intensity", light_intensity);
         for (const RenderPath& path : buffer.render_paths) {
-            if (path.index_buffer_id == index_buffer_id) {
+            if (path.index_buffer_id == ibuffer_id) {
                 set_uniform_color(path.color, shader);
 #if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
@@ -3555,9 +3561,9 @@ void GCodeViewer::render_toolpaths() const
         }
     };
 
-    auto render_as_triangles = [this, set_uniform_color](const TBuffer& buffer, unsigned int index_buffer_id, GLShaderProgram& shader) {
+    auto render_as_triangles = [this, set_uniform_color](const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) {
         for (const RenderPath& path : buffer.render_paths) {
-            if (path.index_buffer_id == index_buffer_id) {
+            if (path.index_buffer_id == ibuffer_id) {
                 set_uniform_color(path.color, shader);
 #if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
@@ -3606,17 +3612,7 @@ void GCodeViewer::render_toolpaths() const
                 switch (buffer.render_primitive_type)
                 {
                 case TBuffer::ERenderPrimitiveType::Point: {
-                    EOptionsColors color;
-                    switch (buffer_type(i))
-                    {
-                    case EMoveType::Tool_change:  { color = EOptionsColors::ToolChanges; break; }
-                    case EMoveType::Color_change: { color = EOptionsColors::ColorChanges; break; }
-                    case EMoveType::Pause_Print:  { color = EOptionsColors::PausePrints; break; }
-                    case EMoveType::Custom_GCode: { color = EOptionsColors::CustomGCodes; break; }
-                    case EMoveType::Retract:      { color = EOptionsColors::Retractions; break; }
-                    case EMoveType::Unretract:    { color = EOptionsColors::Unretractions; break; }
-                    }
-                    render_as_points(buffer, static_cast<unsigned int>(j), color, *shader);
+                    render_as_points(buffer, static_cast<unsigned int>(j), *shader);
                     break;
                 }
                 case TBuffer::ERenderPrimitiveType::Line: {
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index d80cc9dfc..c0bc16f67 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -222,6 +222,7 @@ class GCodeViewer
     {
         // Render path property
         Color                       color;
+        // Index of the buffer in TBuffer::indices
         unsigned int                index_buffer_id;
         // Render path content
         unsigned int                path_id;

From 97c31b2a34161ce2e337a9ac5140d0353c07676a Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Thu, 21 Jan 2021 16:33:35 +0100
Subject: [PATCH 16/20] ENABLE_SPLITTED_VERTEX_BUFFER - Re-enable tech
 ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING after fixing it

---
 src/libslic3r/Technologies.hpp |  2 +-
 src/slic3r/GUI/GCodeViewer.cpp | 92 ++++++++++++++++++++++------------
 2 files changed, 61 insertions(+), 33 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 8552484c1..97c153e50 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -114,7 +114,7 @@
 #define ENABLE_2_3_1 1
 
 #define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1)
-#define ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING (0 && ENABLE_SPLITTED_VERTEX_BUFFER)
+#define ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
 #define ENABLE_UNSIGNED_SHORT_INDEX_BUFFER (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
 
 
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 150f65acf..43bf0345a 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1794,7 +1794,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 #if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
 #if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
-            std::cout << "Splitted v buffer at " << i << "\n";
 #else
         if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes()) {
 #endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
@@ -1807,10 +1806,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         }
 #else
 #if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
-        if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
-//            std::cout << "Splitted v buffer at " << i << "\n";
+        if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes())
             v_multibuffer.push_back(VertexBuffer());
-        }
 #else
         if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes())
             v_multibuffer.push_back(VertexBuffer());
@@ -1851,35 +1848,67 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         };
         auto match_right_vertices = [&](const Path::Sub_Path& prev_sub_path, const Path::Sub_Path& next_sub_path,
             size_t curr_s_id, size_t vertex_size_floats, const Vec3f& displacement_vec) {
-                // offset into the vertex buffer of the next segment 1st vertex
-                size_t next_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats;
-                // offset into the vertex buffer of the right vertex of the previous segment 
-                size_t prev_right_offset = prev_sub_path.last.i_id - next_offset - 3 * vertex_size_floats;
-                // new position of the right vertices
-                Vec3f shared_vertex = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset) + displacement_vec;
-                // update previous segment
-                update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_right_offset, shared_vertex);
-                // offset into the vertex buffer of the right vertex of the next segment
-                size_t r_offset = (curr_s_id == next_sub_path.first.i_id) ? 1 : 0;
-                size_t next_right_offset = next_sub_path.last.i_id - next_offset + r_offset * vertex_size_floats;
-                // update next segment
-                update_position_at(v_multibuffer[next_sub_path.first.b_id], next_right_offset, shared_vertex);
+                if (&prev_sub_path == &next_sub_path) { // previous and next segment are both contained into to the same vertex buffer
+                    VertexBuffer& vbuffer = v_multibuffer[prev_sub_path.first.b_id];
+                    // offset into the vertex buffer of the next segment 1st vertex
+                    size_t next_1st_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats;
+                    // offset into the vertex buffer of the right vertex of the previous segment 
+                    size_t prev_right_offset = prev_sub_path.last.i_id - next_1st_offset - 3 * vertex_size_floats;
+                    // new position of the right vertices
+                    Vec3f shared_vertex = extract_position_at(vbuffer, prev_right_offset) + displacement_vec;
+                    // update previous segment
+                    update_position_at(vbuffer, prev_right_offset, shared_vertex);
+                    // offset into the vertex buffer of the right vertex of the next segment
+                    size_t next_right_offset = next_sub_path.last.i_id - next_1st_offset;
+                    // update next segment
+                    update_position_at(vbuffer, next_right_offset, shared_vertex);
+                }
+                else { // previous and next segment are contained into different vertex buffers
+                    VertexBuffer& prev_vbuffer = v_multibuffer[prev_sub_path.first.b_id];
+                    VertexBuffer& next_vbuffer = v_multibuffer[next_sub_path.first.b_id];
+                    // offset into the previous vertex buffer of the right vertex of the previous segment 
+                    size_t prev_right_offset = prev_sub_path.last.i_id - 3 * vertex_size_floats;
+                    // new position of the right vertices
+                    Vec3f shared_vertex = extract_position_at(prev_vbuffer, prev_right_offset) + displacement_vec;
+                    // update previous segment
+                    update_position_at(prev_vbuffer, prev_right_offset, shared_vertex);
+                    // offset into the next vertex buffer of the right vertex of the next segment
+                    size_t next_right_offset = next_sub_path.first.i_id + 1 * vertex_size_floats;
+                    // update next segment
+                    update_position_at(next_vbuffer, next_right_offset, shared_vertex);
+                }
         };
         auto match_left_vertices = [&](const Path::Sub_Path& prev_sub_path, const Path::Sub_Path& next_sub_path,
             size_t curr_s_id, size_t vertex_size_floats, const Vec3f& displacement_vec) {
-                // offset into the vertex buffer of the next segment 1st vertex
-                size_t next_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats;
-                // offset into the vertex buffer of the left vertex of the previous segment 
-                size_t prev_left_offset = prev_sub_path.last.i_id - next_offset - 1 * vertex_size_floats;
-                // new position of the left vertices
-                Vec3f shared_vertex = extract_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset) + displacement_vec;
-                // update previous segment
-                update_position_at(v_multibuffer[prev_sub_path.first.b_id], prev_left_offset, shared_vertex);
-                // offset into the vertex buffer of the left vertex of the next segment 
-                size_t l_offset = (curr_s_id == next_sub_path.first.i_id) ? 3 : 1;
-                size_t next_left_offset = next_sub_path.last.i_id - next_offset + l_offset * vertex_size_floats;
-                // update next segment
-                update_position_at(v_multibuffer[next_sub_path.first.b_id], next_left_offset, shared_vertex);
+                if (&prev_sub_path == &next_sub_path) { // previous and next segment are both contained into to the same vertex buffer
+                    VertexBuffer& vbuffer = v_multibuffer[prev_sub_path.first.b_id];
+                    // offset into the vertex buffer of the next segment 1st vertex
+                    size_t next_1st_offset = (prev_sub_path.last.s_id - curr_s_id) * 6 * vertex_size_floats;
+                    // offset into the vertex buffer of the left vertex of the previous segment 
+                    size_t prev_left_offset = prev_sub_path.last.i_id - next_1st_offset - 1 * vertex_size_floats;
+                    // new position of the left vertices
+                    Vec3f shared_vertex = extract_position_at(vbuffer, prev_left_offset) + displacement_vec;
+                    // update previous segment
+                    update_position_at(vbuffer, prev_left_offset, shared_vertex);
+                    // offset into the vertex buffer of the left vertex of the next segment
+                    size_t next_left_offset = next_sub_path.last.i_id - next_1st_offset + 1 * vertex_size_floats;
+                    // update next segment
+                    update_position_at(vbuffer, next_left_offset, shared_vertex);
+                }
+                else { // previous and next segment are contained into different vertex buffers
+                    VertexBuffer& prev_vbuffer = v_multibuffer[prev_sub_path.first.b_id];
+                    VertexBuffer& next_vbuffer = v_multibuffer[next_sub_path.first.b_id];
+                    // offset into the previous vertex buffer of the left vertex of the previous segment 
+                    size_t prev_left_offset = prev_sub_path.last.i_id - 1 * vertex_size_floats;
+                    // new position of the left vertices
+                    Vec3f shared_vertex = extract_position_at(prev_vbuffer, prev_left_offset) + displacement_vec;
+                    // update previous segment
+                    update_position_at(prev_vbuffer, prev_left_offset, shared_vertex);
+                    // offset into the next vertex buffer of the left vertex of the next segment
+                    size_t next_left_offset = next_sub_path.first.i_id + 3 * vertex_size_floats;
+                    // update next segment
+                    update_position_at(next_vbuffer, next_left_offset, shared_vertex);
+                }
         };
 
         size_t vertex_size_floats = t_buffer.vertices.vertex_size_floats();
@@ -1927,7 +1956,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                 float sq_prev_length = (curr - prev).squaredNorm();
                 float sq_next_length = (next - curr).squaredNorm();
                 float sq_displacement = sqr(displacement);
-                bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length&& sq_displacement < sq_next_length;
+                bool can_displace = displacement > 0.0f && sq_displacement < sq_prev_length && sq_displacement < sq_next_length;
 
                 if (can_displace) {
                     // displacement to apply to the vertices to match
@@ -2068,7 +2097,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         // create another index buffer
 #if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
-//            std::cout << "Splitted i buffer at " << i << "\n";
 #else
         if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes()) {
 #endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER

From 30f43974fbcc35536a939ecea682ecf5ba73a3f6 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Fri, 22 Jan 2021 12:32:40 +0100
Subject: [PATCH 17/20] ENABLE_GCODE_VIEWER_STATISTICS - GCodeViewer statistic
 dialog modified to not require extra frames render

---
 src/slic3r/GUI/GCodeViewer.cpp | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 43bf0345a..864dcdcc1 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -4576,16 +4576,12 @@ void GCodeViewer::render_statistics() const
         add_time(std::string("Load:"), m_statistics.load_time);
         add_time(std::string("Refresh:"), m_statistics.refresh_time);
         add_time(std::string("Refresh paths:"), m_statistics.refresh_paths_time);
-        wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
-        wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
     }
 
     if (ImGui::CollapsingHeader("OpenGL calls")) {
         add_counter(std::string("Multi GL_POINTS:"), m_statistics.gl_multi_points_calls_count);
         add_counter(std::string("Multi GL_LINES:"), m_statistics.gl_multi_lines_calls_count);
         add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count);
-        wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
-        wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
     }
 
     if (ImGui::CollapsingHeader("CPU memory")) {
@@ -4594,8 +4590,6 @@ void GCodeViewer::render_statistics() const
         ImGui::Separator();
         add_memory(std::string("Paths:"), m_statistics.paths_size);
         add_memory(std::string("Render paths:"), m_statistics.render_paths_size);
-        wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
-        wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
     }
 
     if (ImGui::CollapsingHeader("GPU memory")) {
@@ -4604,8 +4598,6 @@ void GCodeViewer::render_statistics() const
         ImGui::Separator();
         add_memory(std::string("Max VBuffer:"), m_statistics.max_vbuffer_gpu_size);
         add_memory(std::string("Max IBuffer:"), m_statistics.max_ibuffer_gpu_size);
-        wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
-        wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
     }
 
     if (ImGui::CollapsingHeader("Other")) {
@@ -4615,8 +4607,6 @@ void GCodeViewer::render_statistics() const
         ImGui::Separator();
         add_counter(std::string("VBuffers count:"), m_statistics.vbuffers_count);
         add_counter(std::string("IBuffers count:"), m_statistics.ibuffers_count);
-        wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
-        wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
     }
 
     imgui.end();

From 3533473256470a959116cd9c583c72913fec5ed1 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 26 Jan 2021 12:21:50 +0100
Subject: [PATCH 18/20] Tech ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING merge into
 tech ENABLE_SPLITTED_VERTEX_BUFFER

---
 src/libslic3r/Technologies.hpp |  11 +-
 src/slic3r/GUI/GCodeViewer.cpp | 307 ---------------------------------
 2 files changed, 5 insertions(+), 313 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 97c153e50..67d98044a 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -108,13 +108,12 @@
 #define ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING (1 && ENABLE_2_3_0_RC1)
 
 
-//=======
-// 2.3.1 
-//=======
-#define ENABLE_2_3_1 1
+//============== 
+// 2.3.1.alpha1
+//==============
+#define ENABLE_2_3_1_ALPHA1 1
 
-#define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1)
-#define ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
+#define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1_ALPHA1)
 #define ENABLE_UNSIGNED_SHORT_INDEX_BUFFER (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
 
 
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 864dcdcc1..42284c6b4 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1239,7 +1239,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     };
 
     // format data into the buffers to be rendered as solid
-#if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
     auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) {
         auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
             // append position
@@ -1434,296 +1433,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             prev_up = up;
             sq_prev_length = sq_length;
     };
-#else
-    auto add_vertices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, VertexBuffer& vertices, size_t move_id) {
-        static Vec3f prev_dir;
-        static Vec3f prev_up;
-        static float prev_length;
-        auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
-            // append position
-            vertices.push_back(position[0]);
-            vertices.push_back(position[1]);
-            vertices.push_back(position[2]);
-            // append normal
-            vertices.push_back(normal[0]);
-            vertices.push_back(normal[1]);
-            vertices.push_back(normal[2]);
-        };
-        auto extract_position_at = [](const VertexBuffer& vertices, size_t id) {
-            return Vec3f(vertices[id + 0], vertices[id + 1], vertices[id + 2]);
-        };
-        auto update_position_at = [](VertexBuffer& vertices, size_t id, const Vec3f& position) {
-            vertices[id + 0] = position[0];
-            vertices[id + 1] = position[1];
-            vertices[id + 2] = position[2];
-        };
-
-        if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
-            buffer.add_path(curr, 0, 0, move_id - 1);
-            buffer.paths.back().sub_paths.back().first.position = prev.position;
-        }
-
-        unsigned int starting_vertices_size = static_cast<unsigned int>(vertices.size() / buffer.vertices.vertex_size_floats());
-
-        Vec3f dir = (curr.position - prev.position).normalized();
-        Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized();
-        Vec3f left = -right;
-        Vec3f up = right.cross(dir);
-        Vec3f down = -up;
-
-        Path& last_path = buffer.paths.back();
-
-        float half_width = 0.5f * last_path.width;
-        float half_height = 0.5f * last_path.height;
-
-        Vec3f prev_pos = prev.position - half_height * up;
-        Vec3f curr_pos = curr.position - half_height * up;
-
-        float length = (curr_pos - prev_pos).norm();
-        if (last_path.vertices_count() == 1 || vertices.empty()) {
-            // 1st segment or restart into a new vertex buffer
-            // ===============================================
-
-            // vertices 1st endpoint
-            store_vertex(vertices, prev_pos + half_height * up, up);
-            store_vertex(vertices, prev_pos + half_width * right, right);
-            store_vertex(vertices, prev_pos + half_height * down, down);
-            store_vertex(vertices, prev_pos + half_width * left, left);
-
-            // vertices 2nd endpoint
-            store_vertex(vertices, curr_pos + half_height * up, up);
-            store_vertex(vertices, curr_pos + half_width * right, right);
-            store_vertex(vertices, curr_pos + half_height * down, down);
-            store_vertex(vertices, curr_pos + half_width * left, left);
-        }
-        else {
-            // any other segment
-            // =================
-
-            float displacement = 0.0f;
-            float cos_dir = prev_dir.dot(dir);
-            if (cos_dir > -0.9998477f) {
-                // if the angle between adjacent segments is smaller than 179 degrees
-                Vec3f med_dir = (prev_dir + dir).normalized();
-                displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f)));
-            }
-
-            Vec3f displacement_vec = displacement * prev_dir;
-            bool can_displace = displacement > 0.0f && displacement < prev_length && displacement < length;
-
-            size_t prev_right_id = (starting_vertices_size - 3) * buffer.vertices.vertex_size_floats();
-            size_t prev_left_id = (starting_vertices_size - 1) * buffer.vertices.vertex_size_floats();
-            Vec3f prev_right_pos = extract_position_at(vertices, prev_right_id);
-            Vec3f prev_left_pos = extract_position_at(vertices, prev_left_id);
-
-            bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f;
-            // whether the angle between adjacent segments is greater than 45 degrees
-            bool is_sharp = cos_dir < 0.7071068f;
-
-            bool right_displaced = false;
-            bool left_displaced = false;
-
-            // displace the vertex (inner with respect to the corner) of the previous segment 2nd endpoint, if possible
-            if (can_displace) {
-                if (is_right_turn) {
-                    prev_right_pos -= displacement_vec;
-                    update_position_at(vertices, prev_right_id, prev_right_pos);
-                    right_displaced = true;
-                }
-                else {
-                    prev_left_pos -= displacement_vec;
-                    update_position_at(vertices, prev_left_id, prev_left_pos);
-                    left_displaced = true;
-                }
-            }
-
-            if (!is_sharp) {
-                // displace the vertex (outer with respect to the corner) of the previous segment 2nd endpoint, if possible
-                if (can_displace) {
-                    if (is_right_turn) {
-                        prev_left_pos += displacement_vec;
-                        update_position_at(vertices, prev_left_id, prev_left_pos);
-                        left_displaced = true;
-                    }
-                    else {
-                        prev_right_pos += displacement_vec;
-                        update_position_at(vertices, prev_right_id, prev_right_pos);
-                        right_displaced = true;
-                    }
-                }
-
-                // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint)
-                // vertices position matches that of the previous segment 2nd endpoint, if displaced
-                store_vertex(vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right);
-                store_vertex(vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left);
-            }
-            else {
-                // vertices 1st endpoint (top and bottom are from previous segment 2nd endpoint)
-                // the inner corner vertex position matches that of the previous segment 2nd endpoint, if displaced
-                if (is_right_turn) {
-                    store_vertex(vertices, right_displaced ? prev_right_pos : prev_pos + half_width * right, right);
-                    store_vertex(vertices, prev_pos + half_width * left, left);
-                }
-                else {
-                    store_vertex(vertices, prev_pos + half_width * right, right);
-                    store_vertex(vertices, left_displaced ? prev_left_pos : prev_pos + half_width * left, left);
-                }
-            }
-
-            // vertices 2nd endpoint
-            store_vertex(vertices, curr_pos + half_height * up, up);
-            store_vertex(vertices, curr_pos + half_width * right, right);
-            store_vertex(vertices, curr_pos + half_height * down, down);
-            store_vertex(vertices, curr_pos + half_width * left, left);
-        }
-
-        last_path.sub_paths.back().last = { 0, 0, move_id, curr.position };
-        prev_dir = dir;
-        prev_up = up;
-        prev_length = length;
-    };
-    auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
-        size_t& vbuffer_size, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
-            static Vec3f prev_dir;
-            static Vec3f prev_up;
-            static float prev_length;
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
-            auto store_triangle = [](IndexBuffer& indices, IBufferType i1, IBufferType i2, IBufferType i3) {
-#else
-            auto store_triangle = [](IndexBuffer& indices, unsigned int i1, unsigned int i2, unsigned int i3) {
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
-                indices.push_back(i1);
-                indices.push_back(i2);
-                indices.push_back(i3);
-            };
-            auto append_dummy_cap = [store_triangle](IndexBuffer& indices, unsigned int id) {
-                store_triangle(indices, id, id, id);
-                store_triangle(indices, id, id, id);
-            };
-
-            if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
-                buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1);
-                buffer.paths.back().sub_paths.back().first.position = prev.position;
-            }
-
-            Vec3f dir = (curr.position - prev.position).normalized();
-            Vec3f right = (std::abs(std::abs(dir.dot(Vec3f::UnitZ())) - 1.0f) < EPSILON) ? -Vec3f::UnitY() : Vec3f(dir[1], -dir[0], 0.0f).normalized();
-            Vec3f up = right.cross(dir);
-
-            Path& last_path = buffer.paths.back();
-
-            float half_width = 0.5f * last_path.width;
-            float half_height = 0.5f * last_path.height;
-
-            Vec3f prev_pos = prev.position - half_height * up;
-            Vec3f curr_pos = curr.position - half_height * up;
-
-            float length = (curr_pos - prev_pos).norm();
-            if (last_path.vertices_count() == 1 || vbuffer_size == 0) {
-                // 1st segment or restart into a new vertex buffer
-                // ===============================================
-
-                // triangles starting cap
-                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 2, vbuffer_size + 1);
-                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 3, vbuffer_size + 2);
-
-                // dummy triangles outer corner cap
-                append_dummy_cap(indices, vbuffer_size);
-
-                // triangles sides
-                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 1, vbuffer_size + 4);
-                store_triangle(indices, vbuffer_size + 1, vbuffer_size + 5, vbuffer_size + 4);
-                store_triangle(indices, vbuffer_size + 1, vbuffer_size + 2, vbuffer_size + 5);
-                store_triangle(indices, vbuffer_size + 2, vbuffer_size + 6, vbuffer_size + 5);
-                store_triangle(indices, vbuffer_size + 2, vbuffer_size + 3, vbuffer_size + 6);
-                store_triangle(indices, vbuffer_size + 3, vbuffer_size + 7, vbuffer_size + 6);
-                store_triangle(indices, vbuffer_size + 3, vbuffer_size + 0, vbuffer_size + 7);
-                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 4, vbuffer_size + 7);
-
-                // triangles ending cap
-                store_triangle(indices, vbuffer_size + 4, vbuffer_size + 6, vbuffer_size + 7);
-                store_triangle(indices, vbuffer_size + 4, vbuffer_size + 5, vbuffer_size + 6);
-
-                vbuffer_size += 8;
-            }
-            else {
-                // any other segment
-                // =================
-
-                float displacement = 0.0f;
-                float cos_dir = prev_dir.dot(dir);
-                if (cos_dir > -0.9998477f) {
-                    // if the angle between adjacent segments is smaller than 179 degrees
-                    Vec3f med_dir = (prev_dir + dir).normalized();
-                    displacement = half_width * ::tan(::acos(std::clamp(dir.dot(med_dir), -1.0f, 1.0f)));
-                }
-
-                Vec3f displacement_vec = displacement * prev_dir;
-                bool can_displace = displacement > 0.0f && displacement < prev_length&& displacement < length;
-
-                bool is_right_turn = prev_up.dot(prev_dir.cross(dir)) <= 0.0f;
-                // whether the angle between adjacent segments is greater than 45 degrees
-                bool is_sharp = cos_dir < 0.7071068f;
-
-                bool right_displaced = false;
-                bool left_displaced = false;
-
-                if (!is_sharp) {
-                    if (can_displace) {
-                        if (is_right_turn)
-                            left_displaced = true;
-                        else
-                            right_displaced = true;
-                    }
-                }
-
-                // triangles starting cap
-                store_triangle(indices, vbuffer_size - 4, vbuffer_size - 2, vbuffer_size + 0);
-                store_triangle(indices, vbuffer_size - 4, vbuffer_size + 1, vbuffer_size - 2);
-
-                // triangles outer corner cap
-                if (is_right_turn) {
-                    if (left_displaced)
-                        // dummy triangles
-                        append_dummy_cap(indices, vbuffer_size);
-                    else {
-                        store_triangle(indices, vbuffer_size - 4, vbuffer_size + 1, vbuffer_size - 1);
-                        store_triangle(indices, vbuffer_size + 1, vbuffer_size - 2, vbuffer_size - 1);
-                    }
-                }
-                else {
-                    if (right_displaced)
-                        // dummy triangles
-                        append_dummy_cap(indices, vbuffer_size);
-                    else {
-                        store_triangle(indices, vbuffer_size - 4, vbuffer_size - 3, vbuffer_size + 0);
-                        store_triangle(indices, vbuffer_size - 3, vbuffer_size - 2, vbuffer_size + 0);
-                    }
-                }
-
-                // triangles sides
-                store_triangle(indices, vbuffer_size - 4, vbuffer_size + 0, vbuffer_size + 2);
-                store_triangle(indices, vbuffer_size + 0, vbuffer_size + 3, vbuffer_size + 2);
-                store_triangle(indices, vbuffer_size + 0, vbuffer_size - 2, vbuffer_size + 3);
-                store_triangle(indices, vbuffer_size - 2, vbuffer_size + 4, vbuffer_size + 3);
-                store_triangle(indices, vbuffer_size - 2, vbuffer_size + 1, vbuffer_size + 4);
-                store_triangle(indices, vbuffer_size + 1, vbuffer_size + 5, vbuffer_size + 4);
-                store_triangle(indices, vbuffer_size + 1, vbuffer_size - 4, vbuffer_size + 5);
-                store_triangle(indices, vbuffer_size - 4, vbuffer_size + 2, vbuffer_size + 5);
-
-                // triangles ending cap
-                store_triangle(indices, vbuffer_size + 2, vbuffer_size + 4, vbuffer_size + 5);
-                store_triangle(indices, vbuffer_size + 2, vbuffer_size + 3, vbuffer_size + 4);
-
-                vbuffer_size += 6;
-            }
-
-            last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position };
-            prev_dir = dir;
-            prev_up = up;
-            prev_length = length;
-    };
-#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
 
 #if ENABLE_GCODE_VIEWER_STATISTICS
     auto start_time = std::chrono::high_resolution_clock::now();
@@ -1791,7 +1500,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
         // add another vertex buffer
-#if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
 #if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
 #else
@@ -1804,15 +1512,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
                     last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, i - 1);
             }
         }
-#else
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
-        if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes())
-            v_multibuffer.push_back(VertexBuffer());
-#else
-        if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes())
-            v_multibuffer.push_back(VertexBuffer());
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
-#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
 
         VertexBuffer& v_buffer = v_multibuffer.back();
 
@@ -1820,11 +1519,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         {
         case TBuffer::ERenderPrimitiveType::Point:    { add_vertices_as_point(curr, v_buffer); break; }
         case TBuffer::ERenderPrimitiveType::Line:     { add_vertices_as_line(prev, curr, v_buffer); break; }
-#if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
         case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, i); break; }
-#else
-        case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, v_buffer, i); break; }
-#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
         }
 
         // collect options zs for later use
@@ -1835,7 +1530,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         }
     }
 
-#if ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
     // smooth toolpaths corners for the given TBuffer using triangles
     auto smooth_triangle_toolpaths_corners = [&gcode_result](const TBuffer& t_buffer, MultiVertexBuffer& v_multibuffer) {
         auto extract_position_at = [](const VertexBuffer& vertices, size_t offset) {
@@ -1986,7 +1680,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             smooth_triangle_toolpaths_corners(t_buffer, vertices[i]);
         }
     }
-#endif // ENABLE_TOOLPATHS_ALTERNATE_SMOOTHING
 
     for (MultiVertexBuffer& v_multibuffer : vertices) {
         for (VertexBuffer& v_buffer : v_multibuffer) {

From 9304defe7cf8a439f68b8d4ddf61247281b9c364 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 26 Jan 2021 13:32:27 +0100
Subject: [PATCH 19/20] Tech ENABLE_UNSIGNED_SHORT_INDEX_BUFFER merged into
 tech ENABLE_SPLITTED_VERTEX_BUFFER

---
 src/libslic3r/Technologies.hpp |  7 ++--
 src/slic3r/GUI/GCodeViewer.cpp | 72 ----------------------------------
 src/slic3r/GUI/GCodeViewer.hpp | 16 +++-----
 3 files changed, 8 insertions(+), 87 deletions(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 67d98044a..41cfbdda8 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -108,13 +108,12 @@
 #define ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING (1 && ENABLE_2_3_0_RC1)
 
 
-//============== 
-// 2.3.1.alpha1
-//==============
+//====================
+// 2.3.1.alpha1 techs
+//====================
 #define ENABLE_2_3_1_ALPHA1 1
 
 #define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1_ALPHA1)
-#define ENABLE_UNSIGNED_SHORT_INDEX_BUFFER (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
 
 
 #endif // _prusaslicer_technologies_h_
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 42284c6b4..a9e9d6a03 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -884,13 +884,8 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
             glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer.ibo));
             for (size_t j = 0; j < render_path.sizes.size(); ++j) {
                 IndexBuffer indices(render_path.sizes[j]);
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(render_path.offsets[j]),
                     static_cast<GLsizeiptr>(render_path.sizes[j] * sizeof(IBufferType)), static_cast<void*>(indices.data())));
-#else
-                glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(render_path.offsets[j]),
-                    static_cast<GLsizeiptr>(render_path.sizes[j] * sizeof(unsigned int)), static_cast<void*>(indices.data())));
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
                 const size_t triangles_count = render_path.sizes[j] / 3;
                 for (size_t k = 0; k < triangles_count; ++k) {
@@ -1156,10 +1151,6 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
 #if ENABLE_SPLITTED_VERTEX_BUFFER
 void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 {
-#if !ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
-    // max vertex buffer size, in bytes
-    static const size_t VBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
-#endif // !ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
     // max index buffer size, in bytes
     static const size_t IBUFFER_THRESHOLD_BYTES = 64 * 1024 * 1024;
 
@@ -1173,11 +1164,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         int64_t indices_size = 0;
         for (const MultiIndexBuffer& buffers : indices) {
             for (const IndexBuffer& buffer : buffers) {
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 indices_size += SLIC3R_STDVEC_MEMSIZE(buffer, IBufferType);
-#else
-                indices_size += SLIC3R_STDVEC_MEMSIZE(buffer, unsigned int);
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             }
         }
         log_memory_used(label, vertices_size + indices_size);
@@ -1192,11 +1179,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     auto add_indices_as_point = [](const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
         unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
             buffer.add_path(curr, ibuffer_id, indices.size(), move_id);
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             indices.push_back(static_cast<IBufferType>(indices.size()));
-#else
-            indices.push_back(static_cast<unsigned int>(indices.size()));
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
     };
 
     // format data into the buffers to be rendered as lines
@@ -1301,35 +1284,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
             static Vec3f prev_dir;
             static Vec3f prev_up;
             static float sq_prev_length;
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             auto store_triangle = [](IndexBuffer& indices, IBufferType i1, IBufferType i2, IBufferType i3) {
-#else
-            auto store_triangle = [](IndexBuffer& indices, unsigned int i1, unsigned int i2, unsigned int i3) {
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 indices.push_back(i1);
                 indices.push_back(i2);
                 indices.push_back(i3);
             };
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             auto append_dummy_cap = [store_triangle](IndexBuffer& indices, IBufferType id) {
-#else
-            auto append_dummy_cap = [store_triangle](IndexBuffer& indices, unsigned int id) {
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 store_triangle(indices, id, id, id);
                 store_triangle(indices, id, id, id);
             };
             auto store_main_triangles = [&](IndexBuffer& indices, size_t vbuffer_size, const std::array<int, 8>& v_offsets) {
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 std::array<IBufferType, 8> v_ids;
                 for (size_t i = 0; i < v_ids.size(); ++i) {
                     v_ids[i] = static_cast<IBufferType>(static_cast<int>(vbuffer_size) + v_offsets[i]);
                 }
-#else
-                std::array<unsigned int, 8> v_ids;
-                for (size_t i = 0; i < v_ids.size(); ++i) {
-                    v_ids[i] = static_cast<unsigned int>(static_cast<int>(vbuffer_size) + v_offsets[i]);
-                }
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
                 // triangles starting cap
                 store_triangle(indices, v_ids[0], v_ids[2], v_ids[1]);
@@ -1500,11 +1468,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
         // add another vertex buffer
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
-#else
-        if (v_multibuffer.back().size() * sizeof(float) > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes()) {
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             v_multibuffer.push_back(VertexBuffer());
             if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
                 Path& last_path = t_buffer.paths.back();
@@ -1773,11 +1737,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         // if adding the indices for the current segment exceeds the threshold size of the current index buffer
         // create another index buffer
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - t_buffer.indices_per_segment_size_bytes()) {
-#else
-        if (i_multibuffer.back().size() * sizeof(unsigned int) >= IBUFFER_THRESHOLD_BYTES - t_buffer.indices_per_segment_size_bytes()) {
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             i_multibuffer.push_back(IndexBuffer());
             vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
             if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
@@ -1788,11 +1748,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
 
         // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
         // create another index buffer
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
-#else
-        if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > VBUFFER_THRESHOLD_BYTES - t_buffer.max_vertices_per_segment_size_bytes()) {
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             i_multibuffer.push_back(IndexBuffer());
 
             ++curr_vertex_buffer.first;
@@ -1838,11 +1794,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         const MultiIndexBuffer& i_multibuffer = indices[i];
         for (const IndexBuffer& i_buffer : i_multibuffer) {
             size_t size_elements = i_buffer.size();
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             size_t size_bytes = size_elements * sizeof(IBufferType);
-#else
-            size_t size_bytes = size_elements * sizeof(unsigned int);
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
             // stores index buffer informations into TBuffer
             t_buffer.indices.push_back(IBuffer());
@@ -2835,11 +2787,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
                     const IBuffer& i_buffer = buffer.indices[sub_path.first.b_id];
                     unsigned int index = 0;
                     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                     glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(offset * sizeof(IBufferType)), static_cast<GLsizeiptr>(sizeof(IBufferType)), static_cast<void*>(&index)));
-#else
-                    glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLintptr>(offset * sizeof(unsigned int)), static_cast<GLsizeiptr>(sizeof(unsigned int)), static_cast<void*>(&index)));
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 
                     // gets the position from the vertices buffer on gpu
@@ -2923,11 +2871,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
         if (buffer->render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle)
             delta_1st *= buffer->indices_per_segment();
 
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         render_path->offsets.push_back(static_cast<size_t>((sub_path.first.i_id + delta_1st) * sizeof(IBufferType)));
-#else
-        render_path->offsets.push_back(static_cast<size_t>((sub_path.first.i_id + delta_1st) * sizeof(unsigned int)));
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 
 #if 0
         // check sizes and offsets against index buffer size on gpu
@@ -2935,11 +2879,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->indices[render_path->index_buffer_id].ibo));
         glsafe(::glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &buffer_size));
         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         if (render_path->offsets.back() + render_path->sizes.back() * sizeof(IBufferType) > buffer_size)
-#else
-        if (render_path->offsets.back() + render_path->sizes.back() * sizeof(unsigned int) > buffer_size)
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
             BOOST_LOG_TRIVIAL(error) << "GCodeViewer::refresh_render_paths: Invalid render path data";
 #endif 
     }
@@ -3250,11 +3190,7 @@ void GCodeViewer::render_toolpaths() const
         for (const RenderPath& path : buffer.render_paths) {
             if (path.index_buffer_id == ibuffer_id) {
                 set_uniform_color(path.color, shader);
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
-#else
-                glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #if ENABLE_GCODE_VIEWER_STATISTICS
                 ++m_statistics.gl_multi_points_calls_count;
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -3270,11 +3206,7 @@ void GCodeViewer::render_toolpaths() const
         for (const RenderPath& path : buffer.render_paths) {
             if (path.index_buffer_id == ibuffer_id) {
                 set_uniform_color(path.color, shader);
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
-#else
-                glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #if ENABLE_GCODE_VIEWER_STATISTICS
                 ++m_statistics.gl_multi_lines_calls_count;
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -3286,11 +3218,7 @@ void GCodeViewer::render_toolpaths() const
         for (const RenderPath& path : buffer.render_paths) {
             if (path.index_buffer_id == ibuffer_id) {
                 set_uniform_color(path.color, shader);
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
                 glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
-#else
-                glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #if ENABLE_GCODE_VIEWER_STATISTICS
                 ++m_statistics.gl_multi_triangles_calls_count;
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index c0bc16f67..836014ad3 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -19,19 +19,17 @@ namespace GUI {
 
 class GCodeViewer
 {
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+#if ENABLE_SPLITTED_VERTEX_BUFFER
     using IBufferType = unsigned short;
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
     using Color = std::array<float, 3>;
     using VertexBuffer = std::vector<float>;
 #if ENABLE_SPLITTED_VERTEX_BUFFER
     using MultiVertexBuffer = std::vector<VertexBuffer>;
-#endif // ENABLE_SPLITTED_VERTEX_BUFFER
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
     using IndexBuffer = std::vector<IBufferType>;
 #else
     using IndexBuffer = std::vector<unsigned int>;
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
     using MultiIndexBuffer = std::vector<IndexBuffer>;
 
     static const std::vector<Color> Extrusion_Role_Colors;
@@ -77,11 +75,11 @@ class GCodeViewer
         size_t count{ 0 };
 
         size_t data_size_bytes() const { return count * vertex_size_bytes(); }
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+#if ENABLE_SPLITTED_VERTEX_BUFFER
         // We set 65536 as max count of vertices inside a vertex buffer to allow
         // to use unsigned short in place of unsigned int for indices in the index buffer, to save memory
         size_t max_size_bytes() const { return 65536 * vertex_size_bytes(); }
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
+#endif // ENABLE_SPLITTED_VERTEX_BUFFER
 
         size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); }
         size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); }
@@ -304,11 +302,7 @@ class GCodeViewer
             }
         }
 #if ENABLE_SPLITTED_VERTEX_BUFFER
-#if ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
         size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(IBufferType)); }
-#else
-        size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(unsigned int)); }
-#endif // ENABLE_UNSIGNED_SHORT_INDEX_BUFFER
 #endif // ENABLE_SPLITTED_VERTEX_BUFFER
         unsigned int start_segment_vertex_offset() const { return 0; }
         unsigned int end_segment_vertex_offset() const {

From a897dd05047c07d8a0621f6555d3689b4f881f4c Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Tue, 26 Jan 2021 13:56:32 +0100
Subject: [PATCH 20/20] Disabled tech ENABLE_GCODE_VIEWER_STATISTICS

---
 src/libslic3r/Technologies.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 41cfbdda8..d04496bbd 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -24,7 +24,7 @@
 // Use wxDataViewRender instead of wxDataViewCustomRenderer
 #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
 // Enable G-Code viewer statistics imgui dialog
-#define ENABLE_GCODE_VIEWER_STATISTICS 1
+#define ENABLE_GCODE_VIEWER_STATISTICS 0
 // Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation 
 #define ENABLE_GCODE_VIEWER_DATA_CHECKING 0