Skip to content

fix(example/game-2d): write ball radius in CSS pixels for WebGL/WebGPU parity - #140

Merged
eastspire merged 1 commit into
euv-dev:masterfrom
eastspire:master
Sep 4, 2026
Merged

fix(example/game-2d): write ball radius in CSS pixels for WebGL/WebGPU parity#140
eastspire merged 1 commit into
euv-dev:masterfrom
eastspire:master

Conversation

@eastspire

Copy link
Copy Markdown
Collaborator

fix(example/game-2d): write ball radius in CSS pixels for WebGL/WebGPU parity

PR #138 introduced DPR scaling inside game_2d_ball_gpu_record (writing ball.radius * dpr to the shader's pos_radius vec4). That was wrong: the shader's clip space is the same [-1, 1] NDC regardless of the backing store's physical size, so a CSS-unit radius already lands on r * dpr physical pixels via the viewport mapping. Multiplying by dpr in Rust made every WebGL/WebGPU ball render at 2 * radius CSS pixels on a DPR=2 display, so the GPU tabs showed balls visibly twice as large as the Canvas 2D tab's SSAA output. With visual radius > physics radius, the per-ball "free space" looked huge and users reported the balls were no longer colliding.

This change reverts the dpr multiplication: the radius is written in the same CSS-unit space as ball.position, which makes the visual ball size match the Canvas 2D path exactly (both end up as radius CSS pixels after the browser downscale of the physical backing to the element's CSS box) and makes the shader's per-ball disc the same size the physics solver is solving for, so the stacking/collision behavior the user saw in the Canvas 2D tab is restored in the WebGL/WebGPU tabs.

The dpr parameter is preserved on the helper signature so callers keep their threaded-through arguments and a future change to the clip-space mapping assumption can be applied here without a wider signature change.

Validated at DPR=2 headless: 2D / GL / GPU tabs now show visually identical ball sizes and the four initial balls settle into the same cluster at the canvas floor.

…U parity

PR euv-dev#138 introduced DPR scaling inside `game_2d_ball_gpu_record` (writing
`ball.radius * dpr` to the shader's pos_radius vec4). That was wrong:
the shader's clip space is the same [-1, 1] NDC regardless of the
backing store's physical size, so a CSS-unit radius already lands on
`r * dpr` physical pixels via the viewport mapping. Multiplying by dpr
in Rust made every WebGL/WebGPU ball render at `2 * radius` CSS pixels
on a DPR=2 display, so the GPU tabs showed balls visibly twice as
large as the Canvas 2D tab's SSAA output. With visual radius > physics
radius, the per-ball "free space" looked huge and users reported the
balls were no longer colliding.

This change reverts the dpr multiplication: the radius is written in
the same CSS-unit space as `ball.position`, which makes the visual
ball size match the Canvas 2D path exactly (both end up as `radius`
CSS pixels after the browser downscale of the physical backing to the
element's CSS box) and makes the shader's per-ball disc the same size
the physics solver is solving for, so the stacking/collision behavior
the user saw in the Canvas 2D tab is restored in the WebGL/WebGPU tabs.

The `dpr` parameter is preserved on the helper signature so callers
keep their threaded-through arguments and a future change to the
clip-space mapping assumption can be applied here without a wider
signature change.

Validated at DPR=2 headless: 2D / GL / GPU tabs now show visually
identical ball sizes and the four initial balls settle into the same
cluster at the canvas floor.
@eastspire
eastspire merged commit aacf9e4 into euv-dev:master Sep 4, 2026
8 checks passed
eastspire added a commit that referenced this pull request Sep 4, 2026
Includes:
- #140 fix(example/game-2d): write ball radius in CSS pixels for WebGL/WebGPU parity

CI's sync_workspace_version job will propagate the new root version to all sub-crates on master merge.

Co-authored-by: eastspire <eastspire@users.noreply.github.com>
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.

1 participant