Donner 0.5.1
Embeddable browser-grade SVG2 engine
Loading...
Searching...
No Matches
AsyncRenderer.h File Reference

AsyncRenderer owns a svg::Renderer and runs its draw() + takeSnapshot() on a dedicated worker thread so heavy renders don't block the UI thread. More...

#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <functional>
#include <mutex>
#include <optional>
#include <thread>
#include <unordered_map>
#include "donner/base/EcsRegistry.h"
#include "donner/base/Vector2.h"
#include "donner/svg/SVGDocument.h"
#include "donner/svg/SVGElement.h"
#include "donner/svg/compositor/CompositorController.h"
#include "donner/svg/compositor/ScopedCompositorHint.h"
#include "donner/svg/renderer/Renderer.h"
#include "donner/svg/renderer/RendererInterface.h"
Include dependency graph for AsyncRenderer.h:
This graph shows which files directly or indirectly include this file:

Classes

struct  donner::editor::RenderRequest
 Per-request handoff data captured at render-request time so the worker has everything it needs without touching live UI state. More...
struct  donner::editor::RenderRequest::DragPreview
struct  donner::editor::RenderResult
 Bitmap plus the document version it was rendered from. More...
struct  donner::editor::RenderResult::CompositedPreview
class  donner::editor::AsyncRenderer

Namespaces

namespace  donner
 Top-level Donner namespace, which is split into different sub-namespaces such as donner::svg and donner::css.
namespace  donner::svg
 Donner SVG library, which can load, manipulate and render SVG files.

Detailed Description

AsyncRenderer owns a svg::Renderer and runs its draw() + takeSnapshot() on a dedicated worker thread so heavy renders don't block the UI thread.

Threading model

The worker thread owns the Renderer for its entire lifetime — the Renderer is constructed on the worker thread at startup and destroyed on the worker thread at shutdown. This is load-bearing for the Geode (WebGPU) backend under Emscripten pthreads: WebGPU JS objects are per-worker, so the device, pipelines, textures, and readback buffers must all be created and used on a single thread. See the "first render aborts with `getJsObject` assertion" incident that motivated moving renderer ownership here.

The worker additionally takes exclusive ownership of the SVGDocument during an active render. The UI thread must not mutate the document while a render is in flight.

UI thread flow per frame:

  1. pollResult() — if a render just finished, pick up the bitmap.
  2. If NOT busy: process mutations via flushFrame().
  3. If NOT busy AND a new render is needed: requestRender().
  4. If busy: skip flushFrame, leave pending mutations in the queue. They apply on the next idle frame. Input (drags, typing) still gets processed and queued — just not dispatched to the ECS.

The safety invariant: between requestRender() and a non-nullopt return from pollResult(), the UI thread must not mutate the SVGDocument, and must not touch state the overlay renderer reads (selection, etc. — those are snapshotted at request time, see RenderRequest). The UI thread must not call any method on the Renderer at any time — it lives on the worker.


Class Documentation

◆ donner::editor::RenderRequest::DragPreview

struct donner::editor::RenderRequest::DragPreview
Class Members
Entity entity = entt::null
InteractionHint interactionKind = svg::compositor::InteractionHint::ActiveDrag Which interaction phase drove this preview. Selection means the editor is pre-warming a layer for the selected entity before any drag begins. ActiveDrag means the user is actively dragging — the DOM's transform attribute already reflects the cursor delta, so no extra translation is passed here. The compositor stamps the correct InteractionHint on the entity based on this field so downstream introspection stays accurate.