use crate::wgpu::passes::geometry::meshlet::scene::MeshletScene;
use crate::wgpu::passes::geometry::meshlet::types::{
MESHLET_MAX_TRIANGLES, MESHLET_TRIANGLE_ID_BITS, MeshletResolveViewUniform, MeshletViewUniform,
};
use crate::wgpu::render_configs::RenderInputs;
use crate::wgpu::rendergraph::{PassExecutionContext, PassNode};
fn storage_buffer_entry(
binding: u32,
visibility: wgpu::ShaderStages,
) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding,
visibility,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
}
fn uniform_buffer_entry(
binding: u32,
visibility: wgpu::ShaderStages,
) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding,
visibility,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
}
pub struct MeshletPass {
scene: MeshletScene,
visibility_view: Option<wgpu::TextureView>,
visibility_size: (u32, u32),
streams_bind_group_layout: wgpu::BindGroupLayout,
streams_bind_group: Option<wgpu::BindGroup>,
raster_pipeline: wgpu::RenderPipeline,
raster_bind_group_layout: wgpu::BindGroupLayout,
raster_bind_group: Option<wgpu::BindGroup>,
raster_view_buffer: wgpu::Buffer,
software_raster_pipeline: Option<wgpu::ComputePipeline>,
software_raster_bind_group_layout: Option<wgpu::BindGroupLayout>,
software_raster_bind_group: Option<wgpu::BindGroup>,
visibility_clear_pipeline: Option<wgpu::ComputePipeline>,
resolve_pipeline: wgpu::RenderPipeline,
resolve_bind_group_layout: wgpu::BindGroupLayout,
resolve_bind_group: Option<wgpu::BindGroup>,
resolve_view_buffer: wgpu::Buffer,
cull_pipeline: wgpu::ComputePipeline,
software_dispatch_pipeline: Option<wgpu::ComputePipeline>,
cull_bind_group_layout: wgpu::BindGroupLayout,
cull_bind_group: Option<wgpu::BindGroup>,
hiz: crate::wgpu::passes::geometry::HizPass,
occluder_from_world: [[f32; 4]; 4],
occluder_screen_size: (f32, f32),
occlusion_ready: bool,
hiz_size: (u32, u32),
material_texture_bind_group_layout: wgpu::BindGroupLayout,
material_texture_bind_group: Option<wgpu::BindGroup>,
material_bindless_max: Option<u32>,
material_layer_map: std::collections::HashMap<
crate::asset_id::TextureId,
crate::wgpu::material_texture_arrays::MaterialTextureLayer,
>,
material_layers_changed: bool,
bound_generation: u64,
}
fn read_write_storage_buffer_entry(
binding: u32,
visibility: wgpu::ShaderStages,
) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding,
visibility,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
}
fn visibility_buffer_format(software_raster_enabled: bool) -> wgpu::TextureFormat {
if software_raster_enabled {
wgpu::TextureFormat::R64Uint
} else {
wgpu::TextureFormat::R32Uint
}
}
fn streams_bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
let stages =
wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::COMPUTE;
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Meshlet Streams Bind Group Layout"),
entries: &[
storage_buffer_entry(0, stages),
storage_buffer_entry(1, stages),
storage_buffer_entry(2, stages),
storage_buffer_entry(3, stages),
storage_buffer_entry(4, stages),
storage_buffer_entry(5, stages),
storage_buffer_entry(6, stages),
],
})
}
fn storage_texture_entry(
binding: u32,
visibility: wgpu::ShaderStages,
access: wgpu::StorageTextureAccess,
) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding,
visibility,
ty: wgpu::BindingType::StorageTexture {
access,
format: wgpu::TextureFormat::R64Uint,
view_dimension: wgpu::TextureViewDimension::D2,
},
count: None,
}
}
impl MeshletPass {
pub fn new(
device: &wgpu::Device,
color_format: wgpu::TextureFormat,
depth_format: wgpu::TextureFormat,
software_raster_enabled: bool,
material_bindless_max: Option<u32>,
) -> Self {
let material_texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Meshlet Material Texture Bind Group Layout"),
entries: &crate::wgpu::passes::geometry::material_gpu::material_texture_bind_group_layout_entries(
material_bindless_max,
),
});
let shared_defs = super::meshlet_shader_defs();
let cull_shader = crate::wgpu::shader_compose::compile_wgsl_with_defs(
device,
"meshlet_cull.wgsl",
include_str!("../../../shaders/meshlet_cull.wgsl"),
&shared_defs,
);
let cull_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Meshlet Cull Bind Group Layout"),
entries: &[
storage_buffer_entry(0, wgpu::ShaderStages::COMPUTE),
storage_buffer_entry(1, wgpu::ShaderStages::COMPUTE),
storage_buffer_entry(2, wgpu::ShaderStages::COMPUTE),
read_write_storage_buffer_entry(3, wgpu::ShaderStages::COMPUTE),
read_write_storage_buffer_entry(4, wgpu::ShaderStages::COMPUTE),
uniform_buffer_entry(5, wgpu::ShaderStages::COMPUTE),
read_write_storage_buffer_entry(6, wgpu::ShaderStages::COMPUTE),
wgpu::BindGroupLayoutEntry {
binding: 7,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: false },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
],
});
let cull_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Meshlet Cull Pipeline Layout"),
bind_group_layouts: &[Some(&cull_bind_group_layout)],
immediate_size: 0,
});
let cull_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("Meshlet Cull Pipeline"),
layout: Some(&cull_pipeline_layout),
module: &cull_shader,
entry_point: Some("cull_main"),
compilation_options: Default::default(),
cache: None,
});
let software_dispatch_pipeline = software_raster_enabled.then(|| {
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("Meshlet Software Dispatch Pipeline"),
layout: Some(&cull_pipeline_layout),
module: &cull_shader,
entry_point: Some("software_dispatch_main"),
compilation_options: Default::default(),
cache: None,
})
});
let mut visibility_defs = shared_defs.to_vec();
if software_raster_enabled {
visibility_defs.push((
"MESHLET_ATOMIC_VISIBILITY",
naga_oil::compose::ShaderDefValue::Bool(true),
));
}
let raster_shader = crate::wgpu::shader_compose::compile_wgsl_with_defs(
device,
"meshlet_visibility_buffer.wgsl",
include_str!("../../../shaders/meshlet_visibility_buffer.wgsl"),
&visibility_defs,
);
let streams_bind_group_layout = streams_bind_group_layout(device);
let mut raster_entries = vec![uniform_buffer_entry(0, wgpu::ShaderStages::VERTEX)];
if software_raster_enabled {
raster_entries.push(storage_texture_entry(
1,
wgpu::ShaderStages::FRAGMENT,
wgpu::StorageTextureAccess::Atomic,
));
}
let raster_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Meshlet Raster Bind Group Layout"),
entries: &raster_entries,
});
let raster_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Meshlet Raster Pipeline Layout"),
bind_group_layouts: &[
Some(&raster_bind_group_layout),
Some(&streams_bind_group_layout),
],
immediate_size: 0,
});
let raster_color_targets = [Some(wgpu::ColorTargetState {
format: visibility_buffer_format(software_raster_enabled),
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})];
let raster_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Meshlet Raster Pipeline"),
layout: Some(&raster_pipeline_layout),
vertex: wgpu::VertexState {
module: &raster_shader,
entry_point: Some("vertex_main"),
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &raster_shader,
entry_point: Some("fragment_main"),
targets: if software_raster_enabled {
&[]
} else {
&raster_color_targets
},
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: depth_format,
depth_write_enabled: Some(!software_raster_enabled),
depth_compare: Some(wgpu::CompareFunction::GreaterEqual),
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
let software_raster_shader = software_raster_enabled.then(|| {
crate::wgpu::shader_compose::compile_wgsl_with_defs(
device,
"meshlet_software_raster.wgsl",
include_str!("../../../shaders/meshlet_software_raster.wgsl"),
&shared_defs,
)
});
let software_raster_bind_group_layout = software_raster_shader.as_ref().map(|_| {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Meshlet Software Raster Bind Group Layout"),
entries: &[
uniform_buffer_entry(0, wgpu::ShaderStages::COMPUTE),
storage_texture_entry(
1,
wgpu::ShaderStages::COMPUTE,
wgpu::StorageTextureAccess::Atomic,
),
storage_buffer_entry(2, wgpu::ShaderStages::COMPUTE),
],
})
});
let software_raster_pipeline_layout =
software_raster_bind_group_layout.as_ref().map(|layout| {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Meshlet Software Raster Pipeline Layout"),
bind_group_layouts: &[Some(layout), Some(&streams_bind_group_layout)],
immediate_size: 0,
})
});
let software_raster_pipeline = software_raster_shader.as_ref().map(|module| {
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("Meshlet Software Raster Pipeline"),
layout: software_raster_pipeline_layout.as_ref(),
module,
entry_point: Some("software_raster_main"),
compilation_options: Default::default(),
cache: None,
})
});
let visibility_clear_pipeline_layout =
software_raster_bind_group_layout.as_ref().map(|layout| {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Meshlet Visibility Clear Pipeline Layout"),
bind_group_layouts: &[Some(layout)],
immediate_size: 0,
})
});
let visibility_clear_pipeline = software_raster_shader.as_ref().map(|module| {
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("Meshlet Visibility Clear Pipeline"),
layout: visibility_clear_pipeline_layout.as_ref(),
module,
entry_point: Some("clear_main"),
compilation_options: Default::default(),
cache: None,
})
});
let mut resolve_shader_defs = visibility_defs.clone();
if material_bindless_max.is_some() {
resolve_shader_defs.push(("BINDLESS", naga_oil::compose::ShaderDefValue::Bool(true)));
}
let resolve_shader = crate::wgpu::shader_compose::compile_wgsl_with_defs(
device,
"meshlet_resolve.wgsl",
include_str!("../../../shaders/meshlet_resolve.wgsl"),
&resolve_shader_defs,
);
let resolve_visibility_entry = if software_raster_enabled {
storage_texture_entry(
0,
wgpu::ShaderStages::FRAGMENT,
wgpu::StorageTextureAccess::ReadOnly,
)
} else {
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Uint,
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
}
};
let resolve_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Meshlet Resolve Bind Group Layout"),
entries: &[
resolve_visibility_entry,
uniform_buffer_entry(1, wgpu::ShaderStages::FRAGMENT),
storage_buffer_entry(2, wgpu::ShaderStages::FRAGMENT),
],
});
let resolve_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Meshlet Resolve Pipeline Layout"),
bind_group_layouts: &[
Some(&resolve_bind_group_layout),
Some(&streams_bind_group_layout),
Some(&material_texture_bind_group_layout),
],
immediate_size: 0,
});
let resolve_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Meshlet Resolve Pipeline"),
layout: Some(&resolve_pipeline_layout),
vertex: wgpu::VertexState {
module: &resolve_shader,
entry_point: Some("vertex_main"),
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &resolve_shader,
entry_point: Some("fragment_main"),
targets: &[Some(wgpu::ColorTargetState {
format: color_format,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: software_raster_enabled.then(|| wgpu::DepthStencilState {
format: depth_format,
depth_write_enabled: Some(true),
depth_compare: Some(wgpu::CompareFunction::GreaterEqual),
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
let raster_view_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Meshlet Raster View Buffer"),
size: std::mem::size_of::<MeshletViewUniform>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let resolve_view_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Meshlet Resolve View Buffer"),
size: std::mem::size_of::<MeshletResolveViewUniform>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
Self {
scene: MeshletScene::new(device, software_raster_enabled),
visibility_view: None,
visibility_size: (0, 0),
streams_bind_group_layout,
streams_bind_group: None,
raster_pipeline,
raster_bind_group_layout,
raster_bind_group: None,
raster_view_buffer,
software_raster_pipeline,
software_raster_bind_group_layout,
software_raster_bind_group: None,
visibility_clear_pipeline,
resolve_pipeline,
resolve_bind_group_layout,
resolve_bind_group: None,
resolve_view_buffer,
cull_pipeline,
software_dispatch_pipeline,
cull_bind_group_layout,
cull_bind_group: None,
hiz: crate::wgpu::passes::geometry::HizPass::new(device),
occluder_from_world: [[0.0; 4]; 4],
occluder_screen_size: (0.0, 0.0),
occlusion_ready: false,
hiz_size: (0, 0),
material_texture_bind_group_layout,
material_texture_bind_group: None,
material_bindless_max,
material_layer_map: std::collections::HashMap::new(),
material_layers_changed: false,
bound_generation: u64::MAX,
}
}
pub fn add_material_layer_mapping(
&mut self,
texture: crate::asset_id::TextureId,
layer: crate::wgpu::material_texture_arrays::MaterialTextureLayer,
) {
self.material_layer_map.insert(texture, layer);
self.material_layers_changed = true;
}
pub fn apply_material_textures(
&mut self,
device: &wgpu::Device,
arrays: &crate::wgpu::material_texture_arrays::MaterialTextureArrays,
) {
use crate::wgpu::passes::geometry::material_gpu;
if self.material_bindless_max.is_some() && arrays.is_bindless() {
self.material_texture_bind_group = Some(material_gpu::bindless_material_bind_group(
device,
&self.material_texture_bind_group_layout,
&arrays.bindless_view_refs(),
&arrays.samplers(),
"Meshlet Material Bindless Bind Group",
));
return;
}
self.material_texture_bind_group = Some(material_gpu::array_material_bind_group(
device,
&self.material_texture_bind_group_layout,
arrays.srgb_view(),
arrays.linear_view(),
&arrays.samplers(),
"Meshlet Material Texture Bind Group",
));
}
fn resize_visibility_buffer(&mut self, device: &wgpu::Device, size: (u32, u32)) -> bool {
let size = (
self.visibility_size.0.max(size.0.max(1)),
self.visibility_size.1.max(size.1.max(1)),
);
if self.visibility_size == size && self.visibility_view.is_some() {
return false;
}
self.visibility_size = size;
let usage = if self.scene.software_raster_enabled {
wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::STORAGE_ATOMIC
} else {
wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING
};
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Meshlet Visibility Buffer"),
size: wgpu::Extent3d {
width: size.0,
height: size.1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: visibility_buffer_format(self.scene.software_raster_enabled),
usage,
view_formats: &[],
});
self.visibility_view = Some(texture.create_view(&wgpu::TextureViewDescriptor::default()));
true
}
fn build_streams_bind_group(&mut self, device: &wgpu::Device) {
self.streams_bind_group = Some(
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Meshlet Streams Bind Group"),
layout: &self.streams_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: self.scene.clusters_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: self.scene.meshes.meshlets_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: self.scene.meshes.indices_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 3,
resource: self
.scene
.meshes
.vertex_positions_buffer()
.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 4,
resource: self
.scene
.meshes
.vertex_normals_buffer()
.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 5,
resource: self.scene.meshes.vertex_uvs_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 6,
resource: self.scene.instances_buffer().as_entire_binding(),
},
],
}),
);
}
fn build_software_raster_bind_group(&mut self, device: &wgpu::Device) {
let (Some(layout), Some(visibility)) = (
self.software_raster_bind_group_layout.as_ref(),
self.visibility_view.as_ref(),
) else {
return;
};
self.software_raster_bind_group =
Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Meshlet Software Raster Bind Group"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: self.raster_view_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(visibility),
},
wgpu::BindGroupEntry {
binding: 2,
resource: self.scene.dispatch_args_buffer().as_entire_binding(),
},
],
}));
}
fn build_cull_bind_group(&mut self, device: &wgpu::Device) {
self.cull_bind_group = Some(
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Meshlet Cull Bind Group"),
layout: &self.cull_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: self.scene.instances_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: self.scene.meshes.bvh_nodes_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: self
.scene
.meshes
.meshlet_cull_data_buffer()
.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 3,
resource: self.scene.clusters_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 4,
resource: self.scene.draw_args_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 5,
resource: self.scene.cull_view_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 6,
resource: self.scene.dispatch_args_buffer().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 7,
resource: wgpu::BindingResource::TextureView(self.hiz.hiz_view_or_dummy()),
},
],
}),
);
}
fn build_raster_bind_group(&mut self, device: &wgpu::Device) {
let mut entries = vec![wgpu::BindGroupEntry {
binding: 0,
resource: self.raster_view_buffer.as_entire_binding(),
}];
if let Some(visibility) = self
.visibility_view
.as_ref()
.filter(|_| self.scene.software_raster_enabled)
{
entries.push(wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(visibility),
});
}
self.raster_bind_group = Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Meshlet Raster Bind Group"),
layout: &self.raster_bind_group_layout,
entries: &entries,
}));
}
fn build_resolve_bind_group(&mut self, device: &wgpu::Device, visibility: &wgpu::TextureView) {
self.resolve_bind_group = Some(device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Meshlet Resolve Bind Group"),
layout: &self.resolve_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(visibility),
},
wgpu::BindGroupEntry {
binding: 1,
resource: self.resolve_view_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: self.scene.materials_buffer().as_entire_binding(),
},
],
}));
}
}
impl PassNode<RenderInputs> for MeshletPass {
fn name(&self) -> &str {
"meshlet_pass"
}
fn reads(&self) -> Vec<&str> {
vec![]
}
fn writes(&self) -> Vec<&str> {
vec![]
}
fn reads_writes(&self) -> Vec<&str> {
vec!["color", "depth"]
}
fn invalidate_bind_groups(&mut self) {
self.raster_bind_group = None;
self.resolve_bind_group = None;
self.software_raster_bind_group = None;
self.streams_bind_group = None;
}
fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, configs: &RenderInputs) {
let Some(render_view) = configs.scene.render_view.as_ref() else {
return;
};
let materials = crate::wgpu::passes::geometry::meshlet::scene::MeshletMaterialInputs {
render_materials: &configs.scene.render_materials,
layer_map: &self.material_layer_map,
layers_changed: std::mem::take(&mut self.material_layers_changed),
};
let cull_view = configs
.scene
.frozen_cull_view
.as_ref()
.unwrap_or(render_view);
let occlusion = crate::wgpu::passes::geometry::meshlet::scene::MeshletOcclusionInputs {
occluder_from_world: self.occluder_from_world,
screen_size: self.occluder_screen_size,
mip_count: self.hiz.mip_count(),
enabled: self.occlusion_ready && configs.debug_draw.meshlet_occlusion_culling,
};
self.scene.sync(
device,
queue,
&crate::wgpu::passes::geometry::meshlet::scene::MeshletInstanceInputs {
scene_world: &configs.scene_world,
assets: &configs.scene.meshlet_assets,
generation: configs.scene.meshlet_placements_generation,
},
&materials,
&crate::wgpu::passes::geometry::meshlet::scene::MeshletCullInputs {
view: cull_view,
lod_error_threshold: configs.debug_draw.meshlet_lod_error_threshold,
occlusion: &occlusion,
},
);
if self.scene.instance_count == 0 {
return;
}
if self.resize_visibility_buffer(device, render_view.screen_size)
|| self.bound_generation != self.scene.generation
{
self.bound_generation = self.scene.generation;
self.raster_bind_group = None;
self.resolve_bind_group = None;
self.cull_bind_group = None;
self.software_raster_bind_group = None;
self.streams_bind_group = None;
}
if self.cull_bind_group.is_none() {
self.build_cull_bind_group(device);
}
if self.streams_bind_group.is_none() {
self.build_streams_bind_group(device);
}
if self.software_raster_bind_group.is_none() {
self.build_software_raster_bind_group(device);
}
let clip_from_world: [[f32; 4]; 4] = render_view.view_projection.into();
let camera_position = [
render_view.camera_position.x,
render_view.camera_position.y,
render_view.camera_position.z,
1.0,
];
let screen_size = [
render_view.screen_size.0.max(1) as f32,
render_view.screen_size.1.max(1) as f32,
0.0,
0.0,
];
queue.write_buffer(
&self.raster_view_buffer,
0,
bytemuck::bytes_of(&MeshletViewUniform {
clip_from_world,
camera_position,
screen_size,
counts: [self.scene.cluster_capacity, 0, 0, 0],
}),
);
let (fog_color, fog_params) = match configs.scene.active_view.fog.as_ref() {
Some(fog) => (
[
fog.color[0],
fog.color[1],
fog.color[2],
match fog.mode {
crate::config::FogMode::Linear => 1.0,
crate::config::FogMode::Exponential => 2.0,
crate::config::FogMode::ExponentialSquared => 3.0,
},
],
[fog.start, fog.end, 0.0, 0.0],
),
None => ([0.0; 4], [0.0; 4]),
};
let (sun_direction, sun_color) = configs
.scene
.render_lighting
.as_ref()
.map(|lighting| (lighting.sun_direction, lighting.sun_color))
.unwrap_or((
nalgebra_glm::vec3(0.0, 1.0, 0.0),
nalgebra_glm::vec3(1.0, 1.0, 1.0),
));
queue.write_buffer(
&self.resolve_view_buffer,
0,
bytemuck::bytes_of(&MeshletResolveViewUniform {
clip_from_world,
camera_position,
sun_direction: [sun_direction.x, sun_direction.y, sun_direction.z, 0.0],
sun_color: [sun_color.x, sun_color.y, sun_color.z, 1.0],
screen_size: [
render_view.screen_size.0.max(1) as f32,
render_view.screen_size.1.max(1) as f32,
0.0,
0.0,
],
visualization: [
if configs.debug_draw.meshlet_cluster_visualization {
1.0
} else {
0.0
},
0.0,
0.0,
0.0,
],
fog_color,
fog_params,
}),
);
}
fn execute<'r, 'e>(
&mut self,
context: PassExecutionContext<'r, 'e, RenderInputs>,
) -> crate::wgpu::rendergraph::Result<Vec<crate::wgpu::rendergraph::SubGraphRunCommand<'r>>>
{
if self.scene.instance_count == 0 || context.configs.scene.render_view.is_none() {
return Ok(context.into_sub_graph_commands());
}
if let (Some(pipeline), Some(bind_group)) = (
self.visibility_clear_pipeline.as_ref(),
self.software_raster_bind_group.as_ref(),
) {
let mut clear_pass = context
.encoder
.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: Some("Meshlet Visibility Clear Pass"),
timestamp_writes: None,
});
clear_pass.set_pipeline(pipeline);
clear_pass.set_bind_group(0, bind_group, &[]);
let (width, height) = context
.configs
.scene
.render_view
.as_ref()
.map(|view| view.screen_size)
.unwrap_or(self.visibility_size);
clear_pass.dispatch_workgroups(width.max(1).div_ceil(8), height.max(1).div_ceil(8), 1);
}
if let Some(bind_group) = self.cull_bind_group.as_ref() {
let mut cull_pass = context
.encoder
.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: Some("Meshlet Cull Pass"),
timestamp_writes: None,
});
cull_pass.set_bind_group(0, bind_group, &[]);
cull_pass.set_pipeline(&self.cull_pipeline);
cull_pass.dispatch_workgroups(self.scene.instance_count.div_ceil(64), 1, 1);
if let Some(pipeline) = self.software_dispatch_pipeline.as_ref() {
cull_pass.set_pipeline(pipeline);
cull_pass.dispatch_workgroups(1, 1, 1);
}
}
if let (Some(pipeline), Some(bind_group)) = (
self.software_raster_pipeline.as_ref(),
self.software_raster_bind_group.as_ref(),
) {
let mut software_pass =
context
.encoder
.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: Some("Meshlet Software Raster Pass"),
timestamp_writes: None,
});
software_pass.set_pipeline(pipeline);
software_pass.set_bind_group(0, bind_group, &[]);
software_pass.set_bind_group(1, self.streams_bind_group.as_ref(), &[]);
software_pass.dispatch_workgroups_indirect(self.scene.dispatch_args_buffer(), 0);
}
let (depth_view, depth_load, depth_store) = context.get_depth_attachment("depth")?;
if self.raster_bind_group.is_none() {
self.build_raster_bind_group(context.device);
}
if self.resolve_bind_group.is_none() {
let Some(visibility) = self.visibility_view.clone() else {
return Ok(context.into_sub_graph_commands());
};
self.build_resolve_bind_group(context.device, &visibility);
}
{
let visibility_attachment = self.visibility_view.as_ref().map(|view| {
Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: crate::wgpu::passes::geometry::meshlet::MESHLET_VISIBILITY_BUFFER_EMPTY
as f64,
g: 0.0,
b: 0.0,
a: 0.0,
}),
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})
});
let color_attachments: &[Option<wgpu::RenderPassColorAttachment>] = match (
self.scene.software_raster_enabled,
visibility_attachment.as_ref(),
) {
(false, Some(attachment)) => std::slice::from_ref(attachment),
_ => &[],
};
let mut raster_pass = context
.encoder
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Meshlet Visibility Buffer Pass"),
color_attachments,
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: depth_view,
depth_ops: Some(wgpu::Operations {
load: depth_load,
store: if self.scene.software_raster_enabled {
wgpu::StoreOp::Store
} else {
depth_store
},
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
if let Some(bind_group) = self.raster_bind_group.as_ref() {
raster_pass.set_pipeline(&self.raster_pipeline);
raster_pass.set_bind_group(0, bind_group, &[]);
raster_pass.set_bind_group(1, self.streams_bind_group.as_ref(), &[]);
raster_pass.draw_indirect(self.scene.draw_args_buffer(), 0);
}
}
let (color_view, color_load, color_store) = context.get_color_attachment("color")?;
{
let mut resolve_pass = context
.encoder
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Meshlet Resolve Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: color_view,
resolve_target: None,
ops: wgpu::Operations {
load: color_load,
store: color_store,
},
depth_slice: None,
})],
depth_stencil_attachment: self.scene.software_raster_enabled.then_some(
wgpu::RenderPassDepthStencilAttachment {
view: depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
},
),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
if let Some(bind_group) = self.resolve_bind_group.as_ref() {
resolve_pass.set_pipeline(&self.resolve_pipeline);
resolve_pass.set_bind_group(0, bind_group, &[]);
resolve_pass.set_bind_group(1, self.streams_bind_group.as_ref(), &[]);
if let Some(material_textures) = self.material_texture_bind_group.as_ref() {
resolve_pass.set_bind_group(2, material_textures, &[]);
}
resolve_pass.draw(0..3, 0..1);
}
}
if context.configs.scene.frozen_cull_view.is_some() {
return Ok(context.into_sub_graph_commands());
}
if let Some(render_view) = context.configs.scene.render_view.as_ref() {
let (width, height) = (
render_view.screen_size.0.max(1),
render_view.screen_size.1.max(1),
);
if self.hiz_size != (width, height) {
self.hiz_size = (width, height);
self.hiz.resize(context.device, width, height);
self.cull_bind_group = None;
}
self.hiz.invalidate_bind_groups();
self.hiz.rebuild_bind_groups(context.device, depth_view);
self.hiz.execute(context.encoder);
self.occluder_from_world = render_view.view_projection.into();
self.occluder_screen_size = (width as f32, height as f32);
self.occlusion_ready = true;
}
Ok(context.into_sub_graph_commands())
}
}
const _: () = assert!(MESHLET_MAX_TRIANGLES == 1 << MESHLET_TRIANGLE_ID_BITS);