Let the User Leave Out Infill or Everything But the Shell at Rest Too

A plate of large objects can be slow to draw even when nothing is moving, and
the reductions built for dragging cannot help there because they skip layers.
So a second choice, applied at all times, leaves out the internal infill roles
or everything but the shell while drawing every layer. It is a third index
buffer filled by the same walk as the other two and bound whenever the dragging
set is not; the options need no counterpart since every layer is drawn. The
shell classification is shared, computed once when either choice asks for it.

On the 636-layer three-filament fixture at rest, against 448467 segments in
334 ms at full detail:

  internal infill left out        347208 segments  272 ms
  everything but the shell        215797 segments  181 ms

With the shell at rest and the shell at one layer in four while dragging, the
drag frame is 62 ms. At rest with the choice set to nothing, the preview is
pixel identical to before.
This commit is contained in:
Hanif Koh
2026-09-10 17:07:33 +08:00
parent 77d00c0ee4
commit e0275c4d69
9 changed files with 116 additions and 11 deletions

View File

@@ -212,6 +212,12 @@ void AppConfig::set_defaults()
set("preview_reduced_detail_mode", "no_infill");
}
{
const std::string mode = get("preview_rest_detail_mode");
if (mode != "full" && mode != "no_infill" && mode != "shell")
set("preview_rest_detail_mode", "full");
}
if (get("preview_reduced_detail_layer_stride").empty())
set("preview_reduced_detail_layer_stride", "4");
else {

View File

@@ -112,6 +112,12 @@ public:
void set_reduced_detail_mode(EReducedDetailMode mode);
uint32_t get_reduced_detail_layer_stride() const;
void set_reduced_detail_layer_stride(uint32_t value);
//
// ORCA: what is left out even at rest, with every layer drawn. Off draws everything.
// EReducedDetailMode::LayersOnly means the same as Off here.
//
EReducedDetailMode get_rest_detail_mode() const;
void set_rest_detail_mode(EReducedDetailMode mode);
float get_dim_previous_layers_brightness() const;
void set_dim_previous_layers_brightness(float value);
//

View File

@@ -33,6 +33,9 @@ struct Settings
bool reduced_detail{ false };
EReducedDetailMode reduced_detail_mode{ EReducedDetailMode::Off };
uint32_t reduced_detail_layer_stride{ 4 };
// ORCA: what is left out even at rest, with every layer drawn. Bound whenever the reduced set
// above is not. Off draws everything.
EReducedDetailMode rest_detail_mode{ EReducedDetailMode::Off };
//
// Required update flags
//

View File

@@ -107,6 +107,16 @@ void Viewer::set_reduced_detail_layer_stride(uint32_t value)
m_impl->set_reduced_detail_layer_stride(value);
}
EReducedDetailMode Viewer::get_rest_detail_mode() const
{
return m_impl->get_rest_detail_mode();
}
void Viewer::set_rest_detail_mode(EReducedDetailMode mode)
{
m_impl->set_rest_detail_mode(mode);
}
void Viewer::set_dim_previous_layers(bool value)
{
m_impl->set_dim_previous_layers(value);

View File

@@ -896,10 +896,13 @@ void ViewerImpl::reset()
m_enabled_options_count = 0;
m_enabled_segments_reduced_count = 0;
m_enabled_options_reduced_count = 0;
m_enabled_segments_rest_count = 0;
m_shell_bitset = BitSet<>();
m_settings_used_for_ranges = std::nullopt;
delete_textures(m_enabled_segments_rest_tex_id);
delete_buffers(m_enabled_segments_rest_buf_id);
delete_textures(m_enabled_options_reduced_tex_id);
delete_buffers(m_enabled_options_reduced_buf_id);
delete_textures(m_enabled_segments_reduced_tex_id);
@@ -1162,6 +1165,11 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
glsafe(glGenTextures(1, &m_enabled_options_reduced_tex_id));
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_reduced_tex_id));
glsafe(glGenBuffers(1, &m_enabled_segments_rest_buf_id));
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_rest_buf_id));
glsafe(glGenTextures(1, &m_enabled_segments_rest_tex_id));
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_rest_tex_id));
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
#endif // ENABLE_OPENGL_ES
@@ -1182,9 +1190,9 @@ static bool is_interior_infill(EGCodeExtrusionRole role)
role == EGCodeExtrusionRole::InternalBridgeInfill;
}
bool ViewerImpl::reduced_set_keeps(size_t i, const PathVertex& v) const
bool ViewerImpl::reduced_set_keeps(EReducedDetailMode mode, size_t i, const PathVertex& v) const
{
switch (m_settings.reduced_detail_mode) {
switch (mode) {
case EReducedDetailMode::NoInternalInfill: return !is_interior_infill(v.role);
case EReducedDetailMode::ShellOnly: return m_shell_bitset[i];
default: return true;
@@ -1434,14 +1442,17 @@ void ViewerImpl::update_enabled_entities()
#ifndef ENABLE_OPENGL_ES
// ORCA: the reduced sets are filled by the same walk, so switching to them costs no rebuild.
const bool build_reduced = m_settings.reduced_detail_mode != EReducedDetailMode::Off;
const bool build_rest = build_rest_set();
std::vector<uint32_t> enabled_segments_reduced;
std::vector<uint32_t> enabled_options_reduced;
std::vector<uint32_t> enabled_segments_rest;
const uint32_t layer_stride = std::max<uint32_t>(1, m_settings.reduced_detail_layer_stride);
// Whatever else is dropped, both ends of the visible layer range are kept whole: the top is the
// surface the user is looking at, and the only layer drawn at full color in top-layer-only
// mode; the bottom is exposed whenever the range is cut short.
const Interval& layers_range = m_layers.get_view_range();
if (build_reduced && m_settings.reduced_detail_mode == EReducedDetailMode::ShellOnly &&
if (((build_reduced && m_settings.reduced_detail_mode == EReducedDetailMode::ShellOnly) ||
(build_rest && m_settings.rest_detail_mode == EReducedDetailMode::ShellOnly)) &&
m_shell_bitset.size != m_vertices.size())
update_shell_bitset();
#endif // ENABLE_OPENGL_ES
@@ -1494,14 +1505,17 @@ void ViewerImpl::update_enabled_entities()
enabled_segments.push_back(static_cast<uint32_t>(i));
#ifndef ENABLE_OPENGL_ES
const bool whole_layer = v.layer_id == layers_range[0] || v.layer_id == layers_range[1];
const bool keep_anyway = whole_layer || !v.is_extrusion();
if (build_rest && !v.is_option() && (keep_anyway || reduced_set_keeps(m_settings.rest_detail_mode, i, v)))
enabled_segments_rest.push_back(static_cast<uint32_t>(i));
if (!build_reduced)
continue;
const bool whole_layer = v.layer_id == layers_range[0] || v.layer_id == layers_range[1];
if (!whole_layer && (v.layer_id % layer_stride) != 0)
continue;
if (v.is_option())
enabled_options_reduced.push_back(static_cast<uint32_t>(i));
else if (whole_layer || !v.is_extrusion() || reduced_set_keeps(i, v))
else if (keep_anyway || reduced_set_keeps(m_settings.reduced_detail_mode, i, v))
enabled_segments_reduced.push_back(static_cast<uint32_t>(i));
#endif // ENABLE_OPENGL_ES
}
@@ -1534,6 +1548,14 @@ void ViewerImpl::update_enabled_entities()
m_enabled_segments_reduced_count = enabled_segments_reduced.size();
m_enabled_options_reduced_count = enabled_options_reduced.size();
m_enabled_segments_rest_count = enabled_segments_rest.size();
if (build_rest) {
assert(m_enabled_segments_rest_buf_id > 0);
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_rest_buf_id));
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_segments_rest.size() * sizeof(uint32_t),
enabled_segments_rest.empty() ? nullptr : enabled_segments_rest.data(), GL_STATIC_DRAW));
}
if (build_reduced) {
assert(m_enabled_segments_reduced_buf_id > 0);
@@ -1744,6 +1766,14 @@ void ViewerImpl::set_reduced_detail_layer_stride(uint32_t value)
m_settings.update_enabled_entities = true;
}
void ViewerImpl::set_rest_detail_mode(EReducedDetailMode mode)
{
if (m_settings.rest_detail_mode == mode)
return;
m_settings.rest_detail_mode = mode;
m_settings.update_enabled_entities = true;
}
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
void ViewerImpl::set_dim_previous_layers(bool value)
{

View File

@@ -101,6 +101,8 @@ public:
void set_reduced_detail_mode(EReducedDetailMode mode);
uint32_t get_reduced_detail_layer_stride() const { return m_settings.reduced_detail_layer_stride; }
void set_reduced_detail_layer_stride(uint32_t value);
EReducedDetailMode get_rest_detail_mode() const { return m_settings.rest_detail_mode; }
void set_rest_detail_mode(EReducedDetailMode mode);
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
void set_dim_previous_layers_brightness(float value);
@@ -507,6 +509,13 @@ private:
unsigned int m_enabled_options_reduced_tex_id{ 0 };
size_t m_enabled_options_reduced_count{ 0 };
//
// OpenGL buffer to store the segments drawn at rest while Settings::rest_detail_mode is not Off.
// Every layer is drawn at rest, so the options are the full set and need no counterpart.
//
unsigned int m_enabled_segments_rest_buf_id{ 0 };
unsigned int m_enabled_segments_rest_tex_id{ 0 };
size_t m_enabled_segments_rest_count{ 0 };
//
// Caches for size of data sent to gpu, in bytes
//
size_t m_positions_tex_size{ 0 };
@@ -516,18 +525,22 @@ private:
size_t m_enabled_options_tex_size{ 0 };
// The set the next draw reads from: the reduced one only while the user is dragging, and only
// if a reduced set is being built at all.
// if a reduced set is being built at all; otherwise the rest set, if one is being built.
bool use_reduced_set() const {
return m_settings.reduced_detail && m_settings.reduced_detail_mode != EReducedDetailMode::Off;
}
bool build_rest_set() const {
return m_settings.rest_detail_mode != EReducedDetailMode::Off && m_settings.rest_detail_mode != EReducedDetailMode::LayersOnly;
}
bool use_rest_set() const { return !use_reduced_set() && build_rest_set(); }
size_t active_segments_count() const {
return use_reduced_set() ? m_enabled_segments_reduced_count : m_enabled_segments_count;
return use_reduced_set() ? m_enabled_segments_reduced_count : use_rest_set() ? m_enabled_segments_rest_count : m_enabled_segments_count;
}
unsigned int active_segments_buf_id() const {
return use_reduced_set() ? m_enabled_segments_reduced_buf_id : m_enabled_segments_buf_id;
return use_reduced_set() ? m_enabled_segments_reduced_buf_id : use_rest_set() ? m_enabled_segments_rest_buf_id : m_enabled_segments_buf_id;
}
unsigned int active_segments_tex_id() const {
return use_reduced_set() ? m_enabled_segments_reduced_tex_id : m_enabled_segments_tex_id;
return use_reduced_set() ? m_enabled_segments_reduced_tex_id : use_rest_set() ? m_enabled_segments_rest_tex_id : m_enabled_segments_tex_id;
}
size_t active_options_count() const {
return use_reduced_set() ? m_enabled_options_reduced_count : m_enabled_options_count;
@@ -539,8 +552,8 @@ private:
return use_reduced_set() ? m_enabled_options_reduced_tex_id : m_enabled_options_tex_id;
}
// Whether the segment starting at vertex i belongs to the reduced set under the current mode
bool reduced_set_keeps(size_t i, const PathVertex& v) const;
// Whether the segment starting at vertex i belongs to the set built under the given mode
bool reduced_set_keeps(EReducedDetailMode mode, size_t i, const PathVertex& v) const;
void update_shell_bitset();
#endif // ENABLE_OPENGL_ES

View File

@@ -1176,6 +1176,7 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
m_reduced_detail_while_dragging = get_app_config()->get_bool("preview_reduced_detail_while_dragging");
m_reduced_detail_mode = reduced_detail_mode_from_string(get_app_config()->get("preview_reduced_detail_mode"));
m_reduced_detail_layer_stride = static_cast<unsigned int>(std::max(1, std::stoi(get_app_config()->get("preview_reduced_detail_layer_stride"))));
m_rest_detail_mode = reduced_detail_mode_from_string(get_app_config()->get("preview_rest_detail_mode"));
apply_reduced_detail_settings();
// ORCA: darken the layers the preview layer slider is not scrubbed to
@@ -1924,6 +1925,13 @@ void GCodeViewer::apply_reduced_detail_settings()
{
m_viewer.set_reduced_detail_mode(m_reduced_detail_while_dragging ? m_reduced_detail_mode : libvgcode::EReducedDetailMode::Off);
m_viewer.set_reduced_detail_layer_stride(m_reduced_detail_layer_stride);
m_viewer.set_rest_detail_mode(m_rest_detail_mode);
}
void GCodeViewer::set_rest_detail_mode(const std::string& mode)
{
m_rest_detail_mode = reduced_detail_mode_from_string(mode);
apply_reduced_detail_settings();
}
void GCodeViewer::set_reduced_detail_while_dragging(bool value)
@@ -1946,6 +1954,8 @@ void GCodeViewer::set_reduced_detail_layer_stride(unsigned int value)
libvgcode::EReducedDetailMode GCodeViewer::reduced_detail_mode_from_string(const std::string& mode)
{
if (mode == "full")
return libvgcode::EReducedDetailMode::Off;
if (mode == "layers")
return libvgcode::EReducedDetailMode::LayersOnly;
if (mode == "shell")

View File

@@ -234,6 +234,7 @@ private:
bool m_reduced_detail_while_dragging{ false };
libvgcode::EReducedDetailMode m_reduced_detail_mode{ libvgcode::EReducedDetailMode::NoInternalInfill };
unsigned int m_reduced_detail_layer_stride{ 4 };
libvgcode::EReducedDetailMode m_rest_detail_mode{ libvgcode::EReducedDetailMode::Off };
void apply_reduced_detail_settings();
float m_legend_height;
@@ -352,6 +353,8 @@ public:
// the preference's string value: "layers", "no_infill" or "shell"
void set_reduced_detail_mode(const std::string& mode);
void set_reduced_detail_layer_stride(unsigned int value);
// what is left out even at rest: "full", "no_infill" or "shell"
void set_rest_detail_mode(const std::string& mode);
static libvgcode::EReducedDetailMode reduced_detail_mode_from_string(const std::string& mode);
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);

View File

@@ -2016,6 +2016,30 @@ void PreferencesDialog::create_items()
);
g_sizer->Add(item_reduced_detail_layer_stride);
auto item_rest_detail_mode = create_item_combobox(
_L("Always leave out"),
_L("What the preview leaves out at all times, dragging or not, with every layer drawn. "
"Use it when a plate of large objects is slow to draw even when the view is not moving.\n"
"Nothing: the full preview.\n"
"Internal infill: sparse and solid infill hidden inside the walls is left out.\n"
"Everything but the shell: only the toolpaths on the visible surface of the print are drawn. "
"Holes narrower than 5 mm are treated as solid."),
"preview_rest_detail_mode",
{_L("Nothing"), _L("Internal infill"), _L("Everything but the shell")},
{"full", "no_infill", "shell"},
// ORCA: apply the new mode immediately to the currently loaded preview
[](std::string value) {
if (Plater* plater = wxGetApp().plater()) {
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
canvas->get_gcode_viewer().set_rest_detail_mode(value);
canvas->set_as_dirty();
canvas->request_extra_frame();
}
}
}
);
g_sizer->Add(item_rest_detail_mode);
auto item_dim_previous_layers = create_item_checkbox(
_L("Dim lower layers"),
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),