use ash::vk;
use concinnity_core::gfx::mesh_payload::{SkinnedVertex, Vertex};
use concinnity_core::render::backend::{
DrawGeometryUpdate, SkinnedDrawGeometryUpdate, SkinnedSlotLayout,
};
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::geometry_repack;
use concinnity_core::render::range_alloc::RangeAllocator;
use concinnity_core::render::rt_geom;
use super::super::context::VkContext;
use super::super::texture::one_shot_submit;
impl VkContext {
pub(crate) fn rebuild_static_geometry(
&mut self,
changes: Vec<DrawGeometryUpdate>,
) -> RenderResult<()> {
self.wait_idle();
let old_v_bytes = self.geometry.vertex_buffer_bytes;
let old_i_bytes = self.geometry.index_buffer_bytes;
let old_vertices: Vec<Vertex> =
readback_typed(self, self.geometry.vertex_buffer.buffer(), old_v_bytes)?;
let old_indices: Vec<u32> =
readback_typed(self, self.geometry.index_buffer.buffer(), old_i_bytes)?;
let repacked = geometry_repack::repack_static_geometry(
&self.state.draw.objects,
&old_vertices,
&old_indices,
changes,
)?;
if repacked.ignored_changes > 0 {
tracing::warn!(
"rebuild_static_geometry: {} change(s) targeted draw indices not in \
draw_objects (ignored)",
repacked.ignored_changes
);
}
let new_vertices = repacked.vertices;
let new_indices = repacked.indices;
let new_v_bytes = std::mem::size_of_val(new_vertices.as_slice()) as u64;
let new_i_bytes = std::mem::size_of_val(new_indices.as_slice()) as u64;
let shared = super::shared_geometry_usage(self.hw.rt_capable);
let new_vbuf = self.hw.alloc.create_buffer(
new_v_bytes,
vk::BufferUsageFlags::VERTEX_BUFFER | shared,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?;
let new_ibuf = self.hw.alloc.create_buffer(
new_i_bytes,
vk::BufferUsageFlags::INDEX_BUFFER | shared,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?;
let vert_bytes = bytemuck::cast_slice(&new_vertices);
let idx_bytes = bytemuck::cast_slice(&new_indices);
self.write_geometry_region(new_vbuf.buffer(), 0, vert_bytes)?;
self.write_geometry_region(new_ibuf.buffer(), 0, idx_bytes)?;
self.geometry.vertex_buffer = new_vbuf;
self.geometry.vertex_buffer_bytes = new_v_bytes;
self.geometry.index_buffer = new_ibuf;
self.geometry.index_buffer_bytes = new_i_bytes;
self.state.placement.mesh_vtx = RangeAllocator::new();
self.state.placement.mesh_idx = RangeAllocator::new();
for (layout, obj) in repacked
.layouts
.into_iter()
.zip(&mut self.state.draw.objects)
{
layout.apply_to(obj);
}
if self.rt_reflections.is_some() {
self.rebuild_rt_accel();
}
Ok(())
}
pub(crate) fn rebuild_skinned_geometry(
&mut self,
changes: Vec<SkinnedDrawGeometryUpdate>,
) -> RenderResult<Vec<SkinnedSlotLayout>> {
if self.skinned.vertex_buffer.is_null() || self.skinned.index_buffer.is_null() {
return Err(RenderError::Other(
"rebuild_skinned_geometry: no skinned vertex/index buffer (was \
upload_skinned called?)"
.into(),
));
}
self.wait_idle();
let old_v_bytes = self.skinned.vertex_buffer_bytes;
let old_i_bytes = self.skinned.index_buffer_bytes;
let old_vertices: Vec<SkinnedVertex> =
readback_typed(self, self.skinned.vertex_buffer.buffer(), old_v_bytes)?;
let old_indices: Vec<u32> =
readback_typed(self, self.skinned.index_buffer.buffer(), old_i_bytes)?;
let repacked = geometry_repack::repack_skinned_geometry(
&self.state.skinned.draw_objects,
&old_vertices,
&old_indices,
changes,
)?;
if repacked.ignored_changes > 0 {
tracing::warn!(
"rebuild_skinned_geometry: {} change(s) targeted skinned indices not \
in skinned_draw_objects (ignored)",
repacked.ignored_changes
);
}
let new_vertices = &repacked.vertices;
let new_indices = &repacked.indices;
let new_v_bytes = std::mem::size_of_val(new_vertices.as_slice()) as u64;
let new_i_bytes = std::mem::size_of_val(new_indices.as_slice()) as u64;
let skinned_ib_rt = if self.hw.rt_capable {
vk::BufferUsageFlags::STORAGE_BUFFER
| vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS
| vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR
} else {
vk::BufferUsageFlags::empty()
};
let new_vbuf = self.hw.alloc.create_buffer(
new_v_bytes,
vk::BufferUsageFlags::VERTEX_BUFFER
| vk::BufferUsageFlags::TRANSFER_DST
| vk::BufferUsageFlags::STORAGE_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?;
let new_ibuf = self.hw.alloc.create_buffer(
rt_geom::skinned_index_buffer_bytes(new_indices.len()) as u64,
vk::BufferUsageFlags::INDEX_BUFFER | vk::BufferUsageFlags::TRANSFER_DST | skinned_ib_rt,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?;
let vert_bytes = bytemuck::cast_slice(new_vertices);
let idx_bytes = bytemuck::cast_slice(new_indices);
self.write_geometry_region(new_vbuf.buffer(), 0, vert_bytes)?;
self.write_geometry_region(new_ibuf.buffer(), 0, idx_bytes)?;
self.skinned.vertex_buffer = new_vbuf;
self.skinned.vertex_buffer_bytes = new_v_bytes;
self.skinned.index_buffer = new_ibuf;
self.skinned.index_buffer_bytes = new_i_bytes;
repacked.apply_to(&mut self.state.skinned.draw_objects);
self.refresh_main_skin_geometry(new_vertices.len())?;
Ok(repacked.layouts())
}
}
fn readback_typed<T: Copy>(ctx: &VkContext, src: vk::Buffer, bytes: u64) -> RenderResult<Vec<T>> {
if bytes == 0 {
return Ok(Vec::new());
}
let stride = std::mem::size_of::<T>() as u64;
if !bytes.is_multiple_of(stride) {
return Err(RenderError::Other(format!(
"readback_typed: buffer size {} not a multiple of T stride {}",
bytes, stride
)));
}
let count = (bytes / stride) as usize;
let staging = ctx.hw.alloc.create_buffer(
bytes,
vk::BufferUsageFlags::TRANSFER_DST,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)?;
one_shot_submit(
&ctx.hw.device,
ctx.commands.command_pool,
ctx.hw.graphics_queue,
|cmd| {
let copy = vk::BufferCopy::default()
.src_offset(0)
.dst_offset(0)
.size(bytes);
unsafe {
ctx.hw.device.cmd_copy_buffer(
cmd,
src,
staging.buffer(),
std::slice::from_ref(©),
)
};
},
)?;
let mut out: Vec<T> = Vec::with_capacity(count);
unsafe {
std::ptr::copy_nonoverlapping(staging.mapped_ptr() as *const T, out.as_mut_ptr(), count);
out.set_len(count);
}
Ok(out)
}