From 4bd2bff03e24e45de9e4232b5cc3e6db4371c252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Gasi=C7=B9ski?= Date: Fri, 26 Jul 2024 23:55:48 +0200 Subject: [PATCH] Add depth buffer --- src/main.zig | 11 +-- src/utilities.zig | 2 +- src/vulkan_renderer.zig | 167 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 160 insertions(+), 20 deletions(-) diff --git a/src/main.zig b/src/main.zig index 98c8fd8..6849649 100644 --- a/src/main.zig +++ b/src/main.zig @@ -47,14 +47,11 @@ pub fn main() !void { angle -= 360.0; } - var first_model = zm.identity(); - var second_model = zm.identity(); + var first_model = zm.rotationZ(angle); + var second_model = zm.rotationZ(-angle * 2); - first_model = zm.mul(first_model, zm.rotationZ(angle)); - first_model = zm.mul(first_model, zm.translation(-2.0, 0.0, -5.0)); - - second_model = zm.mul(second_model, zm.rotationZ(-angle * 2)); - second_model = zm.mul(second_model, zm.translation(2.0, 0.0, -5.0)); + first_model = zm.mul(first_model, zm.translation(0.0, 0.0, -2.5)); + second_model = zm.mul(second_model, zm.translation(0.0, 0.0, -4.5)); try vulkan_renderer.updateModel(0, first_model); try vulkan_renderer.updateModel(1, second_model); diff --git a/src/utilities.zig b/src/utilities.zig index f99c00f..f9db076 100644 --- a/src/utilities.zig +++ b/src/utilities.zig @@ -36,7 +36,7 @@ pub const SwapchainImage = struct { image_view: vk.ImageView, }; -fn findMemoryTypeIndex(pdev: vk.PhysicalDevice, instance: Instance, allowed_types: u32, properties: vk.MemoryPropertyFlags) u32 { +pub fn findMemoryTypeIndex(pdev: vk.PhysicalDevice, instance: Instance, allowed_types: u32, properties: vk.MemoryPropertyFlags) u32 { // Get properties of physical device memory const memory_properties = instance.getPhysicalDeviceMemoryProperties(pdev); const mem_type_count = memory_properties.memory_type_count; diff --git a/src/vulkan_renderer.zig b/src/vulkan_renderer.zig index 5b7da9f..6a46d93 100644 --- a/src/vulkan_renderer.zig +++ b/src/vulkan_renderer.zig @@ -79,6 +79,10 @@ pub const VulkanRenderer = struct { swapchain_framebuffers: []vk.Framebuffer, command_buffers: []CommandBuffer, + depth_buffer_image: vk.Image, + depth_buffer_image_memory: vk.DeviceMemory, + depth_buffer_image_view: vk.ImageView, + // Descriptors descriptor_set_layout: vk.DescriptorSetLayout, push_constant_range: vk.PushConstantRange, @@ -99,6 +103,7 @@ pub const VulkanRenderer = struct { // Utilities swapchain_image_format: vk.Format, + depth_format: vk.Format, extent: vk.Extent2D, // Synchronisation @@ -126,6 +131,7 @@ pub const VulkanRenderer = struct { try self.getPhysicalDevice(); try self.createLogicalDevice(); try self.createSwapchain(); + try self.createDepthBufferImage(); try self.createRenderPass(); try self.createDescriptorSetLayout(); try self.createPushConstantRange(); @@ -153,16 +159,16 @@ pub const VulkanRenderer = struct { // Vertex Data var mesh_vertices = [_]Vertex{ .{ .pos = .{ -0.4, 0.4, 0.0 }, .col = .{ 1.0, 0.0, 0.0 } }, // 0 - .{ .pos = .{ -0.4, -0.4, 0.0 }, .col = .{ 0.0, 1.0, 0.0 } }, // 1 - .{ .pos = .{ 0.4, -0.4, 0.0 }, .col = .{ 0.0, 0.0, 1.0 } }, // 2 - .{ .pos = .{ 0.4, 0.4, 0.0 }, .col = .{ 0.0, 1.0, 0.0 } }, // 3 + .{ .pos = .{ -0.4, -0.4, 0.0 }, .col = .{ 1.0, 0.0, 0.0 } }, // 1 + .{ .pos = .{ 0.4, -0.4, 0.0 }, .col = .{ 1.0, 0.0, 0.0 } }, // 2 + .{ .pos = .{ 0.4, 0.4, 0.0 }, .col = .{ 1.0, 0.0, 0.0 } }, // 3 }; var mesh_vertices2 = [_]Vertex{ - .{ .pos = .{ -0.25, 0.6, 0.0 }, .col = .{ 1.0, 0.0, 0.0 } }, // 0 - .{ .pos = .{ -0.25, -0.6, 0.0 }, .col = .{ 0.0, 1.0, 0.0 } }, // 1 + .{ .pos = .{ -0.25, 0.6, 0.0 }, .col = .{ 0.0, 0.0, 1.0 } }, // 0 + .{ .pos = .{ -0.25, -0.6, 0.0 }, .col = .{ 0.0, 0.0, 1.0 } }, // 1 .{ .pos = .{ 0.25, -0.6, 0.0 }, .col = .{ 0.0, 0.0, 1.0 } }, // 2 - .{ .pos = .{ 0.25, 0.6, 0.0 }, .col = .{ 0.0, 1.0, 0.0 } }, // 3 + .{ .pos = .{ 0.25, 0.6, 0.0 }, .col = .{ 0.0, 0.0, 1.0 } }, // 3 }; // Index Data @@ -274,6 +280,10 @@ pub const VulkanRenderer = struct { self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null); } + self.device.destroyImageView(self.depth_buffer_image_view, null); + self.device.destroyImage(self.depth_buffer_image, null); + self.device.freeMemory(self.depth_buffer_image_memory, null); + self.device.destroyDescriptorPool(self.descriptor_pool, null); self.device.destroyDescriptorSetLayout(self.descriptor_set_layout, null); @@ -496,6 +506,7 @@ pub const VulkanRenderer = struct { } fn createRenderPass(self: *Self) !void { + // -- Attachments -- // Colour attachment of the render pass const colour_attachment: vk.AttachmentDescription = .{ .format = self.swapchain_image_format, // Format to use for attachment @@ -510,17 +521,36 @@ pub const VulkanRenderer = struct { .final_layout = vk.ImageLayout.present_src_khr, // Image data layout after render pass (to change to) }; + // Depth attachment of render pass + const depth_attachment: vk.AttachmentDescription = .{ + .format = self.depth_format, + .samples = .{ .@"1_bit" = true }, + .load_op = .clear, + .store_op = .dont_care, + .stencil_load_op = .dont_care, + .stencil_store_op = .dont_care, + .initial_layout = .undefined, + .final_layout = .depth_stencil_attachment_optimal, + }; + + // -- References -- // Attachment reference uses an attachment index that refers to index in the attachment list passed to render pass create info const colour_attachment_reference: vk.AttachmentReference = .{ .attachment = 0, .layout = vk.ImageLayout.color_attachment_optimal, }; + const depth_attachment_reference: vk.AttachmentReference = .{ + .attachment = 1, + .layout = vk.ImageLayout.depth_stencil_attachment_optimal, + }; + // Information about a particular subpass the render pass is using const subpass: vk.SubpassDescription = .{ .pipeline_bind_point = .graphics, // Pipeline type subpass is to be bound to .color_attachment_count = 1, .p_color_attachments = @ptrCast(&colour_attachment_reference), + .p_depth_stencil_attachment = &depth_attachment_reference, }; // Need to determine when layout transitions occur using subpass dependencies @@ -549,9 +579,12 @@ pub const VulkanRenderer = struct { }, }; + // Order matters + const render_pass_attachments = [_]vk.AttachmentDescription{ colour_attachment, depth_attachment }; + const render_pass_create_info: vk.RenderPassCreateInfo = .{ - .attachment_count = 1, - .p_attachments = @ptrCast(&colour_attachment), + .attachment_count = @intCast(render_pass_attachments.len), + .p_attachments = &render_pass_attachments, .subpass_count = 1, .p_subpasses = @ptrCast(&subpass), .dependency_count = @intCast(subpass_dependencies.len), @@ -592,6 +625,32 @@ pub const VulkanRenderer = struct { }; } + fn createDepthBufferImage(self: *Self) !void { + // Get supported depth buffer format + const formats = [_]vk.Format{ .d32_sfloat_s8_uint, .d32_sfloat, .d24_unorm_s8_uint }; + self.depth_format = chooseSupportedFormat( + self.physical_device, + self.instance, + &formats, + .optimal, + .{ .depth_stencil_attachment_bit = true }, + ) orelse return error.UnsupportedDepthBufferFormat; + + // Create depth buffer image + self.depth_buffer_image = try self.createImage( + self.extent.width, + self.extent.height, + self.depth_format, + .optimal, + .{ .depth_stencil_attachment_bit = true }, + .{ .device_local_bit = true }, + &self.depth_buffer_image_memory, + ); + + // Create depth buffer image view + self.depth_buffer_image_view = try self.createImageView(self.depth_buffer_image, self.depth_format, .{ .depth_bit = true }); + } + fn createGraphicsPipeline(self: *Self) !void { // Create shader modules const vert = try self.device.createShaderModule(&.{ @@ -759,7 +818,17 @@ pub const VulkanRenderer = struct { self.pipeline_layout = try self.device.createPipelineLayout(&pipeline_layout_create_info, null); // -- Depth stencil testing -- - // TODO: Set a depth stencil testing + const depth_stencil_create_info: vk.PipelineDepthStencilStateCreateInfo = .{ + .depth_test_enable = vk.TRUE, // Enable checking depth to determine fragment write + .depth_write_enable = vk.TRUE, // Enable writing to depth buffer to replace all values + .depth_compare_op = .less, // Comparison operation that allows an overwrite (is in front) + .depth_bounds_test_enable = vk.FALSE, // Depth bounds test: does the depth value exist between two bounds + .stencil_test_enable = vk.FALSE, // Enable stencil test + .front = undefined, + .back = undefined, + .min_depth_bounds = undefined, + .max_depth_bounds = undefined, + }; // -- Graphics pipeline creation -- const pipeline_create_info: vk.GraphicsPipelineCreateInfo = .{ @@ -772,7 +841,7 @@ pub const VulkanRenderer = struct { .p_rasterization_state = &rasterizer_create_info, .p_multisample_state = &multisampling_create_info, .p_color_blend_state = &colour_blending_create_info, - .p_depth_stencil_state = null, + .p_depth_stencil_state = &depth_stencil_create_info, .layout = self.pipeline_layout, // Pipeline layout the pipeline should use .render_pass = self.render_pass, // Renderpass description the pipeline is compatible with .subpass = 0, // Subpass of renderpass to use with pipeline @@ -795,7 +864,8 @@ pub const VulkanRenderer = struct { // Create a frammebuffer for each swapchain image for (self.swapchain_images, 0..) |swapchain_image, i| { - const attachments = [_]vk.ImageView{swapchain_image.image_view}; + // Order matters + const attachments = [_]vk.ImageView{ swapchain_image.image_view, self.depth_buffer_image_view }; const framebuffer_create_info: vk.FramebufferCreateInfo = .{ .render_pass = self.render_pass, // Render pass layout the frambuffer will be used with @@ -973,6 +1043,7 @@ pub const VulkanRenderer = struct { const clear_values = [_]vk.ClearValue{ .{ .color = .{ .float_32 = [4]f32{ 0.6, 0.65, 0.4, 1.0 } } }, + .{ .depth_stencil = .{ .depth = 1.0, .stencil = 1 } }, }; // Information about how to begin a render pass (only needed for graphical application) @@ -982,7 +1053,7 @@ pub const VulkanRenderer = struct { .offset = .{ .x = 0, .y = 0 }, // Start point of render pass in pixels .extent = self.extent, // Size of region to run render pass on (starting at offset) }, - .p_clear_values = &clear_values, // List of clear values (TODO: Depth attachment clear value) + .p_clear_values = &clear_values, // List of clear values .clear_value_count = @intCast(clear_values.len), .framebuffer = undefined, }; @@ -1234,6 +1305,54 @@ pub const VulkanRenderer = struct { }; } + fn createImage( + self: *Self, + width: u32, + height: u32, + format: vk.Format, + tiling: vk.ImageTiling, + use_flags: vk.ImageUsageFlags, + prop_flags: vk.MemoryPropertyFlags, + image_memory: *vk.DeviceMemory, + ) !vk.Image { + // -- Create Image -- + const image_create_info: vk.ImageCreateInfo = .{ + .image_type = .@"2d", // Type of image (1D, 2D or 3D) + .extent = .{ + .width = width, // Width of image extent + .height = height, // Height of image extent + .depth = 1, // Depth of image (just 1, no 3D aspecct) + }, + .mip_levels = 1, // Number of mipmap levels + .array_layers = 1, // Number of level in image array + .format = format, // Format type of image + .tiling = tiling, // How image data should be tiled (arranged for optimal reading) + .initial_layout = .undefined, // Layout of image data on creation + .usage = use_flags, // Bit flags defining what image will be used for + .samples = .{ .@"1_bit" = true }, // Number of samples for multi-sampling + .sharing_mode = .exclusive, // Whether image can be shared between queues + }; + + const image = try self.device.createImage(&image_create_info, null); + + // -- Create memory for image -- + // Get memory requirements for a type of image + const memory_requirements = self.device.getImageMemoryRequirements(image); + + // Allocate memory using image requirements and user-defined properties + const memory_alloc_info: vk.MemoryAllocateInfo = .{ + .allocation_size = memory_requirements.size, + .memory_type_index = Utilities.findMemoryTypeIndex(self.physical_device, self.instance, memory_requirements.memory_type_bits, prop_flags), + }; + + image_memory.* = try self.device.allocateMemory(&memory_alloc_info, null); + + // Connect memory to image + try self.device.bindImageMemory(image, image_memory.*, 0); + + return image; + } + fn createImageView(self: Self, image: vk.Image, format: vk.Format, aspect_flags: vk.ImageAspectFlags) !vk.ImageView { const image_view_create_info: vk.ImageViewCreateInfo = .{ .image = image, @@ -1311,6 +1430,30 @@ fn chooseSwapExtent(window: *sdl.Window, surface_capabilities: vk.SurfaceCapabil return extent; } +fn chooseSupportedFormat( + pdev: vk.PhysicalDevice, + instance: Instance, + formats: []const vk.Format, + tiling: vk.ImageTiling, + feature_flags: vk.FormatFeatureFlags, +) ?vk.Format { + // Loop through the options and find a compatible one + + // Depending on tiling choice. Need to check for different bit flag + for (formats) |format| { + // Get properties for given format on this device + const properties = instance.getPhysicalDeviceFormatProperties(pdev, format); + + if (tiling == .linear and properties.linear_tiling_features.contains(feature_flags)) { + return format; + } else if (tiling == .optimal and properties.optimal_tiling_features.contains(feature_flags)) { + return format; + } + } + + return null; +} + // Validation layers stuff fn createDebugMessenger(instance: Instance) !vk.DebugUtilsMessengerEXT { const debug_create_info = getDebugUtilsCreateInfo();