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