use super::env_map::EnvironmentMap;
use crate::renderer::gpu::compute;
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct GpuHdrEnvDims {
width: u32,
height: u32,
_pad0: u32,
_pad1: u32,
}
const _: () = assert!(size_of::<GpuHdrEnvDims>() == 16);
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct GpuDistDims {
width: u32,
height: u32,
marginal_func_int: f32,
_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,
}
}
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,
}
}
}
pub struct GpuEnvironmentMap {
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
pub sampler: wgpu::Sampler,
}
impl GpuEnvironmentMap {
#[must_use]
pub fn new(
device: &wgpu::Device,
queue: &wgpu::Queue,
data: &[u8],
width: u32,
height: u32,
) -> Self {
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Environment Map"),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba32Float,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
data,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(width * 16),
rows_per_image: Some(height),
},
size,
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::Repeat,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Linear,
..Default::default()
});
Self {
texture,
view,
sampler,
}
}
}