From 549a831a4092a8f06a1eb98c308b73a8edb7dbb0 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 23 Apr 2026 20:59:34 +0200 Subject: [PATCH 01/23] Prepare sample base class for new sync Locked behind a setting until all samples have been updated --- framework/api_vulkan_sample.cpp | 143 ++++++++++++++++++++++++++------ framework/api_vulkan_sample.h | 19 ++++- 2 files changed, 135 insertions(+), 27 deletions(-) diff --git a/framework/api_vulkan_sample.cpp b/framework/api_vulkan_sample.cpp index 89bc6d404..9aa40c0ef 100644 --- a/framework/api_vulkan_sample.cpp +++ b/framework/api_vulkan_sample.cpp @@ -408,8 +408,15 @@ bool ApiVulkanSample::check_command_buffers() void ApiVulkanSample::create_command_buffers() { - // Create one command buffer for each swap chain image and reuse for rendering - draw_cmd_buffers.resize(get_render_context().get_render_frames().size()); + if (use_new_sync) + { + draw_cmd_buffers.resize(max_concurrent_frames); + } + else + { + // Create one command buffer for each swap chain image and reuse for rendering + draw_cmd_buffers.resize(get_render_context().get_render_frames().size()); + } VkCommandBufferAllocateInfo allocate_info = vkb::initializers::command_buffer_allocate_info( @@ -525,25 +532,62 @@ void ApiVulkanSample::prepare_frame() { if (get_render_context().has_swapchain()) { - handle_surface_changes(); - // Acquire the next image from the swap chain - VkResult result = get_render_context().get_swapchain().acquire_next_image(current_buffer, semaphores.acquired_image_ready, VK_NULL_HANDLE); - // Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) - if (result == VK_ERROR_OUT_OF_DATE_KHR) + if (use_new_sync) { - resize(width, height); + // Ensure command buffer execution has finished + VK_CHECK(vkWaitForFences(get_device().get_handle(), 1, &wait_fences[current_buffer], VK_TRUE, UINT64_MAX)); + VK_CHECK(vkResetFences(get_device().get_handle(), 1, &wait_fences[current_buffer])); + handle_surface_changes(); + // Acquire the next image from the swap chain + VkResult result = get_render_context().get_swapchain().acquire_next_image(current_image_index, acquired_image_ready_semaphores[current_buffer], VK_NULL_HANDLE); + // Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) + if (result == VK_ERROR_OUT_OF_DATE_KHR) + { + resize(width, height); + } + // VK_SUBOPTIMAL_KHR means that acquire was successful and semaphore is signaled but image is suboptimal + // allow rendering frame to suboptimal swapchain as otherwise we would have to manually unsignal semaphore and acquire image again + else if (result != VK_SUBOPTIMAL_KHR) + { + VK_CHECK(result); + } } - // VK_SUBOPTIMAL_KHR means that acquire was successful and semaphore is signaled but image is suboptimal - // allow rendering frame to suboptimal swapchain as otherwise we would have to manually unsignal semaphore and acquire image again - else if (result != VK_SUBOPTIMAL_KHR) + else { - VK_CHECK(result); + handle_surface_changes(); + // Acquire the next image from the swap chain + VkResult result = get_render_context().get_swapchain().acquire_next_image(current_buffer, semaphores.acquired_image_ready, VK_NULL_HANDLE); + // Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) + if (result == VK_ERROR_OUT_OF_DATE_KHR) + { + resize(width, height); + } + // VK_SUBOPTIMAL_KHR means that acquire was successful and semaphore is signaled but image is suboptimal + // allow rendering frame to suboptimal swapchain as otherwise we would have to manually unsignal semaphore and acquire image again + else if (result != VK_SUBOPTIMAL_KHR) + { + VK_CHECK(result); + } } } } void ApiVulkanSample::submit_frame() { + if (use_new_sync) + { + VkSubmitInfo submit_info{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &acquired_image_ready_semaphores[current_buffer], + .pWaitDstStageMask = &submit_pipeline_stages, + .commandBufferCount = 1, + .pCommandBuffers = &draw_cmd_buffers[current_buffer], + .signalSemaphoreCount = 1, + .pSignalSemaphores = &render_complete_semaphores[current_image_index]}; + VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + + } if (get_render_context().has_swapchain()) { const auto &queue = get_device().get_queue_by_present(0); @@ -566,11 +610,19 @@ void ApiVulkanSample::submit_frame() } // Check if a wait semaphore has been specified to wait for before presenting the image - if (semaphores.render_complete != VK_NULL_HANDLE) + if (use_new_sync) { - present_info.pWaitSemaphores = &semaphores.render_complete; + present_info.pWaitSemaphores = &render_complete_semaphores[current_image_index]; present_info.waitSemaphoreCount = 1; } + else + { + if (semaphores.render_complete != VK_NULL_HANDLE) + { + present_info.pWaitSemaphores = &semaphores.render_complete; + present_info.waitSemaphoreCount = 1; + } + } VkResult present_result = queue.present(present_info); @@ -589,10 +641,18 @@ void ApiVulkanSample::submit_frame() } } - // DO NOT USE - // vkDeviceWaitIdle and vkQueueWaitIdle are extremely expensive functions, and are used here purely for demonstrating the vulkan API - // without having to concern ourselves with proper syncronization. These functions should NEVER be used inside the render loop like this (every frame). - VK_CHECK(get_device().get_queue_by_present(0).wait_idle()); + if (use_new_sync) + { + // Select the next frame to render to, based on the max. no. of concurrent frames + current_buffer = (current_buffer + 1) % max_concurrent_frames; + } + else + { + // DO NOT USE + // vkDeviceWaitIdle and vkQueueWaitIdle are extremely expensive functions, and are used here purely for demonstrating the vulkan API + // without having to concern ourselves with proper syncronization. These functions should NEVER be used inside the render loop like this (every frame). + VK_CHECK(get_device().get_queue_by_present(0).wait_idle()); + } } ApiVulkanSample::~ApiVulkanSample() @@ -650,19 +710,48 @@ void ApiVulkanSample::build_command_buffers() void ApiVulkanSample::rebuild_command_buffers() { - vkResetCommandPool(get_device().get_handle(), cmd_pool, 0); - build_command_buffers(); + if (!use_new_sync) + { + vkResetCommandPool(get_device().get_handle(), cmd_pool, 0); + build_command_buffers(); + } } void ApiVulkanSample::create_synchronization_primitives() { - // Wait fences to sync command buffer access - VkFenceCreateInfo fence_create_info = vkb::initializers::fence_create_info(VK_FENCE_CREATE_SIGNALED_BIT); - wait_fences.resize(draw_cmd_buffers.size()); - for (auto &fence : wait_fences) + if (use_new_sync) { - VK_CHECK(vkCreateFence(get_device().get_handle(), &fence_create_info, nullptr, &fence)); + // Wait fences to sync command buffer access + wait_fences.resize(max_concurrent_frames); + VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .flags = VK_FENCE_CREATE_SIGNALED_BIT}; + for (auto &fence : wait_fences) + { + VK_CHECK(vkCreateFence(get_device().get_handle(), &fenceCreateInfo, nullptr, &fence)); + } + // Used to ensure that image presentation is complete before starting to submit again + for (auto &semaphore : acquired_image_ready_semaphores) + { + VkSemaphoreCreateInfo semaphoreCI{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + VK_CHECK(vkCreateSemaphore(get_device().get_handle(), &semaphoreCI, nullptr, &semaphore)); + } + // Semaphore used to ensure that all commands submitted have been finished before submitting the image to the queue + render_complete_semaphores.resize(swapchain_buffers.size()); + for (auto &semaphore : render_complete_semaphores) + { + VkSemaphoreCreateInfo semaphoreCI{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + VK_CHECK(vkCreateSemaphore(get_device().get_handle(), &semaphoreCI, nullptr, &semaphore)); + } } + else + { + // Wait fences to sync command buffer access + VkFenceCreateInfo fence_create_info = vkb::initializers::fence_create_info(VK_FENCE_CREATE_SIGNALED_BIT); + wait_fences.resize(draw_cmd_buffers.size()); + for (auto &fence : wait_fences) + { + VK_CHECK(vkCreateFence(get_device().get_handle(), &fence_create_info, nullptr, &fence)); + } + } } void ApiVulkanSample::create_command_pool() @@ -670,6 +759,10 @@ void ApiVulkanSample::create_command_pool() VkCommandPoolCreateInfo command_pool_info = {}; command_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; command_pool_info.queueFamilyIndex = get_device().get_queue_by_flags(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, 0).get_family_index(); + if (use_new_sync) + { + command_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + } VK_CHECK(vkCreateCommandPool(get_device().get_handle(), &command_pool_info, nullptr, &cmd_pool)); } diff --git a/framework/api_vulkan_sample.h b/framework/api_vulkan_sample.h index 45954d1d0..d92c3628b 100644 --- a/framework/api_vulkan_sample.h +++ b/framework/api_vulkan_sample.h @@ -96,6 +96,9 @@ struct Meshlet uint32_t index_count; }; +// @todo +constexpr uint32_t max_concurrent_frames = 2; + /** * @brief Sascha Willems base class for use in his ported samples into the framework * @@ -124,7 +127,10 @@ class ApiVulkanSample : public vkb::VulkanSampleC }; protected: - /// Stores the swapchain image buffers + // @todo + bool use_new_sync = false; + + // Stores the swapchain image buffers std::vector swapchain_buffers; virtual void create_render_context() override; @@ -159,6 +165,9 @@ class ApiVulkanSample : public vkb::VulkanSampleC // List of available frame buffers (same as number of swap chain images) std::vector framebuffers; + // @todo + uint32_t current_image_index = 0; + // Active frame buffer index uint32_t current_buffer = 0; @@ -184,6 +193,12 @@ class ApiVulkanSample : public vkb::VulkanSampleC // Synchronization fences std::vector wait_fences; + // Synchronization primitives + std::array acquired_image_ready_semaphores{}; + std::vector render_complete_semaphores{}; + // @todo: use this + //std::array wait_fences; + /** * @brief Populates the swapchain_buffers vector with the image and imageviews */ @@ -284,7 +299,7 @@ class ApiVulkanSample : public vkb::VulkanSampleC * @brief To be overridden by the derived class. Records the relevant commands to the rendering command buffers * Called when the framebuffers need to be rebuilt */ - virtual void build_command_buffers() = 0; + virtual void build_command_buffers(); /** * @brief Rebuild the command buffers by first resetting the corresponding command pool and then building the command buffers. From 97701fe8285f78803304ca3c4fcb89c1e284a83b Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 23 Apr 2026 21:14:27 +0200 Subject: [PATCH 02/23] Prepare sample base class for new sync Locked behind a setting until all samples have been updated --- framework/api_vulkan_sample.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/api_vulkan_sample.cpp b/framework/api_vulkan_sample.cpp index 9aa40c0ef..2f55a9a23 100644 --- a/framework/api_vulkan_sample.cpp +++ b/framework/api_vulkan_sample.cpp @@ -585,7 +585,7 @@ void ApiVulkanSample::submit_frame() .pCommandBuffers = &draw_cmd_buffers[current_buffer], .signalSemaphoreCount = 1, .pSignalSemaphores = &render_complete_semaphores[current_image_index]}; - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, wait_fences[current_buffer])); } if (get_render_context().has_swapchain()) @@ -599,7 +599,7 @@ void ApiVulkanSample::submit_frame() present_info.pNext = NULL; present_info.swapchainCount = 1; present_info.pSwapchains = ≻ - present_info.pImageIndices = ¤t_buffer; + present_info.pImageIndices = use_new_sync ? ¤t_image_index : ¤t_buffer; VkDisplayPresentInfoKHR disp_present_info{}; if (get_device().get_gpu().is_extension_supported(VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME) && From 3e1a30752a13362c6475c45718e29a5a3eb57906 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 23 Apr 2026 21:44:27 +0200 Subject: [PATCH 03/23] Prepare sample base class for new sync Locked behind a setting until all samples have been updated --- framework/api_vulkan_sample.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/api_vulkan_sample.cpp b/framework/api_vulkan_sample.cpp index 2f55a9a23..2f822b9ed 100644 --- a/framework/api_vulkan_sample.cpp +++ b/framework/api_vulkan_sample.cpp @@ -699,6 +699,18 @@ ApiVulkanSample::~ApiVulkanSample() { vkDestroyFence(get_device().get_handle(), fence, nullptr); } + + if (use_new_sync) + { + for (auto &semaphore : acquired_image_ready_semaphores) + { + vkDestroySemaphore(get_device().get_handle(), semaphore, nullptr); + } + for (auto &semaphore : render_complete_semaphores) + { + vkDestroySemaphore(get_device().get_handle(), semaphore, nullptr); + } + } } } From df829145ec84079681b2962758a984dd0daa3757 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 23 Apr 2026 21:44:51 +0200 Subject: [PATCH 04/23] Update first sample to use new (proper) sync --- .../push_descriptors/push_descriptors.cpp | 176 +++++++++--------- .../push_descriptors/push_descriptors.h | 17 +- 2 files changed, 92 insertions(+), 101 deletions(-) diff --git a/samples/extensions/push_descriptors/push_descriptors.cpp b/samples/extensions/push_descriptors/push_descriptors.cpp index 4ca61681b..ce347b094 100644 --- a/samples/extensions/push_descriptors/push_descriptors.cpp +++ b/samples/extensions/push_descriptors/push_descriptors.cpp @@ -33,7 +33,8 @@ PushDescriptors::PushDescriptors() { - title = "Push descriptors"; + title = "Push descriptors"; + use_new_sync = true; // Enable extension required for push descriptors add_device_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); @@ -48,11 +49,17 @@ PushDescriptors::~PushDescriptors() vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout, nullptr); for (auto &cube : cubes) { - cube.uniform_buffer.reset(); + for (auto &uniform_buffer : cube.uniform_buffers) + { + uniform_buffer.reset(); + } cube.texture.image.reset(); vkDestroySampler(get_device().get_handle(), cube.texture.sampler, nullptr); } - uniform_buffers.scene.reset(); + for (auto &uniform_buffer : uniform_buffers) + { + uniform_buffer.reset(); + } } } @@ -65,8 +72,11 @@ void PushDescriptors::request_gpu_features(vkb::core::PhysicalDeviceC &gpu) } } -void PushDescriptors::build_command_buffers() +void PushDescriptors::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -81,77 +91,73 @@ void PushDescriptors::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - render_pass_begin_info.framebuffer = framebuffers[i]; - - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - const auto &vertex_buffer = models.cube->vertex_buffers.at("vertex_buffer"); - auto &index_buffer = models.cube->index_buffer; + const auto &vertex_buffer = models.cube->vertex_buffers.at("vertex_buffer"); + auto &index_buffer = models.cube->index_buffer; - VkDeviceSize offsets[1] = {0}; - vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, vertex_buffer.get(), offsets); - vkCmdBindIndexBuffer(draw_cmd_buffers[i], index_buffer->get_handle(), 0, models.cube->index_type); + VkDeviceSize offsets[1] = {0}; + vkCmdBindVertexBuffers(draw_cmd_buffer, 0, 1, vertex_buffer.get(), offsets); + vkCmdBindIndexBuffer(draw_cmd_buffer, index_buffer->get_handle(), 0, models.cube->index_type); - // Render two cubes using different descriptor sets using push descriptors - for (auto &cube : cubes) - { - // Instead of preparing the descriptor sets up-front, using push descriptors we can set (push) them inside of a command buffer - // This allows a more dynamic approach without the need to create descriptor sets for each model - // Note: dstSet for each descriptor set write is left at zero as this is ignored when using push descriptors - - std::array write_descriptor_sets{}; - - // Scene matrices - VkDescriptorBufferInfo scene_buffer_descriptor = create_descriptor(*uniform_buffers.scene); - write_descriptor_sets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_descriptor_sets[0].dstSet = 0; - write_descriptor_sets[0].dstBinding = 0; - write_descriptor_sets[0].descriptorCount = 1; - write_descriptor_sets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - write_descriptor_sets[0].pBufferInfo = &scene_buffer_descriptor; - - // Model matrices - VkDescriptorBufferInfo cube_buffer_descriptor = create_descriptor(*cube.uniform_buffer); - write_descriptor_sets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_descriptor_sets[1].dstSet = 0; - write_descriptor_sets[1].dstBinding = 1; - write_descriptor_sets[1].descriptorCount = 1; - write_descriptor_sets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - write_descriptor_sets[1].pBufferInfo = &cube_buffer_descriptor; - - // Texture - VkDescriptorImageInfo image_descriptor = create_descriptor(cube.texture); - write_descriptor_sets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_descriptor_sets[2].dstSet = 0; - write_descriptor_sets[2].dstBinding = 2; - write_descriptor_sets[2].descriptorCount = 1; - write_descriptor_sets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - write_descriptor_sets[2].pImageInfo = &image_descriptor; - - vkCmdPushDescriptorSetKHR(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 3, write_descriptor_sets.data()); - - draw_model(models.cube, draw_cmd_buffers[i]); - } + // Render two cubes using different descriptor sets using push descriptors + for (auto &cube : cubes) + { + // Instead of preparing the descriptor sets up-front, using push descriptors we can set (push) them inside of a command buffer + // This allows a more dynamic approach without the need to create descriptor sets for each model + // Note: dstSet for each descriptor set write is left at zero as this is ignored when using push descriptors + + std::array write_descriptor_sets{}; + + // Scene matrices + VkDescriptorBufferInfo scene_buffer_descriptor = create_descriptor(*uniform_buffers[current_buffer]); + write_descriptor_sets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor_sets[0].dstSet = 0; + write_descriptor_sets[0].dstBinding = 0; + write_descriptor_sets[0].descriptorCount = 1; + write_descriptor_sets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + write_descriptor_sets[0].pBufferInfo = &scene_buffer_descriptor; + + // Model matrices + VkDescriptorBufferInfo cube_buffer_descriptor = create_descriptor(*cube.uniform_buffers[current_buffer]); + write_descriptor_sets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor_sets[1].dstSet = 0; + write_descriptor_sets[1].dstBinding = 1; + write_descriptor_sets[1].descriptorCount = 1; + write_descriptor_sets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + write_descriptor_sets[1].pBufferInfo = &cube_buffer_descriptor; + + // Texture + VkDescriptorImageInfo image_descriptor = create_descriptor(cube.texture); + write_descriptor_sets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor_sets[2].dstSet = 0; + write_descriptor_sets[2].dstBinding = 2; + write_descriptor_sets[2].descriptorCount = 1; + write_descriptor_sets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_descriptor_sets[2].pImageInfo = &image_descriptor; + + vkCmdPushDescriptorSetKHR(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 3, write_descriptor_sets.data()); + + draw_model(models.cube, draw_cmd_buffer); + } - draw_ui(draw_cmd_buffers[i]); + draw_ui(draw_cmd_buffer); - vkCmdEndRenderPass(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffer); - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } void PushDescriptors::load_assets() @@ -247,19 +253,15 @@ void PushDescriptors::prepare_pipelines() void PushDescriptors::prepare_uniform_buffers() { - // Vertex shader scene uniform buffer block - uniform_buffers.scene = std::make_unique(get_device(), - sizeof(UboScene), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - // Vertex shader cube model uniform buffer blocks - for (auto &cube : cubes) + for (uint32_t i = 0; i < max_concurrent_frames; i++) { - cube.uniform_buffer = std::make_unique(get_device(), - sizeof(glm::mat4), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); + // Vertex shader scene uniform buffer block + uniform_buffers[i] = std::make_unique(get_device(), sizeof(UboScene), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + // Vertex shader cube model uniform buffer blocks + for (auto &cube : cubes) + { + cube.uniform_buffers[i] = std::make_unique(get_device(), sizeof(glm::mat4), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } update_uniform_buffers(); @@ -270,7 +272,7 @@ void PushDescriptors::update_uniform_buffers() { ubo_scene.projection = camera.matrices.perspective; ubo_scene.view = camera.matrices.view; - uniform_buffers.scene->convert_and_update(ubo_scene); + uniform_buffers[current_buffer]->convert_and_update(ubo_scene); } void PushDescriptors::update_cube_uniform_buffers(float delta_time) @@ -283,7 +285,7 @@ void PushDescriptors::update_cube_uniform_buffers(float delta_time) cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); - cube.uniform_buffer->convert_and_update(cube.model_mat); + cube.uniform_buffers[current_buffer]->convert_and_update(cube.model_mat); } if (animate) @@ -301,12 +303,12 @@ void PushDescriptors::update_cube_uniform_buffers(float delta_time) } } -void PushDescriptors::draw() +void PushDescriptors::draw(float delta_time) { ApiVulkanSample::prepare_frame(); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + update_cube_uniform_buffers(delta_time); + update_uniform_buffers(); + build_command_buffer(); ApiVulkanSample::submit_frame(); } @@ -366,15 +368,7 @@ void PushDescriptors::render(float delta_time) { return; } - draw(); - if (animate) - { - update_cube_uniform_buffers(delta_time); - } - if (camera.updated) - { - update_uniform_buffers(); - } + draw(delta_time); } void PushDescriptors::on_update_ui_overlay(vkb::Drawer &drawer) diff --git a/samples/extensions/push_descriptors/push_descriptors.h b/samples/extensions/push_descriptors/push_descriptors.h index 6e89f6447..6eab4cda3 100644 --- a/samples/extensions/push_descriptors/push_descriptors.h +++ b/samples/extensions/push_descriptors/push_descriptors.h @@ -40,10 +40,10 @@ class PushDescriptors : public ApiVulkanSample struct Cube { - Texture texture; - std::unique_ptr uniform_buffer; - glm::vec3 rotation; - glm::mat4 model_mat; + Texture texture; + std::array, max_concurrent_frames> uniform_buffers; + glm::vec3 rotation; + glm::mat4 model_mat; }; std::array cubes; @@ -52,10 +52,7 @@ class PushDescriptors : public ApiVulkanSample std::unique_ptr cube; } models; - struct UniformBuffers - { - std::unique_ptr scene; - } uniform_buffers; + std::array, max_concurrent_frames> uniform_buffers; struct UboScene { @@ -70,14 +67,14 @@ class PushDescriptors : public ApiVulkanSample PushDescriptors(); ~PushDescriptors(); virtual void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; - void build_command_buffers() override; + void build_command_buffer(); void load_assets(); void setup_descriptor_set_layout(); void prepare_pipelines(); void prepare_uniform_buffers(); void update_uniform_buffers(); void update_cube_uniform_buffers(float delta_time); - void draw(); + void draw(float delta_time); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; From bed7aa04ffb4834d084ceb5393a4b7af7b55f2a8 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Fri, 24 Apr 2026 15:24:49 +0200 Subject: [PATCH 05/23] Prepare sample base classes for new sync Locked behind a setting until all samples have been updated --- framework/api_vulkan_sample.cpp | 11 +- framework/api_vulkan_sample.h | 6 +- framework/hpp_api_vulkan_sample.cpp | 159 +++++++++++++++++++++++----- framework/hpp_api_vulkan_sample.h | 17 ++- 4 files changed, 156 insertions(+), 37 deletions(-) diff --git a/framework/api_vulkan_sample.cpp b/framework/api_vulkan_sample.cpp index 2f822b9ed..2ed3cae2e 100644 --- a/framework/api_vulkan_sample.cpp +++ b/framework/api_vulkan_sample.cpp @@ -735,23 +735,22 @@ void ApiVulkanSample::create_synchronization_primitives() { // Wait fences to sync command buffer access wait_fences.resize(max_concurrent_frames); - VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .flags = VK_FENCE_CREATE_SIGNALED_BIT}; + VkFenceCreateInfo fence_create_info{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .flags = VK_FENCE_CREATE_SIGNALED_BIT}; for (auto &fence : wait_fences) { - VK_CHECK(vkCreateFence(get_device().get_handle(), &fenceCreateInfo, nullptr, &fence)); + VK_CHECK(vkCreateFence(get_device().get_handle(), &fence_create_info, nullptr, &fence)); } + VkSemaphoreCreateInfo semaphore_create_info{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; // Used to ensure that image presentation is complete before starting to submit again for (auto &semaphore : acquired_image_ready_semaphores) { - VkSemaphoreCreateInfo semaphoreCI{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; - VK_CHECK(vkCreateSemaphore(get_device().get_handle(), &semaphoreCI, nullptr, &semaphore)); + VK_CHECK(vkCreateSemaphore(get_device().get_handle(), &semaphore_create_info, nullptr, &semaphore)); } // Semaphore used to ensure that all commands submitted have been finished before submitting the image to the queue render_complete_semaphores.resize(swapchain_buffers.size()); for (auto &semaphore : render_complete_semaphores) { - VkSemaphoreCreateInfo semaphoreCI{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; - VK_CHECK(vkCreateSemaphore(get_device().get_handle(), &semaphoreCI, nullptr, &semaphore)); + VK_CHECK(vkCreateSemaphore(get_device().get_handle(), &semaphore_create_info, nullptr, &semaphore)); } } else diff --git a/framework/api_vulkan_sample.h b/framework/api_vulkan_sample.h index d92c3628b..880c29595 100644 --- a/framework/api_vulkan_sample.h +++ b/framework/api_vulkan_sample.h @@ -96,9 +96,6 @@ struct Meshlet uint32_t index_count; }; -// @todo -constexpr uint32_t max_concurrent_frames = 2; - /** * @brief Sascha Willems base class for use in his ported samples into the framework * @@ -130,6 +127,9 @@ class ApiVulkanSample : public vkb::VulkanSampleC // @todo bool use_new_sync = false; + // @todo + constexpr static uint32_t max_concurrent_frames = 2; + // Stores the swapchain image buffers std::vector swapchain_buffers; diff --git a/framework/hpp_api_vulkan_sample.cpp b/framework/hpp_api_vulkan_sample.cpp index f37a9bd7a..9f603be22 100644 --- a/framework/hpp_api_vulkan_sample.cpp +++ b/framework/hpp_api_vulkan_sample.cpp @@ -402,7 +402,7 @@ void HPPApiVulkanSample::create_command_buffers() // Create one command buffer for each swap chain image and reuse for rendering vk::CommandBufferAllocateInfo allocate_info{.commandPool = cmd_pool, .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = static_cast(get_render_context().get_render_frames().size())}; + .commandBufferCount = use_new_sync ? max_concurrent_frames : static_cast(get_render_context().get_render_frames().size()) }; draw_cmd_buffers = get_device().get_handle().allocateCommandBuffers(allocate_info); } @@ -484,38 +484,91 @@ void HPPApiVulkanSample::prepare_frame() { if (get_render_context().has_swapchain()) { - handle_surface_changes(); - // Acquire the next image from the swap chain - // Shows how to filter an error code from a vulkan function, which is mapped to an exception but should be handled here! - vk::Result result; - try + if (use_new_sync) { - std::tie(result, current_buffer) = get_render_context().get_swapchain().acquire_next_image(semaphores.acquired_image_ready); + vk::Result result; + + // Ensure command buffer execution has finished + result = get_device().get_handle().waitForFences(wait_fences[current_buffer], true, UINT64_MAX); + if (result != vk::Result::eSuccess) + { + LOGE("Vulkan error on waitForFences: {}", vk::to_string(result)); + abort(); + } + get_device().get_handle().resetFences(wait_fences[current_buffer]); + + handle_surface_changes(); + // Acquire the next image from the swap chain + // Shows how to filter an error code from a vulkan function, which is mapped to an exception but should be handled here! + try + { + std::tie(result, current_image_index) = get_render_context().get_swapchain().acquire_next_image(acquired_image_ready_semaphores[current_buffer]); + } + // Recreate the swapchain if it's no longer compatible with the surface (eErrorOutOfDateKHR) + // Don't catch other failures here, they are propagated up the calling hierarchy + catch (vk::OutOfDateKHRError & /*err*/) + { + resize(extent.width, extent.height); + } + // VK_SUBOPTIMAL_KHR is a success code and means that acquire was successful and semaphore is signaled but image is suboptimal + // allow rendering frame to suboptimal swapchain as otherwise we would have to manually unsignal semaphore and acquire image again } - // Recreate the swapchain if it's no longer compatible with the surface (eErrorOutOfDateKHR) - // Don't catch other failures here, they are propagated up the calling hierarchy - catch (vk::OutOfDateKHRError & /*err*/) + else { - resize(extent.width, extent.height); + handle_surface_changes(); + // Acquire the next image from the swap chain + // Shows how to filter an error code from a vulkan function, which is mapped to an exception but should be handled here! + vk::Result result; + try + { + std::tie(result, current_buffer) = get_render_context().get_swapchain().acquire_next_image(semaphores.acquired_image_ready); + } + // Recreate the swapchain if it's no longer compatible with the surface (eErrorOutOfDateKHR) + // Don't catch other failures here, they are propagated up the calling hierarchy + catch (vk::OutOfDateKHRError & /*err*/) + { + resize(extent.width, extent.height); + } + // VK_SUBOPTIMAL_KHR is a success code and means that acquire was successful and semaphore is signaled but image is suboptimal + // allow rendering frame to suboptimal swapchain as otherwise we would have to manually unsignal semaphore and acquire image again } - // VK_SUBOPTIMAL_KHR is a success code and means that acquire was successful and semaphore is signaled but image is suboptimal - // allow rendering frame to suboptimal swapchain as otherwise we would have to manually unsignal semaphore and acquire image again } } void HPPApiVulkanSample::submit_frame() { + if (use_new_sync) + { + vk::SubmitInfo submit_info{ + .waitSemaphoreCount = 1, + .pWaitSemaphores = &acquired_image_ready_semaphores[current_buffer], + .pWaitDstStageMask = &submit_pipeline_stages, + .commandBufferCount = 1, + .pCommandBuffers = &draw_cmd_buffers[current_buffer], + .signalSemaphoreCount = 1, + .pSignalSemaphores = &render_complete_semaphores[current_image_index]}; + queue.submit(submit_info, wait_fences[current_buffer]); + } + if (get_render_context().has_swapchain()) { const auto &queue = get_device().get_queue_by_present(0); vk::SwapchainKHR swapchain = get_render_context().get_swapchain().get_handle(); - vk::PresentInfoKHR present_info{.swapchainCount = 1, .pSwapchains = &swapchain, .pImageIndices = ¤t_buffer}; + vk::PresentInfoKHR present_info{.swapchainCount = 1, .pSwapchains = &swapchain}; + present_info.pImageIndices = use_new_sync ? ¤t_image_index : ¤t_buffer; // Check if a wait semaphore has been specified to wait for before presenting the image - if (semaphores.render_complete) + if (use_new_sync) { - present_info.setWaitSemaphores(semaphores.render_complete); + present_info.setWaitSemaphores(render_complete_semaphores[current_image_index]); + } + else + { + if (semaphores.render_complete) + { + present_info.setWaitSemaphores(semaphores.render_complete); + } } vk::DisplayPresentInfoKHR disp_present_info; @@ -548,10 +601,18 @@ void HPPApiVulkanSample::submit_frame() } } - // DO NOT USE - // vkDeviceWaitIdle and vkQueueWaitIdle are extremely expensive functions, and are used here purely for demonstrating the vulkan API - // without having to concern ourselves with proper syncronization. These functions should NEVER be used inside the render loop like this (every frame). - get_device().get_queue_by_present(0).get_handle().waitIdle(); + if (use_new_sync) + { + // Select the next frame to render to, based on the max. no. of concurrent frames + current_buffer = (current_buffer + 1) % max_concurrent_frames; + } + else + { + // DO NOT USE + // vkDeviceWaitIdle and vkQueueWaitIdle are extremely expensive functions, and are used here purely for demonstrating the vulkan API + // without having to concern ourselves with proper syncronization. These functions should NEVER be used inside the render loop like this (every frame). + get_device().get_queue_by_present(0).get_handle().waitIdle(); + } } HPPApiVulkanSample::~HPPApiVulkanSample() @@ -597,6 +658,18 @@ HPPApiVulkanSample::~HPPApiVulkanSample() { device.destroyFence(fence); } + + if (use_new_sync) + { + for (auto &semaphore : acquired_image_ready_semaphores) + { + device.destroySemaphore(semaphore); + } + for (auto &semaphore : render_complete_semaphores) + { + device.destroySemaphore(semaphore); + } + } } } @@ -608,18 +681,46 @@ void HPPApiVulkanSample::build_command_buffers() void HPPApiVulkanSample::rebuild_command_buffers() { - get_device().get_handle().resetCommandPool(cmd_pool); - build_command_buffers(); + if (!use_new_sync) + { + get_device().get_handle().resetCommandPool(cmd_pool); + build_command_buffers(); + } } void HPPApiVulkanSample::create_synchronization_primitives() { - // Wait fences to sync command buffer access - vk::FenceCreateInfo fence_create_info{.flags = vk::FenceCreateFlagBits::eSignaled}; - wait_fences.resize(draw_cmd_buffers.size()); - for (auto &fence : wait_fences) + if (use_new_sync) + { + // Wait fences to sync command buffer access + wait_fences.resize(max_concurrent_frames); + vk::FenceCreateInfo fence_create_info{.flags = vk::FenceCreateFlagBits::eSignaled}; + for (auto &fence : wait_fences) + { + fence = get_device().get_handle().createFence(fence_create_info); + } + vk::SemaphoreCreateInfo semaphore_create_info{}; + // Used to ensure that image presentation is complete before starting to submit again + for (auto &semaphore : acquired_image_ready_semaphores) + { + semaphore = get_device().get_handle().createSemaphore(semaphore_create_info); + } + // Semaphore used to ensure that all commands submitted have been finished before submitting the image to the queue + render_complete_semaphores.resize(swapchain_buffers.size()); + for (auto &semaphore : render_complete_semaphores) + { + semaphore = get_device().get_handle().createSemaphore(semaphore_create_info); + } + } + else { - fence = get_device().get_handle().createFence(fence_create_info); + // Wait fences to sync command buffer access + vk::FenceCreateInfo fence_create_info{.flags = vk::FenceCreateFlagBits::eSignaled}; + wait_fences.resize(draw_cmd_buffers.size()); + for (auto &fence : wait_fences) + { + fence = get_device().get_handle().createFence(fence_create_info); + } } } @@ -627,6 +728,10 @@ void HPPApiVulkanSample::create_command_pool() { uint32_t queue_family_index = get_device().get_queue_by_flags(vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute, 0).get_family_index(); vk::CommandPoolCreateInfo command_pool_info{.queueFamilyIndex = queue_family_index}; + if (use_new_sync) + { + command_pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; + } cmd_pool = get_device().get_handle().createCommandPool(command_pool_info); } diff --git a/framework/hpp_api_vulkan_sample.h b/framework/hpp_api_vulkan_sample.h index 01e03a372..34474d1fa 100644 --- a/framework/hpp_api_vulkan_sample.h +++ b/framework/hpp_api_vulkan_sample.h @@ -84,6 +84,12 @@ class HPPApiVulkanSample : public vkb::VulkanSampleCpp }; protected: + // @todo + bool use_new_sync = false; + + // @todo + constexpr static uint32_t max_concurrent_frames = 2; + /// Stores the swapchain image buffers std::vector swapchain_buffers; @@ -120,6 +126,9 @@ class HPPApiVulkanSample : public vkb::VulkanSampleCpp // List of available frame buffers (same as number of swap chain images) std::vector framebuffers; + // @todo + uint32_t current_image_index = 0; + // Active frame buffer index uint32_t current_buffer = 0; @@ -145,6 +154,12 @@ class HPPApiVulkanSample : public vkb::VulkanSampleCpp // Synchronization fences std::vector wait_fences; + // Synchronization primitives + std::array acquired_image_ready_semaphores{}; + std::vector render_complete_semaphores{}; + // @todo: use this + // std::array wait_fences; + /** * @brief Creates a vulkan sampler * @param address_mode The samplers address mode @@ -246,7 +261,7 @@ class HPPApiVulkanSample : public vkb::VulkanSampleCpp * @brief To be overridden by the derived class. Records the relevant commands to the rendering command buffers * Called when the framebuffers need to be rebuilt */ - virtual void build_command_buffers() = 0; + virtual void build_command_buffers(); /** * @brief Rebuild the command buffers by first resetting the corresponding command pool and then building the command buffers. From 646d5c365e5d6e1d0909aa2821b4f8d3ab430cf5 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Fri, 24 Apr 2026 15:25:00 +0200 Subject: [PATCH 06/23] Update first sample to use new (proper) sync --- .../hpp_push_descriptors.cpp | 162 ++++++++---------- .../hpp_push_descriptors.h | 36 ++-- .../push_descriptors/push_descriptors.cpp | 4 - 3 files changed, 91 insertions(+), 111 deletions(-) diff --git a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.cpp b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.cpp index 62591e34a..d606aa6f9 100644 --- a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.cpp +++ b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.cpp @@ -31,7 +31,8 @@ HPPPushDescriptors::HPPPushDescriptors() { - title = "Push descriptors"; + title = "Push descriptors"; + use_new_sync = true; // Enable extension required for push descriptors add_device_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); @@ -92,7 +93,27 @@ void HPPPushDescriptors::request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) } } -void HPPPushDescriptors::build_command_buffers() +void HPPPushDescriptors::on_update_ui_overlay(vkb::Drawer &drawer) +{ + if (drawer.header("Settings")) + { + drawer.checkbox("Animate", &animate); + } + if (drawer.header("Device properties")) + { + drawer.text("maxPushDescriptors: %d", max_push_descriptors); + } +} + +void HPPPushDescriptors::render(float delta_time) +{ + if (prepared) + { + draw(delta_time); + } +} + +void HPPPushDescriptors::build_command_buffer() { vk::CommandBufferBeginInfo command_buffer_begin_info; @@ -113,79 +134,48 @@ void HPPPushDescriptors::build_command_buffers() vk::DeviceSize offset = 0; - vk::DescriptorBufferInfo scene_buffer_descriptor{uniform_buffers.scene->get_handle(), 0, vk::WholeSize}; - - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - render_pass_begin_info.framebuffer = framebuffers[i]; - vk::CommandBuffer &command_buffer = draw_cmd_buffers[i]; - - command_buffer.begin(command_buffer_begin_info); - command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - command_buffer.setViewport(0, viewport); - command_buffer.setScissor(0, scissor); + vk::DescriptorBufferInfo scene_buffer_descriptor{uniform_buffers[current_buffer]->get_handle(), 0, vk::WholeSize}; - command_buffer.bindVertexBuffers(0, vertex_buffer, offset); - command_buffer.bindIndexBuffer(index_buffer, 0, models.cube->get_index_type()); + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; + vk::CommandBuffer &command_buffer = draw_cmd_buffers[current_buffer]; - // Render two cubes using different descriptor sets using push descriptors - for (auto &cube : cubes) - { - // Instead of preparing the descriptor sets up-front, using push descriptors we can set (push) them inside of a command buffer - // This allows a more dynamic approach without the need to create descriptor sets for each model - // Note: dstSet for each descriptor set write is left at nullptr as this is ignored when using push descriptors + command_buffer.begin(command_buffer_begin_info); + command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); + command_buffer.setViewport(0, viewport); + command_buffer.setScissor(0, scissor); - vk::DescriptorBufferInfo cube_buffer_descriptor{cube.uniform_buffer->get_handle(), 0, vk::WholeSize}; - vk::DescriptorImageInfo cube_image_descriptor{cube.texture.sampler, - cube.texture.image->get_vk_image_view().get_handle(), - descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler, - cube.texture.image->get_vk_image_view().get_format())}; + command_buffer.bindVertexBuffers(0, vertex_buffer, offset); + command_buffer.bindIndexBuffer(index_buffer, 0, models.cube->get_index_type()); - std::array write_descriptor_sets = { - {{.dstBinding = 0, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eUniformBuffer, .pBufferInfo = &scene_buffer_descriptor}, - {.dstBinding = 1, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eUniformBuffer, .pBufferInfo = &cube_buffer_descriptor}, - {.dstBinding = 2, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eCombinedImageSampler, .pImageInfo = &cube_image_descriptor}}}; - - command_buffer.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, write_descriptor_sets); + // Render two cubes using different descriptor sets using push descriptors + for (auto &cube : cubes) + { + // Instead of preparing the descriptor sets up-front, using push descriptors we can set (push) them inside of a command buffer + // This allows a more dynamic approach without the need to create descriptor sets for each model + // Note: dstSet for each descriptor set write is left at nullptr as this is ignored when using push descriptors - draw_model(models.cube, command_buffer); - } + vk::DescriptorBufferInfo cube_buffer_descriptor{cube.uniform_buffers[current_buffer]->get_handle(), 0, vk::WholeSize}; + vk::DescriptorImageInfo cube_image_descriptor{cube.texture.sampler, + cube.texture.image->get_vk_image_view().get_handle(), + descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler, + cube.texture.image->get_vk_image_view().get_format())}; - draw_ui(command_buffer); + std::array write_descriptor_sets = { + {{.dstBinding = 0, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eUniformBuffer, .pBufferInfo = &scene_buffer_descriptor}, + {.dstBinding = 1, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eUniformBuffer, .pBufferInfo = &cube_buffer_descriptor}, + {.dstBinding = 2, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eCombinedImageSampler, .pImageInfo = &cube_image_descriptor}}}; - command_buffer.endRenderPass(); + command_buffer.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, write_descriptor_sets); - command_buffer.end(); + draw_model(models.cube, command_buffer); } -} -void HPPPushDescriptors::on_update_ui_overlay(vkb::Drawer &drawer) -{ - if (drawer.header("Settings")) - { - drawer.checkbox("Animate", &animate); - } - if (drawer.header("Device properties")) - { - drawer.text("maxPushDescriptors: %d", max_push_descriptors); - } -} + draw_ui(command_buffer); -void HPPPushDescriptors::render(float delta_time) -{ - if (prepared) - { - draw(); - if (animate) - { - update_cube_uniform_buffers(delta_time); - } - if (camera.updated) - { - update_uniform_buffers(); - } - } + command_buffer.endRenderPass(); + + command_buffer.end(); } void HPPPushDescriptors::create_descriptor_set_layout() @@ -240,23 +230,23 @@ void HPPPushDescriptors::create_pipeline() void HPPPushDescriptors::create_uniform_buffers() { - // Vertex shader scene uniform buffer block - uniform_buffers.scene = std::make_unique(get_device(), - sizeof(UboScene), - vk::BufferUsageFlagBits::eUniformBuffer, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - // Vertex shader cube model uniform buffer blocks - for (auto &cube : cubes) + for (uint32_t i = 0; i < max_concurrent_frames; i++) { - cube.uniform_buffer = std::make_unique(get_device(), - sizeof(glm::mat4), - vk::BufferUsageFlagBits::eUniformBuffer, - VMA_MEMORY_USAGE_CPU_TO_GPU); - } + // Vertex shader scene uniform buffer block + uniform_buffers[i] = std::make_unique(get_device(), + sizeof(UboScene), + vk::BufferUsageFlagBits::eUniformBuffer, + VMA_MEMORY_USAGE_CPU_TO_GPU); - update_uniform_buffers(); - update_cube_uniform_buffers(0.0f); + // Vertex shader cube model uniform buffer blocks + for (auto &cube : cubes) + { + cube.uniform_buffers[i] = std::make_unique(get_device(), + sizeof(glm::mat4), + vk::BufferUsageFlagBits::eUniformBuffer, + VMA_MEMORY_USAGE_CPU_TO_GPU); + } + } } void HPPPushDescriptors::create_pipeline_layout() @@ -265,14 +255,12 @@ void HPPPushDescriptors::create_pipeline_layout() pipeline_layout = get_device().get_handle().createPipelineLayout(pipeline_layout_create_info); } -void HPPPushDescriptors::draw() +void HPPPushDescriptors::draw(float delta_time) { HPPApiVulkanSample::prepare_frame(); - - // Submit to queue - submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]); - queue.submit(submit_info); - + update_cube_uniform_buffers(delta_time); + update_uniform_buffers(); + build_command_buffer(); HPPApiVulkanSample::submit_frame(); } @@ -302,7 +290,7 @@ void HPPPushDescriptors::update_cube_uniform_buffers(float delta_time) cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); - cube.uniform_buffer->convert_and_update(cube.model_mat); + cube.uniform_buffers[current_buffer]->convert_and_update(cube.model_mat); } if (animate) @@ -324,7 +312,7 @@ void HPPPushDescriptors::update_uniform_buffers() { ubo_scene.projection = camera.matrices.perspective; ubo_scene.view = camera.matrices.view; - uniform_buffers.scene->convert_and_update(ubo_scene); + uniform_buffers[current_buffer]->convert_and_update(ubo_scene); } std::unique_ptr create_hpp_push_descriptors() diff --git a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h index 4f048ba62..722b40c88 100644 --- a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h +++ b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h @@ -44,15 +44,16 @@ class HPPPushDescriptors : public HPPApiVulkanSample void request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) override; // from HPPApiVulkanSample - void build_command_buffers() override; void on_update_ui_overlay(vkb::Drawer &drawer) override; void render(float delta_time) override; + void build_command_buffer(); + void create_descriptor_set_layout(); void create_pipeline(); void create_pipeline_layout(); void create_uniform_buffers(); - void draw(); + void draw(float delta_time); void initializeCamera(); void load_assets(); void update_cube_uniform_buffers(float delta_time); @@ -61,10 +62,10 @@ class HPPPushDescriptors : public HPPApiVulkanSample private: struct Cube { - HPPTexture texture; - std::unique_ptr uniform_buffer; - glm::vec3 rotation; - glm::mat4 model_mat; + HPPTexture texture; + std::array, max_concurrent_frames> uniform_buffers; + glm::vec3 rotation; + glm::mat4 model_mat; }; struct Models @@ -78,21 +79,16 @@ class HPPPushDescriptors : public HPPApiVulkanSample glm::mat4 view; }; - struct UniformBuffers - { - std::unique_ptr scene; - }; - private: - bool animate = true; - std::array cubes; - vk::DescriptorSetLayout descriptor_set_layout; - uint32_t max_push_descriptors = 0; - Models models; - vk::Pipeline pipeline; - vk::PipelineLayout pipeline_layout; - UboScene ubo_scene; - UniformBuffers uniform_buffers; + bool animate = true; + std::array cubes; + vk::DescriptorSetLayout descriptor_set_layout; + uint32_t max_push_descriptors = 0; + Models models; + vk::Pipeline pipeline; + vk::PipelineLayout pipeline_layout; + UboScene ubo_scene; + std::array, max_concurrent_frames> uniform_buffers; }; std::unique_ptr create_hpp_push_descriptors(); diff --git a/samples/extensions/push_descriptors/push_descriptors.cpp b/samples/extensions/push_descriptors/push_descriptors.cpp index ce347b094..9c87ea196 100644 --- a/samples/extensions/push_descriptors/push_descriptors.cpp +++ b/samples/extensions/push_descriptors/push_descriptors.cpp @@ -263,9 +263,6 @@ void PushDescriptors::prepare_uniform_buffers() cube.uniform_buffers[i] = std::make_unique(get_device(), sizeof(glm::mat4), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); } } - - update_uniform_buffers(); - update_cube_uniform_buffers(0.0f); } void PushDescriptors::update_uniform_buffers() @@ -357,7 +354,6 @@ bool PushDescriptors::prepare(const vkb::ApplicationOptions &options) prepare_uniform_buffers(); setup_descriptor_set_layout(); prepare_pipelines(); - build_command_buffers(); prepared = true; return true; } From f4cdc9752245798c60a024ed8f53ee919e9b778b Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Fri, 24 Apr 2026 17:17:55 +0200 Subject: [PATCH 07/23] Format and copyright --- framework/api_vulkan_sample.cpp | 3 +-- framework/api_vulkan_sample.h | 2 +- framework/hpp_api_vulkan_sample.cpp | 2 +- framework/hpp_api_vulkan_sample.h | 2 +- .../extensions/hpp_push_descriptors/hpp_push_descriptors.h | 4 ++-- samples/extensions/push_descriptors/push_descriptors.h | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/framework/api_vulkan_sample.cpp b/framework/api_vulkan_sample.cpp index 2ed3cae2e..1f02c019d 100644 --- a/framework/api_vulkan_sample.cpp +++ b/framework/api_vulkan_sample.cpp @@ -586,7 +586,6 @@ void ApiVulkanSample::submit_frame() .signalSemaphoreCount = 1, .pSignalSemaphores = &render_complete_semaphores[current_image_index]}; VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, wait_fences[current_buffer])); - } if (get_render_context().has_swapchain()) { @@ -762,7 +761,7 @@ void ApiVulkanSample::create_synchronization_primitives() { VK_CHECK(vkCreateFence(get_device().get_handle(), &fence_create_info, nullptr, &fence)); } - } + } } void ApiVulkanSample::create_command_pool() diff --git a/framework/api_vulkan_sample.h b/framework/api_vulkan_sample.h index 880c29595..8b816a3c0 100644 --- a/framework/api_vulkan_sample.h +++ b/framework/api_vulkan_sample.h @@ -197,7 +197,7 @@ class ApiVulkanSample : public vkb::VulkanSampleC std::array acquired_image_ready_semaphores{}; std::vector render_complete_semaphores{}; // @todo: use this - //std::array wait_fences; + // std::array wait_fences; /** * @brief Populates the swapchain_buffers vector with the image and imageviews diff --git a/framework/hpp_api_vulkan_sample.cpp b/framework/hpp_api_vulkan_sample.cpp index 9f603be22..30357c7e9 100644 --- a/framework/hpp_api_vulkan_sample.cpp +++ b/framework/hpp_api_vulkan_sample.cpp @@ -402,7 +402,7 @@ void HPPApiVulkanSample::create_command_buffers() // Create one command buffer for each swap chain image and reuse for rendering vk::CommandBufferAllocateInfo allocate_info{.commandPool = cmd_pool, .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = use_new_sync ? max_concurrent_frames : static_cast(get_render_context().get_render_frames().size()) }; + .commandBufferCount = use_new_sync ? max_concurrent_frames : static_cast(get_render_context().get_render_frames().size())}; draw_cmd_buffers = get_device().get_handle().allocateCommandBuffers(allocate_info); } diff --git a/framework/hpp_api_vulkan_sample.h b/framework/hpp_api_vulkan_sample.h index 34474d1fa..7fbb0a29e 100644 --- a/framework/hpp_api_vulkan_sample.h +++ b/framework/hpp_api_vulkan_sample.h @@ -156,7 +156,7 @@ class HPPApiVulkanSample : public vkb::VulkanSampleCpp // Synchronization primitives std::array acquired_image_ready_semaphores{}; - std::vector render_complete_semaphores{}; + std::vector render_complete_semaphores{}; // @todo: use this // std::array wait_fences; diff --git a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h index 722b40c88..f45ed347c 100644 --- a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h +++ b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2019-2025, Sascha Willems - * Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2019-2026, Sascha Willems + * Copyright (c) 2024-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * diff --git a/samples/extensions/push_descriptors/push_descriptors.h b/samples/extensions/push_descriptors/push_descriptors.h index 6eab4cda3..69442eb3b 100644 --- a/samples/extensions/push_descriptors/push_descriptors.h +++ b/samples/extensions/push_descriptors/push_descriptors.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * From 0d30069351bb86e65c626cdcbbb6d702f8250133 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 25 Apr 2026 17:49:19 +0200 Subject: [PATCH 08/23] Prepare GUI overlay for new sync --- framework/api_vulkan_sample.cpp | 2 +- framework/gui.h | 128 ++++++++++++++++++++++++-------- framework/vulkan_sample.h | 13 ++-- 3 files changed, 105 insertions(+), 38 deletions(-) diff --git a/framework/api_vulkan_sample.cpp b/framework/api_vulkan_sample.cpp index 1f02c019d..5671978e3 100644 --- a/framework/api_vulkan_sample.cpp +++ b/framework/api_vulkan_sample.cpp @@ -80,7 +80,7 @@ bool ApiVulkanSample::prepare(const vkb::ApplicationOptions &options) void ApiVulkanSample::prepare_gui() { - create_gui(*window, nullptr, 15.0f, true); + create_gui(*window, nullptr, 15.0f, true, use_new_sync); std::vector shader_stages = { load_shader("uioverlay/uioverlay.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), diff --git a/framework/gui.h b/framework/gui.h index d7971a449..e0ad538b2 100644 --- a/framework/gui.h +++ b/framework/gui.h @@ -167,7 +167,8 @@ class Gui Window const &window, StatsType const *stats = nullptr, float font_size = 21.0f, - bool explicit_update = false); + bool explicit_update = false, + bool use_new_sync = false); /** * @brief Destroys the Gui @@ -179,7 +180,7 @@ class Gui * @brief Draws the Gui * @param command_buffer Command buffer to register draw-commands */ - void draw(CommandBufferType command_buffer); + void draw(CommandBufferType command_buffer, uint32_t current_buffer = 0); /** * @brief Draws the Gui using an external pipeline @@ -333,7 +334,6 @@ class Gui std::vector fonts; std::unique_ptr font_image; std::unique_ptr font_image_view; - std::unique_ptr index_buffer; vk::Pipeline pipeline; vkb::core::HPPPipelineLayout *pipeline_layout = nullptr; bool prev_visible = true; @@ -342,7 +342,22 @@ class Gui StatsView stats_view; Timer timer; // Used to measure duration of input events bool two_finger_tap = false; // Whether or not the GUI has detected a multi touch gesture + std::unique_ptr index_buffer; std::unique_ptr vertex_buffer; + + // @todo + bool use_new_sync{false}; + uint32_t max_concurrent_frames = 2; + uint32_t current_buffer; + + struct Buffers + { + std::unique_ptr index_buffer; + std::unique_ptr vertex_buffer; + uint32_t index_count{0}; + uint32_t vertex_count{0}; + }; + std::vector buffers; }; using GuiC = Gui; @@ -350,11 +365,12 @@ using GuiCpp = Gui; template inline Gui::Gui( - vkb::rendering::RenderContext &render_context_, Window const &window, StatsType const *stats, float font_size, bool explicit_update) : + vkb::rendering::RenderContext &render_context_, Window const &window, StatsType const *stats, float font_size, bool explicit_update, bool use_new_sync) : render_context{render_context_}, content_scale_factor{window.get_content_scale_factor()}, dpi_factor{window.get_dpi_factor() * content_scale_factor}, explicit_update{explicit_update}, + use_new_sync{use_new_sync}, stats_view(stats) { ImGui::CreateContext(); @@ -532,8 +548,10 @@ inline Gui::~Gui() } template -inline void Gui::draw(CommandBufferType command_buffer) +inline void Gui::draw(CommandBufferType command_buffer, uint32_t current_buffer) { + // @todo + this->current_buffer = current_buffer; draw(command_buffer, pipeline, pipeline_layout->get_handle(), descriptor_set); } @@ -623,11 +641,10 @@ inline void push_transform = glm::scale(push_transform, glm::vec3(2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y, 0.0f)); command_buffer.pushConstants(pipeline_layout, vk::ShaderStageFlagBits::eVertex, 0, sizeof(glm::mat4), &push_transform); - vk::DeviceSize vertex_offsets[1] = {0}; - vk::Buffer vertex_buffer_handle = vertex_buffer->get_handle(); - command_buffer.bindVertexBuffers(0, vertex_buffer_handle, vertex_offsets); + vk::DeviceSize vertex_offsets[1] = {0}; + command_buffer.bindVertexBuffers(0, use_new_sync ? buffers[current_buffer].vertex_buffer->get_handle() : vertex_buffer->get_handle(), vertex_offsets); - command_buffer.bindIndexBuffer(index_buffer->get_handle(), 0, vk::IndexType::eUint16); + command_buffer.bindIndexBuffer(use_new_sync ? buffers[current_buffer].index_buffer->get_handle() : index_buffer->get_handle(), 0, vk::IndexType::eUint16); int32_t vertex_offset = 0; int32_t index_offset = 0; @@ -1409,34 +1426,83 @@ inline bool Gui::update_buffers() } bool updated = false; - if (!vertex_buffer->get_handle() || (vertex_buffer_size != vertex_buffer->get_size())) - { - vertex_buffer.reset(); - vertex_buffer = std::make_unique(render_context.get_device(), vertex_buffer_size, - vk::BufferUsageFlagBits::eVertexBuffer, - VMA_MEMORY_USAGE_GPU_TO_CPU); - vertex_buffer->set_debug_name("GUI vertex buffer"); - updated = true; - } - if (!index_buffer->get_handle() || (index_buffer_size != index_buffer->get_size())) + if (use_new_sync) { - index_buffer.reset(); - index_buffer = std::make_unique(render_context.get_device(), index_buffer_size, - vk::BufferUsageFlagBits::eIndexBuffer, - VMA_MEMORY_USAGE_GPU_TO_CPU); - index_buffer->set_debug_name("GUI index buffer"); - updated = true; + // @todo + if (buffers.size() < max_concurrent_frames) + { + buffers.resize(max_concurrent_frames); + } + + // Create buffers with multiple of a chunk size to minimize the need to recreate them + const VkDeviceSize chunkSize = 16384; + vertex_buffer_size = ((vertex_buffer_size + chunkSize - 1) / chunkSize) * chunkSize; + index_buffer_size = ((index_buffer_size + chunkSize - 1) / chunkSize) * chunkSize; + + if (!buffers[current_buffer].vertex_buffer || !buffers[current_buffer].vertex_buffer->get_handle() || (vertex_buffer_size < buffers[current_buffer].vertex_buffer->get_size())) + { + if (buffers[current_buffer].vertex_buffer) + { + buffers[current_buffer].vertex_buffer.reset(); + } + buffers[current_buffer].vertex_buffer = std::make_unique(render_context.get_device(), vertex_buffer_size, vk::BufferUsageFlagBits::eVertexBuffer, VMA_MEMORY_USAGE_GPU_TO_CPU); + buffers[current_buffer].vertex_buffer->set_debug_name("GUI vertex buffer"); + updated = true; + } + + if (!buffers[current_buffer].index_buffer || !buffers[current_buffer].index_buffer->get_handle() || (index_buffer_size < buffers[current_buffer].index_buffer->get_size())) + { + if (buffers[current_buffer].index_buffer) + { + buffers[current_buffer].index_buffer.reset(); + } + buffers[current_buffer].index_buffer = std::make_unique(render_context.get_device(), index_buffer_size, vk::BufferUsageFlagBits::eIndexBuffer, VMA_MEMORY_USAGE_GPU_TO_CPU); + buffers[current_buffer].index_buffer->set_debug_name("GUI index buffer"); + updated = true; + } + + // Upload data + upload_draw_data(draw_data, buffers[current_buffer].vertex_buffer->map(), buffers[current_buffer].index_buffer->map()); + + buffers[current_buffer].vertex_buffer->flush(); + buffers[current_buffer].index_buffer->flush(); + + // @todo: unmap here seems unnecessary + // buffers[current_buffer].vertex_buffer->unmap(); + // buffers[current_buffer].index_buffer->unmap(); } + else + { + if (!vertex_buffer->get_handle() || (vertex_buffer_size != vertex_buffer->get_size())) + { + vertex_buffer.reset(); + vertex_buffer = std::make_unique(render_context.get_device(), vertex_buffer_size, + vk::BufferUsageFlagBits::eVertexBuffer, + VMA_MEMORY_USAGE_GPU_TO_CPU); + vertex_buffer->set_debug_name("GUI vertex buffer"); + updated = true; + } - // Upload data - upload_draw_data(draw_data, vertex_buffer->map(), index_buffer->map()); + if (!index_buffer->get_handle() || (index_buffer_size != index_buffer->get_size())) + { + index_buffer.reset(); + index_buffer = std::make_unique(render_context.get_device(), index_buffer_size, + vk::BufferUsageFlagBits::eIndexBuffer, + VMA_MEMORY_USAGE_GPU_TO_CPU); + index_buffer->set_debug_name("GUI index buffer"); + updated = true; + } - vertex_buffer->flush(); - index_buffer->flush(); + // Upload data + upload_draw_data(draw_data, vertex_buffer->map(), index_buffer->map()); - vertex_buffer->unmap(); - index_buffer->unmap(); + vertex_buffer->flush(); + index_buffer->flush(); + + vertex_buffer->unmap(); + index_buffer->unmap(); + } return updated; } diff --git a/framework/vulkan_sample.h b/framework/vulkan_sample.h index 529de70ab..ff9df387c 100644 --- a/framework/vulkan_sample.h +++ b/framework/vulkan_sample.h @@ -252,7 +252,7 @@ class VulkanSample : public vkb::Application */ void add_device_extension(const char *extension, bool optional = false); - void create_gui(const Window &window, StatsType const *stats = nullptr, const float font_size = 21.0f, bool explicit_update = false); + void create_gui(const Window &window, StatsType const *stats = nullptr, const float font_size = 21.0f, bool explicit_update = false, bool use_new_sync = false); /** * @brief A helper to create a render context @@ -539,8 +539,8 @@ void VulkanSample::create_render_context_impl(const std::vectorget_properties().vsync == Window::Vsync::OFF) ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo; std::vector present_mode_priority_list{vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox, vk::PresentModeKHR::eImmediate}; #else - vk::PresentModeKHR present_mode = (window->get_properties().vsync == Window::Vsync::ON) ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eMailbox; - std::vector present_mode_priority_list{vk::PresentModeKHR::eMailbox, vk::PresentModeKHR::eImmediate, vk::PresentModeKHR::eFifo}; + vk::PresentModeKHR present_mode = (window->get_properties().vsync == Window::Vsync::ON) ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eMailbox; + std::vector present_mode_priority_list{vk::PresentModeKHR::eMailbox, vk::PresentModeKHR::eImmediate, vk::PresentModeKHR::eFifo}; #endif render_context = @@ -1381,11 +1381,11 @@ inline bool VulkanSample::prepare(const ApplicationOptions &options } template -inline void VulkanSample::create_gui(const Window &window, StatsType const *stats, const float font_size, bool explicit_update) +inline void VulkanSample::create_gui(const Window &window, StatsType const *stats, const float font_size, bool explicit_update, bool use_new_sync) { if constexpr (bindingType == BindingType::Cpp) { - gui = std::make_unique(get_render_context(), window, stats, font_size, explicit_update); + gui = std::make_unique(get_render_context(), window, stats, font_size, explicit_update, use_new_sync); } else { @@ -1393,7 +1393,8 @@ inline void VulkanSample::create_gui(const Window &window, StatsTyp window, reinterpret_cast(stats), font_size, - explicit_update); + explicit_update, + use_new_sync); } } From 06f91043c149ae67473ed5b981546b7802d5da32 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 25 Apr 2026 18:03:56 +0200 Subject: [PATCH 09/23] Comments --- framework/gui.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/gui.h b/framework/gui.h index e0ad538b2..fc7054137 100644 --- a/framework/gui.h +++ b/framework/gui.h @@ -162,6 +162,7 @@ class Gui * @param stats A statistics object (null if no statistics are used) * @param font_size The font size * @param explicit_update If true, update buffers every frame + * @param use_new_sync If true, uses frames-in-flight */ Gui(vkb::rendering::RenderContext &render_context, Window const &window, @@ -179,6 +180,7 @@ class Gui /** * @brief Draws the Gui * @param command_buffer Command buffer to register draw-commands + * @param current_buffer Index of the vertex and index buffers (only if using new sync) */ void draw(CommandBufferType command_buffer, uint32_t current_buffer = 0); From 40a9b3196b57e573cd1494c1df0320cba60bdcb7 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sun, 26 Apr 2026 08:48:08 +0200 Subject: [PATCH 10/23] Update samples to use new sync --- .../hpp_separate_image_sampler.cpp | 120 +++++++------ .../hpp_separate_image_sampler.h | 32 ++-- .../separate_image_sampler.cpp | 158 ++++++++---------- .../separate_image_sampler.h | 15 +- .../host_image_copy/host_image_copy.cpp | 99 +++++------ .../host_image_copy/host_image_copy.h | 9 +- 6 files changed, 191 insertions(+), 242 deletions(-) diff --git a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp index af69bf379..01a932a0e 100644 --- a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp +++ b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -27,6 +27,8 @@ HPPSeparateImageSampler::HPPSeparateImageSampler() zoom = -0.5f; rotation = {45.0f, 0.0f, 0.0f}; + + use_new_sync = true; } HPPSeparateImageSampler::~HPPSeparateImageSampler() @@ -85,8 +87,6 @@ bool HPPSeparateImageSampler::prepare(const vkb::ApplicationOptions &options) pipeline_layout = create_pipeline_layout({base_descriptor_set_layout, sampler_descriptor_set_layout}); pipeline = create_graphics_pipeline(); - build_command_buffers(); - prepared = true; } @@ -103,50 +103,45 @@ void HPPSeparateImageSampler::request_gpu_features(vkb::core::PhysicalDeviceCpp } } -void HPPSeparateImageSampler::build_command_buffers() +void HPPSeparateImageSampler::build_command_buffer() { vk::CommandBufferBeginInfo command_buffer_begin_info; std::array clear_values = {{default_clear_color, vk::ClearDepthStencilValue{0.0f, 0}}}; vk::RenderPassBeginInfo render_pass_begin_info{.renderPass = render_pass, + .framebuffer = framebuffers[current_image_index], .renderArea = {{0, 0}, extent}, .clearValueCount = static_cast(clear_values.size()), .pClearValues = clear_values.data()}; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - // Set target frame buffer - render_pass_begin_info.framebuffer = framebuffers[i]; + auto command_buffer = draw_cmd_buffers[current_buffer]; + command_buffer.begin(command_buffer_begin_info); - auto command_buffer = draw_cmd_buffers[i]; - command_buffer.begin(command_buffer_begin_info); + command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); - command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); + vk::Viewport viewport{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}; + command_buffer.setViewport(0, viewport); - vk::Viewport viewport{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}; - command_buffer.setViewport(0, viewport); + vk::Rect2D scissor{{0, 0}, extent}; + command_buffer.setScissor(0, scissor); - vk::Rect2D scissor{{0, 0}, extent}; - command_buffer.setScissor(0, scissor); + // Bind the uniform buffer and sampled image to set 0 + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, base_descriptor_set, {}); + // Bind the selected sampler to set 1 + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 1, sampler_descriptor_sets[selected_sampler], {}); + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - // Bind the uniform buffer and sampled image to set 0 - command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, base_descriptor_set, {}); - // Bind the selected sampler to set 1 - command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 1, sampler_descriptor_sets[selected_sampler], {}); - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); + vk::DeviceSize offset = 0; + command_buffer.bindVertexBuffers(0, vertex_buffer->get_handle(), offset); + command_buffer.bindIndexBuffer(index_buffer->get_handle(), 0, vk::IndexType::eUint32); - vk::DeviceSize offset = 0; - command_buffer.bindVertexBuffers(0, vertex_buffer->get_handle(), offset); - command_buffer.bindIndexBuffer(index_buffer->get_handle(), 0, vk::IndexType::eUint32); + command_buffer.drawIndexed(index_count, 1, 0, 0, 0); - command_buffer.drawIndexed(index_count, 1, 0, 0, 0); + draw_ui(command_buffer); - draw_ui(command_buffer); + command_buffer.endRenderPass(); - command_buffer.endRenderPass(); - - command_buffer.end(); - } + command_buffer.end(); } void HPPSeparateImageSampler::on_update_ui_overlay(vkb::Drawer &drawer) @@ -191,9 +186,9 @@ vk::DescriptorSetLayout HPPSeparateImageSampler::create_base_descriptor_set_layo vk::DescriptorPool HPPSeparateImageSampler::create_descriptor_pool() { std::array pool_sizes = { - {{vk::DescriptorType::eUniformBuffer, 1}, {vk::DescriptorType::eSampledImage, 1}, {vk::DescriptorType::eSampler, 2}}}; + {{vk::DescriptorType::eUniformBuffer, max_concurrent_frames}, {vk::DescriptorType::eSampledImage, max_concurrent_frames}, {vk::DescriptorType::eSampler, 2}}}; - vk::DescriptorPoolCreateInfo descriptor_pool_create_info{.maxSets = 3, + vk::DescriptorPoolCreateInfo descriptor_pool_create_info{.maxSets = max_concurrent_frames + static_cast(samplers.size()), .poolSizeCount = static_cast(pool_sizes.size()), .pPoolSizes = pool_sizes.data()}; @@ -276,13 +271,8 @@ vk::DescriptorSetLayout HPPSeparateImageSampler::create_sampler_descriptor_set_l void HPPSeparateImageSampler::draw() { HPPApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]); - - // Submit to queue - queue.submit(submit_info); - + update_uniform_buffers(); + build_command_buffer(); HPPApiVulkanSample::submit_frame(); } @@ -327,35 +317,37 @@ void HPPSeparateImageSampler::load_assets() // Prepare and initialize uniform buffer containing shader uniforms void HPPSeparateImageSampler::prepare_uniform_buffers() { - // Vertex shader uniform buffer block - uniform_buffer_vs = - std::make_unique(get_device(), sizeof(ubo_vs), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo_vs), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void HPPSeparateImageSampler::update_base_descriptor_set() { - vk::DescriptorBufferInfo buffer_descriptor{uniform_buffer_vs->get_handle(), 0, vk::WholeSize}; - - // Image info only references the image - vk::DescriptorImageInfo image_info{{}, texture.image->get_vk_image_view().get_handle(), vk::ImageLayout::eShaderReadOnlyOptimal}; - - // Sampled image descriptor - std::array write_descriptor_sets = { - { - {.dstSet = base_descriptor_set, - .dstBinding = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_descriptor}, // Binding 0 : Vertex shader uniform buffer - {.dstSet = base_descriptor_set, - .dstBinding = 1, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eSampledImage, - .pImageInfo = &image_info} // Binding 1 : Fragment shader sampled image - }}; - get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {}); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + vk::DescriptorBufferInfo buffer_descriptor{uniform_buffers[i]->get_handle(), 0, vk::WholeSize}; + + // Image info only references the image + vk::DescriptorImageInfo image_info{{}, texture.image->get_vk_image_view().get_handle(), vk::ImageLayout::eShaderReadOnlyOptimal}; + + // Sampled image descriptor + std::array write_descriptor_sets = { + { + {.dstSet = base_descriptor_set, + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &buffer_descriptor}, // Binding 0 : Vertex shader uniform buffer + {.dstSet = base_descriptor_set, + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampledImage, + .pImageInfo = &image_info} // Binding 1 : Fragment shader sampled image + }}; + get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {}); + } } void HPPSeparateImageSampler::update_sampler_descriptor_set(size_t index) @@ -384,7 +376,7 @@ void HPPSeparateImageSampler::update_uniform_buffers() ubo_vs.view_pos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f); - uniform_buffer_vs->convert_and_update(ubo_vs); + uniform_buffers[current_buffer]->convert_and_update(ubo_vs); } std::unique_ptr create_hpp_separate_image_sampler() diff --git a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.h b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.h index 34a597d18..45c61c15b 100644 --- a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.h +++ b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2023-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -53,7 +53,7 @@ class HPPSeparateImageSampler : public HPPApiVulkanSample void request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) override; // from HPPApiVulkanSample - void build_command_buffers() override; + void build_command_buffer(); void on_update_ui_overlay(vkb::Drawer &drawer) override; void render(float delta_time) override; void view_changed() override; @@ -73,20 +73,20 @@ class HPPSeparateImageSampler : public HPPApiVulkanSample void update_uniform_buffers(); private: - vk::DescriptorSet base_descriptor_set; - vk::DescriptorSetLayout base_descriptor_set_layout; - std::unique_ptr index_buffer; - uint32_t index_count = 0; - vk::Pipeline pipeline; - vk::PipelineLayout pipeline_layout; - vk::DescriptorSetLayout sampler_descriptor_set_layout; - std::array sampler_descriptor_sets; - std::array samplers; - int32_t selected_sampler = 0; - HPPTexture texture; - UBO ubo_vs; - std::unique_ptr uniform_buffer_vs; - std::unique_ptr vertex_buffer; + vk::DescriptorSet base_descriptor_set; + vk::DescriptorSetLayout base_descriptor_set_layout; + std::unique_ptr index_buffer; + uint32_t index_count = 0; + vk::Pipeline pipeline; + vk::PipelineLayout pipeline_layout; + vk::DescriptorSetLayout sampler_descriptor_set_layout; + std::array sampler_descriptor_sets; + std::array samplers; + int32_t selected_sampler = 0; + HPPTexture texture; + UBO ubo_vs; + std::array, max_concurrent_frames> uniform_buffers; + std::unique_ptr vertex_buffer; }; std::unique_ptr create_hpp_separate_image_sampler(); diff --git a/samples/api/separate_image_sampler/separate_image_sampler.cpp b/samples/api/separate_image_sampler/separate_image_sampler.cpp index 3559e75cf..635ae75c0 100644 --- a/samples/api/separate_image_sampler/separate_image_sampler.cpp +++ b/samples/api/separate_image_sampler/separate_image_sampler.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2021-2025, Sascha Willems +/* Copyright (c) 2021-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -26,6 +26,8 @@ SeparateImageSampler::SeparateImageSampler() zoom = -0.5f; rotation = {45.0f, 0.0f, 0.0f}; title = "Separate sampler and image"; + + use_new_sync = true; } SeparateImageSampler::~SeparateImageSampler() @@ -59,8 +61,11 @@ void SeparateImageSampler::request_gpu_features(vkb::core::PhysicalDeviceC &gpu) } } -void SeparateImageSampler::build_command_buffers() +void SeparateImageSampler::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -75,40 +80,35 @@ void SeparateImageSampler::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - // Set target frame buffer - render_pass_begin_info.framebuffer = framebuffers[i]; - - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - // Bind the uniform buffer and sampled image to set 0 - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &base_descriptor_set, 0, nullptr); - // Bind the selected sampler to set 1 - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 1, 1, &sampler_descriptor_sets[selected_sampler], 0, nullptr); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + // Bind the uniform buffer and sampled image to set 0 + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &base_descriptor_sets[current_buffer], 0, nullptr); + // Bind the selected sampler to set 1 + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 1, 1, &sampler_descriptor_sets[selected_sampler], 0, nullptr); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - VkDeviceSize offsets[1] = {0}; - vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, vertex_buffer->get(), offsets); - vkCmdBindIndexBuffer(draw_cmd_buffers[i], index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); + VkDeviceSize offsets[1] = {0}; + vkCmdBindVertexBuffers(draw_cmd_buffer, 0, 1, vertex_buffer->get(), offsets); + vkCmdBindIndexBuffer(draw_cmd_buffer, index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(draw_cmd_buffers[i], index_count, 1, 0, 0, 0); + vkCmdDrawIndexed(draw_cmd_buffer, index_count, 1, 0, 0, 0); - draw_ui(draw_cmd_buffers[i]); + draw_ui(draw_cmd_buffer); - vkCmdEndRenderPass(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffer); - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } void SeparateImageSampler::setup_samplers() @@ -156,14 +156,8 @@ void SeparateImageSampler::load_assets() void SeparateImageSampler::draw() { ApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - - // Submit to queue - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); - + update_uniform_buffers(); + build_command_buffer(); ApiVulkanSample::submit_frame(); } @@ -204,15 +198,15 @@ void SeparateImageSampler::generate_quad() void SeparateImageSampler::setup_descriptor_pool() { std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, max_concurrent_frames), vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_SAMPLER, 2)}; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info( static_cast(pool_sizes.size()), pool_sizes.data(), - 3); + max_concurrent_frames + static_cast(samplers.size())); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } @@ -266,42 +260,35 @@ void SeparateImageSampler::setup_descriptor_set_layout() void SeparateImageSampler::setup_descriptor_set() { // We separate the descriptor sets for the uniform buffer + image and samplers, so we don't need to duplicate the descriptors for the former - VkDescriptorSetAllocateInfo descriptor_set_alloc_info{}; - - // Descriptors set for the uniform buffer and the image - descriptor_set_alloc_info = - vkb::initializers::descriptor_set_allocate_info( - descriptor_pool, - &base_descriptor_set_layout, - 1); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &descriptor_set_alloc_info, &base_descriptor_set)); - - VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffer_vs); - - // Image info only references the image - VkDescriptorImageInfo image_info{}; - image_info.imageView = texture.image->get_vk_image_view().get_handle(); - image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - // Sampled image descriptor - VkWriteDescriptorSet image_write_descriptor_set{}; - image_write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - image_write_descriptor_set.dstSet = base_descriptor_set; - image_write_descriptor_set.dstBinding = 1; - image_write_descriptor_set.descriptorCount = 1; - image_write_descriptor_set.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - image_write_descriptor_set.pImageInfo = &image_info; - - std::vector write_descriptor_sets = { - // Binding 0 : Vertex shader uniform buffer - vkb::initializers::write_descriptor_set( - base_descriptor_set, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &buffer_descriptor), - // Binding 1 : Fragment shader sampled image - image_write_descriptor_set}; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + VkDescriptorSetAllocateInfo descriptor_set_alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &base_descriptor_set_layout, 1); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + // Descriptors set for the uniform buffer and the image + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &descriptor_set_alloc_info, &base_descriptor_sets[i])); + + // Uniform buffer + VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffers[i]); + + // Sampled image + VkDescriptorImageInfo image_info{}; + image_info.imageView = texture.image->get_vk_image_view().get_handle(); + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + // Sampled image descriptor + VkWriteDescriptorSet image_write_descriptor_set{}; + image_write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + image_write_descriptor_set.dstBinding = 1; + image_write_descriptor_set.descriptorCount = 1; + image_write_descriptor_set.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + image_write_descriptor_set.pImageInfo = &image_info; + image_write_descriptor_set.dstSet = base_descriptor_sets[i]; + + std::vector write_descriptor_sets = { + vkb::initializers::write_descriptor_set(base_descriptor_sets[i], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), + vkb::initializers::write_descriptor_set(base_descriptor_sets[i], VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, &image_info)}; + + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + } // Sets for each of the sampler descriptor_set_alloc_info.pSetLayouts = &sampler_descriptor_set_layout; @@ -418,13 +405,11 @@ void SeparateImageSampler::prepare_pipelines() // Prepare and initialize uniform buffer containing shader uniforms void SeparateImageSampler::prepare_uniform_buffers() { - // Vertex shader uniform buffer block - uniform_buffer_vs = std::make_unique(get_device(), - sizeof(ubo_vs), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + // Vertex shader uniform buffer block + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void SeparateImageSampler::update_uniform_buffers() @@ -440,7 +425,7 @@ void SeparateImageSampler::update_uniform_buffers() ubo_vs.view_pos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f); - uniform_buffer_vs->convert_and_update(ubo_vs); + uniform_buffers[current_buffer]->convert_and_update(ubo_vs); } bool SeparateImageSampler::prepare(const vkb::ApplicationOptions &options) @@ -457,7 +442,6 @@ bool SeparateImageSampler::prepare(const vkb::ApplicationOptions &options) prepare_pipelines(); setup_descriptor_pool(); setup_descriptor_set(); - build_command_buffers(); prepared = true; return true; } @@ -471,21 +455,13 @@ void SeparateImageSampler::render(float delta_time) draw(); } -void SeparateImageSampler::view_changed() -{ - update_uniform_buffers(); -} - void SeparateImageSampler::on_update_ui_overlay(vkb::Drawer &drawer) { if (drawer.header("Settings")) { const std::vector sampler_names = {"Linear filtering", "Nearest filtering"}; - if (drawer.combo_box("Sampler", &selected_sampler, sampler_names)) - { - update_uniform_buffers(); - } + drawer.combo_box("Sampler", &selected_sampler, sampler_names); } } diff --git a/samples/api/separate_image_sampler/separate_image_sampler.h b/samples/api/separate_image_sampler/separate_image_sampler.h index 42c65e037..184f89817 100644 --- a/samples/api/separate_image_sampler/separate_image_sampler.h +++ b/samples/api/separate_image_sampler/separate_image_sampler.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021-2025, Sascha Willems +/* Copyright (c) 2021-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -46,8 +46,6 @@ class SeparateImageSampler : public ApiVulkanSample std::unique_ptr index_buffer; uint32_t index_count; - std::unique_ptr uniform_buffer_vs; - struct { glm::mat4 projection; @@ -55,9 +53,11 @@ class SeparateImageSampler : public ApiVulkanSample glm::vec4 view_pos; } ubo_vs; - VkPipeline pipeline = VK_NULL_HANDLE; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; - VkDescriptorSet base_descriptor_set = VK_NULL_HANDLE; + std::array, max_concurrent_frames> uniform_buffers{}; + std::array base_descriptor_sets{}; + + VkPipeline pipeline = VK_NULL_HANDLE; + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; VkDescriptorSetLayout base_descriptor_set_layout; VkDescriptorSetLayout sampler_descriptor_set_layout; @@ -65,7 +65,7 @@ class SeparateImageSampler : public ApiVulkanSample SeparateImageSampler(); ~SeparateImageSampler() override; virtual void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; - void build_command_buffers() override; + void build_command_buffer(); void setup_samplers(); void load_assets(); void draw(); @@ -78,7 +78,6 @@ class SeparateImageSampler : public ApiVulkanSample void update_uniform_buffers(); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; - virtual void view_changed() override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; }; diff --git a/samples/extensions/host_image_copy/host_image_copy.cpp b/samples/extensions/host_image_copy/host_image_copy.cpp index 9e68e7290..db2287b7f 100644 --- a/samples/extensions/host_image_copy/host_image_copy.cpp +++ b/samples/extensions/host_image_copy/host_image_copy.cpp @@ -19,6 +19,8 @@ HostImageCopy::HostImageCopy() { + use_new_sync = true; + title = "Host image copy"; zoom = -4.0f; rotation = {-25.0f, 45.0f, 0.0f}; @@ -37,7 +39,6 @@ HostImageCopy::~HostImageCopy() vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr); vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout, nullptr); destroy_texture(texture); - uniform_buffer_vs.reset(); } } @@ -245,8 +246,11 @@ void HostImageCopy::destroy_texture(Texture texture) vkFreeMemory(get_device().get_handle(), texture.device_memory, nullptr); } -void HostImageCopy::build_command_buffers() +void HostImageCopy::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -261,46 +265,35 @@ void HostImageCopy::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - // Set target frame buffer - render_pass_begin_info.framebuffer = framebuffers[i]; - - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer], 0, nullptr); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - draw_model(cube, draw_cmd_buffers[i]); + draw_model(cube, draw_cmd_buffer); - draw_ui(draw_cmd_buffers[i]); + draw_ui(draw_cmd_buffer); - vkCmdEndRenderPass(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffer); - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } void HostImageCopy::draw() { ApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - - // Submit to queue - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); - + update_uniform_buffers(); + build_command_buffer(); ApiVulkanSample::submit_frame(); } @@ -308,7 +301,7 @@ void HostImageCopy::setup_descriptor_pool() { // Example uses one ubo and one image sampler std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames), vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)}; VkDescriptorPoolCreateInfo descriptor_pool_create_info = @@ -339,23 +332,24 @@ void HostImageCopy::setup_descriptor_set_layout() void HostImageCopy::setup_descriptor_set() { - VkDescriptorSetAllocateInfo alloc_info = - vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layout, 1); - - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); - - VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffer_vs); - VkDescriptorImageInfo image_descriptor; image_descriptor.imageView = texture.view; image_descriptor.sampler = texture.sampler; image_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - std::vector write_descriptor_sets = { - vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), - vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &image_descriptor)}; + VkDescriptorSetAllocateInfo alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layout, 1); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets[i])); + + VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffers[i]); + + std::vector write_descriptor_sets = { + vkb::initializers::write_descriptor_set(descriptor_sets[i], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), + vkb::initializers::write_descriptor_set(descriptor_sets[i], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &image_descriptor)}; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + } } void HostImageCopy::prepare_pipelines() @@ -426,13 +420,11 @@ void HostImageCopy::prepare_pipelines() // Prepare and initialize uniform buffer containing shader uniforms void HostImageCopy::prepare_uniform_buffers() { - // Vertex shader uniform buffer block - uniform_buffer_vs = std::make_unique(get_device(), - sizeof(ubo_vs), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + // Vertex shader uniform buffer block + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void HostImageCopy::update_uniform_buffers() @@ -448,7 +440,7 @@ void HostImageCopy::update_uniform_buffers() ubo_vs.view_pos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f); - uniform_buffer_vs->convert_and_update(ubo_vs); + uniform_buffers[current_buffer]->convert_and_update(ubo_vs); } bool HostImageCopy::prepare(const vkb::ApplicationOptions &options) @@ -464,7 +456,6 @@ bool HostImageCopy::prepare(const vkb::ApplicationOptions &options) prepare_pipelines(); setup_descriptor_pool(); setup_descriptor_set(); - build_command_buffers(); prepared = true; return true; } @@ -478,19 +469,11 @@ void HostImageCopy::render(float delta_time) draw(); } -void HostImageCopy::view_changed() -{ - update_uniform_buffers(); -} - void HostImageCopy::on_update_ui_overlay(vkb::Drawer &drawer) { if (drawer.header("Settings")) { - if (drawer.slider_float("LOD bias", &ubo_vs.lod_bias, 0.0f, static_cast(texture.mip_levels))) - { - update_uniform_buffers(); - } + drawer.slider_float("LOD bias", &ubo_vs.lod_bias, 0.0f, static_cast(texture.mip_levels)); } } diff --git a/samples/extensions/host_image_copy/host_image_copy.h b/samples/extensions/host_image_copy/host_image_copy.h index f727132df..b5af31ddd 100644 --- a/samples/extensions/host_image_copy/host_image_copy.h +++ b/samples/extensions/host_image_copy/host_image_copy.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2024-2025, Sascha Willems +/* Copyright (c) 2024-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -46,11 +46,11 @@ class HostImageCopy : public ApiVulkanSample float lod_bias = 0.0f; } ubo_vs; - std::unique_ptr uniform_buffer_vs; + std::array, max_concurrent_frames> uniform_buffers{}; + std::array descriptor_sets{}; VkPipeline pipeline{VK_NULL_HANDLE}; VkPipelineLayout pipeline_layout{VK_NULL_HANDLE}; - VkDescriptorSet descriptor_set{VK_NULL_HANDLE}; VkDescriptorSetLayout descriptor_set_layout{VK_NULL_HANDLE}; HostImageCopy(); @@ -59,7 +59,7 @@ class HostImageCopy : public ApiVulkanSample void load_texture(); void load_assets(); void destroy_texture(Texture texture); - void build_command_buffers() override; + void build_command_buffer(); void draw(); void setup_descriptor_pool(); void setup_descriptor_set_layout(); @@ -69,7 +69,6 @@ class HostImageCopy : public ApiVulkanSample void update_uniform_buffers(); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; - virtual void view_changed() override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; }; From 33c9a6471f701505377f8adc5e64af10e620b922 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sun, 26 Apr 2026 10:07:30 +0200 Subject: [PATCH 11/23] Update samples to use new sync --- samples/api/instancing/instancing.cpp | 185 +++++++++++--------------- samples/api/instancing/instancing.h | 17 +-- 2 files changed, 87 insertions(+), 115 deletions(-) diff --git a/samples/api/instancing/instancing.cpp b/samples/api/instancing/instancing.cpp index 33e7c299d..548cd64a6 100644 --- a/samples/api/instancing/instancing.cpp +++ b/samples/api/instancing/instancing.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -26,6 +26,8 @@ Instancing::Instancing() { title = "Instanced mesh rendering"; + + use_new_sync = true; } Instancing::~Instancing() @@ -66,8 +68,11 @@ void Instancing::request_gpu_features(vkb::core::PhysicalDeviceC &gpu) } }; -void Instancing::build_command_buffers() +void Instancing::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -80,72 +85,60 @@ void Instancing::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - // Set target frame buffer - render_pass_begin_info.framebuffer = framebuffers[i]; - - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - VkDeviceSize offsets[1] = {0}; + VkDeviceSize offsets[1] = {0}; - // Star field - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets.planet, 0, NULL); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.starfield); - vkCmdDraw(draw_cmd_buffers[i], 4, 1, 0, 0); + // Star field + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer].planet, 0, NULL); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.starfield); + vkCmdDraw(draw_cmd_buffer, 4, 1, 0, 0); - // Planet - auto &planet_vertex_buffer = models.planet->vertex_buffers.at("vertex_buffer"); - auto &planet_index_buffer = models.planet->index_buffer; - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets.planet, 0, NULL); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.planet); - vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, planet_vertex_buffer.get(), offsets); - vkCmdBindIndexBuffer(draw_cmd_buffers[i], planet_index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(draw_cmd_buffers[i], models.planet->vertex_indices, 1, 0, 0, 0); + // Planet + auto &planet_vertex_buffer = models.planet->vertex_buffers.at("vertex_buffer"); + auto &planet_index_buffer = models.planet->index_buffer; + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer].planet, 0, NULL); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.planet); + vkCmdBindVertexBuffers(draw_cmd_buffer, 0, 1, planet_vertex_buffer.get(), offsets); + vkCmdBindIndexBuffer(draw_cmd_buffer, planet_index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(draw_cmd_buffer, models.planet->vertex_indices, 1, 0, 0, 0); - // Instanced rocks - auto &rock_vertex_buffer = models.rock->vertex_buffers.at("vertex_buffer"); - auto &rock_index_buffer = models.rock->index_buffer; - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets.instanced_rocks, 0, NULL); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.instanced_rocks); - // Binding point 0 : Mesh vertex buffer - vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, rock_vertex_buffer.get(), offsets); - // Binding point 1 : Instance data buffer - vkCmdBindVertexBuffers(draw_cmd_buffers[i], 1, 1, &instance_buffer.buffer->get_handle(), offsets); - vkCmdBindIndexBuffer(draw_cmd_buffers[i], rock_index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); - // Render instances - vkCmdDrawIndexed(draw_cmd_buffers[i], models.rock->vertex_indices, INSTANCE_COUNT, 0, 0, 0); - - draw_ui(draw_cmd_buffers[i]); - - vkCmdEndRenderPass(draw_cmd_buffers[i]); - - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } + // Instanced rocks + auto &rock_vertex_buffer = models.rock->vertex_buffers.at("vertex_buffer"); + auto &rock_index_buffer = models.rock->index_buffer; + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer].instanced_rocks, 0, NULL); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.instanced_rocks); + // Binding point 0 : Mesh vertex buffer + vkCmdBindVertexBuffers(draw_cmd_buffer, 0, 1, rock_vertex_buffer.get(), offsets); + // Binding point 1 : Instance data buffer + vkCmdBindVertexBuffers(draw_cmd_buffer, 1, 1, &instance_buffer.buffer->get_handle(), offsets); + vkCmdBindIndexBuffer(draw_cmd_buffer, rock_index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); + // Render instances + vkCmdDrawIndexed(draw_cmd_buffer, models.rock->vertex_indices, INSTANCE_COUNT, 0, 0, 0); + + draw_ui(draw_cmd_buffer); + + vkCmdEndRenderPass(draw_cmd_buffer); + + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } void Instancing::load_assets() { - models.rock = load_model("scenes/rock.gltf"); - models.planet = load_model("scenes/planet.gltf"); - - // models.rock.loadFromFile(getAssetPath() + "scenes/rock.gltf", device.get(), queue); - // models.planet.loadFromFile(getAssetPath() + "scenes/planet.gltf", device.get(), queue); - + models.rock = load_model("scenes/rock.gltf"); + models.planet = load_model("scenes/planet.gltf"); textures.rocks = load_texture_array("textures/texturearray_rocks_color_rgba.ktx", vkb::sg::Image::Color); textures.planet = load_texture("textures/lavaplanet_color_rgba.ktx", vkb::sg::Image::Color); - - // textures.rocks.loadFromFile(getAssetPath() + "textures/texturearray_rocks_color_rgba.ktx", device.get(), queue); - // textures.planet.loadFromFile(getAssetPath() + "textures/lavaplanet_color_rgba.ktx", device.get(), queue); } void Instancing::setup_descriptor_pool() @@ -153,15 +146,15 @@ void Instancing::setup_descriptor_pool() // Example uses one ubo std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames * 2), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, max_concurrent_frames * 2), }; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info( vkb::to_u32(pool_sizes.size()), pool_sizes.data(), - 2); + max_concurrent_frames * 2); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } @@ -204,25 +197,27 @@ void Instancing::setup_descriptor_set() descriptor_set_alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layout, 1); - // Instanced rocks - VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffers.scene); - VkDescriptorImageInfo image_descriptor = create_descriptor(textures.rocks); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &descriptor_set_alloc_info, &descriptor_sets.instanced_rocks)); - write_descriptor_sets = { - vkb::initializers::write_descriptor_set(descriptor_sets.instanced_rocks, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), // Binding 0 : Vertex shader uniform buffer - vkb::initializers::write_descriptor_set(descriptor_sets.instanced_rocks, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &image_descriptor) // Binding 1 : Color map - }; - vkUpdateDescriptorSets(get_device().get_handle(), vkb::to_u32(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + // Instanced rocks + VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffers[i]); + VkDescriptorImageInfo image_descriptor = create_descriptor(textures.rocks); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &descriptor_set_alloc_info, &descriptor_sets[i].instanced_rocks)); + write_descriptor_sets = { + vkb::initializers::write_descriptor_set(descriptor_sets[i].instanced_rocks, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), // Binding 0 : Vertex shader uniform buffer + vkb::initializers::write_descriptor_set(descriptor_sets[i].instanced_rocks, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &image_descriptor) // Binding 1 : Color map + }; + vkUpdateDescriptorSets(get_device().get_handle(), vkb::to_u32(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); - // Planet - buffer_descriptor = create_descriptor(*uniform_buffers.scene); - image_descriptor = create_descriptor(textures.planet); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &descriptor_set_alloc_info, &descriptor_sets.planet)); - write_descriptor_sets = { - vkb::initializers::write_descriptor_set(descriptor_sets.planet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), // Binding 0 : Vertex shader uniform buffer - vkb::initializers::write_descriptor_set(descriptor_sets.planet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &image_descriptor) // Binding 1 : Color map - }; - vkUpdateDescriptorSets(get_device().get_handle(), vkb::to_u32(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); + // Planet + image_descriptor = create_descriptor(textures.planet); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &descriptor_set_alloc_info, &descriptor_sets[i].planet)); + write_descriptor_sets = { + vkb::initializers::write_descriptor_set(descriptor_sets[i].planet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &buffer_descriptor), // Binding 0 : Vertex shader uniform buffer + vkb::initializers::write_descriptor_set(descriptor_sets[i].planet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &image_descriptor) // Binding 1 : Color map + }; + vkUpdateDescriptorSets(get_device().get_handle(), vkb::to_u32(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); + } } void Instancing::prepare_pipelines() @@ -427,15 +422,13 @@ void Instancing::prepare_instance_data() void Instancing::prepare_uniform_buffers() { - uniform_buffers.scene = std::make_unique(get_device(), - sizeof(ubo_vs), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffer(0.0f); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } -void Instancing::update_uniform_buffer(float delta_time) +void Instancing::update_uniform_buffers(float delta_time) { ubo_vs.projection = camera.matrices.perspective; ubo_vs.view = camera.matrices.view; @@ -446,20 +439,14 @@ void Instancing::update_uniform_buffer(float delta_time) ubo_vs.glob_speed += delta_time * 0.01f; } - uniform_buffers.scene->convert_and_update(ubo_vs); + uniform_buffers[current_buffer]->convert_and_update(ubo_vs); } -void Instancing::draw() +void Instancing::draw(float delta_time) { ApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - - // Submit to queue - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); - + update_uniform_buffers(delta_time); + build_command_buffer(); ApiVulkanSample::submit_frame(); } @@ -483,7 +470,6 @@ bool Instancing::prepare(const vkb::ApplicationOptions &options) prepare_pipelines(); setup_descriptor_pool(); setup_descriptor_set(); - build_command_buffers(); prepared = true; return true; } @@ -494,11 +480,7 @@ void Instancing::render(float delta_time) { return; } - draw(); - if (!paused || camera.updated) - { - update_uniform_buffer(delta_time); - } + draw(delta_time); } void Instancing::on_update_ui_overlay(vkb::Drawer &drawer) @@ -509,13 +491,6 @@ void Instancing::on_update_ui_overlay(vkb::Drawer &drawer) } } -bool Instancing::resize(const uint32_t width, const uint32_t height) -{ - ApiVulkanSample::resize(width, height); - rebuild_command_buffers(); - return true; -} - std::unique_ptr create_instancing() { return std::make_unique(); diff --git a/samples/api/instancing/instancing.h b/samples/api/instancing/instancing.h index 22c3c2da0..72e9c5f5c 100644 --- a/samples/api/instancing/instancing.h +++ b/samples/api/instancing/instancing.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -69,10 +69,7 @@ class Instancing : public ApiVulkanSample float glob_speed = 0.0f; } ubo_vs; - struct UniformBuffers - { - std::unique_ptr scene; - } uniform_buffers; + std::array, max_concurrent_frames> uniform_buffers{}; VkPipelineLayout pipeline_layout; struct Pipelines @@ -87,12 +84,13 @@ class Instancing : public ApiVulkanSample { VkDescriptorSet instanced_rocks; VkDescriptorSet planet; - } descriptor_sets; + }; + std::array descriptor_sets; Instancing(); ~Instancing(); virtual void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; - void build_command_buffers() override; + void build_command_buffer(); void load_assets(); void setup_descriptor_pool(); void setup_descriptor_set_layout(); @@ -100,12 +98,11 @@ class Instancing : public ApiVulkanSample void prepare_pipelines(); void prepare_instance_data(); void prepare_uniform_buffers(); - void update_uniform_buffer(float delta_time); - void draw(); + void update_uniform_buffers(float delta_time); + void draw(float delta_time); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; - virtual bool resize(const uint32_t width, const uint32_t height) override; }; std::unique_ptr create_instancing(); From b608000a717e080f5d044b7f9dfd72762009879f Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sun, 26 Apr 2026 14:38:31 +0200 Subject: [PATCH 12/23] Clang format --- .../hpp_separate_image_sampler/hpp_separate_image_sampler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp index 01a932a0e..eca4fc449 100644 --- a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp +++ b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp @@ -188,7 +188,7 @@ vk::DescriptorPool HPPSeparateImageSampler::create_descriptor_pool() std::array pool_sizes = { {{vk::DescriptorType::eUniformBuffer, max_concurrent_frames}, {vk::DescriptorType::eSampledImage, max_concurrent_frames}, {vk::DescriptorType::eSampler, 2}}}; - vk::DescriptorPoolCreateInfo descriptor_pool_create_info{.maxSets = max_concurrent_frames + static_cast(samplers.size()), + vk::DescriptorPoolCreateInfo descriptor_pool_create_info{.maxSets = max_concurrent_frames + static_cast(samplers.size()), .poolSizeCount = static_cast(pool_sizes.size()), .pPoolSizes = pool_sizes.data()}; From 62b682fe8f167e2691e9b880804d1fce31267bb9 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 30 Apr 2026 17:45:08 +0200 Subject: [PATCH 13/23] Framework adjustments for new sync --- framework/api_vulkan_sample.cpp | 2 +- framework/api_vulkan_sample.h | 3 --- framework/common/vk_common.h | 3 +++ framework/gui.h | 17 ++++++----------- framework/hpp_api_vulkan_sample.h | 3 --- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/framework/api_vulkan_sample.cpp b/framework/api_vulkan_sample.cpp index 5671978e3..c3a47df76 100644 --- a/framework/api_vulkan_sample.cpp +++ b/framework/api_vulkan_sample.cpp @@ -524,7 +524,7 @@ void ApiVulkanSample::draw_ui(const VkCommandBuffer command_buffer, uint32_t swa vkCmdSetViewport(command_buffer, 0, 1, &viewport); vkCmdSetScissor(command_buffer, 0, 1, &scissor); - get_gui().draw(command_buffer, swapchain_buffers[swapchain_image_index].view, width, height); + get_gui().draw(command_buffer, swapchain_buffers[swapchain_image_index].view, width, height, current_buffer); } } diff --git a/framework/api_vulkan_sample.h b/framework/api_vulkan_sample.h index 8b816a3c0..826780f18 100644 --- a/framework/api_vulkan_sample.h +++ b/framework/api_vulkan_sample.h @@ -127,9 +127,6 @@ class ApiVulkanSample : public vkb::VulkanSampleC // @todo bool use_new_sync = false; - // @todo - constexpr static uint32_t max_concurrent_frames = 2; - // Stores the swapchain image buffers std::vector swapchain_buffers; diff --git a/framework/common/vk_common.h b/framework/common/vk_common.h index 8ddaf4a76..787f6177e 100644 --- a/framework/common/vk_common.h +++ b/framework/common/vk_common.h @@ -34,6 +34,9 @@ #define DEFAULT_FENCE_TIMEOUT 100000000000 // Default fence timeout in nanoseconds +// Max. number of frames in floight +constexpr uint32_t max_concurrent_frames = 2; + template using ShaderStageMap = std::map; diff --git a/framework/gui.h b/framework/gui.h index fc7054137..32e0dd5e6 100644 --- a/framework/gui.h +++ b/framework/gui.h @@ -209,7 +209,7 @@ class Gui * This method starts its own rendering pass, draws the UI, and ends the pass. * Call this after ending your main rendering pass (vkCmdEndRenderingKHR). */ - void draw(CommandBufferType command_buffer, ImageViewType swapchain_view, uint32_t width, uint32_t height); + void draw(CommandBufferType command_buffer, ImageViewType swapchain_view, uint32_t width, uint32_t height, uint32_t current_buffer = 0); Drawer &get_drawer(); @@ -349,7 +349,6 @@ class Gui // @todo bool use_new_sync{false}; - uint32_t max_concurrent_frames = 2; uint32_t current_buffer; struct Buffers @@ -359,7 +358,7 @@ class Gui uint32_t index_count{0}; uint32_t vertex_count{0}; }; - std::vector buffers; + std::array buffers; }; using GuiC = Gui; @@ -552,14 +551,16 @@ inline Gui::~Gui() template inline void Gui::draw(CommandBufferType command_buffer, uint32_t current_buffer) { - // @todo + // @todo: Sascha this->current_buffer = current_buffer; draw(command_buffer, pipeline, pipeline_layout->get_handle(), descriptor_set); } template -inline void Gui::draw(CommandBufferType command_buffer, ImageViewType swapchain_view, uint32_t width, uint32_t height) +inline void Gui::draw(CommandBufferType command_buffer, ImageViewType swapchain_view, uint32_t width, uint32_t height, uint32_t current_buffer) { + // @todo: Sascha + this->current_buffer = current_buffer; if constexpr (bindingType == BindingType::Cpp) { draw_impl(command_buffer, swapchain_view, width, height); @@ -1431,12 +1432,6 @@ inline bool Gui::update_buffers() if (use_new_sync) { - // @todo - if (buffers.size() < max_concurrent_frames) - { - buffers.resize(max_concurrent_frames); - } - // Create buffers with multiple of a chunk size to minimize the need to recreate them const VkDeviceSize chunkSize = 16384; vertex_buffer_size = ((vertex_buffer_size + chunkSize - 1) / chunkSize) * chunkSize; diff --git a/framework/hpp_api_vulkan_sample.h b/framework/hpp_api_vulkan_sample.h index 7fbb0a29e..d096be708 100644 --- a/framework/hpp_api_vulkan_sample.h +++ b/framework/hpp_api_vulkan_sample.h @@ -87,9 +87,6 @@ class HPPApiVulkanSample : public vkb::VulkanSampleCpp // @todo bool use_new_sync = false; - // @todo - constexpr static uint32_t max_concurrent_frames = 2; - /// Stores the swapchain image buffers std::vector swapchain_buffers; From a445d42164e440a591d7befdfc94ce8f4e37f7c3 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 30 Apr 2026 18:35:45 +0200 Subject: [PATCH 14/23] Document new parameter --- framework/gui.h | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/gui.h b/framework/gui.h index 32e0dd5e6..f3014ab6f 100644 --- a/framework/gui.h +++ b/framework/gui.h @@ -205,6 +205,7 @@ class Gui * @param swapchain_view The swapchain image view to render UI onto * @param width Render area width * @param height Render area height + * @param current_buffer Index of the vertex and index buffers (only if using new sync) * * This method starts its own rendering pass, draws the UI, and ends the pass. * Call this after ending your main rendering pass (vkCmdEndRenderingKHR). From fa4c2aa1282d80d9a00070a416548beba7900ec3 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 2 May 2026 12:37:46 +0200 Subject: [PATCH 15/23] Temp fix for GUI issues with dynamic rendering samples --- framework/gui.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/gui.h b/framework/gui.h index f3014ab6f..568db5e19 100644 --- a/framework/gui.h +++ b/framework/gui.h @@ -635,6 +635,12 @@ inline void return; } + // @todo: Sample using dynamic rendering seem to initialize differnt, so buffer might be empty, need to check why + if (use_new_sync && !buffers[current_buffer].vertex_buffer) + { + return; + } + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptor_set, {}); From 3173529f6459facc7740367590925f61b125a0c3 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 2 May 2026 12:38:05 +0200 Subject: [PATCH 16/23] Update samples to use new sync --- .../dynamic_rendering/dynamic_rendering.cpp | 233 +++++----- .../dynamic_rendering/dynamic_rendering.h | 7 +- .../dynamic_rendering_local_read.cpp | 425 +++++++++--------- .../dynamic_rendering_local_read.h | 17 +- 4 files changed, 344 insertions(+), 338 deletions(-) diff --git a/samples/extensions/dynamic_rendering/dynamic_rendering.cpp b/samples/extensions/dynamic_rendering/dynamic_rendering.cpp index af9818095..ae940fcc4 100644 --- a/samples/extensions/dynamic_rendering/dynamic_rendering.cpp +++ b/samples/extensions/dynamic_rendering/dynamic_rendering.cpp @@ -22,6 +22,8 @@ DynamicRendering::DynamicRendering() : enable_dynamic(true) { + use_new_sync = true; + title = "Dynamic Rendering"; add_device_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); @@ -35,7 +37,6 @@ DynamicRendering::~DynamicRendering() textures = {}; skybox.reset(); object.reset(); - ubo.reset(); vkDestroyPipeline(get_device().get_handle(), model_pipeline, VK_NULL_HANDLE); vkDestroyPipeline(get_device().get_handle(), skybox_pipeline, VK_NULL_HANDLE); @@ -114,9 +115,10 @@ void DynamicRendering::load_assets() void DynamicRendering::prepare_uniform_buffers() { - ubo = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void DynamicRendering::update_uniform_buffers() @@ -125,7 +127,7 @@ void DynamicRendering::update_uniform_buffers() ubo_vs.modelview = camera.matrices.view * glm::mat4(1.f); ubo_vs.inverse_modelview = glm::inverse(camera.matrices.view); ubo_vs.skybox_modelview = camera.matrices.view; - ubo->convert_and_update(ubo_vs); + uniform_buffers[current_buffer]->convert_and_update(ubo_vs); } void DynamicRendering::setup_descriptor_set_layout() @@ -156,15 +158,19 @@ void DynamicRendering::create_descriptor_sets() &descriptor_set_layout, 1); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); + VkDescriptorImageInfo environment_image_descriptor = create_descriptor(textures.envmap); + + for (auto i = 0; i < uniform_buffers.size(); i++) + { + VkDescriptorBufferInfo matrix_buffer_descriptor = create_descriptor(*uniform_buffers[current_buffer]); - VkDescriptorBufferInfo matrix_buffer_descriptor = create_descriptor(*ubo); - VkDescriptorImageInfo environment_image_descriptor = create_descriptor(textures.envmap); - std::vector write_descriptor_sets = { - vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &matrix_buffer_descriptor), - vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &environment_image_descriptor), - }; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets[i])); + std::vector write_descriptor_sets = { + vkb::initializers::write_descriptor_set(descriptor_sets[i], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &matrix_buffer_descriptor), + vkb::initializers::write_descriptor_set(descriptor_sets[i], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &environment_image_descriptor), + }; + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + } } void DynamicRendering::create_descriptor_pool() @@ -330,129 +336,126 @@ void DynamicRendering::setup_framebuffer() void DynamicRendering::draw() { ApiVulkanSample::prepare_frame(); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + update_uniform_buffers(); + build_command_buffer(); ApiVulkanSample::submit_frame(); } -void DynamicRendering::build_command_buffers() +void DynamicRendering::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + std::array clear_values{}; clear_values[0].color = {{0.0f, 0.0f, 0.0f, 0.0f}}; clear_values[1].depthStencil = {0.0f, 0}; - int i = -1; - for (auto &draw_cmd_buffer : draw_cmd_buffers) - { - i++; - auto command_begin = vkb::initializers::command_buffer_begin_info(); - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_begin)); + auto command_begin = vkb::initializers::command_buffer_begin_info(); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_begin)); - auto draw_scene = [&] { - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); + auto draw_scene = [&] { + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); - vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - // One descriptor set is used, and the draw type is toggled by a specialization constant - vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr); + // One descriptor set is used, and the draw type is toggled by a specialization constant + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer], 0, nullptr); - // skybox - vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, skybox_pipeline); - draw_model(skybox, draw_cmd_buffer); + // skybox + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, skybox_pipeline); + draw_model(skybox, draw_cmd_buffer); - // object - vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, model_pipeline); - draw_model(object, draw_cmd_buffer); - }; + // object + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, model_pipeline); + draw_model(object, draw_cmd_buffer); + }; - VkImageSubresourceRange range{}; - range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.baseMipLevel = 0; - range.levelCount = VK_REMAINING_MIP_LEVELS; - range.baseArrayLayer = 0; - range.layerCount = VK_REMAINING_ARRAY_LAYERS; + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; - VkImageSubresourceRange depth_range{range}; - depth_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + VkImageSubresourceRange depth_range{range}; + depth_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - if (enable_dynamic) + if (enable_dynamic) + { + vkb::image_layout_transition(draw_cmd_buffer, + swapchain_buffers[current_image_index].image, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + range); + + vkb::image_layout_transition(draw_cmd_buffer, + depth_stencil.image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + depth_range); + + VkRenderingAttachmentInfoKHR color_attachment_info = vkb::initializers::rendering_attachment_info(); + color_attachment_info.imageView = swapchain_buffers[current_image_index].view; + color_attachment_info.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + color_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; + color_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment_info.clearValue = clear_values[0]; + + VkRenderingAttachmentInfoKHR depth_attachment_info = vkb::initializers::rendering_attachment_info(); + depth_attachment_info.imageView = depth_stencil.view; + depth_attachment_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + depth_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; + depth_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depth_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depth_attachment_info.clearValue = clear_values[1]; + + auto render_area = VkRect2D{VkOffset2D{}, VkExtent2D{width, height}}; + auto render_info = vkb::initializers::rendering_info(render_area, 1, &color_attachment_info); + render_info.layerCount = 1; + render_info.pDepthAttachment = &depth_attachment_info; + if (!vkb::is_depth_only_format(depth_format)) { - vkb::image_layout_transition(draw_cmd_buffer, - swapchain_buffers[i].image, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - range); - - vkb::image_layout_transition(draw_cmd_buffer, - depth_stencil.image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - depth_range); - - VkRenderingAttachmentInfoKHR color_attachment_info = vkb::initializers::rendering_attachment_info(); - color_attachment_info.imageView = swapchain_buffers[i].view; // color_attachment.image_view; - color_attachment_info.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - color_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; - color_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - color_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - color_attachment_info.clearValue = clear_values[0]; - - VkRenderingAttachmentInfoKHR depth_attachment_info = vkb::initializers::rendering_attachment_info(); - depth_attachment_info.imageView = depth_stencil.view; - depth_attachment_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; - depth_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; - depth_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depth_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depth_attachment_info.clearValue = clear_values[1]; - - auto render_area = VkRect2D{VkOffset2D{}, VkExtent2D{width, height}}; - auto render_info = vkb::initializers::rendering_info(render_area, 1, &color_attachment_info); - render_info.layerCount = 1; - render_info.pDepthAttachment = &depth_attachment_info; - if (!vkb::is_depth_only_format(depth_format)) - { - render_info.pStencilAttachment = &depth_attachment_info; - } - - vkCmdBeginRenderingKHR(draw_cmd_buffer, &render_info); - draw_scene(); - vkCmdEndRenderingKHR(draw_cmd_buffer); - - draw_ui(draw_cmd_buffer, i); - - vkb::image_layout_transition(draw_cmd_buffer, - swapchain_buffers[i].image, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - range); + render_info.pStencilAttachment = &depth_attachment_info; } - else - { - VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info(); - render_pass_begin_info.renderPass = render_pass; - render_pass_begin_info.framebuffer = framebuffers[i]; - render_pass_begin_info.renderArea.extent.width = width; - render_pass_begin_info.renderArea.extent.height = height; - render_pass_begin_info.clearValueCount = static_cast(clear_values.size()); - render_pass_begin_info.pClearValues = clear_values.data(); - vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderingKHR(draw_cmd_buffer, &render_info); + draw_scene(); + vkCmdEndRenderingKHR(draw_cmd_buffer); - draw_scene(); - draw_ui(draw_cmd_buffer); + draw_ui(draw_cmd_buffer, current_image_index); - vkCmdEndRenderPass(draw_cmd_buffer); - } + vkb::image_layout_transition(draw_cmd_buffer, + swapchain_buffers[current_image_index].image, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + range); + } + else + { + VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info(); + render_pass_begin_info.renderPass = render_pass; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; + render_pass_begin_info.renderArea.extent.width = width; + render_pass_begin_info.renderArea.extent.height = height; + render_pass_begin_info.clearValueCount = static_cast(clear_values.size()); + render_pass_begin_info.pClearValues = clear_values.data(); + + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + + draw_scene(); + draw_ui(draw_cmd_buffer); - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); + vkCmdEndRenderPass(draw_cmd_buffer); } + + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } void DynamicRendering::render(float delta_time) @@ -462,10 +465,6 @@ void DynamicRendering::render(float delta_time) return; } draw(); - if (camera.updated) - { - update_uniform_buffers(); - } } void DynamicRendering::view_changed() diff --git a/samples/extensions/dynamic_rendering/dynamic_rendering.h b/samples/extensions/dynamic_rendering/dynamic_rendering.h index 31a2b3ad3..4f1d5e928 100644 --- a/samples/extensions/dynamic_rendering/dynamic_rendering.h +++ b/samples/extensions/dynamic_rendering/dynamic_rendering.h @@ -30,7 +30,7 @@ class DynamicRendering : public ApiVulkanSample bool prepare(const vkb::ApplicationOptions &options) override; void render(float delta_time) override; - void build_command_buffers() override; + void build_command_buffer(); void view_changed() override; void on_update_ui_overlay(vkb::Drawer &drawer) override; void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; @@ -67,12 +67,13 @@ class DynamicRendering : public ApiVulkanSample std::unique_ptr skybox; std::unique_ptr object; - std::unique_ptr ubo; + + std::array, max_concurrent_frames> uniform_buffers{}; + std::array descriptor_sets{}; VkPipeline model_pipeline{VK_NULL_HANDLE}; VkPipeline skybox_pipeline{VK_NULL_HANDLE}; VkPipelineLayout pipeline_layout{VK_NULL_HANDLE}; - VkDescriptorSet descriptor_set{VK_NULL_HANDLE}; VkDescriptorSetLayout descriptor_set_layout{VK_NULL_HANDLE}; VkDescriptorPool descriptor_pool{VK_NULL_HANDLE}; diff --git a/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.cpp b/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.cpp index 50c21d89d..05f8f5b36 100644 --- a/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.cpp +++ b/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.cpp @@ -23,6 +23,8 @@ DynamicRenderingLocalRead::DynamicRenderingLocalRead() { + use_new_sync = true; + title = "Dynamic Rendering local read"; camera.type = vkb::CameraType::FirstPerson; @@ -122,9 +124,12 @@ void DynamicRenderingLocalRead::setup_framebuffer() for (size_t i = 0; i < descriptor_image_infos.size(); i++) { - write_descriptor_sets.push_back(vkb::initializers::write_descriptor_set(composition_pass.descriptor_set, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, i, &descriptor_image_infos[i])); + write_descriptor_sets.push_back(vkb::initializers::write_descriptor_set(composition_pass.descriptor_sets[0], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, i, &descriptor_image_infos[i])); + } + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + write_descriptor_sets.push_back(vkb::initializers::write_descriptor_set(scene_transparent_pass.descriptor_sets[i], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 0, &descriptor_image_infos[0])); } - write_descriptor_sets.push_back(vkb::initializers::write_descriptor_set(scene_transparent_pass.descriptor_set, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 0, &descriptor_image_infos[0])); vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); } @@ -434,10 +439,13 @@ void DynamicRenderingLocalRead::create_attachments() void DynamicRenderingLocalRead::prepare_buffers() { - buffers.ubo_vs = std::make_unique(get_device(), sizeof(shader_data_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - buffers.ssbo_lights = std::make_unique(get_device(), lights.size() * sizeof(Light), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + uniform_buffers[i] = std::make_unique(get_device(), sizeof(shader_data_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } + + lights_buffer = std::make_unique(get_device(), lights.size() * sizeof(Light), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - update_uniform_buffer(); update_lights_buffer(); } @@ -456,7 +464,7 @@ void DynamicRenderingLocalRead::update_lights_buffer() light.color = glm::vec3(rnd_col(rnd_gen), rnd_col(rnd_gen), rnd_col(rnd_gen)) * 2.0f; } - buffers.ssbo_lights->convert_and_update(lights); + lights_buffer->convert_and_update(lights); } void DynamicRenderingLocalRead::update_uniform_buffer() @@ -464,7 +472,7 @@ void DynamicRenderingLocalRead::update_uniform_buffer() shader_data_vs.projection = camera.matrices.perspective; shader_data_vs.view = camera.matrices.view; shader_data_vs.model = glm::mat4(1.f); - buffers.ubo_vs->convert_and_update(shader_data_vs); + uniform_buffers[current_buffer]->convert_and_update(shader_data_vs); } void DynamicRenderingLocalRead::prepare_layouts_and_descriptors() @@ -505,12 +513,12 @@ void DynamicRenderingLocalRead::prepare_layouts_and_descriptors() // Pool std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames * 2), vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 4), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, max_concurrent_frames * 1), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, max_concurrent_frames + 3), }; - uint32_t num_descriptor_sets = 4; + uint32_t num_descriptor_sets = 2 * max_concurrent_frames + 1; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info(static_cast(pool_sizes.size()), pool_sizes.data(), num_descriptor_sets); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); @@ -531,37 +539,45 @@ void DynamicRenderingLocalRead::prepare_layouts_and_descriptors() VkDescriptorImageInfo tex_descriptor_normal = vkb::initializers::descriptor_image_info(VK_NULL_HANDLE, attachments.normal.view, image_layout); VkDescriptorImageInfo tex_descriptor_albedo = vkb::initializers::descriptor_image_info(VK_NULL_HANDLE, attachments.albedo.view, image_layout); - VkDescriptorBufferInfo ubo_vs_descriptor = create_descriptor(*buffers.ubo_vs); - VkDescriptorBufferInfo ssbo_lights_descriptor = create_descriptor(*buffers.ssbo_lights); + VkDescriptorBufferInfo ssbo_lights_descriptor = create_descriptor(*lights_buffer); VkDescriptorImageInfo glass_image_descriptor = create_descriptor(textures.transparent_glass); - // Opaque scene parts - allocInfo = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &scene_opaque_pass.descriptor_set_layout, 1); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &allocInfo, &scene_opaque_pass.descriptor_set)); - write_descriptor_sets = { - // Binding 0: Vertex shader uniform buffer - vkb::initializers::write_descriptor_set(scene_opaque_pass.descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &ubo_vs_descriptor)}; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + // Sets that use uniform buffers need to be per frame-in-flight + scene_opaque_pass.descriptor_sets.resize(max_concurrent_frames); + scene_transparent_pass.descriptor_sets.resize(max_concurrent_frames); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + VkDescriptorBufferInfo ubo_vs_descriptor = create_descriptor(*uniform_buffers[i]); + + // Opaque scene parts + allocInfo = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &scene_opaque_pass.descriptor_set_layout, 1); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &allocInfo, &scene_opaque_pass.descriptor_sets[i])); + write_descriptor_sets = { + // Binding 0: Vertex shader uniform buffer + vkb::initializers::write_descriptor_set(scene_opaque_pass.descriptor_sets[i], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &ubo_vs_descriptor)}; + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); - // Transparent scene parts - allocInfo = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &scene_transparent_pass.descriptor_set_layout, 1); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &allocInfo, &scene_transparent_pass.descriptor_set)); - write_descriptor_sets = { - vkb::initializers::write_descriptor_set(scene_transparent_pass.descriptor_set, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 0, &tex_descriptor_position), - vkb::initializers::write_descriptor_set(scene_transparent_pass.descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, &ubo_vs_descriptor), - vkb::initializers::write_descriptor_set(scene_transparent_pass.descriptor_set, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &glass_image_descriptor), - }; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + // Transparent scene parts + allocInfo = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &scene_transparent_pass.descriptor_set_layout, 1); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &allocInfo, &scene_transparent_pass.descriptor_sets[i])); + write_descriptor_sets = { + vkb::initializers::write_descriptor_set(scene_transparent_pass.descriptor_sets[i], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 0, &tex_descriptor_position), + vkb::initializers::write_descriptor_set(scene_transparent_pass.descriptor_sets[i], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, &ubo_vs_descriptor), + vkb::initializers::write_descriptor_set(scene_transparent_pass.descriptor_sets[i], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &glass_image_descriptor), + }; + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + } // Composition pass + composition_pass.descriptor_sets.resize(1); allocInfo = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &composition_pass.descriptor_set_layout, 1); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &allocInfo, &composition_pass.descriptor_set)); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &allocInfo, &composition_pass.descriptor_sets[0])); write_descriptor_sets = { - vkb::initializers::write_descriptor_set(composition_pass.descriptor_set, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 0, &tex_descriptor_position), - vkb::initializers::write_descriptor_set(composition_pass.descriptor_set, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, &tex_descriptor_normal), - vkb::initializers::write_descriptor_set(composition_pass.descriptor_set, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, &tex_descriptor_albedo), - vkb::initializers::write_descriptor_set(composition_pass.descriptor_set, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3, &ssbo_lights_descriptor), + vkb::initializers::write_descriptor_set(composition_pass.descriptor_sets[0], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 0, &tex_descriptor_position), + vkb::initializers::write_descriptor_set(composition_pass.descriptor_sets[0], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, &tex_descriptor_normal), + vkb::initializers::write_descriptor_set(composition_pass.descriptor_sets[0], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, &tex_descriptor_albedo), + vkb::initializers::write_descriptor_set(composition_pass.descriptor_sets[0], VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3, &ssbo_lights_descriptor), }; vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); } @@ -723,7 +739,7 @@ void DynamicRenderingLocalRead::prepare_pipelines() pipeline_rendering_create_info.stencilAttachmentFormat = depth_format; } #else - blend_state.attachmentCount = 1; + blend_state.attachmentCount = 1; pipeline_create_info.subpass = 2; #endif @@ -745,7 +761,7 @@ void DynamicRenderingLocalRead::prepare_pipelines() pipeline_rendering_create_info.stencilAttachmentFormat = depth_format; } #else - blend_state.attachmentCount = 1; + blend_state.attachmentCount = 1; pipeline_create_info.subpass = 1; #endif @@ -808,8 +824,11 @@ void DynamicRenderingLocalRead::draw_scene(std::unique_ptr attachment_list = {attachments.positionDepth, attachments.normal, attachments.albedo}; + + VkImageSubresourceRange subresource_range_color{}; + subresource_range_color.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresource_range_color.levelCount = VK_REMAINING_MIP_LEVELS; + subresource_range_color.layerCount = VK_REMAINING_ARRAY_LAYERS; + + VkImageSubresourceRange subresource_range_depth{}; + subresource_range_depth.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + subresource_range_depth.levelCount = VK_REMAINING_MIP_LEVELS; + subresource_range_depth.layerCount = VK_REMAINING_ARRAY_LAYERS; + + vkb::image_layout_transition(draw_cmd_buffer, swapchain_buffers[current_image_index].image, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, subresource_range_color); + vkb::image_layout_transition(draw_cmd_buffer, depth_stencil.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, subresource_range_depth); + + VkRenderingAttachmentInfoKHR color_attachment_info[4]{}; + for (auto j = 0; j < 4; j++) { - auto cmd = draw_cmd_buffers[i]; + color_attachment_info[j] = vkb::initializers::rendering_attachment_info(); + color_attachment_info[j].imageLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR; + color_attachment_info[j].resolveMode = VK_RESOLVE_MODE_NONE; + color_attachment_info[j].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment_info[j].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment_info[j].clearValue = clear_values[j]; + } - vkBeginCommandBuffer(cmd, &command_buffer_begin_info); + color_attachment_info[0].imageView = swapchain_buffers[current_image_index].view; + for (auto i = 0; i < 3; i++) + { + color_attachment_info[i + 1].imageView = attachment_list[i].view; + } -#if defined(USE_DYNAMIC_RENDERING) - // With dynamic rendering and local read there are no render passes + VkRenderingAttachmentInfoKHR depth_attachment_info = vkb::initializers::rendering_attachment_info(); + depth_attachment_info.imageView = depth_stencil.view; + depth_attachment_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + depth_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; + depth_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depth_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depth_attachment_info.clearValue = clear_values[1]; + + VkRenderingInfoKHR render_info = vkb::initializers::rendering_info(); + render_info.renderArea = {0, 0, static_cast(attachment_width), static_cast(attachment_height)}; + render_info.layerCount = 1; + render_info.colorAttachmentCount = 4; + render_info.pColorAttachments = &color_attachment_info[0]; + + render_info.pDepthAttachment = &depth_attachment_info; + if (!vkb::is_depth_only_format(depth_format)) + { + render_info.pStencilAttachment = &depth_attachment_info; + } - const std::vector attachment_list = {attachments.positionDepth, attachments.normal, attachments.albedo}; + /* + Dynamic rendering start + */ + vkCmdBeginRenderingKHR(draw_cmd_buffer, &render_info); - VkImageSubresourceRange subresource_range_color{}; - subresource_range_color.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresource_range_color.levelCount = VK_REMAINING_MIP_LEVELS; - subresource_range_color.layerCount = VK_REMAINING_ARRAY_LAYERS; + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkImageSubresourceRange subresource_range_depth{}; - subresource_range_depth.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - subresource_range_depth.levelCount = VK_REMAINING_MIP_LEVELS; - subresource_range_depth.layerCount = VK_REMAINING_ARRAY_LAYERS; + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - vkb::image_layout_transition(cmd, swapchain_buffers[i].image, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, subresource_range_color); - vkb::image_layout_transition(cmd, depth_stencil.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, subresource_range_depth); + // Set input attachment indices for the composition and transparent passes + vkCmdSetRenderingInputAttachmentIndicesKHR(draw_cmd_buffer, &rendering_attachment_index_info); - VkRenderingAttachmentInfoKHR color_attachment_info[4]{}; - for (auto j = 0; j < 4; j++) - { - color_attachment_info[j] = vkb::initializers::rendering_attachment_info(); - color_attachment_info[j].imageLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR; - color_attachment_info[j].resolveMode = VK_RESOLVE_MODE_NONE; - color_attachment_info[j].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - color_attachment_info[j].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - color_attachment_info[j].clearValue = clear_values[j]; - } + /* + First draw + Fills the G-Buffer attachments containing image data for the deferred composition (color+depth, normals, albedo) + */ - color_attachment_info[0].imageView = swapchain_buffers[i].view; - for (auto i = 0; i < 3; i++) - { - color_attachment_info[i + 1].imageView = attachment_list[i].view; - } + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_opaque_pass.pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_opaque_pass.pipeline_layout, 0, 1, &scene_opaque_pass.descriptor_sets[current_buffer], 0, nullptr); + draw_scene(scenes.opaque, draw_cmd_buffer, scene_opaque_pass.pipeline_layout); - VkRenderingAttachmentInfoKHR depth_attachment_info = vkb::initializers::rendering_attachment_info(); - depth_attachment_info.imageView = depth_stencil.view; - depth_attachment_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; - depth_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; - depth_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depth_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depth_attachment_info.clearValue = clear_values[1]; - - VkRenderingInfoKHR render_info = vkb::initializers::rendering_info(); - render_info.renderArea = {0, 0, static_cast(attachment_width), static_cast(attachment_height)}; - render_info.layerCount = 1; - render_info.colorAttachmentCount = 4; - render_info.pColorAttachments = &color_attachment_info[0]; - - render_info.pDepthAttachment = &depth_attachment_info; - if (!vkb::is_depth_only_format(depth_format)) - { - render_info.pStencilAttachment = &depth_attachment_info; - } + // We want to read the input attachments in the next pass, with dynamic rendering local read this requires use of a barrier with the "by region" flag set + + // A new feature of the dynamic rendering local read extension is the ability to use pipeline barriers in the dynamic render pass + // to allow framebuffer-local dependencies (i.e. read-after-write) between draw calls using the "by region" flag + // So with this barrier we can use the output attachments from the draw call above as input attachments in the next call + VkMemoryBarrier2KHR memoryBarrier{}; + memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2_KHR; + memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + memoryBarrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT; + + VkDependencyInfoKHR dependencyInfo{}; + dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR; + dependencyInfo.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependencyInfo.memoryBarrierCount = 1; + dependencyInfo.pMemoryBarriers = &memoryBarrier; - /* - Dynamic rendering start - */ - vkCmdBeginRenderingKHR(cmd, &render_info); - - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(cmd, 0, 1, &viewport); - - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(cmd, 0, 1, &scissor); - - // Set input attachment indices for the composition and transparent passes - vkCmdSetRenderingInputAttachmentIndicesKHR(cmd, &rendering_attachment_index_info); - - /* - First draw - Fills the G-Buffer attachments containing image data for the deferred composition (color+depth, normals, albedo) - */ - - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_opaque_pass.pipeline); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_opaque_pass.pipeline_layout, 0, 1, &scene_opaque_pass.descriptor_set, 0, nullptr); - draw_scene(scenes.opaque, cmd, scene_opaque_pass.pipeline_layout); - - // We want to read the input attachments in the next pass, with dynamic rendering local read this requires use of a barrier with the "by region" flag set - - // A new feature of the dynamic rendering local read extension is the ability to use pipeline barriers in the dynamic render pass - // to allow framebuffer-local dependencies (i.e. read-after-write) between draw calls using the "by region" flag - // So with this barrier we can use the output attachments from the draw call above as input attachments in the next call - VkMemoryBarrier2KHR memoryBarrier{}; - memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2_KHR; - memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; - memoryBarrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; - memoryBarrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; - memoryBarrier.dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT; - - VkDependencyInfoKHR dependencyInfo{}; - dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR; - dependencyInfo.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - dependencyInfo.memoryBarrierCount = 1; - dependencyInfo.pMemoryBarriers = &memoryBarrier; - - vkCmdPipelineBarrier2KHR(cmd, &dependencyInfo); - - /* - Second draw - This will use the G-Buffer attachments that have been filled in the first draw as input attachment for the deferred scene composition - */ - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, composition_pass.pipeline); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, composition_pass.pipeline_layout, 0, 1, &composition_pass.descriptor_set, 0, nullptr); - vkCmdDraw(cmd, 3, 1, 0, 0); - - // Third draw - // Render transparent geometry using a forward pass that compares against depth generated during the first draw - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_transparent_pass.pipeline); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_transparent_pass.pipeline_layout, 0, 1, &scene_transparent_pass.descriptor_set, 0, nullptr); - draw_scene(scenes.transparent, cmd, scene_transparent_pass.pipeline_layout); - - // End main rendering - vkCmdEndRenderingKHR(cmd); - - // Draw UI - draw_ui(draw_cmd_buffers[i], i); - - /* - Dynamic rendering end - */ - - vkb::image_layout_transition(cmd, swapchain_buffers[i].image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subresource_range_color); + vkCmdPipelineBarrier2KHR(draw_cmd_buffer, &dependencyInfo); + + /* + Second draw + This will use the G-Buffer attachments that have been filled in the first draw as input attachment for the deferred scene composition + */ + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, composition_pass.pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, composition_pass.pipeline_layout, 0, 1, &composition_pass.descriptor_sets[0], 0, nullptr); + vkCmdDraw(draw_cmd_buffer, 3, 1, 0, 0); + + // Third draw + // Render transparent geometry using a forward pass that compares against depth generated during the first draw + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_transparent_pass.pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_transparent_pass.pipeline_layout, 0, 1, &scene_transparent_pass.descriptor_sets[current_buffer], 0, nullptr); + draw_scene(scenes.transparent, draw_cmd_buffer, scene_transparent_pass.pipeline_layout); + + // End main rendering + vkCmdEndRenderingKHR(draw_cmd_buffer); + + // Draw UI + draw_ui(draw_cmd_buffers[current_buffer], current_image_index); + + /* + Dynamic rendering end + */ + + vkb::image_layout_transition(draw_cmd_buffer, swapchain_buffers[current_image_index].image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subresource_range_color); #else - VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info(); - render_pass_begin_info.renderPass = render_pass; - render_pass_begin_info.renderArea.offset.x = 0; - render_pass_begin_info.renderArea.offset.y = 0; - render_pass_begin_info.renderArea.extent.width = width; - render_pass_begin_info.renderArea.extent.height = height; - render_pass_begin_info.clearValueCount = 5; - render_pass_begin_info.pClearValues = clear_values; - render_pass_begin_info.framebuffer = framebuffers[i]; - - // Start our render pass, which contains multiple sub passes - vkCmdBeginRenderPass(cmd, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(cmd, 0, 1, &viewport); - - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(cmd, 0, 1, &scissor); - - // First sub pass - // Renders the components of the scene to the G-Buffer attachments - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_opaque_pass.pipeline); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_opaque_pass.pipeline_layout, 0, 1, &scene_opaque_pass.descriptor_set, 0, nullptr); - draw_scene(scenes.opaque, cmd, scene_opaque_pass.pipeline_layout); - - // Second sub pass - // This subpass will use the G-Buffer components that have been filled in the first subpass as input attachment for the final compositing - vkCmdNextSubpass(cmd, VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, composition_pass.pipeline); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, composition_pass.pipeline_layout, 0, 1, &composition_pass.descriptor_set, 0, nullptr); - vkCmdDraw(cmd, 3, 1, 0, 0); - - // Third subpass - // Render transparent geometry using a forward pass that compares against depth generated during G-Buffer fill - vkCmdNextSubpass(cmd, VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_transparent_pass.pipeline); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_transparent_pass.pipeline_layout, 0, 1, &scene_transparent_pass.descriptor_set, 0, nullptr); - draw_scene(scenes.transparent, cmd, scene_transparent_pass.pipeline_layout); - - draw_ui(draw_cmd_buffers[i]); - - vkCmdEndRenderPass(cmd); + VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info(); + render_pass_begin_info.renderPass = render_pass; + render_pass_begin_info.renderArea.offset.x = 0; + render_pass_begin_info.renderArea.offset.y = 0; + render_pass_begin_info.renderArea.extent.width = width; + render_pass_begin_info.renderArea.extent.height = height; + render_pass_begin_info.clearValueCount = 5; + render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; + + // Start our render pass, which contains multiple sub passes + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); + + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); + + // First sub pass + // Renders the components of the scene to the G-Buffer attachments + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_opaque_pass.pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_opaque_pass.pipeline_layout, 0, 1, &scene_opaque_pass.descriptor_sets[current_buffer], 0, nullptr); + draw_scene(scenes.opaque, draw_cmd_buffer, scene_opaque_pass.pipeline_layout); + + // Second sub pass + // This subpass will use the G-Buffer components that have been filled in the first subpass as input attachment for the final compositing + vkCmdNextSubpass(draw_cmd_buffer, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, composition_pass.pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, composition_pass.pipeline_layout, 0, 1, &composition_pass.descriptor_sets[0], 0, nullptr); + vkCmdDraw(draw_cmd_buffer, 3, 1, 0, 0); + + // Third subpass + // Render transparent geometry using a forward pass that compares against depth generated during G-Buffer fill + vkCmdNextSubpass(draw_cmd_buffer, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_transparent_pass.pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, scene_transparent_pass.pipeline_layout, 0, 1, &scene_transparent_pass.descriptor_sets[current_buffer], 0, nullptr); + draw_scene(scenes.transparent, draw_cmd_buffer, scene_transparent_pass.pipeline_layout); + + draw_ui(draw_cmd_buffers[current_buffer]); + + vkCmdEndRenderPass(draw_cmd_buffer); #endif - VK_CHECK(vkEndCommandBuffer(cmd)); - } + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } bool DynamicRenderingLocalRead::prepare(const vkb::ApplicationOptions &options) @@ -1022,14 +1036,9 @@ void DynamicRenderingLocalRead::render(float delta_time) return; } ApiVulkanSample::prepare_frame(); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + update_uniform_buffer(); + build_command_buffer(); ApiVulkanSample::submit_frame(); - if (camera.updated) - { - update_uniform_buffer(); - } } void DynamicRenderingLocalRead::on_update_ui_overlay(vkb::Drawer &drawer) diff --git a/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.h b/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.h index a5aa591ea..4ce227b5a 100644 --- a/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.h +++ b/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.h @@ -30,7 +30,7 @@ class DynamicRenderingLocalRead : public ApiVulkanSample DynamicRenderingLocalRead(); virtual ~DynamicRenderingLocalRead(); void prepare_pipelines(); - void build_command_buffers() override; + void build_command_buffer(); void render(float delta_time) override; bool prepare(const vkb::ApplicationOptions &options) override; void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; @@ -71,11 +71,8 @@ class DynamicRenderingLocalRead : public ApiVulkanSample std::array lights; - struct - { - std::unique_ptr ubo_vs; - std::unique_ptr ssbo_lights; - } buffers; + std::unique_ptr lights_buffer; + std::array, max_concurrent_frames> uniform_buffers{}; struct PushConstantSceneNode { @@ -85,10 +82,10 @@ class DynamicRenderingLocalRead : public ApiVulkanSample struct Pass { - VkPipelineLayout pipeline_layout{VK_NULL_HANDLE}; - VkPipeline pipeline{VK_NULL_HANDLE}; - VkDescriptorSetLayout descriptor_set_layout{VK_NULL_HANDLE}; - VkDescriptorSet descriptor_set{VK_NULL_HANDLE}; + VkPipelineLayout pipeline_layout{VK_NULL_HANDLE}; + VkPipeline pipeline{VK_NULL_HANDLE}; + VkDescriptorSetLayout descriptor_set_layout{VK_NULL_HANDLE}; + std::vector descriptor_sets; } scene_opaque_pass, scene_transparent_pass, composition_pass; struct FrameBufferAttachment From 0a458d87b0a86d26a835f27a3aa44d71b1dc2d51 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 2 May 2026 12:43:30 +0200 Subject: [PATCH 17/23] Clang format --- .../dynamic_rendering/dynamic_rendering.cpp | 32 +++++++++---------- .../dynamic_rendering/dynamic_rendering.h | 4 +-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/samples/extensions/dynamic_rendering/dynamic_rendering.cpp b/samples/extensions/dynamic_rendering/dynamic_rendering.cpp index ae940fcc4..81c51ad89 100644 --- a/samples/extensions/dynamic_rendering/dynamic_rendering.cpp +++ b/samples/extensions/dynamic_rendering/dynamic_rendering.cpp @@ -385,20 +385,20 @@ void DynamicRendering::build_command_buffer() if (enable_dynamic) { vkb::image_layout_transition(draw_cmd_buffer, - swapchain_buffers[current_image_index].image, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - range); + swapchain_buffers[current_image_index].image, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + range); vkb::image_layout_transition(draw_cmd_buffer, - depth_stencil.image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - depth_range); + depth_stencil.image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + depth_range); VkRenderingAttachmentInfoKHR color_attachment_info = vkb::initializers::rendering_attachment_info(); color_attachment_info.imageView = swapchain_buffers[current_image_index].view; @@ -432,10 +432,10 @@ void DynamicRendering::build_command_buffer() draw_ui(draw_cmd_buffer, current_image_index); vkb::image_layout_transition(draw_cmd_buffer, - swapchain_buffers[current_image_index].image, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - range); + swapchain_buffers[current_image_index].image, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + range); } else { diff --git a/samples/extensions/dynamic_rendering/dynamic_rendering.h b/samples/extensions/dynamic_rendering/dynamic_rendering.h index 4f1d5e928..49b9a3a3c 100644 --- a/samples/extensions/dynamic_rendering/dynamic_rendering.h +++ b/samples/extensions/dynamic_rendering/dynamic_rendering.h @@ -65,8 +65,8 @@ class DynamicRendering : public ApiVulkanSample float modelscale = 0.05f; } ubo_vs; - std::unique_ptr skybox; - std::unique_ptr object; + std::unique_ptr skybox; + std::unique_ptr object; std::array, max_concurrent_frames> uniform_buffers{}; std::array descriptor_sets{}; From 166d7014f70773dff6fb342f7b615d3ef7c84045 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 2 May 2026 12:48:56 +0200 Subject: [PATCH 18/23] Trying to fix Clang format --- .../dynamic_rendering_local_read.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.cpp b/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.cpp index 05f8f5b36..bdcedb2a1 100644 --- a/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.cpp +++ b/samples/extensions/dynamic_rendering_local_read/dynamic_rendering_local_read.cpp @@ -739,7 +739,7 @@ void DynamicRenderingLocalRead::prepare_pipelines() pipeline_rendering_create_info.stencilAttachmentFormat = depth_format; } #else - blend_state.attachmentCount = 1; + blend_state.attachmentCount = 1; pipeline_create_info.subpass = 2; #endif @@ -761,7 +761,7 @@ void DynamicRenderingLocalRead::prepare_pipelines() pipeline_rendering_create_info.stencilAttachmentFormat = depth_format; } #else - blend_state.attachmentCount = 1; + blend_state.attachmentCount = 1; pipeline_create_info.subpass = 1; #endif From d5d5e490dc7fc7c4e96054edc272a37b1862757e Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 2 May 2026 14:14:57 +0200 Subject: [PATCH 19/23] Update samples to use new sync --- .../vertex_dynamic_state.cpp | 219 +++++++++--------- .../vertex_dynamic_state.h | 11 +- 2 files changed, 112 insertions(+), 118 deletions(-) diff --git a/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.cpp b/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.cpp index 4406775bc..e474fb230 100644 --- a/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.cpp +++ b/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.cpp @@ -19,6 +19,8 @@ VertexDynamicState::VertexDynamicState() { + use_new_sync = true; + title = "Vertex Dynamic State"; add_device_extension(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); @@ -32,7 +34,6 @@ VertexDynamicState::~VertexDynamicState() textures = {}; skybox.reset(); object.reset(); - ubo.reset(); vkDestroyPipeline(get_device().get_handle(), model_pipeline, VK_NULL_HANDLE); vkDestroyPipeline(get_device().get_handle(), skybox_pipeline, VK_NULL_HANDLE); @@ -65,7 +66,6 @@ bool VertexDynamicState::prepare(const vkb::ApplicationOptions &options) setup_descriptor_set_layout(); create_descriptor_sets(); create_pipeline(); - build_command_buffers(); prepared = true; return true; @@ -91,9 +91,8 @@ void VertexDynamicState::load_assets() void VertexDynamicState::draw() { ApiVulkanSample::prepare_frame(); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + update_uniform_buffers(); + build_command_buffer(); ApiVulkanSample::submit_frame(); } @@ -108,10 +107,6 @@ void VertexDynamicState::render(float delta_time) return; } draw(); - if (camera.updated) - { - update_uniform_buffers(); - } } /** @@ -120,9 +115,10 @@ void VertexDynamicState::render(float delta_time) */ void VertexDynamicState::prepare_uniform_buffers() { - ubo = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } /** @@ -135,7 +131,7 @@ void VertexDynamicState::update_uniform_buffers() ubo_vs.modelview = camera.matrices.view * glm::mat4(1.f); ubo_vs.inverse_modelview = glm::inverse(camera.matrices.view); ubo_vs.skybox_modelview = camera.matrices.view; - ubo->convert_and_update(ubo_vs); + uniform_buffers[current_buffer]->convert_and_update(ubo_vs); } /** @@ -301,98 +297,81 @@ void VertexDynamicState::create_pipeline() * By default function "load_model" from framework is parsing data from .gltf files and build it every time in declared structure (see Vertex structure in framework files). * Before drawing different models (in case of vertex input data structure) "change_vertex_input_data" fuction is called for dynamically change Vertex Input data. */ -void VertexDynamicState::build_command_buffers() +void VertexDynamicState::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + std::array clear_values{}; clear_values[0].color = {{0.0f, 0.0f, 0.0f, 0.0f}}; clear_values[1].depthStencil = {0.0f, 0}; - int i = -1; - for (auto &draw_cmd_buffer : draw_cmd_buffers) - { - i++; - auto command_begin = vkb::initializers::command_buffer_begin_info(); - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_begin)); - - VkImageSubresourceRange range{}; - range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.baseMipLevel = 0; - range.levelCount = VK_REMAINING_MIP_LEVELS; - range.baseArrayLayer = 0; - range.layerCount = VK_REMAINING_ARRAY_LAYERS; - - VkImageSubresourceRange depth_range{range}; - depth_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - - VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info(); - render_pass_begin_info.renderPass = render_pass; - render_pass_begin_info.framebuffer = framebuffers[i]; - render_pass_begin_info.renderArea.extent.width = width; - render_pass_begin_info.renderArea.extent.height = height; - render_pass_begin_info.clearValueCount = static_cast(clear_values.size()); - render_pass_begin_info.pClearValues = clear_values.data(); - - vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - - VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); - vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - - /* One descriptor set is used, and the draw type is toggled by a specialization constant */ - vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr); - - /* skybox */ - /* First set of vertex input dynamic data (Vertex structure) */ - vertex_bindings_description_ext[0].stride = sizeof(Vertex); - vertex_attribute_description_ext[1].offset = offsetof(Vertex, normal); - vkCmdSetVertexInputEXT(draw_cmd_buffer, - static_cast(vertex_bindings_description_ext.size()), - vertex_bindings_description_ext.data(), - static_cast(vertex_attribute_description_ext.size()), - vertex_attribute_description_ext.data()); - - vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, skybox_pipeline); - draw_model(skybox, draw_cmd_buffer); - - /* object */ - vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, model_pipeline); - draw_model(object, draw_cmd_buffer); - - /* Second set of vertex input dynamic data (SampleVertex structure) */ - vertex_bindings_description_ext[0].stride = sizeof(SampleVertex); - vertex_attribute_description_ext[1].offset = offsetof(SampleVertex, normal); - vkCmdSetVertexInputEXT(draw_cmd_buffer, - static_cast(vertex_bindings_description_ext.size()), - vertex_bindings_description_ext.data(), - static_cast(vertex_attribute_description_ext.size()), - vertex_attribute_description_ext.data()); - - draw_created_model(draw_cmd_buffer); - - /* UI */ - draw_ui(draw_cmd_buffer); - - vkCmdEndRenderPass(draw_cmd_buffer); - - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); - } -} + auto command_begin = vkb::initializers::command_buffer_begin_info(); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_begin)); -/** - * @fn void VertexDynamicState::create_descriptor_pool() - * @brief Creating descriptor pool with size adjusted to use uniform buffer and image sampler - */ -void VertexDynamicState::create_descriptor_pool() -{ - std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2)}; - uint32_t num_descriptor_sets = 4; - VkDescriptorPoolCreateInfo descriptor_pool_create_info = - vkb::initializers::descriptor_pool_create_info(static_cast(pool_sizes.size()), pool_sizes.data(), num_descriptor_sets); - VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; + + VkImageSubresourceRange depth_range{range}; + depth_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + + VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info(); + render_pass_begin_info.renderPass = render_pass; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; + render_pass_begin_info.renderArea.extent.width = width; + render_pass_begin_info.renderArea.extent.height = height; + render_pass_begin_info.clearValueCount = static_cast(clear_values.size()); + render_pass_begin_info.pClearValues = clear_values.data(); + + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); + + VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); + + /* One descriptor set is used, and the draw type is toggled by a specialization constant */ + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer], 0, nullptr); + + /* skybox */ + /* First set of vertex input dynamic data (Vertex structure) */ + vertex_bindings_description_ext[0].stride = sizeof(Vertex); + vertex_attribute_description_ext[1].offset = offsetof(Vertex, normal); + vkCmdSetVertexInputEXT(draw_cmd_buffer, + static_cast(vertex_bindings_description_ext.size()), + vertex_bindings_description_ext.data(), + static_cast(vertex_attribute_description_ext.size()), + vertex_attribute_description_ext.data()); + + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, skybox_pipeline); + draw_model(skybox, draw_cmd_buffer); + + /* object */ + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, model_pipeline); + draw_model(object, draw_cmd_buffer); + + /* Second set of vertex input dynamic data (SampleVertex structure) */ + vertex_bindings_description_ext[0].stride = sizeof(SampleVertex); + vertex_attribute_description_ext[1].offset = offsetof(SampleVertex, normal); + vkCmdSetVertexInputEXT(draw_cmd_buffer, + static_cast(vertex_bindings_description_ext.size()), + vertex_bindings_description_ext.data(), + static_cast(vertex_attribute_description_ext.size()), + vertex_attribute_description_ext.data()); + + draw_created_model(draw_cmd_buffer); + + /* UI */ + draw_ui(draw_cmd_buffer); + + vkCmdEndRenderPass(draw_cmd_buffer); + + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } /** @@ -419,6 +398,21 @@ void VertexDynamicState::setup_descriptor_set_layout() VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout)); } +/** + * @fn void VertexDynamicState::create_descriptor_pool() + * @brief Creating descriptor pool with size adjusted to use uniform buffer and image sampler + */ +void VertexDynamicState::create_descriptor_pool() +{ + std::vector pool_sizes = { + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, max_concurrent_frames * 2)}; + uint32_t num_descriptor_sets = max_concurrent_frames; + VkDescriptorPoolCreateInfo descriptor_pool_create_info = + vkb::initializers::descriptor_pool_create_info(static_cast(pool_sizes.size()), pool_sizes.data(), num_descriptor_sets); + VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); +} + /** * @fn void VertexDynamicState::create_descriptor_sets() * @brief Creating both descriptor set: @@ -427,21 +421,20 @@ void VertexDynamicState::setup_descriptor_set_layout() */ void VertexDynamicState::create_descriptor_sets() { - VkDescriptorSetAllocateInfo alloc_info = - vkb::initializers::descriptor_set_allocate_info( - descriptor_pool, - &descriptor_set_layout, - 1); + VkDescriptorSetAllocateInfo alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layout, 1); + VkDescriptorImageInfo environment_image_descriptor = create_descriptor(textures.envmap); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); - - VkDescriptorBufferInfo matrix_buffer_descriptor = create_descriptor(*ubo); - VkDescriptorImageInfo environment_image_descriptor = create_descriptor(textures.envmap); - std::vector write_descriptor_sets = { - vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &matrix_buffer_descriptor), - vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &environment_image_descriptor), - }; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets[i])); + + VkDescriptorBufferInfo matrix_buffer_descriptor = create_descriptor(*uniform_buffers[i]); + std::vector write_descriptor_sets = { + vkb::initializers::write_descriptor_set(descriptor_sets[i], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &matrix_buffer_descriptor), + vkb::initializers::write_descriptor_set(descriptor_sets[i], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &environment_image_descriptor), + }; + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + } } void VertexDynamicState::request_gpu_features(vkb::core::PhysicalDeviceC &gpu) diff --git a/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.h b/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.h index 996ee3650..35913403c 100644 --- a/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.h +++ b/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.h @@ -56,19 +56,20 @@ class VertexDynamicState : public ApiVulkanSample std::vector vertex_bindings_description_ext{1}; std::vector vertex_attribute_description_ext{2}; - VkDescriptorSet descriptor_set{VK_NULL_HANDLE}; VkDescriptorSetLayout descriptor_set_layout{VK_NULL_HANDLE}; VkDescriptorPool descriptor_pool{VK_NULL_HANDLE}; - std::unique_ptr skybox; - std::unique_ptr object; - std::unique_ptr ubo; + std::unique_ptr skybox; + std::unique_ptr object; + + std::array, max_concurrent_frames> uniform_buffers{}; + std::array descriptor_sets{}; VertexDynamicState(); ~VertexDynamicState(); virtual void render(float delta_time) override; - virtual void build_command_buffers() override; + virtual void build_command_buffer(); virtual bool prepare(const vkb::ApplicationOptions &options) override; virtual void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; From b86eaa8425722e4c6fead7e64a73f4bc8d98c445 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 2 May 2026 18:04:23 +0200 Subject: [PATCH 20/23] Simplify render/draw functions No need to separte --- .../hpp_separate_image_sampler.cpp | 13 ++++--------- .../hpp_separate_image_sampler.h | 1 - samples/api/instancing/instancing.cpp | 13 ++++--------- samples/api/instancing/instancing.h | 1 - .../separate_image_sampler.cpp | 13 ++++--------- .../separate_image_sampler.h | 1 - .../dynamic_rendering/dynamic_rendering.cpp | 13 ++++--------- .../dynamic_rendering/dynamic_rendering.h | 1 - .../host_image_copy/host_image_copy.cpp | 13 ++++--------- .../host_image_copy/host_image_copy.h | 1 - .../hpp_push_descriptors.cpp | 15 +++++---------- .../hpp_push_descriptors/hpp_push_descriptors.h | 1 - .../push_descriptors/push_descriptors.cpp | 15 +++++---------- .../push_descriptors/push_descriptors.h | 1 - .../vertex_dynamic_state.cpp | 17 ++++------------- .../vertex_dynamic_state/vertex_dynamic_state.h | 1 - 16 files changed, 34 insertions(+), 86 deletions(-) diff --git a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp index eca4fc449..dba661667 100644 --- a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp +++ b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.cpp @@ -160,7 +160,10 @@ void HPPSeparateImageSampler::render(float delta_time) { if (prepared) { - draw(); + HPPApiVulkanSample::prepare_frame(); + update_uniform_buffers(); + build_command_buffer(); + HPPApiVulkanSample::submit_frame(); } } @@ -268,14 +271,6 @@ vk::DescriptorSetLayout HPPSeparateImageSampler::create_sampler_descriptor_set_l return get_device().get_handle().createDescriptorSetLayout(descriptor_layout_create_info); } -void HPPSeparateImageSampler::draw() -{ - HPPApiVulkanSample::prepare_frame(); - update_uniform_buffers(); - build_command_buffer(); - HPPApiVulkanSample::submit_frame(); -} - void HPPSeparateImageSampler::generate_quad() { // Setup vertices for a single uv-mapped quad made from two triangles diff --git a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.h b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.h index 45c61c15b..0cc7140fc 100644 --- a/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.h +++ b/samples/api/hpp_separate_image_sampler/hpp_separate_image_sampler.h @@ -64,7 +64,6 @@ class HPPSeparateImageSampler : public HPPApiVulkanSample vk::PipelineLayout create_pipeline_layout(std::vector const &descriptor_set_layouts); vk::Sampler create_sampler(vk::Filter filter); vk::DescriptorSetLayout create_sampler_descriptor_set_layout(); - void draw(); void generate_quad(); void load_assets(); void prepare_uniform_buffers(); diff --git a/samples/api/instancing/instancing.cpp b/samples/api/instancing/instancing.cpp index 548cd64a6..c3cb018c4 100644 --- a/samples/api/instancing/instancing.cpp +++ b/samples/api/instancing/instancing.cpp @@ -442,14 +442,6 @@ void Instancing::update_uniform_buffers(float delta_time) uniform_buffers[current_buffer]->convert_and_update(ubo_vs); } -void Instancing::draw(float delta_time) -{ - ApiVulkanSample::prepare_frame(); - update_uniform_buffers(delta_time); - build_command_buffer(); - ApiVulkanSample::submit_frame(); -} - bool Instancing::prepare(const vkb::ApplicationOptions &options) { if (!ApiVulkanSample::prepare(options)) @@ -480,7 +472,10 @@ void Instancing::render(float delta_time) { return; } - draw(delta_time); + ApiVulkanSample::prepare_frame(); + update_uniform_buffers(delta_time); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } void Instancing::on_update_ui_overlay(vkb::Drawer &drawer) diff --git a/samples/api/instancing/instancing.h b/samples/api/instancing/instancing.h index 72e9c5f5c..062d6f2c6 100644 --- a/samples/api/instancing/instancing.h +++ b/samples/api/instancing/instancing.h @@ -99,7 +99,6 @@ class Instancing : public ApiVulkanSample void prepare_instance_data(); void prepare_uniform_buffers(); void update_uniform_buffers(float delta_time); - void draw(float delta_time); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; diff --git a/samples/api/separate_image_sampler/separate_image_sampler.cpp b/samples/api/separate_image_sampler/separate_image_sampler.cpp index 635ae75c0..0fa18904e 100644 --- a/samples/api/separate_image_sampler/separate_image_sampler.cpp +++ b/samples/api/separate_image_sampler/separate_image_sampler.cpp @@ -153,14 +153,6 @@ void SeparateImageSampler::load_assets() texture = load_texture("textures/metalplate01_rgba.ktx", vkb::sg::Image::Color); } -void SeparateImageSampler::draw() -{ - ApiVulkanSample::prepare_frame(); - update_uniform_buffers(); - build_command_buffer(); - ApiVulkanSample::submit_frame(); -} - void SeparateImageSampler::generate_quad() { // Setup vertices for a single uv-mapped quad made from two triangles @@ -452,7 +444,10 @@ void SeparateImageSampler::render(float delta_time) { return; } - draw(); + ApiVulkanSample::prepare_frame(); + update_uniform_buffers(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } void SeparateImageSampler::on_update_ui_overlay(vkb::Drawer &drawer) diff --git a/samples/api/separate_image_sampler/separate_image_sampler.h b/samples/api/separate_image_sampler/separate_image_sampler.h index 184f89817..b39e0cff2 100644 --- a/samples/api/separate_image_sampler/separate_image_sampler.h +++ b/samples/api/separate_image_sampler/separate_image_sampler.h @@ -68,7 +68,6 @@ class SeparateImageSampler : public ApiVulkanSample void build_command_buffer(); void setup_samplers(); void load_assets(); - void draw(); void generate_quad(); void setup_descriptor_pool(); void setup_descriptor_set_layout(); diff --git a/samples/extensions/dynamic_rendering/dynamic_rendering.cpp b/samples/extensions/dynamic_rendering/dynamic_rendering.cpp index 81c51ad89..4492c716d 100644 --- a/samples/extensions/dynamic_rendering/dynamic_rendering.cpp +++ b/samples/extensions/dynamic_rendering/dynamic_rendering.cpp @@ -333,14 +333,6 @@ void DynamicRendering::setup_framebuffer() } } -void DynamicRendering::draw() -{ - ApiVulkanSample::prepare_frame(); - update_uniform_buffers(); - build_command_buffer(); - ApiVulkanSample::submit_frame(); -} - void DynamicRendering::build_command_buffer() { VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; @@ -464,7 +456,10 @@ void DynamicRendering::render(float delta_time) { return; } - draw(); + ApiVulkanSample::prepare_frame(); + update_uniform_buffers(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } void DynamicRendering::view_changed() diff --git a/samples/extensions/dynamic_rendering/dynamic_rendering.h b/samples/extensions/dynamic_rendering/dynamic_rendering.h index 49b9a3a3c..7400816e9 100644 --- a/samples/extensions/dynamic_rendering/dynamic_rendering.h +++ b/samples/extensions/dynamic_rendering/dynamic_rendering.h @@ -49,7 +49,6 @@ class DynamicRendering : public ApiVulkanSample void create_descriptor_sets(); void create_descriptor_pool(); void create_pipeline(); - void draw(); struct { diff --git a/samples/extensions/host_image_copy/host_image_copy.cpp b/samples/extensions/host_image_copy/host_image_copy.cpp index db2287b7f..a12b0798a 100644 --- a/samples/extensions/host_image_copy/host_image_copy.cpp +++ b/samples/extensions/host_image_copy/host_image_copy.cpp @@ -289,14 +289,6 @@ void HostImageCopy::build_command_buffer() VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } -void HostImageCopy::draw() -{ - ApiVulkanSample::prepare_frame(); - update_uniform_buffers(); - build_command_buffer(); - ApiVulkanSample::submit_frame(); -} - void HostImageCopy::setup_descriptor_pool() { // Example uses one ubo and one image sampler @@ -466,7 +458,10 @@ void HostImageCopy::render(float delta_time) { return; } - draw(); + ApiVulkanSample::prepare_frame(); + update_uniform_buffers(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } void HostImageCopy::on_update_ui_overlay(vkb::Drawer &drawer) diff --git a/samples/extensions/host_image_copy/host_image_copy.h b/samples/extensions/host_image_copy/host_image_copy.h index b5af31ddd..f6430436c 100644 --- a/samples/extensions/host_image_copy/host_image_copy.h +++ b/samples/extensions/host_image_copy/host_image_copy.h @@ -60,7 +60,6 @@ class HostImageCopy : public ApiVulkanSample void load_assets(); void destroy_texture(Texture texture); void build_command_buffer(); - void draw(); void setup_descriptor_pool(); void setup_descriptor_set_layout(); void setup_descriptor_set(); diff --git a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.cpp b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.cpp index d606aa6f9..f6653ac31 100644 --- a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.cpp +++ b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.cpp @@ -109,7 +109,11 @@ void HPPPushDescriptors::render(float delta_time) { if (prepared) { - draw(delta_time); + HPPApiVulkanSample::prepare_frame(); + update_cube_uniform_buffers(delta_time); + update_uniform_buffers(); + build_command_buffer(); + HPPApiVulkanSample::submit_frame(); } } @@ -255,15 +259,6 @@ void HPPPushDescriptors::create_pipeline_layout() pipeline_layout = get_device().get_handle().createPipelineLayout(pipeline_layout_create_info); } -void HPPPushDescriptors::draw(float delta_time) -{ - HPPApiVulkanSample::prepare_frame(); - update_cube_uniform_buffers(delta_time); - update_uniform_buffers(); - build_command_buffer(); - HPPApiVulkanSample::submit_frame(); -} - void HPPPushDescriptors::initializeCamera() { // Note: Using reversed depth-buffer for increased precision, so Znear and Zfar are flipped diff --git a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h index f45ed347c..169d8d0ff 100644 --- a/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h +++ b/samples/extensions/hpp_push_descriptors/hpp_push_descriptors.h @@ -53,7 +53,6 @@ class HPPPushDescriptors : public HPPApiVulkanSample void create_pipeline(); void create_pipeline_layout(); void create_uniform_buffers(); - void draw(float delta_time); void initializeCamera(); void load_assets(); void update_cube_uniform_buffers(float delta_time); diff --git a/samples/extensions/push_descriptors/push_descriptors.cpp b/samples/extensions/push_descriptors/push_descriptors.cpp index 9c87ea196..e30e7ace1 100644 --- a/samples/extensions/push_descriptors/push_descriptors.cpp +++ b/samples/extensions/push_descriptors/push_descriptors.cpp @@ -300,15 +300,6 @@ void PushDescriptors::update_cube_uniform_buffers(float delta_time) } } -void PushDescriptors::draw(float delta_time) -{ - ApiVulkanSample::prepare_frame(); - update_cube_uniform_buffers(delta_time); - update_uniform_buffers(); - build_command_buffer(); - ApiVulkanSample::submit_frame(); -} - bool PushDescriptors::prepare(const vkb::ApplicationOptions &options) { if (!ApiVulkanSample::prepare(options)) @@ -364,7 +355,11 @@ void PushDescriptors::render(float delta_time) { return; } - draw(delta_time); + ApiVulkanSample::prepare_frame(); + update_cube_uniform_buffers(delta_time); + update_uniform_buffers(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } void PushDescriptors::on_update_ui_overlay(vkb::Drawer &drawer) diff --git a/samples/extensions/push_descriptors/push_descriptors.h b/samples/extensions/push_descriptors/push_descriptors.h index 69442eb3b..cd80a6afb 100644 --- a/samples/extensions/push_descriptors/push_descriptors.h +++ b/samples/extensions/push_descriptors/push_descriptors.h @@ -74,7 +74,6 @@ class PushDescriptors : public ApiVulkanSample void prepare_uniform_buffers(); void update_uniform_buffers(); void update_cube_uniform_buffers(float delta_time); - void draw(float delta_time); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; diff --git a/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.cpp b/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.cpp index e474fb230..87b6ee9c0 100644 --- a/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.cpp +++ b/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.cpp @@ -84,18 +84,6 @@ void VertexDynamicState::load_assets() textures.envmap = load_texture_cubemap("textures/uffizi_rgba16f_cube.ktx", vkb::sg::Image::Color); } -/** - * @fn void VertexDynamicState::draw() - * @brief Preparing frame and submitting it to the present queue - */ -void VertexDynamicState::draw() -{ - ApiVulkanSample::prepare_frame(); - update_uniform_buffers(); - build_command_buffer(); - ApiVulkanSample::submit_frame(); -} - /** * @fn void VertexDynamicState::render(float delta_time) * @brief Drawing frames and/or updating uniform buffers when camera position/rotation was changed @@ -106,7 +94,10 @@ void VertexDynamicState::render(float delta_time) { return; } - draw(); + ApiVulkanSample::prepare_frame(); + update_uniform_buffers(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } /** diff --git a/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.h b/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.h index 35913403c..49a90a18c 100644 --- a/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.h +++ b/samples/extensions/vertex_dynamic_state/vertex_dynamic_state.h @@ -76,7 +76,6 @@ class VertexDynamicState : public ApiVulkanSample void prepare_uniform_buffers(); void update_uniform_buffers(); void create_pipeline(); - void draw(); void load_assets(); void create_descriptor_pool(); From 3102e02746bcbf90d5971510f387bcc3dbb4ae2b Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 2 May 2026 21:32:06 +0200 Subject: [PATCH 21/23] Update samples to use new sync --- .../hpp_terrain_tessellation.cpp | 278 +++++++++--------- .../hpp_terrain_tessellation.h | 31 +- .../terrain_tessellation.cpp | 271 ++++++++--------- .../terrain_tessellation.h | 16 +- 4 files changed, 277 insertions(+), 319 deletions(-) diff --git a/samples/api/hpp_terrain_tessellation/hpp_terrain_tessellation.cpp b/samples/api/hpp_terrain_tessellation/hpp_terrain_tessellation.cpp index 459ac2785..62538ed8f 100644 --- a/samples/api/hpp_terrain_tessellation/hpp_terrain_tessellation.cpp +++ b/samples/api/hpp_terrain_tessellation/hpp_terrain_tessellation.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -25,6 +25,8 @@ HPPTerrainTessellation::HPPTerrainTessellation() { + use_new_sync = true; + title = "HPP Dynamic terrain tessellation"; } @@ -58,8 +60,6 @@ bool HPPTerrainTessellation::prepare(const vkb::ApplicationOptions &options) prepare_terrain(); prepare_wireframe(); prepare_statistics(); - build_command_buffers(); - prepared = true; } @@ -92,88 +92,78 @@ void HPPTerrainTessellation::request_gpu_features(vkb::core::PhysicalDeviceCpp & terrain.sampler_anisotropy_supported = available_features.samplerAnisotropy; } -void HPPTerrainTessellation::build_command_buffers() +void HPPTerrainTessellation::build_command_buffer() { + auto command_buffer = draw_cmd_buffers[current_buffer]; + command_buffer.reset(); + vk::CommandBufferBeginInfo command_buffer_begin_info; std::array clear_values = {{default_clear_color, vk::ClearDepthStencilValue{0.0f, 0}}}; vk::RenderPassBeginInfo render_pass_begin_info{.renderPass = render_pass, + .framebuffer = framebuffers[current_image_index], .renderArea = {{0, 0}, extent}, .clearValueCount = static_cast(clear_values.size()), .pClearValues = clear_values.data()}; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - auto command_buffer = draw_cmd_buffers[i]; - command_buffer.begin(command_buffer_begin_info); + command_buffer.begin(command_buffer_begin_info); - if (statistics.query_supported) - { - command_buffer.resetQueryPool(statistics.query_pool, 0, 2); - } + if (statistics.query_supported) + { + command_buffer.resetQueryPool(statistics.query_pool, 0, 2); + } - render_pass_begin_info.framebuffer = framebuffers[i]; - command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); + command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); - vk::Viewport viewport{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}; - command_buffer.setViewport(0, viewport); + vk::Viewport viewport{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}; + command_buffer.setViewport(0, viewport); - vk::Rect2D scissor{{0, 0}, extent}; - command_buffer.setScissor(0, scissor); + vk::Rect2D scissor{{0, 0}, extent}; + command_buffer.setScissor(0, scissor); - vk::DeviceSize offset = 0; + vk::DeviceSize offset = 0; - // Skysphere - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, sky_sphere.pipeline); - command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, sky_sphere.pipeline_layout, 0, sky_sphere.descriptor_set, {}); - draw_model(sky_sphere.geometry, command_buffer); + // Skysphere + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, sky_sphere.pipeline); + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, sky_sphere.pipeline_layout, 0, sky_sphere.descriptor_sets[current_buffer], {}); + draw_model(sky_sphere.geometry, command_buffer); - // Terrain - if (statistics.query_supported) - { - // Begin pipeline statistics query - command_buffer.beginQuery(statistics.query_pool, 0, {}); - } + // Terrain + if (statistics.query_supported) + { + // Begin pipeline statistics query + command_buffer.beginQuery(statistics.query_pool, 0, {}); + } - // Render - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, wireframe.enabled ? wireframe.pipeline : terrain.pipeline); - command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, terrain.pipeline_layout, 0, terrain.descriptor_set, {}); - command_buffer.bindVertexBuffers(0, terrain.vertices->get_handle(), offset); - command_buffer.bindIndexBuffer(terrain.indices->get_handle(), 0, vk::IndexType::eUint32); - command_buffer.drawIndexed(terrain.index_count, 1, 0, 0, 0); + // Render + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, wireframe.enabled ? wireframe.pipeline : terrain.pipeline); + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, terrain.pipeline_layout, 0, terrain.descriptor_sets[current_buffer], {}); + command_buffer.bindVertexBuffers(0, terrain.vertices->get_handle(), offset); + command_buffer.bindIndexBuffer(terrain.indices->get_handle(), 0, vk::IndexType::eUint32); + command_buffer.drawIndexed(terrain.index_count, 1, 0, 0, 0); - if (statistics.query_supported) - { - // End pipeline statistics query - command_buffer.endQuery(statistics.query_pool, 0); - } + if (statistics.query_supported) + { + // End pipeline statistics query + command_buffer.endQuery(statistics.query_pool, 0); + } - draw_ui(command_buffer); + draw_ui(command_buffer); - command_buffer.endRenderPass(); + command_buffer.endRenderPass(); - command_buffer.end(); - } + command_buffer.end(); } void HPPTerrainTessellation::on_update_ui_overlay(vkb::Drawer &drawer) { if (drawer.header("Settings")) { - if (drawer.checkbox("Tessellation", &terrain.tessellation_enabled)) - { - update_uniform_buffers(); - } - if (drawer.input_float("Factor", &terrain.tessellation.tessellation_factor, 0.05f, "%.2f")) - { - update_uniform_buffers(); - } + drawer.checkbox("Tessellation", &terrain.tessellation_enabled); + drawer.input_float("Factor", &terrain.tessellation.tessellation_factor, 0.05f, "%.2f"); if (wireframe.supported) { - if (drawer.checkbox("Wireframe", &wireframe.enabled)) - { - rebuild_command_buffers(); - } + drawer.checkbox("Wireframe", &wireframe.enabled); } } if (statistics.query_supported) @@ -190,7 +180,21 @@ void HPPTerrainTessellation::render(float delta_time) { if (prepared) { - draw(); + HPPApiVulkanSample::prepare_frame(); + update_uniform_buffers(); + build_command_buffer(); + HPPApiVulkanSample::submit_frame(); + if (statistics.query_supported) + { + // Read query results for displaying in next frame + auto result = get_device() + .get_handle() + .getQueryPoolResult>(statistics.query_pool, 0, 1, sizeof(statistics.results), vk::QueryResultFlagBits::e64); + if (result.result == vk::Result::eSuccess) + { + statistics.results = result.value; + } + } } } @@ -201,10 +205,10 @@ void HPPTerrainTessellation::view_changed() vk::DescriptorPool HPPTerrainTessellation::create_descriptor_pool() { - std::array pool_sizes = {{{vk::DescriptorType::eUniformBuffer, 3}, {vk::DescriptorType::eCombinedImageSampler, 3}}}; + std::array pool_sizes = {{{vk::DescriptorType::eUniformBuffer, max_concurrent_frames * 2}, {vk::DescriptorType::eCombinedImageSampler, max_concurrent_frames * 3}}}; return get_device().get_handle().createDescriptorPool( - {.maxSets = 2, .poolSizeCount = static_cast(pool_sizes.size()), .pPoolSizes = pool_sizes.data()}); + {.maxSets = max_concurrent_frames * 2, .poolSizeCount = static_cast(pool_sizes.size()), .pPoolSizes = pool_sizes.data()}); } vk::DescriptorSetLayout HPPTerrainTessellation::create_sky_sphere_descriptor_set_layout() @@ -323,31 +327,6 @@ vk::Pipeline HPPTerrainTessellation::create_terrain_pipeline(vk::PolygonMode pol render_pass); } -void HPPTerrainTessellation::draw() -{ - HPPApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]); - - // Submit to queue - queue.submit(submit_info); - - if (statistics.query_supported) - { - // Read query results for displaying in next frame - auto result = get_device() - .get_handle() - .getQueryPoolResult>(statistics.query_pool, 0, 1, sizeof(statistics.results), vk::QueryResultFlagBits::e64); - if (result.result == vk::Result::eSuccess) - { - statistics.results = result.value; - } - } - - HPPApiVulkanSample::submit_frame(); -} - // Generate a terrain quad patch for feeding to the tessellation control shader void HPPTerrainTessellation::generate_terrain() { @@ -469,7 +448,10 @@ void HPPTerrainTessellation::prepare_sky_sphere() sky_sphere.descriptor_set_layout = create_sky_sphere_descriptor_set_layout(); sky_sphere.pipeline_layout = get_device().get_handle().createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &sky_sphere.descriptor_set_layout}); sky_sphere.pipeline = create_sky_sphere_pipeline(); - sky_sphere.descriptor_set = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, {sky_sphere.descriptor_set_layout}); + for (auto i = 0; i < sky_sphere.descriptor_sets.size(); i++) + { + sky_sphere.descriptor_sets[i] = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, {sky_sphere.descriptor_set_layout}); + } update_sky_sphere_descriptor_set(); } @@ -496,22 +478,24 @@ void HPPTerrainTessellation::prepare_terrain() terrain.descriptor_set_layout = create_terrain_descriptor_set_layout(); terrain.pipeline_layout = get_device().get_handle().createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &terrain.descriptor_set_layout}); terrain.pipeline = create_terrain_pipeline(vk::PolygonMode::eFill); - terrain.descriptor_set = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, {terrain.descriptor_set_layout}); + for (auto i = 0; i < sky_sphere.descriptor_sets.size(); i++) + { + terrain.descriptor_sets[i] = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, {terrain.descriptor_set_layout}); + } update_terrain_descriptor_set(); } // Prepare and initialize uniform buffer containing shader uniforms void HPPTerrainTessellation::prepare_uniform_buffers() { - // Shared tessellation shader stages uniform buffer - terrain.tessellation_buffer = - std::make_unique(get_device(), sizeof(terrain.tessellation), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); - - // Skysphere vertex shader uniform buffer - sky_sphere.transform_buffer = - std::make_unique(get_device(), sizeof(sky_sphere.transform), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); + for (auto i = 0; i < max_concurrent_frames; i++) + { + // Shared tessellation shader stages uniform buffer + terrain.tessellation_buffers[i] = std::make_unique(get_device(), sizeof(terrain.tessellation), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); - update_uniform_buffers(); + // Skysphere vertex shader uniform buffer + sky_sphere.transform_buffers[i] = std::make_unique(get_device(), sizeof(sky_sphere.transform), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void HPPTerrainTessellation::prepare_wireframe() @@ -541,7 +525,7 @@ void HPPTerrainTessellation::update_uniform_buffers() terrain.tessellation.tessellation_factor = 0.0f; } - terrain.tessellation_buffer->convert_and_update(terrain.tessellation); + terrain.tessellation_buffers[current_buffer]->convert_and_update(terrain.tessellation); if (!terrain.tessellation_enabled) { @@ -550,63 +534,69 @@ void HPPTerrainTessellation::update_uniform_buffers() // Skysphere vertex shader sky_sphere.transform = camera.matrices.perspective * glm::mat4(glm::mat3(camera.matrices.view)); - sky_sphere.transform_buffer->convert_and_update(sky_sphere.transform); + sky_sphere.transform_buffers[current_buffer]->convert_and_update(sky_sphere.transform); } void HPPTerrainTessellation::update_sky_sphere_descriptor_set() { - vk::DescriptorBufferInfo skysphere_buffer_descriptor{sky_sphere.transform_buffer->get_handle(), 0, vk::WholeSize}; - - vk::DescriptorImageInfo skysphere_image_descriptor{sky_sphere.texture.sampler, - sky_sphere.texture.image->get_vk_image_view().get_handle(), - descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler, - sky_sphere.texture.image->get_vk_image_view().get_format())}; - - std::array skysphere_write_descriptor_sets = {{{.dstSet = sky_sphere.descriptor_set, - .dstBinding = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &skysphere_buffer_descriptor}, - {.dstSet = sky_sphere.descriptor_set, - .dstBinding = 1, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &skysphere_image_descriptor}}}; - - get_device().get_handle().updateDescriptorSets(skysphere_write_descriptor_sets, {}); + for (auto i = 0; i < sky_sphere.transform_buffers.size(); i++) + { + vk::DescriptorBufferInfo skysphere_buffer_descriptor{sky_sphere.transform_buffers[i]->get_handle(), 0, vk::WholeSize}; + + vk::DescriptorImageInfo skysphere_image_descriptor{sky_sphere.texture.sampler, + sky_sphere.texture.image->get_vk_image_view().get_handle(), + descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler, + sky_sphere.texture.image->get_vk_image_view().get_format())}; + + std::array skysphere_write_descriptor_sets = {{{.dstSet = sky_sphere.descriptor_sets[i], + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &skysphere_buffer_descriptor}, + {.dstSet = sky_sphere.descriptor_sets[i], + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &skysphere_image_descriptor}}}; + + get_device().get_handle().updateDescriptorSets(skysphere_write_descriptor_sets, {}); + } } void HPPTerrainTessellation::update_terrain_descriptor_set() { - vk::DescriptorBufferInfo terrain_buffer_descriptor{terrain.tessellation_buffer->get_handle(), 0, vk::WholeSize}; - - vk::DescriptorImageInfo heightmap_image_descriptor{terrain.height_map.sampler, - terrain.height_map.image->get_vk_image_view().get_handle(), - descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler, - terrain.height_map.image->get_vk_image_view().get_format())}; - - vk::DescriptorImageInfo terrainmap_image_descriptor{terrain.terrain_array.sampler, - terrain.terrain_array.image->get_vk_image_view().get_handle(), - descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler, - terrain.terrain_array.image->get_vk_image_view().get_format())}; - - std::array terrain_write_descriptor_sets = {{{.dstSet = terrain.descriptor_set, - .dstBinding = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &terrain_buffer_descriptor}, - {.dstSet = terrain.descriptor_set, - .dstBinding = 1, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &heightmap_image_descriptor}, - {.dstSet = terrain.descriptor_set, - .dstBinding = 2, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &terrainmap_image_descriptor}}}; - - get_device().get_handle().updateDescriptorSets(terrain_write_descriptor_sets, {}); + for (auto i = 0; i < terrain.tessellation_buffers.size(); i++) + { + vk::DescriptorBufferInfo terrain_buffer_descriptor{terrain.tessellation_buffers[i]->get_handle(), 0, vk::WholeSize}; + + vk::DescriptorImageInfo heightmap_image_descriptor{terrain.height_map.sampler, + terrain.height_map.image->get_vk_image_view().get_handle(), + descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler, + terrain.height_map.image->get_vk_image_view().get_format())}; + + vk::DescriptorImageInfo terrainmap_image_descriptor{terrain.terrain_array.sampler, + terrain.terrain_array.image->get_vk_image_view().get_handle(), + descriptor_type_to_image_layout(vk::DescriptorType::eCombinedImageSampler, + terrain.terrain_array.image->get_vk_image_view().get_format())}; + + std::array terrain_write_descriptor_sets = {{{.dstSet = terrain.descriptor_sets[i], + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &terrain_buffer_descriptor}, + {.dstSet = terrain.descriptor_sets[i], + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &heightmap_image_descriptor}, + {.dstSet = terrain.descriptor_sets[i], + .dstBinding = 2, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &terrainmap_image_descriptor}}}; + + get_device().get_handle().updateDescriptorSets(terrain_write_descriptor_sets, {}); + } } std::unique_ptr create_hpp_terrain_tessellation() diff --git a/samples/api/hpp_terrain_tessellation/hpp_terrain_tessellation.h b/samples/api/hpp_terrain_tessellation/hpp_terrain_tessellation.h index 8418d515a..dbc628e09 100644 --- a/samples/api/hpp_terrain_tessellation/hpp_terrain_tessellation.h +++ b/samples/api/hpp_terrain_tessellation/hpp_terrain_tessellation.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -34,17 +34,17 @@ class HPPTerrainTessellation : public HPPApiVulkanSample private: struct SkySphere { - vk::DescriptorSet descriptor_set; - vk::DescriptorSetLayout descriptor_set_layout; - vk::PipelineLayout pipeline_layout; - vk::Pipeline pipeline; + std::array descriptor_sets; + vk::DescriptorSetLayout descriptor_set_layout; + vk::PipelineLayout pipeline_layout; + vk::Pipeline pipeline; std::unique_ptr geometry; HPPTexture texture; - glm::mat4 transform; - std::unique_ptr transform_buffer; + glm::mat4 transform; + std::array, max_concurrent_frames> transform_buffers; void destroy(vk::Device device) { @@ -89,10 +89,10 @@ class HPPTerrainTessellation : public HPPApiVulkanSample struct Terrain { - vk::DescriptorSet descriptor_set; - vk::DescriptorSetLayout descriptor_set_layout; - vk::PipelineLayout pipeline_layout; - vk::Pipeline pipeline; + std::array descriptor_sets; + vk::DescriptorSetLayout descriptor_set_layout; + vk::PipelineLayout pipeline_layout; + vk::Pipeline pipeline; std::unique_ptr vertices; std::unique_ptr indices; @@ -105,9 +105,9 @@ class HPPTerrainTessellation : public HPPApiVulkanSample std::vector shader_stages; - Tessellation tessellation; - std::unique_ptr tessellation_buffer; - bool tessellation_enabled = true; + Tessellation tessellation; + std::array, max_concurrent_frames> tessellation_buffers; + bool tessellation_enabled = true; void destroy(vk::Device device) { @@ -148,7 +148,6 @@ class HPPTerrainTessellation : public HPPApiVulkanSample void request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) override; // from HPPApiVulkanSample - void build_command_buffers() override; void on_update_ui_overlay(vkb::Drawer &drawer) override; void render(float delta_time) override; void view_changed() override; @@ -158,7 +157,7 @@ class HPPTerrainTessellation : public HPPApiVulkanSample vk::Pipeline create_sky_sphere_pipeline(); vk::DescriptorSetLayout create_terrain_descriptor_set_layout(); vk::Pipeline create_terrain_pipeline(vk::PolygonMode polygon_mode); - void draw(); + void build_command_buffer(); void generate_terrain(); void load_assets(); void prepare_camera(); diff --git a/samples/api/terrain_tessellation/terrain_tessellation.cpp b/samples/api/terrain_tessellation/terrain_tessellation.cpp index 4a2d8382b..a67337a5c 100644 --- a/samples/api/terrain_tessellation/terrain_tessellation.cpp +++ b/samples/api/terrain_tessellation/terrain_tessellation.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -25,6 +25,8 @@ TerrainTessellation::TerrainTessellation() { + use_new_sync = true; + title = "Dynamic terrain tessellation"; } @@ -47,9 +49,6 @@ TerrainTessellation::~TerrainTessellation() vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layouts.terrain, nullptr); vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layouts.skysphere, nullptr); - uniform_buffers.skysphere_vertex.reset(); - uniform_buffers.terrain_tessellation.reset(); - textures.heightmap.image.reset(); vkDestroySampler(get_device().get_handle(), textures.heightmap.sampler, nullptr); textures.skysphere.image.reset(); @@ -186,8 +185,11 @@ void TerrainTessellation::load_assets() VK_CHECK(vkCreateSampler(get_device().get_handle(), &sampler_create_info, nullptr, &textures.terrain_array.sampler)); } -void TerrainTessellation::build_command_buffers() +void TerrainTessellation::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -202,59 +204,55 @@ void TerrainTessellation::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - render_pass_begin_info.framebuffer = framebuffers[i]; - - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); - if (get_device().get_gpu().get_features().pipelineStatisticsQuery) - { - vkCmdResetQueryPool(draw_cmd_buffers[i], query_pool, 0, 2); - } + if (get_device().get_gpu().get_features().pipelineStatisticsQuery) + { + vkCmdResetQueryPool(draw_cmd_buffer, query_pool, 0, 2); + } - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - vkCmdSetLineWidth(draw_cmd_buffers[i], 1.0f); + vkCmdSetLineWidth(draw_cmd_buffer, 1.0f); - VkDeviceSize offsets[1] = {0}; + VkDeviceSize offsets[1] = {0}; - // Skysphere - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skysphere); - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layouts.skysphere, 0, 1, &descriptor_sets.skysphere, 0, NULL); - draw_model(skysphere, draw_cmd_buffers[i]); + // Skysphere + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skysphere); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layouts.skysphere, 0, 1, &descriptor_sets[current_buffer].skysphere, 0, NULL); + draw_model(skysphere, draw_cmd_buffer); - // Terrain - if (get_device().get_gpu().get_features().pipelineStatisticsQuery) - { - // Begin pipeline statistics query - vkCmdBeginQuery(draw_cmd_buffers[i], query_pool, 0, 0); - } - // Render - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.terrain); - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layouts.terrain, 0, 1, &descriptor_sets.terrain, 0, NULL); - vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, terrain.vertices->get(), offsets); - vkCmdBindIndexBuffer(draw_cmd_buffers[i], terrain.indices->get_handle(), 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(draw_cmd_buffers[i], terrain.index_count, 1, 0, 0, 0); - if (get_device().get_gpu().get_features().pipelineStatisticsQuery) - { - // End pipeline statistics query - vkCmdEndQuery(draw_cmd_buffers[i], query_pool, 0); - } + // Terrain + if (get_device().get_gpu().get_features().pipelineStatisticsQuery) + { + // Begin pipeline statistics query + vkCmdBeginQuery(draw_cmd_buffer, query_pool, 0, 0); + } + // Render + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.terrain); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layouts.terrain, 0, 1, &descriptor_sets[current_buffer].terrain, 0, NULL); + vkCmdBindVertexBuffers(draw_cmd_buffer, 0, 1, terrain.vertices->get(), offsets); + vkCmdBindIndexBuffer(draw_cmd_buffer, terrain.indices->get_handle(), 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(draw_cmd_buffer, terrain.index_count, 1, 0, 0, 0); + if (get_device().get_gpu().get_features().pipelineStatisticsQuery) + { + // End pipeline statistics query + vkCmdEndQuery(draw_cmd_buffer, query_pool, 0); + } - draw_ui(draw_cmd_buffers[i]); + draw_ui(draw_cmd_buffer); - vkCmdEndRenderPass(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffer); - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } // Generate a terrain quad patch for feeding to the tessellation control shader @@ -373,14 +371,14 @@ void TerrainTessellation::setup_descriptor_pool() { std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3)}; + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames * 2), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, max_concurrent_frames * 3)}; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info( static_cast(pool_sizes.size()), pool_sizes.data(), - 2); + max_concurrent_frames * 2); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } @@ -443,57 +441,61 @@ void TerrainTessellation::setup_descriptor_sets() std::vector write_descriptor_sets; // Terrain - alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layouts.terrain, 1); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets.terrain)); - - VkDescriptorBufferInfo terrain_buffer_descriptor = create_descriptor(*uniform_buffers.terrain_tessellation); - VkDescriptorImageInfo heightmap_image_descriptor = create_descriptor(textures.heightmap); - VkDescriptorImageInfo terrainmap_image_descriptor = create_descriptor(textures.terrain_array); - write_descriptor_sets = - { - // Binding 0 : Shared tessellation shader ubo - vkb::initializers::write_descriptor_set( - descriptor_sets.terrain, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &terrain_buffer_descriptor), - // Binding 1 : Displacement map - vkb::initializers::write_descriptor_set( - descriptor_sets.terrain, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 1, - &heightmap_image_descriptor), - // Binding 2 : Color map (alpha channel) - vkb::initializers::write_descriptor_set( - descriptor_sets.terrain, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 2, - &terrainmap_image_descriptor), - }; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); + VkDescriptorImageInfo heightmap_image_descriptor = create_descriptor(textures.heightmap); + VkDescriptorImageInfo terrainmap_image_descriptor = create_descriptor(textures.terrain_array); - // Skysphere - alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layouts.skysphere, 1); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets.skysphere)); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layouts.terrain, 1); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets[i].terrain)); + + VkDescriptorBufferInfo terrain_buffer_descriptor = create_descriptor(*uniform_buffers[i].terrain_tessellation); + write_descriptor_sets = + { + // Binding 0 : Shared tessellation shader ubo + vkb::initializers::write_descriptor_set( + descriptor_sets[i].terrain, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, + &terrain_buffer_descriptor), + // Binding 1 : Displacement map + vkb::initializers::write_descriptor_set( + descriptor_sets[i].terrain, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1, + &heightmap_image_descriptor), + // Binding 2 : Color map (alpha channel) + vkb::initializers::write_descriptor_set( + descriptor_sets[i].terrain, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 2, + &terrainmap_image_descriptor), + }; + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); - VkDescriptorBufferInfo skysphere_buffer_descriptor = create_descriptor(*uniform_buffers.skysphere_vertex); - VkDescriptorImageInfo skysphere_image_descriptor = create_descriptor(textures.skysphere); - write_descriptor_sets = - { - // Binding 0 : Vertex shader ubo - vkb::initializers::write_descriptor_set( - descriptor_sets.skysphere, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &skysphere_buffer_descriptor), - // Binding 1 : Fragment shader color map - vkb::initializers::write_descriptor_set( - descriptor_sets.skysphere, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 1, - &skysphere_image_descriptor), - }; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); + // Skysphere + alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layouts.skysphere, 1); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets[i].skysphere)); + + VkDescriptorBufferInfo skysphere_buffer_descriptor = create_descriptor(*uniform_buffers[i].skysphere_vertex); + VkDescriptorImageInfo skysphere_image_descriptor = create_descriptor(textures.skysphere); + write_descriptor_sets = + { + // Binding 0 : Vertex shader ubo + vkb::initializers::write_descriptor_set( + descriptor_sets[i].skysphere, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, + &skysphere_buffer_descriptor), + // Binding 1 : Fragment shader color map + vkb::initializers::write_descriptor_set( + descriptor_sets[i].skysphere, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1, + &skysphere_image_descriptor), + }; + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); + } } void TerrainTessellation::prepare_pipelines() @@ -625,19 +627,13 @@ void TerrainTessellation::prepare_pipelines() // Prepare and initialize uniform buffer containing shader uniforms void TerrainTessellation::prepare_uniform_buffers() { - // Shared tessellation shader stages uniform buffer - uniform_buffers.terrain_tessellation = std::make_unique(get_device(), - sizeof(ubo_tess), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - // Skysphere vertex shader uniform buffer - uniform_buffers.skysphere_vertex = std::make_unique(get_device(), - sizeof(ubo_vs), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + // Shared tessellation shader stages uniform buffer + uniform_buffers[i].terrain_tessellation = std::make_unique(get_device(), sizeof(ubo_tess), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + // Skysphere vertex shader uniform buffer + uniform_buffers[i].skysphere_vertex = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void TerrainTessellation::update_uniform_buffers() @@ -659,7 +655,7 @@ void TerrainTessellation::update_uniform_buffers() ubo_tess.tessellation_factor = 0.0f; } - uniform_buffers.terrain_tessellation->convert_and_update(ubo_tess); + uniform_buffers[current_buffer].terrain_tessellation->convert_and_update(ubo_tess); if (!tessellation) { @@ -668,27 +664,7 @@ void TerrainTessellation::update_uniform_buffers() // Skysphere vertex shader ubo_vs.mvp = camera.matrices.perspective * glm::mat4(glm::mat3(camera.matrices.view)); - uniform_buffers.skysphere_vertex->convert_and_update(ubo_vs.mvp); -} - -void TerrainTessellation::draw() -{ - ApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - - // Submit to queue - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); - - if (get_device().get_gpu().get_features().pipelineStatisticsQuery) - { - // Read query results for displaying in next frame - get_query_results(); - } - - ApiVulkanSample::submit_frame(); + uniform_buffers[current_buffer].skysphere_vertex->convert_and_update(ubo_vs.mvp); } bool TerrainTessellation::prepare(const vkb::ApplicationOptions &options) @@ -716,7 +692,6 @@ bool TerrainTessellation::prepare(const vkb::ApplicationOptions &options) prepare_pipelines(); setup_descriptor_pool(); setup_descriptor_sets(); - build_command_buffers(); prepared = true; return true; } @@ -727,32 +702,26 @@ void TerrainTessellation::render(float delta_time) { return; } - draw(); -} - -void TerrainTessellation::view_changed() -{ + ApiVulkanSample::prepare_frame(); update_uniform_buffers(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); + if (get_device().get_gpu().get_features().pipelineStatisticsQuery) + { + // Read query results for displaying in next frame + get_query_results(); + } } void TerrainTessellation::on_update_ui_overlay(vkb::Drawer &drawer) { if (drawer.header("Settings")) { - if (drawer.checkbox("Tessellation", &tessellation)) - { - update_uniform_buffers(); - } - if (drawer.input_float("Factor", &ubo_tess.tessellation_factor, 0.05f, "%.2f")) - { - update_uniform_buffers(); - } + drawer.checkbox("Tessellation", &tessellation); + drawer.input_float("Factor", &ubo_tess.tessellation_factor, 0.05f, "%.2f"); if (get_device().get_gpu().get_features().fillModeNonSolid) { - if (drawer.checkbox("Wireframe", &wireframe)) - { - rebuild_command_buffers(); - } + drawer.checkbox("Wireframe", &wireframe); } } if (get_device().get_gpu().get_features().pipelineStatisticsQuery) diff --git a/samples/api/terrain_tessellation/terrain_tessellation.h b/samples/api/terrain_tessellation/terrain_tessellation.h index 76e6e81a1..cff343ba2 100644 --- a/samples/api/terrain_tessellation/terrain_tessellation.h +++ b/samples/api/terrain_tessellation/terrain_tessellation.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -54,11 +54,12 @@ class TerrainTessellation : public ApiVulkanSample uint32_t index_count; } terrain; - struct + struct UniformBuffers { std::unique_ptr terrain_tessellation; std::unique_ptr skysphere_vertex; - } uniform_buffers; + }; + std::array uniform_buffers; // Shared values for tessellation control and evaluation stages struct @@ -99,11 +100,12 @@ class TerrainTessellation : public ApiVulkanSample VkPipelineLayout skysphere; } pipeline_layouts; - struct + struct DescriptorSets { VkDescriptorSet terrain; VkDescriptorSet skysphere; - } descriptor_sets; + }; + std::array descriptor_sets; // Pipeline statistics VkQueryPool query_pool = VK_NULL_HANDLE; @@ -118,7 +120,7 @@ class TerrainTessellation : public ApiVulkanSample void setup_query_result_buffer(); void get_query_results(); void load_assets(); - void build_command_buffers() override; + void build_command_buffer(); void generate_terrain(); void setup_descriptor_pool(); void setup_descriptor_set_layouts(); @@ -126,10 +128,8 @@ class TerrainTessellation : public ApiVulkanSample void prepare_pipelines(); void prepare_uniform_buffers(); void update_uniform_buffers(); - void draw(); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; - virtual void view_changed() override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; }; From 6374dcac5eaecaacbcc75e153587c762cc6fa0fe Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sun, 3 May 2026 11:20:54 +0200 Subject: [PATCH 22/23] Update samples to use new sync --- .../hpp_texture_loading.cpp | 144 ++++++------- .../hpp_texture_loading/hpp_texture_loading.h | 26 ++- .../hpp_texture_mipmap_generation.cpp | 161 ++++++-------- .../hpp_texture_mipmap_generation.h | 20 +- .../api/texture_loading/texture_loading.cpp | 166 +++++++-------- samples/api/texture_loading/texture_loading.h | 12 +- .../texture_mipmap_generation.cpp | 200 ++++++++---------- .../texture_mipmap_generation.h | 11 +- 8 files changed, 325 insertions(+), 415 deletions(-) diff --git a/samples/api/hpp_texture_loading/hpp_texture_loading.cpp b/samples/api/hpp_texture_loading/hpp_texture_loading.cpp index 98770dc07..4d0e80e87 100644 --- a/samples/api/hpp_texture_loading/hpp_texture_loading.cpp +++ b/samples/api/hpp_texture_loading/hpp_texture_loading.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2021-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -25,6 +25,8 @@ HPPTextureLoading::HPPTextureLoading() { + use_new_sync = true; + title = "HPP Texture loading"; zoom = -2.5f; @@ -47,7 +49,6 @@ HPPTextureLoading::~HPPTextureLoading() vertex_buffer.reset(); index_buffer.reset(); - vertex_shader_data_buffer.reset(); } bool HPPTextureLoading::prepare(const vkb::ApplicationOptions &options) @@ -63,9 +64,7 @@ bool HPPTextureLoading::prepare(const vkb::ApplicationOptions &options) pipeline_layout = get_device().get_handle().createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &descriptor_set_layout}); pipeline = create_pipeline(); descriptor_pool = create_descriptor_pool(); - descriptor_set = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, {descriptor_set_layout}); update_descriptor_set(); - build_command_buffers(); prepared = true; } @@ -83,8 +82,11 @@ void HPPTextureLoading::request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) } } -void HPPTextureLoading::build_command_buffers() +void HPPTextureLoading::build_command_buffer() { + auto command_buffer = draw_cmd_buffers[current_buffer]; + command_buffer.reset(); + vk::CommandBufferBeginInfo command_buffer_begin_info; vk::ClearValue clear_values[2]; @@ -98,49 +100,39 @@ void HPPTextureLoading::build_command_buffers() render_pass_begin_info.renderArea.extent = extent; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - auto command_buffer = draw_cmd_buffers[i]; - - // Set target frame buffer - render_pass_begin_info.framebuffer = framebuffers[i]; + command_buffer.begin(command_buffer_begin_info); - command_buffer.begin(command_buffer_begin_info); + command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); - command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); + vk::Viewport viewport{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}; + command_buffer.setViewport(0, viewport); - vk::Viewport viewport{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}; - command_buffer.setViewport(0, viewport); + vk::Rect2D scissor{{0, 0}, extent}; + command_buffer.setScissor(0, scissor); - vk::Rect2D scissor{{0, 0}, extent}; - command_buffer.setScissor(0, scissor); + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptor_sets[current_buffer], {}); + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptor_set, {}); - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); + vk::DeviceSize offset = 0; + command_buffer.bindVertexBuffers(0, vertex_buffer->get_handle(), offset); + command_buffer.bindIndexBuffer(index_buffer->get_handle(), 0, vk::IndexType::eUint32); - vk::DeviceSize offset = 0; - command_buffer.bindVertexBuffers(0, vertex_buffer->get_handle(), offset); - command_buffer.bindIndexBuffer(index_buffer->get_handle(), 0, vk::IndexType::eUint32); + command_buffer.drawIndexed(index_count, 1, 0, 0, 0); - command_buffer.drawIndexed(index_count, 1, 0, 0, 0); + draw_ui(command_buffer); - draw_ui(command_buffer); + command_buffer.endRenderPass(); - command_buffer.endRenderPass(); - - command_buffer.end(); - } + command_buffer.end(); } void HPPTextureLoading::on_update_ui_overlay(vkb::Drawer &drawer) { if (drawer.header("Settings")) { - if (drawer.slider_float("LOD bias", &vertex_shader_data.lod_bias, 0.0f, static_cast(texture.mip_levels))) - { - update_uniform_buffers(); - } + drawer.slider_float("LOD bias", &vertex_shader_data.lod_bias, 0.0f, static_cast(texture.mip_levels)); } } @@ -148,15 +140,13 @@ void HPPTextureLoading::render(float delta_time) { if (prepared) { - draw(); + HPPApiVulkanSample::prepare_frame(); + update_uniform_buffers(); + build_command_buffer(); + HPPApiVulkanSample::submit_frame(); } } -void HPPTextureLoading::view_changed() -{ - update_uniform_buffers(); -} - vk::DescriptorPool HPPTextureLoading::create_descriptor_pool() { // Example uses one ubo and one image sampler @@ -218,19 +208,6 @@ vk::Pipeline HPPTextureLoading::create_pipeline() render_pass); } -void HPPTextureLoading::draw() -{ - HPPApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]); - - // Submit to queue - queue.submit(submit_info); - - HPPApiVulkanSample::submit_frame(); -} - void HPPTextureLoading::generate_quad() { // Setup vertices for a single uv-mapped quad made from two triangles @@ -534,39 +511,44 @@ void HPPTextureLoading::load_texture() // Prepare and initialize uniform buffer containing shader uniforms void HPPTextureLoading::prepare_uniform_buffers() { - // Vertex shader uniform buffer block - vertex_shader_data_buffer = - std::make_unique(get_device(), sizeof(vertex_shader_data), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + // Vertex shader uniform buffer block + uniform_buffers[i] = std::make_unique(get_device(), sizeof(vertex_shader_data), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void HPPTextureLoading::update_descriptor_set() { - vk::DescriptorBufferInfo buffer_descriptor{vertex_shader_data_buffer->get_handle(), 0, vk::WholeSize}; - - // Setup a descriptor image info for the current texture to be used as a combined image sampler - vk::DescriptorImageInfo image_descriptor{ - texture.sampler, // The sampler (the sampler describes how to sample the image, including repeat, border, etc.) - texture.image_view, // The image view (the image view describes the image and the subresources that can be accessed) - texture.image_layout // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) - }; - - std::array write_descriptor_sets = {{// Binding 0 : Vertex shader uniform buffer - {.dstSet = descriptor_set, - .dstBinding = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_descriptor}, - // Binding 1 : Fragment shader texture sampler - // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; - {.dstSet = descriptor_set, - .dstBinding = 1, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &image_descriptor}}}; - - get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {}); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + descriptor_sets[i] = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, descriptor_set_layout); + + vk::DescriptorBufferInfo buffer_descriptor{uniform_buffers[i]->get_handle(), 0, vk::WholeSize}; + + // Setup a descriptor image info for the current texture to be used as a combined image sampler + vk::DescriptorImageInfo image_descriptor{ + texture.sampler, // The sampler (the sampler describes how to sample the image, including repeat, border, etc.) + texture.image_view, // The image view (the image view describes the image and the subresources that can be accessed) + texture.image_layout // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) + }; + + std::array write_descriptor_sets = {{// Binding 0 : Vertex shader uniform buffer + {.dstSet = descriptor_sets[i], + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &buffer_descriptor}, + // Binding 1 : Fragment shader texture sampler + // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; + {.dstSet = descriptor_sets[i], + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &image_descriptor}}}; + + get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {}); + } } void HPPTextureLoading::update_uniform_buffers() @@ -582,7 +564,7 @@ void HPPTextureLoading::update_uniform_buffers() vertex_shader_data.view_pos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f); - vertex_shader_data_buffer->convert_and_update(vertex_shader_data); + uniform_buffers[current_buffer]->convert_and_update(vertex_shader_data); } std::unique_ptr create_hpp_texture_loading() diff --git a/samples/api/hpp_texture_loading/hpp_texture_loading.h b/samples/api/hpp_texture_loading/hpp_texture_loading.h index 9170c73bb..99065ca56 100644 --- a/samples/api/hpp_texture_loading/hpp_texture_loading.h +++ b/samples/api/hpp_texture_loading/hpp_texture_loading.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2021-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -75,15 +75,13 @@ class HPPTextureLoading : public HPPApiVulkanSample void request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) override; // from HPPApiVulkanSample - void build_command_buffers() override; void on_update_ui_overlay(vkb::Drawer &drawer) override; void render(float delta_time) override; - void view_changed() override; vk::DescriptorPool create_descriptor_pool(); vk::DescriptorSetLayout create_descriptor_set_layout(); vk::Pipeline create_pipeline(); - void draw(); + void build_command_buffer(); void generate_quad(); void load_texture(); void prepare_uniform_buffers(); @@ -91,16 +89,16 @@ class HPPTextureLoading : public HPPApiVulkanSample void update_uniform_buffers(); private: - vk::DescriptorSet descriptor_set; - vk::DescriptorSetLayout descriptor_set_layout; - std::unique_ptr index_buffer; - uint32_t index_count; - vk::Pipeline pipeline; - vk::PipelineLayout pipeline_layout; - Texture texture; - std::unique_ptr vertex_buffer; - VertexShaderData vertex_shader_data; - std::unique_ptr vertex_shader_data_buffer; + vk::DescriptorSetLayout descriptor_set_layout; + std::unique_ptr index_buffer; + uint32_t index_count; + vk::Pipeline pipeline; + vk::PipelineLayout pipeline_layout; + Texture texture; + std::unique_ptr vertex_buffer; + VertexShaderData vertex_shader_data; + std::array, max_concurrent_frames> uniform_buffers; + std::array descriptor_sets; }; std::unique_ptr create_hpp_texture_loading(); diff --git a/samples/api/hpp_texture_mipmap_generation/hpp_texture_mipmap_generation.cpp b/samples/api/hpp_texture_mipmap_generation/hpp_texture_mipmap_generation.cpp index 166852d05..021464f13 100644 --- a/samples/api/hpp_texture_mipmap_generation/hpp_texture_mipmap_generation.cpp +++ b/samples/api/hpp_texture_mipmap_generation/hpp_texture_mipmap_generation.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -27,6 +27,8 @@ HPPTextureMipMapGeneration::HPPTextureMipMapGeneration() { + use_new_sync = true; + title = "Texture MipMap generation"; zoom = -2.5f; @@ -49,7 +51,6 @@ HPPTextureMipMapGeneration::~HPPTextureMipMapGeneration() device.destroyImageView(texture.view); device.destroyImage(texture.image); device.freeMemory(texture.device_memory); - uniform_buffer.reset(); } } @@ -67,9 +68,7 @@ bool HPPTextureMipMapGeneration::prepare(const vkb::ApplicationOptions &options) pipeline_layout = get_device().get_handle().createPipelineLayout({.setLayoutCount = 1, .pSetLayouts = &descriptor_set_layout}); pipeline = create_pipeline(); descriptor_pool = create_descriptor_pool(); - descriptor_set = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, descriptor_set_layout); update_descriptor_set(); - build_command_buffers(); prepared = true; } @@ -87,44 +86,41 @@ void HPPTextureMipMapGeneration::request_gpu_features(vkb::core::PhysicalDeviceC } } -void HPPTextureMipMapGeneration::build_command_buffers() +void HPPTextureMipMapGeneration::build_command_buffer() { + auto command_buffer = draw_cmd_buffers[current_buffer]; + command_buffer.reset(); + vk::CommandBufferBeginInfo command_buffer_begin_info; std::array clear_values = {{default_clear_color, vk::ClearDepthStencilValue{1.0f, 0}}}; vk::RenderPassBeginInfo render_pass_begin_info{.renderPass = render_pass, + .framebuffer = framebuffers[current_image_index], .renderArea = {{0, 0}, extent}, .clearValueCount = static_cast(clear_values.size()), .pClearValues = clear_values.data()}; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - auto command_buffer = draw_cmd_buffers[i]; - - render_pass_begin_info.framebuffer = framebuffers[i]; + command_buffer.begin(command_buffer_begin_info); - command_buffer.begin(command_buffer_begin_info); + command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); - command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); + vk::Viewport viewport{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}; + command_buffer.setViewport(0, viewport); - vk::Viewport viewport{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}; - command_buffer.setViewport(0, viewport); + vk::Rect2D scissor{{0, 0}, extent}; + command_buffer.setScissor(0, scissor); - vk::Rect2D scissor{{0, 0}, extent}; - command_buffer.setScissor(0, scissor); + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptor_sets[current_buffer], {}); + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptor_set, {}); - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); + draw_model(scene, command_buffer); - draw_model(scene, command_buffer); + draw_ui(command_buffer); - draw_ui(command_buffer); + command_buffer.endRenderPass(); - command_buffer.endRenderPass(); - - command_buffer.end(); - } + command_buffer.end(); } void HPPTextureMipMapGeneration::on_update_ui_overlay(vkb::Drawer &drawer) @@ -132,14 +128,8 @@ void HPPTextureMipMapGeneration::on_update_ui_overlay(vkb::Drawer &drawer) if (drawer.header("Settings")) { drawer.checkbox("Rotate", &rotate_scene); - if (drawer.slider_float("LOD bias", &ubo.lod_bias, 0.0f, static_cast(texture.mip_levels))) - { - update_uniform_buffers(); - } - if (drawer.combo_box("Sampler type", &ubo.sampler_index, sampler_names)) - { - update_uniform_buffers(); - } + drawer.slider_float("LOD bias", &ubo.lod_bias, 0.0f, static_cast(texture.mip_levels)); + drawer.combo_box("Sampler type", &ubo.sampler_index, sampler_names); } } @@ -147,19 +137,13 @@ void HPPTextureMipMapGeneration::render(float delta_time) { if (prepared) { - draw(); - if (rotate_scene) - { - update_uniform_buffers(delta_time); - } + HPPApiVulkanSample::prepare_frame(); + update_uniform_buffers(delta_time); + build_command_buffer(); + HPPApiVulkanSample::submit_frame(); } } -void HPPTextureMipMapGeneration::view_changed() -{ - update_uniform_buffers(); -} - void HPPTextureMipMapGeneration::check_format_features(vk::Format format) const { // Get device properties for the requested texture format @@ -244,19 +228,6 @@ vk::Pipeline HPPTextureMipMapGeneration::create_pipeline() render_pass); } -void HPPTextureMipMapGeneration::draw() -{ - HPPApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]); - - // Submit to queue - queue.submit(submit_info); - - HPPApiVulkanSample::submit_frame(); -} - void HPPTextureMipMapGeneration::load_assets() { scene = load_model("scenes/tunnel_cylinder.gltf"); @@ -396,43 +367,46 @@ void HPPTextureMipMapGeneration::prepare_camera() void HPPTextureMipMapGeneration::prepare_uniform_buffers() { - // Shared parameter uniform buffer block - uniform_buffer = std::make_unique(get_device(), - sizeof(ubo), - vk::BufferUsageFlagBits::eUniformBuffer, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + // Shared parameter uniform buffer block + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo), vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void HPPTextureMipMapGeneration::update_descriptor_set() { - vk::DescriptorBufferInfo buffer_descriptor{uniform_buffer->get_handle(), 0, vk::WholeSize}; - - vk::DescriptorImageInfo image_descriptor{nullptr, texture.view, vk::ImageLayout::eShaderReadOnlyOptimal}; - - std::array sampler_descriptors = {{{samplers[0], nullptr, vk::ImageLayout::eShaderReadOnlyOptimal}, - {samplers[1], nullptr, vk::ImageLayout::eShaderReadOnlyOptimal}, - {samplers[2], nullptr, vk::ImageLayout::eShaderReadOnlyOptimal}}}; - assert(samplers.size() == sampler_descriptors.size()); - - std::array write_descriptor_sets = {{{.dstSet = descriptor_set, - .dstBinding = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_descriptor}, // Binding 0 : Vertex shader uniform buffer - {.dstSet = descriptor_set, - .dstBinding = 1, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eSampledImage, - .pImageInfo = &image_descriptor}, // Binding 1 : Fragment shader texture sampler - {.dstSet = descriptor_set, - .dstBinding = 2, - .descriptorCount = static_cast(sampler_descriptors.size()), - .descriptorType = vk::DescriptorType::eSampler, - .pImageInfo = sampler_descriptors.data()}}}; // Binding 2: Sampler array - - get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {}); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + descriptor_sets[i] = vkb::common::allocate_descriptor_set(get_device().get_handle(), descriptor_pool, descriptor_set_layout); + + vk::DescriptorBufferInfo buffer_descriptor{uniform_buffers[i]->get_handle(), 0, vk::WholeSize}; + + vk::DescriptorImageInfo image_descriptor{nullptr, texture.view, vk::ImageLayout::eShaderReadOnlyOptimal}; + + std::array sampler_descriptors = {{{samplers[0], nullptr, vk::ImageLayout::eShaderReadOnlyOptimal}, + {samplers[1], nullptr, vk::ImageLayout::eShaderReadOnlyOptimal}, + {samplers[2], nullptr, vk::ImageLayout::eShaderReadOnlyOptimal}}}; + assert(samplers.size() == sampler_descriptors.size()); + + std::array write_descriptor_sets = {{{.dstSet = descriptor_sets[i], + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &buffer_descriptor}, // Binding 0 : Vertex shader uniform buffer + {.dstSet = descriptor_sets[i], + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampledImage, + .pImageInfo = &image_descriptor}, // Binding 1 : Fragment shader texture sampler + {.dstSet = descriptor_sets[i], + .dstBinding = 2, + .descriptorCount = static_cast(sampler_descriptors.size()), + .descriptorType = vk::DescriptorType::eSampler, + .pImageInfo = sampler_descriptors.data()}}}; // Binding 2: Sampler array + + get_device().get_handle().updateDescriptorSets(write_descriptor_sets, {}); + } } void HPPTextureMipMapGeneration::update_uniform_buffers(float delta_time) @@ -441,12 +415,15 @@ void HPPTextureMipMapGeneration::update_uniform_buffers(float delta_time) ubo.model = camera.matrices.view; ubo.model = glm::rotate(ubo.model, glm::radians(90.0f + timer * 360.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.model = glm::scale(ubo.model, glm::vec3(0.5f)); - timer += delta_time * 0.005f; - if (timer > 1.0f) + if (rotate_scene) { - timer -= 1.0f; + timer += delta_time * 0.005f; + if (timer > 1.0f) + { + timer -= 1.0f; + } } - uniform_buffer->convert_and_update(ubo); + uniform_buffers[current_buffer]->convert_and_update(ubo); } std::unique_ptr create_hpp_texture_mipmap_generation() diff --git a/samples/api/hpp_texture_mipmap_generation/hpp_texture_mipmap_generation.h b/samples/api/hpp_texture_mipmap_generation/hpp_texture_mipmap_generation.h index aef5a5eab..fec35208a 100644 --- a/samples/api/hpp_texture_mipmap_generation/hpp_texture_mipmap_generation.h +++ b/samples/api/hpp_texture_mipmap_generation/hpp_texture_mipmap_generation.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -56,16 +56,14 @@ class HPPTextureMipMapGeneration : public HPPApiVulkanSample void request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) override; // from HPPApiVulkanSample - void build_command_buffers() override; void on_update_ui_overlay(vkb::Drawer &drawer) override; void render(float delta_time) override; - void view_changed() override; void check_format_features(vk::Format) const; vk::DescriptorPool create_descriptor_pool(); vk::DescriptorSetLayout create_descriptor_set_layout(); vk::Pipeline create_pipeline(); - void draw(); + void build_command_buffer(); void load_assets(); void prepare_camera(); void prepare_uniform_buffers(); @@ -73,18 +71,18 @@ class HPPTextureMipMapGeneration : public HPPApiVulkanSample void update_uniform_buffers(float delta_time = 0.0f); private: - vk::DescriptorSet descriptor_set; vk::DescriptorSetLayout descriptor_set_layout; vk::Pipeline pipeline; vk::PipelineLayout pipeline_layout; bool rotate_scene = false; // To demonstrate mip mapping and filtering this example uses separate samplers - std::vector sampler_names{"No mip maps", "Mip maps (bilinear)", "Mip maps (anisotropic)"}; - std::array samplers; - std::unique_ptr scene; - Texture texture; - UBO ubo; - std::unique_ptr uniform_buffer; + std::vector sampler_names{"No mip maps", "Mip maps (bilinear)", "Mip maps (anisotropic)"}; + std::array samplers; + std::unique_ptr scene; + Texture texture; + UBO ubo; + std::array, max_concurrent_frames> uniform_buffers; + std::array descriptor_sets; }; std::unique_ptr create_hpp_texture_mipmap_generation(); diff --git a/samples/api/texture_loading/texture_loading.cpp b/samples/api/texture_loading/texture_loading.cpp index 61b939ff0..37b0df0a5 100644 --- a/samples/api/texture_loading/texture_loading.cpp +++ b/samples/api/texture_loading/texture_loading.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -23,6 +23,8 @@ TextureLoading::TextureLoading() { + use_new_sync = true; + zoom = -2.5f; rotation = {0.0f, 15.0f, 0.0f}; title = "Texture loading"; @@ -45,7 +47,6 @@ TextureLoading::~TextureLoading() vertex_buffer.reset(); index_buffer.reset(); - uniform_buffer_vs.reset(); } // Enable physical device features required for this example @@ -414,8 +415,11 @@ void TextureLoading::destroy_texture(Texture texture) vkFreeMemory(get_device().get_handle(), texture.device_memory, nullptr); } -void TextureLoading::build_command_buffers() +void TextureLoading::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -430,51 +434,32 @@ void TextureLoading::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - // Set target frame buffer - render_pass_begin_info.framebuffer = framebuffers[i]; - - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); - - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, NULL); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - VkDeviceSize offsets[1] = {0}; - vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, vertex_buffer->get(), offsets); - vkCmdBindIndexBuffer(draw_cmd_buffers[i], index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer], 0, NULL); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); - vkCmdDrawIndexed(draw_cmd_buffers[i], index_count, 1, 0, 0, 0); - - draw_ui(draw_cmd_buffers[i]); - - vkCmdEndRenderPass(draw_cmd_buffers[i]); - - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } -} + VkDeviceSize offsets[1] = {0}; + vkCmdBindVertexBuffers(draw_cmd_buffer, 0, 1, vertex_buffer->get(), offsets); + vkCmdBindIndexBuffer(draw_cmd_buffer, index_buffer->get_handle(), 0, VK_INDEX_TYPE_UINT32); -void TextureLoading::draw() -{ - ApiVulkanSample::prepare_frame(); + vkCmdDrawIndexed(draw_cmd_buffer, index_count, 1, 0, 0, 0); - // Command buffer to be submitted to the queue - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; + draw_ui(draw_cmd_buffer); - // Submit to queue - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); + vkCmdEndRenderPass(draw_cmd_buffer); - ApiVulkanSample::submit_frame(); + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } void TextureLoading::generate_quad() @@ -516,14 +501,14 @@ void TextureLoading::setup_descriptor_pool() // Example uses one ubo and one image sampler std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)}; + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, max_concurrent_frames)}; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info( static_cast(pool_sizes.size()), pool_sizes.data(), - 2); + max_concurrent_frames); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } @@ -560,40 +545,43 @@ void TextureLoading::setup_descriptor_set_layout() void TextureLoading::setup_descriptor_set() { - VkDescriptorSetAllocateInfo alloc_info = - vkb::initializers::descriptor_set_allocate_info( - descriptor_pool, - &descriptor_set_layout, - 1); - - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); - - VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffer_vs); - - // Setup a descriptor image info for the current texture to be used as a combined image sampler - VkDescriptorImageInfo image_descriptor; - image_descriptor.imageView = texture.view; // The image's view (images are never directly accessed by the shader, but rather through views defining subresources) - image_descriptor.sampler = texture.sampler; // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.) - image_descriptor.imageLayout = texture.image_layout; // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) - - std::vector write_descriptor_sets = - { - // Binding 0 : Vertex shader uniform buffer - vkb::initializers::write_descriptor_set( - descriptor_set, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &buffer_descriptor), - // Binding 1 : Fragment shader texture sampler - // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; - vkb::initializers::write_descriptor_set( - descriptor_set, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (sampler and image could be split) - 1, // Shader binding point 1 - &image_descriptor) // Pointer to the descriptor image for our texture - }; - - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + VkDescriptorSetAllocateInfo alloc_info = + vkb::initializers::descriptor_set_allocate_info( + descriptor_pool, + &descriptor_set_layout, + 1); + + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets[i])); + + VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffers[i]); + + // Setup a descriptor image info for the current texture to be used as a combined image sampler + VkDescriptorImageInfo image_descriptor; + image_descriptor.imageView = texture.view; // The image's view (images are never directly accessed by the shader, but rather through views defining subresources) + image_descriptor.sampler = texture.sampler; // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.) + image_descriptor.imageLayout = texture.image_layout; // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) + + std::vector write_descriptor_sets = + { + // Binding 0 : Vertex shader uniform buffer + vkb::initializers::write_descriptor_set( + descriptor_sets[i], + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, + &buffer_descriptor), + // Binding 1 : Fragment shader texture sampler + // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; + vkb::initializers::write_descriptor_set( + descriptor_sets[i], + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // The descriptor set will use a combined image sampler (sampler and image could be split) + 1, // Shader binding point 1 + &image_descriptor) // Pointer to the descriptor image for our texture + }; + + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL); + } } void TextureLoading::prepare_pipelines() @@ -690,13 +678,11 @@ void TextureLoading::prepare_pipelines() // Prepare and initialize uniform buffer containing shader uniforms void TextureLoading::prepare_uniform_buffers() { - // Vertex shader uniform buffer block - uniform_buffer_vs = std::make_unique(get_device(), - sizeof(ubo_vs), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + // Vertex shader uniform buffer block + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo_vs), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void TextureLoading::update_uniform_buffers() @@ -712,7 +698,7 @@ void TextureLoading::update_uniform_buffers() ubo_vs.view_pos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f); - uniform_buffer_vs->convert_and_update(ubo_vs); + uniform_buffers[current_buffer]->convert_and_update(ubo_vs); } bool TextureLoading::prepare(const vkb::ApplicationOptions &options) @@ -728,7 +714,6 @@ bool TextureLoading::prepare(const vkb::ApplicationOptions &options) prepare_pipelines(); setup_descriptor_pool(); setup_descriptor_set(); - build_command_buffers(); prepared = true; return true; } @@ -739,22 +724,17 @@ void TextureLoading::render(float delta_time) { return; } - draw(); -} - -void TextureLoading::view_changed() -{ + ApiVulkanSample::prepare_frame(); update_uniform_buffers(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } void TextureLoading::on_update_ui_overlay(vkb::Drawer &drawer) { if (drawer.header("Settings")) { - if (drawer.slider_float("LOD bias", &ubo_vs.lod_bias, 0.0f, static_cast(texture.mip_levels))) - { - update_uniform_buffers(); - } + drawer.slider_float("LOD bias", &ubo_vs.lod_bias, 0.0f, static_cast(texture.mip_levels)); } } diff --git a/samples/api/texture_loading/texture_loading.h b/samples/api/texture_loading/texture_loading.h index ac05ee715..9a31f97ee 100644 --- a/samples/api/texture_loading/texture_loading.h +++ b/samples/api/texture_loading/texture_loading.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -53,8 +53,6 @@ class TextureLoading : public ApiVulkanSample std::unique_ptr index_buffer; uint32_t index_count; - std::unique_ptr uniform_buffer_vs; - struct { glm::mat4 projection; @@ -69,16 +67,17 @@ class TextureLoading : public ApiVulkanSample } pipelines; VkPipelineLayout pipeline_layout; - VkDescriptorSet descriptor_set; VkDescriptorSetLayout descriptor_set_layout; + std::array, max_concurrent_frames> uniform_buffers; + std::array descriptor_sets{}; + TextureLoading(); ~TextureLoading(); virtual void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; void load_texture(); void destroy_texture(Texture texture); - void build_command_buffers() override; - void draw(); + void build_command_buffer(); void generate_quad(); void setup_descriptor_pool(); void setup_descriptor_set_layout(); @@ -88,7 +87,6 @@ class TextureLoading : public ApiVulkanSample void update_uniform_buffers(); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; - virtual void view_changed() override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; }; diff --git a/samples/api/texture_mipmap_generation/texture_mipmap_generation.cpp b/samples/api/texture_mipmap_generation/texture_mipmap_generation.cpp index 139aeb568..33fbd0a74 100644 --- a/samples/api/texture_mipmap_generation/texture_mipmap_generation.cpp +++ b/samples/api/texture_mipmap_generation/texture_mipmap_generation.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -23,6 +23,8 @@ TextureMipMapGeneration::TextureMipMapGeneration() { + use_new_sync = true; + zoom = -2.5f; rotation = {0.0f, 15.0f, 0.0f}; title = "Texture MipMap generation"; @@ -41,7 +43,6 @@ TextureMipMapGeneration::~TextureMipMapGeneration() } } destroy_texture(texture); - uniform_buffer.reset(); } // Enable physical device features required for this example @@ -296,8 +297,11 @@ void TextureMipMapGeneration::load_assets() scene = load_model("scenes/tunnel_cylinder.gltf"); } -void TextureMipMapGeneration::build_command_buffers() +void TextureMipMapGeneration::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -312,46 +316,28 @@ void TextureMipMapGeneration::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - render_pass_begin_info.framebuffer = framebuffers[i]; - - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, NULL); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer], 0, NULL); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - draw_model(scene, draw_cmd_buffers[i]); + draw_model(scene, draw_cmd_buffer); - draw_ui(draw_cmd_buffers[i]); + draw_ui(draw_cmd_buffer); - vkCmdEndRenderPass(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffer); - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } -} - -void TextureMipMapGeneration::draw() -{ - ApiVulkanSample::prepare_frame(); - - // Command buffer to be submitted to the queue - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - - // Submit to queue - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); - - ApiVulkanSample::submit_frame(); + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } void TextureMipMapGeneration::setup_descriptor_pool() @@ -359,16 +345,16 @@ void TextureMipMapGeneration::setup_descriptor_pool() // Example uses one ubo and one image sampler std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1), - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_SAMPLER, 3), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, max_concurrent_frames), + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_SAMPLER, max_concurrent_frames * 3), }; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info( static_cast(pool_sizes.size()), pool_sizes.data(), - 2); + max_concurrent_frames); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } @@ -412,52 +398,55 @@ void TextureMipMapGeneration::setup_descriptor_set_layout() void TextureMipMapGeneration::setup_descriptor_set() { - VkDescriptorSetAllocateInfo alloc_info = - vkb::initializers::descriptor_set_allocate_info( - descriptor_pool, - &descriptor_set_layout, - 1); - - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); - - VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffer); - - VkDescriptorImageInfo image_descriptor; - image_descriptor.imageView = texture.view; - image_descriptor.sampler = VK_NULL_HANDLE; - image_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - std::vector write_descriptor_sets = - { - // Binding 0 : Vertex shader uniform buffer - vkb::initializers::write_descriptor_set( - descriptor_set, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &buffer_descriptor), - // Binding 1 : Fragment shader texture sampler - vkb::initializers::write_descriptor_set( - descriptor_set, - VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - 1, - &image_descriptor)}; - - // Binding 2: Sampler array - std::vector sampler_descriptors; - for (auto i = 0; i < samplers.size(); i++) + for (auto i = 0; i < uniform_buffers.size(); i++) { - sampler_descriptors.push_back({samplers[i], VK_NULL_HANDLE, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}); + VkDescriptorSetAllocateInfo alloc_info = + vkb::initializers::descriptor_set_allocate_info( + descriptor_pool, + &descriptor_set_layout, + 1); + + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets[i])); + + VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*uniform_buffers[i]); + + VkDescriptorImageInfo image_descriptor; + image_descriptor.imageView = texture.view; + image_descriptor.sampler = VK_NULL_HANDLE; + image_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + std::vector write_descriptor_sets = + { + // Binding 0 : Vertex shader uniform buffer + vkb::initializers::write_descriptor_set( + descriptor_sets[i], + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, + &buffer_descriptor), + // Binding 1 : Fragment shader texture sampler + vkb::initializers::write_descriptor_set( + descriptor_sets[i], + VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + 1, + &image_descriptor)}; + + // Binding 2: Sampler array + std::vector sampler_descriptors; + for (auto i = 0; i < samplers.size(); i++) + { + sampler_descriptors.push_back({samplers[i], VK_NULL_HANDLE, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}); + } + VkWriteDescriptorSet write_descriptor_set{}; + write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor_set.dstSet = descriptor_sets[i]; + write_descriptor_set.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + write_descriptor_set.descriptorCount = static_cast(sampler_descriptors.size()); + write_descriptor_set.pImageInfo = sampler_descriptors.data(); + write_descriptor_set.dstBinding = 2; + write_descriptor_set.dstArrayElement = 0; + write_descriptor_sets.push_back(write_descriptor_set); + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); } - VkWriteDescriptorSet write_descriptor_set{}; - write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_descriptor_set.dstSet = descriptor_set; - write_descriptor_set.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - write_descriptor_set.descriptorCount = static_cast(sampler_descriptors.size()); - write_descriptor_set.pImageInfo = sampler_descriptors.data(); - write_descriptor_set.dstBinding = 2; - write_descriptor_set.dstArrayElement = 0; - write_descriptor_sets.push_back(write_descriptor_set); - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); } void TextureMipMapGeneration::prepare_pipelines() @@ -546,13 +535,11 @@ void TextureMipMapGeneration::prepare_pipelines() void TextureMipMapGeneration::prepare_uniform_buffers() { - // Shared parameter uniform buffer block - uniform_buffer = std::make_unique(get_device(), - sizeof(ubo), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + // Shared parameter uniform buffer block + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void TextureMipMapGeneration::update_uniform_buffers(float delta_time) @@ -561,12 +548,15 @@ void TextureMipMapGeneration::update_uniform_buffers(float delta_time) ubo.model = camera.matrices.view; ubo.model = glm::rotate(ubo.model, glm::radians(90.0f + timer * 360.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.model = glm::scale(ubo.model, glm::vec3(0.5f)); - timer += delta_time * 0.005f; - if (timer > 1.0f) + if (rotate_scene) { - timer -= 1.0f; + timer += delta_time * 0.005f; + if (timer > 1.0f) + { + timer -= 1.0f; + } } - uniform_buffer->convert_and_update(ubo); + uniform_buffers[current_buffer]->convert_and_update(ubo); } bool TextureMipMapGeneration::prepare(const vkb::ApplicationOptions &options) @@ -586,7 +576,6 @@ bool TextureMipMapGeneration::prepare(const vkb::ApplicationOptions &options) prepare_pipelines(); setup_descriptor_pool(); setup_descriptor_set(); - build_command_buffers(); prepared = true; return true; @@ -598,31 +587,20 @@ void TextureMipMapGeneration::render(float delta_time) { return; } - draw(); - if (rotate_scene) - { - update_uniform_buffers(delta_time); - } + ApiVulkanSample::prepare_frame(); + update_uniform_buffers(delta_time); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } -void TextureMipMapGeneration::view_changed() -{ - update_uniform_buffers(); -} void TextureMipMapGeneration::on_update_ui_overlay(vkb::Drawer &drawer) { if (drawer.header("Settings")) { drawer.checkbox("Rotate", &rotate_scene); - if (drawer.slider_float("LOD bias", &ubo.lod_bias, 0.0f, static_cast(texture.mip_levels))) - { - update_uniform_buffers(); - } - if (drawer.combo_box("Sampler type", &ubo.sampler_index, sampler_names)) - { - update_uniform_buffers(); - } + drawer.slider_float("LOD bias", &ubo.lod_bias, 0.0f, static_cast(texture.mip_levels)); + drawer.combo_box("Sampler type", &ubo.sampler_index, sampler_names); } } diff --git a/samples/api/texture_mipmap_generation/texture_mipmap_generation.h b/samples/api/texture_mipmap_generation/texture_mipmap_generation.h index 3796a7706..f18274c04 100644 --- a/samples/api/texture_mipmap_generation/texture_mipmap_generation.h +++ b/samples/api/texture_mipmap_generation/texture_mipmap_generation.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2025, Sascha Willems +/* Copyright (c) 2019-2026, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -61,21 +61,21 @@ class TextureMipMapGeneration : public ApiVulkanSample float lod_bias = 0.0f; int32_t sampler_index = 2; } ubo; - std::unique_ptr uniform_buffer; VkPipeline pipeline = VK_NULL_HANDLE; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; - VkDescriptorSet descriptor_set = VK_NULL_HANDLE; VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; + std::array, max_concurrent_frames> uniform_buffers; + std::array descriptor_sets{}; + TextureMipMapGeneration(); ~TextureMipMapGeneration(); virtual void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; void load_texture_generate_mipmaps(std::string file_name); void destroy_texture(Texture texture); void load_assets(); - void build_command_buffers() override; - void draw(); + void build_command_buffer(); void setup_descriptor_pool(); void setup_descriptor_set_layout(); void setup_descriptor_set(); @@ -84,7 +84,6 @@ class TextureMipMapGeneration : public ApiVulkanSample void update_uniform_buffers(float delta_time = 0.0f); bool prepare(const vkb::ApplicationOptions &options) override; virtual void render(float delta_time) override; - virtual void view_changed() override; virtual void on_update_ui_overlay(vkb::Drawer &drawer) override; }; From b3e2987bfa3490ff6a67d67bb8ca40529357a7f5 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sun, 3 May 2026 14:49:02 +0200 Subject: [PATCH 23/23] Update samples to use new sync --- .../hpp_mesh_shading/hpp_mesh_shading.cpp | 52 ++-- .../hpp_mesh_shading/hpp_mesh_shading.h | 3 +- .../mesh_shader_culling.cpp | 243 +++++++----------- .../mesh_shader_culling/mesh_shader_culling.h | 26 +- .../extensions/mesh_shading/mesh_shading.cpp | 61 ++--- .../extensions/mesh_shading/mesh_shading.h | 5 +- 6 files changed, 158 insertions(+), 232 deletions(-) diff --git a/samples/extensions/hpp_mesh_shading/hpp_mesh_shading.cpp b/samples/extensions/hpp_mesh_shading/hpp_mesh_shading.cpp index 6719b44d5..606e96d58 100644 --- a/samples/extensions/hpp_mesh_shading/hpp_mesh_shading.cpp +++ b/samples/extensions/hpp_mesh_shading/hpp_mesh_shading.cpp @@ -24,6 +24,8 @@ HPPMeshShading::HPPMeshShading() { + use_new_sync = true; + title = "Mesh shading"; // VK_EXT_mesh_shader depends on VK_KHR_spirv_1_4, which in turn depends on Vulkan 1.1 and VK_KHR_shader_float_controls @@ -81,45 +83,44 @@ void HPPMeshShading::request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) /* Command buffer generation */ -void HPPMeshShading::build_command_buffers() +void HPPMeshShading::build_command_buffer() { + auto command_buffer = draw_cmd_buffers[current_buffer]; + command_buffer.reset(); + vk::CommandBufferBeginInfo command_buffer_begin_info; std::array clear_values = {{default_clear_color, vk::ClearDepthStencilValue{0.0f, 0}}}; vk::RenderPassBeginInfo render_pass_begin_info{.renderPass = render_pass, + .framebuffer = framebuffers[current_image_index], .renderArea = {{0, 0}, extent}, .clearValueCount = static_cast(clear_values.size()), .pClearValues = clear_values.data()}; - for (size_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - render_pass_begin_info.framebuffer = framebuffers[i]; - - vk::CommandBuffer command_buffer = draw_cmd_buffers[i]; - - command_buffer.begin(command_buffer_begin_info); - command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); - command_buffer.setViewport(0, {{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}}); - command_buffer.setScissor(0, {{{0, 0}, extent}}); - command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptor_set, {}); - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); + command_buffer.begin(command_buffer_begin_info); + command_buffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline); + command_buffer.setViewport(0, {{0.0f, 0.0f, static_cast(extent.width), static_cast(extent.height), 0.0f, 1.0f}}); + command_buffer.setScissor(0, {{{0, 0}, extent}}); + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptor_set, {}); + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - // Mesh shaders need the vkCmdDrawMeshTasksExt - command_buffer.drawMeshTasksEXT(1, 1, 1); + // Mesh shaders need the vkCmdDrawMeshTasksExt + command_buffer.drawMeshTasksEXT(1, 1, 1); - draw_ui(command_buffer); + draw_ui(command_buffer); - command_buffer.endRenderPass(); - command_buffer.end(); - } + command_buffer.endRenderPass(); + command_buffer.end(); } void HPPMeshShading::render(float delta_time) { if (prepared) { - draw(); + HPPApiVulkanSample::prepare_frame(); + build_command_buffer(); + HPPApiVulkanSample::submit_frame(); } } @@ -170,17 +171,6 @@ vk::Pipeline HPPMeshShading::create_pipeline() return pipeline; } -void HPPMeshShading::draw() -{ - HPPApiVulkanSample::prepare_frame(); - - // Submit to queue - submit_info.setCommandBuffers(draw_cmd_buffers[current_buffer]); - queue.submit(submit_info); - - HPPApiVulkanSample::submit_frame(); -} - std::unique_ptr create_hpp_mesh_shading() { return std::make_unique(); diff --git a/samples/extensions/hpp_mesh_shading/hpp_mesh_shading.h b/samples/extensions/hpp_mesh_shading/hpp_mesh_shading.h index d3632840d..5977a3ffd 100644 --- a/samples/extensions/hpp_mesh_shading/hpp_mesh_shading.h +++ b/samples/extensions/hpp_mesh_shading/hpp_mesh_shading.h @@ -38,11 +38,10 @@ class HPPMeshShading : public HPPApiVulkanSample void request_gpu_features(vkb::core::PhysicalDeviceCpp &gpu) override; // from HPPApiVulkanSample - void build_command_buffers() override; void render(float delta_time) override; vk::Pipeline create_pipeline(); - void draw(); + void build_command_buffer(); private: vk::Pipeline pipeline = nullptr; diff --git a/samples/extensions/mesh_shader_culling/mesh_shader_culling.cpp b/samples/extensions/mesh_shader_culling/mesh_shader_culling.cpp index d2a22713c..c1994af03 100644 --- a/samples/extensions/mesh_shader_culling/mesh_shader_culling.cpp +++ b/samples/extensions/mesh_shader_culling/mesh_shader_culling.cpp @@ -22,6 +22,8 @@ MeshShaderCulling::MeshShaderCulling() { + use_new_sync = true; + title = "Mesh shader culling"; // Adding device extensions @@ -38,11 +40,12 @@ MeshShaderCulling::~MeshShaderCulling() vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr); vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout, nullptr); - if (query_pool != VK_NULL_HANDLE) + for (auto &query_pool : query_pools) { - vkDestroyQueryPool(get_device().get_handle(), query_pool, nullptr); - vkDestroyBuffer(get_device().get_handle(), query_result.buffer, nullptr); - vkFreeMemory(get_device().get_handle(), query_result.memory, nullptr); + if (query_pool != VK_NULL_HANDLE) + { + vkDestroyQueryPool(get_device().get_handle(), query_pool, nullptr); + } } } } @@ -62,8 +65,11 @@ void MeshShaderCulling::request_gpu_features(vkb::core::PhysicalDeviceC &gpu) } } -void MeshShaderCulling::build_command_buffers() +void MeshShaderCulling::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -78,67 +84,62 @@ void MeshShaderCulling::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); + if (get_device().get_gpu().get_features().pipelineStatisticsQuery) { - render_pass_begin_info.framebuffer = framebuffers[i]; - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); - if (get_device().get_gpu().get_features().pipelineStatisticsQuery) - { - vkCmdResetQueryPool(draw_cmd_buffers[i], query_pool, 0, 3); - } + vkCmdResetQueryPool(draw_cmd_buffer, query_pools[current_buffer], 0, 3); + } - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets[current_buffer], 0, nullptr); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - // Mesh shaders need the vkCmdDrawMeshTasksExt - uint32_t N = density_level == 0 ? 4 : (density_level == 1 ? 6 : (density_level == 2 ? 8 : 2)); - // dispatch N * N task shader workgroups - uint32_t num_workgroups_x = N; - uint32_t num_workgroups_y = N; - uint32_t num_workgroups_z = 1; + // Mesh shaders need the vkCmdDrawMeshTasksExt + uint32_t N = density_level == 0 ? 4 : (density_level == 1 ? 6 : (density_level == 2 ? 8 : 2)); + // dispatch N * N task shader workgroups + uint32_t num_workgroups_x = N; + uint32_t num_workgroups_y = N; + uint32_t num_workgroups_z = 1; - if (get_device().get_gpu().get_features().pipelineStatisticsQuery) - { - // Begin pipeline statistics query - vkCmdBeginQuery(draw_cmd_buffers[i], query_pool, 0, 0); - } + if (get_device().get_gpu().get_features().pipelineStatisticsQuery) + { + // Begin pipeline statistics query + vkCmdBeginQuery(draw_cmd_buffer, query_pools[current_buffer], 0, 0); + } - vkCmdDrawMeshTasksEXT(draw_cmd_buffers[i], num_workgroups_x, num_workgroups_y, num_workgroups_z); + vkCmdDrawMeshTasksEXT(draw_cmd_buffer, num_workgroups_x, num_workgroups_y, num_workgroups_z); - if (get_device().get_gpu().get_features().pipelineStatisticsQuery) - { - // Begin pipeline statistics query - vkCmdEndQuery(draw_cmd_buffers[i], query_pool, 0); - } + if (get_device().get_gpu().get_features().pipelineStatisticsQuery) + { + // Begin pipeline statistics query + vkCmdEndQuery(draw_cmd_buffer, query_pools[current_buffer], 0); + } - draw_ui(draw_cmd_buffers[i]); + draw_ui(draw_cmd_buffer); - vkCmdEndRenderPass(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffer); - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } void MeshShaderCulling::setup_descriptor_pool() { std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1)}; - - uint32_t number_of_descriptor_sets = 1; + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, max_concurrent_frames)}; VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info(static_cast(pool_sizes.size()), pool_sizes.data(), - number_of_descriptor_sets); + max_concurrent_frames); VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } @@ -163,21 +164,24 @@ void MeshShaderCulling::setup_descriptor_set_layout() void MeshShaderCulling::setup_descriptor_sets() { - VkDescriptorSetAllocateInfo alloc_info = - vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layout, 1); + for (auto i = 0; i < uniform_buffers.size(); i++) + { + VkDescriptorSetAllocateInfo alloc_info = + vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layout, 1); - // Task shader descriptor set - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); + // Task shader descriptor set + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets[i])); - VkDescriptorBufferInfo uniform_buffer_descriptor = create_descriptor(*uniform_buffer); + VkDescriptorBufferInfo uniform_buffer_descriptor = create_descriptor(*uniform_buffers[i]); - std::vector write_descriptor_sets = { - vkb::initializers::write_descriptor_set(descriptor_set, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &uniform_buffer_descriptor)}; + std::vector write_descriptor_sets = { + vkb::initializers::write_descriptor_set(descriptor_sets[i], + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, + &uniform_buffer_descriptor)}; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr); + } } void MeshShaderCulling::prepare_pipelines() @@ -242,34 +246,15 @@ void MeshShaderCulling::prepare_pipelines() void MeshShaderCulling::prepare_uniform_buffers() { - uniform_buffer = std::make_unique(get_device(), - sizeof(ubo_cull), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - update_uniform_buffers(); + for (uint32_t i = 0; i < max_concurrent_frames; i++) + { + uniform_buffers[i] = std::make_unique(get_device(), sizeof(ubo_cull), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + } } void MeshShaderCulling::update_uniform_buffers() { - uniform_buffer->convert_and_update(ubo_cull); -} - -void MeshShaderCulling::draw() -{ - ApiVulkanSample::prepare_frame(); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - - // Submit to queue - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); - - if (get_device().get_gpu().get_features().pipelineStatisticsQuery) - { - // Read query results for displaying in next frame - get_query_results(); - } - - ApiVulkanSample::submit_frame(); + uniform_buffers[current_buffer]->convert_and_update(ubo_cull); } bool MeshShaderCulling::prepare(const vkb::ApplicationOptions &options) @@ -287,7 +272,7 @@ bool MeshShaderCulling::prepare(const vkb::ApplicationOptions &options) if (get_device().get_gpu().get_features().pipelineStatisticsQuery) { - setup_query_result_buffer(); + setup_query_pools(); } prepare_uniform_buffers(); @@ -295,7 +280,6 @@ bool MeshShaderCulling::prepare(const vkb::ApplicationOptions &options) prepare_pipelines(); setup_descriptor_pool(); setup_descriptor_sets(); - build_command_buffers(); prepared = true; return true; @@ -307,14 +291,26 @@ void MeshShaderCulling::render(float delta_time) { return; } - - draw(); + uint32_t active_buffer = current_buffer; + ApiVulkanSample::prepare_frame(); + update_uniform_buffers(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); + // Retrieves the results of the pipeline statistics query submitted to the command buffer + vkGetQueryPoolResults( + get_device().get_handle(), + query_pools[active_buffer], + 0, + 1, + sizeof(pipeline_stats[active_buffer]), + pipeline_stats[active_buffer], + sizeof(uint64_t), + VK_QUERY_RESULT_64_BIT); if (camera.keys.left || camera.keys.right || camera.keys.up || camera.keys.down) { ubo_cull.cull_center_x = -camera.position.x; ubo_cull.cull_center_y = -camera.position.z; - update_uniform_buffers(); } } @@ -322,87 +318,46 @@ void MeshShaderCulling::on_update_ui_overlay(vkb::Drawer &drawer) { if (drawer.header("Use WASD to move the square\n Configurations:\n")) { - if (drawer.slider_float("Cull Radius: ", &ubo_cull.cull_radius, 0.5f, 2.0f)) - { - update_uniform_buffers(); - } + drawer.slider_float("Cull Radius: ", &ubo_cull.cull_radius, 0.5f, 2.0f); if (drawer.combo_box("Meshlet Density Level: ", &density_level, {"4 x 4", "6 x 6", "8 x 8"})) { ubo_cull.meshlet_density = static_cast(density_level); - update_uniform_buffers(); } if (get_device().get_gpu().get_features().pipelineStatisticsQuery) { if (drawer.header("Pipeline statistics")) { - drawer.text("TS invocations: %d", pipeline_stats[1]); - drawer.text("MS invocations: %d", pipeline_stats[2]); - drawer.text("FS invocations: %d", pipeline_stats[0]); + drawer.text("TS invocations: %d", pipeline_stats[current_buffer][1]); + drawer.text("MS invocations: %d", pipeline_stats[current_buffer][2]); + drawer.text("FS invocations: %d", pipeline_stats[current_buffer][0]); } } } } -bool MeshShaderCulling::resize(uint32_t width, uint32_t height) -{ - ApiVulkanSample::resize(width, height); - update_uniform_buffers(); - return true; -} - -std::unique_ptr create_mesh_shader_culling() -{ - return std::make_unique(); -} - // Setup pool and buffer for storing pipeline statistics results -void MeshShaderCulling::setup_query_result_buffer() +void MeshShaderCulling::setup_query_pools() { - uint32_t buffer_size = 2 * sizeof(uint64_t); - - VkMemoryRequirements memory_requirements; - VkMemoryAllocateInfo memory_allocation = vkb::initializers::memory_allocate_info(); - VkBufferCreateInfo buffer_create_info = - vkb::initializers::buffer_create_info( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - buffer_size); - - // Results are saved in a host visible buffer for easy access by the application - VK_CHECK(vkCreateBuffer(get_device().get_handle(), &buffer_create_info, nullptr, &query_result.buffer)); - vkGetBufferMemoryRequirements(get_device().get_handle(), query_result.buffer, &memory_requirements); - memory_allocation.allocationSize = memory_requirements.size; - memory_allocation.memoryTypeIndex = - get_device().get_gpu().get_memory_type(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - VK_CHECK(vkAllocateMemory(get_device().get_handle(), &memory_allocation, nullptr, &query_result.memory)); - VK_CHECK(vkBindBufferMemory(get_device().get_handle(), query_result.buffer, query_result.memory, 0)); - // Create query pool if (get_device().get_gpu().get_features().pipelineStatisticsQuery) { - VkQueryPoolCreateInfo query_pool_info = {}; - query_pool_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; - query_pool_info.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS; - query_pool_info.pipelineStatistics = - VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT | - VK_QUERY_PIPELINE_STATISTIC_TASK_SHADER_INVOCATIONS_BIT_EXT | - VK_QUERY_PIPELINE_STATISTIC_MESH_SHADER_INVOCATIONS_BIT_EXT; - query_pool_info.queryCount = 3; - VK_CHECK(vkCreateQueryPool(get_device().get_handle(), &query_pool_info, nullptr, &query_pool)); + for (auto i = 0; i < query_pools.size(); i++) + { + VkQueryPoolCreateInfo query_pool_info = {}; + query_pool_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + query_pool_info.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS; + query_pool_info.pipelineStatistics = + VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT | + VK_QUERY_PIPELINE_STATISTIC_TASK_SHADER_INVOCATIONS_BIT_EXT | + VK_QUERY_PIPELINE_STATISTIC_MESH_SHADER_INVOCATIONS_BIT_EXT; + query_pool_info.queryCount = 3; + VK_CHECK(vkCreateQueryPool(get_device().get_handle(), &query_pool_info, nullptr, &query_pools[i])); + } } } -// Retrieves the results of the pipeline statistics query submitted to the command buffer -void MeshShaderCulling::get_query_results() +std::unique_ptr create_mesh_shader_culling() { - // We use vkGetQueryResults to copy the results into a host visible buffer - vkGetQueryPoolResults( - get_device().get_handle(), - query_pool, - 0, - 1, - sizeof(pipeline_stats), - pipeline_stats, - sizeof(uint64_t), - VK_QUERY_RESULT_64_BIT); + return std::make_unique(); } diff --git a/samples/extensions/mesh_shader_culling/mesh_shader_culling.h b/samples/extensions/mesh_shader_culling/mesh_shader_culling.h index dd2ee765d..67e8d4d40 100644 --- a/samples/extensions/mesh_shader_culling/mesh_shader_culling.h +++ b/samples/extensions/mesh_shader_culling/mesh_shader_culling.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2023-2025, Holochip Corporation +/* Copyright (c) 2023-2026, Holochip Corporation * * SPDX-License-Identifier: Apache-2.0 * @@ -25,22 +25,17 @@ class MeshShaderCulling : public ApiVulkanSample { private: - int32_t density_level = 2; - std::unique_ptr uniform_buffer{}; + int32_t density_level = 2; VkPipeline pipeline = VK_NULL_HANDLE; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; - VkDescriptorSet descriptor_set = VK_NULL_HANDLE; VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; - // Pipeline statistics - struct - { - VkBuffer buffer; - VkDeviceMemory memory; - } query_result{}; - VkQueryPool query_pool = VK_NULL_HANDLE; - uint64_t pipeline_stats[3] = {0}; + std::array, max_concurrent_frames> uniform_buffers; + std::array descriptor_sets{}; + + uint64_t pipeline_stats[max_concurrent_frames][3] = {0}; + std::array query_pools{}; public: struct UBO @@ -53,20 +48,17 @@ class MeshShaderCulling : public ApiVulkanSample MeshShaderCulling(); ~MeshShaderCulling() override; void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; - void build_command_buffers() override; + void build_command_buffer(); void setup_descriptor_pool(); void setup_descriptor_set_layout(); void setup_descriptor_sets(); void prepare_pipelines(); void prepare_uniform_buffers(); void update_uniform_buffers(); - void draw(); bool prepare(const vkb::ApplicationOptions &options) override; void render(float delta_time) override; void on_update_ui_overlay(vkb::Drawer &drawer) override; - bool resize(uint32_t width, uint32_t height) override; - void setup_query_result_buffer(); - void get_query_results(); + void setup_query_pools(); }; std::unique_ptr create_mesh_shader_culling(); diff --git a/samples/extensions/mesh_shading/mesh_shading.cpp b/samples/extensions/mesh_shading/mesh_shading.cpp index 011341e64..427041e60 100644 --- a/samples/extensions/mesh_shading/mesh_shading.cpp +++ b/samples/extensions/mesh_shading/mesh_shading.cpp @@ -25,6 +25,8 @@ MeshShading::MeshShading() : pipeline(VK_NULL_HANDLE), pipeline_layout(VK_NULL_HANDLE), descriptor_set(VK_NULL_HANDLE), descriptor_set_layout(VK_NULL_HANDLE) { + use_new_sync = true; + title = "Mesh shading"; // vk_mesh_ext requires device properties 2. SPIR-V must also be set to at least 1.4. @@ -53,8 +55,11 @@ void MeshShading::request_gpu_features(vkb::core::PhysicalDeviceC &gpu) /* Command buffer generation */ -void MeshShading::build_command_buffers() +void MeshShading::build_command_buffer() { + VkCommandBuffer draw_cmd_buffer = draw_cmd_buffers[current_buffer]; + vkResetCommandBuffer(draw_cmd_buffer, 0); + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); VkClearValue clear_values[2]; @@ -69,34 +74,31 @@ void MeshShading::build_command_buffers() render_pass_begin_info.renderArea.extent.height = height; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; + render_pass_begin_info.framebuffer = framebuffers[current_image_index]; - for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i) - { - render_pass_begin_info.framebuffer = framebuffers[i]; - VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info)); - vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_buffer_begin_info)); + vkCmdBeginRenderPass(draw_cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport); + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(draw_cmd_buffer, 0, 1, &viewport); - VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); - vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor); + VkRect2D scissor = vkb::initializers::rect2D(static_cast(width), static_cast(height), 0, 0); + vkCmdSetScissor(draw_cmd_buffer, 0, 1, &scissor); - vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr); - vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr); + vkCmdBindPipeline(draw_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - // Mesh shaders need the vkCmdDrawMeshTasksExt - uint32_t num_workgroups_x = 1; - uint32_t num_workgroups_y = 1; - uint32_t num_workgroups_z = 1; - vkCmdDrawMeshTasksEXT(draw_cmd_buffers[i], num_workgroups_x, num_workgroups_y, num_workgroups_z); + // Mesh shaders need the vkCmdDrawMeshTasksExt + uint32_t num_workgroups_x = 1; + uint32_t num_workgroups_y = 1; + uint32_t num_workgroups_z = 1; + vkCmdDrawMeshTasksEXT(draw_cmd_buffer, num_workgroups_x, num_workgroups_y, num_workgroups_z); - draw_ui(draw_cmd_buffers[i]); + draw_ui(draw_cmd_buffer); - vkCmdEndRenderPass(draw_cmd_buffers[i]); + vkCmdEndRenderPass(draw_cmd_buffer); - VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i])); - } + VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer)); } bool MeshShading::prepare(const vkb::ApplicationOptions &options) @@ -107,24 +109,11 @@ bool MeshShading::prepare(const vkb::ApplicationOptions &options) } prepare_pipelines(); - build_command_buffers(); prepared = true; return true; } -void MeshShading::draw() -{ - ApiVulkanSample::prepare_frame(); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - - // Submit to queue - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); - - ApiVulkanSample::submit_frame(); -} - void MeshShading::prepare_pipelines() { VkDescriptorPoolCreateInfo descriptor_pool_create_info = @@ -232,7 +221,9 @@ void MeshShading::render(float delta_time) { return; } - draw(); + ApiVulkanSample::prepare_frame(); + build_command_buffer(); + ApiVulkanSample::submit_frame(); } std::unique_ptr create_mesh_shading() diff --git a/samples/extensions/mesh_shading/mesh_shading.h b/samples/extensions/mesh_shading/mesh_shading.h index 728bb8c8a..ab5745f62 100644 --- a/samples/extensions/mesh_shading/mesh_shading.h +++ b/samples/extensions/mesh_shading/mesh_shading.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2023-2025 Holochip Corporation +/* Copyright (c) 2023-2026 Holochip Corporation * * SPDX-License-Identifier: Apache-2.0 * @@ -38,8 +38,7 @@ class MeshShading : public ApiVulkanSample void request_gpu_features(vkb::core::PhysicalDeviceC &gpu) override; void prepare_pipelines(); - void build_command_buffers() override; - void draw(); + void build_command_buffer(); bool prepare(const vkb::ApplicationOptions &options) override; void render(float delta_time) override; };