use awsm_renderer_core::{
buffers::{BufferDescriptor, BufferUsage},
error::AwsmCoreError,
renderer::AwsmRendererWebGpu,
};
pub const OCCLUSION_INSTANCE_STRIDE: usize = 48;
const INITIAL_CAPACITY: u32 = 1024;
pub struct OcclusionBuffers {
pub instances_buffer: web_sys::GpuBuffer,
pub visible_buffer: web_sys::GpuBuffer,
pub params_buffer: web_sys::GpuBuffer,
pub capacity: u32,
pub staging: Vec<u8>,
pub(crate) instances_uploader: std::sync::Mutex<crate::buffer::mapped_uploader::MappedUploader>,
pub(crate) params_uploader: std::sync::Mutex<crate::buffer::mapped_uploader::MappedUploader>,
}
impl OcclusionBuffers {
pub fn new(gpu: &AwsmRendererWebGpu) -> Result<Self, AwsmCoreError> {
Self::with_capacity(gpu, INITIAL_CAPACITY)
}
fn with_capacity(gpu: &AwsmRendererWebGpu, capacity: u32) -> Result<Self, AwsmCoreError> {
let capacity = capacity.max(1);
let instances_bytes = capacity as usize * OCCLUSION_INSTANCE_STRIDE;
let visible_bytes = capacity as usize * 4;
let instances_buffer = gpu.create_buffer(
&BufferDescriptor::new(
Some("OcclusionInstances"),
instances_bytes,
BufferUsage::new().with_storage().with_copy_dst(),
)
.into(),
)?;
let visible_buffer = gpu.create_buffer(
&BufferDescriptor::new(
Some("OcclusionVisible"),
visible_bytes,
BufferUsage::new()
.with_storage()
.with_copy_dst()
.with_copy_src(),
)
.into(),
)?;
let params_buffer = gpu.create_buffer(
&BufferDescriptor::new(
Some("OcclusionParams"),
16,
BufferUsage::new().with_uniform().with_copy_dst(),
)
.into(),
)?;
Ok(Self {
instances_buffer,
visible_buffer,
params_buffer,
capacity,
staging: vec![0u8; instances_bytes],
instances_uploader: std::sync::Mutex::new(
crate::buffer::mapped_uploader::MappedUploader::new("OcclusionInstances"),
),
params_uploader: std::sync::Mutex::new(
crate::buffer::mapped_uploader::MappedUploader::new("OcclusionParams"),
),
})
}
pub fn upload_stats(&self) -> crate::buffer::mapped_staging_ring::UploadStats {
let mut s = self.instances_uploader.lock().unwrap().stats();
let b = self.params_uploader.lock().unwrap().stats();
s.peak_ring_depth_used = s.peak_ring_depth_used.max(b.peak_ring_depth_used);
s.fallback_count += b.fallback_count;
s.map_async_wait_ms += b.map_async_wait_ms;
s.bytes_uploaded_via_ring += b.bytes_uploaded_via_ring;
s.bytes_uploaded_via_fallback += b.bytes_uploaded_via_fallback;
s.bytes_uploaded_via_writebuffer += b.bytes_uploaded_via_writebuffer;
s.resize_count += b.resize_count;
s
}
pub fn ensure_capacity(
&mut self,
gpu: &AwsmRendererWebGpu,
needed: u32,
) -> Result<bool, AwsmCoreError> {
if needed <= self.capacity {
return Ok(false);
}
let new_capacity = needed.saturating_mul(2).max(needed);
*self = Self::with_capacity(gpu, new_capacity)?;
Ok(true)
}
pub fn write_params(
&self,
gpu: &AwsmRendererWebGpu,
active_count: u32,
) -> Result<(), AwsmCoreError> {
let mut bytes = [0u8; 16];
bytes[0..4].copy_from_slice(&active_count.to_le_bytes());
self.params_uploader.lock().unwrap().write_dirty_ranges(
gpu,
&self.params_buffer,
16,
&bytes,
&[(0, 16)],
)
}
pub fn write_instances(
&self,
gpu: &AwsmRendererWebGpu,
bytes: &[u8],
) -> Result<(), AwsmCoreError> {
if bytes.is_empty() {
return Ok(());
}
let dest_size = self.capacity as usize * OCCLUSION_INSTANCE_STRIDE;
let n = bytes.len();
self.instances_uploader.lock().unwrap().write_dirty_ranges(
gpu,
&self.instances_buffer,
dest_size,
bytes,
&[(0, n)],
)
}
}