feat(renderer): render keyed collection of chunk meshes with per-chunk offset
This commit is contained in:
parent
2ca9ae801d
commit
dd8a969828
|
|
@ -26,9 +26,9 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||||
use std::ffi::c_char;
|
use std::ffi::c_char;
|
||||||
|
|
||||||
pub use error::RendererError;
|
pub use error::RendererError;
|
||||||
pub use renderer::Renderer;
|
pub use renderer::{MeshKey, Renderer};
|
||||||
|
|
||||||
use crate::mesh::Vertex;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
/// Initializes the Vulkan renderer.
|
/// Initializes the Vulkan renderer.
|
||||||
|
|
@ -42,7 +42,7 @@ impl Renderer {
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `MAX_FRAMES_IN_FLIGHT` or vertex data sizes exceed `u32`/`u64` limits.
|
/// Panics if `MAX_FRAMES_IN_FLIGHT` exceeds `u32`'s range.
|
||||||
// TODO: partial-construction leak. Each `?` below early-returns and leaks every Vulkan resource created so far; only a fully successful `new` reaches `Drop for Renderer`. Once the renderer grows more state, wrap each resource in an RAII guard so failure paths tear them down too.
|
// TODO: partial-construction leak. Each `?` below early-returns and leaks every Vulkan resource created so far; only a fully successful `new` reaches `Drop for Renderer`. Once the renderer grows more state, wrap each resource in an RAII guard so failure paths tear them down too.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
display_handle: RawDisplayHandle,
|
display_handle: RawDisplayHandle,
|
||||||
|
|
@ -125,11 +125,6 @@ impl Renderer {
|
||||||
let graphics_pipeline =
|
let graphics_pipeline =
|
||||||
pipeline::create_graphics_pipeline(&device, pipeline_layout, swapchain_format)?;
|
pipeline::create_graphics_pipeline(&device, pipeline_layout, swapchain_format)?;
|
||||||
|
|
||||||
let (vertex_buffer, vertex_allocation, index_buffer, index_allocation) =
|
|
||||||
create_geometry(&device, &mut allocator)?;
|
|
||||||
|
|
||||||
let index_count = 36;
|
|
||||||
|
|
||||||
let (depth_image, depth_allocation, depth_image_view) =
|
let (depth_image, depth_allocation, depth_image_view) =
|
||||||
create_depth_resources(&device, &mut allocator, swapchain_extent)?;
|
create_depth_resources(&device, &mut allocator, swapchain_extent)?;
|
||||||
|
|
||||||
|
|
@ -153,16 +148,12 @@ impl Renderer {
|
||||||
command_pool,
|
command_pool,
|
||||||
command_buffers,
|
command_buffers,
|
||||||
allocator: Some(allocator),
|
allocator: Some(allocator),
|
||||||
index_buffer,
|
chunk_meshes: HashMap::new(),
|
||||||
index_allocation: Some(index_allocation),
|
|
||||||
index_count,
|
|
||||||
depth_image,
|
depth_image,
|
||||||
depth_allocation: Some(depth_allocation),
|
depth_allocation: Some(depth_allocation),
|
||||||
depth_image_view,
|
depth_image_view,
|
||||||
pipeline_layout,
|
pipeline_layout,
|
||||||
graphics_pipeline,
|
graphics_pipeline,
|
||||||
vertex_buffer,
|
|
||||||
vertex_allocation: Some(vertex_allocation),
|
|
||||||
sync: Some(sync),
|
sync: Some(sync),
|
||||||
current_frame: 0,
|
current_frame: 0,
|
||||||
})
|
})
|
||||||
|
|
@ -193,84 +184,6 @@ fn create_allocator(
|
||||||
Ok(allocator)
|
Ok(allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the 3D geometry buffers (vertex and index) for a cube.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns [`RendererError::AllocationError`] if GPU memory cannot be allocated, or [`RendererError::VulkanError`] if a buffer cannot be created.
|
|
||||||
fn create_geometry(
|
|
||||||
device: &ash::Device,
|
|
||||||
allocator: &mut Allocator,
|
|
||||||
) -> Result<(vk::Buffer, Allocation, vk::Buffer, Allocation), RendererError> {
|
|
||||||
let vertices = [
|
|
||||||
// Front face
|
|
||||||
Vertex {
|
|
||||||
position: [-0.5, -0.5, 0.5],
|
|
||||||
color: [1.0, 0.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, -0.5, 0.5],
|
|
||||||
color: [0.0, 1.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, 0.5, 0.5],
|
|
||||||
color: [0.0, 0.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [-0.5, 0.5, 0.5],
|
|
||||||
color: [1.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
// Back face
|
|
||||||
Vertex {
|
|
||||||
position: [-0.5, -0.5, -0.5],
|
|
||||||
color: [1.0, 0.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, -0.5, -0.5],
|
|
||||||
color: [0.0, 1.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.5, 0.5, -0.5],
|
|
||||||
color: [0.0, 0.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [-0.5, 0.5, -0.5],
|
|
||||||
color: [1.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let indices: [u32; 36] = [
|
|
||||||
0, 1, 2, 2, 3, 0, // front
|
|
||||||
1, 5, 6, 6, 2, 1, // right
|
|
||||||
7, 6, 5, 5, 4, 7, // back
|
|
||||||
4, 0, 3, 3, 7, 4, // left
|
|
||||||
4, 5, 1, 1, 0, 4, // bottom
|
|
||||||
3, 2, 6, 6, 7, 3, // top
|
|
||||||
];
|
|
||||||
|
|
||||||
let (vertex_buffer, vertex_allocation) = create_gpu_buffer(
|
|
||||||
device,
|
|
||||||
allocator,
|
|
||||||
bytemuck::cast_slice(&vertices),
|
|
||||||
vk::BufferUsageFlags::VERTEX_BUFFER,
|
|
||||||
"Vertex Buffer",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (index_buffer, index_allocation) = create_gpu_buffer(
|
|
||||||
device,
|
|
||||||
allocator,
|
|
||||||
bytemuck::cast_slice(&indices),
|
|
||||||
vk::BufferUsageFlags::INDEX_BUFFER,
|
|
||||||
"Index Buffer",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
vertex_buffer,
|
|
||||||
vertex_allocation,
|
|
||||||
index_buffer,
|
|
||||||
index_allocation,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the depth buffer resources (image, memory, and view).
|
/// Creates the depth buffer resources (image, memory, and view).
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
||||||
|
|
@ -36,16 +36,17 @@ pub fn create_shader_module(
|
||||||
///
|
///
|
||||||
/// Returns [`RendererError::VulkanError`] if the device fails to create the pipeline layout.
|
/// Returns [`RendererError::VulkanError`] if the device fails to create the pipeline layout.
|
||||||
pub fn create_pipeline_layout(device: &Device) -> Result<vk::PipelineLayout, RendererError> {
|
pub fn create_pipeline_layout(device: &Device) -> Result<vk::PipelineLayout, RendererError> {
|
||||||
// A single push constant range is defined for the MVP matrix, allowing it to be updated for every draw call with high efficiency.
|
// The push-constant range covers the 64-byte MVP matrix followed by a 16-byte vec4 per-chunk world offset (80 bytes total, within the 128-byte guaranteed minimum).
|
||||||
#[expect(
|
#[expect(
|
||||||
clippy::expect_used,
|
clippy::expect_used,
|
||||||
reason = "size_of::<Mat4>() is 64 bytes, well within u32 range"
|
reason = "80 bytes (Mat4 + vec4) is well within u32 range"
|
||||||
)]
|
)]
|
||||||
let push_constant_range = vk::PushConstantRange::default()
|
let push_constant_range = vk::PushConstantRange::default()
|
||||||
.stage_flags(vk::ShaderStageFlags::VERTEX)
|
.stage_flags(vk::ShaderStageFlags::VERTEX)
|
||||||
.offset(0)
|
.offset(0)
|
||||||
.size(
|
.size(
|
||||||
u32::try_from(std::mem::size_of::<glam::Mat4>()).expect("Mat4 size exceeds u32 range"),
|
u32::try_from(std::mem::size_of::<glam::Mat4>() + std::mem::size_of::<[f32; 4]>())
|
||||||
|
.expect("push-constant size exceeds u32 range"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let layout_create_info = vk::PipelineLayoutCreateInfo::default()
|
let layout_create_info = vk::PipelineLayoutCreateInfo::default()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,26 @@ use crate::sync::SyncPrimitives;
|
||||||
use crate::{error::RendererError, mesh::Vertex};
|
use crate::{error::RendererError, mesh::Vertex};
|
||||||
use ash::{Device, Instance, khr, vk};
|
use ash::{Device, Instance, khr, vk};
|
||||||
use gpu_allocator::vulkan::{Allocation, Allocator};
|
use gpu_allocator::vulkan::{Allocation, Allocator};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Opaque, renderer-side identifier for one uploaded chunk mesh.
|
||||||
|
pub type MeshKey = (i32, i32, i32);
|
||||||
|
|
||||||
|
/// GPU resources for a single chunk mesh, drawn at a fixed world offset.
|
||||||
|
pub(crate) struct GpuMesh {
|
||||||
|
/// Buffer holding the chunk's vertex data.
|
||||||
|
pub(crate) vertex_buffer: vk::Buffer,
|
||||||
|
/// Backing allocation for [`GpuMesh::vertex_buffer`], freed when the mesh is removed.
|
||||||
|
pub(crate) vertex_allocation: Allocation,
|
||||||
|
/// Buffer holding the chunk's index data for indexed drawing.
|
||||||
|
pub(crate) index_buffer: vk::Buffer,
|
||||||
|
/// Backing allocation for [`GpuMesh::index_buffer`], freed when the mesh is removed.
|
||||||
|
pub(crate) index_allocation: Allocation,
|
||||||
|
/// Number of indices submitted in the mesh's `cmd_draw_indexed` call.
|
||||||
|
pub(crate) index_count: u32,
|
||||||
|
/// Chunk origin in world space (blocks); added to every vertex in the vertex shader.
|
||||||
|
pub(crate) world_offset: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
/// The core renderer structure holding the Vulkan resources.
|
/// The core renderer structure holding the Vulkan resources.
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
|
|
@ -56,15 +76,8 @@ pub struct Renderer {
|
||||||
pub(crate) graphics_pipeline: vk::Pipeline,
|
pub(crate) graphics_pipeline: vk::Pipeline,
|
||||||
/// Memory manager for GPU allocations.
|
/// Memory manager for GPU allocations.
|
||||||
pub(crate) allocator: Option<Allocator>,
|
pub(crate) allocator: Option<Allocator>,
|
||||||
/// Buffer containing the vertex data for the initial triangle.
|
/// Uploaded chunk meshes, keyed by an opaque renderer-side handle and drawn independently.
|
||||||
pub(crate) vertex_buffer: vk::Buffer,
|
pub(crate) chunk_meshes: HashMap<MeshKey, GpuMesh>,
|
||||||
/// Memory allocation for the vertex buffer.
|
|
||||||
pub(crate) vertex_allocation: Option<Allocation>,
|
|
||||||
/// Buffer containing the index data for indexed drawing.
|
|
||||||
pub(crate) index_buffer: vk::Buffer,
|
|
||||||
/// Memory allocation for the index buffer.
|
|
||||||
pub(crate) index_allocation: Option<Allocation>,
|
|
||||||
pub(crate) index_count: u32,
|
|
||||||
/// The depth image used for depth testing.
|
/// The depth image used for depth testing.
|
||||||
pub(crate) depth_image: vk::Image,
|
pub(crate) depth_image: vk::Image,
|
||||||
/// Image view for the depth buffer.
|
/// Image view for the depth buffer.
|
||||||
|
|
@ -293,11 +306,6 @@ impl Renderer {
|
||||||
};
|
};
|
||||||
self.device.cmd_set_scissor(cmd, 0, &[scissor]);
|
self.device.cmd_set_scissor(cmd, 0, &[scissor]);
|
||||||
|
|
||||||
self.device
|
|
||||||
.cmd_bind_vertex_buffers(cmd, 0, &[self.vertex_buffer], &[0]);
|
|
||||||
self.device
|
|
||||||
.cmd_bind_index_buffer(cmd, self.index_buffer, 0, vk::IndexType::UINT32);
|
|
||||||
|
|
||||||
let aspect =
|
let aspect =
|
||||||
f64::from(self.swapchain_extent.width) / f64::from(self.swapchain_extent.height);
|
f64::from(self.swapchain_extent.width) / f64::from(self.swapchain_extent.height);
|
||||||
|
|
||||||
|
|
@ -315,6 +323,7 @@ impl Renderer {
|
||||||
// The view matrix is supplied by the caller (the client's camera); the renderer owns only the projection, which depends on the swapchain aspect ratio it manages.
|
// The view matrix is supplied by the caller (the client's camera); the renderer owns only the projection, which depends on the swapchain aspect ratio it manages.
|
||||||
let mvp = projection * camera_view;
|
let mvp = projection * camera_view;
|
||||||
|
|
||||||
|
// The MVP is identical for every chunk this frame, so it is pushed once before the loop.
|
||||||
let mvp_bytes = bytemuck::cast_slice(mvp.as_ref());
|
let mvp_bytes = bytemuck::cast_slice(mvp.as_ref());
|
||||||
self.device.cmd_push_constants(
|
self.device.cmd_push_constants(
|
||||||
cmd,
|
cmd,
|
||||||
|
|
@ -324,8 +333,36 @@ impl Renderer {
|
||||||
mvp_bytes,
|
mvp_bytes,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.device
|
// The per-chunk offset occupies the push-constant range immediately after the 64-byte MVP.
|
||||||
.cmd_draw_indexed(cmd, self.index_count, 1, 0, 0, 0);
|
#[expect(
|
||||||
|
clippy::cast_possible_truncation,
|
||||||
|
reason = "size_of::<Mat4>() is 64 bytes, well within u32 range"
|
||||||
|
)]
|
||||||
|
let chunk_offset_byte = size_of::<glam::Mat4>() as u32;
|
||||||
|
|
||||||
|
for mesh in self.chunk_meshes.values() {
|
||||||
|
// The offset is padded to a vec4 to match the std140 layout of the push-constant block; only xyz is read by the shader.
|
||||||
|
let offset = [
|
||||||
|
mesh.world_offset[0],
|
||||||
|
mesh.world_offset[1],
|
||||||
|
mesh.world_offset[2],
|
||||||
|
0.0_f32,
|
||||||
|
];
|
||||||
|
self.device.cmd_push_constants(
|
||||||
|
cmd,
|
||||||
|
self.pipeline_layout,
|
||||||
|
vk::ShaderStageFlags::VERTEX,
|
||||||
|
chunk_offset_byte,
|
||||||
|
bytemuck::cast_slice(&offset),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.device
|
||||||
|
.cmd_bind_vertex_buffers(cmd, 0, &[mesh.vertex_buffer], &[0]);
|
||||||
|
self.device
|
||||||
|
.cmd_bind_index_buffer(cmd, mesh.index_buffer, 0, vk::IndexType::UINT32);
|
||||||
|
self.device
|
||||||
|
.cmd_draw_indexed(cmd, mesh.index_count, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -368,47 +405,36 @@ impl Renderer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces the currently rendering mesh with a new set of vertices and indices.
|
/// Uploads (or replaces) the mesh stored under `key`, positioned at `world_offset` (in blocks).
|
||||||
|
///
|
||||||
|
/// If a mesh already exists under `key`, its GPU resources are freed before the replacement is
|
||||||
|
/// uploaded.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns [`RendererError::AllocationError`] if GPU memory cannot be allocated, or [`RendererError::VulkanError`] if the vertex or index buffers cannot be created.
|
/// Returns [`RendererError::AllocatorMissing`] if the GPU allocator has been torn down,
|
||||||
|
/// [`RendererError::AllocationError`] if GPU memory cannot be allocated, or
|
||||||
|
/// [`RendererError::VulkanError`] if the vertex or index buffers cannot be created.
|
||||||
#[expect(
|
#[expect(
|
||||||
clippy::cast_possible_truncation,
|
clippy::cast_possible_truncation,
|
||||||
reason = "a chunk mesh's index count never approaches u32::MAX"
|
reason = "a chunk mesh's index count never approaches u32::MAX"
|
||||||
)]
|
)]
|
||||||
pub fn update_mesh(
|
pub fn insert_mesh(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
key: MeshKey,
|
||||||
vertices: &[Vertex],
|
vertices: &[Vertex],
|
||||||
indices: &[u32],
|
indices: &[u32],
|
||||||
|
world_offset: [f32; 3],
|
||||||
) -> Result<(), RendererError> {
|
) -> Result<(), RendererError> {
|
||||||
unsafe {
|
// Free any mesh already stored under this key before uploading its replacement.
|
||||||
let _ = self.device.device_wait_idle();
|
self.remove_mesh(key);
|
||||||
|
|
||||||
let allocator = self
|
|
||||||
.allocator
|
|
||||||
.as_mut()
|
|
||||||
.ok_or(RendererError::AllocatorMissing)?;
|
|
||||||
|
|
||||||
if let Some(alloc) = self.vertex_allocation.take() {
|
|
||||||
let _ = allocator.free(alloc);
|
|
||||||
}
|
|
||||||
self.device.destroy_buffer(self.vertex_buffer, None);
|
|
||||||
self.vertex_buffer = vk::Buffer::null();
|
|
||||||
|
|
||||||
if let Some(alloc) = self.index_allocation.take() {
|
|
||||||
let _ = allocator.free(alloc);
|
|
||||||
}
|
|
||||||
self.device.destroy_buffer(self.index_buffer, None);
|
|
||||||
self.index_buffer = vk::Buffer::null();
|
|
||||||
}
|
|
||||||
|
|
||||||
let allocator = self
|
let allocator = self
|
||||||
.allocator
|
.allocator
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or(RendererError::AllocatorMissing)?;
|
.ok_or(RendererError::AllocatorMissing)?;
|
||||||
|
|
||||||
let (v_buf, v_alloc) = create_gpu_buffer(
|
let (vertex_buffer, vertex_allocation) = create_gpu_buffer(
|
||||||
&self.device,
|
&self.device,
|
||||||
allocator,
|
allocator,
|
||||||
bytemuck::cast_slice(vertices),
|
bytemuck::cast_slice(vertices),
|
||||||
|
|
@ -416,7 +442,7 @@ impl Renderer {
|
||||||
"Chunk Vertex Buffer",
|
"Chunk Vertex Buffer",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (i_buf, i_alloc) = crate::create_gpu_buffer(
|
let (index_buffer, index_allocation) = create_gpu_buffer(
|
||||||
&self.device,
|
&self.device,
|
||||||
allocator,
|
allocator,
|
||||||
bytemuck::cast_slice(indices),
|
bytemuck::cast_slice(indices),
|
||||||
|
|
@ -424,14 +450,39 @@ impl Renderer {
|
||||||
"Chunk Index Buffer",
|
"Chunk Index Buffer",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.vertex_buffer = v_buf;
|
self.chunk_meshes.insert(
|
||||||
self.vertex_allocation = Some(v_alloc);
|
key,
|
||||||
self.index_buffer = i_buf;
|
GpuMesh {
|
||||||
self.index_allocation = Some(i_alloc);
|
vertex_buffer,
|
||||||
self.index_count = indices.len() as u32;
|
vertex_allocation,
|
||||||
|
index_buffer,
|
||||||
|
index_allocation,
|
||||||
|
index_count: indices.len() as u32,
|
||||||
|
world_offset,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Frees the GPU mesh stored under `key`. Does nothing if no mesh is present.
|
||||||
|
pub fn remove_mesh(&mut self, key: MeshKey) {
|
||||||
|
let Some(mesh) = self.chunk_meshes.remove(&key) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Waiting idle per removal is the simple, always-correct approach; in a bulk load/unload loop it serialises the GPU, so a single wait around the loop is preferable if this ever shows up as a measured bottleneck.
|
||||||
|
let _ = self.device.device_wait_idle();
|
||||||
|
|
||||||
|
if let Some(allocator) = self.allocator.as_mut() {
|
||||||
|
let _ = allocator.free(mesh.vertex_allocation);
|
||||||
|
let _ = allocator.free(mesh.index_allocation);
|
||||||
|
}
|
||||||
|
self.device.destroy_buffer(mesh.vertex_buffer, None);
|
||||||
|
self.device.destroy_buffer(mesh.index_buffer, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Renderer {
|
impl Drop for Renderer {
|
||||||
|
|
@ -443,16 +494,18 @@ impl Drop for Renderer {
|
||||||
self.device
|
self.device
|
||||||
.destroy_pipeline_layout(self.pipeline_layout, None);
|
.destroy_pipeline_layout(self.pipeline_layout, None);
|
||||||
|
|
||||||
|
// Drain the chunk meshes so each owned allocation can be freed and its buffers destroyed.
|
||||||
|
let meshes: Vec<GpuMesh> = self.chunk_meshes.drain().map(|(_, mesh)| mesh).collect();
|
||||||
if let Some(allocator) = self.allocator.as_mut() {
|
if let Some(allocator) = self.allocator.as_mut() {
|
||||||
if let Some(alloc) = self.vertex_allocation.take()
|
for mesh in meshes {
|
||||||
&& let Err(e) = allocator.free(alloc)
|
if let Err(e) = allocator.free(mesh.vertex_allocation) {
|
||||||
{
|
tracing::error!("Failed to free chunk vertex allocation: {e}");
|
||||||
tracing::error!("Failed to free vertex buffer allocation: {e}");
|
}
|
||||||
}
|
if let Err(e) = allocator.free(mesh.index_allocation) {
|
||||||
if let Some(alloc) = self.index_allocation.take()
|
tracing::error!("Failed to free chunk index allocation: {e}");
|
||||||
&& let Err(e) = allocator.free(alloc)
|
}
|
||||||
{
|
self.device.destroy_buffer(mesh.vertex_buffer, None);
|
||||||
tracing::error!("Failed to free index buffer allocation: {e}");
|
self.device.destroy_buffer(mesh.index_buffer, None);
|
||||||
}
|
}
|
||||||
if let Some(alloc) = self.depth_allocation.take()
|
if let Some(alloc) = self.depth_allocation.take()
|
||||||
&& let Err(e) = allocator.free(alloc)
|
&& let Err(e) = allocator.free(alloc)
|
||||||
|
|
@ -460,8 +513,6 @@ impl Drop for Renderer {
|
||||||
tracing::error!("Failed to free depth image allocation: {e}");
|
tracing::error!("Failed to free depth image allocation: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.device.destroy_buffer(self.vertex_buffer, None);
|
|
||||||
self.device.destroy_buffer(self.index_buffer, None);
|
|
||||||
self.device.destroy_image_view(self.depth_image_view, None);
|
self.device.destroy_image_view(self.depth_image_view, None);
|
||||||
self.device.destroy_image(self.depth_image, None);
|
self.device.destroy_image(self.depth_image, None);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue