Skip to main content

blade_render/render/
mod.rs

1mod debug;
2
3use crate::{CameraParams, DummyResources, EnvironmentMap};
4use debug::{DebugEntry, DebugVariance};
5
6pub(crate) use debug::DebugRender;
7pub use debug::{DebugBlit, DebugLine, DebugPoint};
8
9use std::{collections::HashMap, mem, num::NonZeroU32, path::Path, ptr};
10
11const MAX_RESOURCES: u32 = 8192;
12const RADIANCE_FORMAT: blade_graphics::TextureFormat = blade_graphics::TextureFormat::Rgba16Float;
13
14fn mat4_transform(t: &blade_graphics::Transform) -> glam::Mat4 {
15    glam::Mat4 {
16        x_axis: t.x.into(),
17        y_axis: t.y.into(),
18        z_axis: t.z.into(),
19        w_axis: glam::Vec4::W,
20    }
21    .transpose()
22}
23fn mat3_transform(t_orig: &blade_graphics::Transform) -> glam::Mat3 {
24    let t = mint::ColumnMatrix3x4::from(*t_orig);
25    glam::Mat3 {
26        x_axis: t.x.into(),
27        y_axis: t.y.into(),
28        z_axis: t.z.into(),
29    }
30}
31
32#[derive(Clone, Copy, Debug)]
33pub struct RenderConfig {
34    pub surface_size: blade_graphics::Extent,
35    pub surface_info: blade_graphics::SurfaceInfo,
36    pub max_debug_lines: u32,
37}
38
39struct Samplers {
40    nearest: blade_graphics::Sampler,
41    linear: blade_graphics::Sampler,
42}
43
44#[derive(
45    Clone, Copy, Debug, Default, PartialEq, PartialOrd, blade_macros::AsPrimitive, strum::EnumIter,
46)]
47#[repr(u32)]
48pub enum DebugMode {
49    #[default]
50    Final = 0,
51    Depth = 1,
52    DiffuseAlbedoTexture = 2,
53    DiffuseAlbedoFactor = 3,
54    NormalTexture = 4,
55    NormalScale = 5,
56    GeometryNormal = 6,
57    ShadingNormal = 7,
58    Motion = 8,
59    HitConsistency = 9,
60    SampleReuse = 10,
61    Variance = 15,
62}
63
64bitflags::bitflags! {
65    #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd)]
66    pub struct DebugDrawFlags: u32 {
67        const SPACE = 1;
68        const GEOMETRY = 2;
69        const RESTIR = 4;
70    }
71}
72
73bitflags::bitflags! {
74    #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd)]
75    pub struct DebugTextureFlags: u32 {
76        const ALBEDO = 1;
77        const NORMAL = 2;
78    }
79}
80
81#[derive(Clone, Copy, Debug, Default)]
82pub struct DebugConfig {
83    pub view_mode: DebugMode,
84    pub draw_flags: DebugDrawFlags,
85    pub texture_flags: DebugTextureFlags,
86    pub mouse_pos: Option<[i32; 2]>,
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
90pub struct RayConfig {
91    pub num_environment_samples: u32,
92    pub environment_importance_sampling: bool,
93    pub tap_count: u32,
94    pub tap_radius: u32,
95    pub tap_confidence_near: u32,
96    pub tap_confidence_far: u32,
97    pub t_start: f32,
98    /// Evaluate MIS factor for ReSTIR in a pair-wise fashion.
99    /// Adds 2 extra visibility rays per reused sample.
100    pub pairwise_mis: bool,
101    /// Defensive MIS factor for the canonical sample.
102    /// Can be between 0 and 1.
103    pub defensive_mis: f32,
104}
105
106#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
107pub struct DenoiserConfig {
108    pub num_passes: u32,
109    pub temporal_weight: f32,
110}
111
112#[derive(Clone, Copy, Debug)]
113pub struct PostProcConfig {
114    //TODO: compute automatically
115    pub average_luminocity: f32,
116    pub exposure_key_value: f32,
117    pub white_level: f32,
118}
119impl Default for PostProcConfig {
120    fn default() -> Self {
121        Self {
122            average_luminocity: 1.0,
123            exposure_key_value: 1.0,
124            white_level: 1.0,
125        }
126    }
127}
128
129pub struct SelectionInfo {
130    pub std_deviation: mint::Vector3<f32>,
131    pub std_deviation_history: u32,
132    pub custom_index: u32,
133    pub depth: f32,
134    pub position: mint::Vector3<f32>,
135    pub normal: mint::Vector3<f32>,
136    pub tex_coords: mint::Vector2<f32>,
137    pub base_color_texture: Option<blade_asset::Handle<crate::Texture>>,
138    pub normal_texture: Option<blade_asset::Handle<crate::Texture>>,
139}
140impl Default for SelectionInfo {
141    fn default() -> Self {
142        Self {
143            std_deviation: [0.0; 3].into(),
144            std_deviation_history: 0,
145            custom_index: 0,
146            depth: 0.0,
147            position: [0.0; 3].into(),
148            normal: [0.0; 3].into(),
149            tex_coords: [0.0; 2].into(),
150            base_color_texture: None,
151            normal_texture: None,
152        }
153    }
154}
155
156struct RenderTarget<const N: usize> {
157    texture: blade_graphics::Texture,
158    views: [blade_graphics::TextureView; N],
159}
160impl<const N: usize> RenderTarget<N> {
161    fn new(
162        name: &str,
163        format: blade_graphics::TextureFormat,
164        size: blade_graphics::Extent,
165        encoder: &mut blade_graphics::CommandEncoder,
166        gpu: &blade_graphics::Context,
167    ) -> Self {
168        let texture = gpu.create_texture(blade_graphics::TextureDesc {
169            name,
170            format,
171            size,
172            dimension: blade_graphics::TextureDimension::D2,
173            array_layer_count: N as u32,
174            mip_level_count: 1,
175            usage: blade_graphics::TextureUsage::RESOURCE | blade_graphics::TextureUsage::STORAGE,
176            sample_count: 1,
177            external: None,
178        });
179        encoder.init_texture(texture);
180
181        let mut views = [blade_graphics::TextureView::default(); N];
182        for (i, view) in views.iter_mut().enumerate() {
183            *view = gpu.create_texture_view(
184                texture,
185                blade_graphics::TextureViewDesc {
186                    name: &format!("{name}{i}"),
187                    format,
188                    dimension: blade_graphics::ViewDimension::D2,
189                    subresources: &blade_graphics::TextureSubresources {
190                        base_array_layer: i as u32,
191                        array_layer_count: NonZeroU32::new(1),
192                        ..Default::default()
193                    },
194                },
195            );
196        }
197
198        Self { texture, views }
199    }
200
201    fn destroy(&self, gpu: &blade_graphics::Context) {
202        gpu.destroy_texture(self.texture);
203        for view in self.views.iter() {
204            gpu.destroy_texture_view(*view);
205        }
206    }
207}
208
209struct RestirTargets {
210    reservoir_buf: [blade_graphics::Buffer; 2],
211    debug: RenderTarget<1>,
212    depth: RenderTarget<2>,
213    basis: RenderTarget<2>,
214    flat_normal: RenderTarget<2>,
215    albedo: RenderTarget<1>,
216    motion: RenderTarget<1>,
217    light_diffuse: RenderTarget<3>,
218    camera_params: [CameraParams; 2],
219}
220
221impl RestirTargets {
222    fn new(
223        size: blade_graphics::Extent,
224        reservoir_size: u32,
225        encoder: &mut blade_graphics::CommandEncoder,
226        gpu: &blade_graphics::Context,
227    ) -> Self {
228        let total_reservoirs = size.width as usize * size.height as usize;
229        let mut reservoir_buf = [blade_graphics::Buffer::default(); 2];
230        for (i, rb) in reservoir_buf.iter_mut().enumerate() {
231            *rb = gpu.create_buffer(blade_graphics::BufferDesc {
232                name: &format!("reservoirs{i}"),
233                size: reservoir_size as u64 * total_reservoirs as u64,
234                memory: blade_graphics::Memory::Device,
235            });
236        }
237
238        Self {
239            reservoir_buf,
240            debug: RenderTarget::new(
241                "debug",
242                blade_graphics::TextureFormat::Rgba8Unorm,
243                size,
244                encoder,
245                gpu,
246            ),
247            depth: RenderTarget::new(
248                "depth",
249                blade_graphics::TextureFormat::R32Float,
250                size,
251                encoder,
252                gpu,
253            ),
254            basis: RenderTarget::new(
255                "basis",
256                blade_graphics::TextureFormat::Rgba8Snorm,
257                size,
258                encoder,
259                gpu,
260            ),
261            flat_normal: RenderTarget::new(
262                "flat-normal",
263                blade_graphics::TextureFormat::Rgba8Snorm,
264                size,
265                encoder,
266                gpu,
267            ),
268            albedo: RenderTarget::new(
269                "albedo",
270                blade_graphics::TextureFormat::Rgba8Unorm,
271                size,
272                encoder,
273                gpu,
274            ),
275            motion: RenderTarget::new(
276                "motion",
277                blade_graphics::TextureFormat::Rg8Snorm,
278                size,
279                encoder,
280                gpu,
281            ),
282            light_diffuse: RenderTarget::new("light-diffuse", RADIANCE_FORMAT, size, encoder, gpu),
283            camera_params: [CameraParams::default(); 2],
284        }
285    }
286
287    fn destroy(&self, gpu: &blade_graphics::Context) {
288        for rb in self.reservoir_buf.iter() {
289            gpu.destroy_buffer(*rb);
290        }
291        self.debug.destroy(gpu);
292        self.depth.destroy(gpu);
293        self.basis.destroy(gpu);
294        self.flat_normal.destroy(gpu);
295        self.albedo.destroy(gpu);
296        self.motion.destroy(gpu);
297        self.light_diffuse.destroy(gpu);
298    }
299}
300
301struct Blur {
302    temporal_accum_pipeline: blade_graphics::ComputePipeline,
303    a_trous_pipeline: blade_graphics::ComputePipeline,
304}
305
306/// Blade RayTracer is a comprehensive rendering solution for
307/// end user applications.
308///
309/// It takes care of the shaders, geometry buffers, acceleration structures,
310/// dummy resources, and debug drawing.
311///
312/// It doesn't:
313///   - manage or submit any command encoders
314///   - know about the window to display on
315pub struct RayTracer {
316    shaders: Shaders,
317    targets: RestirTargets,
318    post_proc_input_index: usize,
319    fill_pipeline: blade_graphics::ComputePipeline,
320    main_pipeline: blade_graphics::ComputePipeline,
321    post_proc_pipeline: blade_graphics::RenderPipeline,
322    blur: Blur,
323    acceleration_structure: blade_graphics::AccelerationStructure,
324    prev_acceleration_structure: blade_graphics::AccelerationStructure,
325    env_map: EnvironmentMap,
326    dummy: DummyResources,
327    hit_buffer: blade_graphics::Buffer,
328    vertex_buffers: blade_graphics::BufferArray<MAX_RESOURCES>,
329    index_buffers: blade_graphics::BufferArray<MAX_RESOURCES>,
330    textures: blade_graphics::TextureArray<MAX_RESOURCES>,
331    samplers: Samplers,
332    reservoir_size: u32,
333    debug: DebugRender,
334    surface_size: blade_graphics::Extent,
335    surface_info: blade_graphics::SurfaceInfo,
336    frame_index: usize,
337    frame_scene_built: usize,
338    is_frozen: bool,
339    //TODO: refactor `ResourceArray` to not carry the freelist logic
340    // This way we can embed user info into the allocator.
341    texture_resource_lookup:
342        HashMap<blade_graphics::ResourceIndex, blade_asset::Handle<crate::Texture>>,
343}
344
345#[repr(C)]
346#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
347pub(crate) struct DebugParams {
348    view_mode: u32,
349    draw_flags: u32,
350    texture_flags: u32,
351    unused: u32,
352    mouse_pos: [i32; 2],
353}
354
355#[repr(C)]
356#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
357struct MainParams {
358    frame_index: u32,
359    num_environment_samples: u32,
360    environment_importance_sampling: u32,
361    tap_count: u32,
362    tap_radius: f32,
363    tap_confidence_near: f32,
364    tap_confidence_far: f32,
365    t_start: f32,
366    use_pairwise_mis: u32,
367    defensive_mis: f32,
368    use_motion_vectors: u32,
369}
370
371#[derive(blade_macros::ShaderData)]
372struct FillData<'a> {
373    camera: CameraParams,
374    prev_camera: CameraParams,
375    debug: DebugParams,
376    acc_struct: blade_graphics::AccelerationStructure,
377    hit_entries: blade_graphics::BufferPiece,
378    index_buffers: &'a blade_graphics::BufferArray<MAX_RESOURCES>,
379    vertex_buffers: &'a blade_graphics::BufferArray<MAX_RESOURCES>,
380    textures: &'a blade_graphics::TextureArray<MAX_RESOURCES>,
381    sampler_linear: blade_graphics::Sampler,
382    debug_buf: blade_graphics::BufferPiece,
383    out_depth: blade_graphics::TextureView,
384    out_basis: blade_graphics::TextureView,
385    out_flat_normal: blade_graphics::TextureView,
386    out_albedo: blade_graphics::TextureView,
387    out_motion: blade_graphics::TextureView,
388    out_debug: blade_graphics::TextureView,
389}
390
391#[derive(blade_macros::ShaderData)]
392struct MainData {
393    camera: CameraParams,
394    prev_camera: CameraParams,
395    debug: DebugParams,
396    parameters: MainParams,
397    acc_struct: blade_graphics::AccelerationStructure,
398    prev_acc_struct: blade_graphics::AccelerationStructure,
399    sampler_linear: blade_graphics::Sampler,
400    sampler_nearest: blade_graphics::Sampler,
401    env_map: blade_graphics::TextureView,
402    env_weights: blade_graphics::TextureView,
403    t_depth: blade_graphics::TextureView,
404    t_prev_depth: blade_graphics::TextureView,
405    t_basis: blade_graphics::TextureView,
406    t_prev_basis: blade_graphics::TextureView,
407    t_flat_normal: blade_graphics::TextureView,
408    t_prev_flat_normal: blade_graphics::TextureView,
409    t_motion: blade_graphics::TextureView,
410    debug_buf: blade_graphics::BufferPiece,
411    reservoirs: blade_graphics::BufferPiece,
412    prev_reservoirs: blade_graphics::BufferPiece,
413    out_diffuse: blade_graphics::TextureView,
414    out_debug: blade_graphics::TextureView,
415}
416
417#[repr(C)]
418#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
419struct BlurParams {
420    extent: [u32; 2],
421    temporal_weight: f32,
422    iteration: i32,
423    use_motion_vectors: u32,
424    pad: u32,
425}
426
427#[derive(blade_macros::ShaderData)]
428struct TemporalAccumData {
429    camera: CameraParams,
430    prev_camera: CameraParams,
431    params: BlurParams,
432    input: blade_graphics::TextureView,
433    t_depth: blade_graphics::TextureView,
434    t_prev_depth: blade_graphics::TextureView,
435    t_flat_normal: blade_graphics::TextureView,
436    t_prev_flat_normal: blade_graphics::TextureView,
437    t_motion: blade_graphics::TextureView,
438    output: blade_graphics::TextureView,
439}
440
441#[derive(blade_macros::ShaderData)]
442struct ATrousData {
443    params: BlurParams,
444    input: blade_graphics::TextureView,
445    t_depth: blade_graphics::TextureView,
446    t_flat_normal: blade_graphics::TextureView,
447    output: blade_graphics::TextureView,
448}
449
450#[repr(C)]
451#[derive(Clone, Copy, Default, bytemuck::Zeroable, bytemuck::Pod)]
452struct ToneMapParams {
453    enabled: u32,
454    average_lum: f32,
455    key_value: f32,
456    white_level: f32,
457}
458
459#[derive(blade_macros::ShaderData)]
460struct PostProcData {
461    t_albedo: blade_graphics::TextureView,
462    light_diffuse: blade_graphics::TextureView,
463    t_debug: blade_graphics::TextureView,
464    tone_map_params: ToneMapParams,
465    debug_params: DebugParams,
466}
467
468#[repr(C)]
469#[derive(Debug)]
470struct HitEntry {
471    index_buf: u32,
472    vertex_buf: u32,
473    winding: f32,
474    geometry_to_world_rotation: [i8; 4],
475    //Note: it's technically `mat4x3` on WGSL side,
476    // but it's aligned and sized the same way as `mat4`.
477    geometry_to_object: mint::ColumnMatrix4<f32>,
478    prev_object_to_world: mint::ColumnMatrix4<f32>,
479    base_color_texture: u32,
480    base_color_factor: [u8; 4],
481    normal_texture: u32,
482    normal_scale: f32,
483}
484
485#[derive(Clone, PartialEq)]
486pub struct Shaders {
487    pub(crate) env_prepare: blade_asset::Handle<crate::Shader>,
488    pub(crate) fill_gbuf: blade_asset::Handle<crate::Shader>,
489    pub(crate) ray_trace: blade_asset::Handle<crate::Shader>,
490    pub(crate) a_trous: blade_asset::Handle<crate::Shader>,
491    pub(crate) post_proc: blade_asset::Handle<crate::Shader>,
492    pub(crate) raster: blade_asset::Handle<crate::Shader>,
493    pub(crate) debug_draw: blade_asset::Handle<crate::Shader>,
494    pub(crate) debug_blit: blade_asset::Handle<crate::Shader>,
495}
496
497impl Shaders {
498    pub fn load(
499        path: &Path,
500        asset_hub: &crate::AssetHub,
501        ray_tracing: bool,
502    ) -> (Self, choir::RunningTask) {
503        let mut ctx = asset_hub.open_context(path, "shader finish");
504        let noop = if ray_tracing {
505            None
506        } else {
507            Some(ctx.load_shader("noop.wgsl"))
508        };
509        let shaders = Self {
510            env_prepare: noop.unwrap_or_else(|| ctx.load_shader("env-prepare.wgsl")),
511            fill_gbuf: noop.unwrap_or_else(|| ctx.load_shader("fill-gbuf.wgsl")),
512            ray_trace: noop.unwrap_or_else(|| ctx.load_shader("ray-trace.wgsl")),
513            a_trous: noop.unwrap_or_else(|| ctx.load_shader("a-trous.wgsl")),
514            post_proc: noop.unwrap_or_else(|| ctx.load_shader("post-proc.wgsl")),
515            raster: ctx.load_shader("raster.wgsl"),
516            debug_draw: ctx.load_shader("debug-draw.wgsl"),
517            debug_blit: ctx.load_shader("debug-blit.wgsl"),
518        };
519        (shaders, ctx.close())
520    }
521}
522
523struct ShaderPipelines {
524    fill: blade_graphics::ComputePipeline,
525    main: blade_graphics::ComputePipeline,
526    temporal_accum: blade_graphics::ComputePipeline,
527    a_trous: blade_graphics::ComputePipeline,
528    post_proc: blade_graphics::RenderPipeline,
529    env_prepare: blade_graphics::ComputePipeline,
530    reservoir_size: u32,
531}
532
533impl ShaderPipelines {
534    fn create_gbuf_fill(
535        shader: &blade_graphics::Shader,
536        gpu: &blade_graphics::Context,
537    ) -> blade_graphics::ComputePipeline {
538        shader.check_struct_size::<crate::Vertex>();
539        shader.check_struct_size::<HitEntry>();
540        let layout = <FillData as blade_graphics::ShaderData>::layout();
541        gpu.create_compute_pipeline(blade_graphics::ComputePipelineDesc {
542            name: "fill-gbuf",
543            data_layouts: &[&layout],
544            compute: shader.at("main"),
545        })
546    }
547    fn create_ray_trace(
548        shader: &blade_graphics::Shader,
549        gpu: &blade_graphics::Context,
550    ) -> blade_graphics::ComputePipeline {
551        shader.check_struct_size::<CameraParams>();
552        shader.check_struct_size::<DebugParams>();
553        shader.check_struct_size::<MainParams>();
554        shader.check_struct_size::<DebugVariance>();
555        shader.check_struct_size::<DebugEntry>();
556        let layout = <MainData as blade_graphics::ShaderData>::layout();
557        gpu.create_compute_pipeline(blade_graphics::ComputePipelineDesc {
558            name: "ray-trace",
559            data_layouts: &[&layout],
560            compute: shader.at("main"),
561        })
562    }
563
564    fn create_temporal_accum(
565        shader: &blade_graphics::Shader,
566        gpu: &blade_graphics::Context,
567    ) -> blade_graphics::ComputePipeline {
568        let layout = <TemporalAccumData as blade_graphics::ShaderData>::layout();
569        gpu.create_compute_pipeline(blade_graphics::ComputePipelineDesc {
570            name: "temporal-accum",
571            data_layouts: &[&layout],
572            compute: shader.at("temporal_accum"),
573        })
574    }
575
576    fn create_a_trous(
577        shader: &blade_graphics::Shader,
578        gpu: &blade_graphics::Context,
579    ) -> blade_graphics::ComputePipeline {
580        let layout = <ATrousData as blade_graphics::ShaderData>::layout();
581        gpu.create_compute_pipeline(blade_graphics::ComputePipelineDesc {
582            name: "a-trous",
583            data_layouts: &[&layout],
584            compute: shader.at("atrous3x3"),
585        })
586    }
587
588    fn create_post_proc(
589        shader: &blade_graphics::Shader,
590        info: blade_graphics::SurfaceInfo,
591        gpu: &blade_graphics::Context,
592    ) -> blade_graphics::RenderPipeline {
593        let layout = <PostProcData as blade_graphics::ShaderData>::layout();
594        gpu.create_render_pipeline(blade_graphics::RenderPipelineDesc {
595            name: "main",
596            data_layouts: &[&layout],
597            primitive: blade_graphics::PrimitiveState {
598                topology: blade_graphics::PrimitiveTopology::TriangleStrip,
599                ..Default::default()
600            },
601            vertex: shader.at("postfx_vs"),
602            vertex_fetches: &[],
603            fragment: Some(shader.at("postfx_fs")),
604            color_targets: &[info.format.into()],
605            depth_stencil: None,
606            multisample_state: blade_graphics::MultisampleState::default(),
607        })
608    }
609
610    fn init(
611        shaders: &Shaders,
612        config: &RenderConfig,
613        gpu: &blade_graphics::Context,
614        shader_man: &blade_asset::AssetManager<crate::shader::Baker>,
615    ) -> Result<Self, &'static str> {
616        let sh_main = shader_man[shaders.ray_trace].raw.as_ref().unwrap();
617        let sh_a_trous = shader_man[shaders.a_trous].raw.as_ref().unwrap();
618        Ok(Self {
619            fill: Self::create_gbuf_fill(shader_man[shaders.fill_gbuf].raw.as_ref().unwrap(), gpu),
620            main: Self::create_ray_trace(sh_main, gpu),
621            temporal_accum: Self::create_temporal_accum(sh_a_trous, gpu),
622            a_trous: Self::create_a_trous(sh_a_trous, gpu),
623            post_proc: Self::create_post_proc(
624                shader_man[shaders.post_proc].raw.as_ref().unwrap(),
625                config.surface_info,
626                gpu,
627            ),
628            env_prepare: EnvironmentMap::init_pipeline(
629                shader_man[shaders.env_prepare].raw.as_ref().unwrap(),
630                gpu,
631            )?,
632            reservoir_size: sh_main.get_struct_size("StoredReservoir"),
633        })
634    }
635}
636
637#[derive(Clone, Copy, Default)]
638pub struct FrameConfig {
639    pub frozen: bool,
640    pub debug_draw: bool,
641    pub reset_variance: bool,
642    pub reset_reservoirs: bool,
643}
644
645/// Temporary resources associated with a GPU frame.
646#[derive(Default)]
647pub struct FrameResources {
648    pub buffers: Vec<blade_graphics::Buffer>,
649    pub acceleration_structures: Vec<blade_graphics::AccelerationStructure>,
650}
651
652impl RayTracer {
653    /// Create a new renderer with a given configuration.
654    ///
655    /// Panics if the system is not compatible.
656    /// Records initialization routines into the given command encoder.
657    #[profiling::function]
658    pub fn new(
659        encoder: &mut blade_graphics::CommandEncoder,
660        gpu: &blade_graphics::Context,
661        shaders: Shaders,
662        shader_man: &blade_asset::AssetManager<crate::shader::Baker>,
663        config: &RenderConfig,
664    ) -> Self {
665        let capabilities = gpu.capabilities();
666        assert!(
667            capabilities
668                .ray_query
669                .contains(blade_graphics::ShaderVisibility::COMPUTE)
670        );
671
672        let sp = ShaderPipelines::init(&shaders, config, gpu, shader_man).unwrap();
673        let debug = {
674            let sh_draw = shader_man[shaders.debug_draw].raw.as_ref().unwrap();
675            let sh_blit = shader_man[shaders.debug_blit].raw.as_ref().unwrap();
676            DebugRender::init(
677                encoder,
678                gpu,
679                sh_draw,
680                sh_blit,
681                config.max_debug_lines,
682                config.surface_info,
683            )
684        };
685
686        let targets = RestirTargets::new(config.surface_size, sp.reservoir_size, encoder, gpu);
687        let dummy = DummyResources::new(encoder, gpu);
688
689        let samplers = Samplers {
690            nearest: gpu.create_sampler(blade_graphics::SamplerDesc {
691                name: "nearest",
692                address_modes: [blade_graphics::AddressMode::ClampToEdge; 3],
693                mag_filter: blade_graphics::FilterMode::Nearest,
694                min_filter: blade_graphics::FilterMode::Nearest,
695                mipmap_filter: blade_graphics::FilterMode::Nearest,
696                ..Default::default()
697            }),
698            linear: gpu.create_sampler(blade_graphics::SamplerDesc {
699                name: "linear",
700                address_modes: [blade_graphics::AddressMode::Repeat; 3],
701                mag_filter: blade_graphics::FilterMode::Linear,
702                min_filter: blade_graphics::FilterMode::Linear,
703                mipmap_filter: blade_graphics::FilterMode::Linear,
704                ..Default::default()
705            }),
706        };
707
708        Self {
709            shaders,
710            targets,
711            post_proc_input_index: 0,
712            fill_pipeline: sp.fill,
713            main_pipeline: sp.main,
714            post_proc_pipeline: sp.post_proc,
715            blur: Blur {
716                temporal_accum_pipeline: sp.temporal_accum,
717                a_trous_pipeline: sp.a_trous,
718            },
719            acceleration_structure: blade_graphics::AccelerationStructure::default(),
720            prev_acceleration_structure: blade_graphics::AccelerationStructure::default(),
721            env_map: EnvironmentMap::with_pipeline(&dummy, sp.env_prepare),
722            dummy,
723            hit_buffer: blade_graphics::Buffer::default(),
724            vertex_buffers: blade_graphics::BufferArray::new(),
725            index_buffers: blade_graphics::BufferArray::new(),
726            textures: blade_graphics::TextureArray::new(),
727            samplers,
728            reservoir_size: sp.reservoir_size,
729            debug,
730            surface_size: config.surface_size,
731            surface_info: config.surface_info,
732            frame_index: 0,
733            frame_scene_built: 0,
734            is_frozen: false,
735            texture_resource_lookup: HashMap::default(),
736        }
737    }
738
739    /// Destroy all internally managed GPU resources.
740    pub fn destroy(&mut self, gpu: &blade_graphics::Context) {
741        // internal resources
742        self.targets.destroy(gpu);
743        if self.hit_buffer != blade_graphics::Buffer::default() {
744            gpu.destroy_buffer(self.hit_buffer);
745        }
746        gpu.destroy_acceleration_structure(self.acceleration_structure);
747        if self.prev_acceleration_structure != blade_graphics::AccelerationStructure::default() {
748            gpu.destroy_acceleration_structure(self.prev_acceleration_structure);
749        }
750        // env map, dummy, and debug
751        self.env_map.destroy(gpu);
752        self.dummy.destroy(gpu);
753        self.debug.destroy(gpu);
754        // samplers
755        gpu.destroy_sampler(self.samplers.nearest);
756        gpu.destroy_sampler(self.samplers.linear);
757        // pipelines
758        gpu.destroy_compute_pipeline(&mut self.blur.temporal_accum_pipeline);
759        gpu.destroy_compute_pipeline(&mut self.blur.a_trous_pipeline);
760        gpu.destroy_compute_pipeline(&mut self.fill_pipeline);
761        gpu.destroy_compute_pipeline(&mut self.main_pipeline);
762        gpu.destroy_render_pipeline(&mut self.post_proc_pipeline);
763    }
764
765    #[profiling::function]
766    pub fn hot_reload(
767        &mut self,
768        asset_hub: &crate::AssetHub,
769        gpu: &blade_graphics::Context,
770        sync_point: &blade_graphics::SyncPoint,
771    ) -> bool {
772        let mut tasks = Vec::new();
773        let old = self.shaders.clone();
774
775        tasks.extend(asset_hub.shaders.hot_reload(&mut self.shaders.fill_gbuf));
776        tasks.extend(asset_hub.shaders.hot_reload(&mut self.shaders.ray_trace));
777        tasks.extend(asset_hub.shaders.hot_reload(&mut self.shaders.a_trous));
778        tasks.extend(asset_hub.shaders.hot_reload(&mut self.shaders.post_proc));
779        tasks.extend(asset_hub.shaders.hot_reload(&mut self.shaders.debug_draw));
780        tasks.extend(asset_hub.shaders.hot_reload(&mut self.shaders.debug_blit));
781
782        if tasks.is_empty() {
783            return false;
784        }
785
786        log::info!("Hot reloading shaders");
787        let _ = gpu.wait_for(sync_point, !0);
788        for task in tasks {
789            let _ = task.join();
790        }
791
792        if self.shaders.fill_gbuf != old.fill_gbuf
793            && let Ok(ref shader) = asset_hub.shaders[self.shaders.fill_gbuf].raw
794        {
795            self.fill_pipeline = ShaderPipelines::create_gbuf_fill(shader, gpu);
796        }
797        if self.shaders.ray_trace != old.ray_trace
798            && let Ok(ref shader) = asset_hub.shaders[self.shaders.ray_trace].raw
799        {
800            assert_eq!(
801                shader.get_struct_size("StoredReservoir"),
802                self.reservoir_size
803            );
804            self.main_pipeline = ShaderPipelines::create_ray_trace(shader, gpu);
805        }
806        if self.shaders.a_trous != old.a_trous
807            && let Ok(ref shader) = asset_hub.shaders[self.shaders.a_trous].raw
808        {
809            self.blur.temporal_accum_pipeline = ShaderPipelines::create_temporal_accum(shader, gpu);
810            self.blur.a_trous_pipeline = ShaderPipelines::create_a_trous(shader, gpu);
811        }
812        if self.shaders.post_proc != old.post_proc
813            && let Ok(ref shader) = asset_hub.shaders[self.shaders.post_proc].raw
814        {
815            self.post_proc_pipeline =
816                ShaderPipelines::create_post_proc(shader, self.surface_info, gpu);
817        }
818        if self.shaders.debug_draw != old.debug_draw
819            && let Ok(ref shader) = asset_hub.shaders[self.shaders.debug_draw].raw
820        {
821            self.debug.recreate_draw_pipeline(shader, gpu);
822        }
823        if self.shaders.debug_blit != old.debug_blit
824            && let Ok(ref shader) = asset_hub.shaders[self.shaders.debug_blit].raw
825        {
826            self.debug.recreate_blit_pipeline(shader, gpu);
827        }
828
829        true
830    }
831
832    pub fn get_surface_size(&self) -> blade_graphics::Extent {
833        self.surface_size
834    }
835
836    pub fn view_dummy_white(&self) -> blade_graphics::TextureView {
837        self.dummy.white_view
838    }
839    pub fn view_environment_main(&self) -> blade_graphics::TextureView {
840        self.env_map.main_view
841    }
842    pub fn view_environment_weight(&self) -> blade_graphics::TextureView {
843        self.env_map.weight_view
844    }
845
846    #[profiling::function]
847    pub fn resize_screen(
848        &mut self,
849        size: blade_graphics::Extent,
850        encoder: &mut blade_graphics::CommandEncoder,
851        gpu: &blade_graphics::Context,
852    ) {
853        self.surface_size = size;
854        self.targets.destroy(gpu);
855        self.targets = RestirTargets::new(size, self.reservoir_size, encoder, gpu);
856    }
857
858    #[profiling::function]
859    pub fn build_scene(
860        &mut self,
861        command_encoder: &mut blade_graphics::CommandEncoder,
862        objects: &[crate::Object],
863        env_map: Option<blade_asset::Handle<crate::Texture>>,
864        asset_hub: &crate::AssetHub,
865        gpu: &blade_graphics::Context,
866        temp: &mut FrameResources,
867    ) {
868        let (env_view, env_extent) = match env_map {
869            Some(handle) => {
870                let asset = &asset_hub.textures[handle];
871                (asset.view, asset.extent)
872            }
873            None => (self.dummy.white_view, blade_graphics::Extent::default()),
874        };
875        self.env_map
876            .assign(env_view, env_extent, command_encoder, gpu);
877
878        if self.prev_acceleration_structure != blade_graphics::AccelerationStructure::default() {
879            temp.acceleration_structures
880                .push(self.prev_acceleration_structure);
881        }
882        self.prev_acceleration_structure = self.acceleration_structure;
883
884        let geometry_count = objects
885            .iter()
886            .map(|object| {
887                let model = &asset_hub.models[object.model];
888                model.geometries.len()
889            })
890            .sum::<usize>();
891        let hit_size = (geometry_count.max(1) * mem::size_of::<HitEntry>()) as u64;
892        //TODO: reuse the hit buffer
893        if self.hit_buffer != blade_graphics::Buffer::default() {
894            temp.buffers.push(self.hit_buffer);
895        }
896        self.hit_buffer = gpu.create_buffer(blade_graphics::BufferDesc {
897            name: "hit entries",
898            size: hit_size,
899            memory: blade_graphics::Memory::Device,
900        });
901        let hit_staging = gpu.create_buffer(blade_graphics::BufferDesc {
902            name: "hit staging",
903            size: hit_size,
904            memory: blade_graphics::Memory::Upload,
905        });
906        temp.buffers.push(hit_staging);
907        {
908            let mut transfers = command_encoder.transfer("build-scene");
909            transfers.copy_buffer_to_buffer(hit_staging.at(0), self.hit_buffer.at(0), hit_size);
910        }
911
912        self.vertex_buffers.clear();
913        self.index_buffers.clear();
914        self.textures.clear();
915        let dummy_white = self.textures.alloc(self.dummy.white_view);
916        let dummy_black = self.textures.alloc(self.dummy.black_view);
917
918        let mut geometry_index = 0;
919        let mut instances = Vec::with_capacity(objects.len());
920        let mut blases = Vec::with_capacity(objects.len());
921        let mut texture_indices = HashMap::new();
922
923        for object in objects {
924            let m3_object = mat3_transform(&object.transform);
925            let model = &asset_hub.models[object.model];
926            instances.push(blade_graphics::AccelerationStructureInstance {
927                acceleration_structure_index: blases.len() as u32,
928                transform: object.transform,
929                mask: 0xFF,
930                custom_index: geometry_index as u32,
931            });
932            blases.push(model.acceleration_structure);
933
934            for geometry in model.geometries.iter() {
935                let material = &model.materials[geometry.material_index];
936                let vertex_offset =
937                    geometry.vertex_range.start as u64 * mem::size_of::<crate::Vertex>() as u64;
938                let geometry_to_world_rotation = {
939                    let m3_geo = mat3_transform(&geometry.transform);
940                    let m3_normal = (m3_object * m3_geo).inverse().transpose();
941                    let quat = glam::Quat::from_mat3(&m3_normal);
942                    let qv = glam::Vec4::from(quat) * 127.0;
943                    [qv.x as i8, qv.y as i8, qv.z as i8, qv.w as i8]
944                };
945
946                let hit_entry = HitEntry {
947                    index_buf: match geometry.index_type {
948                        Some(_) => self
949                            .index_buffers
950                            .alloc(model.index_buffer.at(geometry.index_offset)),
951                        None => !0,
952                    },
953                    vertex_buf: self
954                        .vertex_buffers
955                        .alloc(model.vertex_buffer.at(vertex_offset)),
956                    winding: model.winding,
957                    geometry_to_world_rotation,
958                    geometry_to_object: mint::ColumnMatrix4::from(mint::RowMatrix4 {
959                        x: geometry.transform.x,
960                        y: geometry.transform.y,
961                        z: geometry.transform.z,
962                        w: [0.0, 0.0, 0.0, 1.0].into(),
963                    }),
964                    prev_object_to_world: mat4_transform(&object.prev_transform).into(),
965                    base_color_texture: match material.base_color_texture {
966                        Some(handle) => *texture_indices.entry(handle).or_insert_with(|| {
967                            let texture = &asset_hub.textures[handle];
968                            self.textures.alloc(texture.view)
969                        }),
970                        None => dummy_white,
971                    },
972                    base_color_factor: {
973                        let c = material.base_color_factor;
974                        [
975                            (c[0] * 255.0) as u8,
976                            (c[1] * 255.0) as u8,
977                            (c[2] * 255.0) as u8,
978                            (c[3] * 255.0) as u8,
979                        ]
980                    },
981                    normal_texture: match material.normal_texture {
982                        Some(handle) => *texture_indices.entry(handle).or_insert_with(|| {
983                            let texture = &asset_hub.textures[handle];
984                            self.textures.alloc(texture.view)
985                        }),
986                        None => dummy_black,
987                    },
988                    normal_scale: material.normal_scale,
989                };
990
991                log::debug!("Entry[{geometry_index}] = {hit_entry:?}");
992                unsafe {
993                    ptr::write(
994                        (hit_staging.data() as *mut HitEntry).add(geometry_index),
995                        hit_entry,
996                    );
997                }
998                geometry_index += 1;
999            }
1000        }
1001
1002        self.texture_resource_lookup.clear();
1003        for (handle, res_id) in texture_indices {
1004            self.texture_resource_lookup.insert(res_id, handle);
1005        }
1006
1007        assert_eq!(geometry_index, geometry_count);
1008        log::info!(
1009            "Preparing ray tracing with {} geometries in total",
1010            geometry_count
1011        );
1012
1013        // Needs to be a separate encoder in order to force synchronization
1014        let sizes = gpu.get_top_level_acceleration_structure_sizes(instances.len() as u32);
1015        self.acceleration_structure =
1016            gpu.create_acceleration_structure(blade_graphics::AccelerationStructureDesc {
1017                name: "TLAS",
1018                ty: blade_graphics::AccelerationStructureType::TopLevel,
1019                size: sizes.data,
1020            });
1021        let instance_buf = gpu.create_acceleration_structure_instance_buffer(&instances, &blases);
1022        let scratch_buf = gpu.create_buffer(blade_graphics::BufferDesc {
1023            name: "TLAS scratch",
1024            size: sizes.scratch,
1025            memory: blade_graphics::Memory::Device,
1026        });
1027
1028        let mut tlas_encoder = command_encoder.acceleration_structure("TLAS");
1029        tlas_encoder.build_top_level(
1030            self.acceleration_structure,
1031            &blases,
1032            instances.len() as u32,
1033            instance_buf.at(0),
1034            scratch_buf.at(0),
1035        );
1036
1037        temp.buffers.push(instance_buf);
1038        temp.buffers.push(scratch_buf);
1039        self.frame_scene_built = self.frame_index + 1;
1040    }
1041
1042    fn make_debug_params(&self, config: &DebugConfig) -> DebugParams {
1043        DebugParams {
1044            view_mode: config.view_mode as u32,
1045            draw_flags: config.draw_flags.bits(),
1046            texture_flags: config.texture_flags.bits(),
1047            unused: 0,
1048            mouse_pos: config.mouse_pos.unwrap_or([-1; 2]),
1049        }
1050    }
1051
1052    fn make_camera_params(&self, camera: &super::Camera) -> CameraParams {
1053        let fov_x = 2.0
1054            * ((camera.fov_y * 0.5).tan() * self.surface_size.width as f32
1055                / self.surface_size.height as f32)
1056                .atan();
1057        CameraParams {
1058            position: camera.pos.into(),
1059            depth: camera.depth,
1060            orientation: camera.rot.into(),
1061            fov: [fov_x, camera.fov_y],
1062            target_size: [self.surface_size.width, self.surface_size.height],
1063        }
1064    }
1065
1066    fn work_indices(&self) -> (usize, usize) {
1067        let cur = self.frame_index & 1;
1068        let prev = if cur < self.frame_index { cur ^ 1 } else { cur };
1069        (cur, prev)
1070    }
1071
1072    /// Prepare to render a frame.
1073    #[profiling::function]
1074    pub fn prepare(
1075        &mut self,
1076        command_encoder: &mut blade_graphics::CommandEncoder,
1077        camera: &crate::Camera,
1078        config: FrameConfig,
1079    ) {
1080        let mut transfer = command_encoder.transfer("prepare");
1081
1082        if config.debug_draw {
1083            self.debug.reset_lines(&mut transfer);
1084            self.debug.enable_draw(&mut transfer, true);
1085        } else {
1086            self.debug.enable_draw(&mut transfer, false);
1087        }
1088
1089        if config.reset_reservoirs || config.reset_variance {
1090            self.debug.reset_variance(&mut transfer);
1091        } else {
1092            self.debug.update_variance(&mut transfer);
1093        }
1094        self.debug.update_entry(&mut transfer);
1095
1096        if config.reset_reservoirs {
1097            if !config.debug_draw {
1098                self.debug.reset_lines(&mut transfer);
1099            }
1100            let total_reservoirs = self.surface_size.width as u64 * self.surface_size.height as u64;
1101            for reservoir_buf in self.targets.reservoir_buf.iter() {
1102                transfer.fill_buffer(
1103                    reservoir_buf.at(0),
1104                    total_reservoirs * self.reservoir_size as u64,
1105                    0,
1106                );
1107            }
1108        }
1109
1110        if !config.frozen {
1111            self.frame_index += 1;
1112        }
1113        self.is_frozen = config.frozen;
1114        self.targets.camera_params[self.frame_index % 2] = self.make_camera_params(camera);
1115        self.post_proc_input_index = self.frame_index % 2;
1116    }
1117
1118    /// Ray trace the scene.
1119    ///
1120    /// The result is stored internally in an HDR render target.
1121    #[profiling::function]
1122    pub fn ray_trace(
1123        &self,
1124        command_encoder: &mut blade_graphics::CommandEncoder,
1125        debug_config: DebugConfig,
1126        ray_config: RayConfig,
1127    ) {
1128        let debug = self.make_debug_params(&debug_config);
1129        let (cur, prev) = self.work_indices();
1130        assert_eq!(cur, self.post_proc_input_index);
1131
1132        if let mut pass = command_encoder.compute("fill-gbuf") {
1133            let mut pc = pass.with(&self.fill_pipeline);
1134            let groups = self.fill_pipeline.get_dispatch_for(self.surface_size);
1135            pc.bind(
1136                0,
1137                &FillData {
1138                    camera: self.targets.camera_params[cur],
1139                    prev_camera: self.targets.camera_params[prev],
1140                    debug,
1141                    acc_struct: self.acceleration_structure,
1142                    hit_entries: self.hit_buffer.into(),
1143                    index_buffers: &self.index_buffers,
1144                    vertex_buffers: &self.vertex_buffers,
1145                    textures: &self.textures,
1146                    sampler_linear: self.samplers.linear,
1147                    debug_buf: self.debug.buffer_resource(),
1148                    out_depth: self.targets.depth.views[cur],
1149                    out_basis: self.targets.basis.views[cur],
1150                    out_flat_normal: self.targets.flat_normal.views[cur],
1151                    out_albedo: self.targets.albedo.views[0],
1152                    out_motion: self.targets.motion.views[0],
1153                    out_debug: self.targets.debug.views[0],
1154                },
1155            );
1156            pc.dispatch(groups);
1157        }
1158
1159        if let mut pass = command_encoder.compute("ray-trace") {
1160            let mut pc = pass.with(&self.main_pipeline);
1161            let groups = self.main_pipeline.get_dispatch_for(self.surface_size);
1162            pc.bind(
1163                0,
1164                &MainData {
1165                    camera: self.targets.camera_params[cur],
1166                    prev_camera: self.targets.camera_params[prev],
1167                    debug,
1168                    parameters: MainParams {
1169                        frame_index: self.frame_index as u32,
1170                        num_environment_samples: ray_config.num_environment_samples,
1171                        environment_importance_sampling: ray_config.environment_importance_sampling
1172                            as u32,
1173                        tap_count: ray_config.tap_count,
1174                        tap_radius: ray_config.tap_radius as f32,
1175                        tap_confidence_near: ray_config.tap_confidence_near as f32,
1176                        tap_confidence_far: ray_config.tap_confidence_far as f32,
1177                        t_start: ray_config.t_start,
1178                        use_pairwise_mis: ray_config.pairwise_mis as u32,
1179                        defensive_mis: ray_config.defensive_mis,
1180                        use_motion_vectors: (self.frame_scene_built >= self.frame_index) as u32,
1181                    },
1182                    acc_struct: self.acceleration_structure,
1183                    prev_acc_struct: if self.frame_scene_built < self.frame_index
1184                        || self.prev_acceleration_structure
1185                            == blade_graphics::AccelerationStructure::default()
1186                    {
1187                        self.acceleration_structure
1188                    } else {
1189                        self.prev_acceleration_structure
1190                    },
1191                    sampler_linear: self.samplers.linear,
1192                    sampler_nearest: self.samplers.nearest,
1193                    env_map: self.env_map.main_view,
1194                    env_weights: self.env_map.weight_view,
1195                    t_depth: self.targets.depth.views[cur],
1196                    t_prev_depth: self.targets.depth.views[prev],
1197                    t_basis: self.targets.basis.views[cur],
1198                    t_prev_basis: self.targets.basis.views[prev],
1199                    t_flat_normal: self.targets.flat_normal.views[cur],
1200                    t_prev_flat_normal: self.targets.flat_normal.views[prev],
1201                    t_motion: self.targets.motion.views[0],
1202                    debug_buf: self.debug.buffer_resource(),
1203                    reservoirs: self.targets.reservoir_buf[cur].into(),
1204                    prev_reservoirs: self.targets.reservoir_buf[prev].into(),
1205                    out_diffuse: self.targets.light_diffuse.views[cur],
1206                    out_debug: self.targets.debug.views[0],
1207                },
1208            );
1209            pc.dispatch(groups);
1210        }
1211    }
1212
1213    /// Perform noise reduction using SVGF.
1214    #[profiling::function]
1215    pub fn denoise(
1216        &mut self, //TODO: borrow immutably
1217        command_encoder: &mut blade_graphics::CommandEncoder,
1218        denoiser_config: DenoiserConfig,
1219    ) {
1220        let mut params = BlurParams {
1221            extent: [self.surface_size.width, self.surface_size.height],
1222            temporal_weight: denoiser_config.temporal_weight,
1223            iteration: 0,
1224            use_motion_vectors: (self.frame_scene_built >= self.frame_index) as u32,
1225            pad: 0,
1226        };
1227        let (cur, prev) = self.work_indices();
1228
1229        if denoiser_config.temporal_weight < 1.0 {
1230            let mut pass = command_encoder.compute("temporal-accum");
1231            let mut pc = pass.with(&self.blur.temporal_accum_pipeline);
1232            let groups = self
1233                .blur
1234                .a_trous_pipeline
1235                .get_dispatch_for(self.surface_size);
1236            pc.bind(
1237                0,
1238                &TemporalAccumData {
1239                    camera: self.targets.camera_params[cur],
1240                    prev_camera: self.targets.camera_params[prev],
1241                    params,
1242                    input: self.targets.light_diffuse.views[prev],
1243                    t_depth: self.targets.depth.views[cur],
1244                    t_prev_depth: self.targets.depth.views[prev],
1245                    t_flat_normal: self.targets.flat_normal.views[cur],
1246                    t_prev_flat_normal: self.targets.flat_normal.views[prev],
1247                    t_motion: self.targets.motion.views[0],
1248                    output: self.targets.light_diffuse.views[cur],
1249                },
1250            );
1251            pc.dispatch(groups);
1252        }
1253
1254        assert_eq!(cur, self.post_proc_input_index);
1255        let mut ping_pong = [2, if self.is_frozen { cur } else { prev }];
1256        for _ in 0..denoiser_config.num_passes {
1257            let mut pass = command_encoder.compute("a-trous");
1258            let mut pc = pass.with(&self.blur.a_trous_pipeline);
1259            let groups = self
1260                .blur
1261                .a_trous_pipeline
1262                .get_dispatch_for(self.surface_size);
1263            pc.bind(
1264                0,
1265                &ATrousData {
1266                    params,
1267                    input: self.targets.light_diffuse.views[self.post_proc_input_index],
1268                    t_depth: self.targets.depth.views[cur],
1269                    t_flat_normal: self.targets.flat_normal.views[cur],
1270                    output: self.targets.light_diffuse.views[ping_pong[0]],
1271                },
1272            );
1273            pc.dispatch(groups);
1274            self.post_proc_input_index = ping_pong[0];
1275            ping_pong.swap(0, 1);
1276            params.iteration += 1;
1277        }
1278    }
1279
1280    /// Blit the rendering result into a specified render pass.
1281    #[profiling::function]
1282    pub fn post_proc(
1283        &self,
1284        pass: &mut blade_graphics::RenderCommandEncoder,
1285        debug_config: DebugConfig,
1286        pp_config: PostProcConfig,
1287        debug_lines: &[DebugLine],
1288        debug_blits: &[DebugBlit],
1289    ) {
1290        let cur = self.frame_index % 2;
1291        if let mut pc = pass.with(&self.post_proc_pipeline) {
1292            let debug_params = self.make_debug_params(&debug_config);
1293            pc.bind(
1294                0,
1295                &PostProcData {
1296                    t_albedo: self.targets.albedo.views[0],
1297                    light_diffuse: self.targets.light_diffuse.views[self.post_proc_input_index],
1298                    t_debug: self.targets.debug.views[0],
1299                    tone_map_params: ToneMapParams {
1300                        enabled: 1,
1301                        average_lum: pp_config.average_luminocity,
1302                        key_value: pp_config.exposure_key_value,
1303                        white_level: pp_config.white_level,
1304                    },
1305                    debug_params,
1306                },
1307            );
1308            pc.draw(0, 3, 0, 1);
1309        }
1310
1311        self.debug.render_lines(
1312            debug_lines,
1313            self.targets.camera_params[cur],
1314            self.targets.depth.views[cur],
1315            pass,
1316        );
1317        self.debug
1318            .render_blits(debug_blits, self.samplers.linear, self.surface_size, pass);
1319    }
1320
1321    #[profiling::function]
1322    pub fn read_debug_selection_info(&self) -> SelectionInfo {
1323        let (db_v, db_e) = self.debug.read_shared_data();
1324        SelectionInfo {
1325            std_deviation: if db_v.count == 0 {
1326                [0.0; 3].into()
1327            } else {
1328                let sum_avg = glam::Vec3::from(db_v.color_sum) / (db_v.count as f32);
1329                let sum2_avg = glam::Vec3::from(db_v.color2_sum) / (db_v.count as f32);
1330                let variance = sum2_avg - sum_avg * sum_avg;
1331                mint::Vector3 {
1332                    x: variance.x.sqrt(),
1333                    y: variance.y.sqrt(),
1334                    z: variance.z.sqrt(),
1335                }
1336            },
1337            std_deviation_history: db_v.count,
1338            custom_index: db_e.custom_index,
1339            depth: db_e.depth,
1340            position: db_e.position.into(),
1341            normal: db_e.normal.into(),
1342            tex_coords: db_e.tex_coords.into(),
1343            base_color_texture: self
1344                .texture_resource_lookup
1345                .get(&db_e.base_color_texture)
1346                .cloned(),
1347            normal_texture: self
1348                .texture_resource_lookup
1349                .get(&db_e.normal_texture)
1350                .cloned(),
1351        }
1352    }
1353}