use crate::ecs::mesh::components::SkinnedVertex;
use crate::ecs::skin::systems::SkinningCache;
use std::collections::{HashMap, HashSet};
use super::super::super::shadow_inputs;
use super::super::types::*;
use super::{SkinnedMeshPass, skinned_texture_bind_group_layout_entries};
impl SkinnedMeshPass {
pub async fn new(
device: &wgpu::Device,
queue: &wgpu::Queue,
color_format: wgpu::TextureFormat,
depth_format: wgpu::TextureFormat,
bindless_config: crate::render::wgpu::material_texture_arrays::BindlessConfig,
) -> Self {
let material_bindless_max = bindless_config
.enabled
.then_some(bindless_config.max_textures);
let material_shader_defs: &[(&str, naga_oil::compose::ShaderDefValue)] =
if bindless_config.enabled {
&[("BINDLESS", naga_oil::compose::ShaderDefValue::Bool(true))]
} else {
&[]
};
let skinning_compute_shader = crate::render::wgpu::shader_compose::compile_wgsl(
device,
"skinning_compute.wgsl",
include_str!("../../../../shaders/skinning_compute.wgsl"),
);
let skinned_mesh_shader = crate::render::wgpu::shader_compose::compile_wgsl_with_defs(
device,
"skinned_mesh.wgsl",
include_str!("../../../../shaders/skinned_mesh.wgsl"),
material_shader_defs,
);
let bone_transforms_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Bone Transforms Buffer"),
size: (std::mem::size_of::<[[f32; 4]; 4]>() * INITIAL_MAX_TOTAL_BONES) as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let inverse_bind_matrices_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Inverse Bind Matrices Buffer"),
size: (std::mem::size_of::<[[f32; 4]; 4]>() * MAX_JOINTS_PER_SKIN * INITIAL_MAX_SKINS)
as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let joint_matrices_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Joint Matrices Buffer"),
size: (std::mem::size_of::<[[f32; 4]; 4]>() * MAX_JOINTS_PER_SKIN * INITIAL_MAX_SKINS)
as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let skin_data_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skin Data Buffer"),
size: (std::mem::size_of::<crate::ecs::skin::systems::GpuSkinData>()
* INITIAL_MAX_SKINS) as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let skinning_compute_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skinning Compute Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let skinning_compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Skinning Compute Bind Group"),
layout: &skinning_compute_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: bone_transforms_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: inverse_bind_matrices_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: skin_data_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 3,
resource: joint_matrices_buffer.as_entire_binding(),
},
],
});
let skinning_compute_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Skinning Compute Pipeline Layout"),
bind_group_layouts: &[Some(&skinning_compute_bind_group_layout)],
immediate_size: 0,
});
let skinning_compute_pipeline =
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("Skinning Compute Pipeline"),
layout: Some(&skinning_compute_pipeline_layout),
module: &skinning_compute_shader,
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
crate::render::wgpu::stagger::yield_to_event_loop(queue).await;
let cull_shader = crate::render::wgpu::shader_compose::compile_wgsl(
device,
"skinned_cull.wgsl",
include_str!("../../../../shaders/skinned_cull.wgsl"),
);
let cull_storage_entry = |binding: u32, read_only: bool| wgpu::BindGroupLayoutEntry {
binding,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
};
let cull_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skinned Mesh Cull Bind Group Layout"),
entries: &[
cull_storage_entry(0, true),
cull_storage_entry(1, false),
cull_storage_entry(2, false),
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let cull_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Skinned Mesh 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("Skinned Mesh Cull Pipeline"),
layout: Some(&cull_pipeline_layout),
module: &cull_shader,
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
crate::render::wgpu::stagger::yield_to_event_loop(queue).await;
let batch_shader = crate::render::wgpu::shader_compose::compile_wgsl(
device,
"skinned_gpu_batch.wgsl",
include_str!("../../../../shaders/skinned_gpu_batch.wgsl"),
);
let batch_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skinned Mesh Batch Bind Group Layout"),
entries: &[
cull_storage_entry(0, true),
cull_storage_entry(1, false),
cull_storage_entry(2, false),
cull_storage_entry(3, false),
cull_storage_entry(4, false),
cull_storage_entry(5, false),
cull_storage_entry(6, true),
wgpu::BindGroupLayoutEntry {
binding: 7,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let batch_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Skinned Mesh Batch Pipeline Layout"),
bind_group_layouts: &[Some(&batch_bind_group_layout)],
immediate_size: 0,
});
let make_batch_pipeline = |label: &str, entry: &str| {
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some(label),
layout: Some(&batch_pipeline_layout),
module: &batch_shader,
entry_point: Some(entry),
compilation_options: Default::default(),
cache: None,
})
};
let batch_clear_pipeline = make_batch_pipeline("Skinned Mesh Batch Clear", "clear");
let batch_count_pipeline = make_batch_pipeline("Skinned Mesh Batch Count", "count");
let batch_build_pipeline = make_batch_pipeline("Skinned Mesh Batch Build", "build");
let batch_assign_pipeline = make_batch_pipeline("Skinned Mesh Batch Assign", "assign");
let batch_params_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skinned Mesh Batch Params Buffer"),
size: (std::mem::size_of::<u32>() * 4) as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let batch_mesh_geo_buffer_size = 64;
let batch_mesh_geo_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skinned Mesh Batch Mesh Geo Buffer"),
size: (std::mem::size_of::<[u32; 4]>() * batch_mesh_geo_buffer_size) as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skinned Mesh Uniform Buffer"),
size: std::mem::size_of::<SkinnedMeshUniforms>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skinned Mesh Uniform Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Skinned Mesh Uniform Bind Group"),
layout: &uniform_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}],
});
let morph_displacement_buffer_size = INITIAL_MORPH_DISPLACEMENT_COUNT;
let morph_displacement_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skinned Mesh Morph Displacement Buffer"),
size: (std::mem::size_of::<MorphDisplacement>() * morph_displacement_buffer_size)
as u64,
usage: wgpu::BufferUsages::STORAGE
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let cluster_bounds_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skinned Mesh Cluster Bounds Buffer"),
size: (std::mem::size_of::<ClusterBounds>() * TOTAL_CLUSTERS as usize) as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let view_matrix_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skinned Mesh View Matrix Buffer"),
size: std::mem::size_of::<[[f32; 4]; 4]>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let instance_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skinned Mesh Instance Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 4,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 5,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 6,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 7,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 8,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 9,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 10,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 11,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skinned Mesh Texture Bind Group Layout"),
entries: &skinned_texture_bind_group_layout_entries(material_bindless_max),
});
let shadow_sampler =
shadow_inputs::shadow_comparison_sampler(device, "Skinned Mesh Shadow Sampler");
let spotlight_shadow_buffer =
shadow_inputs::spotlight_shadow_buffer(device, "Skinned Mesh Spotlight Shadow Buffer");
let shadow_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skinned Mesh Shadow Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Depth,
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Depth,
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 4,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 5,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 6,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 7,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::Cube,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 8,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 9,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::Cube,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 10,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 11,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: false },
view_dimension: wgpu::TextureViewDimension::CubeArray,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 12,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 13,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 14,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::Cube,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 15,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::Cube,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 16,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 17,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
});
let dummy_depth_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Skinned Mesh Dummy Depth Texture"),
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth32Float,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let dummy_depth_view = dummy_depth_texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("Skinned Mesh Dummy Depth View"),
..Default::default()
});
let dummy_brdf_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Skinned Mesh Dummy BRDF LUT"),
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let dummy_brdf_view =
dummy_brdf_texture.create_view(&wgpu::TextureViewDescriptor::default());
let dummy_ibl_cube = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Skinned Mesh Dummy IBL Cube"),
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 6,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let dummy_ibl_cube_view = dummy_ibl_cube.create_view(&wgpu::TextureViewDescriptor {
dimension: Some(wgpu::TextureViewDimension::Cube),
..Default::default()
});
let ibl_sampler_init = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Skinned Mesh IBL Init Sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Linear,
..Default::default()
});
let point_shadow_buffer =
shadow_inputs::point_shadow_buffer(device, "Skinned Mesh Point Light Shadow Buffer");
let point_shadow_sampler =
shadow_inputs::shadow_comparison_sampler(device, "Skinned Mesh Point Shadow Sampler");
let dummy_point_shadow_view = shadow_inputs::dummy_point_shadow_cubemap_view(
device,
"Skinned Mesh Dummy Point Shadow Cubemap Array",
);
let shadow_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Skinned Mesh Shadow Bind Group"),
layout: &shadow_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&dummy_depth_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&shadow_sampler),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(&dummy_depth_view),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Sampler(&shadow_sampler),
},
wgpu::BindGroupEntry {
binding: 4,
resource: spotlight_shadow_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 5,
resource: wgpu::BindingResource::TextureView(&dummy_brdf_view),
},
wgpu::BindGroupEntry {
binding: 6,
resource: wgpu::BindingResource::Sampler(&ibl_sampler_init),
},
wgpu::BindGroupEntry {
binding: 7,
resource: wgpu::BindingResource::TextureView(&dummy_ibl_cube_view),
},
wgpu::BindGroupEntry {
binding: 8,
resource: wgpu::BindingResource::Sampler(&ibl_sampler_init),
},
wgpu::BindGroupEntry {
binding: 9,
resource: wgpu::BindingResource::TextureView(&dummy_ibl_cube_view),
},
wgpu::BindGroupEntry {
binding: 10,
resource: wgpu::BindingResource::Sampler(&ibl_sampler_init),
},
wgpu::BindGroupEntry {
binding: 11,
resource: wgpu::BindingResource::TextureView(&dummy_point_shadow_view),
},
wgpu::BindGroupEntry {
binding: 12,
resource: wgpu::BindingResource::Sampler(&point_shadow_sampler),
},
wgpu::BindGroupEntry {
binding: 13,
resource: point_shadow_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 14,
resource: wgpu::BindingResource::TextureView(&dummy_ibl_cube_view),
},
wgpu::BindGroupEntry {
binding: 15,
resource: wgpu::BindingResource::TextureView(&dummy_ibl_cube_view),
},
wgpu::BindGroupEntry {
binding: 16,
resource: wgpu::BindingResource::TextureView(&dummy_brdf_view),
},
wgpu::BindGroupEntry {
binding: 17,
resource: wgpu::BindingResource::Sampler(&ibl_sampler_init),
},
],
});
let dummy_white_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Skinned Mesh Dummy White Texture"),
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
let dummy_white_view = dummy_white_texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("Skinned Mesh Dummy White View"),
..Default::default()
});
let dummy_cube_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Skinned Mesh Dummy Cube Texture"),
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 6,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
let dummy_cube_view = dummy_cube_texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("Skinned Mesh Dummy Cube View"),
dimension: Some(wgpu::TextureViewDimension::Cube),
..Default::default()
});
let dummy_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Skinned Mesh Dummy Sampler"),
address_mode_u: wgpu::AddressMode::Repeat,
address_mode_v: wgpu::AddressMode::Repeat,
address_mode_w: wgpu::AddressMode::Repeat,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
..Default::default()
});
let ibl_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Skinned Mesh IBL Sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Linear,
..Default::default()
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Skinned Mesh Pipeline Layout"),
bind_group_layouts: &[
Some(&uniform_bind_group_layout),
Some(&instance_bind_group_layout),
Some(&texture_bind_group_layout),
Some(&shadow_bind_group_layout),
],
immediate_size: 0,
});
let skinned_vertex_layout = wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<SkinnedVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: 12,
shader_location: 1,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: 24,
shader_location: 2,
format: wgpu::VertexFormat::Float32x2,
},
wgpu::VertexAttribute {
offset: 32,
shader_location: 3,
format: wgpu::VertexFormat::Float32x2,
},
wgpu::VertexAttribute {
offset: 40,
shader_location: 4,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: 56,
shader_location: 5,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: 72,
shader_location: 6,
format: wgpu::VertexFormat::Uint32x4,
},
wgpu::VertexAttribute {
offset: 88,
shader_location: 7,
format: wgpu::VertexFormat::Float32x4,
},
],
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Skinned Mesh Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &skinned_mesh_shader,
entry_point: Some("vs_main"),
buffers: std::slice::from_ref(&skinned_vertex_layout),
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &skinned_mesh_shader,
entry_point: Some("fs_main"),
targets: &[
Some(wgpu::ColorTargetState {
format: color_format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
}),
Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::R32Float,
blend: None,
write_mask: wgpu::ColorWrites::RED,
}),
],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: Some(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,
});
crate::render::wgpu::stagger::yield_to_event_loop(queue).await;
let skinned_mesh_oit_shader = crate::render::wgpu::shader_compose::compile_wgsl_with_defs(
device,
"skinned_mesh_oit.wgsl",
include_str!("../../../../shaders/skinned_mesh_oit.wgsl"),
material_shader_defs,
);
let oit_render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Skinned Mesh OIT Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &skinned_mesh_oit_shader,
entry_point: Some("vs_main"),
buffers: std::slice::from_ref(&skinned_vertex_layout),
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &skinned_mesh_oit_shader,
entry_point: Some("fs_main"),
targets: &[
Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL,
}),
Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::R16Float,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::Zero,
dst_factor: wgpu::BlendFactor::Src,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::Zero,
dst_factor: wgpu::BlendFactor::Src,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL,
}),
Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::R32Float,
blend: None,
write_mask: wgpu::ColorWrites::RED,
}),
],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: depth_format,
depth_write_enabled: Some(false),
depth_compare: Some(wgpu::CompareFunction::GreaterEqual),
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
crate::render::wgpu::stagger::yield_to_event_loop(queue).await;
let blend_opaque_depth_prepass_pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Skinned Mesh Blend Opaque Depth Prepass Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &skinned_mesh_oit_shader,
entry_point: Some("vs_main"),
buffers: std::slice::from_ref(&skinned_vertex_layout),
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &skinned_mesh_oit_shader,
entry_point: Some("fs_blend_opaque_prepass"),
targets: &[],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: depth_format,
depth_write_enabled: Some(true),
depth_compare: Some(wgpu::CompareFunction::Greater),
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
crate::render::wgpu::stagger::yield_to_event_loop(queue).await;
let oit_composite_shader = crate::render::wgpu::shader_compose::compile_wgsl(
device,
"oit_composite.wgsl",
include_str!("../../../../shaders/oit_composite.wgsl"),
);
let oit_composite_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skinned Mesh OIT Composite Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
});
let oit_composite_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Skinned Mesh OIT Composite Pipeline Layout"),
bind_group_layouts: &[Some(&oit_composite_bind_group_layout)],
immediate_size: 0,
});
let oit_composite_pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Skinned Mesh OIT Composite Pipeline"),
layout: Some(&oit_composite_pipeline_layout),
vertex: wgpu::VertexState {
module: &oit_composite_shader,
entry_point: Some("vs_main"),
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &oit_composite_shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format: color_format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent::OVER,
}),
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,
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
crate::render::wgpu::stagger::yield_to_event_loop(queue).await;
let cluster_compute =
crate::render::wgpu::passes::geometry::cluster::ClusterCompute::new(device);
let cluster_bounds_pipeline = cluster_compute.bounds_pipeline;
let cluster_assign_pipeline = cluster_compute.assign_pipeline;
let cluster_bounds_bind_group_layout = cluster_compute.bounds_bind_group_layout;
let cluster_assign_bind_group_layout = cluster_compute.assign_bind_group_layout;
let oit_accum_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Skinned Mesh OIT Accum Texture"),
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba16Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let oit_accum_view = oit_accum_texture.create_view(&wgpu::TextureViewDescriptor::default());
let oit_reveal_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Skinned Mesh OIT Reveal Texture"),
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::R16Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let oit_reveal_view =
oit_reveal_texture.create_view(&wgpu::TextureViewDescriptor::default());
let oit_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Skinned Mesh OIT Sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
..Default::default()
});
let skinned_vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skinned Vertex Buffer"),
size: (std::mem::size_of::<SkinnedVertex>() * INITIAL_SKINNED_VERTEX_COUNT) as u64,
usage: wgpu::BufferUsages::VERTEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let skinned_index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Skinned Index Buffer"),
size: (std::mem::size_of::<u32>() * INITIAL_SKINNED_INDEX_COUNT) as u64,
usage: wgpu::BufferUsages::INDEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
Self {
skinning_compute_pipeline,
skinning_compute_bind_group,
_skinning_compute_bind_group_layout: skinning_compute_bind_group_layout,
bone_transforms_buffer,
inverse_bind_matrices_buffer,
joint_matrices_buffer,
skin_data_buffer,
cached_bone_transforms: Vec::new(),
animation: super::super::animation::SkinnedAnimationGpu::new(device),
bone_transforms_generation: 0,
bone_transforms_buffer_size: INITIAL_MAX_TOTAL_BONES,
inverse_bind_matrices_buffer_size: MAX_JOINTS_PER_SKIN * INITIAL_MAX_SKINS,
joint_matrices_buffer_size: MAX_JOINTS_PER_SKIN * INITIAL_MAX_SKINS,
skin_data_buffer_size: INITIAL_MAX_SKINS,
render_pipeline,
oit_render_pipeline,
blend_opaque_depth_prepass_pipeline,
oit_composite_pipeline,
oit_composite_bind_group_layout,
cull_pipeline,
cull_bind_group_layout,
batch_clear_pipeline,
batch_count_pipeline,
batch_build_pipeline,
batch_assign_pipeline,
batch_bind_group_layout,
batch_params_buffer,
batch_mesh_geo_buffer,
batch_mesh_geo_buffer_size,
gpu_batching_enabled: false,
uniform_buffer,
uniform_bind_group,
instance_bind_group_layout,
texture_bind_group_layout,
shadow_bind_group,
shadow_bind_group_layout,
shadow_sampler,
spotlight_shadow_buffer,
point_shadow_buffer,
point_shadow_sampler,
point_shadow_cubemap_view: dummy_point_shadow_view,
dummy_white_view,
dummy_cube_view,
dummy_sampler,
brdf_lut_view: None,
irradiance_map_view: None,
prefiltered_env_view: None,
irradiance_b_view: None,
prefiltered_b_view: None,
ibl_sampler,
skinned_vertex_buffer,
skinned_index_buffer,
skinned_vertex_buffer_size: (std::mem::size_of::<SkinnedVertex>()
* INITIAL_SKINNED_VERTEX_COUNT) as u64,
skinned_index_buffer_size: (std::mem::size_of::<u32>() * INITIAL_SKINNED_INDEX_COUNT)
as u64,
skinned_meshes: HashMap::new(),
skinned_mesh_data: Vec::new(),
current_skinned_vertex_offset: 0,
current_skinned_index_offset: 0,
morph_displacement_buffer,
morph_displacement_buffer_size,
current_morph_displacement_offset: 0,
total_joints_to_dispatch: 0,
registered_textures: HashMap::new(),
material_array_srgb_view: None,
material_array_linear_view: None,
material_array_sampler: None,
material_bind_group: None,
material_bindless_max,
material_layer_map: HashMap::new(),
skinning_cache: SkinningCache::default(),
oit_accum_texture,
oit_accum_view,
oit_reveal_texture,
oit_reveal_view,
oit_sampler,
oit_texture_size: (1, 1),
oit_resource_cache: Vec::new(),
cluster_bounds_buffer,
view_matrix_buffer,
cluster_bounds_pipeline,
cluster_assign_pipeline,
cluster_bounds_bind_group_layout,
cluster_assign_bind_group_layout,
current_world_id: 0,
world_states: Vec::new(),
frame_counter: 0,
dirty_skinned_mesh_names: HashSet::new(),
last_mesh_cache_clear_generation: 0,
}
}
}