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

PipelinedRenderer — a multi-threaded, in-process renderer that reuses the sandbox wire format as a producer/consumer boundary between the main thread (parser + driver + serializer) and a worker thread (deserializer + real backend). More...

#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <mutex>
#include <optional>
#include <span>
#include <string>
#include <thread>
#include <vector>
#include "donner/svg/renderer/RendererInterface.h"
Include dependency graph for PipelinedRenderer.h:
This graph shows which files directly or indirectly include this file:

Classes

struct  donner::editor::sandbox::PipelinedFrame
 One frame's worth of rendered pixels and metadata handed back to the main thread after PipelinedRenderer completes a submission. More...
class  donner::editor::sandbox::PipelinedRenderer
 A pipelined renderer that moves rasterization off the caller's thread. More...

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.

Typedefs

using donner::editor::sandbox::RendererFactory = std::unique_ptr<svg::RendererInterface> (*)()
 Factory for the real backend the worker thread should rasterize into. Called exactly once, on the worker thread, before the first replay. The worker owns the returned renderer for its whole lifetime.

Functions

std::unique_ptr< svg::RendererInterfacedonner::editor::sandbox::MakeDefaultRenderer ()
 Default factory that returns a Renderer backed by whichever renderer backend was selected at build time (tiny-skia or Geode).

Detailed Description

PipelinedRenderer — a multi-threaded, in-process renderer that reuses the sandbox wire format as a producer/consumer boundary between the main thread (parser + driver + serializer) and a worker thread (deserializer + real backend).

Motivation: the same observation that drives the sandbox design — RendererInterface is a self-contained command stream — means the same serialize/replay pipeline works across any boundary:

Boundary Producer → Consumer Transport
Process (sandbox) child → host pipe
Thread (this file) main → render worker std::vector buf
Time (frame inspect) last frame → inspector pane in-memory replay
File (record/replay) now → later .rnr file

PipelinedRenderer targets the threading case. The main thread calls submit(document), which runs the parser/driver inline (fast, mostly cache-local) and serializes the RendererInterface calls into a byte buffer. The buffer is handed off to a worker thread that decodes it and invokes a real backend (Renderer, resolving to the build-selected backend). The main thread returns as soon as serialization completes, freeing it to mutate the document for frame N+1 while frame N rasterizes in parallel.

This is "newest wins": if submit() is called while a previous frame is still rasterizing, the old frame is abandoned on the worker side. We're building an interactive editor — the user cares about the current frame, not the complete history.

Why not just pass SVGDocument across threads? Two reasons:

  1. The ECS registry inside SVGDocument has mutable caches (layout, computed style) that the driver writes during traversal. Passing the same document to a worker while the main thread is mutating it races. Serializing freezes the snapshot.
  2. The wire buffer is a natural sync point. Once submit() returns, the caller can mutate the document freely — the worker is operating on bytes, not references.