diff options
| author | vimene <vincent.menegaux@gmail.com> | 2025-12-28 17:34:59 +0100 |
|---|---|---|
| committer | vimene <vincent.menegaux@gmail.com> | 2025-12-28 17:34:59 +0100 |
| commit | dd187445972989dc44428e8cf185964da9e5c0c4 (patch) | |
| tree | 17e23c6bdce4dd686bac2d373243bce99849eb29 /src/engine.cpp | |
| parent | 8874f4f4c6f95c2db7654c769e7747b3e3cf863a (diff) | |
| download | engine-dd187445972989dc44428e8cf185964da9e5c0c4.tar.gz | |
added depth buffer
Diffstat (limited to 'src/engine.cpp')
| -rw-r--r-- | src/engine.cpp | 232 |
1 files changed, 213 insertions, 19 deletions
diff --git a/src/engine.cpp b/src/engine.cpp index 8c517c0..0cf3266 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -329,6 +329,7 @@ struct PhysicalDeviceEntry { uint32_t graphics_queue_family_index, present_queue_family_index; VkPhysicalDeviceProperties2 props; VkPhysicalDeviceFeatures2 features; + VkFormat depth_format; }; static bool check_validation_layer_support() { @@ -411,6 +412,41 @@ static std::tuple<std::optional<uint32_t>, std::optional<uint32_t>> find_queue_f return { graphics_queue_family_index, present_queue_family_index }; } +static std::optional<VkFormat> find_format(VkPhysicalDevice physical_device, const std::vector<VkFormat>& formats, + VkImageTiling tiling, VkFormatFeatureFlags flags) { + for (const auto& format : formats) { + VkFormatProperties2 format_props {}; + format_props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + format_props.pNext = nullptr; + vkGetPhysicalDeviceFormatProperties2(physical_device, format, &format_props); + switch (tiling) { + case VK_IMAGE_TILING_OPTIMAL: + if ((format_props.formatProperties.optimalTilingFeatures & flags) == flags) + return format; + break; + case VK_IMAGE_TILING_LINEAR: + if ((format_props.formatProperties.linearTilingFeatures & flags) == flags) + return format; + break; + default: + std::cerr << "tiling not supported: " << string_VkImageTiling(tiling) << std::endl; + std::exit(EXIT_FAILURE); + } + } + return {}; +} + +static bool has_stencil(VkFormat format) { + switch (format) { + case VK_FORMAT_D16_UNORM_S8_UINT: + case VK_FORMAT_D24_UNORM_S8_UINT: + case VK_FORMAT_D32_SFLOAT_S8_UINT: + return true; + default: + return false; + } +} + static uint32_t find_mem_type(VkPhysicalDevice physical_device, uint32_t type_filter, VkMemoryPropertyFlags flags) { VkPhysicalDeviceMemoryProperties2 props {}; props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; @@ -614,6 +650,43 @@ create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, ui return std::tuple { texture_img, texture_img_device_mem }; } +static std::tuple<VkImage, VkDeviceMemory, VkImageView> +create_depth_resources(VkPhysicalDevice physical_device, VkDevice device, VkExtent2D swapchain_extent, VkFormat depth_format) { + auto [depth_img, depth_img_device_mem] = create_img(physical_device, device, swapchain_extent.width, swapchain_extent.height, depth_format, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + auto depth_img_view = [&]() { + VkImageViewCreateInfo img_view_ci { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = {}, + .flags = {}, + .image = depth_img, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = depth_format, + .components = {}, + .subresourceRange = { // VkImageSubresourceRange + .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + VkImageView depth_img_view; + if (VkResult res = vkCreateImageView(device, &img_view_ci, nullptr, &depth_img_view); res != VK_SUCCESS) { + std::cerr << "failed to create depth image view, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + return depth_img_view; + }(); + return std::tuple { depth_img, depth_img_device_mem, depth_img_view }; +} + +static void destroy_depth_resources(VkDevice device, VkImage depth_img, VkDeviceMemory depth_img_device_mem, VkImageView depth_img_view) { + vkDestroyImageView(device, depth_img_view, nullptr); + vkFreeMemory(device, depth_img_device_mem, nullptr); + vkDestroyImage(device, depth_img, nullptr); +} + static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img, VkImageLayout old_layout, VkImageLayout new_layout, VkAccessFlags2 src_access_mask, VkAccessFlags2 dst_access_mask, @@ -672,13 +745,21 @@ static int main_graphical() { // TODO: verify alignment requirements // TODO: maybe move these definitions? const std::array vertices { - engine::vk::Vertex { { -.5f, -.5f }, { 1.f, 0.f, 0.f }, { 1.f, 0.f } }, - engine::vk::Vertex { { .5f, -.5f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f } }, - engine::vk::Vertex { { .5f, .5f }, { 0.f, 0.f, 1.f }, { 0.f, 1.f } }, - engine::vk::Vertex { { -.5f, .5f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, + engine::vk::Vertex { { -.5f, -.5f, 0.f }, { 1.f, 0.f, 0.f }, { 1.f, 0.f } }, + engine::vk::Vertex { { .5f, -.5f, 0.f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f } }, + engine::vk::Vertex { { .5f, .5f, 0.f }, { 0.f, 0.f, 1.f }, { 0.f, 1.f } }, + engine::vk::Vertex { { -.5f, .5f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, + + engine::vk::Vertex { { -.5f, -.5f, -.5f }, { 1.f, 0.f, 0.f }, { 1.f, 0.f } }, + engine::vk::Vertex { { .5f, -.5f, -.5f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f } }, + engine::vk::Vertex { { .5f, .5f, -.5f }, { 0.f, 0.f, 1.f }, { 0.f, 1.f } }, + engine::vk::Vertex { { -.5f, .5f, -.5f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, }; - const std::array<uint16_t, 6> indices { 0, 1, 2, 2, 3, 0 }; + const std::array<uint16_t, 12> indices { + 0, 1, 2, 2, 3, 0, + 4, 5, 6, 6, 7, 4, + }; // init Vulkan std::cout << "Vulkan loader version: " << engine::vk::api { VK_HEADER_VERSION_COMPLETE } << std::endl; @@ -785,7 +866,7 @@ static int main_graphical() { }(); // select physical device and queues - auto [physical_device, graphics_queue_family_index, present_queue_family_index, physical_device_props, physical_device_features] = [&]() { + auto [physical_device, graphics_queue_family_index, present_queue_family_index, physical_device_props, physical_device_features, depth_format] = [&]() { std::multimap<unsigned, PhysicalDeviceEntry> physical_devices; auto avail_physical_devices = [&]() { @@ -877,6 +958,16 @@ static int main_graphical() { std::cout << "none"; std::cout << std::endl; + auto depth_format = find_format(avail_physical_device, + { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT }, + VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + std::cout << " depth format: "; + if (depth_format) + std::cout << string_VkFormat(*depth_format); + else + std::cout << "none"; + std::cout << std::endl; + auto score = [&]() { if (VK_API_VERSION_VARIANT(physical_device_props.properties.apiVersion) != 0 || physical_device_props.properties.apiVersion < VK_API_VERSION_1_4) @@ -891,6 +982,8 @@ static int main_graphical() { return std::optional<unsigned> {}; if (!physical_device_features.features.samplerAnisotropy) return std::optional<unsigned> {}; + if (!depth_format) + return std::optional<unsigned> {}; // TODO: lots of checks are probably missing, like checking if the swapchain is // supported (I'm not sure if it's needed) @@ -931,6 +1024,7 @@ static int main_graphical() { .present_queue_family_index = *present_queue_family_index, .props = physical_device_props, .features = physical_device_features, + .depth_format = *depth_format, } }); } @@ -947,7 +1041,7 @@ static int main_graphical() { std::cout << "picking: " << (best->second.idx + 1) << ". " << best->second.props.properties.deviceName << " (score: " << best->first << ")" << std::endl; return std::tuple { best->second.physical_device, best->second.graphics_queue_family_index, - best->second.present_queue_family_index, best->second.props, best->second.features }; + best->second.present_queue_family_index, best->second.props, best->second.features, best->second.depth_format }; }(); auto device = [&]() { @@ -1052,11 +1146,18 @@ static int main_graphical() { // problem (but I'm not sure) // TODO: pass the old swapchain to VkSwapchainCreateInfoKHR so the new swapchain can be created // while the old one is in-flight + // TODO: with the same intention of recreating the swapchain in-flight, we should also do + // something about the depth image. If we recreate the swapchain in-flight without worrying + // about the depth image, we might destroy it while it's being used. But I'm not sure, we have + // to test it VkExtent2D swapchain_extent; VkSurfaceFormatKHR surface_format; VkSwapchainKHR swapchain = nullptr; std::vector<VkImage> swapchain_imgs; std::vector<VkImageView> swapchain_img_views; + VkImage depth_img; + VkDeviceMemory depth_img_device_mem; + VkImageView depth_img_view; const auto destroy_swapchain = [&]() { for (auto it_img_view = swapchain_img_views.rbegin(); it_img_view != swapchain_img_views.rend(); ++it_img_view) @@ -1065,11 +1166,13 @@ static int main_graphical() { }; const auto recreate_swapchain = [&]() { - if (swapchain != nullptr) { + bool is_first_swapchain = (swapchain == nullptr); + if (!is_first_swapchain) { if (VkResult res = vkDeviceWaitIdle(device); res != VK_SUCCESS) { std::cerr << "failed to wait idle for device, error code: " << string_VkResult(res) << std::endl; std::exit(EXIT_FAILURE); } + destroy_depth_resources(device, depth_img, depth_img_device_mem, depth_img_view); destroy_swapchain(); } @@ -1234,6 +1337,9 @@ static int main_graphical() { } } }(); + + if (!is_first_swapchain) + std::tie(depth_img, depth_img_device_mem, depth_img_view) = create_depth_resources(physical_device, device, swapchain_extent, depth_format); }; recreate_swapchain(); @@ -1397,6 +1503,21 @@ static int main_graphical() { .alphaToOneEnable = VK_FALSE, }; + VkPipelineDepthStencilStateCreateInfo pl_depth_stencil_state_ci { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = {}, + .depthTestEnable = VK_TRUE, + .depthWriteEnable = VK_TRUE, + .depthCompareOp = VK_COMPARE_OP_LESS, + .depthBoundsTestEnable = VK_FALSE, + .stencilTestEnable = VK_FALSE, + .front = {}, + .back = {}, + .minDepthBounds = {}, + .maxDepthBounds = {}, + }; + VkPipelineColorBlendAttachmentState pl_col_blend_attachment_state { .blendEnable = VK_FALSE, .srcColorBlendFactor = {}, @@ -1448,7 +1569,7 @@ static int main_graphical() { .viewMask = {}, .colorAttachmentCount = 1, .pColorAttachmentFormats = &surface_format.format, - .depthAttachmentFormat = {}, + .depthAttachmentFormat = depth_format, .stencilAttachmentFormat = {}, }; @@ -1464,7 +1585,7 @@ static int main_graphical() { .pViewportState = &pl_viewport_state_ci, .pRasterizationState = &pl_raster_state_ci, .pMultisampleState = &pl_ms_state_ci, - .pDepthStencilState = {}, + .pDepthStencilState = &pl_depth_stencil_state_ci, .pColorBlendState = &pl_col_blend_state_ci, .pDynamicState = &pl_dyn_state_ci, .layout = pl_layout, @@ -1487,6 +1608,7 @@ static int main_graphical() { return std::tuple { pl_layout, graphics_pl }; }(); + // create command pool auto cmd_pool = [&]() { VkCommandPoolCreateInfo cmd_pool_ci { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, @@ -1502,6 +1624,11 @@ static int main_graphical() { return cmd_pool; }(); + // create depth resources + // TODO: check why we only need one image. From the looks of it, we need max_frames_in_flight + // depth images, because there is at most max_frames_in_flight rendering at the same time + std::tie(depth_img, depth_img_device_mem, depth_img_view) = create_depth_resources(physical_device, device, swapchain_extent, depth_format); + // create texture image auto [texture_img, texture_img_device_mem] = [&]() { int w, h, channels; @@ -1984,14 +2111,68 @@ static int main_graphical() { } } - transition_image_layout(cmd_bufs[frame_idx], swapchain_imgs[img_idx], - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - {}, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT); + // transition layouts + [&]() { + std::array img_mem_barriers { + VkImageMemoryBarrier2 { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = {}, + .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = swapchain_imgs[img_idx], + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }, + VkImageMemoryBarrier2 { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + .srcAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + .dstAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = (has_stencil(depth_format) ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL), + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = depth_img, + .subresourceRange = { + .aspectMask = static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT | (has_stencil(depth_format) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0)), + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }, + }; + VkDependencyInfo dependency_info { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = {}, + .memoryBarrierCount = 0, + .pMemoryBarriers = {}, + .bufferMemoryBarrierCount = 0, + .pBufferMemoryBarriers = {}, + .imageMemoryBarrierCount = img_mem_barriers.size(), + .pImageMemoryBarriers = img_mem_barriers.data(), + }; + vkCmdPipelineBarrier2(cmd_bufs[frame_idx], &dependency_info); + }(); { - VkClearValue clear_val { .color = { .float32 = { 0.f, 0.f, 0.f, 1.f } }, }; - VkRenderingAttachmentInfo attachment_info { + VkClearValue clear_color_val { .color { .float32 = { 0.f, 0.f, 0.f, 1.f } } }; + VkClearValue clear_depth_val { .depthStencil { .depth = 1.f, .stencil = 0 } }; + VkRenderingAttachmentInfo rendering_color_info { .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .pNext = nullptr, .imageView = swapchain_img_views[img_idx], @@ -2001,7 +2182,19 @@ static int main_graphical() { .resolveImageLayout = {}, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = clear_val, + .clearValue = clear_color_val, + }; + VkRenderingAttachmentInfo rendering_depth_info { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = depth_img_view, + .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + .resolveMode = {}, + .resolveImageView = {}, + .resolveImageLayout = {}, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .clearValue = clear_depth_val, }; VkRenderingInfo render_info { .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, @@ -2011,8 +2204,8 @@ static int main_graphical() { .layerCount = 1, .viewMask = {}, .colorAttachmentCount = 1, - .pColorAttachments = &attachment_info, - .pDepthAttachment = {}, + .pColorAttachments = &rendering_color_info, + .pDepthAttachment = &rendering_depth_info, .pStencilAttachment = {}, }; vkCmdBeginRendering(cmd_bufs[frame_idx], &render_info); @@ -2136,6 +2329,7 @@ static int main_graphical() { vkDestroyImageView(device, texture_img_view, nullptr); vkFreeMemory(device, texture_img_device_mem, nullptr); vkDestroyImage(device, texture_img, nullptr); + destroy_depth_resources(device, depth_img, depth_img_device_mem, depth_img_view); // no need to free cmd_buf because destroying the command pool will vkDestroyCommandPool(device, cmd_pool, nullptr); // no need to free descriptor_sets because destroying the descriptor pool will |
