use std::sync::Arc;
use anyhow::Result;
pub const CAMERA_UNIFORM_SIZE: u64 = 224;
pub const MODEL_UNIFORM_SIZE: u64 = 128;
pub const MATERIAL_UNIFORM_SIZE: u64 = 48;
pub const LIGHT_ARRAY_UNIFORM_SIZE: u64 = 304;
pub const MORPH_PARAMS_UNIFORM_SIZE: u64 = 272;
pub const TONEMAP_PARAMS_SIZE: u64 = 16;
pub struct CameraBindGroupLayout(pub wgpu::BindGroupLayout);
impl CameraBindGroupLayout {
pub fn create(device: &wgpu::Device) -> Self {
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("camera_bgl"),
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: wgpu::BufferSize::new(CAMERA_UNIFORM_SIZE),
},
count: None,
}],
});
Self(layout)
}
pub fn bind(&self, device: &wgpu::Device, buffer: &wgpu::Buffer) -> Result<wgpu::BindGroup> {
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("camera_bg"),
layout: &self.0,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
});
Ok(bg)
}
}
pub struct ModelBindGroupLayout(pub wgpu::BindGroupLayout);
impl ModelBindGroupLayout {
pub fn create(device: &wgpu::Device) -> Self {
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("model_bgl"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(MODEL_UNIFORM_SIZE),
},
count: None,
}],
});
Self(layout)
}
pub fn bind(&self, device: &wgpu::Device, buffer: &wgpu::Buffer) -> Result<wgpu::BindGroup> {
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("model_bg"),
layout: &self.0,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
});
Ok(bg)
}
}
pub struct MaterialBindGroupResources<'a> {
pub material_buffer: &'a wgpu::Buffer,
pub albedo_texture: &'a wgpu::TextureView,
pub albedo_sampler: &'a wgpu::Sampler,
pub normal_texture: &'a wgpu::TextureView,
pub normal_sampler: &'a wgpu::Sampler,
pub metallic_roughness_tex: &'a wgpu::TextureView,
pub metallic_roughness_smp: &'a wgpu::Sampler,
}
pub struct MaterialBindGroupLayout(pub wgpu::BindGroupLayout);
impl MaterialBindGroupLayout {
pub fn create(device: &wgpu::Device) -> Self {
let entries = &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(MATERIAL_UNIFORM_SIZE),
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
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: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
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: 4,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
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,
},
];
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("material_bgl"),
entries,
});
Self(layout)
}
pub fn bind(
&self,
device: &wgpu::Device,
res: &MaterialBindGroupResources<'_>,
) -> Result<wgpu::BindGroup> {
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("material_bg"),
layout: &self.0,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: res.material_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(res.albedo_texture),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::Sampler(res.albedo_sampler),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::TextureView(res.normal_texture),
},
wgpu::BindGroupEntry {
binding: 4,
resource: wgpu::BindingResource::Sampler(res.normal_sampler),
},
wgpu::BindGroupEntry {
binding: 5,
resource: wgpu::BindingResource::TextureView(res.metallic_roughness_tex),
},
wgpu::BindGroupEntry {
binding: 6,
resource: wgpu::BindingResource::Sampler(res.metallic_roughness_smp),
},
],
});
Ok(bg)
}
}
pub struct LightsBindGroupLayout(pub wgpu::BindGroupLayout);
impl LightsBindGroupLayout {
pub fn create(device: &wgpu::Device) -> Self {
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("lights_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: wgpu::BufferSize::new(LIGHT_ARRAY_UNIFORM_SIZE),
},
count: None,
}],
});
Self(layout)
}
pub fn bind(&self, device: &wgpu::Device, buffer: &wgpu::Buffer) -> Result<wgpu::BindGroup> {
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("lights_bg"),
layout: &self.0,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
});
Ok(bg)
}
}
pub struct MorphComputeResources<'a> {
pub in_positions: &'a wgpu::Buffer,
pub morph_deltas: &'a wgpu::Buffer,
pub out_positions: &'a wgpu::Buffer,
pub params: &'a wgpu::Buffer,
}
pub struct MorphComputeBindGroupLayout(pub wgpu::BindGroupLayout);
impl MorphComputeBindGroupLayout {
pub fn create(device: &wgpu::Device) -> Self {
let 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: false },
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::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(MORPH_PARAMS_UNIFORM_SIZE),
},
count: None,
},
];
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("morph_compute_bgl"),
entries,
});
Self(layout)
}
pub fn bind(
&self,
device: &wgpu::Device,
res: &MorphComputeResources<'_>,
) -> Result<wgpu::BindGroup> {
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("morph_compute_bg"),
layout: &self.0,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: res.in_positions.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: res.morph_deltas.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: res.out_positions.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 3,
resource: res.params.as_entire_binding(),
},
],
});
Ok(bg)
}
}
pub struct FullscreenBindGroupResources<'a> {
pub hdr_texture: &'a wgpu::TextureView,
pub hdr_sampler: &'a wgpu::Sampler,
pub tonemap_params: &'a wgpu::Buffer,
}
pub struct FullscreenBindGroupLayout(pub wgpu::BindGroupLayout);
impl FullscreenBindGroupLayout {
pub fn create(device: &wgpu::Device) -> Self {
let 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::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(TONEMAP_PARAMS_SIZE),
},
count: None,
},
];
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("fullscreen_bgl"),
entries,
});
Self(layout)
}
pub fn bind(
&self,
device: &wgpu::Device,
res: &FullscreenBindGroupResources<'_>,
) -> Result<wgpu::BindGroup> {
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("fullscreen_bg"),
layout: &self.0,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(res.hdr_texture),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(res.hdr_sampler),
},
wgpu::BindGroupEntry {
binding: 2,
resource: res.tonemap_params.as_entire_binding(),
},
],
});
Ok(bg)
}
}
pub struct BindGroupLayouts {
pub camera: Arc<wgpu::BindGroupLayout>,
pub model: Arc<wgpu::BindGroupLayout>,
pub material: Arc<wgpu::BindGroupLayout>,
pub lights: Arc<wgpu::BindGroupLayout>,
pub morph_compute: Arc<wgpu::BindGroupLayout>,
pub fullscreen: Arc<wgpu::BindGroupLayout>,
}
impl BindGroupLayouts {
pub fn create(device: &wgpu::Device) -> Self {
Self {
camera: Arc::new(CameraBindGroupLayout::create(device).0),
model: Arc::new(ModelBindGroupLayout::create(device).0),
material: Arc::new(MaterialBindGroupLayout::create(device).0),
lights: Arc::new(LightsBindGroupLayout::create(device).0),
morph_compute: Arc::new(MorphComputeBindGroupLayout::create(device).0),
fullscreen: Arc::new(FullscreenBindGroupLayout::create(device).0),
}
}
}