Donner 0.5.1
Embeddable browser-grade SVG2 engine
Loading...
Searching...
No Matches
donner::svg::compositor::CompositorController Class Reference

Controls compositor layer promotion/demotion and orchestrates composited rendering. More...

#include "donner/svg/compositor/CompositorController.h"

Classes

struct  FastPathCounters
 Diagnostic counters for the translation-only fast path. Tests read these to assert that a drag is taking the fast path every frame, not falling through to prepareDocumentForRendering. More...

Public Member Functions

 CompositorController (SVGDocument &document, RendererInterface &renderer, CompositorConfig config={})
 Construct a compositor controller.
const CompositorConfigconfig () const
 Returns the runtime config this controller was constructed with.
 ~CompositorController ()
 Destructor.
 CompositorController (const CompositorController &)=delete
CompositorControlleroperator= (const CompositorController &)=delete
 CompositorController (CompositorController &&) noexcept
CompositorControlleroperator= (CompositorController &&) noexcept
bool promoteEntity (Entity entity, InteractionHint interactionKind=InteractionHint::ActiveDrag)
 Promote an entity to its own compositor layer.
void demoteEntity (Entity entity)
 Demote a previously promoted entity back to the root layer.
bool isPromoted (Entity entity) const
 Returns true if the given entity is currently promoted to its own layer.
Transform2d layerComposeOffset (Entity entity) const
 Returns the current bitmap-compose offset for a promoted entity's layer, or identity if the entity is not promoted or has no cached bitmap.
void renderFrame (const RenderViewport &viewport)
 Prepare and render a composited frame.
size_t layerCount () const
 Returns the number of currently active layers (excluding the root layer).
size_t totalBitmapMemory () const
 Returns the total memory used by all layer bitmaps, in bytes.
SVGDocumentdocument ()
 Returns a reference to the underlying SVG document.
FallbackReason fallbackReasonsOf (Entity entity) const
 Returns the fallback reasons for a promoted entity, or FallbackReason::None if not promoted.
bool hasSplitStaticLayers () const
 Returns true when the compositor has cached a split underlay/overlay pair for drag preview.
const RendererBitmapbackgroundBitmap () const
 Cached underlay bitmap for the single-promoted-layer drag-preview case.
const RendererBitmapforegroundBitmap () const
 Cached overlay bitmap for the single-promoted-layer drag-preview case.
const RendererBitmaplayerBitmapOf (Entity entity) const
 Cached bitmap for the promoted entity, or an empty bitmap if unavailable.
const FastPathCountersfastPathCountersForTesting () const
void resetAllLayers (bool documentReplaced=false)
 Clear all layers and cached state.
bool remapAfterStructuralReplace (const std::unordered_map< Entity, Entity > &remap)
 Rewire the compositor's entity-keyed state (activeHints_, mandatoryDetector_, complexityBucketer_, layers_) from the old document's entity space onto a new one, after a structurally identical setDocument.
void setTightBoundedSegmentsEnabled (bool enabled)
 Flip tight-bounded segment rasterization on or off at runtime. See CompositorConfig::tightBoundedSegments for semantics. Marks every cached static segment dirty so the next renderFrame call re-rasterizes under the new policy (otherwise the flip would affect only segments that happened to get re-rasterized for other reasons).
bool tightBoundedSegmentsEnabled () const
 Returns the current tight-bounded-segments setting. Mirrors config().tightBoundedSegments for convenience.
void setSkipMainComposeDuringSplit (bool skip)
 When true, renderFrame() skips the main-renderer compose step while the split-static-layers cache (bg/drag/fg triple) is populated. The editor's drag overlay reads those bitmaps directly via GL, so the per-frame drawImage calls into the main renderer are wasted work — on a 892×512 Skia backend with a few filter layers the skip saves ~100 ms per drag frame. The flat snapshot the editor uploads stays stale during drag but is only drawn after drag ends, by which point the settle render (no split cache) has refreshed it.
std::vector< CompositorTilesnapshotTilesForUpload () const
 Enumerate every cacheable unit (static segments + promoted layer bitmaps) interleaved in paint order. Each tile carries a generation counter that advances only when the tile's pixel content was actually re-rasterized — so the editor can gate its GL texture uploads to the minimum set that actually changed this frame. On a click-to-drag, the user should observe at most 3 tiles advance: the two halves of the split segment and the new drag-target layer. All other filter layers, segments, and bucket layers keep their generation and their GL texture binding.

Detailed Description

Controls compositor layer promotion/demotion and orchestrates composited rendering.

The compositor splits the document into layers: one root layer (everything not promoted) and zero or more promoted layers (one per promoted entity subtree). The DOM is the sole source of truth for entity position: during a drag, callers mutate the entity's transform directly (element.setTransform(...)) and the compositor's fast path diffs the new absolute transform against the cached bitmap's rasterize-time transform. When the delta is a pure translation, the bitmap is reused and only the internal compose offset updates — no re-rasterization.

Usage:

CompositorController compositor(document, renderer);
// Promote drag target
compositor.promoteEntity(dragTarget);
// During drag — mutate the DOM; the compositor tracks the delta internally.
dragElement.setTransform(Transform2d::Translate(dx, dy));
compositor.renderFrame(viewport);
// When drag ends
compositor.demoteEntity(dragTarget);
CompositorController(SVGDocument &document, RendererInterface &renderer, CompositorConfig config={})
Construct a compositor controller.
SVGDocument & document()
Returns a reference to the underlying SVG document.
Definition CompositorController.h:275
static Transform2 Translate(const Vector2< double > &offset)
Definition Transform.h:142

Class Documentation

◆ donner::svg::compositor::CompositorController::FastPathCounters

struct donner::svg::compositor::CompositorController::FastPathCounters

Diagnostic counters for the translation-only fast path. Tests read these to assert that a drag is taking the fast path every frame, not falling through to prepareDocumentForRendering.

Class Members
uint64_t fastPathFrames = 0 Incremented every frame that takes the fast path and successfully handled every dirty entity via a compose-transform update.
uint64_t noDirtyFrames = 0 Incremented every frame where the fast-path eligibility check wasn't reached because no entities were dirty (e.g. page-load, selection-change-only frames).
uint64_t slowPathFramesWithDirty = 0 Incremented every frame whose dirty-entity set disqualified the fast path (transform+other flags, subtree with non-translation delta, missing layer, etc.) and fell through to the slow path.

Constructor & Destructor Documentation

◆ CompositorController()

donner::svg::compositor::CompositorController::CompositorController ( SVGDocument & document,
RendererInterface & renderer,
CompositorConfig config = {} )

Construct a compositor controller.

Parameters
documentThe SVG document to composite.
rendererThe renderer backend to use for rasterization and composition.
configRuntime feature gates. Default-constructed enables everything.

Member Function Documentation

◆ demoteEntity()

void donner::svg::compositor::CompositorController::demoteEntity ( Entity entity)

Demote a previously promoted entity back to the root layer.

The entity's explicit compositor hint is removed, its computed layer assignment is updated, and the layer is destroyed. The root layer is marked dirty to include the demoted entity on the next render.

Parameters
entityThe entity to demote. No-op if not currently promoted.

◆ fallbackReasonsOf()

FallbackReason donner::svg::compositor::CompositorController::fallbackReasonsOf ( Entity entity) const
nodiscard

Returns the fallback reasons for a promoted entity, or FallbackReason::None if not promoted.

Parameters
entityThe entity to query.

◆ isPromoted()

bool donner::svg::compositor::CompositorController::isPromoted ( Entity entity) const
nodiscard

Returns true if the given entity is currently promoted to its own layer.

Parameters
entityThe entity to check.

◆ layerComposeOffset()

Transform2d donner::svg::compositor::CompositorController::layerComposeOffset ( Entity entity) const
nodiscard

Returns the current bitmap-compose offset for a promoted entity's layer, or identity if the entity is not promoted or has no cached bitmap.

The compose offset is the delta between the cached bitmap's rasterize-time world transform and the entity's current absolute world transform. Callers who draw the promoted layer's bitmap independently (e.g. the editor's split-layer display path) must apply this offset so the bitmap aligns with the bg/fg render the compositor just produced.

Parameters
entityThe entity to query.

◆ promoteEntity()

bool donner::svg::compositor::CompositorController::promoteEntity ( Entity entity,
InteractionHint interactionKind = InteractionHint::ActiveDrag )

Promote an entity to its own compositor layer.

The entity and its subtree will be rasterized into a separate bitmap. During composition, the layer bitmap is blitted with its composition transform, avoiding re-rasterization of the rest of the scene.

Under CompositorConfig::autoPromoteInteractions (default on), this publishes an Interaction hint tagged with interactionKind. When the gate is off it falls back to an Explicit hint, ignoring interactionKind.

Parameters
entityThe entity to promote.
interactionKindSemantic kind for the Interaction hint. Use Selection for selection-driven pre-warm (no drag in progress) and ActiveDrag for an active user drag. Defaults to ActiveDrag for callers that only use this API during drag.
Returns
true if promotion succeeded, false if the layer limit or memory budget was reached.

◆ remapAfterStructuralReplace()

bool donner::svg::compositor::CompositorController::remapAfterStructuralReplace ( const std::unordered_map< Entity, Entity > & remap)
nodiscard

Rewire the compositor's entity-keyed state (activeHints_, mandatoryDetector_, complexityBucketer_, layers_) from the old document's entity space onto a new one, after a structurally identical setDocument.

The cached bitmaps (segments, layer bitmaps, backgroundBitmap_, foregroundBitmap_) survive untouched — they're keyed on position-in-paint-order, not entity id. This is the fast alternative to resetAllLayers(documentReplaced =true) for the editor's drag-end writeback round-trip through ReplaceDocumentCommand: with a structurally equal reparse, the new document describes the same render tree with the identical visual result, and the compositor can simply swap ids.

Parameters
remapMapping from old entity id → new entity id. Every entity in activeHints_ and in each CompositorLayer (entity, firstEntity, lastEntity) must have an entry; detectors rebuild against the new registry so their hint set doesn't need remap entries.
Returns
true on success. On false, the compositor is in an indeterminate state — the caller MUST follow up with resetAllLayers(documentReplaced=true) to recover.

◆ renderFrame()

void donner::svg::compositor::CompositorController::renderFrame ( const RenderViewport & viewport)

Prepare and render a composited frame.

This method:

  1. Prepares the document (computes styles, layout, render tree) if needed.
  2. Checks dirty flags on promoted entities and marks their layers dirty.
  3. Re-rasterizes dirty layers via createOffscreenInstance() + drawEntityRange().
  4. Rasterizes the root layer (everything not in a promoted layer).
  5. Composes all layers in paint order via drawImage().
Parameters
viewportThe viewport for the render pass.

◆ resetAllLayers()

void donner::svg::compositor::CompositorController::resetAllLayers ( bool documentReplaced = false)

Clear all layers and cached state.

Two callers, two semantics:

  • documentReplaced = false (the default, used by tests and by the compositor's own internal paths that reset against the still-live registry): runs the normal ~ScopedCompositorHint cleanup, which removes CompositorHintComponents from the live registry and lets the resolver strip the now-orphan ComputedLayerAssignment Components.
  • documentReplaced = true (used by AsyncRenderer when it detects documentGeneration has bumped, i.e. a ReplaceDocumentCommand swapped the inner SVGDocument at the same optional storage address): the old Registry was destroyed in place and a brand-new one constructed at the same address. Every ScopedCompositorHint's cached Registry* now aims at a live object that knows nothing about the old entity IDs, so calling registry.valid(old_entity) from the dtor SIGSEGVs inside entt's sparse-set lookup. In this mode the hints are release()-defused before clearing — the old CompositorHintComponents went down with the old registry anyway.

After this call, layerCount() is 0 and all cached bitmaps are released. The next renderFrame() will do a full render.

◆ setTightBoundedSegmentsEnabled()

void donner::svg::compositor::CompositorController::setTightBoundedSegmentsEnabled ( bool enabled)

Flip tight-bounded segment rasterization on or off at runtime. See CompositorConfig::tightBoundedSegments for semantics. Marks every cached static segment dirty so the next renderFrame call re-rasterizes under the new policy (otherwise the flip would affect only segments that happened to get re-rasterized for other reasons).

Intended as a bisection knob for the editor: if a visual regression seems to originate in 0027-tight_bounded_segments, flip the toggle and watch whether it disappears. Not a hot path — re-rasterizing every segment on the next frame costs one full render's worth of work.


The documentation for this class was generated from the following file: