Donner 0.5.1
Embeddable browser-grade SVG2 engine
Loading...
Searching...
No Matches
donner::geode::GeodeDevice Class Reference

Owns (or wraps) a WebGPU device/queue pair for GPU rendering. More...

#include "donner/svg/renderer/geode/GeodeDevice.h"

Public Member Functions

 ~GeodeDevice ()
 Destructor releases the device and all GPU resources.
 GeodeDevice (const GeodeDevice &)=delete
GeodeDevice & operator= (const GeodeDevice &)=delete
 GeodeDevice (GeodeDevice &&)=delete
GeodeDevice & operator= (GeodeDevice &&)=delete
const wgpu::Device & device () const
 Returns the wgpu::Device. Guaranteed valid for the lifetime of this object.
const wgpu::Queue & queue () const
 Returns the default queue.
const wgpu::Adapter & adapter () const
 Returns the adapter backing this device. May be null in embedded mode if the host did not provide an adapter.
wgpu::TextureFormat textureFormat () const
 Render-target texture format. Defaults to RGBA8Unorm for headless devices; set by the host via GeodeEmbedConfig::textureFormat in embedded mode.
void deferDestroy (wgpu::Buffer buffer)
 Enqueue a GPU buffer for deferred destruction.
void deferDestroy (wgpu::Texture texture)
 Enqueue a GPU texture for deferred destruction.
void drainDeferredDestroys ()
 Drop all deferred-destroy handles, releasing their GPU resources.
bool useAlphaCoverageAA () const
 Whether to use alpha-coverage AA instead of hardware 4× MSAA with @builtin(sample_mask).
uint32_t sampleCount () const
 MSAA sample count for render pipelines and render-target textures.
void setCounters (GeodeCounters *counters)
 Install a GeodeCounters struct for this device.
GeodeCounterscounters () const
 Non-owning pointer to the installed counters, or null.
void countBuffer () const
void countBindGroup () const
void countTexture () const
uint64_t lifetimeTextureCreates () const
 Cumulative number of countTexture() calls since this GeodeDevice was created. Does not account for textures released back into a pool — it is an allocation-site counter, not a live-count.
uint64_t lifetimeBufferCreates () const
 Cumulative number of countBuffer() calls since this GeodeDevice was created. Same caveat as lifetimeTextureCreates().
void countSubmit () const
void countPathEncode () const
void countDraw () const
void countPipelineSwitch () const
bool supportsTimestamps () const
 Whether the driver supports GPU timestamp queries.
Shared dummy resources (design doc 0030 Milestone 4.2)

GeoEncoder's bind groups always include pattern + clip-mask texture/sampler slots, even when the current draw doesn't actually use them. Each slot is filled with a 1×1 "identity" texture when the feature is inactive. Prior to M4.2 every GeoEncoder instance created its own dummies (two textures per encoder), which showed up as 2+ textureCreates per frame per push/pop. Caching the dummies on the device — one instance per GeodeDevice — drops that to zero steady-state.

const wgpu::Texture & dummyPatternTexture () const
 1×1 opaque-black RGBA8 dummy. Bound into the pattern slot when the current draw is solid / gradient (not a pattern). The shader does not sample from it, but the bind group layout still requires a valid binding.
const wgpu::TextureView & dummyPatternTextureView () const
 View of dummyPatternTexture().
const wgpu::Sampler & dummyPatternSampler () const
 Linear-Repeat sampler used for both the dummy and real pattern tiles.
const wgpu::Texture & dummyClipMaskTexture () const
 1×1 R8Unorm with value 0xFF (= 1.0 coverage). Bound into the clip-mask slot when no clip mask is active — the shader multiplies coverage by this value, so 1.0 is a no-op.
const wgpu::TextureView & dummyClipMaskTextureView () const
 View of dummyClipMaskTexture().
const wgpu::Sampler & dummyClipMaskSampler () const
 Linear-ClampToEdge sampler used for both the dummy and real clip masks.
const wgpu::Buffer & identityInstanceTransformBuffer () const
 One-element instance-transform storage buffer carrying the identity affine. Bound at binding 7 of the Slug fill bind-group layout by every non-instanced solid fill so the bind-group layout stays stable across draw calls regardless of whether fillPathInstanced is in play. See design doc 0030 §M6 Bullet 2.
Shared render / compute pipelines (issue #575 fix)

Every wgpu pipeline created by createRenderPipeline / createComputePipeline is retained internally by wgpu-native even after the public handle's refcount drops to zero — wgpuDevicePoll does not drain it. Prior to this, RendererGeode constructed the four pipeline objects below (~18 wgpu pipelines in total, most inside GeodeFilterEngine) per-instance, so the image-comparison suite leaked ~1.6 MB per test and ultimately exhausted the driver memory budget (see issue #575). Moving ownership here — one copy per GeodeDevice — caps the pipeline footprint at a fixed cost.

Every renderer that talks to this device shares the same pipeline objects; their state is intentionally immutable after construction (no per-draw mutation), so concurrent use from sibling renderers is safe as long as it is serialized at the wgpu::Queue level (which Donner's render path already is).

GeodePipelinepipeline () const
 Slug solid-fill render pipeline.
GeodeGradientPipelinegradientPipeline () const
 Slug gradient-fill render pipeline.
GeodeImagePipelineimagePipeline () const
 Image-blit render pipeline (used by GeoEncoder::drawImage and the pattern / layer composition path).
GeodeMaskPipelinemaskPipeline () const
 Clip-path mask render pipeline. Built lazily on first access rather than eagerly at device creation, matching the prior per-encoder lazy path — most documents don't use <clipPath> and the production WASM path avoids the cost.
GeodeFilterEnginefilterEngine () const
 GPU filter-graph executor. Owns ~15 compute pipelines for SVG filter primitives.

Static Public Member Functions

static std::unique_ptr< GeodeDevice > CreateHeadless ()
 Create a headless GeodeDevice.
static std::unique_ptr< GeodeDevice > CreateFromExternal (const GeodeEmbedConfig &config)
 Create a GeodeDevice wrapping a host-provided device and queue.

Detailed Description

Owns (or wraps) a WebGPU device/queue pair for GPU rendering.

GeodeDevice is the entry point to the Geode rendering backend. In headless mode (CreateHeadless), it creates a WebGPU instance, selects a default adapter, and creates a device — all without any window system integration.

In embedded mode (CreateFromExternal), it wraps a device and queue already created by the host application. The host retains ownership of the underlying WebGPU objects; GeodeDevice's destructor will not destroy them.

Typical headless usage:

auto maybeDevice = GeodeDevice::CreateHeadless();
if (!maybeDevice) {
  // No GPU available.
  return;
}

Typical embedded usage:

GeodeEmbedConfig config;
config.device = hostDevice;
config.queue = hostQueue;
auto geodeDevice = GeodeDevice::CreateFromExternal(config);

Member Function Documentation

◆ CreateFromExternal()

std::unique_ptr< GeodeDevice > donner::geode::GeodeDevice::CreateFromExternal ( const GeodeEmbedConfig & config)
static

Create a GeodeDevice wrapping a host-provided device and queue.

The returned device does NOT own the underlying WebGPU instance, adapter, device, or queue — the host is responsible for keeping them alive.

Parameters
configEmbedding configuration with valid device/queue handles.
Returns
A valid GeodeDevice on success, or null if config.device or config.queue is null.

◆ CreateHeadless()

std::unique_ptr< GeodeDevice > donner::geode::GeodeDevice::CreateHeadless ( )
static

Create a headless GeodeDevice.

Returns
A valid GeodeDevice on success, or an empty unique_ptr if the runtime could not create an adapter/device (e.g., no GPU, no driver).

◆ deferDestroy() [1/2]

void donner::geode::GeodeDevice::deferDestroy ( wgpu::Buffer buffer)

Enqueue a GPU buffer for deferred destruction.

The buffer handle is kept alive until drainDeferredDestroys() is called, preventing the underlying GPU resource from being freed while an in-flight command buffer may still reference it.

◆ deferDestroy() [2/2]

void donner::geode::GeodeDevice::deferDestroy ( wgpu::Texture texture)

Enqueue a GPU texture for deferred destruction.

Same semantics as the buffer variant.

◆ drainDeferredDestroys()

void donner::geode::GeodeDevice::drainDeferredDestroys ( )

Drop all deferred-destroy handles, releasing their GPU resources.

Called at the top of each frame (before new allocations) so resources from the previous frame's command buffer submission have had time to complete on the GPU. WebGPU internally reference-counts resources used by submitted command buffers, so dropping our handle here is safe even without an explicit device.poll().

◆ identityInstanceTransformBuffer()

const wgpu::Buffer & donner::geode::GeodeDevice::identityInstanceTransformBuffer ( ) const

One-element instance-transform storage buffer carrying the identity affine. Bound at binding 7 of the Slug fill bind-group layout by every non-instanced solid fill so the bind-group layout stays stable across draw calls regardless of whether fillPathInstanced is in play. See design doc 0030 §M6 Bullet 2.

Layout mirrors the WGSL InstanceTransform struct in shaders/slug_fill.wgsl: two vec4f per entry, row-major affine, so the identity is {(1, 0, 0, 0), (0, 1, 0, 0)}. The .z components carry the translation (0 for identity).

◆ sampleCount()

uint32_t donner::geode::GeodeDevice::sampleCount ( ) const
inline

MSAA sample count for render pipelines and render-target textures.

Returns 1 on the alpha-coverage path (Intel Arc + Vulkan), where MSAA resolve triggers flaky GPU hangs on Mesa ANV / Xe KMD. All other adapters get 4× MSAA with hardware sample-mask AA.

◆ setCounters()

void donner::geode::GeodeDevice::setCounters ( GeodeCounters * counters)
inline

Install a GeodeCounters struct for this device.

Non-owning; the caller must keep the struct alive for as long as the device might increment it. Pass nullptr to disable instrumentation.

See design doc 0030 (geode_performance). All Geode components that hold a GeodeDevice& route their per-frame hot-path allocation and submission sites through this hook.

◆ supportsTimestamps()

bool donner::geode::GeodeDevice::supportsTimestamps ( ) const
inline

Whether the driver supports GPU timestamp queries.

Always false today — reserved for future work (design doc 0030, "Future Work").

◆ useAlphaCoverageAA()

bool donner::geode::GeodeDevice::useAlphaCoverageAA ( ) const
inline

Whether to use alpha-coverage AA instead of hardware 4× MSAA with @builtin(sample_mask).

Returns true on Intel + Vulkan, where writing @builtin(sample_mask) from overlapping band quads hangs Mesa ANV / Xe KMD (observed on Arc A380, Mesa 25.2.8). The alpha-coverage path folds coverage into the fragment color instead of relying on the hardware sample mask, and runs at sampleCount() == 1 (no MSAA / no resolve) to avoid a second class of flaky MSAA-resolve hangs on the same driver.


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