use awsm_renderer_core::{
buffers::{BufferDescriptor, BufferUsage},
error::AwsmCoreError,
renderer::AwsmRendererWebGpu,
};
pub const INDIRECT_ARGS_STRIDE: u32 = 16;
pub fn header_bytes(bucket_count: u32) -> u32 {
let args_bytes = bucket_count * INDIRECT_ARGS_STRIDE;
let offsets_bytes = bucket_count * 4;
let capacity_bytes = 4;
let unpadded = args_bytes + offsets_bytes + capacity_bytes;
(unpadded + 15) & !15
}
pub struct ClassifyBuffers {
pub buffer: web_sys::GpuBuffer,
pub bucket_capacity: u32,
pub bucket_count: u32,
pub size_bytes: u32,
header_scratch: Vec<u8>,
}
impl ClassifyBuffers {
pub fn new(
gpu: &AwsmRendererWebGpu,
bucket_capacity: u32,
bucket_count: u32,
) -> Result<Self, AwsmCoreError> {
let bucket_capacity = bucket_capacity.max(1);
let bucket_count = bucket_count.max(1);
let header = header_bytes(bucket_count);
let tiles_bytes = bucket_capacity
.saturating_mul(bucket_count)
.saturating_mul(8);
let size_bytes = header + tiles_bytes;
let buffer = gpu.create_buffer(
&BufferDescriptor::new(
Some("MaterialClassifyBuckets"),
size_bytes as usize,
BufferUsage::new()
.with_storage()
.with_indirect()
.with_copy_dst(),
)
.into(),
)?;
let mut header_scratch = vec![0u8; header as usize];
write_header(&mut header_scratch, bucket_capacity, bucket_count);
Ok(Self {
buffer,
bucket_capacity,
bucket_count,
size_bytes,
header_scratch,
})
}
pub fn ensure_capacity(
&mut self,
gpu: &AwsmRendererWebGpu,
needed_capacity: u32,
) -> Result<bool, AwsmCoreError> {
if needed_capacity <= self.bucket_capacity {
return Ok(false);
}
let new_capacity = (needed_capacity * 2).max(needed_capacity);
*self = Self::new(gpu, new_capacity, self.bucket_count)?;
Ok(true)
}
pub fn ensure_bucket_count(
&mut self,
gpu: &AwsmRendererWebGpu,
needed_bucket_count: u32,
) -> Result<bool, AwsmCoreError> {
if needed_bucket_count <= self.bucket_count {
return Ok(false);
}
*self = Self::new(gpu, self.bucket_capacity, needed_bucket_count)?;
Ok(true)
}
pub fn reset_header(&self, gpu: &AwsmRendererWebGpu) -> Result<(), AwsmCoreError> {
gpu.write_buffer(
&self.buffer,
None,
self.header_scratch.as_slice(),
None,
None,
)
}
}
pub fn write_header(dst: &mut [u8], bucket_capacity: u32, bucket_count: u32) {
let one = 1u32.to_ne_bytes();
for bucket in 0..bucket_count as usize {
let base = bucket * INDIRECT_ARGS_STRIDE as usize;
dst[base..base + 4].copy_from_slice(&[0; 4]); dst[base + 4..base + 8].copy_from_slice(&one); dst[base + 8..base + 12].copy_from_slice(&one); dst[base + 12..base + 16].copy_from_slice(&[0; 4]); }
let offsets_base = (bucket_count * INDIRECT_ARGS_STRIDE) as usize;
for bucket in 0..bucket_count as usize {
let off = (bucket as u32).saturating_mul(bucket_capacity);
let dst_base = offsets_base + bucket * 4;
dst[dst_base..dst_base + 4].copy_from_slice(&off.to_ne_bytes());
}
let cap_base = offsets_base + (bucket_count * 4) as usize;
dst[cap_base..cap_base + 4].copy_from_slice(&bucket_capacity.to_ne_bytes());
}
pub fn indirect_args_offset(bucket_index: u32) -> u32 {
bucket_index * INDIRECT_ARGS_STRIDE
}