EngineTest
Prompt
resolve.wgsl (voxelcraft, WGSL) -- part 1 of 8. This is the hot per-pixel shading stage of a ray-marched voxel renderer: every line here runs for every pixel, every frame, so a cycle saved here is worth more than anywhere else in the codebase. YOUR TASK -- take your time and reason carefully before writing anything. A. DIAGNOSE. Read this part properly and say what is actually wrong with it: incorrect or fragile math, precision hazards, work recomputed per-pixel that is loop-invariant or constant, duplicated evaluations of the same field, dead terms, branches that cannot be taken, reads that could be folded or hoisted. B. OPTIMISE, hardest-hitting first. Rank what you find by real impact -- cost x how often it executes -- and fix the ones that matter. Cheap equivalent math, hoisting, CSE, fewer texture/tree fetches. Do not micro-tune things that run once. C. PRESERVE the visible output bit-for-bit unless I say otherwise below. If an optimisation changes pixels at all, do not apply it -- list it instead. D. CONTAIN THE BLAST RADIUS. Prefer edits that stay inside one function. Any change that would touch a signature, a shared constant, a binding, an entry point, or a line another file reads -- DO NOT APPLY IT. Put it under RISKY with your reasoning and let me decide. E. If a part of this is already good, say so and leave it alone. Inventing changes to look busy is worse than doing nothing here. HARD CONSTRAINTS -- violating any of these breaks the build: 1. This file is NOT valid WGSL on disk and does NOT brace-balance (435 { vs 432 } whole-file). That is CORRECT. Never "fix" it. 2. Lines //#rr-on / //#rr-off / //#rr-end are load-bearing directives read by select_rr in src/render/mod.rs. Keep every one, verbatim, in place. Never merge, reorder or drop the arms between them. 3. Entry points resolve / water_sec / glass_sec / bake_tint are called by name from Rust. Never rename them or change their signatures. 4. ~35 test files pin lines of this shader as literal SOURCE TEXT. Do not reword, reformat or reflow any line you are not deliberately changing. 5. Never introduce a // comment inside a string literal; test pins split on //. 6. Shared constants (e.g. SURFACE_EPS, BRICK_WORDS) are defined once and mirrored in Rust/common.wgsl. Do not rename or redefine them. 7. This shader sits on a shader-compile cliff: near-any addition wedges pipeline creation. Smallest possible edit wins. Add nothing speculative. 8. You are seeing part 1 of 8 only. If something looks unused or wrong, it may be used in a part you cannot see -- say so rather than deleting it. ISSUE I AM HITTING: <<< DESCRIBE YOUR ACTUAL PROBLEM HERE >>> REPLY IN THIS ORDER: 1) FINDINGS -- one line each, ranked by impact, with your confidence. 2) The rewritten part, complete and verbatim, nothing elided. 3) RISKY -- anything you chose not to apply, and why. --- BEGIN PART 1/8 --- fn surface_sky_fill(n: vec3<f32>) -> vec3<f32> { let sky = sky_ambient_tint(n); if !SPEC_LIGHTING_REPAIR { return sky; } return sky * (0.60 * mix(0.35, 1.0, clamp(n.y * 0.5 + 0.5, 0.0, 1.0))); } fn surface_light(ambient: vec3<f32>, sun: vec3<f32>, ao: f32, floor_light: vec3<f32>, face: f32) -> vec3<f32> { if !SPEC_LIGHTING_REPAIR { return ((ambient + sun) * ao + floor_light) * face; } return ambient * ao * face + sun + floor_light; } fn surface_radiance(albedo: vec3<f32>, light: vec3<f32>, spec: vec3<f32>, id: u32) -> vec3<f32> { if SPEC_LIGHTING_REPAIR && id == GLOWSTONE_ID { return albedo * (vec3<f32>(1.0, 0.7111, 0.3024) * 1.5 + light * 0.15); } return albedo * light + spec; } //#rr-on var<private> rr_pixel: vec2<u32> = vec2<u32>(0u); fn rr_spec_albedo(roughness: f32, f0: vec3<f32>, ndotv: f32) -> vec3<f32> { let alpha = roughness * roughness; let nov = abs(ndotv); let x = vec4<f32>(1.0, nov, nov * nov, nov * nov * nov); let y = vec4<f32>(1.0, alpha, alpha * alpha, alpha * alpha * alpha); let m1 = vec2<f32>(0.99044 * x.x - 1.28514 * x.y, 1.29678 * x.x - 0.755907 * x.y); let m2 = vec3<f32>(x.x + 2.92338 * x.y + 59.4188 * x.w, 20.3225 * x.x - 27.0302 * x.y + 222.592 * x.w, 121.563 * x.x + 626.13 * x.y + 316.627 * x.w); let m3 = vec2<f32>(0.0365463 * x.x + 3.32707 * x.y, 9.0632 * x.x - 9.04756 * x.y); let m4 = vec3<f32>(x.x + 3.59685 * x.z - 1.36772 * x.w, 9.04401 * x.x - 16.3174 * x.z + 9.22949 * x.w, 5.56589 * x.x + 19.7886 * x.z - 20.2123 * x.w); var bias = dot(m1, y.xy) / dot(m2, y.xyw); let scale = dot(m3, y.xy) / dot(m4, y.xyw); bias = bias * clamp(f0.g * 50.0, 0.0, 1.0); return f0 * max(0.0, scale) + vec3<f32>(max(0.0, bias)); } fn rr_debug_color(albedo: vec3<f32>, normal: vec3<f32>, roughness: f32, f0: vec3<f32>, ndotv: f32) -> vec4<f32> { if frame.surface_debug == 8u { return vec4<f32>(albedo, 1.0); } if frame.surface_debug == 9u { return vec4<f32>(normal * 0.5 + vec3<f32>(0.5), 1.0); } if frame.surface_debug == 10u { return vec4<f32>(vec3<f32>(clamp(roughness, 0.0, 1.0)), 1.0); } if frame.surface_debug == 11u { return vec4<f32>(rr_spec_albedo(roughness, f0, ndotv), 1.0); } return vec4<f32>(0.0); } //#rr-end fn face_shade(normal_id: u32) -> f32 { switch normal_id { case 3u: { return 1.00; } case 2u: { return 0.50; } case 4u, 5u: { return 0.80; } case 6u, 7u: { return 0.90; } default: { return 0.62; } } } fn block_ambient(blk: vec3<f32>, sk: f32, sky: vec3<f32>) -> vec3<f32> { let bk = max(blk.r, max(blk.g, blk.b)); let tint = blk / max(bk, 1e-4); return mix(sky, tint, bk / max(sk + bk, 1e-4)) * max(sk, bk); } fn heat(v: f32) -> vec3<f32> { let t = clamp(v, 0.0, 1.0); let c = vec3<f32>( smoothstep(0.35, 0.85, t), smoothstep(0.0, 0.45, t) - smoothstep(0.65, 1.0, t), 1.0 - smoothstep(0.0, 0.45, t), ); return pow(c, vec3<f32>(2.2)); } fn light_curve(level: f32) -> f32 { let l = level / 15.0; return l * l * (0.6 + 0.4 * l); } fn light_curve3(level: vec3<f32>) -> vec3<f32> { let l = level / 15.0; return l * l * (vec3<f32>(0.6) + 0.4 * l); } const SKY_TINT: vec3<f32> = vec3<f32>(0.6038, 0.7084, 1.0000); fn luma3(c: vec3<f32>) -> f32 { return 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b; } const SKY_W_UP: f32 = 0.78431373; const SKY_W_SIDE: f32 = 0.58578900; const SKY_AMBIENT_REF: vec3<f32> = vec3<f32>(0.14658824, 0.30178431, 0.84105882); const SKY_TINT_LUMA: f32 = 0.70721556; const SKY_HALO_ENERGY: f32 = 4.0; const SKY_TINT_SAT: f32 = 2.5; fn sky_ambient_tint(n: vec3<f32>) -> vec3<f32> { if !SPEC_SKY_TINT { return SKY_TINT; } let day = frame.daylight; let horizon = mix(SKY_NIGHT_HORIZON, SKY_DAY_HORIZON, day); let zenith = mix(SKY_NIGHT_ZENITH, SKY_DAY_ZENITH, day); let w = select(select(SKY_W_SIDE, SKY_W_UP, n.y > 0.5), 0.0, n.y < -0.5); var c = mix(horizon, zenith, w); c = c + SCATTER_TINT * (frame.fog_scatter * day * SKY_HALO_ENERGY * max(dot(n, frame.sun_dir), 0.0)); let t = SKY_TINT * pow(c / SKY_AMBIENT_REF, vec3<f32>(SKY_TINT_SAT)); return t * (SKY_TINT_LUMA / max(luma3(t), 1e-6)); } const BLOCK_TINT: vec3<f32> = vec3<f32>(1.0000, 0.6500, 0.3200); const AO_STRENGTH: f32 = 0.25; const PROBE_DIM: f32 = 128.0; const PROBE_SPACING: f32 = 4.0; const PROBE_INV_SPAN: f32 = 1.0 / (PROBE_DIM * PROBE_SPACING); const PROBE_TAPS: u32 = 1u; const PROBE_TAP_STRIDE: f32 = 0.37; const PROBE_SLABS: f32 = 6.0; const PROBE_NORMAL_BIAS: f32 = PROBE_SPACING * 0.5; fn probe_field(p: vec3<f32>, normal_id: u32) -> vec4<f32> { let cross = normal_id >= NORMAL_CROSS_A; let slab = select(normal_id, 3u, cross); let t = p * (1.0 / PROBE_SPACING); if (frame.flags_hi & FLAG_PROBE_NEAREST) != 0u { let cx = ((i32(floor(t.x)) % i32(PROBE_DIM)) + i32(PROBE_DIM)) % i32(PROBE_DIM); let cz = ((i32(floor(t.z)) % i32(PROBE_DIM)) + i32(PROBE_DIM)) % i32(PROBE_DIM); let cy = clamp(i32(floor(t.y)), 0, i32(PROBE_DIM) - 1) + i32(slab) * i32(PROBE_DIM); return textureLoad(probe_tex, vec3<i32>(cx, cy, cz), 0); } let y = clamp(t.y, 0.5, PROBE_DIM - 0.5); let uvw = vec3<f32>( t.x * (1.0 / PROBE_DIM), (f32(slab) * PROBE_DIM + y) * (1.0 / (PROBE_DIM * PROBE_SLABS)), t.z * (1.0 / PROBE_DIM), ); return textureSampleLevel(probe_tex, probe_samp, uvw, 0.0); } fn probe_tap(p: vec3<f32>) -> vec3<f32> { var acc = vec3<f32>(1.0); for (var i = 0u; i < PROBE_TAPS; i = i + 1u) { let uvw = fract(p * PROBE_INV_SPAN + vec3<f32>(0.0, 0.0, f32(i) * PROBE_TAP_STRIDE)); acc = acc * textureSampleLevel(probe_tex, linear_samp, uvw, 0.0).rgb; } return acc; } fn face_uv(normal_id: u32, local: vec3<f32>) -> vec2<f32> { if normal_id >= NORMAL_CROSS_A { return vec2<f32>(local.x, 1.0 - local.y); } let axis = normal_id >> 1u; var uv: vec2<f32>; if axis == 0u { uv = vec2<f32>(local.z, 1.0 - local.y); } else if axis == 1u { uv = vec2<f32>(local.x, local.z); } else { uv = vec2<f32>(local.x, 1.0 - local.y); } if normal_id == 1u || normal_id == 4u { uv.x = 1.0 - uv.x; } return uv; } fn perm_key(box_min: vec3<f32>, normal_id: u32) -> u32 { let c = vec3<i32>(floor(box_min)); return hash_u32(bitcast<u32>(c.x) * 0x9e3779b9u ^ bitcast<u32>(c.y) * 0x85ebca6bu ^ bitcast<u32>(c.z) * 0xc2b2ae35u ^ normal_id * 0x27d4eb2fu); } fn permute_uv(uv: vec2<f32>, pclass: u32, h: u32) -> vec2<f32> { let d4 = pclass == PERM_D4; var p = select(uv, vec2<f32>(uv.y, uv.x), d4 && (h & 1u) != 0u); p.x = select(p.x, 1.0 - p.x, pclass != PERM_NONE && (h & 2u) != 0u); p.y = select(p.y, 1.0 - p.y, d4 && (h & 4u) != 0u); return p; } const BIOME_BAND: f32 = 0.22; const BIOME_BLEND: f32 = 0.18; const BIOME_TEMP_SEED: i32 = 7; const BIOME_HUMID_SEED: i32 = 8; const BIOME_PRIME_X: u32 = 501125321u; const BIOME_PRIME_Y: u32 = 1136930381u; const GRAD24_FIRST: f32 = 1.4398966328953218; const GRAD24_STEP: f32 = 0.26179938779914946; const GRAD8_FIRST: f32 = 1.1780972450961724; const GRAD8_STEP: f32 = 0.7853981633974483; fn simplex_grad(seed: u32, x_primed: u32, y_primed: u32, xd: f32, yd: f32) -> f32 { let h = (seed ^ x_primed ^ y_primed) * 0x27d4eb2du; let hs = bitcast<i32>(h); let idx = u32((hs ^ (hs >> 15u)) & 254i); var angle: f32; if idx < 240u { angle = GRAD24_FIRST - GRAD24_STEP * f32((idx % 48u) >> 1u); } else { angle = GRAD8_FIRST - GRAD8_STEP * f32((idx - 240u) >> 1u); } return xd * cos(angle) + yd * sin(angle); } fn biome_fast_floor(f: f32) -> i32 { if f >= 0.0 { return i32(f); } return i32(f) - 1; } fn biome_noise(seed_i: i32, freq: f32, p: vec2<f32>) -> f32 { let seed = bitcast<u32>(seed_i); let sqrt3 = 1.7320508075688772; let g2 = (3.0 - sqrt3) / 6.0; let f2 = 0.5 * (sqrt3 - 1.0); var xy = p * freq; let s = (xy.x + xy.y) * f2; xy = xy + vec2<f32>(s, s); let i0 = biome_fast_floor(xy.x); let j0 = biome_fast_floor(xy.y); let xi = xy.x - f32(i0); let yi = xy.y - f32(j0); let t = (xi + yi) * g2; let x0 = xi - t; let y0 = yi - t; let i = bitcast<u32>(i0) * BIOME_PRIME_X; let j = bitcast<u32>(j0) * BIOME_PRIME_Y; let a = 0.5 - x0 * x0 - y0 * y0; var n0 = 0.0; if a > 0.0 { n0 = (a * a) * (a * a) * simplex_grad(seed, i, j, x0, y0); } let c = (2.0 * (1.0 - 2.0 * g2) * (1.0 / g2 - 2.0)) * t + ((-2.0 * (1.0 - 2.0 * g2) * (1.0 - 2.0 * g2)) + a); var n2 = 0.0; if c > 0.0 { let x2 = x0 + (2.0 * g2 - 1.0); let y2 = y0 + (2.0 * g2 - 1.0); n2 = (c * c) * (c * c) * simplex_grad(seed, i + BIOME_PRIME_X, j + BIOME_PRIME_Y, x2, y2); } var n1 = 0.0; if y0 > x0 { let x1 = x0 + g2; let y1 = y0 + (g2 - 1.0); let b = 0.5 - x1 * x1 - y1 * y1; if b > 0.0 { n1 = (b * b) * (b * b) * simplex_grad(seed, i, j + BIOME_PRIME_Y, x1, y1); } } else { let x1 = x0 + (g2 - 1.0); let y1 = y0 + g2; let b = 0.5 - x1 * x1 - y1 * y1; if b > 0.0 { n1 = (b * b) * (b * b) * simplex_grad(seed, i + BIOME_PRIME_X, j, x1, y1); } } return (n0 + n1 + n2) * 99.83685446303647; } fn biome_band_weights(n: f32) -> vec3<f32> { let hot = smoothstep(BIOME_BAND - BIOME_BLEND, BIOME_BAND + BIOME_BLEND, n); let cold = 1.0 - smoothstep(-BIOME_BAND - BIOME_BLEND, -BIOME_BAND + BIOME_BLEND, n); return vec3<f32>(cold, 1.0 - cold - hot, hot); } const TINT_PLAINS: vec3<f32> = vec3<f32>(1.000, 1.000, 1.000); const TINT_PLAINS_BALANCED: vec3<f32> = vec3<f32>(0.935, 1.067, 0.892); const TINT_FOREST: vec3<f32> = vec3<f32>(0.664, 1.045, 1.000); const TINT_SAVANNA: vec3<f32> = vec3<f32>(1.842, 0.935, 0.793); const TINT_DESERT: vec3<f32> = vec3<f32>(2.096, 0.957, 0.699); const TINT_TAIGA: vec3<f32> = vec3<f32>(0.832, 0.935, 2.096); const TINT_TUNDRA: vec3<f32> = vec3<f32>(0.755, 0.893, 2.812); fn biome_palette(temperature: f32, humidity: f32) -> vec3<f32> { let wt = biome_band_weights(temperature); let wh = biome_band_weights(humidity); let plains = select(TINT_PLAINS, TINT_PLAINS_BALANCED, SPEC_TINT_BALANCE); let dry = wt.x * TINT_TUNDRA + wt.y * plains + wt.z * TINT_DESERT; let mid = wt.x * TINT_TAIGA + wt.y * plains + wt.z * TINT_SAVANNA; let wet = wt.x * TINT_TAIGA + wt.y * TINT_FOREST + wt.z * TINT_FOREST; return wh.x * dry + wh.y * mid + wh.z * wet; } const TINT_BAKE_DIM: u32 = 256u; const TINT_BAKE_CELL: f32 = 8.0; fn tint_lut_fetch(cell: vec2<i32>) -> vec2<f32> { let cx = clamp(cell.x, 0, i32(TINT_BAKE_DIM) - 1); let cz = clamp(cell.y, 0, i32(TINT_BAKE_DIM) - 1); let w = tint_lut[u32(cz) * TINT_BAKE_DIM + u32(cx)]; return vec2<f32>(f32(w >> 16u) / 65535.0 * 2.0 - 1.0, f32(w & 0xFFFFu) / 65535.0 * 2.0 - 1.0); } fn biome_tint_baked(world_xz: vec2<f32>) -> vec3<f32> { let g = (world_xz - frame.tint_bake_origin) / TINT_BAKE_CELL + vec2<f32>(f32(TINT_BAKE_DIM) * 0.5); let f = fract(g); let c = vec2<i32>(floor(g)); let t00 = tint_lut_fetch(c); let t10 = tint_lut_fetch(c + vec2<i32>(1, 0)); let t01 = tint_lut_fetch(c + vec2<i32>(0, 1)); let t11 = tint_lut_fetch(c + vec2<i32>(1, 1)); let n = mix(mix(t00, t10, f.x), mix(t01, t11, f.x), f.y); return biome_palette(n.x, n.y); } fn biome_tint(world_xz: vec2<f32>) -> vec3<f32> { if (frame.flags_hi & FLAG_TINT_BAKE) != 0u { return biome_tint_baked(world_xz); } let temperature = biome_noise(frame.tint_seed + BIOME_TEMP_SEED, frame.tint_freq_t, world_xz); let humidity = biome_noise(frame.tint_seed + BIOME_HUMID_SEED, frame.tint_freq_h, world_xz); return biome_palette(temperature, humidity); } @compute @workgroup_size(8, 8, 1) --- END PART 1/8 ---