use std::borrow::Cow;
use crate::low_level::out_bgl;
use wgpu::{
BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, Buffer, BufferAddress, BufferDescriptor, BufferUsages, CommandEncoder, ComputePipelineDescriptor, Device, Limits, PipelineLayoutDescriptor, PushConstantRange, ShaderModuleDescriptor, ShaderStages
};
pub mod internal {
use wesl::include_wesl;
pub const FRAME_END_PROCESSING_SOURCE: &str = include_wesl!("end_of_frame");
}
bitflags::bitflags! {
pub struct BufferType: u16 {
const MARKOV_CHAIN_SCREEN_SPACE = 1 << 0;
#[cfg(feature = "wip-features")]
const SPATIAL_RESAMPLING = 1 << 1;
const MARKOV_CHAIN_WORLD_SPACE = 1 << 2;
}
}
#[repr(C)]
struct MarkovChain {
_light_source: [f32; 3],
_mean_cosine: f32,
_weight_sum: f32,
_num_samples: u32,
_score: f32,
_align: u32,
}
#[repr(C)]
struct AtomicMarkovChain {
_light_source: [f32; 3],
_mean_cosine: f32,
_weight_sum: f32,
_num_samples: u32,
_score: f32,
}
#[repr(C)]
struct WorldMarkovStorage {
_secondary_hash: u32,
_num_samples: u32,
_radiance: [u32; 3],
_lock: u32,
_chain: AtomicMarkovChain,
_timeout: u32,
}
struct TemporalBuffers {
current: Buffer,
old: Buffer,
}
impl TemporalBuffers {
fn new(device: &Device, size: BufferAddress) -> Self {
let current = device.create_buffer(&BufferDescriptor {
label: None,
size,
usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let old = device.create_buffer(&BufferDescriptor {
label: None,
size,
usage: BufferUsages::STORAGE | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
Self { current, old }
}
fn advance_frame(&self, encoder: &mut CommandEncoder) {
encoder.copy_buffer_to_buffer(&self.current, 0, &self.old, 0, self.current.size());
encoder.clear_buffer(&self.current, 0, None);
}
fn advance_frame_no_temporal(&self, encoder: &mut CommandEncoder) {
encoder.clear_buffer(&self.current, 0, None);
encoder.clear_buffer(&self.old, 0, None);
}
}
pub struct DataBuffers {
markov_chain_screen_space: TemporalBuffers,
spatial_resampling: TemporalBuffers,
markov_chain_world_space: TemporalBuffers,
info: Buffer,
#[cfg_attr(not(feature = "wip-features"), expect(dead_code))]
gi_reservoir_processing_pipeline: wgpu::ComputePipeline,
processing_bind_group: wgpu::BindGroup,
limits: Limits,
world_markov_chain_processing_pipeline: wgpu::ComputePipeline,
}
impl DataBuffers {
pub fn new(
device: &Device,
mut width: u32,
mut height: u32,
world_space_buffer_size: u32,
buffers: BufferType,
) -> Self {
width = width.max(1);
height = height.max(1);
#[cfg(feature = "wip-features")]
let spatial_buffers = buffers.contains(BufferType::SPATIAL_RESAMPLING);
#[cfg(not(feature = "wip-features"))]
let spatial_buffers = false;
let size_spatial = if spatial_buffers {
width as BufferAddress * height as BufferAddress
} else {
1
} * size_of::<crate::importance_sampling::Reservoir>() as BufferAddress;
let size_markov = if buffers.contains(BufferType::MARKOV_CHAIN_SCREEN_SPACE) {
width as BufferAddress * height as BufferAddress
} else {
1
} * size_of::<MarkovChain>() as BufferAddress;
let size_markov_world = if buffers.contains(BufferType::MARKOV_CHAIN_WORLD_SPACE) {
world_space_buffer_size.max(1) as BufferAddress
} else {
1
} * size_of::<WorldMarkovStorage>() as BufferAddress;
let info_size = width as BufferAddress
* height as BufferAddress
* size_of::<crate::importance_sampling::Info>() as BufferAddress;
let info = device.create_buffer(&BufferDescriptor {
label: None,
size: info_size,
usage: BufferUsages::STORAGE | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let spatial_buffers = TemporalBuffers::new(device, size_spatial);
let markov_buffers = TemporalBuffers::new(device, size_markov);
let markov_world_buffers = TemporalBuffers::new(device, size_markov_world);
let shader = device.create_shader_module(ShaderModuleDescriptor {
label: Some("(phosph_rs internal) End of frame processing shader"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(internal::FRAME_END_PROCESSING_SOURCE)),
});
let processing_bgl = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("(phosph_rs internal) End of frame processing bind group layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let processing_pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("(phosph_rs internal) End of frame processing pipeline layout"),
bind_group_layouts: &[&processing_bgl],
push_constant_ranges: &[PushConstantRange {
stages: ShaderStages::COMPUTE,
range: 0..4,
}],
});
let gi_reservoir_processing_pipeline =
device.create_compute_pipeline(&ComputePipelineDescriptor {
label: Some("(phosph_rs internal) End of frame gi reservoir processor"),
layout: Some(&processing_pipeline_layout),
module: &shader,
entry_point: Some("process_gi_reservoirs"),
compilation_options: Default::default(),
cache: None,
});
let world_markov_chain_processing_pipeline =
device.create_compute_pipeline(&ComputePipelineDescriptor {
label: Some("(phosph_rs internal) End of frame world space marcov chain processor"),
layout: Some(&processing_pipeline_layout),
module: &shader,
entry_point: Some("process_world_markov_chains"),
compilation_options: Default::default(),
cache: None,
});
let processing_bind_group = device.create_bind_group(&BindGroupDescriptor {
label: Some("(phosph_rs internal) End of frame processing bind group"),
layout: &processing_bgl,
entries: &[
BindGroupEntry {
binding: 0,
resource: spatial_buffers.current.as_entire_binding(),
},
BindGroupEntry {
binding: 1,
resource: markov_world_buffers.current.as_entire_binding(),
},
],
});
Self {
markov_chain_screen_space: markov_buffers,
spatial_resampling: spatial_buffers,
markov_chain_world_space: markov_world_buffers,
info,
gi_reservoir_processing_pipeline,
world_markov_chain_processing_pipeline,
processing_bind_group,
limits: device.limits(),
}
}
pub fn advance_frame(
&self,
encoder: &mut CommandEncoder,
buffers_to_temporally_advance: BufferType,
) {
fn run_process_pipeline(
buffers: &DataBuffers,
encoder: &mut CommandEncoder,
pipeline: &wgpu::ComputePipeline,
mut num_workgroups: u32,
) {
while num_workgroups != 0 {
let execution_work_groups = buffers
.limits
.max_compute_workgroups_per_dimension
.min(num_workgroups.try_into().unwrap_or(<u32>::MAX));
num_workgroups -= execution_work_groups;
let mut pass = encoder.begin_compute_pass(&Default::default());
pass.set_pipeline(pipeline);
pass.set_bind_group(0, &buffers.processing_bind_group, &[]);
pass.set_push_constants(0, &num_workgroups.to_ne_bytes());
pass.dispatch_workgroups(execution_work_groups, 1, 1);
}
}
#[cfg(feature = "wip-features")]
if buffers_to_temporally_advance.contains(BufferType::SPATIAL_RESAMPLING) {
run_process_pipeline(
self,
encoder,
&self.gi_reservoir_processing_pipeline,
((self.spatial_resampling.current.size() / size_of::<crate::importance_sampling::Reservoir>() as u64) as u32).div_ceil(256),
);
self.spatial_resampling.advance_frame(encoder);
} else {
self.spatial_resampling.advance_frame_no_temporal(encoder);
}
if buffers_to_temporally_advance.contains(BufferType::MARKOV_CHAIN_SCREEN_SPACE) {
self.markov_chain_screen_space.advance_frame(encoder);
} else {
self.markov_chain_screen_space
.advance_frame_no_temporal(encoder);
}
if buffers_to_temporally_advance.contains(BufferType::MARKOV_CHAIN_WORLD_SPACE) {
run_process_pipeline(
self,
encoder,
&self.world_markov_chain_processing_pipeline,
((self.markov_chain_world_space.current.size() / size_of::<WorldMarkovStorage>() as u64) as u32).div_ceil(256),
);
encoder.copy_buffer_to_buffer(
&self.markov_chain_world_space.current,
0,
&self.markov_chain_world_space.old,
0,
self.markov_chain_world_space.current.size(),
);
} else {
self.markov_chain_world_space
.advance_frame_no_temporal(encoder);
}
}
pub fn create_bind_group(
&self,
device: &Device,
cam_buf: &Buffer,
tex_view: &wgpu::TextureView,
tex_normal_view: &wgpu::TextureView,
tex_albedo_view: &wgpu::TextureView,
tex_background_view: &wgpu::TextureView,
) -> wgpu::BindGroup {
let out_bgl = out_bgl(device);
device.create_bind_group(&BindGroupDescriptor {
label: None,
layout: &out_bgl,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::Buffer(cam_buf.as_entire_buffer_binding()),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(tex_view),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(tex_normal_view),
},
BindGroupEntry {
binding: 3,
resource: BindingResource::TextureView(tex_albedo_view),
},
BindGroupEntry {
binding: 4,
resource: BindingResource::TextureView(tex_background_view),
},
BindGroupEntry {
binding: 5,
resource: BindingResource::Buffer(
self.spatial_resampling.current.as_entire_buffer_binding(),
),
},
BindGroupEntry {
binding: 6,
resource: BindingResource::Buffer(
self.spatial_resampling.old.as_entire_buffer_binding(),
),
},
BindGroupEntry {
binding: 7,
resource: BindingResource::Buffer(self.info.as_entire_buffer_binding()),
},
BindGroupEntry {
binding: 8,
resource: BindingResource::Buffer(
self.markov_chain_screen_space
.current
.as_entire_buffer_binding(),
),
},
BindGroupEntry {
binding: 9,
resource: BindingResource::Buffer(
self.markov_chain_screen_space
.old
.as_entire_buffer_binding(),
),
},
BindGroupEntry {
binding: 10,
resource: BindingResource::Buffer(
self.markov_chain_world_space
.current
.as_entire_buffer_binding(),
),
},
BindGroupEntry {
binding: 11,
resource: BindingResource::Buffer(
self.markov_chain_world_space.old.as_entire_buffer_binding(),
),
},
],
})
}
}