1use crate::resources::{DualPipeline, ViewportGpuResources};
7use crate::scene::material::ItemSettings;
8use wgpu::util::DeviceExt as _;
9
10#[repr(C)]
28#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
29pub struct ImplicitPrimitive {
30 pub kind: u32,
32 pub blend: f32,
35 #[doc(hidden)]
36 pub _pad: [f32; 2],
37 pub params: [f32; 8],
39 pub colour: [f32; 4],
42}
43
44#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
46pub enum ImplicitBlendMode {
47 #[default]
49 Union,
50 SmoothUnion,
52 Intersection,
54}
55
56#[derive(Clone, Copy, Debug)]
58pub struct GpuImplicitOptions {
59 pub max_steps: u32,
61 pub step_scale: f32,
64 pub hit_threshold: f32,
66 pub max_distance: f32,
68}
69
70impl Default for GpuImplicitOptions {
71 fn default() -> Self {
72 Self {
73 max_steps: 128,
74 step_scale: 0.85,
75 hit_threshold: 5e-4,
76 max_distance: 40.0,
77 }
78 }
79}
80
81#[non_exhaustive]
101pub struct GpuImplicitItem {
102 pub primitives: Vec<ImplicitPrimitive>,
104 pub blend_mode: ImplicitBlendMode,
106 pub march_options: GpuImplicitOptions,
108 pub settings: ItemSettings,
110}
111
112impl Default for GpuImplicitItem {
113 fn default() -> Self {
114 Self {
115 primitives: Vec::new(),
116 blend_mode: crate::resources::ImplicitBlendMode::Union,
117 march_options: GpuImplicitOptions::default(),
118 settings: ItemSettings::default(),
119 }
120 }
121}
122
123impl ImplicitPrimitive {
124 pub fn zeroed() -> Self {
126 bytemuck::Zeroable::zeroed()
127 }
128}
129
130#[repr(C)]
138#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
139pub(crate) struct ImplicitUniformRaw {
140 pub num_primitives: u32,
141 pub blend_mode: u32,
142 pub max_steps: u32,
143 pub unlit: u32,
144 pub step_scale: f32,
145 pub hit_threshold: f32,
146 pub max_distance: f32,
147 pub opacity: f32,
148 pub primitives: [ImplicitPrimitive; 16],
149}
150
151pub(crate) struct ImplicitGpuItem {
153 pub _uniform_buf: wgpu::Buffer,
154 pub bind_group: wgpu::BindGroup,
155}
156
157impl ViewportGpuResources {
162 pub(crate) fn ensure_implicit_pipeline(&mut self, device: &wgpu::Device) {
167 if self.implicit_pipeline.is_some() {
168 return;
169 }
170
171 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
172 label: Some("implicit_shader"),
173 source: wgpu::ShaderSource::Wgsl(include_str!(concat!(env!("OUT_DIR"), "/implicit.wgsl")).into()),
174 });
175
176 let implicit_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
178 label: Some("implicit_bgl"),
179 entries: &[wgpu::BindGroupLayoutEntry {
180 binding: 0,
181 visibility: wgpu::ShaderStages::FRAGMENT,
182 ty: wgpu::BindingType::Buffer {
183 ty: wgpu::BufferBindingType::Uniform,
184 has_dynamic_offset: false,
185 min_binding_size: None,
186 },
187 count: None,
188 }],
189 });
190
191 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
193 label: Some("implicit_pipeline_layout"),
194 bind_group_layouts: &[&self.camera_bind_group_layout, &implicit_bgl],
195 push_constant_ranges: &[],
196 });
197
198 let make = |fmt: wgpu::TextureFormat| {
199 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
200 label: Some("implicit_pipeline"),
201 layout: Some(&layout),
202 vertex: wgpu::VertexState {
203 module: &shader,
204 entry_point: Some("vs_main"),
205 buffers: &[],
206 compilation_options: wgpu::PipelineCompilationOptions::default(),
207 },
208 fragment: Some(wgpu::FragmentState {
209 module: &shader,
210 entry_point: Some("fs_main"),
211 targets: &[Some(wgpu::ColorTargetState {
212 format: fmt,
213 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
214 write_mask: wgpu::ColorWrites::ALL,
215 })],
216 compilation_options: wgpu::PipelineCompilationOptions::default(),
217 }),
218 primitive: wgpu::PrimitiveState {
219 topology: wgpu::PrimitiveTopology::TriangleList,
220 cull_mode: None,
221 ..Default::default()
222 },
223 depth_stencil: Some(wgpu::DepthStencilState {
225 format: wgpu::TextureFormat::Depth24PlusStencil8,
226 depth_write_enabled: true,
227 depth_compare: wgpu::CompareFunction::LessEqual,
228 stencil: wgpu::StencilState::default(),
229 bias: wgpu::DepthBiasState::default(),
230 }),
231 multisample: wgpu::MultisampleState {
232 count: 1,
233 mask: !0,
234 alpha_to_coverage_enabled: false,
235 },
236 multiview: None,
237 cache: None,
238 })
239 };
240
241 self.implicit_bgl = Some(implicit_bgl);
242 self.implicit_pipeline = Some(DualPipeline {
243 ldr: make(self.target_format),
244 hdr: make(wgpu::TextureFormat::Rgba16Float),
245 });
246 }
247
248 pub(crate) fn ensure_implicit_outline_mask_pipeline(&mut self, device: &wgpu::Device) {
254 if self.implicit_outline_mask_pipeline.is_some() {
255 return;
256 }
257
258 let implicit_bgl = self.implicit_bgl.as_ref().expect(
259 "ensure_implicit_pipeline must be called before ensure_implicit_outline_mask_pipeline",
260 );
261
262 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
263 label: Some("implicit_outline_mask_shader"),
264 source: wgpu::ShaderSource::Wgsl(
265 include_str!(concat!(env!("OUT_DIR"), "/implicit_outline_mask.wgsl")).into(),
266 ),
267 });
268
269 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
270 label: Some("implicit_outline_mask_pipeline_layout"),
271 bind_group_layouts: &[&self.camera_bind_group_layout, implicit_bgl],
272 push_constant_ranges: &[],
273 });
274
275 self.implicit_outline_mask_pipeline = Some(device.create_render_pipeline(
276 &wgpu::RenderPipelineDescriptor {
277 label: Some("implicit_outline_mask_pipeline"),
278 layout: Some(&layout),
279 vertex: wgpu::VertexState {
280 module: &shader,
281 entry_point: Some("vs_main"),
282 buffers: &[],
283 compilation_options: wgpu::PipelineCompilationOptions::default(),
284 },
285 fragment: Some(wgpu::FragmentState {
286 module: &shader,
287 entry_point: Some("fs_main"),
288 targets: &[Some(wgpu::ColorTargetState {
289 format: wgpu::TextureFormat::R8Unorm,
290 blend: None,
291 write_mask: wgpu::ColorWrites::ALL,
292 })],
293 compilation_options: wgpu::PipelineCompilationOptions::default(),
294 }),
295 primitive: wgpu::PrimitiveState {
296 topology: wgpu::PrimitiveTopology::TriangleList,
297 cull_mode: None,
298 ..Default::default()
299 },
300 depth_stencil: Some(wgpu::DepthStencilState {
301 format: wgpu::TextureFormat::Depth24PlusStencil8,
302 depth_write_enabled: true,
303 depth_compare: wgpu::CompareFunction::Less,
304 stencil: wgpu::StencilState::default(),
305 bias: wgpu::DepthBiasState::default(),
306 }),
307 multisample: wgpu::MultisampleState {
308 count: 1,
309 mask: !0,
310 alpha_to_coverage_enabled: false,
311 },
312 multiview: None,
313 cache: None,
314 },
315 ));
316 }
317
318 pub(crate) fn upload_implicit_item(
322 &self,
323 device: &wgpu::Device,
324 item: &GpuImplicitItem,
325 ) -> ImplicitGpuItem {
326 let blend_mode_u32 = match item.blend_mode {
328 ImplicitBlendMode::Union => 0u32,
329 ImplicitBlendMode::SmoothUnion => 1,
330 ImplicitBlendMode::Intersection => 2,
331 };
332
333 let mut raw = ImplicitUniformRaw {
334 num_primitives: item.primitives.len().min(16) as u32,
335 blend_mode: blend_mode_u32,
336 max_steps: item.march_options.max_steps,
337 unlit: item.settings.unlit as u32,
338 step_scale: item.march_options.step_scale,
339 hit_threshold: item.march_options.hit_threshold,
340 max_distance: item.march_options.max_distance,
341 opacity: item.settings.opacity,
342 primitives: [ImplicitPrimitive::zeroed(); 16],
343 };
344
345 for (i, prim) in item.primitives.iter().take(16).enumerate() {
346 raw.primitives[i] = *prim;
347 }
348
349 let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
350 label: Some("implicit_uniform_buf"),
351 contents: bytemuck::bytes_of(&raw),
352 usage: wgpu::BufferUsages::UNIFORM,
353 });
354
355 let bgl = self
356 .implicit_bgl
357 .as_ref()
358 .expect("ensure_implicit_pipeline not called");
359
360 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
361 label: Some("implicit_bind_group"),
362 layout: bgl,
363 entries: &[wgpu::BindGroupEntry {
364 binding: 0,
365 resource: uniform_buf.as_entire_binding(),
366 }],
367 });
368
369 ImplicitGpuItem {
370 _uniform_buf: uniform_buf,
371 bind_group,
372 }
373 }
374}