use super::env_map::EnvironmentMap;
use crate::renderer::gpu::compute;
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub(crate) struct GpuHdrEnvDims {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) _pad0: u32,
pub(crate) _pad1: u32,
}
const _: () = assert!(size_of::<GpuHdrEnvDims>() == 16);
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub(crate) struct GpuDistDims {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) marginal_func_int: f32,
pub(crate) _pad0: f32,
}
const _: () = assert!(size_of::<GpuDistDims>() == 16);
pub(crate) struct HdrEnvGpuData {
pub(crate) texels: wgpu::Buffer,
pub(crate) dims: wgpu::Buffer,
pub(crate) dist_func: wgpu::Buffer,
pub(crate) dist_cdf: wgpu::Buffer,
pub(crate) dist_dims: wgpu::Buffer,
}
fn flatten_distribution(
device: &wgpu::Device,
dist: &super::env_map::Distribution2D,
label_prefix: &str,
) -> (wgpu::Buffer, wgpu::Buffer, wgpu::Buffer) {
let width = dist.width();
let height = dist.height();
let marginal = dist.marginal();
let conditional = dist.conditional();
let mut func = Vec::with_capacity(width * height + height);
let mut cdf = Vec::with_capacity(height * (width + 1) + (height + 1));
for row in conditional {
func.extend_from_slice(row.func());
cdf.extend_from_slice(row.cdf());
}
func.extend_from_slice(marginal.func());
cdf.extend_from_slice(marginal.cdf());
let func_buf = compute::upload(
device,
&format!("{label_prefix} dist func"),
&func,
wgpu::BufferUsages::STORAGE,
);
let cdf_buf = compute::upload(
device,
&format!("{label_prefix} dist cdf"),
&cdf,
wgpu::BufferUsages::STORAGE,
);
let dims = GpuDistDims {
width: width as u32,
height: height as u32,
marginal_func_int: marginal.func_int(),
_pad0: 0.0,
};
let dims_buf = compute::upload(
device,
&format!("{label_prefix} dist dims"),
std::slice::from_ref(&dims),
wgpu::BufferUsages::UNIFORM,
);
(func_buf, cdf_buf, dims_buf)
}
impl HdrEnvGpuData {
pub(crate) fn upload(device: &wgpu::Device, map: &EnvironmentMap) -> Self {
let texels: Vec<[f32; 4]> = map
.pixels()
.iter()
.map(|&[r, g, b]| [r, g, b, 0.0])
.collect();
let texels_buf = compute::upload(
device,
"hdr env texels",
&texels,
wgpu::BufferUsages::STORAGE,
);
let dims = GpuHdrEnvDims {
width: map.width() as u32,
height: map.height() as u32,
_pad0: 0,
_pad1: 0,
};
let dims_buf = compute::upload(
device,
"hdr env dims",
std::slice::from_ref(&dims),
wgpu::BufferUsages::UNIFORM,
);
let (dist_func, dist_cdf, dist_dims) =
flatten_distribution(device, map.distribution(), "hdr env");
Self {
texels: texels_buf,
dims: dims_buf,
dist_func,
dist_cdf,
dist_dims,
}
}
#[must_use]
pub(crate) fn fits_storage_binding(device: &wgpu::Device, map: &EnvironmentMap) -> bool {
let width = u64::try_from(map.width()).unwrap_or(u64::MAX);
let height = u64::try_from(map.height()).unwrap_or(u64::MAX);
let texel_bytes = width.saturating_mul(height).saturating_mul(16);
let max_binding_bytes = device.limits().max_storage_buffer_binding_size;
texel_bytes <= max_binding_bytes
}
pub(crate) fn dummy(device: &wgpu::Device) -> Self {
let texels: [[f32; 4]; 1] = [[0.0, 0.0, 0.0, 0.0]];
let texels_buf = compute::upload(
device,
"hdr env texels (dummy, non-HDR scene)",
&texels,
wgpu::BufferUsages::STORAGE,
);
let dims = GpuHdrEnvDims {
width: 1,
height: 1,
_pad0: 0,
_pad1: 0,
};
let dims_buf = compute::upload(
device,
"hdr env dims (dummy, non-HDR scene)",
std::slice::from_ref(&dims),
wgpu::BufferUsages::UNIFORM,
);
let dummy_map = EnvironmentMap::uniform(1, 1, [0.0, 0.0, 0.0]);
let (dist_func, dist_cdf, dist_dims) = flatten_distribution(
device,
dummy_map.distribution(),
"hdr env (dummy, non-HDR scene)",
);
Self {
texels: texels_buf,
dims: dims_buf,
dist_func,
dist_cdf,
dist_dims,
}
}
}