1use crate::plugin_api::{
6 MaskTargetDesc, OitTargetDesc, OpaqueTargetDesc, PickTargetDesc, ShadowTargetDesc,
7 SharedBindings,
8 target_desc::{OIT_ACCUM_BLEND, OIT_REVEAL_BLEND},
9};
10use crate::resources::DeviceResources;
11
12pub const HDR_COLOR_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float;
15
16pub const SCENE_DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24PlusStencil8;
18
19pub const SHADOW_DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
21
22pub const MASK_COLOR_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R8Unorm;
24
25pub const PICK_COLOR_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R32Uint;
27
28impl DeviceResources {
29 pub fn shared_bindings(&self) -> SharedBindings<'_> {
36 SharedBindings {
37 group0_layout: &self.camera_bind_group_layout,
38 }
39 }
40
41 pub fn opaque_target_desc(&self) -> OpaqueTargetDesc {
43 OpaqueTargetDesc {
44 color_format: HDR_COLOR_FORMAT,
45 depth_format: SCENE_DEPTH_FORMAT,
46 sample_count: self.sample_count,
47 }
48 }
49
50 pub fn oit_target_desc(&self) -> OitTargetDesc {
52 OitTargetDesc {
53 accum_format: HDR_COLOR_FORMAT,
54 reveal_format: MASK_COLOR_FORMAT,
55 depth_format: SCENE_DEPTH_FORMAT,
56 accum_blend: OIT_ACCUM_BLEND,
57 reveal_blend: OIT_REVEAL_BLEND,
58 sample_count: self.sample_count,
59 }
60 }
61
62 pub fn mask_target_desc(&self) -> MaskTargetDesc {
64 MaskTargetDesc {
65 color_format: MASK_COLOR_FORMAT,
66 depth_format: SCENE_DEPTH_FORMAT,
67 sample_count: 1,
68 }
69 }
70
71 pub fn pick_target_desc(&self) -> PickTargetDesc {
73 PickTargetDesc {
74 color_format: PICK_COLOR_FORMAT,
75 depth_format: SCENE_DEPTH_FORMAT,
76 sample_count: 1,
77 }
78 }
79
80 pub fn shadow_target_desc(&self) -> ShadowTargetDesc {
82 ShadowTargetDesc {
83 depth_format: SHADOW_DEPTH_FORMAT,
84 sample_count: 1,
85 }
86 }
87
88 pub fn texture_view(&self, id: crate::resources::TextureId) -> Option<&wgpu::TextureView> {
106 self.content.textures.get(id).map(|t| &t.view)
107 }
108
109 pub fn texture_sampler(&self, id: crate::resources::TextureId) -> Option<&wgpu::Sampler> {
115 self.content.textures.get(id).map(|t| &t.sampler)
116 }
117
118 pub fn material_sampler(&self) -> &wgpu::Sampler {
123 &self.material_sampler
124 }
125
126 pub fn lut_sampler(&self) -> &wgpu::Sampler {
131 &self.lut_sampler
132 }
133
134 pub fn shadow_filter_sampler(&self) -> &wgpu::Sampler {
139 &self.shadow_sampler
140 }
141
142 pub fn deform_bind_group_layout(&self) -> &wgpu::BindGroupLayout {
148 &self.deform.bind_group_layout
149 }
150
151 pub fn texture_count(&self) -> usize {
157 self.content.textures.len()
158 }
159
160 pub fn build_opaque_pipeline(
171 &self,
172 device: &wgpu::Device,
173 opts: &PluginPipelineOpts<'_>,
174 ) -> wgpu::RenderPipeline {
175 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
176 let desc = self.opaque_target_desc();
177 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
178 label: opts.label,
179 layout: Some(&layout),
180 vertex: wgpu::VertexState {
181 module: opts.shader,
182 entry_point: Some(opts.vs_entry),
183 buffers: opts.vertex_layouts,
184 compilation_options: Default::default(),
185 },
186 fragment: Some(wgpu::FragmentState {
187 module: opts.shader,
188 entry_point: Some(opts.fs_entry),
189 targets: &[Some(wgpu::ColorTargetState {
190 format: desc.color_format,
191 blend: opts.color_blend,
192 write_mask: wgpu::ColorWrites::ALL,
193 })],
194 compilation_options: Default::default(),
195 }),
196 primitive: opts.primitive,
197 depth_stencil: Some(wgpu::DepthStencilState {
198 format: desc.depth_format,
199 depth_write_enabled: opts.depth_write,
200 depth_compare: opts.depth_compare,
201 stencil: wgpu::StencilState::default(),
202 bias: wgpu::DepthBiasState::default(),
203 }),
204 multisample: wgpu::MultisampleState {
205 count: desc.sample_count,
206 ..Default::default()
207 },
208 multiview: None,
209 cache: None,
210 })
211 }
212
213 pub fn build_oit_pipeline(
219 &self,
220 device: &wgpu::Device,
221 opts: &PluginPipelineOpts<'_>,
222 ) -> wgpu::RenderPipeline {
223 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
224 let desc = self.oit_target_desc();
225 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
226 label: opts.label,
227 layout: Some(&layout),
228 vertex: wgpu::VertexState {
229 module: opts.shader,
230 entry_point: Some(opts.vs_entry),
231 buffers: opts.vertex_layouts,
232 compilation_options: Default::default(),
233 },
234 fragment: Some(wgpu::FragmentState {
235 module: opts.shader,
236 entry_point: Some(opts.fs_entry),
237 targets: &[
238 Some(wgpu::ColorTargetState {
239 format: desc.accum_format,
240 blend: Some(desc.accum_blend),
241 write_mask: wgpu::ColorWrites::ALL,
242 }),
243 Some(wgpu::ColorTargetState {
244 format: desc.reveal_format,
245 blend: Some(desc.reveal_blend),
246 write_mask: wgpu::ColorWrites::RED,
247 }),
248 ],
249 compilation_options: Default::default(),
250 }),
251 primitive: opts.primitive,
252 depth_stencil: Some(wgpu::DepthStencilState {
253 format: desc.depth_format,
254 depth_write_enabled: false,
255 depth_compare: wgpu::CompareFunction::LessEqual,
256 stencil: wgpu::StencilState::default(),
257 bias: wgpu::DepthBiasState::default(),
258 }),
259 multisample: wgpu::MultisampleState {
260 count: desc.sample_count,
261 ..Default::default()
262 },
263 multiview: None,
264 cache: None,
265 })
266 }
267
268 pub fn build_mask_pipeline(
274 &self,
275 device: &wgpu::Device,
276 opts: &PluginPipelineOpts<'_>,
277 ) -> wgpu::RenderPipeline {
278 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
279 let desc = self.mask_target_desc();
280 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
281 label: opts.label,
282 layout: Some(&layout),
283 vertex: wgpu::VertexState {
284 module: opts.shader,
285 entry_point: Some(opts.vs_entry),
286 buffers: opts.vertex_layouts,
287 compilation_options: Default::default(),
288 },
289 fragment: Some(wgpu::FragmentState {
290 module: opts.shader,
291 entry_point: Some(opts.fs_entry),
292 targets: &[Some(wgpu::ColorTargetState {
293 format: desc.color_format,
294 blend: None,
295 write_mask: wgpu::ColorWrites::RED,
296 })],
297 compilation_options: Default::default(),
298 }),
299 primitive: opts.primitive,
300 depth_stencil: Some(wgpu::DepthStencilState {
301 format: desc.depth_format,
302 depth_write_enabled: false,
303 depth_compare: wgpu::CompareFunction::LessEqual,
304 stencil: wgpu::StencilState::default(),
305 bias: wgpu::DepthBiasState::default(),
306 }),
307 multisample: wgpu::MultisampleState {
308 count: desc.sample_count,
309 ..Default::default()
310 },
311 multiview: None,
312 cache: None,
313 })
314 }
315
316 pub fn build_pick_pipeline(
321 &self,
322 device: &wgpu::Device,
323 opts: &PluginPipelineOpts<'_>,
324 ) -> wgpu::RenderPipeline {
325 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
326 let desc = self.pick_target_desc();
327 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
328 label: opts.label,
329 layout: Some(&layout),
330 vertex: wgpu::VertexState {
331 module: opts.shader,
332 entry_point: Some(opts.vs_entry),
333 buffers: opts.vertex_layouts,
334 compilation_options: Default::default(),
335 },
336 fragment: Some(wgpu::FragmentState {
337 module: opts.shader,
338 entry_point: Some(opts.fs_entry),
339 targets: &[Some(wgpu::ColorTargetState {
340 format: desc.color_format,
341 blend: None,
342 write_mask: wgpu::ColorWrites::RED,
343 })],
344 compilation_options: Default::default(),
345 }),
346 primitive: opts.primitive,
347 depth_stencil: Some(wgpu::DepthStencilState {
348 format: desc.depth_format,
349 depth_write_enabled: true,
350 depth_compare: wgpu::CompareFunction::LessEqual,
351 stencil: wgpu::StencilState::default(),
352 bias: wgpu::DepthBiasState::default(),
353 }),
354 multisample: wgpu::MultisampleState {
355 count: desc.sample_count,
356 ..Default::default()
357 },
358 multiview: None,
359 cache: None,
360 })
361 }
362
363 pub fn build_shadow_pipeline(
370 &self,
371 device: &wgpu::Device,
372 opts: &PluginPipelineOpts<'_>,
373 ) -> wgpu::RenderPipeline {
374 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
375 let desc = self.shadow_target_desc();
376 let fragment = if opts.fs_entry.is_empty() {
377 None
378 } else {
379 Some(wgpu::FragmentState {
380 module: opts.shader,
381 entry_point: Some(opts.fs_entry),
382 targets: &[],
383 compilation_options: Default::default(),
384 })
385 };
386 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
387 label: opts.label,
388 layout: Some(&layout),
389 vertex: wgpu::VertexState {
390 module: opts.shader,
391 entry_point: Some(opts.vs_entry),
392 buffers: opts.vertex_layouts,
393 compilation_options: Default::default(),
394 },
395 fragment,
396 primitive: opts.primitive,
397 depth_stencil: Some(wgpu::DepthStencilState {
398 format: desc.depth_format,
399 depth_write_enabled: true,
400 depth_compare: wgpu::CompareFunction::LessEqual,
401 stencil: wgpu::StencilState::default(),
402 bias: wgpu::DepthBiasState {
403 constant: 2,
404 slope_scale: 2.0,
405 clamp: 0.0,
406 },
407 }),
408 multisample: wgpu::MultisampleState {
409 count: desc.sample_count,
410 ..Default::default()
411 },
412 multiview: None,
413 cache: None,
414 })
415 }
416}
417
418pub struct PluginPipelineOpts<'a> {
421 pub label: Option<&'a str>,
423 pub shader: &'a wgpu::ShaderModule,
425 pub vs_entry: &'a str,
427 pub fs_entry: &'a str,
430 pub vertex_layouts: &'a [wgpu::VertexBufferLayout<'a>],
432 pub extra_bind_group_layouts: &'a [&'a wgpu::BindGroupLayout],
436 pub primitive: wgpu::PrimitiveState,
438 pub color_blend: Option<wgpu::BlendState>,
442 pub depth_write: bool,
445 pub depth_compare: wgpu::CompareFunction,
448}
449
450impl<'a> PluginPipelineOpts<'a> {
451 pub fn new(
456 label: Option<&'a str>,
457 shader: &'a wgpu::ShaderModule,
458 vs_entry: &'a str,
459 fs_entry: &'a str,
460 vertex_layouts: &'a [wgpu::VertexBufferLayout<'a>],
461 ) -> Self {
462 Self {
463 label,
464 shader,
465 vs_entry,
466 fs_entry,
467 vertex_layouts,
468 extra_bind_group_layouts: &[],
469 primitive: wgpu::PrimitiveState {
470 topology: wgpu::PrimitiveTopology::TriangleList,
471 cull_mode: Some(wgpu::Face::Back),
472 ..Default::default()
473 },
474 color_blend: None,
475 depth_write: true,
476 depth_compare: wgpu::CompareFunction::LessEqual,
477 }
478 }
479}
480
481fn build_layout(
482 device: &wgpu::Device,
483 label: Option<&str>,
484 res: &DeviceResources,
485 extras: &[&wgpu::BindGroupLayout],
486) -> wgpu::PipelineLayout {
487 let mut bgls: Vec<&wgpu::BindGroupLayout> = Vec::with_capacity(1 + extras.len());
488 bgls.push(&res.camera_bind_group_layout);
489 bgls.extend(extras.iter().copied());
490 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
491 label,
492 bind_group_layouts: &bgls,
493 push_constant_ranges: &[],
494 })
495}