Repository navigation
fix(scene): sample transparent pictures premultiplied - #47
Conversation
Quest-Claim: 2a06fa636f663bc1
Bilinear sampling mixed straight-alpha texels, so a scaled picture took on the color of its transparent neighbours at its edges. Stills now upload premultiplied and canvases and rendered frames, being opaque, are marked so, keeping them on the sampler's fast path. An overlay's straight paint, and a straight mask, premultiply each of their four taps in the shader before mixing them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A picture drawn at its own size samples on texel centers, where one texel is the whole mix, so it skips the four loads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 25 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 9984e60
No actionable findings. Tracking opaque frames fits the existing upload and composition paths, and the pinned video renderer produces opaque alpha. The author-reported scaled-overlay stress case has a 67% frame-time increase, so that trade-off merits monitoring.
Verification: GitHub-only static review; I did not run GPU tests or benchmarks. The new GPU regression is ignored by ordinary CI, and the hosted Check was still running when inspected.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — this run reviewed the full 5-file diff at 9984e60 and checked that every picture producer's alpha convention matches the new premultiplied mixing path.
- CPU premultiply for stills and colors —
Compositor::updatepremultipliesPicture::Rgbaonce before its single upload;Mailbox::takemoves the buffer out, so there is no double premultiply. - Opaque frame/canvas contract — moq-video writes alpha 1.0 for every frame and transition canvases are opaque, so marking them premultiplied needs no conversion pass;
Effects::placenow honors the picture's flag. - Per-tap premultiply in
effect.wgsl—filtered()premultiplies eachtextureLoadbilinear tap and keeps a one-sample fast path at texel centers; masks andcoverage()use the same path without a second premultiply. - Vello straight-alpha premise — confirmed against vello v0.11.0 (
research/vello_shaders/shader/fine.wgsl), which unpremultiplies at its final store, so overlays correctly stay straight. - Quest and test updates — deletes the completed quest plus its README row and adds a GPU regression that fails on
main; the#[ignore]reason matches the nightlyrender-swlane.
The author-reported +67% frame time for scaled overlays is a measured, documented trade-off with no added GPU copies, not a defect.
big-pickle (free) | 𝕏
Vello paints straight alpha into a texture of its own, and one pass after each paint copies it premultiplied into the texture the compositor samples. Every picture is now premultiplied, so draws and masks take the sampler's hardware path, and the effect pass drops its premultiplied flag and the per-tap bilinear path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Design change: per the maintainer's 2026-11-05 decision, overlays are no longer premultiplied per bilinear tap in (Written by Claude Opus 5.5) |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit af0fe72. No actionable findings in the substantive update since 9984e60, accounting for merged base changes. Premultiplying overlays once per repaint rather than per tap is a reasonable simplification of the rendering path.
Verification: GitHub-only static review of texture lifetimes, ordering, caching, masks, and shared-canvas interactions. I did not run tests or benchmarks.

Finishes quest/m0/premultiply.md: a scaled source with transparency, such as a PNG logo or an overlay, draws without a dark or colored fringe at its edges.
Change
Bilinear sampling mixed straight-alpha texels, so a transparent texel's color (often black, sometimes junk) bled into its neighbours when a picture was scaled. Every picture is now stored premultiplied, so every draw and mask mixes premultiplied on the sampler's hardware path:
Picture::Rgba) are premultiplied on the CPU incompositor.rsbefore their one upload.moq/rs/moq-video/src/render/shader.wgsl), and opaque straight equals premultiplied. Canvases used by transitions are opaque too. A page arrives premultiplied, as before.overlay.rs,PREMULTIPLY) copies it premultiplied into the texture the compositor samples. The pass runs only when the overlay repaints, never per frame.Since every input is now premultiplied, the effect pass drops its
premultipliedflag (Picture,Input, the uniform'smode.y, and the mask op'sparams.y), andeffect.wgsldrops the per-tapfiltered()path;straight()always divides.Decisions
effect.wgslFrame time
Measured on this Mac (Apple Silicon, Metal) with a temporary bench, not committed: a 1080p canvas rendering preview and program each frame, waiting on the GPU, the clock advancing 33 ms a frame. Medians of 300 frames after 30 to warm up; 15 rounds interleaving the three builds, rotating their order each round; the table gives the median of the 15 medians (the minimum was within 0.1 ms of it in every cell).
mainis origin/main,per-tapis this PR's previous head merged with main,passis this head.Scaled overlays are back to
main's cost (-43% against per-tap). The bench was rebuilt from the previous description, so its scenes approximate the earlier ones; its per-tap column reproduces the earlier numbers within 0.1 ms.Repaint cost
The same scenes with the overlay's CSS animating, so it repaints every frame:
A repaint (Vello and, on
pass, the premultiply copy) adds 0.56 ms onmainand 0.70 ms onpassfor the 1920x1080 overlay, so the copy costs about 0.14 ms per repaint there, and about 0.04 ms for the 700x700 one (0.57 against 0.61 ms).GPU copies: one per overlay repaint, none per frame. Encoder load: unchanged (the program texture is the same size and format).
Choices made without asking
Compositor::updatenames that coupling.Validation
just ci: passed onaf0fe72.cargo nextest run -p moq-scene --lib --release --run-ignored only -E 'test(/^compositor::tests::/)'): 31 passed onaf0fe72.compositor::tests::scaling_bleeds_no_transparent_colorscales a still with a transparent green texel, an overlay with a transparent texel, and a luminance mask with a transparent white texel. It fails onmain(logo at 2: [195, 28, 0, 255]), and with the premultiply pass's shader made to copy straight, its overlay row fails.Review
Codex (gpt-6.1-sol, medium effort),
quest review --new --base origin/mainround 1 on headceb6137d3f58ec38232879484edd1241b6999166, after the design change: no findings. Headaf0fe72adds only conflict-free merges of origin/main (#46, #49, #50), which touch none of this PR's code.Follow-ups
None.
🤖 Generated with Claude Code