myth_render 0.3.0

Core rendering system for the Myth engine (render graph, pipeline, GPU management).
Documentation
// Debug View — visualise intermediate RDG textures.
//
// A full-screen post-process pass that remaps arbitrary texture formats
// into a displayable [0, 1] RGB range.  The `view_mode` uniform selects
// the mapping strategy so a single pipeline can handle depth, normals,
// single-channel occlusion, and standard colour buffers alike.

{$ include 'core/full_screen_vertex' $}
{{ clustered_lighting_structs }}

struct DebugUniforms {
    view_mode: u32,
    custom_scale: f32,
    z_near: f32,
    z_far: f32,
};

// Group 0 (static): sampler + uniforms — owned by the Feature.
@group(0) @binding(0) var debug_sampler: sampler;
@group(0) @binding(1) var<uniform> uniforms: DebugUniforms;

// Group 1 (transient): source texture — rebuilt each frame.
$$ if IS_DEPTH
@group(1) @binding(0) var debug_texture: texture_depth_2d;
$$ else
@group(1) @binding(0) var debug_texture: texture_2d<f32>;
$$ endif
@group(1) @binding(1) var<uniform> u_clustered_lighting: ClusteredLightingParams;
@group(1) @binding(2) var<storage, read> st_cluster_records: array<ClusterRecord>;

fn heatmap_color(t: f32) -> vec3<f32> {
    let value = clamp(t, 0.0, 1.0);
    if (value < 0.33) {
        let local = value / 0.33;
        return mix(vec3<f32>(0.04, 0.08, 0.24), vec3<f32>(0.00, 0.72, 0.96), local);
    }
    if (value < 0.66) {
        let local = (value - 0.33) / 0.33;
        return mix(vec3<f32>(0.00, 0.72, 0.96), vec3<f32>(0.98, 0.86, 0.18), local);
    }
    let local = (value - 0.66) / 0.34;
    return mix(vec3<f32>(0.98, 0.86, 0.18), vec3<f32>(0.96, 0.18, 0.12), local);
}

fn tonemap_debug(color: vec3<f32>, exposure: f32) -> vec3<f32> {
    let hdr = max(color * max(exposure, 0.001), vec3<f32>(0.0));
    let mapped = hdr / (vec3<f32>(1.0) + hdr);
    return pow(mapped, vec3<f32>(1.0 / 2.2));
}

fn debug_world_hit_grid(world_pos: vec3<f32>) -> vec3<f32> {
    let scaled = world_pos * 0.75;
    let base = fract(vec3<f32>(
        dot(scaled, vec3<f32>(0.1031, 0.11369, 0.13787)),
        dot(scaled, vec3<f32>(0.2695, 0.1833, 0.2461)),
        dot(scaled, vec3<f32>(0.2473, 0.2921, 0.1737))
    ));
    let cell = abs(fract(scaled) - vec3<f32>(0.5));
    let line = 1.0 - smoothstep(0.46, 0.5, min(min(cell.x, cell.y), cell.z));
    return mix(base, vec3<f32>(1.0), line * 0.85);
}

fn debug_trace_texel_diagnostic(diag: vec4<f32>) -> vec3<f32> {
    let delta_pixels = diag.xy;
    let error_pixels = diag.z;
    let direction = clamp(delta_pixels * 0.5 + vec2<f32>(0.5, 0.5), vec2<f32>(0.0), vec2<f32>(1.0));
    let magnitude = clamp(error_pixels / 1.5, 0.0, 1.0);
    let axis_emphasis = clamp(abs(delta_pixels) / 0.5, vec2<f32>(0.0), vec2<f32>(1.0));

    let direction_color = vec3<f32>(direction.x, direction.y, 1.0 - 0.5 * (direction.x + direction.y));
    let magnitude_color = heatmap_color(magnitude);
    let axis_overlay = vec3<f32>(axis_emphasis.x, axis_emphasis.y, max(axis_emphasis.x, axis_emphasis.y));

    var color = mix(vec3<f32>(0.02, 0.04, 0.08), magnitude_color, magnitude);
    color = mix(color, direction_color, 0.45);
    color = mix(color, axis_overlay, 0.20 * step(0.02, error_pixels));
    return color;
}

fn debug_trace_state(diag: vec4<f32>) -> vec3<f32> {
    let stage_value = diag.w;
    let stage = u32(floor(stage_value + 1e-3));
    let near_clip_active = fract(stage_value) > 0.25;

    var color = vec3<f32>(0.0);
    switch stage {
        case 1u: {
            color = vec3<f32>(0.18, 0.18, 0.18);
        }
        case 2u: {
            color = vec3<f32>(0.86, 0.16, 0.68);
        }
        case 3u: {
            color = vec3<f32>(0.14, 0.86, 0.92);
        }
        case 4u: {
            color = vec3<f32>(0.94, 0.90, 0.24);
        }
        case 5u: {
            color = vec3<f32>(0.74, 0.22, 0.92);
        }
        case 6u: {
            color = vec3<f32>(0.18, 0.50, 1.0);
        }
        case 7u: {
            color = vec3<f32>(1.0, 0.58, 0.12);
        }
        case 8u: {
            color = vec3<f32>(1.0, 0.78, 0.22);
        }
        case 9u: {
            color = vec3<f32>(1.0, 0.22, 0.32);
        }
        case 10u: {
            color = vec3<f32>(0.96, 0.88, 0.18);
        }
        case 11u: {
            color = vec3<f32>(0.20, 0.94, 0.36);
        }
        case 12u: {
            color = vec3<f32>(0.96, 0.36, 0.78);
        }
        default: {
            color = vec3<f32>(0.0);
        }
    }

    if (near_clip_active) {
        color = mix(color, vec3<f32>(0.18, 0.96, 1.0), 0.45);
    }

    return color;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    $$ if IS_DEPTH
    let depth_val = textureSampleLevel(debug_texture, debug_sampler, in.uv, 0i);
    let tex_color = vec4<f32>(depth_val, depth_val, depth_val, 1.0);
    $$ else
    let tex_color = textureSampleLevel(debug_texture, debug_sampler, in.uv, 0.0);
    $$ endif

    switch uniforms.view_mode {
        // Mode 1: SSAO / Roughness / Metallic
        case 1u: {
            return vec4<f32>(tex_color.rrr, 1.0);
        }
        // Mode 2: World/View Normals
        case 2u: {
            return vec4<f32>(tex_color.rgb * 0.5 + 0.5, 1.0);
        }
        // Mode 3: Velocity / Motion Vectors
        case 3u: {
            let vel = tex_color.xy * uniforms.custom_scale;
            let abs_vel = abs(vel);
            let positive_vel = max(vel, vec2<f32>(0.0));
            let negative_vel = max(-vel, vec2<f32>(0.0));
            
            let color = vec3<f32>(
                positive_vel.x + negative_vel.y, // R
                positive_vel.y + negative_vel.x, // G
                negative_vel.x + negative_vel.y  // B
            );

            return vec4<f32>(color + vec3<f32>(length(vel)), 1.0);
        }
        // Mode 4: Depth (Reverse-Z)
        case 4u: {
            let ndc_z = tex_color.r; 
            let linear_depth = (uniforms.z_near * uniforms.z_far) / 
                               (uniforms.z_near + ndc_z * (uniforms.z_far - uniforms.z_near));
            
            let display_depth = linear_depth / uniforms.z_far;
            
            let fract_depth = fract(display_depth * uniforms.custom_scale);

            return vec4<f32>(vec3<f32>(display_depth * 0.8 + fract_depth * 0.2), 1.0);
        }
        // Mode 5: Clustered lighting heatmap
        case 5u: {
            let clustered_enabled = (u_clustered_lighting.budget.z & 1u) != 0u;
            if (!clustered_enabled) {
                return vec4<f32>(0.0, 0.0, 0.0, 1.0);
            }

            $$ if IS_DEPTH
            let depth_ndc = textureSampleLevel(debug_texture, debug_sampler, in.uv, 0i);
            if (depth_ndc <= 0.0) {
                return vec4<f32>(0.0, 0.0, 0.0, 1.0);
            }

            let screen_size = vec2<f32>(
                max(f32(u_clustered_lighting.screen_dimensions.x), 1.0),
                max(f32(u_clustered_lighting.screen_dimensions.y), 1.0),
            );
            let pixel = clamp(in.uv * screen_size, vec2<f32>(0.0), screen_size - vec2<f32>(1.0));
            let grid_x = max(u_clustered_lighting.screen_dimensions.z, 1u);
            let grid_y = max(u_clustered_lighting.screen_dimensions.w, 1u);
            let grid_z = max(u_clustered_lighting.grid_dimensions.x, 1u);
            let tile_size_x = max(f32(u_clustered_lighting.grid_dimensions.z), 1.0);
            let tile_size_y = max(f32(u_clustered_lighting.grid_dimensions.w), 1.0);

            let cluster_x = min(u32(pixel.x / tile_size_x), grid_x - 1u);
            let cluster_y = min(u32(pixel.y / tile_size_y), grid_y - 1u);
            let linear_depth = clamp(
                u_clustered_lighting.depth_params.x / max(depth_ndc, 0.00001),
                u_clustered_lighting.depth_params.x,
                u_clustered_lighting.depth_params.y,
            );
            let cluster_z = min(
                u32(max(
                    floor(log(linear_depth) * u_clustered_lighting.depth_params.z
                        + u_clustered_lighting.depth_params.w),
                    0.0,
                )),
                grid_z - 1u,
            );

            let cluster_index = min(
                cluster_z * (grid_x * grid_y) + cluster_y * grid_x + cluster_x,
                max(u_clustered_lighting.grid_dimensions.y, 1u) - 1u,
            );
            let cluster_count = f32(st_cluster_records[cluster_index].count);
            let cluster_budget = max(f32(u_clustered_lighting.budget.x), 1.0);
            let normalized = cluster_count / cluster_budget;
            var color = heatmap_color(normalized);

            let grid = abs(fract(pixel / vec2<f32>(tile_size_x, tile_size_y)) - 0.5);
            let grid_line = 1.0
                - smoothstep(0.46, 0.5, max(grid.x, grid.y));
            color = mix(color, vec3<f32>(0.0), grid_line * 0.35);

            return vec4<f32>(color, 1.0);
            $$ else
            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
            $$ endif
        }
        // Mode 6: SSGI raw indirect radiance
        case 6u: {
            return vec4<f32>(tonemap_debug(tex_color.rgb, uniforms.custom_scale), 1.0);
        }
        // Mode 7: SSGI denoised indirect radiance + accumulation hint
        case 7u: {
            let mapped = tonemap_debug(tex_color.rgb, uniforms.custom_scale);
            let confidence = clamp(tex_color.a / 8.0, 0.0, 1.0);
            let overlay = mix(vec3<f32>(0.08, 0.16, 0.44), vec3<f32>(0.28, 0.96, 0.36), confidence);
            return vec4<f32>(mix(mapped, overlay, 0.18), 1.0);
        }
        // Mode 8: SSR raw reflection confidence
        case 8u: {
            let mapped = tonemap_debug(tex_color.rgb, uniforms.custom_scale);
            let confidence = clamp(tex_color.a, 0.0, 1.0);
            let overlay = mix(vec3<f32>(0.10, 0.06, 0.22), vec3<f32>(0.94, 0.58, 0.18), confidence);
            return vec4<f32>(mix(mapped, overlay, 0.16), 1.0);
        }
        // Mode 9: SSR resolved reflection confidence
        case 9u: {
            let mapped = tonemap_debug(tex_color.rgb, uniforms.custom_scale);
            let confidence = clamp(tex_color.a, 0.0, 1.0);
            let overlay = mix(vec3<f32>(0.05, 0.10, 0.20), vec3<f32>(0.30, 0.92, 0.86), confidence);
            return vec4<f32>(mix(mapped, overlay, 0.18), 1.0);
        }
        // Mode 10: SSR trace consistency diagnostic
        case 10u: {
            if (tex_color.a <= 1e-4) {
                return vec4<f32>(0.0, 0.0, 0.0, 1.0);
            }
            return vec4<f32>(debug_trace_texel_diagnostic(tex_color), 1.0);
        }
        // Mode 11: SSR trace state diagnostic
        case 11u: {
            if (tex_color.a <= 1e-4) {
                return vec4<f32>(0.0, 0.0, 0.0, 1.0);
            }
            return vec4<f32>(debug_trace_state(tex_color), 1.0);
        }
        // Default: colour pass-through
        default: {
            return vec4<f32>(tex_color.rgb, 1.0);
        }
    }
}