Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libslic3r/MacUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Slic3r {

bool is_macos_support_boost_add_file_log();
bool IsMacVersion15();

}

Expand Down
12 changes: 12 additions & 0 deletions src/libslic3r/MacUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions src/slic3r/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
363 changes: 347 additions & 16 deletions src/slic3r/GUI/3DScene.cpp

Large diffs are not rendered by default.

40 changes: 33 additions & 7 deletions src/slic3r/GUI/3DScene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ using ModelObjectPtrs = std::vector<ModelObject*>;
// 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;
Expand All @@ -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<int, 4> s_curViewport;

enum EHoverState : unsigned char
{
HS_None,
Expand Down Expand Up @@ -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<GUI::GLModel> m_modelMiddle;
std::shared_ptr<GUI::GLModel> m_modelSmall;
const TriangleMesh* m_oriMesh{ nullptr };
std::pair<size_t, size_t> 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) {}
Expand Down Expand Up @@ -334,11 +356,11 @@ class GLVolume {
//BBS: add simple render function for thumbnail
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& 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<GUI::GLModel> model, LODLevel lod) const;
bool SimplifyMesh(const indexed_triangle_set& its, std::shared_ptr<GUI::GLModel> model, LODLevel lod) const;

void set_bounding_boxes_as_dirty();

bool is_sla_support() const;
bool is_sla_pad() const;
Expand Down Expand Up @@ -443,7 +465,8 @@ class GLVolumeCollection
const std::vector<int> &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,
Expand All @@ -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,
Expand Down Expand Up @@ -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); }

Expand Down
31 changes: 26 additions & 5 deletions src/slic3r/GUI/GLCanvas3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <slic3r/GUI/GUI_Utils.hpp>

Expand Down Expand Up @@ -2302,22 +2306,22 @@ void GLCanvas3D::set_volumes_z_range(const std::array<double, 2>& range)
m_volumes.set_range(range[0] - 1e-6, range[1] + 1e-6);
}

std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs)
std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector<int> 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<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
std::vector<int> 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<int>());
return load_object(*model_object, obj_idx, std::vector<int>(), lodEnabled);
}

return std::vector<int>();
Expand Down Expand Up @@ -2596,6 +2600,23 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
}
}
m_volumes.volumes = std::move(glvolumes_new);

// 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))
{
enableLod = false;
}

#ifdef __APPLE__
// Disable LOD on macOS 15 due to known rendering compatibility issues
if (Slic3r::IsMacVersion15()) {
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) {
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions src/slic3r/GUI/GLCanvas3D.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -937,8 +937,8 @@ class GLCanvas3D
std::vector<CustomGCode::Item>& 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<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
std::vector<int> load_object(const Model& model, int obj_idx);
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs, bool lodEnabled = true);
std::vector<int> load_object(const Model& model, int obj_idx, bool lodEnabled = true);

void mirror_selection(Axis axis);

Expand Down
78 changes: 78 additions & 0 deletions src/slic3r/Utils/CpuMemory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "CpuMemory.hpp"

namespace Slic3r {
#ifdef _WIN32
#include <windows.h>
unsigned long long GetFreeMemoryWin()
{
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
return status.ullAvailPhys;
}
#endif

#ifdef __linux__
#include <unistd.h>
#include <sys/sysinfo.h>
#elif __APPLE__
#include <mach/mach_host.h>
#include <sys/sysctl.h>
#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;

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
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;
}
if (cur_size < size) {
if (first_meet_size_gb) {
first_meet_size_gb = false;
}
return true;
}
return false;
}

}
18 changes: 18 additions & 0 deletions src/slic3r/Utils/CpuMemory.hpp
Original file line number Diff line number Diff line change
@@ -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
Loading