From 3ce7962d4c71e7c73ed6be4a350ea351242a8c25 Mon Sep 17 00:00:00 2001 From: Draklaw Date: Mon, 7 Mar 2022 18:15:35 +0100 Subject: [PATCH] Use indirect draw call. --- src/VulkanTutorial.cpp | 42 +++++++++++++++++++++++++++++++++++++----- src/VulkanTutorial.h | 8 ++++++++ src/vk/Context.cpp | 36 +++++++++++++++++++++++++++++------- src/vk/Context.h | 5 ++++- src/vk/Memory.cpp | 6 +++--- src/vk/Memory.h | 6 ++++-- 6 files changed, 85 insertions(+), 18 deletions(-) diff --git a/src/VulkanTutorial.cpp b/src/VulkanTutorial.cpp index ad348b4..faa278e 100644 --- a/src/VulkanTutorial.cpp +++ b/src/VulkanTutorial.cpp @@ -241,6 +241,25 @@ void VulkanTutorial::draw_frame() { std::memcpy(uniform_buffer, &uniforms, sizeof(Uniforms)); m_uniform_buffer_memory.unmap(m_context); + std::vector draw_commands { + { + .indexCount = uint32_t(indices.size()), + .instanceCount = 1, + }, + }; + Byte* draw_buffer = image_states.m_draw_buffer.memory().map( + m_context, + 0, + DrawBufferOffset + 1 * sizeof(VkDrawIndexedIndirectCommand) + ); + *reinterpret_cast(draw_buffer) = uint32_t(draw_commands.size()); + std::memcpy( + draw_buffer + DrawBufferOffset, + draw_commands.data(), + draw_commands.size() * sizeof(VkDrawIndexedIndirectCommand) + ); + image_states.m_draw_buffer.memory().unmap(m_context); + VkSemaphore wait_semaphores[] = { m_swapchain.ready_to_render(), }; @@ -559,6 +578,8 @@ void VulkanTutorial::initialize_image_states(size_t image_index) { create_uniform_buffer(image_states); create_descriptor_set(image_states); + create_draw_buffer(image_states); + create_command_buffer(image_states); image_states.m_render_done = vk::Semaphore(m_context); @@ -636,6 +657,16 @@ void VulkanTutorial::create_descriptor_set(ImageStates& image_states) { ); } +void VulkanTutorial::create_draw_buffer(ImageStates& image_states) { + image_states.m_draw_buffer = vk::Buffer( + m_context, + DrawBufferOffset + MaxDrawTileCount * sizeof(VkDrawIndexedIndirectCommand), + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + ); +} + void VulkanTutorial::create_command_buffer(ImageStates& image_states) { image_states.m_command_buffer = vk::CommandBuffer(m_context, m_command_pool); @@ -716,13 +747,14 @@ void VulkanTutorial::create_command_buffer(ImageStates& image_states) { 0, nullptr ); - vkCmdDrawIndexed( + m_context.cmdDrawIndexedIndirectCount( image_states.m_command_buffer, - uint32_t(indices.size()), - 1, - 0, + image_states.m_draw_buffer, + DrawBufferOffset, + image_states.m_draw_buffer, 0, - 0 + MaxDrawTileCount, + sizeof(VkDrawIndexedIndirectCommand) ); vkCmdEndRenderPass(image_states.m_command_buffer); diff --git a/src/VulkanTutorial.h b/src/VulkanTutorial.h index 091581f..fb826c3 100644 --- a/src/VulkanTutorial.h +++ b/src/VulkanTutorial.h @@ -84,8 +84,14 @@ private: void create_uniform_buffer(ImageStates& image_states); void create_descriptor_set(ImageStates& image_states); + void create_draw_buffer(ImageStates& image_states); + void create_command_buffer(ImageStates& image_states); +private: + static constexpr uint32_t DrawBufferOffset = 4; + static constexpr uint32_t MaxDrawTileCount = 256; + private: vk::Context m_context; @@ -117,6 +123,8 @@ private: VkDeviceSize m_uniform_buffer_offset; vk::DescriptorSet m_descriptor_set; + vk::Buffer m_draw_buffer; + vk::CommandBuffer m_command_buffer; vk::Semaphore m_render_done; }; diff --git a/src/vk/Context.cpp b/src/vk/Context.cpp index 2600011..218a532 100644 --- a/src/vk/Context.cpp +++ b/src/vk/Context.cpp @@ -562,7 +562,7 @@ void Context::create_instance(const ContextSettings& settings) { if(vkCreateInstance(&instance_info, nullptr, &m_instance) != VK_SUCCESS) throw std::runtime_error("failed to create vulkan instance"); - initialize_extension_functions(); + initialize_instance_extension_functions(); if(settings.debug()) { createDebugUtilsMessenger( @@ -733,7 +733,9 @@ void Context::create_device(const ContextSettings& settings) { .geometryShader = true, }; - std::vector extensions; + std::vector extensions { + VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME, + }; if(m_window) { extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); }; @@ -756,6 +758,8 @@ void Context::create_device(const ContextSettings& settings) { ) != VK_SUCCESS) throw std::runtime_error("failed to create logical device"); + initialize_device_extension_functions(); + m_queues.resize(m_queue_families.size()); for(size_t index = 0; index < m_queue_families.size(); ++index) { vkGetDeviceQueue( @@ -777,10 +781,10 @@ void Context::create_internal_objects() { m_transfer_fence = Fence(*this); } -void Context::initialize_extension_functions() { +void Context::initialize_instance_extension_functions() { uint32_t errors_count = 0; - #define GET_PROC_ADDR(ptr_name, func_name) \ + #define GET_INSTANCE_PROC_ADDR(ptr_name, func_name) \ ptr_name = (PFN_vk ## func_name)vkGetInstanceProcAddr( \ m_instance, "vk" #func_name); \ if(ptr_name == nullptr) \ @@ -789,9 +793,27 @@ void Context::initialize_extension_functions() { errors_count += 1; \ } - GET_PROC_ADDR(createDebugUtilsMessenger, CreateDebugUtilsMessengerEXT) - GET_PROC_ADDR(destroyDebugUtilsMessenger, DestroyDebugUtilsMessengerEXT) - GET_PROC_ADDR(setDebugUtilsObjectName, SetDebugUtilsObjectNameEXT) + GET_INSTANCE_PROC_ADDR(createDebugUtilsMessenger, CreateDebugUtilsMessengerEXT) + GET_INSTANCE_PROC_ADDR(destroyDebugUtilsMessenger, DestroyDebugUtilsMessengerEXT) + GET_INSTANCE_PROC_ADDR(setDebugUtilsObjectName, SetDebugUtilsObjectNameEXT) + + if(errors_count != 0) + throw std::runtime_error("failed to load extensions"); +} + +void Context::initialize_device_extension_functions() { + uint32_t errors_count = 0; + + #define GET_DEVICE_PROC_ADDR(ptr_name, func_name) \ + ptr_name = (PFN_vk ## func_name)vkGetDeviceProcAddr( \ + m_device, "vk" #func_name); \ + if(ptr_name == nullptr) \ + { \ + logger.error() << "failed to load extension function 'vk" #func_name "'"; \ + errors_count += 1; \ + } + + GET_DEVICE_PROC_ADDR(cmdDrawIndexedIndirectCount, CmdDrawIndexedIndirectCountKHR) if(errors_count != 0) throw std::runtime_error("failed to load extensions"); diff --git a/src/vk/Context.h b/src/vk/Context.h index 2fe4110..da594f6 100644 --- a/src/vk/Context.h +++ b/src/vk/Context.h @@ -154,6 +154,8 @@ public: PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessenger = nullptr; PFN_vkSetDebugUtilsObjectNameEXT setDebugUtilsObjectName = nullptr; + PFN_vkCmdDrawIndexedIndirectCountKHR cmdDrawIndexedIndirectCount = nullptr; + private: void create_instance(const ContextSettings& settings); void create_surface(const ContextSettings& settings); @@ -165,7 +167,8 @@ private: void create_device(const ContextSettings& settings); void create_internal_objects(); - void initialize_extension_functions(); + void initialize_instance_extension_functions(); + void initialize_device_extension_functions(); static VKAPI_ATTR VkBool32 VKAPI_CALL log_debug_message( VkDebugUtilsMessageSeverityFlagBitsEXT severity, diff --git a/src/vk/Memory.cpp b/src/vk/Memory.cpp index 8fd02d2..9e59927 100644 --- a/src/vk/Memory.cpp +++ b/src/vk/Memory.cpp @@ -80,11 +80,11 @@ void MemoryBlock::free() noexcept { } -void* MemoryBlock::map(Context& context) { +Byte* MemoryBlock::map(Context& context) { return map(context, 0, m_size); } -void* MemoryBlock::map(Context& context, VkDeviceSize offset, VkDeviceSize size) { +Byte* MemoryBlock::map(Context& context, VkDeviceSize offset, VkDeviceSize size) { assert(!is_null()); assert(context); assert(offset + size <= m_size); @@ -100,7 +100,7 @@ void* MemoryBlock::map(Context& context, VkDeviceSize offset, VkDeviceSize size) ) != VK_SUCCESS) throw std::runtime_error("failed to map memory"); - return ptr; + return reinterpret_cast(ptr); } void MemoryBlock::unmap(Context& context) noexcept { diff --git a/src/vk/Memory.h b/src/vk/Memory.h index e7f93d7..621344c 100644 --- a/src/vk/Memory.h +++ b/src/vk/Memory.h @@ -1,6 +1,8 @@ // Copyright 2022 Simon Boyé #pragma once +#include + #include #include @@ -61,8 +63,8 @@ public: void free() noexcept; - void* map(Context& context); - void* map(Context& context, VkDeviceSize offset, VkDeviceSize size); + Byte* map(Context& context); + Byte* map(Context& context, VkDeviceSize offset, VkDeviceSize size); void unmap(Context& context) noexcept; void flush(Context& context);