Draw the Preview From a Reduced Toolpath Set While the User Is Dragging

The preview's frame cost is linear in the number of visible segments and nothing
else: at 46 % of the layers the frame is 45 % of the time. A tall wipe tower puts
enough segments on screen that dragging the camera or a slider cannot keep up, and
no amount of shaving per-segment work fixes that - only drawing fewer segments does.

Everything cheaper was measured first and ruled out. Of the remaining frame, 31 % is
vertex shading and 69 % is triangle setup, so both halves scale with segment count.
Screen-space decimation is useless here: the median extrusion segment is 2.5 mm and
only 0.4 % are sub-pixel at full-plate zoom, so a threshold small enough to be
invisible removes nothing. Merging collinear runs is no better - 2.6 % of this
print's moves are collinear with the one before.

So the reduction has to be one the user can see, which means it only happens while
they are dragging, and only if they ask for it. update_enabled_entities() already
walks every vertex to decide what is visible; it now fills a second pair of index
buffers in the same walk, dropping the interior infill roles and keeping one layer
in four. Because both sets are built together, switching between them is a buffer
binding - no rebuild, no per-frame CPU work, which is what made occlusion culling
unattractive. GLCanvas3D flips the flag while the mouse is down on the canvas or
either slider is live, and asks for one more frame when the level changes so the
full-detail frame lands on release. The top of the visible layer range is always
kept whole: it is the surface being looked at, and the only layer drawn at full
colour in top-layer-only mode.

On the 636-layer, 351k-vertex three-filament fixture the set drops from 448467
segments to 85661 (19 %), and a frame during a drag goes from 298 ms to 52 ms.

Off by default, behind Preferences > Graphics > G-code Preview. With it off the
preview is pixel identical to before, and with it on the preview at rest is pixel
identical to the preview with it off - the reduced set is only ever bound mid-drag.
Verified both, plus that releasing the mouse restores full detail at an unchanged
camera. The OpenGL ES path keeps a single set of entities and is untouched.
This commit is contained in:
Hanif Koh
2026-09-10 13:31:58 +08:00
parent 7cf9884952
commit 8fd5d77220
10 changed files with 192 additions and 9 deletions
+10
View File
@@ -1173,6 +1173,7 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
m_viewer.toggle_top_layer_only_view_range();
// ORCA: darken the layers the preview layer slider is not scrubbed to
m_reduced_detail_while_dragging = get_app_config()->get_bool("preview_reduced_detail_while_dragging");
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
@@ -1907,6 +1908,15 @@ void GCodeViewer::update_layers_slider_mode()
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
}
bool GCodeViewer::set_interacting(bool interacting)
{
const bool reduced = m_reduced_detail_while_dragging && interacting;
if (reduced == m_viewer.is_reduced_detail())
return false;
m_viewer.set_reduced_detail(reduced);
return true;
}
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
{
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));