From ec0c37913cdc2d6b4c47a34b7ab46deab786f3c9 Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Thu, 11 Jun 2026 15:33:14 +0800 Subject: [PATCH 1/5] [Feat]: render_optimize, add LODSystem to rendering --- .../i18n/zh_CN/Snapmaker_Orca_zh_CN.po | 6 + .../i18n/zh_TW/Snapmaker_Orca_zh_TW.po | 6 + src/libslic3r/AppConfig.cpp | 4 + src/libslic3r/MacUtils.hpp | 1 + src/libslic3r/MacUtils.mm | 12 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/3DScene.cpp | 363 +++++++++++++++++- src/slic3r/GUI/3DScene.hpp | 40 +- src/slic3r/GUI/GLCanvas3D.cpp | 31 +- src/slic3r/GUI/GLCanvas3D.hpp | 4 +- src/slic3r/GUI/Preferences.cpp | 5 + src/slic3r/Utils/CpuMemory.cpp | 36 ++ src/slic3r/Utils/CpuMemory.hpp | 18 + 13 files changed, 498 insertions(+), 30 deletions(-) create mode 100644 src/slic3r/Utils/CpuMemory.cpp create mode 100644 src/slic3r/Utils/CpuMemory.hpp diff --git a/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po b/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po index abef0c29816..7fbd5fc36fd 100644 --- a/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po +++ b/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po @@ -6436,6 +6436,12 @@ msgstr "反转鼠标缩放" msgid "If enabled, reverses the direction of zoom with mouse wheel." msgstr "如果启用,使用鼠标滚轮缩放的方向会反转。" +msgid "Improve rendering performance by LOD" +msgstr "通过LOD提升渲染性能" + +msgid "Improved rendering performance under the scene of multiple plates and many models. Takes effect after restart." +msgstr "在多盘和多模型场景下提升渲染性能。重启后生效。" + msgid "Orbit speed multiplier" msgstr "轨道速度倍数" diff --git a/localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po b/localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po index 5bf5cc3f5a8..128b3e57e87 100644 --- a/localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po +++ b/localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po @@ -6665,6 +6665,12 @@ msgstr "反轉滑鼠滾輪縮放方向" msgid "If enabled, reverses the direction of zoom with mouse wheel." msgstr "啟用後,改變滑鼠滾輪縮放方向。" +msgid "Improve rendering performance by LOD" +msgstr "透過LOD提升渲染效能" + +msgid "Improved rendering performance under the scene of multiple plates and many models. Takes effect after restart." +msgstr "在多盤和多模型場景下提升渲染效能。重啟後生效。" + msgid "Orbit speed multiplier" msgstr "視角旋轉速度倍率" diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 440ac33387c..3e13f1e3875 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -185,6 +185,10 @@ void AppConfig::set_defaults() if (get("use_perspective_camera").empty()) set_bool("use_perspective_camera", true); + // LOD (Level of Detail) rendering optimization - enabled by default + if (get("enable_lod").empty()) + set_bool("enable_lod", true); + if (get("auto_perspective").empty()) set_bool("auto_perspective", false); diff --git a/src/libslic3r/MacUtils.hpp b/src/libslic3r/MacUtils.hpp index 388baa11902..802cc1e4225 100644 --- a/src/libslic3r/MacUtils.hpp +++ b/src/libslic3r/MacUtils.hpp @@ -4,6 +4,7 @@ namespace Slic3r { bool is_macos_support_boost_add_file_log(); +bool IsMacVersion15(); } diff --git a/src/libslic3r/MacUtils.mm b/src/libslic3r/MacUtils.mm index bb9ecd1e463..93a1e211ef9 100644 --- a/src/libslic3r/MacUtils.mm +++ b/src/libslic3r/MacUtils.mm @@ -12,4 +12,16 @@ bool is_macos_support_boost_add_file_log() } } +bool IsMacVersion15() +{ + if (@available(macOS 15.0, *)) + { + return true; + } + else + { + return false; + } +} + }; // namespace Slic3r diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 86dcc060108..b79093fb900 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -562,6 +562,8 @@ set(SLIC3R_GUI_SOURCES Utils/bambu_networking.hpp Utils/Bonjour.cpp Utils/Bonjour.hpp + Utils/CpuMemory.cpp + Utils/CpuMemory.hpp Utils/CalibUtils.cpp Utils/CalibUtils.hpp Utils/ColorSpaceConvert.cpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 67c9709bbc2..364fa0c8fcc 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -22,6 +22,9 @@ #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Tesselate.hpp" #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/QuadricEdgeCollapse.hpp" +#include +#include #include #include @@ -104,6 +107,88 @@ Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA& colors) namespace Slic3r { +// LOD mesh sharing map: maps TriangleMesh* -> set of GLVolume* sharing the same mesh +// When multiple volumes reference the same TriangleMesh, LOD simplified models are shared +static std::map> g_meshVolumesMap; + +// LOD run-time constants +const unsigned char LOD_UPDATE_FREQUENCY = 20; +const float ZOOM_THRESHOLD = 0.3f; +// pixel thresholds for LOD screen-size evaluation +const Vec2i32 LOD_SCREEN_MIN = Vec2i32(150, 110); +const Vec2i32 LOD_SCREEN_MAX = Vec2i32(300, 200); +const int SUPER_LARGE_FACES = 500000; +const int LARGE_FACES = 100000; + +//QEM face threshold +const int INIT_FACE_LOW_COUNT = 200; +const int FINAL_FACE_LOW_COUNT = 1000; +const float QEM_FACE_RATIO = 0.5f; +const float AABB_RANGE_EPSILON = 1.0f; + +//QEM Middle Small max error threshold +const float MIDDLE_LOD_NORMAL_FACE_MAX_ERROR = 0.1f; +const float MIDDLE_LOD_SUPER_LARGE_FACE_MAX_ERROR = 0.08f; +const float MIDDLE_LOD_LARGE_FACE_MAX_ERROR = 0.05f; + +const float SMALL_LOD_NORMAL_FACE_MAX_ERROR = 0.5f; +const float SMALL_LOD_SUPER_LARGE_FACE_MAX_ERROR = 0.4f; +const float SMALL_LOD_LARGE_FACE_MAX_ERROR = 0.3f; + +// Cached camera state for LOD evaluation +float GLVolume::s_lastCameraZoomValue = 0.0f; +float GLVolume::s_curZoom = 1.0f; +Matrix4d GLVolume::s_curViewProjMatrix = Matrix4d::Identity(); +std::array GLVolume::s_curViewport = {0, 0, 0, 0}; + +// Project a 3D point to 2D screen coordinates using the view-projection matrix +static Vec2f CalcPtInScreen(const Vec3d& pt, const Matrix4d& viewProjMat, int windowWidth, int windowHeight) +{ + Vec4d point(pt.x(), pt.y(), pt.z(), 1.0); + Vec4d pointNDCSpace = viewProjMat * point; + Vec3d pointScreenSpace = Vec3d(pointNDCSpace.x(), pointNDCSpace.y(), pointNDCSpace.z()) / pointNDCSpace.w(); + float x = 0.5f * (1 + pointScreenSpace(0)) * windowWidth; + float y = 0.5f * (1 - pointScreenSpace(1)) * windowHeight; + return Vec2f(x, y); +} + +// Determine which LOD level to use based on the volume's bounding box screen-space size +static LODLevel CalcVolumeBoxInScreenBiggerThanThreshold(const BoundingBoxf3& worldAABB, const Matrix4d& viewProjMat, int windowWidth, int windowHeight) +{ + const Vec3d& min3d = worldAABB.min; + const Vec3d& max3d = worldAABB.max; + std::array srcVertices; + srcVertices[0] = min3d; + srcVertices[1] = Vec3d(max3d.x(), min3d.y(), min3d.z()); + srcVertices[2] = Vec3d(max3d.x(), max3d.y(), min3d.z()); + srcVertices[3] = Vec3d(min3d.x(), max3d.y(), min3d.z()); + srcVertices[4] = Vec3d(min3d.x(), min3d.y(), max3d.z()); + srcVertices[5] = Vec3d(max3d.x(), min3d.y(), max3d.z()); + srcVertices[6] = max3d; + srcVertices[7] = Vec3d(min3d.x(), max3d.y(), max3d.z()); + + BoundingBoxf box2d; + for (int i = 0; i < srcVertices.size(); i++) + { + Vec2f screenPt = CalcPtInScreen(srcVertices[i], viewProjMat, windowWidth, windowHeight); + box2d.merge(screenPt.cast()); + } + double sizeX = box2d.size().x(); + double sizeY = box2d.size().y(); + if (sizeX >= LOD_SCREEN_MAX.x() || sizeY >= LOD_SCREEN_MAX.y()) + { + return LODLevel::High; + } + if (sizeX <= LOD_SCREEN_MIN.x() && sizeY <= LOD_SCREEN_MIN.y()) + { + return LODLevel::Small; + } + else + { + return LODLevel::Middle; + } +} + const float GLVolume::SinkingContours::HalfWidth = 0.25f; void GLVolume::SinkingContours::render() @@ -223,6 +308,7 @@ GLVolume::GLVolume(float r, float g, float b, float a) , force_sinking_contours(false) , picking(false) , tverts_range(0, size_t(-1)) + , m_tvertsRangeLod(0, size_t(-1)) { color = {r, g, b, a}; set_render_color(color); @@ -310,6 +396,109 @@ ColorRGBA color_from_model_volume(const ModelVolume& model_volume) return color; } +bool GLVolume::SimplifyMesh(const TriangleMesh& mesh, std::shared_ptr model, LODLevel lod) const +{ + return SimplifyMesh(mesh.its, model, lod); +} + +bool GLVolume::SimplifyMesh(const indexed_triangle_set& its, std::shared_ptr model, LODLevel lod) const +{ + if (its.indices.size() == 0 || its.vertices.size() == 0) + { + return false; + } + + auto itsCopy = std::make_unique(its); + + float maxError = std::numeric_limits::max(); + if (lod == LODLevel::Middle) + { + maxError = MIDDLE_LOD_NORMAL_FACE_MAX_ERROR; + if (its.indices.size() > SUPER_LARGE_FACES) + { + maxError = MIDDLE_LOD_SUPER_LARGE_FACE_MAX_ERROR; + } + else if(its.indices.size() > LARGE_FACES) + { + maxError = MIDDLE_LOD_LARGE_FACE_MAX_ERROR; + } + } + if (lod == LODLevel::Small) + { + maxError = SMALL_LOD_NORMAL_FACE_MAX_ERROR; + if (its.indices.size() > SUPER_LARGE_FACES) + { + maxError = SMALL_LOD_SUPER_LARGE_FACE_MAX_ERROR; + } + else if(its.indices.size() > LARGE_FACES) + { + maxError = SMALL_LOD_LARGE_FACE_MAX_ERROR; + } + } + + TriangleMesh originMesh(*itsCopy); + + // Run simplification in background thread (async, detached) + // Ref: https://people.eecs.berkeley.edu/~jrs/meshpapers/GarlandHeckbert2.pdf + std::thread worker = std::thread( + [model, maxError, originMesh](std::unique_ptr itsPtr) { + int initFaceCount = itsPtr->indices.size(); + uint32_t triangleCount = 0; + float maxErrCopy = maxError; + + its_quadric_edge_collapse(*itsPtr, triangleCount, &maxErrCopy); + + // Validate simplification quality + int endFaceCount = (*itsPtr).indices.size(); + if (initFaceCount < INIT_FACE_LOW_COUNT || (initFaceCount < FINAL_FACE_LOW_COUNT && endFaceCount < initFaceCount * QEM_FACE_RATIO)) + { + BOOST_LOG_TRIVIAL(warning) << "LOD simplify: rejected (too few faces) init=" << initFaceCount << " end=" << endFaceCount; + return; + } + + TriangleMesh simplifiedMesh(*itsPtr); + Vec3f originMin = originMesh.stats().min - Vec3f(AABB_RANGE_EPSILON, AABB_RANGE_EPSILON, AABB_RANGE_EPSILON); + Vec3f originMax = originMesh.stats().max + Vec3f(AABB_RANGE_EPSILON, AABB_RANGE_EPSILON, AABB_RANGE_EPSILON); + + // Ensure simplified mesh stays within original bounding box + if (originMin.x() < simplifiedMesh.stats().min.x() && + originMin.y() < simplifiedMesh.stats().min.y() && + originMin.z() < simplifiedMesh.stats().min.z() && + originMax.x() > simplifiedMesh.stats().max.x() && + originMax.y() > simplifiedMesh.stats().max.y() && + originMax.z() > simplifiedMesh.stats().max.z()) { + if (model && model.use_count() >= 2) { + model->init_from(simplifiedMesh); + BOOST_LOG_TRIVIAL(warning) << "LOD simplify: completed successfully, faces=" << initFaceCount + << " -> " << endFaceCount + << " (use_count=" << model.use_count() << ")"; + } else { + BOOST_LOG_TRIVIAL(warning) << "LOD simplify: skipped init (use_count=" + << (model ? model.use_count() : 0) << ")"; + } + } else { + BOOST_LOG_TRIVIAL(warning) << "LOD simplify: rejected (out of AABB bounds)"; + } + + }, + std::move(itsCopy)); + + if (worker.joinable()) + { + worker.detach(); + } + return true; +} + +void GLVolume::set_bounding_boxes_as_dirty() +{ + // Force immediate LOD re-evaluation + m_lodUpdateIndex = LOD_UPDATE_FREQUENCY; + m_transformed_bounding_box.reset(); + m_transformed_convex_hull_bounding_box.reset(); + m_transformed_non_sinking_bounding_box.reset(); +} + Transform3d GLVolume::world_matrix() const { Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); @@ -470,10 +659,29 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size) glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0)); } glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - if (tverts_range == std::make_pair(0, -1)) - model.render(); - else - model.render(this->tverts_range); + { + // Use LOD model for depth pass if available (consistent with body rendering) + auto renderDepthModel = [this]() { + if (!picking) { + if (m_curLodLevel == LODLevel::Small && m_modelSmall && m_modelSmall->is_initialized()) { + m_modelSmall->set_color(render_color); + m_modelSmall->render(); + return; + } + if (m_curLodLevel == LODLevel::Middle && m_modelMiddle && m_modelMiddle->is_initialized()) { + m_modelMiddle->set_color(render_color); + m_modelMiddle->render(); + return; + } + } + model.set_color(render_color); + if (tverts_range == std::make_pair(0, -1)) + model.render(); + else + model.render(this->tverts_range); + }; + renderDepthModel(); + } glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); // 2nd. render pass, just a normal render with the depth buffer passed as a texture @@ -543,6 +751,9 @@ void GLVolume::simple_render(GLShaderProgram* shader, } } while (0); + // LOD evaluation is now done once per frame in GLVolumeCollection::render(). + // m_curLodLevel is already set before simple_render is called. + if (color_volume && !picking) { // when force_transparent, we need to keep the alpha if (force_native_color && render_color.is_transparent()) { @@ -593,10 +804,45 @@ void GLVolume::simple_render(GLShaderProgram* shader, m.render(this->tverts_range); } } else { - if (tverts_range == std::make_pair(0, -1)) - model.render(); - else - model.render(this->tverts_range); + // Select LOD model based on current LOD level + static int lodRenderLogCounter = 0; + lodRenderLogCounter++; + if (!picking) { + // DEBUG: color-code LOD levels for visual verification + // GREEN = HIGH (original), BLUE = MIDDLE, RED = SMALL + if (m_curLodLevel == LODLevel::Small && m_modelSmall && m_modelSmall->is_initialized()) { + if (lodRenderLogCounter % 180 == 0) + BOOST_LOG_TRIVIAL(warning) << "LOD: SMALL '" << name << "'"; + m_modelSmall->set_color(render_color); + m_modelSmall->set_color(ColorRGBA::GREEN()); + m_modelSmall->render(); + } else if (m_curLodLevel == LODLevel::Middle && m_modelMiddle && m_modelMiddle->is_initialized()) { + if (lodRenderLogCounter % 180 == 0) + BOOST_LOG_TRIVIAL(warning) << "LOD: MID '" << name << "'"; + m_modelMiddle->set_color(render_color); + m_modelMiddle->set_color(ColorRGBA::BLUE()); + m_modelMiddle->render(); + } else { + if (lodRenderLogCounter % 180 == 0) { + BOOST_LOG_TRIVIAL(warning) << "LOD: HIGH fallback '" << name + << "' lv=" << static_cast(m_curLodLevel) + << " s=" << (m_modelSmall ? (int)m_modelSmall->is_initialized() : -1) + << " m=" << (m_modelMiddle ? (int)m_modelMiddle->is_initialized() : -1); + } + // model.set_color() already called in render loop line 1301 + model.set_color(ColorRGBA::RED()); + if (tverts_range == std::make_pair(0, -1)) + model.render(); + else + model.render(this->tverts_range); + } + } else { + // Picking: always use full-resolution model + if (tverts_range == std::make_pair(0, -1)) + model.render(); + else + model.render(this->tverts_range); + } } if (this->is_left_handed()) glFrontFace(GL_CCW); @@ -660,13 +906,15 @@ std::vector GLVolumeCollection::load_object(const ModelObject* model_o const std::vector& instance_idxs, const std::string& color_by, bool opengl_initialized, - bool need_raycaster) + bool need_raycaster, + bool lodEnabled) { 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, - opengl_initialized, false, false, need_raycaster)); + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume( + model_object, obj_idx, volume_idx, instance_idx, color_by, + opengl_initialized, false, false, need_raycaster, lodEnabled)); return volumes_idx; } @@ -678,7 +926,8 @@ int GLVolumeCollection::load_object_volume(const ModelObject* model_object, bool opengl_initialized, bool in_assemble_view, bool use_loaded_id, - bool need_raycaster) + bool need_raycaster, + bool lodEnabled) { const ModelVolume* model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); @@ -686,20 +935,58 @@ int GLVolumeCollection::load_object_volume(const ModelObject* model_object, auto color = GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4]; color.a(model_volume->is_model_part() ? 0.7f : 0.4f); - std::shared_ptr mesh = model_volume->mesh_ptr(); + std::shared_ptr meshSharedPtr = model_volume->mesh_ptr(); + const TriangleMesh* meshPtr = meshSharedPtr.get(); this->volumes.emplace_back(new GLVolume(color)); GLVolume& v = *this->volumes.back(); v.set_color(color_from_model_volume(*model_volume)); v.name = model_volume->name; + // LOD mesh sharing: if another volume already loaded this mesh, reuse its LOD data + v.m_oriMesh = meshPtr; + bool needCreateMesh = true; + auto iter = g_meshVolumesMap.find(meshPtr); + if (iter != g_meshVolumesMap.end()) { + std::set& volumeSet = iter->second; + if (!volumeSet.empty()) { + GLVolume* firstVolume = *(volumeSet.begin()); + // Share LOD models via shared_ptr (ref-counted, safe GPU buffer sharing) + v.m_modelMiddle = firstVolume->m_modelMiddle; + v.m_modelSmall = firstVolume->m_modelSmall; + // Note: model (main mesh) is always created per-volume since it's a value type + // This avoids dangling GPU buffer issues when one volume is destroyed + } + volumeSet.emplace(&v); + } else { + std::set volumeSet; + volumeSet.emplace(&v); + g_meshVolumesMap.emplace(meshPtr, std::move(volumeSet)); + } + + // Always init the main model (GLModel is a value type, not shared) + const TriangleMesh& mesh = *meshPtr; #if ENABLE_SMOOTH_NORMALS v.model.init_from(mesh, true); #else - v.model.init_from(*mesh); + v.model.init_from(mesh); +#endif // ENABLE_SMOOTH_NORMALS + + // Generate LOD simplified models only once (shared via shared_ptr) + if (lodEnabled && !v.m_modelMiddle && !v.m_modelSmall) { + BOOST_LOG_TRIVIAL(warning) << "LOD: Creating simplified models for '" << v.name + << "' faces=" << mesh.its.indices.size(); + v.m_modelMiddle = std::make_shared(); + v.SimplifyMesh(mesh, v.m_modelMiddle, LODLevel::Middle); + + v.m_modelSmall = std::make_shared(); + v.SimplifyMesh(mesh, v.m_modelSmall, LODLevel::Small); + } else if (!lodEnabled) { + BOOST_LOG_TRIVIAL(warning) << "LOD: Disabled for '" << v.name << "'"; + } + if (need_raycaster) { - v.mesh_raycaster = std::make_unique(mesh); + v.mesh_raycaster = std::make_unique(meshSharedPtr); } -#endif // ENABLE_SMOOTH_NORMALS v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); if (model_volume->is_model_part()) { @@ -830,6 +1117,20 @@ GLVolume* GLVolumeCollection::new_nontoolpath_volume(const ColorRGBA& rgba) return out; } +void GLVolumeCollection::release_volume(GLVolume* volume) +{ + if (volume->m_oriMesh) { + auto iter = g_meshVolumesMap.find(volume->m_oriMesh); + if (iter != g_meshVolumesMap.end()) { + std::set& volumeSet = iter->second; + volumeSet.erase(volume); + if (volumeSet.empty()) { + g_meshVolumesMap.erase(iter); + } + } + } +} + GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, @@ -905,6 +1206,36 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, if (disable_cullface) glsafe(::glDisable(GL_CULL_FACE)); + // Set static camera state for LOD evaluation in GLVolume rendering + GLVolume::s_curZoom = camera.get_zoom(); + GLVolume::s_curViewProjMatrix = (projection_matrix.matrix() * view_matrix.matrix()).eval(); + GLVolume::s_curViewport = camera.get_viewport(); + + // Evaluate LOD level for each volume once per frame + float curZoom = GLVolume::s_curZoom; + bool shouldEvaluate = (std::abs(curZoom - GLVolume::s_lastCameraZoomValue) > ZOOM_THRESHOLD); + if (shouldEvaluate) + { + GLVolume::s_lastCameraZoomValue = curZoom; + } + for (GLVolumeWithIdAndZ& volume : to_render) + { + GLVolume* v = volume.first; + if (!v->picking && (shouldEvaluate || ++v->m_lodUpdateIndex >= LOD_UPDATE_FREQUENCY)) + { + v->m_lodUpdateIndex = 0; + LODLevel prevLod = v->m_curLodLevel; + v->m_curLodLevel = CalcVolumeBoxInScreenBiggerThanThreshold( + v->transformed_bounding_box(), GLVolume::s_curViewProjMatrix, + GLVolume::s_curViewport[2], GLVolume::s_curViewport[3]); + if (prevLod != v->m_curLodLevel) { + BOOST_LOG_TRIVIAL(warning) << "LOD level changed: " << static_cast(prevLod) + << " -> " << static_cast(v->m_curLodLevel) + << " (zoom=" << curZoom << ", name=" << v->name << ")"; + } + } + } + for (GLVolumeWithIdAndZ& volume : to_render) { //CPU Frustum culling auto _worldAABB = volume.first->transformed_bounding_box(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 68c6ace8c6b..7b15d36e8ce 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -63,6 +63,13 @@ using ModelObjectPtrs = std::vector; // Return appropriate color based on the ModelVolume. extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume); +// LOD (Level of Detail) rendering optimization +enum class LODLevel { + High, // Original full-resolution mesh + Middle, // Medium simplification + Small, // High simplification +}; + class GLVolume { public: std::string name; @@ -85,6 +92,12 @@ class GLVolume { static float explosion_ratio; static float last_explosion_ratio; + // Cached camera state for LOD evaluation (set before render) + static float s_lastCameraZoomValue; + static float s_curZoom; + static Matrix4d s_curViewProjMatrix; + static std::array s_curViewport; + enum EHoverState : unsigned char { HS_None, @@ -139,6 +152,15 @@ class GLVolume { // Color used to render this volume. ColorRGBA render_color; + // LOD (Level of Detail) rendering + // Each LOD level has its own simplified model; shared across volumes with the same mesh + mutable LODLevel m_curLodLevel{ LODLevel::High }; + mutable unsigned char m_lodUpdateIndex{ 0 }; + std::shared_ptr m_modelMiddle; + std::shared_ptr m_modelSmall; + const TriangleMesh* m_oriMesh{ nullptr }; + std::pair m_tvertsRangeLod; + struct CompositeID { CompositeID(int object_id, int volume_id, int instance_id) : object_id(object_id), volume_id(volume_id), instance_id(instance_id) {} CompositeID() : object_id(-1), volume_id(-1), instance_id(-1) {} @@ -334,11 +356,11 @@ class GLVolume { //BBS: add simple render function for thumbnail void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector& extruder_colors, bool ban_light =false); - void set_bounding_boxes_as_dirty() { - m_transformed_bounding_box.reset(); - m_transformed_convex_hull_bounding_box.reset(); - m_transformed_non_sinking_bounding_box.reset(); - } + // LOD mesh simplification (async, uses quadric edge collapse) + bool SimplifyMesh(const TriangleMesh& mesh, std::shared_ptr model, LODLevel lod) const; + bool SimplifyMesh(const indexed_triangle_set& its, std::shared_ptr model, LODLevel lod) const; + + void set_bounding_boxes_as_dirty(); bool is_sla_support() const; bool is_sla_pad() const; @@ -443,7 +465,8 @@ class GLVolumeCollection const std::vector &instance_idxs, const std::string &color_by, bool opengl_initialized, - bool need_raycaster = true); + bool need_raycaster = true, + bool lodEnabled = true); int load_object_volume( const ModelObject *model_object, @@ -454,7 +477,8 @@ class GLVolumeCollection bool opengl_initialized, bool in_assemble_view = false, bool use_loaded_id = false, - bool need_raycaster = true); + bool need_raycaster = true, + bool lodEnabled = true); // Load SLA auxiliary GLVolumes (for support trees or pad). void load_object_auxiliary( const SLAPrintObject *print_object, @@ -485,6 +509,8 @@ class GLVolumeCollection // Clear the geometry void clear() { for (auto *v : volumes) delete v; volumes.clear(); } + void release_volume(GLVolume* volume); + bool empty() const { return volumes.empty(); } void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 837889ac872..223990ed5f6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -34,7 +34,11 @@ #include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp" #include "slic3r/Utils/UndoRedo.hpp" +#include "slic3r/Utils/CpuMemory.hpp" #include "slic3r/Utils/MacDarkMode.hpp" +#ifdef __APPLE__ +#include "libslic3r/MacUtils.hpp" +#endif #include @@ -2302,22 +2306,22 @@ void GLCanvas3D::set_volumes_z_range(const std::array& range) m_volumes.set_range(range[0] - 1e-6, range[1] + 1e-6); } -std::vector GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs) +std::vector GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs, bool lodEnabled) { if (instance_idxs.empty()) { for (unsigned int i = 0; i < model_object.instances.size(); ++i) { instance_idxs.emplace_back(i); } } - return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_initialized); + return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_initialized, true, lodEnabled); } -std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) +std::vector GLCanvas3D::load_object(const Model& model, int obj_idx, bool lodEnabled) { if (0 <= obj_idx && obj_idx < (int)model.objects.size()) { const ModelObject* model_object = model.objects[obj_idx]; if (model_object != nullptr) - return load_object(*model_object, obj_idx, std::vector()); + return load_object(*model_object, obj_idx, std::vector(), lodEnabled); } return std::vector(); @@ -2596,6 +2600,23 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } m_volumes.volumes = std::move(glvolumes_new); + + // Determine if LOD rendering optimization should be enabled + bool enableLod = GUI::wxGetApp().app_config->get_bool("enable_lod"); + + // Disable LOD if free memory is less than 5GB + if (enableLod && CpuMemory::CurFreeMemoryLessThanSpecifySizeGb(LOD_FREE_MEMORY_SIZE)) + { + enableLod = false; + } + +#ifdef __APPLE__ + // Disable LOD on macOS 15 due to known rendering compatibility issues + if (Slic3r::is_mac_version_15()) { + enableLod = false; + } +#endif + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++ obj_idx) { const ModelObject &model_object = *m_model->objects[obj_idx]; for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) { @@ -2616,7 +2637,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh // later in this function. it->volume_idx = m_volumes.volumes.size(); - m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized, m_canvas_type == ECanvasType::CanvasAssembleView); + m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized, m_canvas_type == ECanvasType::CanvasAssembleView, false, true, enableLod); m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; } else { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index bd2df0bcec3..0529a8377b6 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -937,8 +937,8 @@ class GLCanvas3D std::vector& get_custom_gcode_per_print_z() { return m_gcode_viewer.get_custom_gcode_per_print_z(); } size_t get_gcode_extruders_count() { return m_gcode_viewer.get_extruders_count(); } - std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); - std::vector load_object(const Model& model, int obj_idx); + std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs, bool lodEnabled = true); + std::vector load_object(const Model& model, int obj_idx, bool lodEnabled = true); void mirror_selection(Axis axis); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 9b7d4b2bd37..0402475ff54 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1260,6 +1260,10 @@ wxWindow* PreferencesDialog::create_general_page() }); auto camera_orbit_mult = create_camera_orbit_mult_input(_L("Orbit speed multiplier"), page, _L("Multiplies the orbit speed for finer or coarser camera movement.")); + auto enableLodSettings = create_item_checkbox(_L("Improve rendering performance by LOD"), page, + _L("Improved rendering performance under the scene of multiple plates and many models. Takes effect after restart."), + 50, "enable_lod"); + auto item_show_splash_screen = create_item_checkbox(_L("Show splash screen"), page, _L("Show the splash screen during startup."), 50, "show_splash_screen"); auto item_hints = create_item_checkbox(_L("Show \"Tip of the day\" notification after start"), page, _L("If enabled, useful hints are displayed at startup."), 50, "show_hints"); @@ -1364,6 +1368,7 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(reverse_mouse_zoom, 0, wxTOP, FromDIP(3)); sizer_page->Add(allow_filament_temp_mixing, 0, wxTOP, FromDIP(3)); sizer_page->Add(camera_orbit_mult, 0, wxTOP, FromDIP(3)); + sizer_page->Add(enableLodSettings, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_show_splash_screen, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_calc_in_long_retract, 0, wxTOP, FromDIP(3)); diff --git a/src/slic3r/Utils/CpuMemory.cpp b/src/slic3r/Utils/CpuMemory.cpp new file mode 100644 index 00000000000..b677fd3a742 --- /dev/null +++ b/src/slic3r/Utils/CpuMemory.cpp @@ -0,0 +1,36 @@ +#include "CpuMemory.hpp" + +#ifdef _WIN32 +#include +#elif defined(__APPLE__) || defined(__linux__) +#include +#include +#endif + +namespace Slic3r { + +bool CpuMemory::CurFreeMemoryLessThanSpecifySizeGb(int sizeGb) +{ +#ifdef _WIN32 + MEMORYSTATUSEX memStatus; + memStatus.dwLength = sizeof(MEMORYSTATUSEX); + if (GlobalMemoryStatusEx(&memStatus)) { + unsigned long long freeMemGb = memStatus.ullAvailPhys / (1024ULL * 1024ULL * 1024ULL); + return freeMemGb < static_cast(sizeGb); + } + return false; // Can't determine, default to not blocking +#elif defined(__APPLE__) || defined(__linux__) + // Linux and macOS + long pages = sysconf(_SC_AVPHYS_PAGES); + long pageSize = sysconf(_SC_PAGESIZE); + if (pages > 0 && pageSize > 0) { + unsigned long long freeMemGb = (unsigned long long)pages * (unsigned long long)pageSize / (1024ULL * 1024ULL * 1024ULL); + return freeMemGb < static_cast(sizeGb); + } + return false; // Can't determine, default to not blocking +#else + return false; +#endif +} + +} // namespace Slic3r diff --git a/src/slic3r/Utils/CpuMemory.hpp b/src/slic3r/Utils/CpuMemory.hpp new file mode 100644 index 00000000000..dd350302d40 --- /dev/null +++ b/src/slic3r/Utils/CpuMemory.hpp @@ -0,0 +1,18 @@ +#ifndef slic3r_CpuMemory_hpp_ +#define slic3r_CpuMemory_hpp_ + +namespace Slic3r { + +// Minimum free memory required for LOD rendering (in GB) +#define LOD_FREE_MEMORY_SIZE 5 + +class CpuMemory +{ +public: + // Returns true if current free memory is less than the specified size in GB + static bool CurFreeMemoryLessThanSpecifySizeGb(int sizeGb); +}; + +} // namespace Slic3r + +#endif From 9e7a25ce80ba9e8ac2bf2dac8c0147339a936484 Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Thu, 16 Jul 2026 10:31:38 +0800 Subject: [PATCH 2/5] [Draft]: delete debug log code --- src/slic3r/GUI/3DScene.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 364fa0c8fcc..822ea541921 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -814,13 +814,13 @@ void GLVolume::simple_render(GLShaderProgram* shader, if (lodRenderLogCounter % 180 == 0) BOOST_LOG_TRIVIAL(warning) << "LOD: SMALL '" << name << "'"; m_modelSmall->set_color(render_color); - m_modelSmall->set_color(ColorRGBA::GREEN()); + //m_modelSmall->set_color(ColorRGBA::GREEN()); m_modelSmall->render(); } else if (m_curLodLevel == LODLevel::Middle && m_modelMiddle && m_modelMiddle->is_initialized()) { if (lodRenderLogCounter % 180 == 0) BOOST_LOG_TRIVIAL(warning) << "LOD: MID '" << name << "'"; m_modelMiddle->set_color(render_color); - m_modelMiddle->set_color(ColorRGBA::BLUE()); + //m_modelMiddle->set_color(ColorRGBA::BLUE()); m_modelMiddle->render(); } else { if (lodRenderLogCounter % 180 == 0) { @@ -830,7 +830,7 @@ void GLVolume::simple_render(GLShaderProgram* shader, << " m=" << (m_modelMiddle ? (int)m_modelMiddle->is_initialized() : -1); } // model.set_color() already called in render loop line 1301 - model.set_color(ColorRGBA::RED()); + //model.set_color(ColorRGBA::RED()); if (tverts_range == std::make_pair(0, -1)) model.render(); else From 930672e74e345ffa5cc17325a4f4248f852f5c73 Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Thu, 16 Jul 2026 11:13:11 +0800 Subject: [PATCH 3/5] [Refactor]: remove enable_lod preference, make LOD always-on Remove the "Improve rendering performance by LOD" preference item and its enable_lod config plumbing. LOD rendering is now always enabled, still gated by the low-memory and macOS-15 runtime guards. Co-Authored-By: Claude Opus 4.8 (1M context) --- localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po | 6 ------ localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po | 6 ------ src/libslic3r/AppConfig.cpp | 4 ---- src/slic3r/GUI/GLCanvas3D.cpp | 4 ++-- src/slic3r/GUI/Preferences.cpp | 5 ----- 5 files changed, 2 insertions(+), 23 deletions(-) diff --git a/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po b/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po index 7fbd5fc36fd..abef0c29816 100644 --- a/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po +++ b/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po @@ -6436,12 +6436,6 @@ msgstr "反转鼠标缩放" msgid "If enabled, reverses the direction of zoom with mouse wheel." msgstr "如果启用,使用鼠标滚轮缩放的方向会反转。" -msgid "Improve rendering performance by LOD" -msgstr "通过LOD提升渲染性能" - -msgid "Improved rendering performance under the scene of multiple plates and many models. Takes effect after restart." -msgstr "在多盘和多模型场景下提升渲染性能。重启后生效。" - msgid "Orbit speed multiplier" msgstr "轨道速度倍数" diff --git a/localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po b/localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po index 128b3e57e87..5bf5cc3f5a8 100644 --- a/localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po +++ b/localization/i18n/zh_TW/Snapmaker_Orca_zh_TW.po @@ -6665,12 +6665,6 @@ msgstr "反轉滑鼠滾輪縮放方向" msgid "If enabled, reverses the direction of zoom with mouse wheel." msgstr "啟用後,改變滑鼠滾輪縮放方向。" -msgid "Improve rendering performance by LOD" -msgstr "透過LOD提升渲染效能" - -msgid "Improved rendering performance under the scene of multiple plates and many models. Takes effect after restart." -msgstr "在多盤和多模型場景下提升渲染效能。重啟後生效。" - msgid "Orbit speed multiplier" msgstr "視角旋轉速度倍率" diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 3e13f1e3875..440ac33387c 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -185,10 +185,6 @@ void AppConfig::set_defaults() if (get("use_perspective_camera").empty()) set_bool("use_perspective_camera", true); - // LOD (Level of Detail) rendering optimization - enabled by default - if (get("enable_lod").empty()) - set_bool("enable_lod", true); - if (get("auto_perspective").empty()) set_bool("auto_perspective", false); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 223990ed5f6..d8cd2c12707 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2601,8 +2601,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } m_volumes.volumes = std::move(glvolumes_new); - // Determine if LOD rendering optimization should be enabled - bool enableLod = GUI::wxGetApp().app_config->get_bool("enable_lod"); + // LOD rendering optimization is always enabled + bool enableLod = true; // Disable LOD if free memory is less than 5GB if (enableLod && CpuMemory::CurFreeMemoryLessThanSpecifySizeGb(LOD_FREE_MEMORY_SIZE)) diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 0402475ff54..9b7d4b2bd37 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1260,10 +1260,6 @@ wxWindow* PreferencesDialog::create_general_page() }); auto camera_orbit_mult = create_camera_orbit_mult_input(_L("Orbit speed multiplier"), page, _L("Multiplies the orbit speed for finer or coarser camera movement.")); - auto enableLodSettings = create_item_checkbox(_L("Improve rendering performance by LOD"), page, - _L("Improved rendering performance under the scene of multiple plates and many models. Takes effect after restart."), - 50, "enable_lod"); - auto item_show_splash_screen = create_item_checkbox(_L("Show splash screen"), page, _L("Show the splash screen during startup."), 50, "show_splash_screen"); auto item_hints = create_item_checkbox(_L("Show \"Tip of the day\" notification after start"), page, _L("If enabled, useful hints are displayed at startup."), 50, "show_hints"); @@ -1368,7 +1364,6 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(reverse_mouse_zoom, 0, wxTOP, FromDIP(3)); sizer_page->Add(allow_filament_temp_mixing, 0, wxTOP, FromDIP(3)); sizer_page->Add(camera_orbit_mult, 0, wxTOP, FromDIP(3)); - sizer_page->Add(enableLodSettings, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_show_splash_screen, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_calc_in_long_retract, 0, wxTOP, FromDIP(3)); From 85638d04448f2de6f07c8f01472e455aaef47e3a Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Thu, 16 Jul 2026 14:22:43 +0800 Subject: [PATCH 4/5] [Refactor]: fix IsMacVersion15 PascalCase naming convention Co-Authored-By: Claude Opus 4.8 (1M context) --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d8cd2c12707..c221e320ed7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2612,7 +2612,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #ifdef __APPLE__ // Disable LOD on macOS 15 due to known rendering compatibility issues - if (Slic3r::is_mac_version_15()) { + if (Slic3r::IsMacVersion15()) { enableLod = false; } #endif From d316a240feaa5af2061bc8c877d95d760e7321f2 Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Thu, 16 Jul 2026 16:34:06 +0800 Subject: [PATCH 5/5] [Fix]: macOS build error in CpuMemory - use Mach API instead of Linux sysinfo sys/sysinfo.h is Linux-only and sysconf(_SC_AVPHYS_PAGES) is unsupported on macOS. Split the __APPLE__ branch out from __linux__ and query free memory via host_statistics64 (vm_statistics64) instead. --- src/slic3r/Utils/CpuMemory.cpp | 84 +++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/src/slic3r/Utils/CpuMemory.cpp b/src/slic3r/Utils/CpuMemory.cpp index b677fd3a742..06e82df2959 100644 --- a/src/slic3r/Utils/CpuMemory.cpp +++ b/src/slic3r/Utils/CpuMemory.cpp @@ -1,36 +1,78 @@ #include "CpuMemory.hpp" +namespace Slic3r { #ifdef _WIN32 #include -#elif defined(__APPLE__) || defined(__linux__) +unsigned long long GetFreeMemoryWin() +{ + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + return status.ullAvailPhys; +} +#endif + +#ifdef __linux__ #include #include +#elif __APPLE__ +#include +#include #endif +#if defined(__linux__) || defined(__APPLE__) +unsigned long long GetFreMemoryUnix() +{ +#ifdef __linux__ + struct sysinfo info; + if (sysinfo(&info) == 0) { + return info.freeram * info.mem_unit; + } +#elif __APPLE__ + int mib[2] = {CTL_HW, HW_MEMSIZE}; + uint64_t memsize; + size_t len = sizeof(memsize); + if (sysctl(mib, 2, &memsize, &len, NULL, 0) == 0) { + vm_size_t page_size; + mach_port_t mach_port; + mach_msg_type_number_t count; + vm_statistics64_data_t vm_stats; -namespace Slic3r { - -bool CpuMemory::CurFreeMemoryLessThanSpecifySizeGb(int sizeGb) + mach_port = mach_host_self(); + count = sizeof(vm_stats) / sizeof(natural_t); + if (host_page_size(mach_port, &page_size) == KERN_SUCCESS && host_statistics64(mach_port, HOST_VM_INFO, (host_info64_t) &vm_stats, &count) == KERN_SUCCESS) { + return (vm_stats.free_count + vm_stats.inactive_count) * page_size; + } + } +#endif + return 0; +} +#endif +unsigned long long get_free_memory() { #ifdef _WIN32 - MEMORYSTATUSEX memStatus; - memStatus.dwLength = sizeof(MEMORYSTATUSEX); - if (GlobalMemoryStatusEx(&memStatus)) { - unsigned long long freeMemGb = memStatus.ullAvailPhys / (1024ULL * 1024ULL * 1024ULL); - return freeMemGb < static_cast(sizeGb); + return GetFreeMemoryWin(); +#elif defined(__linux__) || defined(__APPLE__) + return GetFreMemoryUnix(); +#else + return 0; +#endif +} +bool CpuMemory::CurFreeMemoryLessThanSpecifySizeGb(int size) +{ + unsigned long long free_mem = get_free_memory(); + auto cur_size = free_mem / (1024.0 * 1024.0 * 1024.0); + static bool first_debug_free_memory = true; + static bool first_meet_size_gb = true; + if (first_debug_free_memory) { + first_debug_free_memory = false; } - return false; // Can't determine, default to not blocking -#elif defined(__APPLE__) || defined(__linux__) - // Linux and macOS - long pages = sysconf(_SC_AVPHYS_PAGES); - long pageSize = sysconf(_SC_PAGESIZE); - if (pages > 0 && pageSize > 0) { - unsigned long long freeMemGb = (unsigned long long)pages * (unsigned long long)pageSize / (1024ULL * 1024ULL * 1024ULL); - return freeMemGb < static_cast(sizeGb); + if (cur_size < size) { + if (first_meet_size_gb) { + first_meet_size_gb = false; + } + return true; } - return false; // Can't determine, default to not blocking -#else return false; -#endif } -} // namespace Slic3r +}