feat(renderer): implement depth testing and indexed 3d rendering
This commit is contained in:
parent
32ef9ca4c6
commit
38077c980d
|
|
@ -1,9 +1,9 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec2 in_tex_coord;
|
layout(location = 0) in vec3 frag_color;
|
||||||
|
|
||||||
layout(location = 0) out vec4 out_color;
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
void main () {
|
void main () {
|
||||||
out_color = vec4(in_tex_coord, 1.0, 1.0);
|
out_color = vec4(frag_color, 1.0);
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3874fa1c7782cba103fa7df1505c900506ab27a19a00f6e15c073c6a96b0f34c
|
oid sha256:f3f5c483dddf88cd90dbb62aa579c7f815b7ff304407d27c19a9a52cc450b4bb
|
||||||
size 556
|
size 572
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec3 in_position;
|
layout(location = 0) in vec3 in_position;
|
||||||
layout(location = 1) in vec2 in_tex_coord;
|
layout(location = 1) in vec3 in_color;
|
||||||
|
|
||||||
layout(location = 0) out vec2 out_tex_coord;
|
layout(location = 0) out vec3 frag_color;
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
layout(push_constant) uniform PushConstants {
|
||||||
mat4 mvp;
|
mat4 mvp;
|
||||||
|
|
@ -13,5 +13,5 @@ layout(push_constant) uniform PushConstants {
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = push_constants.mvp * vec4(in_position, 1.0);
|
gl_Position = push_constants.mvp * vec4(in_position, 1.0);
|
||||||
|
|
||||||
out_tex_coord = in_tex_coord;
|
frag_color = in_color;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:dcac088d9ed53209a22008ab3ad49d88dbb8e8f6b4b2a17fef5a8368856019ea
|
oid sha256:27d230b5332e98c109e6c8c0da8a316f341d82886c1c0a6d568f512b50e31d86
|
||||||
size 1360
|
size 1320
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,15 @@ mod sync;
|
||||||
pub const MAX_FRAMES_IN_FLIGHT: usize = 3;
|
pub const MAX_FRAMES_IN_FLIGHT: usize = 3;
|
||||||
|
|
||||||
use ash::{Entry, vk};
|
use ash::{Entry, vk};
|
||||||
|
use gpu_allocator::vulkan::{Allocation, Allocator};
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
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::Renderer;
|
||||||
|
|
||||||
|
use crate::mesh::Vertex;
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
/// Initializes the Vulkan renderer.
|
/// Initializes the Vulkan renderer.
|
||||||
///
|
///
|
||||||
|
|
@ -113,8 +116,11 @@ 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)?;
|
||||||
|
|
||||||
// 13. Vertex Buffer Initialization
|
let (vertex_buffer, vertex_allocation, index_buffer, index_allocation) =
|
||||||
let (vertex_buffer, vertex_allocation) = create_vertex_buffer(&device, &mut allocator)?;
|
create_geometry(&device, &mut allocator)?;
|
||||||
|
|
||||||
|
let (depth_image, depth_allocation, depth_image_view) =
|
||||||
|
create_depth_resources(&device, &mut allocator, swapchain_extent)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
_entry: entry,
|
_entry: entry,
|
||||||
|
|
@ -136,6 +142,11 @@ impl Renderer {
|
||||||
command_pool,
|
command_pool,
|
||||||
command_buffers,
|
command_buffers,
|
||||||
allocator,
|
allocator,
|
||||||
|
index_buffer,
|
||||||
|
index_allocation,
|
||||||
|
depth_image,
|
||||||
|
depth_allocation,
|
||||||
|
depth_image_view,
|
||||||
pipeline_layout,
|
pipeline_layout,
|
||||||
graphics_pipeline,
|
graphics_pipeline,
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
|
|
@ -166,36 +177,161 @@ fn create_allocator(
|
||||||
Ok(allocator)
|
Ok(allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a vertex buffer and populates it with initial triangle data.
|
/// Creates the 3D geometry buffers (vertex and index) for a cube.
|
||||||
#[allow(clippy::expect_used)]
|
fn create_geometry(
|
||||||
fn create_vertex_buffer(
|
|
||||||
device: &ash::Device,
|
device: &ash::Device,
|
||||||
allocator: &mut gpu_allocator::vulkan::Allocator,
|
allocator: &mut Allocator,
|
||||||
) -> Result<(vk::Buffer, gpu_allocator::vulkan::Allocation), RendererError> {
|
) -> Result<(vk::Buffer, Allocation, vk::Buffer, Allocation), RendererError> {
|
||||||
let vertices = [
|
let vertices = [
|
||||||
mesh::Vertex {
|
// Front face
|
||||||
position: [0.0, -0.5, 0.0],
|
Vertex {
|
||||||
tex_coord: [0.5, 0.0],
|
position: [-0.5, -0.5, 0.5],
|
||||||
|
color: [1.0, 0.0, 0.0],
|
||||||
},
|
},
|
||||||
mesh::Vertex {
|
Vertex {
|
||||||
position: [-0.5, 0.5, 0.0],
|
position: [0.5, -0.5, 0.5],
|
||||||
tex_coord: [0.0, 1.0],
|
color: [0.0, 1.0, 0.0],
|
||||||
},
|
},
|
||||||
mesh::Vertex {
|
Vertex {
|
||||||
position: [0.5, 0.5, 0.0],
|
position: [0.5, 0.5, 0.5],
|
||||||
tex_coord: [1.0, 1.0],
|
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).
|
||||||
|
fn create_depth_resources(
|
||||||
|
device: &ash::Device,
|
||||||
|
allocator: &mut Allocator,
|
||||||
|
extent: vk::Extent2D,
|
||||||
|
) -> Result<(vk::Image, Allocation, vk::ImageView), RendererError> {
|
||||||
|
let depth_format = vk::Format::D32_SFLOAT;
|
||||||
|
|
||||||
|
let image_create_info = vk::ImageCreateInfo::default()
|
||||||
|
.image_type(vk::ImageType::TYPE_2D)
|
||||||
|
.format(depth_format)
|
||||||
|
.extent(vk::Extent3D {
|
||||||
|
width: extent.width,
|
||||||
|
height: extent.height,
|
||||||
|
depth: 1,
|
||||||
|
})
|
||||||
|
.mip_levels(1)
|
||||||
|
.array_layers(1)
|
||||||
|
.samples(vk::SampleCountFlags::TYPE_1)
|
||||||
|
.tiling(vk::ImageTiling::OPTIMAL)
|
||||||
|
.usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
|
||||||
|
.sharing_mode(vk::SharingMode::EXCLUSIVE)
|
||||||
|
.initial_layout(vk::ImageLayout::UNDEFINED);
|
||||||
|
|
||||||
|
let depth_image = unsafe { device.create_image(&image_create_info, None)? };
|
||||||
|
|
||||||
|
let requirements = unsafe { device.get_image_memory_requirements(depth_image) };
|
||||||
|
|
||||||
|
let depth_allocation = allocator.allocate(&gpu_allocator::vulkan::AllocationCreateDesc {
|
||||||
|
name: "Depth Image",
|
||||||
|
requirements,
|
||||||
|
location: gpu_allocator::MemoryLocation::GpuOnly,
|
||||||
|
linear: false,
|
||||||
|
allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
device.bind_image_memory(
|
||||||
|
depth_image,
|
||||||
|
depth_allocation.memory(),
|
||||||
|
depth_allocation.offset(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let view_create_info = vk::ImageViewCreateInfo::default()
|
||||||
|
.image(depth_image)
|
||||||
|
.view_type(vk::ImageViewType::TYPE_2D)
|
||||||
|
.format(depth_format)
|
||||||
|
.subresource_range(vk::ImageSubresourceRange {
|
||||||
|
aspect_mask: vk::ImageAspectFlags::DEPTH,
|
||||||
|
base_mip_level: 0,
|
||||||
|
level_count: 1,
|
||||||
|
base_array_layer: 0,
|
||||||
|
layer_count: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
let depth_image_view = unsafe { device.create_image_view(&view_create_info, None)? };
|
||||||
|
|
||||||
|
Ok((depth_image, depth_allocation, depth_image_view))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to create and populate a GPU buffer.
|
||||||
|
fn create_gpu_buffer(
|
||||||
|
device: &ash::Device,
|
||||||
|
allocator: &mut Allocator,
|
||||||
|
data: &[u8],
|
||||||
|
usage: vk::BufferUsageFlags,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<(vk::Buffer, Allocation), RendererError> {
|
||||||
|
let size = data.len() as u64;
|
||||||
|
|
||||||
let buffer_info = vk::BufferCreateInfo::default()
|
let buffer_info = vk::BufferCreateInfo::default()
|
||||||
.size(u64::try_from(std::mem::size_of_val(&vertices)).expect("Vertices size exceeds u64"))
|
.size(size)
|
||||||
.usage(vk::BufferUsageFlags::VERTEX_BUFFER);
|
.usage(usage)
|
||||||
|
.sharing_mode(vk::SharingMode::EXCLUSIVE);
|
||||||
|
|
||||||
let vertex_buffer = unsafe { device.create_buffer(&buffer_info, None)? };
|
let buffer = unsafe { device.create_buffer(&buffer_info, None)? };
|
||||||
let requirements = unsafe { device.get_buffer_memory_requirements(vertex_buffer) };
|
|
||||||
|
|
||||||
let vertex_allocation = allocator.allocate(&gpu_allocator::vulkan::AllocationCreateDesc {
|
let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
|
||||||
name: "Vertex Buffer",
|
let allocation = allocator.allocate(&gpu_allocator::vulkan::AllocationCreateDesc {
|
||||||
|
name,
|
||||||
requirements,
|
requirements,
|
||||||
location: gpu_allocator::MemoryLocation::CpuToGpu,
|
location: gpu_allocator::MemoryLocation::CpuToGpu,
|
||||||
linear: true,
|
linear: true,
|
||||||
|
|
@ -203,23 +339,17 @@ fn create_vertex_buffer(
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
device.bind_buffer_memory(
|
device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?;
|
||||||
vertex_buffer,
|
}
|
||||||
vertex_allocation.memory(),
|
|
||||||
vertex_allocation.offset(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let ptr = vertex_allocation
|
let ptr = allocation
|
||||||
.mapped_ptr()
|
.mapped_ptr()
|
||||||
.ok_or(RendererError::NoSuitableGpu)? // Should have a better error but for now
|
.ok_or(RendererError::NoSuitableGpu)?
|
||||||
.as_ptr();
|
.as_ptr();
|
||||||
|
|
||||||
std::ptr::copy_nonoverlapping(
|
unsafe {
|
||||||
vertices.as_ptr().cast::<u8>(),
|
std::ptr::copy_nonoverlapping(data.as_ptr(), ptr.cast(), data.len());
|
||||||
ptr.cast::<u8>(),
|
|
||||||
std::mem::size_of_val(&vertices),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((vertex_buffer, vertex_allocation))
|
Ok((buffer, allocation))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@ use bytemuck::{Pod, Zeroable};
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
/// 3D position of the vertex (X, Y, Z).
|
/// 3D position of the vertex (X, Y, Z).
|
||||||
pub position: [f32; 3],
|
pub position: [f32; 3],
|
||||||
/// 2D texture coordinates (U, V).
|
pub color: [f32; 3],
|
||||||
pub tex_coord: [f32; 2],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vertex {
|
impl Vertex {
|
||||||
|
|
@ -42,15 +41,11 @@ impl Vertex {
|
||||||
.location(0)
|
.location(0)
|
||||||
.format(ash::vk::Format::R32G32B32_SFLOAT)
|
.format(ash::vk::Format::R32G32B32_SFLOAT)
|
||||||
.offset(0),
|
.offset(0),
|
||||||
// Location 1: tex_coord (vec2 -> R32G32_SFLOAT)
|
|
||||||
ash::vk::VertexInputAttributeDescription::default()
|
ash::vk::VertexInputAttributeDescription::default()
|
||||||
.binding(0)
|
.binding(0)
|
||||||
.location(1)
|
.location(1)
|
||||||
.format(ash::vk::Format::R32G32_SFLOAT)
|
.format(ash::vk::Format::R32G32B32_SFLOAT)
|
||||||
.offset(
|
.offset(12),
|
||||||
u32::try_from(std::mem::size_of::<[f32; 3]>())
|
|
||||||
.expect("Vertex offset exceeds u32 range"),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,16 @@ pub fn create_graphics_pipeline(
|
||||||
vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
|
vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
|
||||||
|
|
||||||
let color_formats = [color_format];
|
let color_formats = [color_format];
|
||||||
let mut rendering_info =
|
let mut rendering_info = vk::PipelineRenderingCreateInfo::default()
|
||||||
vk::PipelineRenderingCreateInfo::default().color_attachment_formats(&color_formats);
|
.color_attachment_formats(&color_formats)
|
||||||
|
.depth_attachment_format(vk::Format::D32_SFLOAT);
|
||||||
|
|
||||||
|
let depth_stencil_state = &vk::PipelineDepthStencilStateCreateInfo::default()
|
||||||
|
.depth_test_enable(true)
|
||||||
|
.depth_write_enable(true)
|
||||||
|
.depth_compare_op(vk::CompareOp::LESS)
|
||||||
|
.depth_bounds_test_enable(false)
|
||||||
|
.stencil_test_enable(false);
|
||||||
|
|
||||||
// 3. Finalize Pipeline Creation
|
// 3. Finalize Pipeline Creation
|
||||||
let pipeline_info = vk::GraphicsPipelineCreateInfo::default()
|
let pipeline_info = vk::GraphicsPipelineCreateInfo::default()
|
||||||
|
|
@ -124,7 +132,8 @@ pub fn create_graphics_pipeline(
|
||||||
.multisample_state(&multisampling)
|
.multisample_state(&multisampling)
|
||||||
.color_blend_state(&color_blending)
|
.color_blend_state(&color_blending)
|
||||||
.dynamic_state(&dynamic_state_info)
|
.dynamic_state(&dynamic_state_info)
|
||||||
.layout(layout);
|
.layout(layout)
|
||||||
|
.depth_stencil_state(depth_stencil_state);
|
||||||
|
|
||||||
let pipeline = unsafe {
|
let pipeline = unsafe {
|
||||||
device
|
device
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ pub struct Renderer {
|
||||||
pub(crate) vertex_buffer: vk::Buffer,
|
pub(crate) vertex_buffer: vk::Buffer,
|
||||||
/// Memory allocation for the vertex buffer.
|
/// Memory allocation for the vertex buffer.
|
||||||
pub(crate) vertex_allocation: Allocation,
|
pub(crate) vertex_allocation: 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: Allocation,
|
||||||
|
/// The depth image used for depth testing.
|
||||||
|
pub(crate) depth_image: vk::Image,
|
||||||
|
/// Image view for the depth buffer.
|
||||||
|
pub(crate) depth_image_view: vk::ImageView,
|
||||||
|
/// Memory allocation for the depth image.
|
||||||
|
pub(crate) depth_allocation: Allocation,
|
||||||
/// Synchronization primitives for frame-by-frame execution.
|
/// Synchronization primitives for frame-by-frame execution.
|
||||||
pub(crate) sync: SyncPrimitives,
|
pub(crate) sync: SyncPrimitives,
|
||||||
/// Index of the current frame being processed (0 to `crate::MAX_FRAMES_IN_FLIGHT` - 1).
|
/// Index of the current frame being processed (0 to `crate::MAX_FRAMES_IN_FLIGHT` - 1).
|
||||||
|
|
@ -99,7 +109,95 @@ impl Renderer {
|
||||||
let image = self.swapchain_images[image_index as usize];
|
let image = self.swapchain_images[image_index as usize];
|
||||||
let view = self.swapchain_image_views[image_index as usize];
|
let view = self.swapchain_image_views[image_index as usize];
|
||||||
|
|
||||||
// 4. Transition the swapchain image to a layout suitable for drawing
|
// 4. Record the actual rendering commands
|
||||||
|
self.record_commands(cmd, view, image)?;
|
||||||
|
|
||||||
|
// 5. Submit the work to the GPU
|
||||||
|
let submit_info = vk::SubmitInfo::default()
|
||||||
|
.wait_semaphores(std::slice::from_ref(&image_available_semaphore))
|
||||||
|
.wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT])
|
||||||
|
.command_buffers(std::slice::from_ref(&cmd))
|
||||||
|
.signal_semaphores(std::slice::from_ref(&render_finished_semaphore));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.device
|
||||||
|
.queue_submit(self.graphics_queue, &[submit_info], in_flight_fence)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Present the result to the screen
|
||||||
|
let present_info = vk::PresentInfoKHR::default()
|
||||||
|
.wait_semaphores(std::slice::from_ref(&render_finished_semaphore))
|
||||||
|
.swapchains(std::slice::from_ref(&self.swapchain))
|
||||||
|
.image_indices(std::slice::from_ref(&image_index));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.swapchain_loader
|
||||||
|
.queue_present(self.graphics_queue, &present_info)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance the frame index for the next call
|
||||||
|
self.current_frame = (self.current_frame + 1) % crate::MAX_FRAMES_IN_FLIGHT;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Records the drawing commands into the given command buffer.
|
||||||
|
fn record_commands(
|
||||||
|
&self,
|
||||||
|
cmd: vk::CommandBuffer,
|
||||||
|
view: vk::ImageView,
|
||||||
|
image: vk::Image,
|
||||||
|
) -> Result<(), RendererError> {
|
||||||
|
// Transition layouts for drawing
|
||||||
|
self.transition_to_draw_layout(cmd, image);
|
||||||
|
|
||||||
|
// Begin rendering
|
||||||
|
let color_attachment = vk::RenderingAttachmentInfo::default()
|
||||||
|
.image_view(view)
|
||||||
|
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||||
|
.load_op(vk::AttachmentLoadOp::CLEAR)
|
||||||
|
.store_op(vk::AttachmentStoreOp::STORE)
|
||||||
|
.clear_value(vk::ClearValue {
|
||||||
|
color: vk::ClearColorValue {
|
||||||
|
float32: [0.1, 0.2, 0.4, 1.0],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let depth_attachment = vk::RenderingAttachmentInfo::default()
|
||||||
|
.image_view(self.depth_image_view)
|
||||||
|
.image_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
|
||||||
|
.load_op(vk::AttachmentLoadOp::CLEAR)
|
||||||
|
.store_op(vk::AttachmentStoreOp::STORE)
|
||||||
|
.clear_value(vk::ClearValue {
|
||||||
|
depth_stencil: vk::ClearDepthStencilValue {
|
||||||
|
depth: 1.0,
|
||||||
|
stencil: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let rendering_info = vk::RenderingInfo::default()
|
||||||
|
.render_area(vk::Rect2D {
|
||||||
|
offset: vk::Offset2D { x: 0, y: 0 },
|
||||||
|
extent: self.swapchain_extent,
|
||||||
|
})
|
||||||
|
.layer_count(1)
|
||||||
|
.color_attachments(std::slice::from_ref(&color_attachment))
|
||||||
|
.depth_attachment(&depth_attachment);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.device.cmd_begin_rendering(cmd, &rendering_info);
|
||||||
|
self.issue_draw_calls(cmd);
|
||||||
|
self.device.cmd_end_rendering(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transition back to present
|
||||||
|
self.transition_to_present_layout(cmd, image)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transitions the swapchain and depth images to layouts suitable for drawing.
|
||||||
|
fn transition_to_draw_layout(&self, cmd: vk::CommandBuffer, image: vk::Image) {
|
||||||
let range = vk::ImageSubresourceRange {
|
let range = vk::ImageSubresourceRange {
|
||||||
aspect_mask: vk::ImageAspectFlags::COLOR,
|
aspect_mask: vk::ImageAspectFlags::COLOR,
|
||||||
base_mip_level: 0,
|
base_mip_level: 0,
|
||||||
|
|
@ -118,38 +216,105 @@ impl Renderer {
|
||||||
.old_layout(vk::ImageLayout::UNDEFINED)
|
.old_layout(vk::ImageLayout::UNDEFINED)
|
||||||
.new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
|
.new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
let dependency_info = vk::DependencyInfo::default()
|
let depth_range = vk::ImageSubresourceRange {
|
||||||
.image_memory_barriers(std::slice::from_ref(&barrier_to_draw));
|
aspect_mask: vk::ImageAspectFlags::DEPTH,
|
||||||
|
base_mip_level: 0,
|
||||||
|
level_count: 1,
|
||||||
|
base_array_layer: 0,
|
||||||
|
layer_count: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let depth_barrier = vk::ImageMemoryBarrier2::default()
|
||||||
|
.image(self.depth_image)
|
||||||
|
.subresource_range(depth_range)
|
||||||
|
.src_stage_mask(vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS)
|
||||||
|
.src_access_mask(vk::AccessFlags2::empty())
|
||||||
|
.dst_stage_mask(vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS)
|
||||||
|
.dst_access_mask(vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE)
|
||||||
|
.old_layout(vk::ImageLayout::UNDEFINED)
|
||||||
|
.new_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
|
let barriers = [barrier_to_draw, depth_barrier];
|
||||||
|
let dependency_info = vk::DependencyInfo::default().image_memory_barriers(&barriers);
|
||||||
|
|
||||||
unsafe { self.device.cmd_pipeline_barrier2(cmd, &dependency_info) };
|
unsafe { self.device.cmd_pipeline_barrier2(cmd, &dependency_info) };
|
||||||
|
|
||||||
// 5. Begin Dynamic Rendering with a clear color
|
|
||||||
let color_attachment = vk::RenderingAttachmentInfo::default()
|
|
||||||
.image_view(view)
|
|
||||||
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
|
||||||
.load_op(vk::AttachmentLoadOp::CLEAR)
|
|
||||||
.store_op(vk::AttachmentStoreOp::STORE)
|
|
||||||
.clear_value(vk::ClearValue {
|
|
||||||
color: vk::ClearColorValue {
|
|
||||||
float32: [0.1, 0.2, 0.4, 1.0], // Project Catalyst Blue
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let rendering_info = vk::RenderingInfo::default()
|
|
||||||
.render_area(vk::Rect2D {
|
|
||||||
offset: vk::Offset2D { x: 0, y: 0 },
|
|
||||||
extent: self.swapchain_extent,
|
|
||||||
})
|
|
||||||
.layer_count(1)
|
|
||||||
.color_attachments(std::slice::from_ref(&color_attachment));
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.device.cmd_begin_rendering(cmd, &rendering_info);
|
|
||||||
// Future draw calls will go here
|
|
||||||
self.device.cmd_end_rendering(cmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Transition the image back to Present layout
|
/// Issues the actual draw calls for the frame.
|
||||||
|
fn issue_draw_calls(&self, cmd: vk::CommandBuffer) {
|
||||||
|
unsafe {
|
||||||
|
self.device.cmd_bind_pipeline(
|
||||||
|
cmd,
|
||||||
|
vk::PipelineBindPoint::GRAPHICS,
|
||||||
|
self.graphics_pipeline,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
let viewport = vk::Viewport {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: self.swapchain_extent.width as f32,
|
||||||
|
height: self.swapchain_extent.height as f32,
|
||||||
|
min_depth: 0.0,
|
||||||
|
max_depth: 1.0,
|
||||||
|
};
|
||||||
|
self.device.cmd_set_viewport(cmd, 0, &[viewport]);
|
||||||
|
|
||||||
|
let scissor = vk::Rect2D {
|
||||||
|
offset: vk::Offset2D { x: 0, y: 0 },
|
||||||
|
extent: self.swapchain_extent,
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
let aspect =
|
||||||
|
f64::from(self.swapchain_extent.width) / f64::from(self.swapchain_extent.height);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
let mut projection =
|
||||||
|
glam::Mat4::perspective_rh(45.0_f32.to_radians(), aspect as f32, 0.1, 100.0);
|
||||||
|
projection.col_mut(1).y *= -1.0;
|
||||||
|
|
||||||
|
let view = glam::Mat4::look_at_rh(
|
||||||
|
glam::vec3(2.0, 2.0, 2.0),
|
||||||
|
glam::vec3(0.0, 0.0, 0.0),
|
||||||
|
glam::vec3(0.0, 1.0, 0.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
let model = glam::Mat4::from_rotation_y(0.0);
|
||||||
|
let mvp = projection * view * model;
|
||||||
|
|
||||||
|
let mvp_bytes = bytemuck::cast_slice(mvp.as_ref());
|
||||||
|
self.device.cmd_push_constants(
|
||||||
|
cmd,
|
||||||
|
self.pipeline_layout,
|
||||||
|
vk::ShaderStageFlags::VERTEX,
|
||||||
|
0,
|
||||||
|
mvp_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.device.cmd_draw_indexed(cmd, 36, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transitions the swapchain image back to the presentation layout.
|
||||||
|
fn transition_to_present_layout(
|
||||||
|
&self,
|
||||||
|
cmd: vk::CommandBuffer,
|
||||||
|
image: vk::Image,
|
||||||
|
) -> Result<(), RendererError> {
|
||||||
|
let range = vk::ImageSubresourceRange {
|
||||||
|
aspect_mask: vk::ImageAspectFlags::COLOR,
|
||||||
|
base_mip_level: 0,
|
||||||
|
level_count: 1,
|
||||||
|
base_array_layer: 0,
|
||||||
|
layer_count: 1,
|
||||||
|
};
|
||||||
|
|
||||||
let barrier_to_present = vk::ImageMemoryBarrier2::default()
|
let barrier_to_present = vk::ImageMemoryBarrier2::default()
|
||||||
.image(image)
|
.image(image)
|
||||||
.subresource_range(range)
|
.subresource_range(range)
|
||||||
|
|
@ -168,32 +333,6 @@ impl Renderer {
|
||||||
self.device.end_command_buffer(cmd)?;
|
self.device.end_command_buffer(cmd)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Submit the work to the GPU
|
|
||||||
let submit_info = vk::SubmitInfo::default()
|
|
||||||
.wait_semaphores(std::slice::from_ref(&image_available_semaphore))
|
|
||||||
.wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT])
|
|
||||||
.command_buffers(std::slice::from_ref(&cmd))
|
|
||||||
.signal_semaphores(std::slice::from_ref(&render_finished_semaphore));
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.device
|
|
||||||
.queue_submit(self.graphics_queue, &[submit_info], in_flight_fence)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Present the result to the screen
|
|
||||||
let present_info = vk::PresentInfoKHR::default()
|
|
||||||
.wait_semaphores(std::slice::from_ref(&render_finished_semaphore))
|
|
||||||
.swapchains(std::slice::from_ref(&self.swapchain))
|
|
||||||
.image_indices(std::slice::from_ref(&image_index));
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.swapchain_loader
|
|
||||||
.queue_present(self.graphics_queue, &present_info)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance the frame index for the next call
|
|
||||||
self.current_frame = (self.current_frame + 1) % crate::MAX_FRAMES_IN_FLIGHT;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -213,9 +352,25 @@ impl Drop for Renderer {
|
||||||
{
|
{
|
||||||
tracing::error!("Failed to free vertex buffer allocation: {e}");
|
tracing::error!("Failed to free vertex buffer allocation: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.device.destroy_buffer(self.vertex_buffer, None);
|
self.device.destroy_buffer(self.vertex_buffer, None);
|
||||||
|
|
||||||
|
if let Err(e) = self
|
||||||
|
.allocator
|
||||||
|
.free(std::ptr::read(&raw const self.index_allocation))
|
||||||
|
{
|
||||||
|
tracing::error!("Failed to free index buffer allocation: {e}");
|
||||||
|
}
|
||||||
|
self.device.destroy_buffer(self.index_buffer, None);
|
||||||
|
|
||||||
|
self.device.destroy_image_view(self.depth_image_view, None);
|
||||||
|
if let Err(e) = self
|
||||||
|
.allocator
|
||||||
|
.free(std::ptr::read(&raw const self.depth_allocation))
|
||||||
|
{
|
||||||
|
tracing::error!("Failed to free depth image allocation: {e}");
|
||||||
|
}
|
||||||
|
self.device.destroy_image(self.depth_image, None);
|
||||||
|
|
||||||
self.device.destroy_command_pool(self.command_pool, None);
|
self.device.destroy_command_pool(self.command_pool, None);
|
||||||
|
|
||||||
// Use the safe cleanup function from sync module
|
// Use the safe cleanup function from sync module
|
||||||
|
|
@ -237,9 +392,9 @@ impl Drop for Renderer {
|
||||||
// Destroy the surface
|
// Destroy the surface
|
||||||
self.surface_loader.destroy_surface(self.surface, None);
|
self.surface_loader.destroy_surface(self.surface, None);
|
||||||
|
|
||||||
// Destroy the debug messenger if we created one
|
// Destroy the debug messenger if it exists
|
||||||
if let Some(utils) = &self.debug_utils {
|
if let Some(debug_utils) = self.debug_utils.as_ref() {
|
||||||
utils.destroy_debug_utils_messenger(self.debug_messenger, None);
|
debug_utils.destroy_debug_utils_messenger(self.debug_messenger, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy the instance
|
// Destroy the instance
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue