diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index a042facaf..7b895f48c 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -49,6 +49,12 @@ bool VulkanCommandProcessor::SetupContext() { const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); VkDevice device = provider.device(); + // No specific reason for 32768, just the "too much" amount from Direct3D 12 + // PIX warnings. + transient_descriptor_pool_uniform_buffers_ = + std::make_unique( + provider, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 32768, 32768); + VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info; descriptor_set_layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; @@ -77,7 +83,7 @@ bool VulkanCommandProcessor::SetupContext() { &descriptor_set_layout_binding_uniform_buffer; if (dfn.vkCreateDescriptorSetLayout( device, &descriptor_set_layout_create_info, nullptr, - &descriptor_set_layout_ub_fetch_bool_loop_constants_) != VK_SUCCESS) { + &descriptor_set_layout_fetch_bool_loop_constants_) != VK_SUCCESS) { XELOGE( "Failed to create a Vulkan descriptor set layout for the fetch, bool " "and loop constants uniform buffer"); @@ -87,7 +93,7 @@ bool VulkanCommandProcessor::SetupContext() { shader_stages_guest_vertex; if (dfn.vkCreateDescriptorSetLayout( device, &descriptor_set_layout_create_info, nullptr, - &descriptor_set_layout_ub_float_constants_vertex_) != VK_SUCCESS) { + &descriptor_set_layout_float_constants_vertex_) != VK_SUCCESS) { XELOGE( "Failed to create a Vulkan descriptor set layout for the vertex shader " "float constants uniform buffer"); @@ -97,7 +103,7 @@ bool VulkanCommandProcessor::SetupContext() { VK_SHADER_STAGE_FRAGMENT_BIT; if (dfn.vkCreateDescriptorSetLayout( device, &descriptor_set_layout_create_info, nullptr, - &descriptor_set_layout_ub_float_constants_pixel_) != VK_SUCCESS) { + &descriptor_set_layout_float_constants_pixel_) != VK_SUCCESS) { XELOGE( "Failed to create a Vulkan descriptor set layout for the pixel shader " "float constants uniform buffer"); @@ -111,7 +117,7 @@ bool VulkanCommandProcessor::SetupContext() { } if (dfn.vkCreateDescriptorSetLayout( device, &descriptor_set_layout_create_info, nullptr, - &descriptor_set_layout_ub_system_constants_) != VK_SUCCESS) { + &descriptor_set_layout_system_constants_) != VK_SUCCESS) { XELOGE( "Failed to create a Vulkan descriptor set layout for the system " "constants uniform buffer"); @@ -257,19 +263,21 @@ void VulkanCommandProcessor::ShutdownContext() { descriptor_set_layout_shared_memory_and_edram_); ui::vulkan::util::DestroyAndNullHandle( dfn.vkDestroyDescriptorSetLayout, device, - descriptor_set_layout_ub_system_constants_); + descriptor_set_layout_system_constants_); ui::vulkan::util::DestroyAndNullHandle( dfn.vkDestroyDescriptorSetLayout, device, - descriptor_set_layout_ub_float_constants_pixel_); + descriptor_set_layout_float_constants_pixel_); ui::vulkan::util::DestroyAndNullHandle( dfn.vkDestroyDescriptorSetLayout, device, - descriptor_set_layout_ub_float_constants_vertex_); + descriptor_set_layout_float_constants_vertex_); ui::vulkan::util::DestroyAndNullHandle( dfn.vkDestroyDescriptorSetLayout, device, - descriptor_set_layout_ub_fetch_bool_loop_constants_); + descriptor_set_layout_fetch_bool_loop_constants_); ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, device, descriptor_set_layout_empty_); + transient_descriptor_pool_uniform_buffers_.reset(); + sparse_bind_wait_stage_mask_ = 0; sparse_buffer_binds_.clear(); sparse_memory_binds_.clear(); @@ -454,15 +462,26 @@ bool VulkanCommandProcessor::GetPipelineLayout( VkDescriptorSetLayout descriptor_set_layouts[SpirvShaderTranslator::kDescriptorSetCount]; - // Fill any unused set layouts with empty layouts. - // TODO(Triang3l): Remove this. - for (size_t i = 0; i < xe::countof(descriptor_set_layouts); ++i) { - descriptor_set_layouts[i] = descriptor_set_layout_empty_; - } + descriptor_set_layouts[SpirvShaderTranslator::kDescriptorSetFetchConstants] = + descriptor_set_layout_fetch_bool_loop_constants_; + descriptor_set_layouts + [SpirvShaderTranslator::kDescriptorSetFloatConstantsVertex] = + descriptor_set_layout_float_constants_vertex_; + descriptor_set_layouts + [SpirvShaderTranslator::kDescriptorSetFloatConstantsPixel] = + descriptor_set_layout_float_constants_pixel_; descriptor_set_layouts[SpirvShaderTranslator::kDescriptorSetTexturesPixel] = descriptor_set_layout_textures_pixel; descriptor_set_layouts[SpirvShaderTranslator::kDescriptorSetTexturesVertex] = descriptor_set_layout_textures_vertex; + descriptor_set_layouts[SpirvShaderTranslator::kDescriptorSetSystemConstants] = + descriptor_set_layout_system_constants_; + descriptor_set_layouts + [SpirvShaderTranslator::kDescriptorSetBoolLoopConstants] = + descriptor_set_layout_fetch_bool_loop_constants_; + descriptor_set_layouts + [SpirvShaderTranslator::kDescriptorSetSharedMemoryAndEdram] = + descriptor_set_layout_shared_memory_and_edram_; VkPipelineLayoutCreateInfo pipeline_layout_create_info; pipeline_layout_create_info.sType = @@ -640,6 +659,9 @@ void VulkanCommandProcessor::CheckSubmissionFence(uint64_t await_submission) { command_buffers_submitted_.pop_front(); } + // Reclaim descriptor pools. + transient_descriptor_pool_uniform_buffers_->Reclaim(submission_completed_); + shared_memory_->CompletedSubmissionUpdated(); } @@ -888,6 +910,9 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) { if (cache_clear_requested_ && AwaitAllQueueOperationsCompletion()) { cache_clear_requested_ = false; + transient_descriptor_pool_uniform_buffers_->ClearCache(); + + assert_true(command_buffers_submitted_.empty()); for (const CommandBuffer& command_buffer : command_buffers_writable_) { dfn.vkDestroyCommandPool(device, command_buffer.pool, nullptr); } diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.h b/src/xenia/gpu/vulkan/vulkan_command_processor.h index 983599d5d..90df3f39b 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.h +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include "xenia/gpu/vulkan/vulkan_shared_memory.h" #include "xenia/gpu/xenos.h" #include "xenia/kernel/kernel_state.h" +#include "xenia/ui/vulkan/transient_descriptor_pool.h" #include "xenia/ui/vulkan/vulkan_context.h" namespace xe { @@ -162,15 +164,18 @@ class VulkanCommandProcessor : public CommandProcessor { std::vector sparse_buffer_bind_infos_temp_; VkPipelineStageFlags sparse_bind_wait_stage_mask_ = 0; + std::unique_ptr + transient_descriptor_pool_uniform_buffers_; + // Descriptor set layouts used by different shaders. VkDescriptorSetLayout descriptor_set_layout_empty_ = VK_NULL_HANDLE; - VkDescriptorSetLayout descriptor_set_layout_ub_fetch_bool_loop_constants_ = + VkDescriptorSetLayout descriptor_set_layout_fetch_bool_loop_constants_ = VK_NULL_HANDLE; - VkDescriptorSetLayout descriptor_set_layout_ub_float_constants_vertex_ = + VkDescriptorSetLayout descriptor_set_layout_float_constants_vertex_ = VK_NULL_HANDLE; - VkDescriptorSetLayout descriptor_set_layout_ub_float_constants_pixel_ = + VkDescriptorSetLayout descriptor_set_layout_float_constants_pixel_ = VK_NULL_HANDLE; - VkDescriptorSetLayout descriptor_set_layout_ub_system_constants_ = + VkDescriptorSetLayout descriptor_set_layout_system_constants_ = VK_NULL_HANDLE; VkDescriptorSetLayout descriptor_set_layout_shared_memory_and_edram_ = VK_NULL_HANDLE; diff --git a/src/xenia/ui/graphics_upload_buffer_pool.cc b/src/xenia/ui/graphics_upload_buffer_pool.cc index 2a780b0c9..5eb04fba3 100644 --- a/src/xenia/ui/graphics_upload_buffer_pool.cc +++ b/src/xenia/ui/graphics_upload_buffer_pool.cc @@ -71,7 +71,7 @@ void GraphicsUploadBufferPool::FlushWrites() { GraphicsUploadBufferPool::Page* GraphicsUploadBufferPool::Request( uint64_t submission_index, size_t size, size_t alignment, size_t& offset_out) { - assert_not_zero(alignment); + alignment = std::max(alignment, size_t(1)); assert_true(xe::is_pow2(alignment)); size = xe::align(size, alignment); assert_true(size <= page_size_); @@ -126,7 +126,7 @@ GraphicsUploadBufferPool::Page* GraphicsUploadBufferPool::Request( GraphicsUploadBufferPool::Page* GraphicsUploadBufferPool::RequestPartial( uint64_t submission_index, size_t size, size_t alignment, size_t& offset_out, size_t& size_out) { - assert_not_zero(alignment); + alignment = std::max(alignment, size_t(1)); assert_true(xe::is_pow2(alignment)); size = xe::align(size, alignment); size = std::min(size, page_size_); diff --git a/src/xenia/ui/vulkan/transient_descriptor_pool.cc b/src/xenia/ui/vulkan/transient_descriptor_pool.cc new file mode 100644 index 000000000..af2c0f424 --- /dev/null +++ b/src/xenia/ui/vulkan/transient_descriptor_pool.cc @@ -0,0 +1,160 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/vulkan/transient_descriptor_pool.h" + +#include + +#include "xenia/base/assert.h" +#include "xenia/base/logging.h" + +namespace xe { +namespace ui { +namespace vulkan { + +TransientDescriptorPool::TransientDescriptorPool( + const VulkanProvider& provider, VkDescriptorType descriptor_type, + uint32_t page_descriptor_set_count, uint32_t page_descriptor_count) + : provider_(provider), + descriptor_type_(descriptor_type), + page_descriptor_set_count_(page_descriptor_set_count), + page_descriptor_count_(page_descriptor_count) { + assert_not_zero(page_descriptor_set_count); + assert_true(page_descriptor_set_count <= page_descriptor_count); +} + +TransientDescriptorPool::~TransientDescriptorPool() { ClearCache(); } + +void TransientDescriptorPool::Reclaim(uint64_t completed_submission_index) { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + while (!pages_submitted_.empty()) { + const auto& descriptor_pool_pair = pages_submitted_.front(); + if (descriptor_pool_pair.second > completed_submission_index) { + break; + } + dfn.vkResetDescriptorPool(device, descriptor_pool_pair.first, 0); + pages_writable_.push_back(descriptor_pool_pair.first); + pages_submitted_.pop_front(); + } +} + +void TransientDescriptorPool::ClearCache() { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + for (const auto& descriptor_pool_pair : pages_submitted_) { + dfn.vkDestroyDescriptorPool(device, descriptor_pool_pair.first, nullptr); + } + pages_submitted_.clear(); + page_current_descriptors_used_ = 0; + page_current_descriptor_sets_used_ = 0; + page_current_last_submission_ = 0; + for (VkDescriptorPool descriptor_pool : pages_writable_) { + dfn.vkDestroyDescriptorPool(device, descriptor_pool, nullptr); + } + pages_writable_.clear(); +} + +VkDescriptorSet TransientDescriptorPool::Request( + uint64_t submission_index, VkDescriptorSetLayout layout, + uint32_t layout_descriptor_count) { + assert_true(submission_index >= page_current_last_submission_); + assert_not_zero(layout_descriptor_count); + assert_true(layout_descriptor_count <= page_descriptor_count_); + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkDescriptorSetAllocateInfo descriptor_set_allocate_info; + descriptor_set_allocate_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descriptor_set_allocate_info.pNext = nullptr; + descriptor_set_allocate_info.descriptorSetCount = 1; + descriptor_set_allocate_info.pSetLayouts = &layout; + VkDescriptorSet descriptor_set; + + // Try to allocate as normal. + if (!pages_writable_.empty()) { + if (page_current_descriptor_sets_used_ < page_descriptor_set_count_ && + page_current_descriptors_used_ + layout_descriptor_count <= + page_descriptor_count_) { + descriptor_set_allocate_info.descriptorPool = pages_writable_.front(); + switch (dfn.vkAllocateDescriptorSets( + device, &descriptor_set_allocate_info, &descriptor_set)) { + case VK_SUCCESS: + page_current_last_submission_ = submission_index; + ++page_current_descriptor_sets_used_; + page_current_descriptors_used_ += layout_descriptor_count; + return descriptor_set; + case VK_ERROR_FRAGMENTED_POOL: + case VK_ERROR_OUT_OF_POOL_MEMORY: + // Need to create a new pool. + break; + default: + XELOGE( + "Failed to allocate a transient Vulkan descriptor set with {} " + "descriptors of type {}", + layout_descriptor_count, uint32_t(descriptor_type_)); + return VK_NULL_HANDLE; + } + } + + // Overflow - go to the next pool. + pages_submitted_.emplace_back(pages_writable_.front(), + page_current_last_submission_); + pages_writable_.front() = pages_writable_.back(); + pages_writable_.pop_back(); + page_current_descriptor_sets_used_ = 0; + page_current_descriptors_used_ = 0; + } + + if (pages_writable_.empty()) { + VkDescriptorPoolSize descriptor_pool_size; + descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_size.descriptorCount = page_descriptor_count_; + VkDescriptorPoolCreateInfo descriptor_pool_create_info; + descriptor_pool_create_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptor_pool_create_info.pNext = nullptr; + descriptor_pool_create_info.flags = 0; + descriptor_pool_create_info.maxSets = page_descriptor_set_count_; + descriptor_pool_create_info.poolSizeCount = 1; + descriptor_pool_create_info.pPoolSizes = &descriptor_pool_size; + VkDescriptorPool descriptor_pool; + if (dfn.vkCreateDescriptorPool(device, &descriptor_pool_create_info, + nullptr, &descriptor_pool) != VK_SUCCESS) { + XELOGE( + "Failed to create a transient Vulkan descriptor pool for {} sets of " + "up to {} descriptors of type {}", + page_descriptor_set_count_, page_descriptor_count_, + uint32_t(descriptor_type_)); + return VK_NULL_HANDLE; + } + pages_writable_.push_back(descriptor_pool); + } + + // Try to allocate after handling overflow. + descriptor_set_allocate_info.descriptorPool = pages_writable_.front(); + if (dfn.vkAllocateDescriptorSets(device, &descriptor_set_allocate_info, + &descriptor_set) != VK_SUCCESS) { + XELOGE( + "Failed to allocate a transient Vulkan descriptor set with {} " + "descriptors of type {}", + layout_descriptor_count, uint32_t(descriptor_type_)); + return VK_NULL_HANDLE; + } + page_current_last_submission_ = submission_index; + ++page_current_descriptor_sets_used_; + page_current_descriptors_used_ += layout_descriptor_count; + return descriptor_set; +} + +} // namespace vulkan +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/vulkan/transient_descriptor_pool.h b/src/xenia/ui/vulkan/transient_descriptor_pool.h new file mode 100644 index 000000000..07760aff0 --- /dev/null +++ b/src/xenia/ui/vulkan/transient_descriptor_pool.h @@ -0,0 +1,61 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VULKAN_TRANSIENT_DESCRIPTOR_POOL_H_ +#define XENIA_UI_VULKAN_TRANSIENT_DESCRIPTOR_POOL_H_ + +#include +#include +#include +#include + +#include "xenia/ui/vulkan/vulkan_provider.h" + +namespace xe { +namespace ui { +namespace vulkan { + +// A pool of descriptor pools for single-submission use. For simplicity of +// tracking when overflow happens, only allocating descriptors for sets +// containing descriptors of a single type. +class TransientDescriptorPool { + public: + TransientDescriptorPool(const VulkanProvider& provider, + VkDescriptorType descriptor_type, + uint32_t page_descriptor_set_count, + uint32_t page_descriptor_count); + ~TransientDescriptorPool(); + + void Reclaim(uint64_t completed_submission_index); + void ClearCache(); + + // Returns the allocated set, or VK_NULL_HANDLE if failed to allocate. + VkDescriptorSet Request(uint64_t submission_index, + VkDescriptorSetLayout layout, + uint32_t layout_descriptor_count); + + private: + const VulkanProvider& provider_; + + VkDescriptorType descriptor_type_; + uint32_t page_descriptor_set_count_; + uint32_t page_descriptor_count_; + + std::vector pages_writable_; + uint64_t page_current_last_submission_ = 0; + uint32_t page_current_descriptor_sets_used_ = 0; + uint32_t page_current_descriptors_used_ = 0; + std::deque> pages_submitted_; +}; + +} // namespace vulkan +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_VULKAN_TRANSIENT_DESCRIPTOR_POOL_H_ diff --git a/src/xenia/ui/vulkan/vulkan_provider.cc b/src/xenia/ui/vulkan/vulkan_provider.cc index 6f0e1e707..ba43f43d0 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.cc +++ b/src/xenia/ui/vulkan/vulkan_provider.cc @@ -615,6 +615,7 @@ bool VulkanProvider::Initialize() { XE_VULKAN_LOAD_DFN(vkGetSwapchainImagesKHR); XE_VULKAN_LOAD_DFN(vkMapMemory); XE_VULKAN_LOAD_DFN(vkResetCommandPool); + XE_VULKAN_LOAD_DFN(vkResetDescriptorPool); XE_VULKAN_LOAD_DFN(vkResetFences); XE_VULKAN_LOAD_DFN(vkQueueBindSparse); XE_VULKAN_LOAD_DFN(vkQueuePresentKHR); diff --git a/src/xenia/ui/vulkan/vulkan_provider.h b/src/xenia/ui/vulkan/vulkan_provider.h index 1345dea61..31753472c 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.h +++ b/src/xenia/ui/vulkan/vulkan_provider.h @@ -193,6 +193,7 @@ class VulkanProvider : public GraphicsProvider { PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; PFN_vkMapMemory vkMapMemory; PFN_vkResetCommandPool vkResetCommandPool; + PFN_vkResetDescriptorPool vkResetDescriptorPool; PFN_vkResetFences vkResetFences; PFN_vkQueueBindSparse vkQueueBindSparse; PFN_vkQueuePresentKHR vkQueuePresentKHR;