1use std::collections::HashMap;
17
18use super::{CallbackResources, ScreenDescriptor};
19
20const BLIT_WGSL: &str = r#"
22@group(0) @binding(0) var scene_tex: texture_2d<f32>;
23@group(0) @binding(1) var scene_smp: sampler;
24struct VsOut {
25 @builtin(position) pos: vec4<f32>,
26 @location(0) uv: vec2<f32>,
27};
28@vertex
29fn vs_main(@builtin(vertex_index) i: u32) -> VsOut {
30 let x = f32(i / 2u) * 4.0 - 1.0;
31 let y = f32(i % 2u) * 4.0 - 1.0;
32 var out: VsOut;
33 out.pos = vec4<f32>(x, y, 0.0, 1.0);
34 out.uv = vec2<f32>((x + 1.0) * 0.5, (1.0 - y) * 0.5);
35 return out;
36}
37@fragment
38fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
39 return textureSample(scene_tex, scene_smp, in.uv);
40}
41"#;
42
43#[derive(Default)]
50pub struct DepthComposite {
51 targets: HashMap<String, Target>,
52}
53
54struct Target {
55 key: (wgpu::TextureFormat, u32, u32, u32),
56 #[allow(dead_code)]
57 scene: wgpu::Texture,
58 scene_view: wgpu::TextureView,
59 #[allow(dead_code)]
60 depth: wgpu::Texture,
61 depth_view: wgpu::TextureView,
62 depth_format: wgpu::TextureFormat,
63 blit_pipeline: wgpu::RenderPipeline,
64 blit_bind: wgpu::BindGroup,
65}
66
67impl DepthComposite {
68 pub fn get(resources: &mut CallbackResources) -> &mut Self {
71 resources.get_or_insert_with::<Self>()
72 }
73
74 #[allow(clippy::too_many_arguments)] pub fn ensure(
79 &mut self,
80 device: &wgpu::Device,
81 screen: &ScreenDescriptor,
82 id: &str,
83 w: u32,
84 h: u32,
85 ) {
86 let w = w.max(1);
87 let h = h.max(1);
88 let key = (screen.target_format, screen.sample_count, w, h);
89 if self.targets.get(id).is_some_and(|t| t.key == key) {
90 return;
91 }
92 let depth_format = wgpu::TextureFormat::Depth24PlusStencil8;
93 let scene = device.create_texture(&wgpu::TextureDescriptor {
94 label: Some("depth_composite_scene"),
95 size: wgpu::Extent3d {
96 width: w,
97 height: h,
98 depth_or_array_layers: 1,
99 },
100 mip_level_count: 1,
101 sample_count: 1,
102 dimension: wgpu::TextureDimension::D2,
103 format: screen.target_format,
104 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
105 view_formats: &[],
106 });
107 let scene_view = scene.create_view(&wgpu::TextureViewDescriptor::default());
108 let depth = device.create_texture(&wgpu::TextureDescriptor {
109 label: Some("depth_composite_depth"),
110 size: wgpu::Extent3d {
111 width: w,
112 height: h,
113 depth_or_array_layers: 1,
114 },
115 mip_level_count: 1,
116 sample_count: 1,
117 dimension: wgpu::TextureDimension::D2,
118 format: depth_format,
119 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
120 view_formats: &[],
121 });
122 let depth_view = depth.create_view(&wgpu::TextureViewDescriptor::default());
123 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
124 label: Some("depth_composite_blit"),
125 source: wgpu::ShaderSource::Wgsl(BLIT_WGSL.into()),
126 });
127 let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
128 label: Some("depth_composite_blit_bgl"),
129 entries: &[
130 wgpu::BindGroupLayoutEntry {
131 binding: 0,
132 visibility: wgpu::ShaderStages::FRAGMENT,
133 ty: wgpu::BindingType::Texture {
134 sample_type: wgpu::TextureSampleType::Float { filterable: true },
135 view_dimension: wgpu::TextureViewDimension::D2,
136 multisampled: false,
137 },
138 count: None,
139 },
140 wgpu::BindGroupLayoutEntry {
141 binding: 1,
142 visibility: wgpu::ShaderStages::FRAGMENT,
143 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
144 count: None,
145 },
146 ],
147 });
148 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
149 label: Some("depth_composite_blit_sampler"),
150 address_mode_u: wgpu::AddressMode::ClampToEdge,
151 address_mode_v: wgpu::AddressMode::ClampToEdge,
152 address_mode_w: wgpu::AddressMode::ClampToEdge,
153 mag_filter: wgpu::FilterMode::Linear,
154 min_filter: wgpu::FilterMode::Linear,
155 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
156 lod_min_clamp: 0.0,
157 lod_max_clamp: 1.0,
158 compare: None,
159 anisotropy_clamp: 1,
160 border_color: None,
161 });
162 let bind = device.create_bind_group(&wgpu::BindGroupDescriptor {
163 label: Some("depth_composite_blit_bg"),
164 layout: &layout,
165 entries: &[
166 wgpu::BindGroupEntry {
167 binding: 0,
168 resource: wgpu::BindingResource::TextureView(&scene_view),
169 },
170 wgpu::BindGroupEntry {
171 binding: 1,
172 resource: wgpu::BindingResource::Sampler(&sampler),
173 },
174 ],
175 });
176 let pipe_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
177 label: Some("depth_composite_blit_pl"),
178 bind_group_layouts: &[Some(&layout)],
179 immediate_size: 0,
180 });
181 let blit_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
182 label: Some("depth_composite_blit"),
183 layout: Some(&pipe_layout),
184 vertex: wgpu::VertexState {
185 module: &shader,
186 entry_point: Some("vs_main"),
187 buffers: &[],
188 compilation_options: Default::default(),
189 },
190 fragment: Some(wgpu::FragmentState {
191 module: &shader,
192 entry_point: Some("fs_main"),
193 targets: &[Some(wgpu::ColorTargetState {
194 format: screen.target_format,
195 blend: Some(wgpu::BlendState::REPLACE),
196 write_mask: wgpu::ColorWrites::ALL,
197 })],
198 compilation_options: Default::default(),
199 }),
200 primitive: wgpu::PrimitiveState {
201 topology: wgpu::PrimitiveTopology::TriangleList,
202 ..Default::default()
203 },
204 depth_stencil: Some(wgpu::DepthStencilState {
206 format: depth_format,
207 depth_write_enabled: Some(false),
208 depth_compare: Some(wgpu::CompareFunction::Always),
209 stencil: wgpu::StencilState {
210 front: wgpu::StencilFaceState {
211 compare: wgpu::CompareFunction::LessEqual,
212 ..Default::default()
213 },
214 back: wgpu::StencilFaceState {
215 compare: wgpu::CompareFunction::LessEqual,
216 ..Default::default()
217 },
218 ..Default::default()
219 },
220 bias: wgpu::DepthBiasState::default(),
221 }),
222 multisample: wgpu::MultisampleState {
223 count: screen.sample_count,
224 mask: !0,
225 alpha_to_coverage_enabled: false,
226 },
227 multiview_mask: None,
228 cache: None,
229 });
230 self.targets.insert(
231 id.to_string(),
232 Target {
233 key,
234 scene,
235 scene_view,
236 depth,
237 depth_view,
238 depth_format,
239 blit_pipeline,
240 blit_bind: bind,
241 },
242 );
243 }
244
245 pub fn begin_scene<'a>(
250 &'a self,
251 id: &str,
252 encoder: &'a mut wgpu::CommandEncoder,
253 clear: [f32; 4],
254 ) -> Option<wgpu::RenderPass<'a>> {
255 let t = self.targets.get(id)?;
256 let (w, h) = (t.key.2 as f32, t.key.3 as f32);
257 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
258 label: Some("depth_composite_scene"),
259 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
260 view: &t.scene_view,
261 depth_slice: None,
262 resolve_target: None,
263 ops: wgpu::Operations {
264 load: wgpu::LoadOp::Clear(wgpu::Color {
265 r: clear[0] as f64,
266 g: clear[1] as f64,
267 b: clear[2] as f64,
268 a: clear[3] as f64,
269 }),
270 store: wgpu::StoreOp::Store,
271 },
272 })],
273 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
274 view: &t.depth_view,
275 depth_ops: Some(wgpu::Operations {
276 load: wgpu::LoadOp::Clear(1.0),
277 store: wgpu::StoreOp::Store,
278 }),
279 stencil_ops: Some(wgpu::Operations {
280 load: wgpu::LoadOp::Clear(0),
281 store: wgpu::StoreOp::Store,
282 }),
283 }),
284 timestamp_writes: None,
285 occlusion_query_set: None,
286 multiview_mask: None,
287 });
288 pass.set_viewport(0.0, 0.0, w, h, 0.0, 1.0);
289 Some(pass)
290 }
291
292 pub fn depth_format(&self, id: &str) -> Option<wgpu::TextureFormat> {
294 self.targets.get(id).map(|t| t.depth_format)
295 }
296
297 pub fn blit(&self, id: &str, rpass: &mut wgpu::RenderPass<'_>) {
302 let Some(t) = self.targets.get(id) else {
303 return;
304 };
305 rpass.set_pipeline(&t.blit_pipeline);
306 rpass.set_bind_group(0, &t.blit_bind, &[]);
307 rpass.draw(0..3, 0..1);
308 }
309}