use crate::deferred::DeferredState;
use crate::pipeline::{load_shader, SceneState};
pub struct SsgiState {
pub ssgi_texture: wgpu::Texture,
pub ssgi_view: wgpu::TextureView,
pub ssgi_blurred_texture: wgpu::Texture,
pub ssgi_blurred_view: wgpu::TextureView,
pub ssgi_pipeline: wgpu::RenderPipeline,
ssgi_bgl: wgpu::BindGroupLayout,
pub ssgi_bind_group: wgpu::BindGroup,
pub blur_pipeline: wgpu::RenderPipeline,
blur_bgl: wgpu::BindGroupLayout,
pub blur_bind_group: wgpu::BindGroup,
pub apply_pipeline: wgpu::RenderPipeline,
apply_bgl: wgpu::BindGroupLayout,
pub apply_bind_group: wgpu::BindGroup,
_nearest_sampler: wgpu::Sampler,
linear_sampler: wgpu::Sampler,
pub width: u32,
pub height: u32,
}
impl SsgiState {
pub fn new(
device: &wgpu::Device,
scene: &SceneState,
deferred: &DeferredState,
hdr_view: &wgpu::TextureView,
width: u32,
height: u32,
) -> Self {
let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
let linear_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
..Default::default()
});
let half_w = (width / 2).max(1);
let half_h = (height / 2).max(1);
let (ssgi_texture, ssgi_view) = Self::mk_tex(device, half_w, half_h, "ssgi_texture");
let (ssgi_blurred_texture, ssgi_blurred_view) =
Self::mk_tex(device, half_w, half_h, "ssgi_blurred_texture");
let ssgi_bgl = Self::mk_ssgi_bgl(device);
let blur_bgl = Self::mk_blur_bgl(device);
let apply_bgl = Self::mk_apply_bgl(device);
let ssgi_bind_group = Self::mk_ssgi_bg(
device,
&ssgi_bgl,
hdr_view,
&deferred.normal_roughness_view,
&deferred.world_position_view,
&linear_sampler,
);
let blur_bind_group = Self::mk_blur_bg(device, &blur_bgl, &ssgi_view, &linear_sampler);
let apply_bind_group =
Self::mk_apply_bg(device, &apply_bgl, &ssgi_blurred_view, &linear_sampler);
let ssgi_pipeline = Self::mk_ssgi_pipeline(device, scene, &ssgi_bgl);
let blur_pipeline = Self::mk_blur_pipeline(device, &blur_bgl);
let apply_pipeline = Self::mk_apply_pipeline(device, &apply_bgl);
Self {
ssgi_texture,
ssgi_view,
ssgi_blurred_texture,
ssgi_blurred_view,
ssgi_pipeline,
ssgi_bgl,
ssgi_bind_group,
blur_pipeline,
blur_bgl,
blur_bind_group,
apply_pipeline,
apply_bgl,
apply_bind_group,
_nearest_sampler: nearest_sampler,
linear_sampler,
width,
height,
}
}
pub fn resize(
&mut self,
device: &wgpu::Device,
deferred: &DeferredState,
hdr_view: &wgpu::TextureView,
width: u32,
height: u32,
) {
let half_w = (width / 2).max(1);
let half_h = (height / 2).max(1);
let (t1, v1) = Self::mk_tex(device, half_w, half_h, "ssgi_texture");
let (t2, v2) = Self::mk_tex(device, half_w, half_h, "ssgi_blurred_texture");
self.ssgi_bind_group = Self::mk_ssgi_bg(
device,
&self.ssgi_bgl,
hdr_view,
&deferred.normal_roughness_view,
&deferred.world_position_view,
&self.linear_sampler,
);
self.blur_bind_group = Self::mk_blur_bg(device, &self.blur_bgl, &v1, &self.linear_sampler);
self.apply_bind_group =
Self::mk_apply_bg(device, &self.apply_bgl, &v2, &self.linear_sampler);
self.ssgi_texture = t1;
self.ssgi_view = v1;
self.ssgi_blurred_texture = t2;
self.ssgi_blurred_view = v2;
self.width = width;
self.height = height;
}
fn mk_tex(
device: &wgpu::Device,
w: u32,
h: u32,
label: &str,
) -> (wgpu::Texture, wgpu::TextureView) {
let t = device.create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d {
width: w,
height: h,
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 v = t.create_view(&wgpu::TextureViewDescriptor::default());
(t, v)
}
fn mk_ssgi_bgl(device: &wgpu::Device) -> wgpu::BindGroupLayout {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("ssgi_bgl"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: false },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: false },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
})
}
fn mk_blur_bgl(device: &wgpu::Device) -> wgpu::BindGroupLayout {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("ssgi_blur_bgl"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
})
}
fn mk_apply_bgl(device: &wgpu::Device) -> wgpu::BindGroupLayout {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("ssgi_apply_bgl"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
})
}
fn mk_ssgi_bg(
device: &wgpu::Device,
layout: &wgpu::BindGroupLayout,
hdr: &wgpu::TextureView,
nrm: &wgpu::TextureView,
pos: &wgpu::TextureView,
samp: &wgpu::Sampler,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("ssgi_bg"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(hdr),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(nrm),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(pos),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Sampler(samp),
},
],
})
}
fn mk_blur_bg(
device: &wgpu::Device,
layout: &wgpu::BindGroupLayout,
ssgi_raw: &wgpu::TextureView,
samp: &wgpu::Sampler,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("ssgi_blur_bg"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(ssgi_raw),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(samp),
},
],
})
}
fn mk_apply_bg(
device: &wgpu::Device,
layout: &wgpu::BindGroupLayout,
ssgi_blurred: &wgpu::TextureView,
samp: &wgpu::Sampler,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("ssgi_apply_bg"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(ssgi_blurred),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(samp),
},
],
})
}
fn mk_ssgi_pipeline(
device: &wgpu::Device,
scene: &SceneState,
bgl: &wgpu::BindGroupLayout,
) -> wgpu::RenderPipeline {
let shader = load_shader(
device,
"demo/assets/shaders/ssgi.wgsl",
include_str!("shaders/ssgi.wgsl"),
"SSGI Shader",
);
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("ssgi_layout"),
bind_group_layouts: &[&scene.global_bind_group_layout, bgl],
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("ssgi_pipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
compilation_options: Default::default(),
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
cull_mode: None,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
})
}
fn mk_blur_pipeline(
device: &wgpu::Device,
bgl: &wgpu::BindGroupLayout,
) -> wgpu::RenderPipeline {
let shader = load_shader(
device,
"demo/assets/shaders/ssgi_blur.wgsl",
include_str!("shaders/ssgi_blur.wgsl"),
"SSGI Blur Shader",
);
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("ssgi_blur_layout"),
bind_group_layouts: &[bgl],
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("ssgi_blur_pipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
compilation_options: Default::default(),
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
cull_mode: None,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
})
}
fn mk_apply_pipeline(
device: &wgpu::Device,
bgl: &wgpu::BindGroupLayout,
) -> wgpu::RenderPipeline {
let shader = load_shader(
device,
"demo/assets/shaders/ssgi_apply.wgsl",
include_str!("shaders/ssgi_apply.wgsl"),
"SSGI Apply Shader",
);
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("ssgi_apply_layout"),
bind_group_layouts: &[bgl],
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("ssgi_apply_pipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
compilation_options: Default::default(),
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
compilation_options: Default::default(),
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::REPLACE,
}),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
cull_mode: None,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
})
}
}