Skip to content

render target: allow linear filtering, keep nearest as the default - #3

Open
shadowcodex wants to merge 1 commit into
ericdrowell:mainfrom
shadowcodex-forks:render-target-linear-filtering
Open

render target: allow linear filtering, keep nearest as the default#3
shadowcodex wants to merge 1 commit into
ericdrowell:mainfrom
shadowcodex-forks:render-target-linear-filtering

Conversation

@shadowcodex

@shadowcodex shadowcodex commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

createRenderTarget hard-codes its sampler to nearest:

// src/runtime/webgpu.ts
const sampler = device.createSampler({ magFilter: 'nearest', minFilter: 'nearest' });

The comment above it explains why:

rgba32float is not filterable without an opt-in feature, and averaging two particles' positions would be meaningless anyway — nearest, always.

The second half of that is right and this PR keeps it. The first half no longer matches the code: TARGET_FORMAT is rgba16float, which is filterable in core WebGPU with no optional feature, and no rgba32float target is created anywhere in the runtime. So the hardware constraint the comment cites does not apply to any target that exists.

Why it matters

It matters for a target that holds an image rather than state. A bloom downsample chain is the case: each level samples the one above it at half resolution, and with point sampling the result is blocky and crawls under motion. Today the only way to get a smooth downsample is many taps per texel, emulating in the shader the bilinear filter the hardware would do for free.

What changed

filter?: 'nearest' | 'linear' on RenderTargetOptions, defaulting to 'nearest'. No existing caller changes behaviour. The doc comment on RenderTarget is updated, since it currently states unfiltered sampling as unconditional.

Tests

Two checks added to scripts/gpu/entry.ts, following that file's rule that every assertion reads pixels back off the canvas. A 2×1 target holding a hard black/white step is magnified across the canvas and probed at 40% across — nearer the left texel's centre:

  • nearest must return that texel unchanged
  • linear must return a blend

Probing at exactly 50% would land on the tie-break between the two texels, where nearest is implementation-defined. I got that wrong first time and the test failed for the wrong reason, so the probe position now has a comment saying why it is 40%.

Both pass on Chrome/WebGPU. The node suite is unchanged at 318 passing, and npm run typecheck is clean. WebKit did not run locally — playwright-core install webkit was missing in my environment, not related to this change.

Context

Found while building an HDR post-processing pipeline on 0.17.2. It is one of two independent runtime fixes, submitted separately so each stands on its own; the sibling is offscreen multisampling. We are carrying both as local patches over the published package in the meantime, so there is no urgency from our side.


Over to you

Genuine questions, not politeness:

  • Is this applicable at all? If it does not fit where BroMetal is going, say so and close it — no offence taken. We carry it as a local patch over the published package today, so nothing of ours is blocked on it landing.
  • Is there a better approach? We arrived at this from the outside, without the context you have on the design. If you would solve it differently — a different API shape, a different layer, or a reason the current behaviour is deliberate — we would rather write your version than have you merge ours.
  • Anything you want changed — naming, comment style, test placement, scope — tell us and we will do it.

@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

@shadowcodex is attempting to deploy a commit to the Eric Rowell's projects Team on Vercel.

A member of the Team first needs to authorize it.

@shadowcodex

Copy link
Copy Markdown
Contributor Author

Please let me know if this isn't applicable, and if there is an alternative.

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