use crate::renderer::GpuImplicitItem;
use crate::resources::DeviceResources;
use wgpu::util::DeviceExt as _;
#[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 colour: [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,
}
}
}
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 unlit: u32,
pub step_scale: f32,
pub hit_threshold: f32,
pub max_distance: f32,
pub opacity: f32,
pub primitives: [ImplicitPrimitive; 16],
}
pub(crate) struct ImplicitGpuItem {
pub _uniform_buf: wgpu::Buffer,
pub bind_group: wgpu::BindGroup,
}
impl DeviceResources {
pub(crate) fn ensure_implicit_pipeline(&mut self, device: &wgpu::Device) {
if self.implicit.pipeline.is_some() {
return;
}
let shader = crate::resources::builders::wgsl_module(
device,
"implicit_shader",
crate::resources::builders::wgsl_source!("implicit"),
);
let implicit_bgl = crate::resources::builders::uniform_bgl(
device,
"implicit_bgl",
wgpu::ShaderStages::FRAGMENT,
);
let layout = crate::resources::builders::standard_scene_layout(
device,
"implicit_pipeline_layout",
&self.camera_bind_group_layout,
&implicit_bgl,
);
self.implicit.bgl = Some(implicit_bgl);
self.implicit.pipeline = Some(crate::resources::builders::build_dual_pipeline(
device,
&crate::resources::builders::DualPipelineDesc {
label: "implicit_pipeline",
layout: &layout,
shader: &shader,
vertex_entry: "vs_main",
fragment_entry: "fs_main",
vertex_buffers: &[],
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
topology: wgpu::PrimitiveTopology::TriangleList,
cull_mode: None,
depth_write: true,
depth_compare: wgpu::CompareFunction::LessEqual,
sample_count: 1,
ldr_format: self.target_format,
},
));
}
pub(crate) fn ensure_implicit_outline_mask_pipeline(&mut self, device: &wgpu::Device) {
if self.implicit.outline_mask_pipeline.is_some() {
return;
}
let implicit_bgl = self.implicit.bgl.as_ref().expect(
"ensure_implicit_pipeline must be called before ensure_implicit_outline_mask_pipeline",
);
let shader = crate::resources::builders::wgsl_module(
device,
"implicit_outline_mask_shader",
crate::resources::builders::wgsl_source!("implicit_outline_mask"),
);
let layout = crate::resources::builders::standard_scene_layout(
device,
"implicit_outline_mask_pipeline_layout",
&self.camera_bind_group_layout,
implicit_bgl,
);
self.implicit.outline_mask_pipeline =
Some(crate::resources::builders::build_outline_mask_pipeline(
device,
"implicit_outline_mask_pipeline",
&layout,
&shader,
wgpu::TextureFormat::R8Unorm,
&[],
None,
true,
wgpu::CompareFunction::Less,
));
}
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,
unlit: item.settings.unlit as u32,
step_scale: item.march_options.step_scale,
hit_threshold: item.march_options.hit_threshold,
max_distance: item.march_options.max_distance,
opacity: item.settings.opacity,
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: uniform_buf,
bind_group,
}
}
}