Skip to content

refactor(path-layer): separate dash arclength from geometry - #10647

Open
chrisgervang wants to merge 3 commits into
codex/path-style-trips-dash-baselinefrom
codex/path-style-dash-varyings
Open

refactor(path-layer): separate dash arclength from geometry#10647
chrisgervang wants to merge 3 commits into
codex/path-style-trips-dash-baselinefrom
codex/path-style-dash-varyings

Conversation

@chrisgervang

@chrisgervang chrisgervang commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Goal

Keep PathLayer's geometric coordinates geometric when dashing is enabled.

DASH_ENABLED previously repurposed vPathPosition.y and vPathLength for 3D/source-segment arclength. The fragment shader then needed a second bounds varying just to recover the original cap and joint geometry. This change gives dashing one dedicated packed coordinate and lets PathLayer use the same geometric cap, joint, miter, and analytic-AA logic in every variant.

Stack (merge bottom-up): #10644#10646#10647#10648#10649

Changes

  • Replace vPathBounds with vDashSegment, a packed [source-segment position, complete segment length] varying.
  • Keep vPathPosition, vPathLength, and geometry.uv geometric with or without DASH_ENABLED.
  • Use one unconditional geometric cap/joint predicate in the PathLayer fragment shader.
  • Make PathStyleExtension consume the dedicated dash coordinate for local phase and segment justification.
  • Scale geometric and dash coordinates together in refactor(extensions): order PathStyle shader coordinates #10644's ordered offset pipeline.
  • Preserve TripsLayer's source-relative timestamp interpolation when camera clipping shortens a dashed billboard segment.
  • Mirror the interface in GLSL and WGSL while reusing the existing two-component varying footprint and WGSL location 8.

No public TypeScript props/types, uniforms, attributes, package-entrypoint exports, or checked-in golden images change. PathStyleExtension remains WebGL-only; the WGSL work keeps the core synthetic DASH_ENABLED path structurally aligned and compilable, but does not add WebGPU PathStyle support.

Render equivalence

A strict same-machine A/B against #10646 captured 55 WebGL behaviors: 44 PathStyle dash cases, 10 PathLayer cases, and the TripsLayer case. All 55 behavior captures were byte-for-byte and pixel-for-pixel identical.

That matrix includes the clipped dash composition baseline and #10646's clipped Trips interpolation baseline, plus segment/path modes, units, justification, offsets, rounded caps, analytic AA, subpixel patterns, billboard paths, 3D paths, and ordinary PathLayer/Trips rendering. No golden image changed in this PR.

Internal shader compatibility

vPathPosition, vPathLength, and geometry.uv are internal shader values rather than public layer API. A custom shader injection that intentionally relied on their dash-only source-arclength meaning should use vDashSegment under DASH_ENABLED. Built-in TripsLayer is migrated in this commit.

Performance and resource impact

The refactor adds no vertex attribute, buffer layout, uniform, binding, draw call, or per-instance byte. vDashSegment replaces vPathBounds as one packed vec2 and reuses WGSL location 8. There is one narrower linked-shader cost: in whole-path mode the geometric vPathLength is now used by core cap/joint logic, whereas the parent shader could optimize that scalar away. Active WebGL interstage use moves from 10 variables / 20 scalar components to 11 / 21 in path mode, and is unchanged in segment mode.

The existing PathStyle attribute costs remain:

Capability WebGL attribute locations Accessor-backed storage per rendered segment
segment dash array +1 up to 8 bytes (2 floats)
path-mode phase and total length +1 8 bytes (2 floats)
offset +1 up to 4 bytes (1 float)

At 100,000 rendered segments with accessor-driven values, those existing PathStyle buffers cost about 0.76 MiB for each two-float attribute and 0.38 MiB for offsets, or up to 1.91 MiB for path-mode dashing plus offset. Constant dash arrays and offsets can use constant attributes instead of per-segment buffers. This PR allocates none of that memory anew and changes none of those sizes.

On the local 16-location WebGL2 device, plain PathLayer uses 13 locations, segment dashing 14, path dashing 15, and path dashing plus offset 16. Plain TripsLayer uses 15 and TripsLayer plus segment dashing uses 16. TripsLayer plus path-mode dashing requires 17 and fails to link on that device; adding offset can require 17 or 18. These are inherited feature-composition limits, not a delta from this refactor.

The resulting varying budget remains comfortable: the largest audited WebGL combination, TripsLayer plus dashing, uses 12 logical varying locations / 22 scalar components, versus 31 / 124 on headless SwiftShader and 30 / 120 on the Apple M1 Max / ANGLE Metal benchmark device. The WGSL path reuses location 8 for the replacement pair rather than allocating a new interstage location.

Apple M1 Max, Chrome/ANGLE Metal, WebGL2 timestamp queries, 3840x2160. The table is the median of four alternating parent/head rounds with 30 samples per variant and workload in each round, comparing 17137f6c0c with 35b98fef56:

100K-path workload parent separated coordinates delta
segment dash 2.8238 ms 2.8316 ms +0.31%
path dash, widths 2.9183 ms 2.9446 ms +0.89%
offset only 2.5626 ms 2.5653 ms +0.10%
all controls 2.8974 ms 2.9367 ms +1.35%

The fragment-heavy path and all-controls cases measured +1.42% and +1.32%. The plain control moved +0.10%, so the path-mode signal is larger than ordinary run drift and is consistent with the extra live geometric scalar. The largest absolute sparse delta was 0.0393 ms, about 0.24% of a 60 Hz frame or 0.47% of a 120 Hz frame. The latest synchronized merge commits preserve these direct feature trees.

Validation

  • yarn build
  • yarn lint — 1,181 files; lockfile valid
  • yarn test-headless --run test/modules/layers/path-antialiasing.spec.ts test/modules/extensions/path.spec.ts test/modules/extensions/clip.spec.ts test/modules/geo-layers/trips-layer.spec.ts — 39/39 passed
  • RENDER_TEST_DEVICE=webgl yarn test-render --run test/render/test-cases/path-dash.spec.ts test/render/test-cases/path-layer.spec.ts test/render/test-cases/polygon-layer.spec.ts test/render/test-cases/trips-layer.spec.ts — 63 passed, 5 intentional skips
  • Strict parent/child capture comparison — 55/55 behavior captures byte- and pixel-identical
  • Pre-commit affected tests — 19/19 passed
  • git diff --exit-code origin/codex/path-style-trips-dash-baseline -- test/render/golden-images

The focused WGSL assembly/device tests passed locally, and GitHub's test-webgpu check passed on the current synchronized head. PathStyleExtension itself remains WebGL-only.


Note

Medium Risk
Changes core path/Trips shader varyings and coordinate semantics; custom shader injections that relied on dash-only meaning of vPathPosition.y must switch to vDashSegment, though public layer API is unchanged.

Overview
PathLayer no longer overloads vPathPosition / vPathLength for dash math when DASH_ENABLED is on. A dedicated vDashSegment varying ([position along source segment, full segment length]) replaces vPathBounds, so caps, joints, analytic AA, and clipping use the same geometric coordinates with or without dashing.

PathStyleExtension dash phase, justification, and offset remapping now read vDashSegment instead of repurposed path position. TripsLayer (GLSL/WGSL) interpolates trip time from vDashSegment when dashing is enabled so timestamps stay tied to the full source segment when billboard clipping shortens visible geometry.

Core PathLayer fragment shaders drop dash-specific bound checks; tests assert dash and non-dash fragment bodies stay aligned aside from the new varying.

Reviewed by Cursor Bugbot for commit 5dd2826. Bugbot is set up for automated code reviews on this repo. Configure here.

@coveralls

coveralls commented Sep 3, 2026

Copy link
Copy Markdown

Coverage Status

Coverage is 82.498%codex/path-style-dash-varyings into codex/path-style-trips-dash-baseline. No base build found for codex/path-style-trips-dash-baseline.

@greptile-apps

greptile-apps Bot commented Sep 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR separates dash arclength from PathLayer’s geometric coordinates while preserving clipped TripsLayer timestamp interpolation.

  • Replaces the dash-only vPathBounds varying with the packed vDashSegment source coordinate in GLSL and WGSL.
  • Keeps cap, joint, miter, and antialiasing calculations on invariant geometric coordinates.
  • Updates PathStyleExtension dash phase and justification calculations to consume the dedicated dash coordinate.
  • Updates TripsLayer to interpolate timestamps over the complete source segment when billboard clipping shortens visible geometry.
  • Extends shader assembly and structural tests for the new interface.

Confidence Score: 5/5

The PR appears safe to merge with no concrete correctness, security, or build failures identified.

The GLSL and WGSL implementations consistently separate geometric and dash coordinates, preserve source-relative interpolation through clipping, and maintain matching conditional shader interfaces with focused assembly and rendering coverage.

Important Files Changed

Filename Overview
modules/layers/src/path-layer/path-layer-vertex.glsl.ts Separates geometric segment coordinates from source-segment dash arclength while preserving billboard clipping and 3D scaling.
modules/layers/src/path-layer/path-layer-fragment.glsl.ts Unifies cap, joint, and antialiasing geometry checks across dashed and undashed variants.
modules/layers/src/path-layer/path-layer.wgsl.ts Mirrors the coordinate separation and location-8 varying replacement in the WGSL implementation.
modules/extensions/src/path-style/shaders.glsl.ts Moves dash phase, justification, and offset remapping to the dedicated source-segment coordinate.
modules/geo-layers/src/trips-layer/trips-layer.ts Uses source-relative dash progress for timestamp interpolation when clipping shortens billboard geometry.
modules/geo-layers/src/trips-layer/trips-layer.wgsl.ts Mirrors conditional source-relative timestamp interpolation in WGSL.
test/modules/extensions/path.spec.ts Updates shader-ordering and dash-justification assertions for vDashSegment.
test/modules/geo-layers/trips-layer.spec.ts Adds enabled and disabled WGSL assembly coverage for dash-based TripsLayer interpolation.
test/modules/layers/path-antialiasing.spec.ts Verifies dashed and undashed core fragment geometry remain structurally identical.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Source[Source segment] --> Vertex[PathLayer vertex shader]
  Vertex --> Geometry[Geometric coordinates<br/>vPathPosition + vPathLength]
  Vertex --> Dash[Dash arclength<br/>vDashSegment]
  Geometry --> Core[Caps, joints, miters, analytic AA]
  Dash --> Style[PathStyle dash phase and justification]
  Dash --> Trips[TripsLayer source-relative time]
  Core --> Fragment[Fragment output]
  Style --> Fragment
  Trips --> Fragment
Loading

Reviews (1): Last reviewed commit: "Merge updated clipped Trips baseline" | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants