mirror of
https://github.com/penpot/penpot.git
synced 2026-08-14 00:38:42 +00:00
⚡ Soft-drain GPU during recursive drop-shadow passes
Frame drop shadows re-render every child silhouette inside a single walker node, so the usual partial drain cadence (every N tree nodes) lets GPU ops pile up. Flush every few shadow passes instead.
This commit is contained in:
parent
072730574d
commit
f25a2c701f
@ -30,6 +30,9 @@
|
||||
- Zoom settle: visible tiles present via `FrameType::ViewportReady` before interest-ring
|
||||
work; crop-cache rebuild is deferred to the later `Full` so the soft→sharp snap is
|
||||
compose+present only.
|
||||
- Drop-shadow cadence: walker soft-drains every N tree nodes; recursive frame shadows
|
||||
also soft-drain every M silhouette/filter passes (`partial_gpu_drain_shadow_every_n`,
|
||||
default 2) because one frame node can trigger many child re-renders.
|
||||
- Interactive transforms are distinct from viewport fast mode. `set_modifiers_start` enables fast mode and interactive transform; interactive transform still flushes each animation frame.
|
||||
- During interactive transform, modifier tile invalidation is deferred to `render()` once per rAF. Outside interactive transform, `set_modifiers` rebuilds modifier tiles immediately.
|
||||
- `set_modifiers_end` disables fast/interactive state and cancels pending async render; the caller must request the final full-quality render.
|
||||
|
||||
@ -424,6 +424,8 @@ pub(crate) struct RenderState {
|
||||
/// Visible tiles were already presented this pass; interest-ring fill may
|
||||
/// still be running. Final Full should not re-present.
|
||||
pub viewport_presented: bool,
|
||||
/// Drop-shadow silhouette ops since the last shadow cadence drain (reset per tile).
|
||||
shadow_gpu_ops_since_drain: i32,
|
||||
}
|
||||
|
||||
pub struct InteractiveDragCrop {
|
||||
@ -609,6 +611,7 @@ impl RenderState {
|
||||
tile_atlas_flushed: false,
|
||||
drop_shadows_ops_warmed: false,
|
||||
viewport_presented: false,
|
||||
shadow_gpu_ops_since_drain: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@ -2298,6 +2301,7 @@ impl RenderState {
|
||||
// reorder by distance to the center.
|
||||
self.current_tile = None;
|
||||
self.viewport_presented = false;
|
||||
self.shadow_gpu_ops_since_drain = 0;
|
||||
}
|
||||
|
||||
pub fn start_render_loop(
|
||||
@ -2719,6 +2723,23 @@ impl RenderState {
|
||||
crate::get_gpu_state().context.flush(None);
|
||||
}
|
||||
|
||||
/// Count one expensive drop-shadow GPU pass and soft-drain on the shadow
|
||||
/// cadence. Frame/group shadows re-render many children inside a single
|
||||
/// walker node, so they need tighter flushing than [`Self::drain_partial_gpu_soft`]
|
||||
/// alone (which runs every N tree nodes).
|
||||
#[inline]
|
||||
fn maybe_drain_after_shadow_gpu_op(&mut self) {
|
||||
let every = self.options.partial_gpu_drain_shadow_every_n;
|
||||
if every <= 0 {
|
||||
return;
|
||||
}
|
||||
self.shadow_gpu_ops_since_drain += 1;
|
||||
if self.shadow_gpu_ops_since_drain >= every {
|
||||
self.shadow_gpu_ops_since_drain = 0;
|
||||
Self::drain_partial_gpu_soft();
|
||||
}
|
||||
}
|
||||
|
||||
/// Skip all drop/inner shadows in fast mode, or when even a large design-space
|
||||
/// shadow would be subpixel. Otherwise filter per shadow via
|
||||
/// [`Shadow::is_perceptible_at_scale_for`] (stricter for recursive shapes).
|
||||
@ -3136,6 +3157,7 @@ impl RenderState {
|
||||
})?;
|
||||
|
||||
self.surfaces.canvas(SurfaceId::DropShadows).restore();
|
||||
self.maybe_drain_after_shadow_gpu_op();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -3179,6 +3201,7 @@ impl RenderState {
|
||||
})?;
|
||||
|
||||
self.surfaces.canvas(SurfaceId::DropShadows).restore();
|
||||
self.maybe_drain_after_shadow_gpu_op();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -3260,6 +3283,7 @@ impl RenderState {
|
||||
drop_canvas.restore();
|
||||
}
|
||||
|
||||
self.maybe_drain_after_shadow_gpu_op();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -3379,6 +3403,7 @@ impl RenderState {
|
||||
)
|
||||
})?;
|
||||
self.surfaces.canvas(SurfaceId::DropShadows).restore();
|
||||
self.maybe_drain_after_shadow_gpu_op();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3413,6 +3438,7 @@ impl RenderState {
|
||||
self.surfaces
|
||||
.canvas(SurfaceId::DropShadows)
|
||||
.clear(skia::Color::TRANSPARENT);
|
||||
self.maybe_drain_after_shadow_gpu_op();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@ -3925,6 +3951,7 @@ impl RenderState {
|
||||
self.current_tile_had_shapes = false;
|
||||
self.tile_atlas_flushed = false;
|
||||
self.drop_shadows_ops_warmed = false;
|
||||
self.shadow_gpu_ops_since_drain = 0;
|
||||
|
||||
let viewer_masked_pass = self.viewer_masked_pass();
|
||||
|
||||
|
||||
@ -14,6 +14,10 @@ const NODE_BATCH_THRESHOLD: i32 = 3;
|
||||
/// buffers bounded when many shapes paint cheaply to Current (release packs
|
||||
/// far more per budget than debug).
|
||||
const PARTIAL_GPU_DRAIN_EVERY_N: i32 = 64;
|
||||
/// Soft-drain during recursive drop-shadow passes (each silhouette or filter
|
||||
/// surface). The walker counts tree nodes, not shadow children re-rendered
|
||||
/// inside one frame, so frame shadows need a tighter cadence.
|
||||
const PARTIAL_GPU_DRAIN_SHADOW_EVERY_N: i32 = 2;
|
||||
const BLUR_DOWNSCALE_THRESHOLD: f32 = 8.0;
|
||||
const ANTIALIAS_THRESHOLD: f32 = 7.0;
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
@ -35,6 +39,9 @@ pub struct RenderOptions {
|
||||
/// Soft-flush GPU every N nodes during progressive tile walks (see
|
||||
/// [`PARTIAL_GPU_DRAIN_EVERY_N`]).
|
||||
pub partial_gpu_drain_every_n: i32,
|
||||
/// Soft-flush GPU every N drop-shadow silhouette passes (see
|
||||
/// [`PARTIAL_GPU_DRAIN_SHADOW_EVERY_N`]).
|
||||
pub partial_gpu_drain_shadow_every_n: i32,
|
||||
pub blur_downscale_threshold: f32,
|
||||
pub capture_frames: i32,
|
||||
}
|
||||
@ -52,6 +59,7 @@ impl Default for RenderOptions {
|
||||
max_blocking_time_ms: MAX_BLOCKING_TIME_MS,
|
||||
node_batch_threshold: NODE_BATCH_THRESHOLD,
|
||||
partial_gpu_drain_every_n: PARTIAL_GPU_DRAIN_EVERY_N,
|
||||
partial_gpu_drain_shadow_every_n: PARTIAL_GPU_DRAIN_SHADOW_EVERY_N,
|
||||
blur_downscale_threshold: BLUR_DOWNSCALE_THRESHOLD,
|
||||
capture_frames: 0,
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user