From 99090e0a22bde23d38050d6aa692d68cc39574f7 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Mon, 1 Aug 2016 16:30:28 -0500 Subject: [PATCH] CircularBuffer: Allow users to bind their own memory to our buffer. --- src/xenia/gpu/vulkan/buffer_cache.cc | 26 +++++++---- src/xenia/gpu/vulkan/buffer_cache.h | 5 ++ src/xenia/gpu/vulkan/texture_cache.cc | 6 +-- src/xenia/ui/vulkan/circular_buffer.cc | 65 +++++++++++++++++++++----- src/xenia/ui/vulkan/circular_buffer.h | 11 +++-- 5 files changed, 86 insertions(+), 27 deletions(-) diff --git a/src/xenia/gpu/vulkan/buffer_cache.cc b/src/xenia/gpu/vulkan/buffer_cache.cc index 02bd88a83..393abcb65 100644 --- a/src/xenia/gpu/vulkan/buffer_cache.cc +++ b/src/xenia/gpu/vulkan/buffer_cache.cc @@ -28,11 +28,17 @@ constexpr VkDeviceSize kConstantRegisterUniformRange = BufferCache::BufferCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device, size_t capacity) : register_file_(register_file), device_(*device) { - transient_buffer_ = std::make_unique(device); - if (!transient_buffer_->Initialize(capacity, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)) { + transient_buffer_ = std::make_unique( + device, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + capacity); + + VkMemoryRequirements pool_reqs; + transient_buffer_->GetBufferMemoryRequirements(&pool_reqs); + gpu_memory_pool_ = device->AllocateMemory(pool_reqs); + + if (!transient_buffer_->Initialize(gpu_memory_pool_, 0)) { assert_always(); } @@ -134,6 +140,10 @@ BufferCache::~BufferCache() { vkDestroyDescriptorSetLayout(device_, descriptor_set_layout_, nullptr); vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr); transient_buffer_->Shutdown(); + + if (gpu_memory_pool_) { + vkFreeMemory(device_, gpu_memory_pool_, nullptr); + } } std::pair BufferCache::UploadConstantRegisters( @@ -221,8 +231,6 @@ std::pair BufferCache::UploadConstantRegisters( std::pair BufferCache::UploadIndexBuffer( const void* source_ptr, size_t source_length, IndexFormat format, std::shared_ptr fence) { - // TODO(benvanik): check cache. - // Allocate space in the buffer for our data. auto offset = AllocateTransientData(source_length, fence); if (offset == VK_WHOLE_SIZE) { @@ -249,8 +257,6 @@ std::pair BufferCache::UploadIndexBuffer( std::pair BufferCache::UploadVertexBuffer( const void* source_ptr, size_t source_length, Endian endian, std::shared_ptr fence) { - // TODO(benvanik): check cache. - // Allocate space in the buffer for our data. auto offset = AllocateTransientData(source_length, fence); if (offset == VK_WHOLE_SIZE) { @@ -324,7 +330,7 @@ void BufferCache::InvalidateCache() { } void BufferCache::ClearCache() { - // TODO(benvanik): caching. + transient_cache_.clear(); } void BufferCache::Scavenge() { transient_buffer_->Scavenge(); } diff --git a/src/xenia/gpu/vulkan/buffer_cache.h b/src/xenia/gpu/vulkan/buffer_cache.h index 8695fc36d..2fda48720 100644 --- a/src/xenia/gpu/vulkan/buffer_cache.h +++ b/src/xenia/gpu/vulkan/buffer_cache.h @@ -17,6 +17,8 @@ #include "xenia/ui/vulkan/vulkan.h" #include "xenia/ui/vulkan/vulkan_device.h" +#include + namespace xe { namespace gpu { namespace vulkan { @@ -101,9 +103,12 @@ class BufferCache { RegisterFile* register_file_ = nullptr; VkDevice device_ = nullptr; + VkDeviceMemory gpu_memory_pool_ = nullptr; + // Staging ringbuffer we cycle through fast. Used for data we don't // plan on keeping past the current frame. std::unique_ptr transient_buffer_ = nullptr; + std::unordered_map transient_cache_; VkDescriptorPool descriptor_pool_ = nullptr; VkDescriptorSetLayout descriptor_set_layout_ = nullptr; diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index b95f3a1ce..08d267b95 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -111,7 +111,8 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file, register_file_(register_file), trace_writer_(trace_writer), device_(device), - staging_buffer_(device) { + staging_buffer_(device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + kStagingBufferSize) { // Descriptor pool used for all of our cached descriptors. VkDescriptorPoolCreateInfo descriptor_pool_info; descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; @@ -152,8 +153,7 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file, nullptr, &texture_descriptor_set_layout_); CheckResult(err, "vkCreateDescriptorSetLayout"); - if (!staging_buffer_.Initialize(kStagingBufferSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) { + if (!staging_buffer_.Initialize()) { assert_always(); } diff --git a/src/xenia/ui/vulkan/circular_buffer.cc b/src/xenia/ui/vulkan/circular_buffer.cc index 94d2996ce..5767b7f85 100644 --- a/src/xenia/ui/vulkan/circular_buffer.cc +++ b/src/xenia/ui/vulkan/circular_buffer.cc @@ -19,13 +19,10 @@ namespace xe { namespace ui { namespace vulkan { -CircularBuffer::CircularBuffer(VulkanDevice* device) : device_(device) {} -CircularBuffer::~CircularBuffer() { Shutdown(); } - -bool CircularBuffer::Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage, - VkDeviceSize alignment) { +CircularBuffer::CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage, + VkDeviceSize capacity, VkDeviceSize alignment) + : device_(device), capacity_(capacity) { VkResult status = VK_SUCCESS; - capacity = xe::round_up(capacity, alignment); // Create our internal buffer. VkBufferCreateInfo buffer_info; @@ -40,15 +37,52 @@ bool CircularBuffer::Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage, status = vkCreateBuffer(*device_, &buffer_info, nullptr, &gpu_buffer_); CheckResult(status, "vkCreateBuffer"); if (status != VK_SUCCESS) { + assert_always(); + } + + VkMemoryRequirements reqs; + vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs); + alignment_ = reqs.alignment; +} +CircularBuffer::~CircularBuffer() { Shutdown(); } + +bool CircularBuffer::Initialize(VkDeviceMemory memory, VkDeviceSize offset) { + assert_true(offset % alignment_ == 0); + gpu_memory_ = memory; + gpu_base_ = offset; + + VkResult status = VK_SUCCESS; + + // Bind the buffer to its backing memory. + status = vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_); + CheckResult(status, "vkBindBufferMemory"); + if (status != VK_SUCCESS) { + XELOGE("CircularBuffer::Initialize - Failed to bind memory!"); + Shutdown(); return false; } + // Map the memory so we can access it. + status = vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0, + reinterpret_cast(&host_base_)); + CheckResult(status, "vkMapMemory"); + if (status != VK_SUCCESS) { + XELOGE("CircularBuffer::Initialize - Failed to map memory!"); + Shutdown(); + return false; + } + + return true; +} + +bool CircularBuffer::Initialize() { + VkResult status = VK_SUCCESS; + VkMemoryRequirements reqs; vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs); // Allocate memory from the device to back the buffer. - assert_true(reqs.size == capacity); - reqs.alignment = std::max(alignment, reqs.alignment); + owns_gpu_memory_ = true; gpu_memory_ = device_->AllocateMemory(reqs); if (!gpu_memory_) { XELOGE("CircularBuffer::Initialize - Failed to allocate memory!"); @@ -56,7 +90,6 @@ bool CircularBuffer::Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage, return false; } - alignment_ = reqs.alignment; capacity_ = reqs.size; gpu_base_ = 0; @@ -92,12 +125,16 @@ void CircularBuffer::Shutdown() { vkDestroyBuffer(*device_, gpu_buffer_, nullptr); gpu_buffer_ = nullptr; } - if (gpu_memory_) { + if (gpu_memory_ && owns_gpu_memory_) { vkFreeMemory(*device_, gpu_memory_, nullptr); gpu_memory_ = nullptr; } } +void CircularBuffer::GetBufferMemoryRequirements(VkMemoryRequirements* reqs) { + vkGetBufferMemoryRequirements(*device_, gpu_buffer_, reqs); +} + bool CircularBuffer::CanAcquire(VkDeviceSize length) { // Make sure the length is aligned. length = xe::round_up(length, alignment_); @@ -166,7 +203,8 @@ CircularBuffer::Allocation* CircularBuffer::Acquire( return alloc; } else if ((read_head_ - 0) >= aligned_length) { - // Free space from begin -> read + // Not enough space from write -> capacity, but there is enough free space + // from begin -> read auto alloc = new Allocation(); alloc->host_ptr = host_base_ + 0; alloc->gpu_memory = gpu_memory_; @@ -220,6 +258,11 @@ void CircularBuffer::Scavenge() { delete *it; it = allocations_.erase(it); } + + if (allocations_.empty()) { + // Reset R/W heads to work around fragmentation issues. + read_head_ = write_head_ = 0; + } } } // namespace vulkan diff --git a/src/xenia/ui/vulkan/circular_buffer.h b/src/xenia/ui/vulkan/circular_buffer.h index 54aa916fd..42338c98e 100644 --- a/src/xenia/ui/vulkan/circular_buffer.h +++ b/src/xenia/ui/vulkan/circular_buffer.h @@ -27,7 +27,8 @@ namespace vulkan { // ends of the buffer), where trailing older allocations are freed after use. class CircularBuffer { public: - CircularBuffer(VulkanDevice* device); + CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage, + VkDeviceSize capacity, VkDeviceSize alignment = 256); ~CircularBuffer(); struct Allocation { @@ -42,10 +43,12 @@ class CircularBuffer { std::shared_ptr fence; }; - bool Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage, - VkDeviceSize alignment = 256); + bool Initialize(VkDeviceMemory memory, VkDeviceSize offset); + bool Initialize(); void Shutdown(); + void GetBufferMemoryRequirements(VkMemoryRequirements* reqs); + VkDeviceSize alignment() const { return alignment_; } VkDeviceSize capacity() const { return capacity_; } VkBuffer gpu_buffer() const { return gpu_buffer_; } @@ -66,12 +69,14 @@ class CircularBuffer { void Scavenge(); private: + // All of these variables are relative to gpu_base VkDeviceSize capacity_ = 0; VkDeviceSize alignment_ = 0; VkDeviceSize write_head_ = 0; VkDeviceSize read_head_ = 0; VulkanDevice* device_; + bool owns_gpu_memory_ = false; VkBuffer gpu_buffer_ = nullptr; VkDeviceMemory gpu_memory_ = nullptr; VkDeviceSize gpu_base_ = 0;