CircularBuffer: Allow users to bind their own memory to our buffer.
This commit is contained in:
parent
22794902f3
commit
99090e0a22
|
@ -28,11 +28,17 @@ constexpr VkDeviceSize kConstantRegisterUniformRange =
|
||||||
BufferCache::BufferCache(RegisterFile* register_file,
|
BufferCache::BufferCache(RegisterFile* register_file,
|
||||||
ui::vulkan::VulkanDevice* device, size_t capacity)
|
ui::vulkan::VulkanDevice* device, size_t capacity)
|
||||||
: register_file_(register_file), device_(*device) {
|
: register_file_(register_file), device_(*device) {
|
||||||
transient_buffer_ = std::make_unique<ui::vulkan::CircularBuffer>(device);
|
transient_buffer_ = std::make_unique<ui::vulkan::CircularBuffer>(
|
||||||
if (!transient_buffer_->Initialize(capacity,
|
device,
|
||||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
|
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
||||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
VK_BUFFER_USAGE_VERTEX_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();
|
assert_always();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +140,10 @@ BufferCache::~BufferCache() {
|
||||||
vkDestroyDescriptorSetLayout(device_, descriptor_set_layout_, nullptr);
|
vkDestroyDescriptorSetLayout(device_, descriptor_set_layout_, nullptr);
|
||||||
vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr);
|
vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr);
|
||||||
transient_buffer_->Shutdown();
|
transient_buffer_->Shutdown();
|
||||||
|
|
||||||
|
if (gpu_memory_pool_) {
|
||||||
|
vkFreeMemory(device_, gpu_memory_pool_, nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<VkDeviceSize, VkDeviceSize> BufferCache::UploadConstantRegisters(
|
std::pair<VkDeviceSize, VkDeviceSize> BufferCache::UploadConstantRegisters(
|
||||||
|
@ -221,8 +231,6 @@ std::pair<VkDeviceSize, VkDeviceSize> BufferCache::UploadConstantRegisters(
|
||||||
std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadIndexBuffer(
|
std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadIndexBuffer(
|
||||||
const void* source_ptr, size_t source_length, IndexFormat format,
|
const void* source_ptr, size_t source_length, IndexFormat format,
|
||||||
std::shared_ptr<ui::vulkan::Fence> fence) {
|
std::shared_ptr<ui::vulkan::Fence> fence) {
|
||||||
// TODO(benvanik): check cache.
|
|
||||||
|
|
||||||
// Allocate space in the buffer for our data.
|
// Allocate space in the buffer for our data.
|
||||||
auto offset = AllocateTransientData(source_length, fence);
|
auto offset = AllocateTransientData(source_length, fence);
|
||||||
if (offset == VK_WHOLE_SIZE) {
|
if (offset == VK_WHOLE_SIZE) {
|
||||||
|
@ -249,8 +257,6 @@ std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadIndexBuffer(
|
||||||
std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadVertexBuffer(
|
std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadVertexBuffer(
|
||||||
const void* source_ptr, size_t source_length, Endian endian,
|
const void* source_ptr, size_t source_length, Endian endian,
|
||||||
std::shared_ptr<ui::vulkan::Fence> fence) {
|
std::shared_ptr<ui::vulkan::Fence> fence) {
|
||||||
// TODO(benvanik): check cache.
|
|
||||||
|
|
||||||
// Allocate space in the buffer for our data.
|
// Allocate space in the buffer for our data.
|
||||||
auto offset = AllocateTransientData(source_length, fence);
|
auto offset = AllocateTransientData(source_length, fence);
|
||||||
if (offset == VK_WHOLE_SIZE) {
|
if (offset == VK_WHOLE_SIZE) {
|
||||||
|
@ -324,7 +330,7 @@ void BufferCache::InvalidateCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCache::ClearCache() {
|
void BufferCache::ClearCache() {
|
||||||
// TODO(benvanik): caching.
|
transient_cache_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCache::Scavenge() { transient_buffer_->Scavenge(); }
|
void BufferCache::Scavenge() { transient_buffer_->Scavenge(); }
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include "xenia/ui/vulkan/vulkan.h"
|
#include "xenia/ui/vulkan/vulkan.h"
|
||||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
namespace vulkan {
|
namespace vulkan {
|
||||||
|
@ -101,9 +103,12 @@ class BufferCache {
|
||||||
RegisterFile* register_file_ = nullptr;
|
RegisterFile* register_file_ = nullptr;
|
||||||
VkDevice device_ = nullptr;
|
VkDevice device_ = nullptr;
|
||||||
|
|
||||||
|
VkDeviceMemory gpu_memory_pool_ = nullptr;
|
||||||
|
|
||||||
// Staging ringbuffer we cycle through fast. Used for data we don't
|
// Staging ringbuffer we cycle through fast. Used for data we don't
|
||||||
// plan on keeping past the current frame.
|
// plan on keeping past the current frame.
|
||||||
std::unique_ptr<ui::vulkan::CircularBuffer> transient_buffer_ = nullptr;
|
std::unique_ptr<ui::vulkan::CircularBuffer> transient_buffer_ = nullptr;
|
||||||
|
std::unordered_map<uint64_t, VkDeviceSize> transient_cache_;
|
||||||
|
|
||||||
VkDescriptorPool descriptor_pool_ = nullptr;
|
VkDescriptorPool descriptor_pool_ = nullptr;
|
||||||
VkDescriptorSetLayout descriptor_set_layout_ = nullptr;
|
VkDescriptorSetLayout descriptor_set_layout_ = nullptr;
|
||||||
|
|
|
@ -111,7 +111,8 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file,
|
||||||
register_file_(register_file),
|
register_file_(register_file),
|
||||||
trace_writer_(trace_writer),
|
trace_writer_(trace_writer),
|
||||||
device_(device),
|
device_(device),
|
||||||
staging_buffer_(device) {
|
staging_buffer_(device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
|
kStagingBufferSize) {
|
||||||
// Descriptor pool used for all of our cached descriptors.
|
// Descriptor pool used for all of our cached descriptors.
|
||||||
VkDescriptorPoolCreateInfo descriptor_pool_info;
|
VkDescriptorPoolCreateInfo descriptor_pool_info;
|
||||||
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_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_);
|
nullptr, &texture_descriptor_set_layout_);
|
||||||
CheckResult(err, "vkCreateDescriptorSetLayout");
|
CheckResult(err, "vkCreateDescriptorSetLayout");
|
||||||
|
|
||||||
if (!staging_buffer_.Initialize(kStagingBufferSize,
|
if (!staging_buffer_.Initialize()) {
|
||||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) {
|
|
||||||
assert_always();
|
assert_always();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,10 @@ namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace vulkan {
|
namespace vulkan {
|
||||||
|
|
||||||
CircularBuffer::CircularBuffer(VulkanDevice* device) : device_(device) {}
|
CircularBuffer::CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage,
|
||||||
CircularBuffer::~CircularBuffer() { Shutdown(); }
|
VkDeviceSize capacity, VkDeviceSize alignment)
|
||||||
|
: device_(device), capacity_(capacity) {
|
||||||
bool CircularBuffer::Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage,
|
|
||||||
VkDeviceSize alignment) {
|
|
||||||
VkResult status = VK_SUCCESS;
|
VkResult status = VK_SUCCESS;
|
||||||
capacity = xe::round_up(capacity, alignment);
|
|
||||||
|
|
||||||
// Create our internal buffer.
|
// Create our internal buffer.
|
||||||
VkBufferCreateInfo buffer_info;
|
VkBufferCreateInfo buffer_info;
|
||||||
|
@ -40,15 +37,52 @@ bool CircularBuffer::Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage,
|
||||||
status = vkCreateBuffer(*device_, &buffer_info, nullptr, &gpu_buffer_);
|
status = vkCreateBuffer(*device_, &buffer_info, nullptr, &gpu_buffer_);
|
||||||
CheckResult(status, "vkCreateBuffer");
|
CheckResult(status, "vkCreateBuffer");
|
||||||
if (status != VK_SUCCESS) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map the memory so we can access it.
|
||||||
|
status = vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0,
|
||||||
|
reinterpret_cast<void**>(&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;
|
VkMemoryRequirements reqs;
|
||||||
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
|
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
|
||||||
|
|
||||||
// Allocate memory from the device to back the buffer.
|
// Allocate memory from the device to back the buffer.
|
||||||
assert_true(reqs.size == capacity);
|
owns_gpu_memory_ = true;
|
||||||
reqs.alignment = std::max(alignment, reqs.alignment);
|
|
||||||
gpu_memory_ = device_->AllocateMemory(reqs);
|
gpu_memory_ = device_->AllocateMemory(reqs);
|
||||||
if (!gpu_memory_) {
|
if (!gpu_memory_) {
|
||||||
XELOGE("CircularBuffer::Initialize - Failed to allocate memory!");
|
XELOGE("CircularBuffer::Initialize - Failed to allocate memory!");
|
||||||
|
@ -56,7 +90,6 @@ bool CircularBuffer::Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
alignment_ = reqs.alignment;
|
|
||||||
capacity_ = reqs.size;
|
capacity_ = reqs.size;
|
||||||
gpu_base_ = 0;
|
gpu_base_ = 0;
|
||||||
|
|
||||||
|
@ -92,12 +125,16 @@ void CircularBuffer::Shutdown() {
|
||||||
vkDestroyBuffer(*device_, gpu_buffer_, nullptr);
|
vkDestroyBuffer(*device_, gpu_buffer_, nullptr);
|
||||||
gpu_buffer_ = nullptr;
|
gpu_buffer_ = nullptr;
|
||||||
}
|
}
|
||||||
if (gpu_memory_) {
|
if (gpu_memory_ && owns_gpu_memory_) {
|
||||||
vkFreeMemory(*device_, gpu_memory_, nullptr);
|
vkFreeMemory(*device_, gpu_memory_, nullptr);
|
||||||
gpu_memory_ = nullptr;
|
gpu_memory_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CircularBuffer::GetBufferMemoryRequirements(VkMemoryRequirements* reqs) {
|
||||||
|
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, reqs);
|
||||||
|
}
|
||||||
|
|
||||||
bool CircularBuffer::CanAcquire(VkDeviceSize length) {
|
bool CircularBuffer::CanAcquire(VkDeviceSize length) {
|
||||||
// Make sure the length is aligned.
|
// Make sure the length is aligned.
|
||||||
length = xe::round_up(length, alignment_);
|
length = xe::round_up(length, alignment_);
|
||||||
|
@ -166,7 +203,8 @@ CircularBuffer::Allocation* CircularBuffer::Acquire(
|
||||||
|
|
||||||
return alloc;
|
return alloc;
|
||||||
} else if ((read_head_ - 0) >= aligned_length) {
|
} 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();
|
auto alloc = new Allocation();
|
||||||
alloc->host_ptr = host_base_ + 0;
|
alloc->host_ptr = host_base_ + 0;
|
||||||
alloc->gpu_memory = gpu_memory_;
|
alloc->gpu_memory = gpu_memory_;
|
||||||
|
@ -220,6 +258,11 @@ void CircularBuffer::Scavenge() {
|
||||||
delete *it;
|
delete *it;
|
||||||
it = allocations_.erase(it);
|
it = allocations_.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allocations_.empty()) {
|
||||||
|
// Reset R/W heads to work around fragmentation issues.
|
||||||
|
read_head_ = write_head_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
|
|
|
@ -27,7 +27,8 @@ namespace vulkan {
|
||||||
// ends of the buffer), where trailing older allocations are freed after use.
|
// ends of the buffer), where trailing older allocations are freed after use.
|
||||||
class CircularBuffer {
|
class CircularBuffer {
|
||||||
public:
|
public:
|
||||||
CircularBuffer(VulkanDevice* device);
|
CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage,
|
||||||
|
VkDeviceSize capacity, VkDeviceSize alignment = 256);
|
||||||
~CircularBuffer();
|
~CircularBuffer();
|
||||||
|
|
||||||
struct Allocation {
|
struct Allocation {
|
||||||
|
@ -42,10 +43,12 @@ class CircularBuffer {
|
||||||
std::shared_ptr<Fence> fence;
|
std::shared_ptr<Fence> fence;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage,
|
bool Initialize(VkDeviceMemory memory, VkDeviceSize offset);
|
||||||
VkDeviceSize alignment = 256);
|
bool Initialize();
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
|
void GetBufferMemoryRequirements(VkMemoryRequirements* reqs);
|
||||||
|
|
||||||
VkDeviceSize alignment() const { return alignment_; }
|
VkDeviceSize alignment() const { return alignment_; }
|
||||||
VkDeviceSize capacity() const { return capacity_; }
|
VkDeviceSize capacity() const { return capacity_; }
|
||||||
VkBuffer gpu_buffer() const { return gpu_buffer_; }
|
VkBuffer gpu_buffer() const { return gpu_buffer_; }
|
||||||
|
@ -66,12 +69,14 @@ class CircularBuffer {
|
||||||
void Scavenge();
|
void Scavenge();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// All of these variables are relative to gpu_base
|
||||||
VkDeviceSize capacity_ = 0;
|
VkDeviceSize capacity_ = 0;
|
||||||
VkDeviceSize alignment_ = 0;
|
VkDeviceSize alignment_ = 0;
|
||||||
VkDeviceSize write_head_ = 0;
|
VkDeviceSize write_head_ = 0;
|
||||||
VkDeviceSize read_head_ = 0;
|
VkDeviceSize read_head_ = 0;
|
||||||
|
|
||||||
VulkanDevice* device_;
|
VulkanDevice* device_;
|
||||||
|
bool owns_gpu_memory_ = false;
|
||||||
VkBuffer gpu_buffer_ = nullptr;
|
VkBuffer gpu_buffer_ = nullptr;
|
||||||
VkDeviceMemory gpu_memory_ = nullptr;
|
VkDeviceMemory gpu_memory_ = nullptr;
|
||||||
VkDeviceSize gpu_base_ = 0;
|
VkDeviceSize gpu_base_ = 0;
|
||||||
|
|
Loading…
Reference in New Issue