184 lines
7.1 KiB
C++
184 lines
7.1 KiB
C++
#include "Buffers.hpp"
|
|
#include "../Allocator.hpp"
|
|
#include "../../../../core/utils/logger.hpp"
|
|
|
|
using namespace render::vk;
|
|
|
|
Buffer::~Buffer() {
|
|
vkDestroyBuffer(Allocator::GetDefault()->getDevice(), ref, ALLOC);
|
|
//NOTE: memory_ptr self destroy
|
|
}
|
|
|
|
memory::ptr render::vk::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, const render::data_view view, Buffer::info &out) {
|
|
auto alloc = Allocator::GetDefault();
|
|
auto device = alloc->getDevice();
|
|
|
|
VkBufferCreateInfo bufferInfo{};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
bufferInfo.size = size;
|
|
bufferInfo.usage = usage;
|
|
if (view)
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
|
|
if (vkCreateBuffer(device, &bufferInfo, ALLOC, &out.ref) != VK_SUCCESS) {
|
|
LOG_E("Failed to create buffer");
|
|
return memory::GetNull();
|
|
}
|
|
out.offset = 0;
|
|
|
|
VkMemoryRequirements memRequirements;
|
|
vkGetBufferMemoryRequirements(device, out.ref, &memRequirements);
|
|
|
|
auto memory = alloc->allocate(memRequirements, properties);
|
|
if (!memory || vkBindBufferMemory(device, out.ref, memory->ref, memory->offset) != VK_SUCCESS) {
|
|
LOG_E("Failed to allocate buffer memory");
|
|
return memory::GetNull();
|
|
}
|
|
|
|
if (view) {
|
|
if (memory->ptr != nullptr) {
|
|
memory->write(view.ptr, view.size);
|
|
} else if(auto staging = WritableBuffer::Create(size)) {
|
|
staging->write(view, 0);
|
|
alloc->copyBuffer(staging->getRef(), out.ref, size);
|
|
} else {
|
|
FATAL("Cannot allocate staging memory");
|
|
return memory::GetNull();
|
|
}
|
|
}
|
|
return memory;
|
|
}
|
|
memory::ptr createBuffers(const std::vector<Buffer::requirement>& requirements, VkMemoryPropertyFlags properties, std::vector<Buffer::info>& out) {
|
|
assert(!requirements.empty());
|
|
out.resize(requirements.size()+1);
|
|
|
|
auto alloc = Allocator::GetDefault();
|
|
auto device = alloc->getDevice();
|
|
|
|
// Create buffers
|
|
VkMemoryRequirements memRequirements = {0, 0, UINT32_MAX};
|
|
std::vector<VkDeviceSize> sizes;
|
|
sizes.resize(requirements.size());
|
|
for (size_t i = 0; i < requirements.size(); i++) {
|
|
VkBufferCreateInfo bufferInfo{};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
bufferInfo.size = requirements[i].size;
|
|
bufferInfo.usage = static_cast<VkBufferUsageFlags>(requirements[i].usage);
|
|
if (requirements[i].view)
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
|
|
if (vkCreateBuffer(device, &bufferInfo, ALLOC, &out[i].ref) != VK_SUCCESS) {
|
|
LOG_E("Failed to create buffer");
|
|
return memory::GetNull();
|
|
}
|
|
|
|
VkMemoryRequirements individualMemRequirements;
|
|
vkGetBufferMemoryRequirements(device, out[i].ref, &individualMemRequirements);
|
|
memRequirements.alignment = std::max(memRequirements.alignment, individualMemRequirements.alignment);
|
|
memRequirements.memoryTypeBits &= individualMemRequirements.memoryTypeBits;
|
|
sizes[i] = individualMemRequirements.size;
|
|
}
|
|
|
|
// Align blocks
|
|
auto aligned = [&](VkDeviceSize offset) {
|
|
if (offset % memRequirements.alignment == 0)
|
|
return offset;
|
|
return offset + memRequirements.alignment - (offset % memRequirements.alignment);
|
|
};
|
|
out[0].offset = 0;
|
|
for (size_t i = 1; i < out.size(); i++) {
|
|
out[i].offset = aligned(out[i-1].offset + sizes[i-1]);
|
|
}
|
|
memRequirements.size = out.back().offset;
|
|
out.pop_back();
|
|
|
|
// Bind memory
|
|
auto memory = alloc->allocate(memRequirements, properties);
|
|
if (!memory) {
|
|
LOG_E("Failed to allocate buffers");
|
|
return memory::GetNull();
|
|
}
|
|
for (size_t i = 0; i < requirements.size(); i++) {
|
|
if (vkBindBufferMemory(device, out[i].ref, memory->ref, memory->offset + out[i].offset) != VK_SUCCESS) {
|
|
LOG_E("Failed to bind buffer");
|
|
return memory::GetNull();
|
|
}
|
|
}
|
|
|
|
VkDeviceSize stagingSize = 0;
|
|
for (auto& requirement: requirements)
|
|
if (requirement.view)
|
|
stagingSize = std::max(stagingSize, requirement.size);
|
|
|
|
// Copy views
|
|
// MAYBE: allow single copy
|
|
if (stagingSize != 0) {
|
|
if (memory->ptr != nullptr) {
|
|
for (size_t i = 0; i < requirements.size(); i++) {
|
|
if (requirements[i].view) {
|
|
assert(requirements[i].view.size <= requirements[i].size);
|
|
memory->write(requirements[i].view.ptr, requirements[i].view.size, out[i].offset);
|
|
}
|
|
}
|
|
} else if(auto staging = WritableBuffer::Create(stagingSize)) {
|
|
for (size_t i = 0; i < requirements.size(); i++) {
|
|
if (requirements[i].view) {
|
|
assert(requirements[i].view.size <= requirements[i].size);
|
|
staging->write(requirements[i].view, 0);
|
|
alloc->copyBuffer(staging->getRef(), out[i].ref, requirements[i].size);
|
|
}
|
|
}
|
|
} else {
|
|
FATAL("Cannot allocate staging memory");
|
|
return memory::GetNull();
|
|
}
|
|
}
|
|
|
|
return memory;
|
|
}
|
|
|
|
void Buffer::MakeDefault() { }
|
|
|
|
std::unique_ptr<WritableBuffer> WritableBuffer::Create(size_t size, Usage usage, const data_view write) {
|
|
vk::Buffer::info tmp;
|
|
auto mem = createBuffer(size, static_cast<int>(usage), memory::HOST_EASILY_WRITABLE, write, tmp);
|
|
return std::unique_ptr<WritableBuffer>(new WritableBuffer(tmp.ref, std::move(mem), tmp.offset));
|
|
}
|
|
void WritableBuffer::write(const data_view view, size_t offset) {
|
|
memory->write(view.ptr, view.size, memOffset + offset);
|
|
}
|
|
void WritableBuffer::read(data_ref ref, size_t offset) {
|
|
memory->read(ref.ptr, ref.size, memOffset + offset);
|
|
}
|
|
|
|
ShortIndexedVertexBuffer::~ShortIndexedVertexBuffer() {
|
|
vkDestroyBuffer(Allocator::GetDefault()->getDevice(), vertex, ALLOC);
|
|
vkDestroyBuffer(Allocator::GetDefault()->getDevice(), index, ALLOC);
|
|
//NOTE: memory_ptr self destroy
|
|
}
|
|
std::unique_ptr<ShortIndexedVertexBuffer> ShortIndexedVertexBuffer::Create(const data_view vertices, const data_view indices) {
|
|
std::vector<vk::Buffer::info> tmp;
|
|
auto mem = createBuffers({{vertices.size, Usage::VERTEX, vertices}, {indices.size, Usage::INDEX, indices}}, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, tmp);
|
|
return std::unique_ptr<ShortIndexedVertexBuffer>(new ShortIndexedVertexBuffer(tmp.at(0).ref, tmp.at(1).ref, std::move(mem)));
|
|
}
|
|
|
|
BufferGroup::~BufferGroup() {
|
|
free();
|
|
}
|
|
|
|
void BufferGroup::allocate(const std::vector<Buffer::requirement> & reqs, bool writable) {
|
|
free();
|
|
memory = createBuffers(reqs, writable ? memory::HOST_EASILY_WRITABLE : (VkMemoryPropertyFlags)VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, refs);
|
|
}
|
|
void BufferGroup::free() {
|
|
for (const auto& buffer: refs) {
|
|
vkDestroyBuffer(Allocator::GetDefault()->getDevice(), buffer.ref, ALLOC);
|
|
}
|
|
refs.clear();
|
|
memory = memory::GetNull();
|
|
}
|
|
void BufferGroup::write(size_t i, const data_view view) {
|
|
memory->write(view.ptr, view.size, refs.at(i).offset);
|
|
} |