mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-30 13:08:07 -06:00
Cause: Auto dropped to Half below 75 % magnification, so text between 50 % and 75 % was rendered with fewer pixels than the screen shows and looked soft. Below 100 % the frame texture was minified with a plain bilinear sampler and no mip levels (Full at 46 % skipped most frame pixels), so edges stair-stepped. Fix: - Auto renders the pixels the magnification needs, as in After Effects: the coarsest whole factor that still gives every screen pixel a rendered one (Full above 50 %, Half down to 33.3 %, Third down to 25 %, Quarter below). - GPU frames: the display texture carries a full mip chain, each level the 2 x 2 mean of the one above (display.wgsl `mip`, same bind group layout as `display`), sampled trilinearly with Viewer Zoom Quality More Accurate (Faster keeps the nearest pixel of the full-size frame). - CPU frames (egui-wgpu textures have no mips): uploaded averaged down by the whole factor that leaves about one texel per screen pixel, the same box average as the texture-limit fit (#201), re-uploaded when the magnification calls for another factor; drawn with texture coordinates that keep each texel over its pixels when the size isn't a multiple. Show Channel / exposure textures follow. Tests: gpu tests::display_frames_are_mipmapped (level count and level 1 = 2 x 2 means of level 0), viewer_tools auto_resolution_follows_the_magnification (60 % was Half) and minified_frames_keep_their_texels_in_place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
37 lines
1.6 KiB
WebGPU Shading Language
37 lines
1.6 KiB
WebGPU Shading Language
// Viewer display: premultiplied f32 → premultiplied RGBA8 (ui-egui's frames::to_color_image),
|
||
// written straight into a texture egui samples.
|
||
|
||
@group(0) @binding(1) var dsrc: texture_2d<f32>;
|
||
@group(0) @binding(3) var dout: texture_storage_2d<rgba8unorm, write>;
|
||
|
||
@compute @workgroup_size(16, 16)
|
||
fn display(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||
let dims = vec2<i32>(textureDimensions(dout));
|
||
let p = vec2<i32>(gid.xy);
|
||
if (p.x >= dims.x || p.y >= dims.y) {
|
||
return;
|
||
}
|
||
let s = textureLoad(dsrc, p, 0);
|
||
let a = clamp(s.w, 0.0, 1.0);
|
||
// (v · 255 + 0.5) truncated, as the CPU conversion.
|
||
let q = floor(vec4<f32>(clamp(s.xyz, vec3<f32>(0.0), vec3<f32>(a)), a) * 255.0 + 0.5) / 255.0;
|
||
textureStore(dout, p, q);
|
||
}
|
||
|
||
// The display texture's next mip level (`dsrc` = the level above, `dout` = this one): each texel
|
||
// the mean of the 2 × 2 premultiplied texels it covers, as ui-egui's fitted CPU textures average
|
||
// (#417). A level one texel wide or high repeats its edge.
|
||
@compute @workgroup_size(16, 16)
|
||
fn mip(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||
let dims = vec2<i32>(textureDimensions(dout));
|
||
let p = vec2<i32>(gid.xy);
|
||
if (p.x >= dims.x || p.y >= dims.y) {
|
||
return;
|
||
}
|
||
let last = vec2<i32>(textureDimensions(dsrc)) - vec2<i32>(1);
|
||
let q = p * 2;
|
||
let s = textureLoad(dsrc, min(q, last), 0) + textureLoad(dsrc, min(q + vec2<i32>(1, 0), last), 0)
|
||
+ textureLoad(dsrc, min(q + vec2<i32>(0, 1), last), 0) + textureLoad(dsrc, min(q + vec2<i32>(1, 1), last), 0);
|
||
textureStore(dout, p, s * 0.25);
|
||
}
|