use wgpu::util::DeviceExt as _;
use crate::resources::ViewportGpuResources;
#[repr(C)]
#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct ImplicitPrimitive {
pub kind: u32,
pub blend: f32,
#[doc(hidden)]
pub _pad: [f32; 2],
pub params: [f32; 8],
pub color: [f32; 4],
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ImplicitBlendMode {
#[default]
Union,
SmoothUnion,
Intersection,
}
#[derive(Clone, Copy, Debug)]
pub struct GpuImplicitOptions {
pub max_steps: u32,
pub step_scale: f32,
pub hit_threshold: f32,
pub max_distance: f32,
}
impl Default for GpuImplicitOptions {
fn default() -> Self {
Self {
max_steps: 128,
step_scale: 0.85,
hit_threshold: 5e-4,
max_distance: 40.0,
}
}
}
pub struct GpuImplicitItem {
pub primitives: Vec<ImplicitPrimitive>,
pub blend_mode: ImplicitBlendMode,
pub march_options: GpuImplicitOptions,
}
impl ImplicitPrimitive {
pub fn zeroed() -> Self {
bytemuck::Zeroable::zeroed()
}
}
#[repr(C)]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub(crate) struct ImplicitUniformRaw {
pub num_primitives: u32,
pub blend_mode: u32,
pub max_steps: u32,
pub _pad0: u32,
pub step_scale: f32,
pub hit_threshold: f32,
pub max_distance: f32,
pub _pad1: f32,
pub primitives: [ImplicitPrimitive; 16],
}
pub(crate) struct ImplicitGpuItem {
pub uniform_buf: wgpu::Buffer,
pub bind_group: wgpu::BindGroup,
}
impl ViewportGpuResources {
pub(crate) fn ensure_implicit_pipeline(&mut self, device: &wgpu::Device) {
if self.implicit_pipeline.is_some() {
return;
}
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("implicit_shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/implicit.wgsl").into()),
});
let implicit_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("implicit_bgl"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("implicit_pipeline_layout"),
bind_group_layouts: &[&self.camera_bind_group_layout, &implicit_bgl],
push_constant_ranges: &[],
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("implicit_pipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_main"),
buffers: &[],
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format: self.target_format,
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
cull_mode: None,
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24PlusStencil8,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::LessEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
});
self.implicit_bgl = Some(implicit_bgl);
self.implicit_pipeline = Some(pipeline);
}
pub(crate) fn upload_implicit_item(
&self,
device: &wgpu::Device,
item: &GpuImplicitItem,
) -> ImplicitGpuItem {
let blend_mode_u32 = match item.blend_mode {
ImplicitBlendMode::Union => 0u32,
ImplicitBlendMode::SmoothUnion => 1,
ImplicitBlendMode::Intersection => 2,
};
let mut raw = ImplicitUniformRaw {
num_primitives: item.primitives.len().min(16) as u32,
blend_mode: blend_mode_u32,
max_steps: item.march_options.max_steps,
_pad0: 0,
step_scale: item.march_options.step_scale,
hit_threshold: item.march_options.hit_threshold,
max_distance: item.march_options.max_distance,
_pad1: 0.0,
primitives: [ImplicitPrimitive::zeroed(); 16],
};
for (i, prim) in item.primitives.iter().take(16).enumerate() {
raw.primitives[i] = *prim;
}
let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("implicit_uniform_buf"),
contents: bytemuck::bytes_of(&raw),
usage: wgpu::BufferUsages::UNIFORM,
});
let bgl = self
.implicit_bgl
.as_ref()
.expect("ensure_implicit_pipeline not called");
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("implicit_bind_group"),
layout: bgl,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buf.as_entire_binding(),
}],
});
ImplicitGpuItem { uniform_buf, bind_group }
}
}