|
|
Donner 0.5.1
Embeddable browser-grade SVG2 engine
|
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 CompositorConfig & | config () const |
| Returns the runtime config this controller was constructed with. | |
| ~CompositorController () | |
| Destructor. | |
| CompositorController (const CompositorController &)=delete | |
| CompositorController & | operator= (const CompositorController &)=delete |
| CompositorController (CompositorController &&) noexcept | |
| CompositorController & | operator= (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. | |
| SVGDocument & | document () |
| 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 RendererBitmap & | backgroundBitmap () const |
| Cached underlay bitmap for the single-promoted-layer drag-preview case. | |
| const RendererBitmap & | foregroundBitmap () const |
| Cached overlay bitmap for the single-promoted-layer drag-preview case. | |
| const RendererBitmap & | layerBitmapOf (Entity entity) const |
| Cached bitmap for the promoted entity, or an empty bitmap if unavailable. | |
| const FastPathCounters & | fastPathCountersForTesting () 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< CompositorTile > | snapshotTilesForUpload () 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. | |
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:
| 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.
| donner::svg::compositor::CompositorController::CompositorController | ( | SVGDocument & | document, |
| RendererInterface & | renderer, | ||
| CompositorConfig | config = {} ) |
Construct a compositor controller.
| document | The SVG document to composite. |
| renderer | The renderer backend to use for rasterization and composition. |
| config | Runtime feature gates. Default-constructed enables everything. |
| 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.
| entity | The entity to demote. No-op if not currently promoted. |
|
nodiscard |
Returns the fallback reasons for a promoted entity, or FallbackReason::None if not promoted.
| entity | The entity to query. |
|
nodiscard |
Returns true if the given entity is currently promoted to its own layer.
| entity | The entity to check. |
|
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.
| entity | The entity to query. |
| 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.
| entity | The entity to promote. |
| interactionKind | Semantic 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. |
|
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.
| remap | Mapping 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. |
| void donner::svg::compositor::CompositorController::renderFrame | ( | const RenderViewport & | viewport | ) |
Prepare and render a composited frame.
This method:
| viewport | The viewport for the render pass. |
| void donner::svg::compositor::CompositorController::resetAllLayers | ( | bool | documentReplaced = false | ) |
Clear all layers and cached state.
Two callers, two semantics:
After this call, layerCount() is 0 and all cached bitmaps are released. The next renderFrame() will do a full render.
| 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.