use super::types::decide_buffer_sizing;
use super::*;
#[derive(Copy, Clone)]
pub(crate) struct InstanceTextureData {
pub(crate) texture_ids: [Option<u64>; 2],
pub(crate) texture_uv_scales: [[f32; 2]; 2],
}
fn upsert_gpu_buffer(
device: &wgpu::Device,
queue: &wgpu::Queue,
buffer: &mut Option<wgpu::Buffer>,
label: &'static str,
bytes: &[u8],
usage: wgpu::BufferUsages,
) {
let decision =
decide_buffer_sizing(buffer.as_ref().map(|existing| existing.size()), bytes.len());
if decision.should_reallocate {
*buffer = Some(crate::pipeline::create_buffer_init(
device,
Some(label),
bytes,
usage,
));
} else if let Some(existing_buffer) = buffer.as_ref() {
queue.write_buffer(existing_buffer, 0, bytes);
}
}
fn append_aggregated_geometry(
temp_vertices: &mut Vec<crate::vertex::CustomVertex>,
temp_indices: &mut Vec<u16>,
vertices: &[crate::vertex::CustomVertex],
indices: &[u16],
) -> Option<(usize, usize)> {
if vertices.is_empty() || indices.is_empty() {
return None;
}
let vertex_start = temp_vertices.len();
if vertex_start > u16::MAX as usize {
warn!(
"Aggregated vertex count ({}) exceeds u16 limit. Rendering artifacts may occur.",
vertex_start
);
}
let index_start = temp_indices.len();
let vertex_offset = vertex_start as u16;
temp_vertices.extend_from_slice(vertices);
for &index in indices {
temp_indices.push(index + vertex_offset);
}
Some((index_start, indices.len()))
}
pub(crate) fn append_aggregated_geometry_for_shape(
cached_shape_data: &CachedShapeDrawData,
temp_vertices: &mut Vec<crate::vertex::CustomVertex>,
temp_indices: &mut Vec<u16>,
geometry_dedup_map: &mut HashMap<u64, (usize, usize)>,
) -> Option<(usize, usize)> {
let geometry_id = cached_shape_data.cached_shape.geometry_id;
if let Some(&existing_range) = geometry_id.and_then(|id| geometry_dedup_map.get(&id)) {
Some(existing_range)
} else {
let cached_shape = &cached_shape_data.cached_shape;
let vertex_buffers = cached_shape.vertex_buffers();
let range = append_aggregated_geometry(
temp_vertices,
temp_indices,
&vertex_buffers.vertices,
&vertex_buffers.indices,
);
if let (Some(id), Some(range)) = (geometry_id, range) {
geometry_dedup_map.insert(id, range);
}
range
}
}
pub(crate) fn append_instance_data(
temp_instance_transforms: &mut Vec<InstanceTransform>,
temp_instance_colors: &mut Vec<InstanceColor>,
temp_instance_metadata: &mut Vec<InstanceMetadata>,
transform: Option<InstanceTransform>,
color_override: Option<[f32; 4]>,
texture_data: InstanceTextureData,
) -> usize {
let instance_index = temp_instance_transforms.len();
temp_instance_transforms.push(transform.unwrap_or_else(InstanceTransform::identity));
temp_instance_colors.push(InstanceColor {
color: color_override.unwrap_or([0.0, 0.0, 0.0, 0.0]),
});
let texture_flags = (texture_data.texture_ids[0].is_some() as u32)
| ((texture_data.texture_ids[1].is_some() as u32) << 1);
temp_instance_metadata.push(InstanceMetadata {
draw_order: instance_index as f32,
texture_flags: texture_flags as f32,
texture_uv_scale_layer0: texture_data.texture_uv_scales[0],
texture_uv_scale_layer1: texture_data.texture_uv_scales[1],
});
instance_index
}
impl<'a> Renderer<'a> {
fn ensure_identity_instance_buffers(&mut self) {
if self.identity_instance_transform_buffer.is_none() {
let identity = InstanceTransform::identity();
self.identity_instance_transform_buffer = Some(crate::pipeline::create_buffer_init(
&self.device,
Some("Identity Instance Transform Buffer"),
bytemuck::cast_slice(&[identity]),
BufferUsages::VERTEX | BufferUsages::COPY_DST,
));
}
if self.identity_instance_color_buffer.is_none() {
let transparent = InstanceColor::transparent();
self.identity_instance_color_buffer = Some(crate::pipeline::create_buffer_init(
&self.device,
Some("Identity Instance Color Buffer"),
bytemuck::cast_slice(&[transparent]),
BufferUsages::VERTEX | BufferUsages::COPY_DST,
));
}
if self.identity_instance_metadata_buffer.is_none() {
let metadata = InstanceMetadata::default();
self.identity_instance_metadata_buffer = Some(crate::pipeline::create_buffer_init(
&self.device,
Some("Identity Instance Metadata Buffer"),
bytemuck::cast_slice(&[metadata]),
BufferUsages::VERTEX | BufferUsages::COPY_DST,
));
}
}
pub(super) fn clear_buffers(&mut self) {
self.temp_vertices.clear();
self.temp_indices.clear();
self.temp_instance_transforms.clear();
self.temp_instance_colors.clear();
self.temp_instance_metadata.clear();
self.geometry_dedup_map.clear();
}
pub(super) fn upload_buffers_for_frame(&mut self) {
if !self.temp_vertices.is_empty() {
upsert_gpu_buffer(
&self.device,
&self.queue,
&mut self.aggregated_vertex_buffer,
"Aggregated Vertex Buffer",
bytemuck::cast_slice(&self.temp_vertices),
BufferUsages::VERTEX | BufferUsages::COPY_DST,
);
}
if !self.temp_indices.is_empty() {
upsert_gpu_buffer(
&self.device,
&self.queue,
&mut self.aggregated_index_buffer,
"Aggregated Index Buffer",
bytemuck::cast_slice(&self.temp_indices),
BufferUsages::INDEX | BufferUsages::COPY_DST,
);
}
self.ensure_identity_instance_buffers();
if !self.temp_instance_transforms.is_empty() {
upsert_gpu_buffer(
&self.device,
&self.queue,
&mut self.aggregated_instance_transform_buffer,
"Aggregated Instance Transform Buffer",
bytemuck::cast_slice(&self.temp_instance_transforms),
BufferUsages::VERTEX | BufferUsages::COPY_DST,
);
}
if !self.temp_instance_colors.is_empty() {
upsert_gpu_buffer(
&self.device,
&self.queue,
&mut self.aggregated_instance_color_buffer,
"Aggregated Instance Color Buffer",
bytemuck::cast_slice(&self.temp_instance_colors),
BufferUsages::VERTEX | BufferUsages::COPY_DST,
);
}
if !self.temp_instance_metadata.is_empty() {
upsert_gpu_buffer(
&self.device,
&self.queue,
&mut self.aggregated_instance_metadata_buffer,
"Aggregated Instance Metadata Buffer",
bytemuck::cast_slice(&self.temp_instance_metadata),
BufferUsages::VERTEX | BufferUsages::COPY_DST,
);
}
}
pub(super) fn prepare_render(&mut self) {
self.upload_buffers_for_frame();
}
}