diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index d764d6cef..a3d1f0615 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -585,6 +585,23 @@ int GLVolumeCollection::load_wipe_tower_preview(
     return int(this->volumes.size() - 1);
 }
 
+GLVolume* GLVolumeCollection::new_toolpath_volume(const float *rgba, size_t reserve_vbo_floats)
+{
+	GLVolume *out = new_nontoolpath_volume(rgba, reserve_vbo_floats);
+	out->is_extrusion_path = true;
+	return out;
+}
+
+GLVolume* GLVolumeCollection::new_nontoolpath_volume(const float *rgba, size_t reserve_vbo_floats)
+{
+	GLVolume *out = new GLVolume(rgba);
+	out->is_extrusion_path = false;
+	// Reserving number of vertices (3x position + 3x color)
+	out->indexed_vertex_array.reserve(reserve_vbo_floats / 6);
+	this->volumes.emplace_back(out);
+	return out;
+}
+
 GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
 {
     GLVolumeWithIdAndZList list;
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index 5acc85217..8c1d68f77 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -522,6 +522,9 @@ public:
     int load_wipe_tower_preview(
         int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized);
 
+    GLVolume* new_toolpath_volume(const float *rgba, size_t reserve_vbo_floats = 0);
+    GLVolume* new_nontoolpath_volume(const float *rgba, size_t reserve_vbo_floats = 0);
+
     // Render the volumes by OpenGL.
     void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
 
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 3e668c4d8..079177d78 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -70,6 +70,11 @@ static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f };
 static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f };
 //static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } };
 
+// Number of floats
+static const float MAX_VERTEX_BUFFER_SIZE     = 131072 * 6; // 3.15MB
+// Reserve size in number of floats.
+static const float VERTEX_BUFFER_RESERVE_SIZE = 131072 * 2; // 1.05MB
+
 namespace Slic3r {
 namespace GUI {
 
@@ -1518,14 +1523,8 @@ void GLCanvas3D::render()
     if (m_canvas == nullptr)
         return;
 
-#ifndef __WXMAC__
-    // on Mac this check causes flickering when changing view
-    if (!_is_shown_on_screen())
-        return;
-#endif // __WXMAC__
-
     // ensures this canvas is current and initialized
-    if (!_set_current() || !_3DScene::init(m_canvas))
+    if (! _is_shown_on_screen() || !_set_current() || !_3DScene::init(m_canvas))
         return;
 
 #if ENABLE_RENDER_STATISTICS
@@ -2084,6 +2083,52 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
     m_dirty = true;
 }
 
+static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old, bool gl_initialized)
+{
+	// Assign the large pre-allocated buffers to the new GLVolume.
+	vol_new.indexed_vertex_array = std::move(vol_old.indexed_vertex_array);
+	// Copy the content back to the old GLVolume.
+	vol_old.indexed_vertex_array = vol_new.indexed_vertex_array;
+	// Clear the buffers, but keep them pre-allocated.
+	vol_new.indexed_vertex_array.clear();
+	// Just make sure that clear did not clear the reserved memory.
+	// Reserving number of vertices (3x position + 3x color)
+	vol_new.indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6);
+	// Finalize the old geometry, possibly move data to the graphics card.
+	vol_old.finalize_geometry(gl_initialized);
+}
+
+static void load_gcode_retractions(const GCodePreviewData::Retraction& retractions, GLCanvas3D::GCodePreviewVolumeIndex::EType extrusion_type, GLVolumeCollection &volumes, GLCanvas3D::GCodePreviewVolumeIndex &volume_index, bool gl_initialized)
+{
+	volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size());
+
+	// nothing to render, return
+	if (retractions.positions.empty())
+		return;
+
+	GLVolume *volume = volumes.new_nontoolpath_volume(retractions.color.rgba, VERTEX_BUFFER_RESERVE_SIZE);
+
+	GCodePreviewData::Retraction::PositionsList copy(retractions.positions);
+	std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2) { return p1.position(2) < p2.position(2); });
+
+	for (const GCodePreviewData::Retraction::Position& position : copy)
+	{
+		volume->print_zs.push_back(unscale<double>(position.position(2)));
+		volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
+		volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
+
+		_3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
+
+		// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
+		if (volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
+			GLVolume &vol = *volume;
+			volume = volumes.new_nontoolpath_volume(vol.color);
+			reserve_new_volume_finalize_old_volume(*volume, vol, gl_initialized);
+		}
+	}
+	volume->indexed_vertex_array.finalize_geometry(gl_initialized);
+}
+
 void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
 {
     const Print *print = this->fff_print();
@@ -2099,8 +2144,8 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
             
             _load_gcode_extrusion_paths(preview_data, tool_colors);
             _load_gcode_travel_paths(preview_data, tool_colors);
-            _load_gcode_retractions(preview_data);
-            _load_gcode_unretractions(preview_data);
+			load_gcode_retractions(preview_data.retraction,   GCodePreviewVolumeIndex::Retraction,   m_volumes, m_gcode_preview_volume_index, m_initialized);
+			load_gcode_retractions(preview_data.unretraction, GCodePreviewVolumeIndex::Unretraction, m_volumes, m_gcode_preview_volume_index, m_initialized);
             
             if (!m_volumes.empty())
             {
@@ -2129,6 +2174,8 @@ void GLCanvas3D::load_sla_preview()
     if ((m_canvas != nullptr) && (print != nullptr))
     {
         _set_current();
+	    // Release OpenGL data before generating new data.
+	    this->reset_volumes();
         _load_sla_shells();
         _update_sla_shells_outside_state();
         _show_warning_texture_if_needed(WarningTexture::SlaSupportsOutside);
@@ -2143,18 +2190,13 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, c
 
     _set_current();
 
+    // Release OpenGL data before generating new data.
+    this->reset_volumes();
+
     _load_print_toolpaths();
     _load_wipe_tower_toolpaths(str_tool_colors);
     for (const PrintObject* object : print->objects())
-    {
-        if (object != nullptr)
-            _load_print_object_toolpaths(*object, str_tool_colors, color_print_values);
-    }
-
-    for (GLVolume* volume : m_volumes.volumes)
-    {
-        volume->is_extrusion_path = true;
-    }
+        _load_print_object_toolpaths(*object, str_tool_colors, color_print_values);
 
     _update_toolpath_volumes_outside_state();
     _show_warning_texture_if_needed(WarningTexture::ToolpathOutside);
@@ -3716,11 +3758,7 @@ bool GLCanvas3D::_init_undoredo_toolbar()
 
 bool GLCanvas3D::_set_current()
 {
-    if (_is_shown_on_screen() && (m_context != nullptr)) {
-        return m_canvas->SetCurrent(*m_context);
-    }
-
-    return false;
+    return m_context != nullptr && m_canvas->SetCurrent(*m_context);
 }
 
 void GLCanvas3D::_resize(unsigned int w, unsigned int h)
@@ -4547,18 +4585,23 @@ void GLCanvas3D::_load_print_toolpaths()
     if (print_zs.size() > skirt_height)
         print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
 
-    m_volumes.volumes.emplace_back(new GLVolume(color));
-    GLVolume& volume = *m_volumes.volumes.back();
-    for (size_t i = 0; i < skirt_height; ++i) {
-        volume.print_zs.push_back(print_zs[i]);
-        volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
-        volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
-        if (i == 0)
-            _3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), volume);
 
-        _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), volume);
+    GLVolume *volume = m_volumes.new_toolpath_volume(color, VERTEX_BUFFER_RESERVE_SIZE);
+    for (size_t i = 0; i < skirt_height; ++i) {
+        volume->print_zs.push_back(print_zs[i]);
+        volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
+        volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
+        if (i == 0)
+            _3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), *volume);
+        _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), *volume);
+        // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
+        if (volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
+        	GLVolume &vol = *volume;
+            volume = m_volumes.new_toolpath_volume(vol.color);
+            reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized);
+        }
     }
-    volume.indexed_vertex_array.finalize_geometry(m_initialized);
+    volume->indexed_vertex_array.finalize_geometry(m_initialized);
 }
 
 void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values)
@@ -4575,12 +4618,6 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
         const std::vector<float>*    tool_colors;
         const std::vector<double>*   color_print_values;
 
-        // Number of vertices (each vertex is 6x4=24 bytes long)
-        static const size_t          alloc_size_max() { return 131072; } // 3.15MB
-        //        static const size_t          alloc_size_max    () { return 65536; } // 1.57MB 
-        //        static const size_t          alloc_size_max    () { return 32768; } // 786kB
-        static const size_t          alloc_size_reserve() { return alloc_size_max() * 2; }
-
         static const float*          color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
         static const float*          color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
         static const float*          color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
@@ -4630,10 +4667,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
     size_t          grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
     tbb::spin_mutex new_volume_mutex;
     auto            new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
-        auto *volume = new GLVolume(color);
-        new_volume_mutex.lock();
+    	// Allocate the volume before locking.
+		GLVolume *volume = new GLVolume(color);
+		volume->is_extrusion_path = true;
+    	tbb::spin_mutex::scoped_lock lock;
+    	// Lock by ROII, so if the emplace_back() fails, the lock will be released.
+        lock.acquire(new_volume_mutex);
         m_volumes.volumes.emplace_back(volume);
-        new_volume_mutex.unlock();
+        lock.release();
         return volume;
     };
     const size_t    volumes_cnt_initial = m_volumes.volumes.size();
@@ -4671,7 +4712,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
         else
             vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
         for (GLVolume *vol : vols)
-        	vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+			// Reserving number of vertices (3x position + 3x color)
+        	vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6);
         for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
             const Layer *layer = ctxt.layers[idx_layer];
             for (GLVolume *vol : vols)
@@ -4715,18 +4757,9 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
             // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
 	        for (size_t i = 0; i < vols.size(); ++i) {
 	            GLVolume &vol = *vols[i];
-	            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
-	                // Store the vertex arrays and restart their containers, 
+	            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
 	                vols[i] = new_volume(vol.color);
-	                GLVolume &vol_new = *vols[i];
-	                // Assign the large pre-allocated buffers to the new GLVolume.
-	                vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
-	                // Copy the content back to the old GLVolume.
-	                vol.indexed_vertex_array = vol_new.indexed_vertex_array;
-                     // Clear the buffers, but keep them pre-allocated.
-	                vol_new.indexed_vertex_array.clear();
-	                // Just make sure that clear did not clear the reserved memory.
-	                vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+	                reserve_new_volume_finalize_old_volume(*vols[i], vol, false);
 	            }
 	        }
         }
@@ -4766,10 +4799,6 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
         Vec2f                        wipe_tower_pos;
         float                        wipe_tower_angle;
 
-        // Number of vertices (each vertex is 6x4=24 bytes long)
-        static const size_t          alloc_size_max() { return 131072; } // 3.15MB
-        static const size_t          alloc_size_reserve() { return alloc_size_max() * 2; }
-
         static const float*          color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
 
         // For cloring by a tool, return a parsed color.
@@ -4810,9 +4839,11 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
     tbb::spin_mutex new_volume_mutex;
     auto            new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
         auto *volume = new GLVolume(color);
-        new_volume_mutex.lock();
+		volume->is_extrusion_path = true;
+        tbb::spin_mutex::scoped_lock lock;
+        lock.acquire(new_volume_mutex);
         m_volumes.volumes.emplace_back(volume);
-        new_volume_mutex.unlock();
+        lock.release();
         return volume;
     };
     const size_t   volumes_cnt_initial = m_volumes.volumes.size();
@@ -4829,7 +4860,8 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
         else
             vols = { new_volume(ctxt.color_support()) };
         for (GLVolume *volume : vols)
-            volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+			// Reserving number of vertices (3x position + 3x color)
+            volume->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6);
         for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
             const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
             for (size_t i = 0; i < vols.size(); ++i) {
@@ -4886,18 +4918,9 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
         }
         for (size_t i = 0; i < vols.size(); ++i) {
             GLVolume &vol = *vols[i];
-            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
-                // Store the vertex arrays and restart their containers, 
+            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
                 vols[i] = new_volume(vol.color);
-                GLVolume &vol_new = *vols[i];
-                // Assign the large pre-allocated buffers to the new GLVolume.
-                vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
-                // Copy the content back to the old GLVolume.
-                vol.indexed_vertex_array = vol_new.indexed_vertex_array;
-                // Clear the buffers, but keep them pre-allocated.
-                vol_new.indexed_vertex_array.clear();
-                // Just make sure that clear did not clear the reserved memory.
-                vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+                reserve_new_volume_finalize_old_volume(*vols[i], vol, false);
             }
         }
         for (GLVolume *vol : vols)
@@ -5049,15 +5072,9 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
     for (Filter& filter : filters)
     {
         m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)m_volumes.volumes.size());
-        GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba);
-        if (volume != nullptr)
-        {
-            filter.volume = volume;
-            volume->is_extrusion_path = true;
-            m_volumes.volumes.emplace_back(volume);
-        }
-        else
-        {
+        try {
+            filter.volume = m_volumes.new_toolpath_volume(Helper::path_color(preview_data, tool_colors, filter.value).rgba, VERTEX_BUFFER_RESERVE_SIZE);
+        } catch (std::bad_alloc& /* err */) {
             // an error occourred - restore to previous state and return
             m_gcode_preview_volume_index.first_volumes.pop_back();
             if (initial_volumes_count != m_volumes.volumes.size())
@@ -5065,11 +5082,9 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
                 GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count;
                 GLVolumePtrs::iterator end = m_volumes.volumes.end();
                 for (GLVolumePtrs::iterator it = begin; it < end; ++it)
-                {
-                    GLVolume* volume = *it;
-                    delete volume;
-                }
+                    delete *it;
                 m_volumes.volumes.erase(begin, end);
+                //FIXME rethrow bad_alloc?
                 return;
             }
         }
@@ -5086,18 +5101,25 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
             FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role()));
             if (filter != filters.end())
             {
-                filter->volume->print_zs.push_back(layer.z);
-                filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size());
-                filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size());
+            	GLVolume &vol = *filter->volume;
+                vol.print_zs.push_back(layer.z);
+                vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+                vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
 
-                _3DScene::extrusionentity_to_verts(path, layer.z, *filter->volume);
+                _3DScene::extrusionentity_to_verts(path, layer.z, vol);
+
+	            // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
+	            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
+		            filter->volume = m_volumes.new_toolpath_volume(vol.color);
+	                reserve_new_volume_finalize_old_volume(*filter->volume, vol, m_initialized);
+	            }
             }
         }
     }
 
-    // finalize volumes and sends geometry to gpu
-    for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
-        m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
+    // Finalize volumes and sends geometry to gpu
+    for (Filter &filter : filters)
+    	filter.volume->indexed_vertex_array.finalize_geometry(m_initialized);
 
     BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - end" << m_volumes.log_memory_info() << log_memory_info();
 }
@@ -5135,19 +5157,12 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data,
             GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count;
             GLVolumePtrs::iterator end = m_volumes.volumes.end();
             for (GLVolumePtrs::iterator it = begin; it < end; ++it)
-            {
-                GLVolume* volume = *it;
-                delete volume;
-            }
+                delete *it;
             m_volumes.volumes.erase(begin, end);
         }
 
         return;
     }
-
-    // finalize volumes and sends geometry to gpu
-    for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
-		m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
 }
 
 bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
@@ -5188,16 +5203,7 @@ bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
 
     // creates a new volume for each type
     for (Type& type : types)
-    {
-        GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba);
-        if (volume == nullptr)
-            return false;
-        else
-        {
-            type.volume = volume;
-            m_volumes.volumes.emplace_back(volume);
-        }
-    }
+        type.volume = m_volumes.new_nontoolpath_volume(preview_data.travel.type_colors[type.value].rgba, VERTEX_BUFFER_RESERVE_SIZE);
 
     // populates volumes
     for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
@@ -5205,14 +5211,24 @@ bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
         TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type));
         if (type != types.end())
         {
-            type->volume->print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
-            type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size());
-            type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size());
+        	GLVolume &vol = *type->volume;
+            vol.print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
+            vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+            vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
 
-            _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume);
+            _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, vol);
+
+            // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
+            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
+	            type->volume = m_volumes.new_nontoolpath_volume(vol.color);
+                reserve_new_volume_finalize_old_volume(*type->volume, vol, m_initialized);
+            }
         }
     }
 
+    for (Type &type : types)
+    	type.volume->finalize_geometry(m_initialized);
+
     return true;
 }
 
@@ -5254,31 +5270,32 @@ bool GLCanvas3D::_travel_paths_by_feedrate(const GCodePreviewData& preview_data)
 
     // creates a new volume for each feedrate
     for (Feedrate& feedrate : feedrates)
-    {
-        GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba);
-        if (volume == nullptr)
-            return false;
-        else
-        {
-            feedrate.volume = volume;
-            m_volumes.volumes.emplace_back(volume);
-        }
-    }
+		feedrate.volume = m_volumes.new_nontoolpath_volume(preview_data.get_feedrate_color(feedrate.value).rgba, VERTEX_BUFFER_RESERVE_SIZE);
 
-    // populates volumes
+	// populates volumes
     for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
     {
         FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate));
         if (feedrate != feedrates.end())
         {
-            feedrate->volume->print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
-            feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size());
-            feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size());
+        	GLVolume &vol = *feedrate->volume;
+            vol.print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
+            vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+            vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
 
             _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume);
+
+            // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
+            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
+	            feedrate->volume = m_volumes.new_nontoolpath_volume(vol.color);
+                reserve_new_volume_finalize_old_volume(*feedrate->volume, vol, m_initialized);
+            }
         }
     }
 
+    for (Feedrate &feedrate : feedrates)
+    	feedrate.volume->finalize_geometry(m_initialized);
+
     return true;
 }
 
@@ -5322,17 +5339,8 @@ bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, con
     for (Tool& tool : tools)
     {
         // tool.value could be invalid (as it was with https://github.com/prusa3d/PrusaSlicer/issues/2179), we better check
-        if (tool.value >= tool_colors.size())
-            continue;
-
-        GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4);
-        if (volume == nullptr)
-            return false;
-        else
-        {
-            tool.volume = volume;
-            m_volumes.volumes.emplace_back(volume);
-        }
+        if (tool.value < tool_colors.size())
+			tool.volume = m_volumes.new_nontoolpath_volume(tool_colors.data() + tool.value * 4, VERTEX_BUFFER_RESERVE_SIZE);
     }
 
     // populates volumes
@@ -5341,73 +5349,27 @@ bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, con
         ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id));
         if (tool != tools.end() && tool->volume != nullptr)
         {
-            tool->volume->print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
-            tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size());
-            tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size());
+        	GLVolume &vol = *tool->volume;
+            vol.print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
+            vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+            vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
 
             _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume);
+
+            // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
+            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
+	            tool->volume = m_volumes.new_nontoolpath_volume(vol.color);
+                reserve_new_volume_finalize_old_volume(*tool->volume, vol, m_initialized);
+            }
         }
     }
 
+    for (Tool& tool : tools)
+    	tool.volume->finalize_geometry(m_initialized);
+
     return true;
 }
 
-void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data)
-{
-    m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)m_volumes.volumes.size());
-
-    // nothing to render, return
-    if (preview_data.retraction.positions.empty())
-        return;
-
-    GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba);
-    if (volume != nullptr)
-    {
-        m_volumes.volumes.emplace_back(volume);
-
-        GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions);
-        std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position(2) < p2.position(2); });
-
-        for (const GCodePreviewData::Retraction::Position& position : copy)
-        {
-            volume->print_zs.push_back(unscale<double>(position.position(2)));
-            volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
-            volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
-
-            _3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
-        }
-        volume->indexed_vertex_array.finalize_geometry(m_initialized);
-    }
-}
-
-void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data)
-{
-    m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)m_volumes.volumes.size());
-
-    // nothing to render, return
-    if (preview_data.unretraction.positions.empty())
-        return;
-
-    GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba);
-    if (volume != nullptr)
-    {
-        m_volumes.volumes.emplace_back(volume);
-
-        GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions);
-        std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position(2) < p2.position(2); });
-
-        for (const GCodePreviewData::Retraction::Position& position : copy)
-        {
-            volume->print_zs.push_back(unscale<double>(position.position(2)));
-            volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
-            volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
-
-            _3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
-        }
-        volume->indexed_vertex_array.finalize_geometry(m_initialized);
-    }
-}
-
 void GLCanvas3D::_load_fff_shells()
 {
     size_t initial_volumes_count = m_volumes.volumes.size();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index d23e29478..0c971518e 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -131,6 +131,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
 
 class GLCanvas3D
 {
+public:
     struct GCodePreviewVolumeIndex
     {
         enum EType
@@ -158,6 +159,7 @@ class GLCanvas3D
         void reset() { first_volumes.clear(); }
     };
 
+private:
     class LayersEditing
     {
     public:
@@ -720,10 +722,6 @@ private:
     bool _travel_paths_by_type(const GCodePreviewData& preview_data);
     bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data);
     bool _travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
-    // generates gcode retractions geometry
-    void _load_gcode_retractions(const GCodePreviewData& preview_data);
-    // generates gcode unretractions geometry
-    void _load_gcode_unretractions(const GCodePreviewData& preview_data);
     // generates objects and wipe tower geometry
     void _load_fff_shells();
     // Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.