Where draw and pick decide "is this element here?" — and whether they decide
it the same way.
Draw and pick are two shaders over one geometry. Every per-element
visibility rule (thinning, near fade, the label filter, …) is written into
both, and every time the two drifted a pick landed on something not drawn,
or a drawn element could not be picked: the capsule line pick ignored the
density guard's drop for a release; the gsplat pick quad once reached less
far than the drawn one. Neither changes a pixel of the drawn image, so no
image gate could see it.
So each rule is declared here, for every (type, primitive variant):
'same': draw and pick both run the ONE shared helper, on both backends.
tests/unit/rendering/picking/draw-pick-parity.test.ts asserts the
helper's call in both GLSL sources and its emitted block in both TSL
codegen snapshots (src/tests/__codegen__), so a pick that forgets the
rule — or calls it without consuming the result, which TSL does not even
emit — fails.
{ deliberate }: the pick footprint is a deliberate SUBSET of the drawn
one (pick ⊂ draw), with the evidence that it still is.
{ drawOnly }: a draw-pass split that picking must not see.
{ neither }: the rule does not exist for this type — asserted absent
from both, so a rule that starts applying to it shows up here.
The CPU-side rules (nD slice, slab, position clipping) need no row per
shader: the pick node REUSES the visual node's geometry, which the parity
test asserts for every type (SHARED_GEOMETRY_RULES).
The pixel-level half lives in the TSL harness (tsl-shader-parity.spec.ts,
pick ⊆ draw masks) and the render gate's pickWithinDraw judgement.
Where draw and pick decide "is this element here?" — and whether they decide it the same way.
Draw and pick are two shaders over one geometry. Every per-element visibility rule (thinning, near fade, the label filter, …) is written into both, and every time the two drifted a pick landed on something not drawn, or a drawn element could not be picked: the capsule line pick ignored the density guard's drop for a release; the gsplat pick quad once reached less far than the drawn one. Neither changes a pixel of the drawn image, so no image gate could see it.
So each rule is declared here, for every
(type, primitive variant):'same': draw and pick both run the ONE shared helper, on both backends.tests/unit/rendering/picking/draw-pick-parity.test.tsasserts the helper's call in both GLSL sources and its emitted block in both TSL codegen snapshots (src/tests/__codegen__), so a pick that forgets the rule — or calls it without consuming the result, which TSL does not even emit — fails.{ deliberate }: the pick footprint is a deliberate SUBSET of the drawn one (pick ⊂ draw), with the evidence that it still is.{ drawOnly }: a draw-pass split that picking must not see.{ neither }: the rule does not exist for this type — asserted absent from both, so a rule that starts applying to it shows up here.The CPU-side rules (nD slice, slab, position clipping) need no row per shader: the pick node REUSES the visual node's geometry, which the parity test asserts for every type (
SHARED_GEOMETRY_RULES).The pixel-level half lives in the TSL harness (
tsl-shader-parity.spec.ts, pick ⊆ draw masks) and the render gate'spickWithinDrawjudgement.