diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index bc3730026..21faffe53 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -561,11 +561,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ } if (print->config().remaining_times.value) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode"; + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode" << log_memory_info(); m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); m_normal_time_estimator.reset(); if (m_silent_time_estimator_enabled) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode"; + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode" << log_memory_info(); m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); m_silent_time_estimator.reset(); } @@ -573,7 +573,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ // starts analyzer calculations if (m_enable_analyzer) { - BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data"; + BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data" << log_memory_info(); m_analyzer.calc_gcode_preview_data(*preview_data, [print]() { print->throw_if_canceled(); }); m_analyzer.reset(); } @@ -1820,7 +1820,8 @@ void GCode::process_layer( ", time estimator memory: " << format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0) << ", analyzer memory: " << - format_memsize_MB(m_analyzer.memory_used()); + format_memsize_MB(m_analyzer.memory_used()) << + log_memory_info(); } void GCode::apply_print_config(const PrintConfig &print_config) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index dba595846..b9a79f3a9 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -12,6 +12,7 @@ #include "libslic3r/GCode/Analyzer.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "libslic3r/Format/STL.hpp" +#include "libslic3r/Utils.hpp" #include #include @@ -74,15 +75,19 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh) } } -void GLIndexedVertexArray::finalize_geometry() const +void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized) { assert(this->vertices_and_normals_interleaved_VBO_id == 0); assert(this->triangle_indices_VBO_id == 0); assert(this->quad_indices_VBO_id == 0); - this->shrink_to_fit(); + if (! opengl_initialized) { + // Shrink the data vectors to conserve memory in case the data cannot be transfered to the OpenGL driver yet. + this->shrink_to_fit(); + return; + } - if (! empty()) { + if (! this->vertices_and_normals_interleaved.empty()) { glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW)); @@ -124,13 +129,8 @@ void GLIndexedVertexArray::release_geometry() void GLIndexedVertexArray::render() const { - if (this->vertices_and_normals_interleaved_VBO_id == 0) - { - // sends data to gpu, if not done yet - finalize_geometry(); - if (this->vertices_and_normals_interleaved_VBO_id == 0) - return; - } + assert(this->vertices_and_normals_interleaved_VBO_id != 0); + assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); @@ -161,13 +161,8 @@ void GLIndexedVertexArray::render( const std::pair& tverts_range, const std::pair& qverts_range) const { - if (this->vertices_and_normals_interleaved_VBO_id == 0) - { - // sends data to gpu, if not done yet - finalize_geometry(); - if (this->vertices_and_normals_interleaved_VBO_id == 0) - return; - } + assert(this->vertices_and_normals_interleaved_VBO_id != 0); + assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0); // Render using the Vertex Buffer Objects. glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); @@ -415,30 +410,32 @@ bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == - bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposBasePool); } std::vector GLVolumeCollection::load_object( - const ModelObject* model_object, + const ModelObject *model_object, int obj_idx, - const std::vector& instance_idxs, - const std::string& color_by) + const std::vector &instance_idxs, + const std::string &color_by, + bool opengl_initialized) { std::vector volumes_idx; for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx) for (int instance_idx : instance_idxs) - volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by)); + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized)); return volumes_idx; } int GLVolumeCollection::load_object_volume( - const ModelObject* model_object, - int obj_idx, - int volume_idx, - int instance_idx, - const std::string& color_by) + const ModelObject *model_object, + int obj_idx, + int volume_idx, + int instance_idx, + const std::string &color_by, + bool opengl_initialized) { - const ModelVolume* model_volume = model_object->volumes[volume_idx]; - const int extruder_id = model_volume->extruder_id(); - const ModelInstance* instance = model_object->instances[instance_idx]; - const TriangleMesh& mesh = model_volume->mesh(); - float color[4]; + const ModelVolume *model_volume = model_object->volumes[volume_idx]; + const int extruder_id = model_volume->extruder_id(); + const ModelInstance *instance = model_object->instances[instance_idx]; + const TriangleMesh &mesh = model_volume->mesh(); + float color[4]; memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); /* if (model_volume->is_support_blocker()) { color[0] = 1.0f; @@ -455,6 +452,7 @@ int GLVolumeCollection::load_object_volume( GLVolume& v = *this->volumes.back(); v.set_color_from_model_volume(model_volume); v.indexed_vertex_array.load_mesh(mesh); + v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); if (model_volume->is_model_part()) { @@ -475,13 +473,14 @@ int GLVolumeCollection::load_object_volume( // This function produces volumes for multiple instances in a single shot, // as some object specific mesh conversions may be expensive. void GLVolumeCollection::load_object_auxiliary( - const SLAPrintObject* print_object, + const SLAPrintObject *print_object, int obj_idx, // pairs of const std::vector>& instances, SLAPrintObjectStep milestone, // Timestamp of the last change of the milestone - size_t timestamp) + size_t timestamp, + bool opengl_initialized) { assert(print_object->is_step_done(milestone)); Transform3d mesh_trafo_inv = print_object->trafo().inverse(); @@ -495,6 +494,7 @@ void GLVolumeCollection::load_object_auxiliary( this->volumes.emplace_back(new GLVolume((milestone == slaposBasePool) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); GLVolume& v = *this->volumes.back(); v.indexed_vertex_array.load_mesh(mesh); + v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); v.geometry_id = std::pair(timestamp, model_instance.id().id); // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. @@ -511,7 +511,7 @@ void GLVolumeCollection::load_object_auxiliary( } int GLVolumeCollection::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) + 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) { if (depth < 0.01f) return int(this->volumes.size() - 1); @@ -564,6 +564,7 @@ int GLVolumeCollection::load_wipe_tower_preview( this->volumes.emplace_back(new GLVolume(color)); GLVolume& v = *this->volumes.back(); v.indexed_vertex_array.load_mesh(mesh); + v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0); @@ -823,6 +824,27 @@ std::vector GLVolumeCollection::get_current_print_zs(bool active_only) c return print_zs; } +size_t GLVolumeCollection::cpu_memory_used() const +{ + size_t memsize = sizeof(*this) + this->volumes.capacity() * sizeof(GLVolume); + for (const GLVolume *volume : this->volumes) + memsize += volume->cpu_memory_used(); + return memsize; +} + +size_t GLVolumeCollection::gpu_memory_used() const +{ + size_t memsize = 0; + for (const GLVolume *volume : this->volumes) + memsize += volume->gpu_memory_used(); + return memsize; +} + +std::string GLVolumeCollection::log_memory_info() const +{ + return " (GLVolumeCollection RAM: " + format_memsize_MB(this->cpu_memory_used()) + " GPU: " + format_memsize_MB(this->gpu_memory_used()) + " Both: " + format_memsize_MB(this->gpu_memory_used()) + ")"; +} + // caller is responsible for supplying NO lines with zero length static void thick_lines_to_indexed_vertex_array( const Lines &lines, @@ -1598,6 +1620,7 @@ bool GLArrow::on_init() triangles.emplace_back(7, 13, 6); m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles)); + m_volume.indexed_vertex_array.finalize_geometry(true); return true; } @@ -1711,6 +1734,7 @@ bool GLCurvedArrow::on_init() triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1); m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles)); + m_volume.indexed_vertex_array.finalize_geometry(true); return true; } @@ -1737,6 +1761,7 @@ bool GLBed::on_init_from_file(const std::string& filename) m_filename = filename; m_volume.indexed_vertex_array.load_mesh(model.mesh()); + m_volume.indexed_vertex_array.finalize_geometry(true); float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f }; set_color(color, 4); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 8ae57eeae..3d89a3a5f 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -64,7 +64,7 @@ public: vertices_and_normals_interleaved_VBO_id(0), triangle_indices_VBO_id(0), quad_indices_VBO_id(0) - {} + { assert(! rhs.has_VBOs()); } GLIndexedVertexArray(GLIndexedVertexArray &&rhs) : vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)), triangle_indices(std::move(rhs.triangle_indices)), @@ -72,7 +72,7 @@ public: vertices_and_normals_interleaved_VBO_id(0), triangle_indices_VBO_id(0), quad_indices_VBO_id(0) - {} + { assert(! rhs.has_VBOs()); } ~GLIndexedVertexArray() { release_geometry(); } @@ -80,14 +80,17 @@ public: { assert(vertices_and_normals_interleaved_VBO_id == 0); assert(triangle_indices_VBO_id == 0); - assert(triangle_indices_VBO_id == 0); - this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved; - this->triangle_indices = rhs.triangle_indices; - this->quad_indices = rhs.quad_indices; - this->m_bounding_box = rhs.m_bounding_box; - vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size; - triangle_indices_size = rhs.triangle_indices_size; - quad_indices_size = rhs.quad_indices_size; + assert(quad_indices_VBO_id == 0); + assert(rhs.vertices_and_normals_interleaved == 0); + assert(rhs.triangle_indices_VBO_id == 0); + assert(rhs.quad_indices_VBO_id == 0); + this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved; + this->triangle_indices = rhs.triangle_indices; + this->quad_indices = rhs.quad_indices; + this->m_bounding_box = rhs.m_bounding_box; + this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size; + this->triangle_indices_size = rhs.triangle_indices_size; + this->quad_indices_size = rhs.quad_indices_size; return *this; } @@ -95,21 +98,24 @@ public: { assert(vertices_and_normals_interleaved_VBO_id == 0); assert(triangle_indices_VBO_id == 0); - assert(triangle_indices_VBO_id == 0); - this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved); - this->triangle_indices = std::move(rhs.triangle_indices); - this->quad_indices = std::move(rhs.quad_indices); - this->m_bounding_box = std::move(rhs.m_bounding_box); - vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size; - triangle_indices_size = rhs.triangle_indices_size; - quad_indices_size = rhs.quad_indices_size; + assert(quad_indices_VBO_id == 0); + assert(rhs.vertices_and_normals_interleaved == 0); + assert(rhs.triangle_indices_VBO_id == 0); + assert(rhs.quad_indices_VBO_id == 0); + this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved); + this->triangle_indices = std::move(rhs.triangle_indices); + this->quad_indices = std::move(rhs.quad_indices); + this->m_bounding_box = std::move(rhs.m_bounding_box); + this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size; + this->triangle_indices_size = rhs.triangle_indices_size; + this->quad_indices_size = rhs.quad_indices_size; return *this; } // Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x) - mutable std::vector vertices_and_normals_interleaved; - mutable std::vector triangle_indices; - mutable std::vector quad_indices; + std::vector vertices_and_normals_interleaved; + std::vector triangle_indices; + std::vector quad_indices; // When the geometry data is loaded into the graphics card as Vertex Buffer Objects, // the above mentioned std::vectors are cleared and the following variables keep their original length. @@ -119,9 +125,9 @@ public: // IDs of the Vertex Array Objects, into which the geometry has been loaded. // Zero if the VBOs are not sent to GPU yet. - mutable unsigned int vertices_and_normals_interleaved_VBO_id{ 0 }; - mutable unsigned int triangle_indices_VBO_id{ 0 }; - mutable unsigned int quad_indices_VBO_id{ 0 }; + unsigned int vertices_and_normals_interleaved_VBO_id{ 0 }; + unsigned int triangle_indices_VBO_id{ 0 }; + unsigned int quad_indices_VBO_id{ 0 }; void load_mesh_full_shading(const TriangleMesh &mesh); void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); } @@ -141,12 +147,12 @@ public: if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity()) this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6)); - this->vertices_and_normals_interleaved.push_back(nx); - this->vertices_and_normals_interleaved.push_back(ny); - this->vertices_and_normals_interleaved.push_back(nz); - this->vertices_and_normals_interleaved.push_back(x); - this->vertices_and_normals_interleaved.push_back(y); - this->vertices_and_normals_interleaved.push_back(z); + this->vertices_and_normals_interleaved.emplace_back(nx); + this->vertices_and_normals_interleaved.emplace_back(ny); + this->vertices_and_normals_interleaved.emplace_back(nz); + this->vertices_and_normals_interleaved.emplace_back(x); + this->vertices_and_normals_interleaved.emplace_back(y); + this->vertices_and_normals_interleaved.emplace_back(z); this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size(); m_bounding_box.merge(Vec3f(x, y, z).cast()); @@ -167,9 +173,9 @@ public: if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity()) this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3)); - this->triangle_indices.push_back(idx1); - this->triangle_indices.push_back(idx2); - this->triangle_indices.push_back(idx3); + this->triangle_indices.emplace_back(idx1); + this->triangle_indices.emplace_back(idx2); + this->triangle_indices.emplace_back(idx3); this->triangle_indices_size = this->triangle_indices.size(); }; @@ -180,17 +186,17 @@ public: if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity()) this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4)); - this->quad_indices.push_back(idx1); - this->quad_indices.push_back(idx2); - this->quad_indices.push_back(idx3); - this->quad_indices.push_back(idx4); + this->quad_indices.emplace_back(idx1); + this->quad_indices.emplace_back(idx2); + this->quad_indices.emplace_back(idx3); + this->quad_indices.emplace_back(idx4); this->quad_indices_size = this->quad_indices.size(); }; // Finalize the initialization of the geometry & indices, // upload the geometry and indices to OpenGL VBO objects // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. - void finalize_geometry() const; + void finalize_geometry(bool opengl_initialized); // Release the geometry data, release OpenGL VBOs. void release_geometry(); @@ -211,7 +217,7 @@ public: } // Shrink the internal storage to tighly fit the data stored. - void shrink_to_fit() const { + void shrink_to_fit() { this->vertices_and_normals_interleaved.shrink_to_fit(); this->triangle_indices.shrink_to_fit(); this->quad_indices.shrink_to_fit(); @@ -219,6 +225,22 @@ public: const BoundingBoxf3& bounding_box() const { return m_bounding_box; } + // Return an estimate of the memory consumed by this class. + size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); } + // Return an estimate of the memory held by GPU vertex buffers. + size_t gpu_memory_used() const + { + size_t memsize = 0; + if (this->vertices_and_normals_interleaved_VBO_id != 0) + memsize += this->vertices_and_normals_interleaved_size * 4; + if (this->triangle_indices_VBO_id != 0) + memsize += this->triangle_indices_size * 4; + if (this->quad_indices_VBO_id != 0) + memsize += this->quad_indices_size * 4; + return memsize; + } + size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } + private: BoundingBoxf3 m_bounding_box; }; @@ -250,7 +272,7 @@ private: Geometry::Transformation m_volume_transformation; // Shift in z required by sla supports+pad - double m_sla_shift_z; + double m_sla_shift_z; // Bounding box of this volume, in unscaled coordinates. mutable BoundingBoxf3 m_transformed_bounding_box; // Whether or not is needed to recalculate the transformed bounding box. @@ -420,13 +442,22 @@ public: void render() const; void render(int color_id, int detection_id, int worldmatrix_id) const; - void finalize_geometry() { this->indexed_vertex_array.finalize_geometry(); } + void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); } void release_geometry() { this->indexed_vertex_array.release_geometry(); } void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } bool is_sla_support() const; bool is_sla_pad() const; + + // Return an estimate of the memory consumed by this class. + size_t cpu_memory_used() const { + //FIXME what to do wih m_convex_hull? + return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t); + } + // Return an estimate of the memory held by GPU vertex buffers. + size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); } + size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } }; typedef std::vector GLVolumePtrs; @@ -461,30 +492,33 @@ public: ~GLVolumeCollection() { clear(); }; std::vector load_object( - const ModelObject* model_object, + const ModelObject *model_object, int obj_idx, - const std::vector& instance_idxs, - const std::string& color_by); + const std::vector &instance_idxs, + const std::string &color_by, + bool opengl_initialized); int load_object_volume( - const ModelObject* model_object, - int obj_idx, - int volume_idx, - int instance_idx, - const std::string& color_by); + const ModelObject *model_object, + int obj_idx, + int volume_idx, + int instance_idx, + const std::string &color_by, + bool opengl_initialized); // Load SLA auxiliary GLVolumes (for support trees or pad). void load_object_auxiliary( - const SLAPrintObject* print_object, + const SLAPrintObject *print_object, int obj_idx, // pairs of const std::vector>& instances, SLAPrintObjectStep milestone, // Timestamp of the last change of the milestone - size_t timestamp); + size_t timestamp, + bool opengl_initialized); 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); + 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); // Render the volumes by OpenGL. void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function filter_func = std::function()) const; @@ -492,7 +526,7 @@ public: // Finalize the initialization of the geometry & indices, // upload the geometry and indices to OpenGL VBO objects // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. - void finalize_geometry() { for (auto* v : volumes) v->finalize_geometry(); } + void finalize_geometry(bool opengl_initialized) { for (auto* v : volumes) v->finalize_geometry(opengl_initialized); } // Release the geometry data assigned to the volumes. // If OpenGL VBOs were allocated, an OpenGL context has to be active to release them. void release_geometry() { for (auto *v : volumes) v->release_geometry(); } @@ -520,6 +554,14 @@ public: // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs(bool active_only) const; + // Return an estimate of the memory consumed by this class. + size_t cpu_memory_used() const; + // Return an estimate of the memory held by GPU vertex buffers. + size_t gpu_memory_used() const; + size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } + // Return CPU, GPU and total memory log line. + std::string log_memory_info() const; + private: GLVolumeCollection(const GLVolumeCollection &other); GLVolumeCollection& operator=(const GLVolumeCollection &); @@ -537,6 +579,7 @@ public: GLModel(); virtual ~GLModel(); + // init() / init_from_file() shall be called with the OpenGL context active! bool init() { return on_init(); } bool init_from_file(const std::string& filename) { return on_init_from_file(filename); } @@ -566,7 +609,7 @@ protected: class GLArrow : public GLModel { protected: - virtual bool on_init(); + bool on_init() override; }; class GLCurvedArrow : public GLModel @@ -577,13 +620,13 @@ public: explicit GLCurvedArrow(unsigned int resolution); protected: - virtual bool on_init(); + bool on_init() override; }; class GLBed : public GLModel { protected: - virtual bool on_init_from_file(const std::string& filename); + bool on_init_from_file(const std::string& filename) override; }; class _3DScene diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5e505bb41..ac3ac5d26 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1234,10 +1234,9 @@ bool GLCanvas3D::init() return false; } -// // on linux the gl context is not valid until the canvas is not shown on screen -// // we defer the geometry finalization of volumes until the first call to render() -// if (!m_volumes.empty()) -// m_volumes.finalize_geometry(); + // on linux the gl context is not valid until the canvas is not shown on screen + // we defer the geometry finalization of volumes until the first call to render() + m_volumes.finalize_geometry(true); if (m_gizmos.is_enabled() && !m_gizmos.init()) std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; @@ -1691,7 +1690,7 @@ std::vector GLCanvas3D::load_object(const ModelObject& model_object, int ob instance_idxs.push_back(i); } } - return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by); + return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_initialized); } std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -1879,7 +1878,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); if (it->new_geometry()) { // New volume. - m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by); + m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized); m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; } else { @@ -1952,7 +1951,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re for (size_t istep = 0; istep < sla_steps.size(); ++istep) if (!instances[istep].empty()) - m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); + m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); } // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed @@ -1992,7 +1991,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re depth = (900.f/w) * (float)(extruders_count - 1); int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), - brim_spacing * 4.5f); + brim_spacing * 4.5f, m_initialized); if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; } @@ -4511,6 +4510,7 @@ void GLCanvas3D::_load_print_toolpaths() _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), volume); } + volume.indexed_vertex_array.finalize_geometry(m_initialized); } void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors, const std::vector& color_print_values) @@ -4576,7 +4576,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); // Maximum size of an allocation block: 32MB / sizeof(float) - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start" << m_volumes.log_memory_info() << log_memory_info(); //FIXME Improve the heuristics for a grain size. size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); @@ -4682,16 +4682,22 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } } } + for (GLVolume *vol : vols) + // Ideally one would call vol->indexed_vertex_array.finalize() here to move the buffers to the OpenGL driver, + // but this code runs in parallel and the OpenGL driver is not thread safe. + vol->indexed_vertex_array.shrink_to_fit(); }); - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); // Remove empty volumes from the newly added volumes. m_volumes.volumes.erase( std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), [](const GLVolume *volume) { return volume->empty(); }), m_volumes.volumes.end()); + for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) + m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized); - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); } void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_tool_colors) @@ -4748,7 +4754,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI; ctxt.wipe_tower_pos = Vec2f(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value); - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; + BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start" << m_volumes.log_memory_info() << log_memory_info(); //FIXME Improve the heuristics for a grain size. size_t n_items = print->wipe_tower_data().tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); @@ -4846,16 +4852,20 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); } } + for (GLVolume *vol : vols) + vol->indexed_vertex_array.shrink_to_fit(); }); - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; + BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); // Remove empty volumes from the newly added volumes. m_volumes.volumes.erase( std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), [](const GLVolume *volume) { return volume->empty(); }), m_volumes.volumes.end()); + for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) + m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized); - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; + BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); } static inline int hex_digit_to_int(const char c) @@ -4868,6 +4878,8 @@ static inline int hex_digit_to_int(const char c) void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors) { + BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - start" << m_volumes.log_memory_info() << log_memory_info(); + // helper functions to select data in dependence of the extrusion view type struct Helper { @@ -4983,6 +4995,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat if (filters.empty()) return; + BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - create volumes" << m_volumes.log_memory_info() << log_memory_info(); + // creates a new volume for each filter for (Filter& filter : filters) { @@ -5013,6 +5027,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat } } + BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - populate volumes" << m_volumes.log_memory_info() << log_memory_info(); + // populates volumes for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) { @@ -5030,6 +5046,12 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat } } } + + // 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); + + BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - end" << m_volumes.log_memory_info() << log_memory_info(); } void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors) @@ -5074,6 +5096,10 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, 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) @@ -5302,6 +5328,7 @@ void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data) _3DScene::point3_to_verts(position.position, position.width, position.height, *volume); } + volume->indexed_vertex_array.finalize_geometry(m_initialized); } } @@ -5329,6 +5356,7 @@ void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data) _3DScene::point3_to_verts(position.position, position.width, position.height, *volume); } + volume->indexed_vertex_array.finalize_geometry(m_initialized); } } @@ -5354,7 +5382,7 @@ void GLCanvas3D::_load_fff_shells() instance_ids[i] = i; } - m_volumes.load_object(model_obj, object_id, instance_ids, "object"); + m_volumes.load_object(model_obj, object_id, instance_ids, "object", m_initialized); ++object_id; } @@ -5376,7 +5404,7 @@ void GLCanvas3D::_load_fff_shells() if (!print->is_step_done(psWipeTower)) depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1); m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, - !print->is_step_done(psWipeTower), brim_spacing * 4.5f); + !print->is_step_done(psWipeTower), brim_spacing * 4.5f, m_initialized); } } } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2488947bc..11d038b9d 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -100,6 +100,7 @@ void Selection::set_volumes(GLVolumePtrs* volumes) update_valid(); } +// Init shall be called from the OpenGL render function, so that the OpenGL context is initialized! bool Selection::init() { if (!m_arrow.init())