|
|
Donner SVG 0.8.0-pre
SVG editor and embeddable C++20 engine.
|
Drawing API for the Geode GPU renderer. More...
#include "donner/svg/renderer/geode/GeoEncoder.h"
Classes | |
| struct | ScenePaint |
| Paint carried by one cross-entity batch instance: a solid colour, or a resolved gradient whose parameters and stop ramp the encoder copies into the slot's persistent paint block. More... | |
| struct | SceneRecordState |
| The record fields a batched instance takes from encoder-side state rather than from its own arguments: the paint scalars published into its slot and the clip rectangle live at the time it was appended. More... | |
| struct | SceneBatchBinding |
| One cross-entity ordered batch: consecutive resident slots of one slab chunk plus a run of consecutive record-slab slots. More... | |
| struct | PatternPaint |
| Describes a pattern tile used as a paint source for fillPathPattern. More... | |
Public Member Functions | |
| GeoEncoder (GeodeDevice &device, const GeodePipeline &fillPipeline, const GeodeGradientPipeline &gradientPipeline, const GeodeImagePipeline &imagePipeline, const gpu::Texture &target, const gpu::Extent2d &targetSize) | |
| Create an encoder targeting the given texture. | |
| GeoEncoder (GeodeDevice &device, const GeodePipeline &fillPipeline, const GeodeGradientPipeline &gradientPipeline, const GeodeImagePipeline &imagePipeline, const gpu::Texture &target, const gpu::Extent2d &targetSize, gpu::CommandEncoder &sharedCommandEncoder) | |
| Shared-CommandEncoder constructor. | |
| void | setBufferPool (GeodeBufferPool *pool) |
| Install a cross-frame buffer pool. | |
| void | setGeometryAdmission (GeometryAdmission *admission) |
| Install the mandatory geometry admission gate for renderer-owned encoders. | |
| std::size_t | patternGpuPreparationsForTesting () const |
| Number of pattern sampler/view preparation attempts made by this encoder. | |
| std::size_t | pendingSceneAdmissionsForTesting () const |
| Number of admitted scene instances not yet consumed by a batch draw. | |
| void | injectScenePreparationFailureAfterForTesting (std::size_t successfulPreparations) |
| Fail one scene preparation after successfulPreparations successful calls. | |
| void | recordGeometryDebugInstance (const EncodedPath &encoded, std::span< const float > instanceTransforms) |
| Observe Slug draws recorded by this encoder. | |
| void | setGeometryDebugSink (GeometryDebugSink *sink, const Transform2d &rootFromTarget=Transform2d()) |
| GeoEncoder (const GeoEncoder &)=delete | |
| GeoEncoder & | operator= (const GeoEncoder &)=delete |
| GeoEncoder (GeoEncoder &&) noexcept | |
| Move constructor. | |
| GeoEncoder & | operator= (GeoEncoder &&) noexcept |
| Move assignment operator. | |
| void | clear (const css::RGBA &color) |
| Clear the target texture to the given color. | |
| void | setLoadPreserve () |
| Switch the next render pass's load op from Clear to Load, preserving whatever the target texture already contains. | |
| void | setAntialias (bool antialias) |
| Enable analytic edge anti-aliasing for subsequent path and mask draws. Disabled mode emits binary coverage at the pixel center. | |
| bool | hasActiveClipState () const |
| True when a clip polygon or a clip mask is active. Batchable residence and cross-entity batching are only taken with no active clip state (the cached bind groups and shared batch uniform carry zero clip flags). | |
| uint64_t | clipStateVersion () const |
| Monotonic clip-state version: every scissor, clip-polygon, and clip-mask mutation bumps it. The ordered batch machinery snapshots it per batch so a batch never spans a clip change. | |
| bool | hasOpenMaskPass () const |
| True while a mask pass is open, i.e. draws are being recorded into a mask texture through the mask pipeline rather than into the main pass. Residency and the ordered batch gate both exclude these draws: a batch defers to flush time, by which point the mask pass has closed and the draw would land in the wrong target. | |
| void | setTransform (const Transform2d &transform) |
| Set the model-view transform for subsequent draw calls. | |
| void | setScissorRect (int32_t x, int32_t y, int32_t w, int32_t h) |
| Set a scissor rectangle in target-pixel coordinates. | |
| void | clearScissorRect () |
| Remove any active scissor, restoring full-target rasterization. | |
| void | setClipPolygon (const Vector2d corners[4]) |
| Activate a convex 4-vertex clip polygon. | |
| void | clearClipPolygon () |
| Clear any active clip polygon, restoring unclipped rasterisation (or falling back to just the scissor rect, if one is set). | |
| void | beginMaskPass (const gpu::Texture &mask) |
| Open a new render pass that writes into the given mask texture. | |
| void | fillPathIntoMask (const Path &path, FillRule rule, const EncodedPath *precomputedEncoded=nullptr) |
| Fill path into the currently open mask pass using the Slug mask pipeline. | |
| void | endMaskPass () |
| Close the mask render pass opened by beginMaskPass. | |
| void | setClipMask (const gpu::TextureView &maskView) |
| Bind maskView as the clip mask texture for subsequent fill / gradient draws. | |
| void | setClipMask (const gpu::Texture &maskTexture, const gpu::TextureView &maskView) |
| Preferred overload: sets both the view AND the parent texture so the encoder keeps the underlying Vulkan resource alive for as long as it's bound. | |
| void | clearClipMask () |
| Remove any active clip mask, restoring unclipped rasterisation. | |
| void | blitFullTarget (const gpu::Texture &src, double opacity) |
| Blit an offscreen texture across the entire target with an alpha multiplier. | |
| void | blitFullTargetMasked (const gpu::Texture &content, const gpu::Texture &mask, svg::MaskType maskType, const std::optional< Box2d > &maskBounds) |
| <mask> compositing. | |
| void | blitFullTargetBlended (const gpu::Texture &layer, const gpu::Texture &dstSnapshot, uint32_t blendMode, double opacity) |
| mix-blend-mode compositing. | |
| void | drawImage (const svg::ImageResource &image, const Box2d &destRect, double opacity, svg::ImageRendering imageRendering) |
| Draw a raster image into the given destination rectangle. | |
| void | drawImage (const svg::ImageResource &image, const Box2d &destRect, double opacity, bool pixelated) |
| Source-compatible legacy overload. New callers pass the resolved image-rendering enum. | |
| void | drawTexture (const gpu::Texture &texture, const Box2d &destRect, const Box2d &sourceUv, double opacity, bool pixelated, bool sourceIsPremultiplied) |
| Draw an already-GPU-resident RGBA texture into the destination rectangle. | |
| void | fillPath (const Path &path, const css::RGBA &color, FillRule rule, const EncodedPath *precomputedEncoded=nullptr) |
| Fill a path with a solid color. | |
| void | fillPathResident (GeodeResidentSlot &slot, const EncodedPath &encoded, const css::RGBA &color, FillRule rule, uint64_t frameId) |
| Solid fill of a cached path with persistent GPU residence. | |
| void | fillPathInstanced (const EncodedPath &encoded, const css::RGBA &color, FillRule rule, std::span< const float > instanceTransforms) |
| Fill N copies of the same encoded path at N different affine transforms, in one GPU draw call. | |
| bool | ensureResidentSceneRecord (GeodeResidentSlot &slot, const EncodedPath &encoded, const ScenePaint &paint, FillRule rule, const Transform2d &recordTransform, const GeodeRecordSlab::Slot *recordSlotOverride=nullptr, std::vector< uint8_t > *overrideRecordCache=nullptr, SceneRecordState *recordState=nullptr, bool publishPaint=true) |
| Ensure a resident slot's geometry is uploaded and its instance record (record-slab slot, chunk-relative bases, transform-bearing) is current WITHOUT drawing. | |
| void | releasePreparedSceneAdmission (const EncodedPath &encoded) |
| Cancel one prepared scene instance before demoting it to a different draw path. | |
| void | fillPathSceneBatch (const css::RGBA &color, FillRule rule, const SceneBatchBinding &binding) |
| Record ONE GPU draw covering a cross-entity batch of resident solid fills (ordered batching). | |
| void | fillPathLinearGradient (const Path &path, const LinearGradientParams ¶ms, FillRule rule, const EncodedPath *precomputedEncoded=nullptr) |
| Fill a path with a linear gradient. | |
| void | fillPathRadialGradient (const Path &path, const RadialGradientParams ¶ms, FillRule rule, const EncodedPath *precomputedEncoded=nullptr) |
| Fill a path with a radial gradient. | |
| void | fillPathLinearGradientResident (GeodeResidentGradientSlot &slot, const EncodedPath &encoded, const LinearGradientParams ¶ms, FillRule rule, uint64_t frameId) |
| Fill a path with a linear gradient through a persistent GPU residence slot. | |
| void | fillPathRadialGradientResident (GeodeResidentGradientSlot &slot, const EncodedPath &encoded, const RadialGradientParams ¶ms, FillRule rule, uint64_t frameId) |
| Radial variant of fillPathLinearGradientResident. | |
| void | fillPathPattern (const Path &path, FillRule rule, const PatternPaint &paint, const EncodedPath *precomputedEncoded=nullptr) |
| Fill a path with a repeating pattern tile. | |
| void | finish () |
| Submit all encoded commands to the GPU queue. | |
Drawing API for the Geode GPU renderer.
GeoEncoder is a per-frame command builder. Construct one against a target texture, issue draw calls (fillPath, clear, etc.), then call finish() to submit the command buffer to the GPU.
The encoder owns no GPU buffers itself - each draw call allocates fresh vertex / band / curve / uniform buffers. This is the simplest possible implementation; later phases will add buffer pooling and the ECS-backed GeodePathCacheComponent for paths whose geometry hasn't changed.
Typical usage:
GeoEncoder encoder(device, pipeline, targetTexture); encoder.clear(css::RGBA::White); encoder.setTransform(Transform2d::Scale(2.0)); encoder.fillPath(myPath, css::RGBA::Red, FillRule::NonZero); encoder.finish();
| struct donner::geode::GeoEncoder::SceneRecordState |
The record fields a batched instance takes from encoder-side state rather than from its own arguments: the paint scalars published into its slot and the clip rectangle live at the time it was appended.
A batch re-derives every instance's record when it flushes, which can be arbitrarily far from the append: other draws of the same entity, and other clip changes, can happen in between. Reading these back off the slot or off the encoder at that point reads the LATEST state, not the appended one. The caller therefore captures them at append and hands the same values back at flush, which is what makes the re-derivation reproduce byte-identical bytes.
| Class Members | ||
|---|---|---|
| float | clipRect[4] = {0.0f, 0.0f, 0.0f, 0.0f} | |
| uint32_t | clipRectActive = 0 | |
| uint32_t | gradientSpread = 0 | |
| uint32_t | gradientStopCount = 0 | |
| uint32_t | paintMode = 0 | |
| struct donner::geode::GeoEncoder::SceneBatchBinding |
One cross-entity ordered batch: consecutive resident slots of one slab chunk plus a run of consecutive record-slab slots.
| Class Members | ||
|---|---|---|
| BufferRef | chunkBuffer | Slab chunk holding every instance's geometry. |
| uint64_t | chunkBufferId = 0 | |
| uint64_t | chunkBytes = 0 | Byte size of chunkBuffer; scene batches bind the whole chunk. |
| uint64_t | firstRecordOffset = 0 | BYTE offset of the first instance's record inside recordBuffer. Record-slot indices are global across slab chunks, so an index is not a byte offset once the slab has grown; the caller passes the first slot's own buffer-relative offset instead. |
| uint32_t | instanceCount = 1 | |
| BufferRef | recordBuffer | Record-slab buffer holding the records. Stable identities of chunkBuffer / recordBuffer (see GeodeDevice::AllocateBufferId). The bind-group cache outlives the document that owns these buffers, so it keys on these ids; the raw handle addresses are recycled once that document is destroyed. |
| uint64_t | recordBufferId = 0 | |
| GeodeRecordSlab * | recordSlab = nullptr | |
| uint32_t | vertexCount = 0 | Max fan vertex count over the instances. Record slab the instances' records live in. Supplies the persistent batch-uniform buffer so a steady frame writes nothing; null falls back to the encoder's per-frame uniform arena. |
| struct donner::geode::GeoEncoder::PatternPaint |
Describes a pattern tile used as a paint source for fillPathPattern.
The tile texture is expected to contain pre-rendered pattern content in premultiplied RGBA. The Slug fill shader samples it with the Repeat wrap mode (equivalent to SVG <pattern> default behaviour) using the provided transform to map path-space positions into tile-space.
| Class Members | ||
|---|---|---|
| double | opacity = 1.0 | Multiplicative alpha applied to the sampled tile color. Usually fill-opacity * opacity. |
| Transform2d | patternFromPath | Transform from path space (where the path being filled lives) to pattern tile space. Typically inverse(targetFromPattern) composed with the current encoder transform - the RendererGeode layer builds the right composition. |
| const Texture * | tile = nullptr | Pre-rendered tile texture (RGBA8, premultiplied). |
| Vector2d | tileSize | Size of the tile rectangle in pattern space (width, height). The shader uses this to wrap sample positions via fract(). |
| donner::geode::GeoEncoder::GeoEncoder | ( | GeodeDevice & | device, |
| const GeodePipeline & | fillPipeline, | ||
| const GeodeGradientPipeline & | gradientPipeline, | ||
| const GeodeImagePipeline & | imagePipeline, | ||
| const gpu::Texture & | target, | ||
| const gpu::Extent2d & | targetSize ) |
Create an encoder targeting the given texture.
| device | The Geode device owning the shared pipelines and counters. |
| fillPipeline | The Slug fill pipeline. |
| gradientPipeline | The Slug gradient-fill pipeline. |
| imagePipeline | The image-blit pipeline. |
| target | Single-sample render target. Usage must include RenderAttachment; add TextureBinding or CopySrc when callers sample or read it after rendering. The texture must outlive finish(). |
| donner::geode::GeoEncoder::GeoEncoder | ( | GeodeDevice & | device, |
| const GeodePipeline & | fillPipeline, | ||
| const GeodeGradientPipeline & | gradientPipeline, | ||
| const GeodeImagePipeline & | imagePipeline, | ||
| const gpu::Texture & | target, | ||
| const gpu::Extent2d & | targetSize, | ||
| gpu::CommandEncoder & | sharedCommandEncoder ) |
Shared-CommandEncoder constructor.
Uses the provided sharedCommandEncoder instead of creating its own. When this overload is used, finish() only ends any open render pass - it does NOT finish the CommandEncoder or submit to the queue. The caller (typically RendererGeode) owns the lifetime of the shared encoder and is responsible for calling sharedCommandEncoder.finish() + queue.submit() exactly once at the end of the frame.
Enables push/pop of isolated layers / filter layers / mask layers without forcing a queue submit per layer boundary - the whole frame's render passes batch into a single command buffer.
| void donner::geode::GeoEncoder::beginMaskPass | ( | const gpu::Texture & | mask | ) |
Open a new render pass that writes into the given mask texture.
Used by RendererGeode::pushClip to materialise a path-based clip into an RGBA8Unorm coverage texture that subsequent fill/gradient draws can sample.
The main render pass, if open, is closed first. Subsequent fillPathIntoMask calls add paths to the mask via the Slug mask pipeline. endMaskPass closes the mask pass and re-opens the main pass (with LoadOp::Load) when the next draw lands.
| mask | Single-sample RGBA8Unorm target. Sampled by setClipMask after endMaskPass. |
| void donner::geode::GeoEncoder::blitFullTarget | ( | const gpu::Texture & | src, |
| double | opacity ) |
Blit an offscreen texture across the entire target with an alpha multiplier.
Used by RendererGeode::popIsolatedLayer to composite a sub-layer's content back onto the outer target. The source texture must be the SAME SIZE as this encoder's target - the blit takes the full texture and maps it 1:1 onto the full target. The current setTransform does NOT affect the blit (it's a device-pixel-space operation, not a model-space draw).
| src | Source texture (RGBA8, premultiplied). |
| opacity | Overall alpha multiplier in [0, 1]. |
| void donner::geode::GeoEncoder::blitFullTargetBlended | ( | const gpu::Texture & | layer, |
| const gpu::Texture & | dstSnapshot, | ||
| uint32_t | blendMode, | ||
| double | opacity ) |
mix-blend-mode compositing.
Blits layer across the entire target using one of the 16 W3C Compositing Level 1 blend formulas, reading the backdrop from dstSnapshot (which the caller has already copied from the parent target because the shader cannot read the render pass's own color attachment).
Both textures must be the SAME SIZE as this encoder's target and already stored in premultiplied alpha. The encoder's pass must be in a LoadOp::Clear state - the blend shader writes the final pixel directly and relies on the render target being zeroed before the draw.
| layer | Offscreen RGBA8 layer texture (premultiplied). |
| dstSnapshot | Frozen copy of the parent target's current state (premultiplied) - the blend's backdrop. |
| blendMode | Value in 1..=16 matching the donner::svg::MixBlendMode enumeration. A value of 0 silently falls through to no-op. |
| opacity | Group opacity in [0, 1]. Applied to the layer BEFORE the blend formula so an opacity + mix-blend-mode combo on the same element composes correctly - the source colour that enters the blend is layer * opacity. |
| void donner::geode::GeoEncoder::blitFullTargetMasked | ( | const gpu::Texture & | content, |
| const gpu::Texture & | mask, | ||
| svg::MaskType | maskType, | ||
| const std::optional< Box2d > & | maskBounds ) |
<mask> compositing.
Same as blitFullTarget but additionally samples a mask texture and derives coverage according to maskType. When maskBounds is provided, pixels outside the rect are discarded so the <mask> element's x/y/width/height are honoured.
| content | Offscreen RGBA8 content texture, premultiplied, same size as this encoder's target. |
| mask | Offscreen RGBA8 mask texture, premultiplied, same size as content. |
| maskType | Whether mask coverage comes from luminance or alpha. |
| maskBounds | Optional clip rect in target-pixel space. |
| void donner::geode::GeoEncoder::clear | ( | const css::RGBA & | color | ) |
Clear the target texture to the given color.
Must be called before any draw calls - clear is implemented as the load op of the first render pass, so calling it after a draw is a no-op. Subsequent calls override the previous clear color.
| void donner::geode::GeoEncoder::drawImage | ( | const svg::ImageResource & | image, |
| const Box2d & | destRect, | ||
| double | opacity, | ||
| svg::ImageRendering | imageRendering ) |
Draw a raster image into the given destination rectangle.
The image's straight-alpha RGBA8 pixels are uploaded to a fresh GPU texture and sampled through the image-blit pipeline. The current transform is applied via the MVP matrix, and the destination rectangle is specified in local (pre-transform) coordinates - i.e., the transform and the rect compose the same way as any other draw call against this encoder.
| image | Decoded RGBA8 image resource. No-op if empty or zero-size. |
| destRect | Destination rectangle in local (pre-transform) space. |
| opacity | Overall opacity in [0, 1], combined with the sampled texel's alpha in the fragment shader. |
| imageRendering | Resolved smooth, crisp, or two-stage pixelated sampling policy. |
| void donner::geode::GeoEncoder::drawTexture | ( | const gpu::Texture & | texture, |
| const Box2d & | destRect, | ||
| const Box2d & | sourceUv, | ||
| double | opacity, | ||
| bool | pixelated, | ||
| bool | sourceIsPremultiplied ) |
Draw an already-GPU-resident RGBA texture into the destination rectangle.
The current transform is applied exactly like drawImage, but the texture is sampled directly without a CPU upload.
| texture | GPU-resident RGBA texture. |
| destRect | Destination rectangle in local (pre-transform) space. |
| sourceUv | Source UV rectangle in [0,1] x [0,1]. Producers that keep an oversized backing allocation pass the sub-rect that holds the valid content. |
| opacity | Overall opacity in [0, 1]. |
| pixelated | If true, use nearest-neighbor filtering. Otherwise, bilinear. |
| sourceIsPremultiplied | True when the texels are premultiplied, as Geode render-target snapshots are. CPU bitmaps uploaded by host presentation code are straight-alpha and must pass false. |
| bool donner::geode::GeoEncoder::ensureResidentSceneRecord | ( | GeodeResidentSlot & | slot, |
| const EncodedPath & | encoded, | ||
| const ScenePaint & | paint, | ||
| FillRule | rule, | ||
| const Transform2d & | recordTransform, | ||
| const GeodeRecordSlab::Slot * | recordSlotOverride = nullptr, | ||
| std::vector< uint8_t > * | overrideRecordCache = nullptr, | ||
| SceneRecordState * | recordState = nullptr, | ||
| bool | publishPaint = true ) |
Ensure a resident slot's geometry is uploaded and its instance record (record-slab slot, chunk-relative bases, transform-bearing) is current WITHOUT drawing.
Used by the cross-entity ordered-batch path, which draws many slots in one GPU call; solo draws call fillPathResident instead, which performs the same work and then records the draw.
| slot | Resident slot (geometry + record slab wiring installed by the renderer). |
| encoded | Precomputed EncodedPath shared with the cache. |
| color | Solid fill color (NOT premultiplied). |
| rule | Fill rule. |
| recordTransform | The full deviceFromLocal transform to bake into the record (the batch uniform is orthographic-only). |
| recordSlotOverride | When non-null, write the record into this slot instead of the entity's primary slot (same-frame repeat draws need one record per draw, and per-occurrence text records live outside the shared glyph slot entirely). |
| overrideRecordCache | Optional caller-owned copy of the override slot's last contents. Supplying it turns the override write into the same skip-when-unchanged write the primary record gets, which is what lets a persistent per-occurrence record reach a zero-write steady state. Null means the override slot is per-frame scratch and is always written. Ignored when recordSlotOverride is null. |
| recordState | Carries the record fields that come from encoder-side state. When publishPaint is true this is an OUT parameter: paint is published into the slot and the resulting scalars, plus the live clip rectangle, are written here. When it is false this is an IN parameter: the record takes these exact values and neither the slot's paint nor the encoder's current clip is consulted. Null on either side falls back to the slot's and the encoder's current state. |
| publishPaint | True to publish paint into the slot's paint block and paint scalars; false to replay recordState instead. A batch's flush-time re-ensure passes false, because by then the slot's paint may belong to a later draw and the encoder's clip may have moved on. |
| void donner::geode::GeoEncoder::fillPath | ( | const Path & | path, |
| const css::RGBA & | color, | ||
| FillRule | rule, | ||
| const EncodedPath * | precomputedEncoded = nullptr ) |
Fill a path with a solid color.
The path is encoded into Slug band data on the CPU, uploaded to GPU buffers, and a draw call is recorded. The fill is applied with the current transform.
| path | The path to fill. |
| color | Solid fill color (NOT premultiplied - the encoder handles premultiplication for the blend pipeline). |
| rule | Fill rule (NonZero or EvenOdd). |
| precomputedEncoded | Optional cache-hit payload - see GeodePathCacheComponent. When non-null, skips encode + counter bump. |
| void donner::geode::GeoEncoder::fillPathInstanced | ( | const EncodedPath & | encoded, |
| const css::RGBA & | color, | ||
| FillRule | rule, | ||
| std::span< const float > | instanceTransforms ) |
Fill N copies of the same encoded path at N different affine transforms, in one GPU draw call.
The caller has already packed each transform into the wire format the shader expects: two vec4f rows per instance (row-major affine, 32 bytes per entry). See donner/svg/renderer/geode/shaders/slug_fill.wgsl struct InstanceTransform for the exact layout.
The vertex shader composes each instance's transform with the currently-bound uniforms.mvp, so encoder.setTransform(...) still applies an outer world transform; per-instance data is the delta relative to that. Pass Transform2d::Identity to setTransform if instance transforms already encode the full path→clip-space mapping (the common case for <use> batching, where each instance's transform is the full worldFromEntity).
If instanceTransforms is empty the call is a no-op. If instanceTransforms.size() == 1 this is equivalent to a single fillPath with the given transform folded in - the caller can still prefer it when bouncing through the batcher.
| encoded | Precomputed EncodedPath shared across all instances. Required (non-null) - there's no "encode inline" path here; the whole point is to amortize one encode across many draws. |
| color | Solid fill color (NOT premultiplied). |
| rule | Fill rule. |
| instanceTransforms | Span of {row0, row1} vec4f pairs, two consecutive vec4f per instance (32 bytes each). The span must contain exactly 8 * instanceCount floats. |
| void donner::geode::GeoEncoder::fillPathIntoMask | ( | const Path & | path, |
| FillRule | rule, | ||
| const EncodedPath * | precomputedEncoded = nullptr ) |
Fill path into the currently open mask pass using the Slug mask pipeline.
Must be called between beginMaskPass and endMaskPass. The current encoder transform applies (so clip paths use the same device-pixel mapping as the content being clipped).
| precomputedEncoded | Optional cache-hit payload. When non-null, the encoder skips GeodePathEncoder::encode and the pathEncodes counter bump. Used by RendererGeode to plumb a cached GeodePathCacheComponent::strokeSlot result. |
| void donner::geode::GeoEncoder::fillPathLinearGradient | ( | const Path & | path, |
| const LinearGradientParams & | params, | ||
| FillRule | rule, | ||
| const EncodedPath * | precomputedEncoded = nullptr ) |
Fill a path with a linear gradient.
Same CPU-side Slug band encoding as fillPath, but the shading stage uses the linear-gradient pipeline - each pixel inside the path is colored by sampling the stop list at a per-pixel parameter t computed from params.gradientFromPath, params.startGrad, and params.endGrad, then folded through params.spreadMode.
If the path encodes to zero bands or the stop list is empty, the call is a no-op.
| path | The path to fill. |
| params | Linear gradient parameters. Colors are straight alpha in 0..1 per channel; the encoder premultiplies before upload. Up to 16 stops are honored; excess are silently truncated with a one-shot verbose warning at the call site in RendererGeode. |
| rule | Fill rule (NonZero or EvenOdd). |
| void donner::geode::GeoEncoder::fillPathLinearGradientResident | ( | GeodeResidentGradientSlot & | slot, |
| const EncodedPath & | encoded, | ||
| const LinearGradientParams & | params, | ||
| FillRule | rule, | ||
| uint64_t | frameId ) |
Fill a path with a linear gradient through a persistent GPU residence slot.
The slot's combined buffer holds the encoded band/curve/grid data and the 672-byte gradient uniform block; on an unchanged frame the draw rewrites nothing and reuses the cached 11-binding bind group. Falls back to the per-frame arena path internally when a clip mask, clip polygon, or mask pass is active (the cached bind group binds dummy clip-mask resources, which is only stable when no clip is active).
| slot | Resident gradient slot for this entity's fill. |
| encoded | Precomputed encode from the cached-path encode cache. |
| params | Resolved linear gradient parameters. |
| rule | Fill rule (NonZero or EvenOdd). |
| frameId | Current frame index (drives the once-per-frame gate). |
| void donner::geode::GeoEncoder::fillPathPattern | ( | const Path & | path, |
| FillRule | rule, | ||
| const PatternPaint & | paint, | ||
| const EncodedPath * | precomputedEncoded = nullptr ) |
Fill a path with a repeating pattern tile.
The pattern texture must have been rendered earlier (e.g., by a nested GeoEncoder) and contains premultiplied RGBA. The shader applies the Slug winding-number coverage test identically to fillPath, and samples the tile for pixels inside the path.
| void donner::geode::GeoEncoder::fillPathRadialGradient | ( | const Path & | path, |
| const RadialGradientParams & | params, | ||
| FillRule | rule, | ||
| const EncodedPath * | precomputedEncoded = nullptr ) |
Fill a path with a radial gradient.
Same CPU encoding and GPU-dispatch machinery as fillPathLinearGradient, but the gradient parameter t is derived from a two-circle radial construction in the shader (see radial_t() in shaders/slug_gradient.wgsl).
If the path encodes to zero bands, the stop list is empty, or params.radius <= 0, the call is a no-op.
| path | The path to fill. |
| params | Radial gradient parameters (center + radius, optional focal point + radius, shared transform and stops). |
| rule | Fill rule (NonZero or EvenOdd). |
| void donner::geode::GeoEncoder::fillPathRadialGradientResident | ( | GeodeResidentGradientSlot & | slot, |
| const EncodedPath & | encoded, | ||
| const RadialGradientParams & | params, | ||
| FillRule | rule, | ||
| uint64_t | frameId ) |
Radial variant of fillPathLinearGradientResident.
Same residence contract and arena fallback.
| void donner::geode::GeoEncoder::fillPathResident | ( | GeodeResidentSlot & | slot, |
| const EncodedPath & | encoded, | ||
| const css::RGBA & | color, | ||
| FillRule | rule, | ||
| uint64_t | frameId ) |
Solid fill of a cached path with persistent GPU residence.
slot is a per-entity GeodeResidentSlot (owned by GeodeResidentPathComponent, invalidated by the same ComputedPathComponent listener that clears the CPU encode cache). On first use the encoder uploads encoded into a persistent combined Vertex|Storage|Uniform buffer and builds a cached bind group; on every subsequent unchanged frame it re-uses both and writes zero geometry bytes. Only the 288-byte per-draw uniform is rewritten, and only when it actually changed (camera/color move), so a static document's steady-state frame writes ~zero bytes and creates zero bind groups.
Residency is taken ONLY for the fast, cacheable case: no active clip mask, no clip polygon, no open mask pass. When any of those hold the call transparently falls back to the per-frame arena path (a fresh per-draw bind group) so clipped / masked draws stay bit-exact.
| slot | Persistent residence for this entity's fill or stroke encode. Must outlive the frame's submit (it does: the ECS component is only removed on geometry change, after the prior frame submitted). |
| encoded | Cached encode (stable across frames). Must be non-empty for a draw to land. |
| color | Solid fill color (NOT premultiplied - premultiplied here, identically to fillPath). |
| rule | Fill rule. |
| frameId | Monotonic frame index. A slot serves at most one resident draw per frame (its single uniform buffer cannot hold distinct per-instance uniforms); repeat draws in the same frame fall back to the arena path. |
| void donner::geode::GeoEncoder::fillPathSceneBatch | ( | const css::RGBA & | color, |
| FillRule | rule, | ||
| const SceneBatchBinding & | binding ) |
Record ONE GPU draw covering a cross-entity batch of resident solid fills (ordered batching).
The caller has already ensured each instance's record via ensureResidentSceneRecord. The bind group binds the whole slab chunk for every geometry class (the records carry chunk-relative element bases) and the record span for binding 7. The draw issues firstInstance = binding.firstInstance and the maximum fan vertex count; the vertex shader folds extra triangles of smaller instances into a degenerate position.
The encoder transform must be identity when this is called (the batch uniform carries the orthographic mapping only).
| color | Solid fill color shared by every instance. |
| rule | Fill rule shared by every instance. |
| binding | Batch binding (chunk + record span + counts). |
| void donner::geode::GeoEncoder::finish | ( | ) |
Submit all encoded commands to the GPU queue.
After this call, the encoder is in a "finished" state and no further draws can be issued. The caller is responsible for any synchronization (e.g., MapAsync + Tick loop) needed to actually use the rendered output.
| void donner::geode::GeoEncoder::recordGeometryDebugInstance | ( | const EncodedPath & | encoded, |
| std::span< const float > | instanceTransforms ) |
Observe Slug draws recorded by this encoder.
rootFromTarget maps this encoder's target pixels to the owning renderer's final target. Pass null to disable observation. The default path stores one pointer and one branch per actual Slug submission. Record one scene-batch instance's geometry into the debug-overlay sink (no-op without a sink). Ordered batches issue a single GPU draw, so the caller reports each batched instance here in paint order, passing the instance's transform in the same packed 8-float wire format instanced draws use. The encoder transform must already be identity, matching the batch draw's uniform.
| void donner::geode::GeoEncoder::setAntialias | ( | bool | antialias | ) |
Enable analytic edge anti-aliasing for subsequent path and mask draws. Disabled mode emits binary coverage at the pixel center.
| antialias | True to retain analytic edge coverage. |
| void donner::geode::GeoEncoder::setBufferPool | ( | GeodeBufferPool * | pool | ) |
Install a cross-frame buffer pool.
When set, arena buffer growth prefers recycled buffers from the pool and the destructor returns the fully-grown arena buffers to it instead of destroying them, driving steady-state per-frame bufferCreates toward zero.
Call before the first draw. The pool (typically owned by RendererGeode::Impl) must outlive this encoder. Pass nullptr to disable (default).
| void donner::geode::GeoEncoder::setClipMask | ( | const gpu::Texture & | maskTexture, |
| const gpu::TextureView & | maskView ) |
Preferred overload: sets both the view AND the parent texture so the encoder keeps the underlying Vulkan resource alive for as long as it's bound.
The 1-arg overload is kept only for call sites that guarantee the parent's lifetime by other means.
See the activeClipMaskTexture field comment (and issue #551) for the bug this addresses - without the parent keepalive, popping the clip stack can free a VkImage while the encoder's createBindGroup still references its view.
| void donner::geode::GeoEncoder::setClipMask | ( | const gpu::TextureView & | maskView | ) |
Bind maskView as the clip mask texture for subsequent fill / gradient draws.
The view must reference a 1-sample RGBA8Unorm texture the same size as the encoder's target - typically the resolve texture produced by beginMaskPass + endMaskPass.
The shader samples .r at the pixel center and multiplies it into fragment coverage, so the mask represents a pre-rendered clip region in [0, 1].
| void donner::geode::GeoEncoder::setClipPolygon | ( | const Vector2d | corners[4] | ) |
Activate a convex 4-vertex clip polygon.
Unlike setScissorRect, this clips to the exact parallelogram described by the 4 corners - used for <symbol> / <svg> / <use> viewports that have a non-axis-aligned ancestor transform where WebGPU's rectangular scissor can only express the AABB of the transformed rect, not the true polygon. The fragment shader tests each of 4 edge half-planes at the fragment position and discards fragments outside the polygon.
| corners | 4 polygon vertices in target-pixel space, given in consistent (clockwise OR counter-clockwise) winding order. The encoder normalises edge normals so a fragment strictly INSIDE the polygon satisfies every half-plane test. |
| void donner::geode::GeoEncoder::setLoadPreserve | ( | ) |
Switch the next render pass's load op from Clear to Load, preserving whatever the target texture already contains.
Useful when the encoder is being reused to append draws on top of previously submitted content (e.g., resuming outer-frame drawing after a nested pattern-tile pass).
Must be called before any draws - once a render pass is open it's too late to change its load op.
| void donner::geode::GeoEncoder::setScissorRect | ( | int32_t | x, |
| int32_t | y, | ||
| int32_t | w, | ||
| int32_t | h ) |
Set a scissor rectangle in target-pixel coordinates.
Subsequent draws are clipped to the intersection of (0,0,targetWidth,targetHeight) and this rectangle. Used by RendererGeode::pushClip to implement SVG viewport rect clipping (nested <svg> clip, overflow: hidden, etc.).
The scissor persists across setTransform, fillPath, and friends until explicitly cleared via clearScissorRect.
Negative x/y and widths/heights that extend past the target are clamped internally; the caller can pass any AABB in pixel space without bounds-checking.