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::ViewportGpuResources;
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 ViewportGpuResources {
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: u64) -> Option<&wgpu::TextureView> {
107 self.textures.get(id as usize).map(|t| &t.view)
108 }
109
110 pub fn texture_sampler(&self, id: u64) -> Option<&wgpu::Sampler> {
116 self.textures.get(id as usize).map(|t| &t.sampler)
117 }
118
119 pub fn material_sampler(&self) -> &wgpu::Sampler {
124 &self.material_sampler
125 }
126
127 pub fn lut_sampler(&self) -> &wgpu::Sampler {
132 &self.lut_sampler
133 }
134
135 pub fn shadow_filter_sampler(&self) -> &wgpu::Sampler {
140 &self.shadow_sampler
141 }
142
143 pub fn deform_bind_group_layout(&self) -> &wgpu::BindGroupLayout {
149 &self.deform.bind_group_layout
150 }
151
152 pub fn texture_count(&self) -> usize {
158 self.textures.len()
159 }
160
161 pub fn build_opaque_pipeline(
172 &self,
173 device: &wgpu::Device,
174 opts: &PluginPipelineOpts<'_>,
175 ) -> wgpu::RenderPipeline {
176 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
177 let desc = self.opaque_target_desc();
178 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
179 label: opts.label,
180 layout: Some(&layout),
181 vertex: wgpu::VertexState {
182 module: opts.shader,
183 entry_point: Some(opts.vs_entry),
184 buffers: opts.vertex_layouts,
185 compilation_options: Default::default(),
186 },
187 fragment: Some(wgpu::FragmentState {
188 module: opts.shader,
189 entry_point: Some(opts.fs_entry),
190 targets: &[Some(wgpu::ColorTargetState {
191 format: desc.color_format,
192 blend: opts.color_blend,
193 write_mask: wgpu::ColorWrites::ALL,
194 })],
195 compilation_options: Default::default(),
196 }),
197 primitive: opts.primitive,
198 depth_stencil: Some(wgpu::DepthStencilState {
199 format: desc.depth_format,
200 depth_write_enabled: opts.depth_write,
201 depth_compare: opts.depth_compare,
202 stencil: wgpu::StencilState::default(),
203 bias: wgpu::DepthBiasState::default(),
204 }),
205 multisample: wgpu::MultisampleState {
206 count: desc.sample_count,
207 ..Default::default()
208 },
209 multiview: None,
210 cache: None,
211 })
212 }
213
214 pub fn build_oit_pipeline(
220 &self,
221 device: &wgpu::Device,
222 opts: &PluginPipelineOpts<'_>,
223 ) -> wgpu::RenderPipeline {
224 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
225 let desc = self.oit_target_desc();
226 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
227 label: opts.label,
228 layout: Some(&layout),
229 vertex: wgpu::VertexState {
230 module: opts.shader,
231 entry_point: Some(opts.vs_entry),
232 buffers: opts.vertex_layouts,
233 compilation_options: Default::default(),
234 },
235 fragment: Some(wgpu::FragmentState {
236 module: opts.shader,
237 entry_point: Some(opts.fs_entry),
238 targets: &[
239 Some(wgpu::ColorTargetState {
240 format: desc.accum_format,
241 blend: Some(desc.accum_blend),
242 write_mask: wgpu::ColorWrites::ALL,
243 }),
244 Some(wgpu::ColorTargetState {
245 format: desc.reveal_format,
246 blend: Some(desc.reveal_blend),
247 write_mask: wgpu::ColorWrites::RED,
248 }),
249 ],
250 compilation_options: Default::default(),
251 }),
252 primitive: opts.primitive,
253 depth_stencil: Some(wgpu::DepthStencilState {
254 format: desc.depth_format,
255 depth_write_enabled: false,
256 depth_compare: wgpu::CompareFunction::LessEqual,
257 stencil: wgpu::StencilState::default(),
258 bias: wgpu::DepthBiasState::default(),
259 }),
260 multisample: wgpu::MultisampleState {
261 count: desc.sample_count,
262 ..Default::default()
263 },
264 multiview: None,
265 cache: None,
266 })
267 }
268
269 pub fn build_mask_pipeline(
275 &self,
276 device: &wgpu::Device,
277 opts: &PluginPipelineOpts<'_>,
278 ) -> wgpu::RenderPipeline {
279 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
280 let desc = self.mask_target_desc();
281 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
282 label: opts.label,
283 layout: Some(&layout),
284 vertex: wgpu::VertexState {
285 module: opts.shader,
286 entry_point: Some(opts.vs_entry),
287 buffers: opts.vertex_layouts,
288 compilation_options: Default::default(),
289 },
290 fragment: Some(wgpu::FragmentState {
291 module: opts.shader,
292 entry_point: Some(opts.fs_entry),
293 targets: &[Some(wgpu::ColorTargetState {
294 format: desc.color_format,
295 blend: None,
296 write_mask: wgpu::ColorWrites::RED,
297 })],
298 compilation_options: Default::default(),
299 }),
300 primitive: opts.primitive,
301 depth_stencil: Some(wgpu::DepthStencilState {
302 format: desc.depth_format,
303 depth_write_enabled: false,
304 depth_compare: wgpu::CompareFunction::LessEqual,
305 stencil: wgpu::StencilState::default(),
306 bias: wgpu::DepthBiasState::default(),
307 }),
308 multisample: wgpu::MultisampleState {
309 count: desc.sample_count,
310 ..Default::default()
311 },
312 multiview: None,
313 cache: None,
314 })
315 }
316
317 pub fn build_pick_pipeline(
322 &self,
323 device: &wgpu::Device,
324 opts: &PluginPipelineOpts<'_>,
325 ) -> wgpu::RenderPipeline {
326 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
327 let desc = self.pick_target_desc();
328 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
329 label: opts.label,
330 layout: Some(&layout),
331 vertex: wgpu::VertexState {
332 module: opts.shader,
333 entry_point: Some(opts.vs_entry),
334 buffers: opts.vertex_layouts,
335 compilation_options: Default::default(),
336 },
337 fragment: Some(wgpu::FragmentState {
338 module: opts.shader,
339 entry_point: Some(opts.fs_entry),
340 targets: &[Some(wgpu::ColorTargetState {
341 format: desc.color_format,
342 blend: None,
343 write_mask: wgpu::ColorWrites::RED,
344 })],
345 compilation_options: Default::default(),
346 }),
347 primitive: opts.primitive,
348 depth_stencil: Some(wgpu::DepthStencilState {
349 format: desc.depth_format,
350 depth_write_enabled: true,
351 depth_compare: wgpu::CompareFunction::LessEqual,
352 stencil: wgpu::StencilState::default(),
353 bias: wgpu::DepthBiasState::default(),
354 }),
355 multisample: wgpu::MultisampleState {
356 count: desc.sample_count,
357 ..Default::default()
358 },
359 multiview: None,
360 cache: None,
361 })
362 }
363
364 pub fn build_shadow_pipeline(
371 &self,
372 device: &wgpu::Device,
373 opts: &PluginPipelineOpts<'_>,
374 ) -> wgpu::RenderPipeline {
375 let layout = build_layout(device, opts.label, self, opts.extra_bind_group_layouts);
376 let desc = self.shadow_target_desc();
377 let fragment = if opts.fs_entry.is_empty() {
378 None
379 } else {
380 Some(wgpu::FragmentState {
381 module: opts.shader,
382 entry_point: Some(opts.fs_entry),
383 targets: &[],
384 compilation_options: Default::default(),
385 })
386 };
387 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
388 label: opts.label,
389 layout: Some(&layout),
390 vertex: wgpu::VertexState {
391 module: opts.shader,
392 entry_point: Some(opts.vs_entry),
393 buffers: opts.vertex_layouts,
394 compilation_options: Default::default(),
395 },
396 fragment,
397 primitive: opts.primitive,
398 depth_stencil: Some(wgpu::DepthStencilState {
399 format: desc.depth_format,
400 depth_write_enabled: true,
401 depth_compare: wgpu::CompareFunction::LessEqual,
402 stencil: wgpu::StencilState::default(),
403 bias: wgpu::DepthBiasState {
404 constant: 2,
405 slope_scale: 2.0,
406 clamp: 0.0,
407 },
408 }),
409 multisample: wgpu::MultisampleState {
410 count: desc.sample_count,
411 ..Default::default()
412 },
413 multiview: None,
414 cache: None,
415 })
416 }
417}
418
419pub struct PluginPipelineOpts<'a> {
422 pub label: Option<&'a str>,
424 pub shader: &'a wgpu::ShaderModule,
426 pub vs_entry: &'a str,
428 pub fs_entry: &'a str,
431 pub vertex_layouts: &'a [wgpu::VertexBufferLayout<'a>],
433 pub extra_bind_group_layouts: &'a [&'a wgpu::BindGroupLayout],
437 pub primitive: wgpu::PrimitiveState,
439 pub color_blend: Option<wgpu::BlendState>,
443 pub depth_write: bool,
446 pub depth_compare: wgpu::CompareFunction,
449}
450
451impl<'a> PluginPipelineOpts<'a> {
452 pub fn new(
457 label: Option<&'a str>,
458 shader: &'a wgpu::ShaderModule,
459 vs_entry: &'a str,
460 fs_entry: &'a str,
461 vertex_layouts: &'a [wgpu::VertexBufferLayout<'a>],
462 ) -> Self {
463 Self {
464 label,
465 shader,
466 vs_entry,
467 fs_entry,
468 vertex_layouts,
469 extra_bind_group_layouts: &[],
470 primitive: wgpu::PrimitiveState {
471 topology: wgpu::PrimitiveTopology::TriangleList,
472 cull_mode: Some(wgpu::Face::Back),
473 ..Default::default()
474 },
475 color_blend: None,
476 depth_write: true,
477 depth_compare: wgpu::CompareFunction::LessEqual,
478 }
479 }
480}
481
482fn build_layout(
483 device: &wgpu::Device,
484 label: Option<&str>,
485 res: &ViewportGpuResources,
486 extras: &[&wgpu::BindGroupLayout],
487) -> wgpu::PipelineLayout {
488 let mut bgls: Vec<&wgpu::BindGroupLayout> = Vec::with_capacity(1 + extras.len());
489 bgls.push(&res.camera_bind_group_layout);
490 bgls.extend(extras.iter().copied());
491 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
492 label,
493 bind_group_layouts: &bgls,
494 push_constant_ranges: &[],
495 })
496}