use wgpu::BufferUsages;
use crate::{
geometry::GpuFacetPlane,
optics::raytracer::EnvironmentSource,
renderer::{
buffers::{GpuCameraParams, GpuGemMaterial},
env_map_gpu::HdrEnvGpuData,
gpu::{GpuContext, compute},
},
};
use super::{DEBUG_BUFFER_FLOATS, GpuFrameError};
pub struct TransportOutputs {
xyz: wgpu::Buffer,
radiance: wgpu::Buffer,
lambdas: wgpu::Buffer,
path_pdf: wgpu::Buffer,
compat: wgpu::Buffer,
pub(super) capacity: usize,
}
impl TransportOutputs {
pub(crate) fn new(device: &wgpu::Device, capacity: usize) -> Self {
let usage = BufferUsages::STORAGE | BufferUsages::COPY_SRC;
Self {
xyz: compute::zeroed_buffer::<f32>(device, "transport out xyz", capacity * 3, usage),
radiance: compute::zeroed_buffer::<f32>(
device,
"transport out radiance",
capacity * 8,
usage,
),
lambdas: compute::zeroed_buffer::<f32>(
device,
"transport out lambdas",
capacity * 8,
usage,
),
path_pdf: compute::zeroed_buffer::<f32>(
device,
"transport out path_pdf",
capacity * 8,
usage,
),
compat: compute::zeroed_buffer::<u32>(
device,
"transport out compat",
capacity * 8,
usage,
),
capacity,
}
}
pub(crate) fn new_production(device: &wgpu::Device, capacity: usize) -> Self {
let usage = BufferUsages::STORAGE | BufferUsages::COPY_SRC;
Self {
xyz: compute::zeroed_buffer::<f32>(
device,
"transport out xyz (production)",
capacity * 3,
usage,
),
radiance: compute::zeroed_buffer::<f32>(
device,
"transport out radiance (production, write_debug_buffers=0, unused)",
DEBUG_BUFFER_FLOATS,
usage,
),
lambdas: compute::zeroed_buffer::<f32>(
device,
"transport out lambdas (production, write_debug_buffers=0, unused)",
DEBUG_BUFFER_FLOATS,
usage,
),
path_pdf: compute::zeroed_buffer::<f32>(
device,
"transport out path_pdf (production, write_debug_buffers=0, unused)",
DEBUG_BUFFER_FLOATS,
usage,
),
compat: compute::zeroed_buffer::<u32>(
device,
"transport out compat (production, write_debug_buffers=0, unused)",
DEBUG_BUFFER_FLOATS,
usage,
),
capacity,
}
}
pub(crate) const fn xyz(&self) -> &wgpu::Buffer {
&self.xyz
}
pub(crate) const fn radiance(&self) -> &wgpu::Buffer {
&self.radiance
}
pub(crate) const fn lambdas(&self) -> &wgpu::Buffer {
&self.lambdas
}
pub(crate) const fn path_pdf(&self) -> &wgpu::Buffer {
&self.path_pdf
}
pub(crate) const fn compat(&self) -> &wgpu::Buffer {
&self.compat
}
}
pub struct FrameSceneBuffers {
pub(super) camera: wgpu::Buffer,
pub(super) material: wgpu::Buffer,
pub(super) planes: wgpu::Buffer,
planes_capacity: usize,
pub(super) facet_finishes: wgpu::Buffer,
facet_finishes_capacity: usize,
pub(super) hdr_env: HdrEnvGpuData,
hdr_env_identity: Option<usize>,
}
pub fn build_hdr_env(
device: &wgpu::Device,
environment: EnvironmentSource<'_>,
) -> Result<(HdrEnvGpuData, Option<usize>), GpuFrameError> {
match environment {
EnvironmentSource::HdrMap(map) => {
if !HdrEnvGpuData::fits_storage_binding(device, map) {
return Err(GpuFrameError::UnsupportedEnvironment);
}
let identity = std::ptr::from_ref(map) as usize;
Ok((HdrEnvGpuData::upload(device, map), Some(identity)))
}
EnvironmentSource::Studio { .. } => Ok((HdrEnvGpuData::dummy(device), None)),
}
}
#[derive(Clone, Copy)]
pub struct SceneBufferInputs<'a> {
pub(crate) camera_params: &'a GpuCameraParams,
pub(crate) material: &'a GpuGemMaterial,
pub(crate) planes: &'a [GpuFacetPlane],
pub(crate) facet_finishes: &'a [u32],
pub(crate) environment: EnvironmentSource<'a>,
}
impl FrameSceneBuffers {
fn new(device: &wgpu::Device, inputs: &SceneBufferInputs<'_>) -> Result<Self, GpuFrameError> {
let (hdr_env, hdr_env_identity) = build_hdr_env(device, inputs.environment)?;
Ok(Self {
camera: compute::upload(
device,
"transport camera (persistent)",
std::slice::from_ref(inputs.camera_params),
BufferUsages::UNIFORM | BufferUsages::COPY_DST,
),
material: compute::upload(
device,
"transport material (persistent)",
std::slice::from_ref(inputs.material),
BufferUsages::STORAGE | BufferUsages::COPY_DST,
),
planes: compute::upload(
device,
"transport planes (persistent)",
inputs.planes,
BufferUsages::STORAGE | BufferUsages::COPY_DST,
),
planes_capacity: inputs.planes.len(),
facet_finishes: compute::upload(
device,
"transport facet finishes (persistent)",
inputs.facet_finishes,
BufferUsages::STORAGE | BufferUsages::COPY_DST,
),
facet_finishes_capacity: inputs.facet_finishes.len(),
hdr_env,
hdr_env_identity,
})
}
fn update(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
inputs: &SceneBufferInputs<'_>,
) -> Result<(), GpuFrameError> {
let SceneBufferInputs {
camera_params,
material,
planes,
facet_finishes,
environment,
} = *inputs;
queue.write_buffer(&self.camera, 0, bytemuck::bytes_of(camera_params));
queue.write_buffer(&self.material, 0, bytemuck::bytes_of(material));
if planes.len() > self.planes_capacity {
self.planes = compute::upload(
device,
"transport planes (persistent, grown)",
planes,
BufferUsages::STORAGE | BufferUsages::COPY_DST,
);
self.planes_capacity = planes.len();
} else {
queue.write_buffer(&self.planes, 0, bytemuck::cast_slice(planes));
}
if facet_finishes.len() > self.facet_finishes_capacity {
self.facet_finishes = compute::upload(
device,
"transport facet finishes (persistent, grown)",
facet_finishes,
BufferUsages::STORAGE | BufferUsages::COPY_DST,
);
self.facet_finishes_capacity = facet_finishes.len();
} else {
queue.write_buffer(
&self.facet_finishes,
0,
bytemuck::cast_slice(facet_finishes),
);
}
let new_identity = match environment {
EnvironmentSource::HdrMap(map) => Some(std::ptr::from_ref(map) as usize),
EnvironmentSource::Studio { .. } => None,
};
if new_identity != self.hdr_env_identity {
let (hdr_env, hdr_env_identity) = build_hdr_env(device, environment)?;
self.hdr_env = hdr_env;
self.hdr_env_identity = hdr_env_identity;
}
Ok(())
}
pub(crate) fn ensure(
existing: Option<Self>,
ctx: &GpuContext,
inputs: &SceneBufferInputs<'_>,
) -> Result<Self, GpuFrameError> {
existing.map_or_else(
|| Self::new(&ctx.device, inputs),
|mut existing| {
existing.update(&ctx.device, &ctx.queue, inputs)?;
Ok(existing)
},
)
}
}
pub struct WavefrontRayBuffers {
pub(super) origin: wgpu::Buffer,
pub(super) dir: wgpu::Buffer,
pub(super) k: wgpu::Buffer,
pub(super) prev_plane_normal: wgpu::Buffer,
pub(super) flags: wgpu::Buffer,
pub(super) stokes: wgpu::Buffer,
pub(super) radiance: wgpu::Buffer,
pub(super) path_pdf: wgpu::Buffer,
pub(super) split_radiance: wgpu::Buffer,
pub(super) compat: wgpu::Buffer,
pub(super) pending_light_mis: wgpu::Buffer,
pub(super) pending_light_mis_dir: wgpu::Buffer,
pub(super) nee_xyz: wgpu::Buffer,
pub(super) lambdas: wgpu::Buffer,
pub(super) seed: wgpu::Buffer,
pub(super) active_in: wgpu::Buffer,
pub(super) active_out: wgpu::Buffer,
pub(super) local_offset: wgpu::Buffer,
pub(super) block_alive_count: wgpu::Buffer,
pub(super) block_offset: wgpu::Buffer,
}
impl WavefrontRayBuffers {
pub(crate) fn new(device: &wgpu::Device, tuples: usize) -> Self {
use compute::zeroed_buffer as zeroed;
let storage_rw = wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST;
let storage_rw_src = storage_rw | wgpu::BufferUsages::COPY_SRC;
let workgroups = tuples.div_ceil(super::WORKGROUP_SIZE);
Self {
origin: zeroed::<[f32; 4]>(device, "wf ray_origin", tuples, storage_rw),
dir: zeroed::<[f32; 4]>(device, "wf ray_dir", tuples, storage_rw),
k: zeroed::<[f32; 4]>(device, "wf ray_k", tuples, storage_rw),
prev_plane_normal: zeroed::<[f32; 4]>(
device,
"wf ray_prev_plane_normal",
tuples,
storage_rw,
),
flags: zeroed::<u32>(device, "wf ray_flags", tuples, storage_rw),
stokes: zeroed::<[f32; 4]>(device, "wf ray_stokes", tuples * 8, storage_rw),
radiance: zeroed::<f32>(device, "wf ray_radiance", tuples * 8, storage_rw),
path_pdf: zeroed::<f32>(device, "wf ray_path_pdf", tuples * 8, storage_rw),
split_radiance: zeroed::<f32>(device, "wf ray_split_radiance", tuples * 8, storage_rw),
compat: zeroed::<u32>(device, "wf ray_compat", tuples * 8, storage_rw),
pending_light_mis: zeroed::<f32>(
device,
"wf ray_pending_light_mis",
tuples,
storage_rw,
),
pending_light_mis_dir: zeroed::<[f32; 4]>(
device,
"wf ray_pending_light_mis_dir",
tuples,
storage_rw,
),
nee_xyz: zeroed::<[f32; 4]>(device, "wf ray_nee_xyz", tuples, storage_rw),
lambdas: zeroed::<f32>(device, "wf ray_lambdas", tuples * 8, storage_rw),
seed: zeroed::<u32>(device, "wf ray_seed", tuples, storage_rw),
active_in: zeroed::<u32>(device, "wf active_ray_indices", tuples, storage_rw),
active_out: zeroed::<u32>(device, "wf active_ray_indices_next", tuples, storage_rw_src),
local_offset: zeroed::<u32>(device, "wf compact_local_offset", tuples, storage_rw),
block_alive_count: zeroed::<u32>(
device,
"wf compact_block_alive_count",
workgroups.max(1),
storage_rw_src,
),
block_offset: zeroed::<u32>(
device,
"wf compact_block_offset",
workgroups.max(1),
storage_rw,
),
}
}
}