khora_lanes/render_lane/shadows_lane/algo/
atlas_2d.rs1use std::borrow::Cow;
25use std::sync::RwLock;
26
27use khora_core::math::{Extent3D, Mat4};
28use khora_core::renderer::api::resource::{
29 CameraUniformData, ImageAspect, TextureDescriptor, TextureDimension, TextureId, TextureUsage,
30 TextureViewDescriptor, TextureViewDimension, TextureViewId,
31};
32use khora_core::renderer::api::util::dynamic_uniform_buffer::DynamicUniformRingBuffer;
33use khora_core::renderer::api::util::{SampleCount, TextureFormat};
34use khora_core::renderer::error::RenderError;
35use khora_core::renderer::GraphicsDevice;
36
37use super::pass::{build_draw_cmds, AttachmentPass};
38
39pub struct Atlas2D {
41 pub texture: RwLock<Option<TextureId>>,
43 pub view: RwLock<Option<TextureViewId>>,
46}
47
48impl Default for Atlas2D {
49 fn default() -> Self {
50 Self {
51 texture: RwLock::new(None),
52 view: RwLock::new(None),
53 }
54 }
55}
56
57impl Atlas2D {
58 pub fn create(
65 &self,
66 device: &dyn GraphicsDevice,
67 resolution: u32,
68 max_lights: u32,
69 label: &str,
70 ) -> Result<(), RenderError> {
71 use crate::render_lane::util::lock::write_lock_render;
72
73 let texture = device
74 .create_texture(&TextureDescriptor {
75 label: Some(Cow::Owned(format!("{label} Texture"))),
76 size: Extent3D {
77 width: resolution,
78 height: resolution,
79 depth_or_array_layers: max_lights,
80 },
81 mip_level_count: 1,
82 sample_count: SampleCount::X1,
83 dimension: TextureDimension::D2,
84 format: TextureFormat::Depth32Float,
85 usage: TextureUsage::DEPTH_STENCIL_ATTACHMENT | TextureUsage::TEXTURE_BINDING,
86 view_formats: Cow::Borrowed(&[]),
87 })
88 .map_err(RenderError::ResourceError)?;
89
90 let view = device
91 .create_texture_view(
92 texture,
93 &TextureViewDescriptor {
94 label: Some(Cow::Owned(format!("{label} View"))),
95 format: Some(TextureFormat::Depth32Float),
96 dimension: Some(TextureViewDimension::D2Array),
97 aspect: ImageAspect::DepthOnly,
98 base_mip_level: 0,
99 mip_level_count: Some(1),
100 base_array_layer: 0,
101 array_layer_count: Some(max_lights),
102 },
103 )
104 .map_err(RenderError::ResourceError)?;
105
106 *write_lock_render(&self.texture, "Atlas2D.texture")? = Some(texture);
107 *write_lock_render(&self.view, "Atlas2D.view")? = Some(view);
108 Ok(())
109 }
110
111 pub fn view_id(&self) -> Option<TextureViewId> {
113 self.view.read().ok().and_then(|g| *g)
114 }
115
116 pub fn destroy(&self, device: &dyn GraphicsDevice) {
118 if let Some(view) = self.view.write().ok().and_then(|mut g| g.take()) {
119 if let Err(e) = device.destroy_texture_view(view) {
120 log::warn!("Atlas2D: failed to destroy view: {:?}", e);
121 }
122 }
123 if let Some(texture) = self.texture.write().ok().and_then(|mut g| g.take()) {
124 if let Err(e) = device.destroy_texture(texture) {
125 log::warn!("Atlas2D: failed to destroy texture: {:?}", e);
126 }
127 }
128 }
129}
130
131#[allow(clippy::too_many_arguments)]
138pub fn collect_pass(
139 atlas_view: TextureViewId,
140 layer: u32,
141 light_pos: khora_core::math::Vec3,
142 view_proj: Mat4,
143 device: &dyn GraphicsDevice,
144 render_world: &khora_data::render::RenderWorld,
145 gpu_meshes: &khora_data::assets::Assets<khora_core::renderer::api::scene::GpuMesh>,
146 camera_ring: &mut DynamicUniformRingBuffer,
147 model_ring: &mut DynamicUniformRingBuffer,
148) -> Option<AttachmentPass> {
149 let camera_data = CameraUniformData {
150 view_projection: view_proj.to_cols_array_2d(),
151 camera_position: [light_pos.x, light_pos.y, light_pos.z, 1.0],
152 };
153 let camera_offset = match camera_ring.push(device, bytemuck::bytes_of(&camera_data)) {
154 Ok(off) => off,
155 Err(e) => {
156 log::error!("Atlas2D: failed to push camera uniform: {:?}", e);
157 return None;
158 }
159 };
160 let camera_bg = *camera_ring.current_bind_group();
161
162 let draw_cmds = build_draw_cmds(device, render_world, gpu_meshes, model_ring);
163
164 Some(AttachmentPass {
165 target_view: atlas_view,
166 base_array_layer: layer,
167 camera_bg,
168 camera_offset,
169 draw_cmds,
170 })
171}