added buffer manager for vertex and index buffers
This commit is contained in:
parent
342395129c
commit
17033bf104
152
src/buffer_manager.cpp
Normal file
152
src/buffer_manager.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
#include "buffer_manager.hpp"
|
||||
|
||||
#include "exceptions.hpp"
|
||||
#include "vulkan_allocator.hpp"
|
||||
#include "vulkan_instance.hpp"
|
||||
#include <gz-util/string/to_string.hpp>
|
||||
|
||||
namespace gz::vlk {
|
||||
std::string BufferInfo::toString() const {
|
||||
return "<index: " + gz::toString(index) + ", offset: " + gz::toString(offset) + ", count:" + gz::toString(count) + ">";
|
||||
}
|
||||
|
||||
template class BufferManager<Vertex2D, uint32_t>;
|
||||
template class BufferManager<Vertex3D, uint32_t>;
|
||||
template class BufferManager<Vertex2D, uint16_t>;
|
||||
template class BufferManager<Vertex3D, uint16_t>;
|
||||
template<VertexType VertexT, SupportedIndexType IndexT>
|
||||
BufferManager<VertexT, IndexT>::BufferManager(VulkanInstance& instance)
|
||||
: vk(instance)
|
||||
{
|
||||
bLog = vk.createSublog(true, "BufferManager", gz::Color::BO_CYAN);
|
||||
vk.registerCleanupCallback(std::bind(&BufferManager::cleanup, this));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<VertexType VertexT, SupportedIndexType IndexT>
|
||||
vk::Buffer BufferManager<VertexT, IndexT>::getVertexBuffer(const BufferInfo& vertexBufferInfo)const {
|
||||
if (vertexBuffers.size() <= vertexBufferInfo.index) {
|
||||
throw VkUserError("Invalid buffer index: " + gz::toString(vertexBufferInfo.index), "BufferManager::getVertexBuffer");
|
||||
}
|
||||
return vertexBuffers.at(vertexBufferInfo.index).buffer;
|
||||
}
|
||||
template<VertexType VertexT, SupportedIndexType IndexT>
|
||||
vk::Buffer BufferManager<VertexT, IndexT>::getIndexBuffer(const BufferInfo& indexBufferInfo)const {
|
||||
if (indexBuffers.size() <= indexBufferInfo.index) {
|
||||
throw VkUserError("Invalid buffer index: " + gz::toString(indexBufferInfo.index), "BufferManager::getIndexBuffer");
|
||||
}
|
||||
return indexBuffers.at(indexBufferInfo.index).buffer;
|
||||
}
|
||||
|
||||
|
||||
// helpers
|
||||
bool searchForSpaceInBuffers(std::vector<Buffer>& buffers, uint32_t allocSize, BufferInfo& bufferInfo) {
|
||||
// search for allocSize space in buffers, return true and set bufferinfo to found space if found
|
||||
for (auto bufferIt = buffers.begin(); bufferIt != buffers.end(); bufferIt++) {
|
||||
try {
|
||||
bufferInfo.offset = bufferIt->allocator.allocate(allocSize, 1);
|
||||
bufferInfo.index = bufferIt - buffers.begin();
|
||||
return true;
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
template<typename T>
|
||||
void allocateNewBuffer(std::vector<Buffer>& buffers, uint32_t count, VulkanInstance& vk) {
|
||||
uint32_t bufferSize = sizeof(T) * count;
|
||||
buffers.emplace_back(Buffer{.buffer = vk::Buffer(), .bufferMemory = MemoryInfo(), .bufferSize = bufferSize, .allocator = DynBlockAllocator(bufferSize)});
|
||||
if constexpr (VertexType<T>) {
|
||||
vk.createVertexBuffer<T>(count, buffers.back().buffer, buffers.back().bufferMemory, buffers.back().bufferSize);
|
||||
}
|
||||
else if constexpr (SupportedIndexType<T>) {
|
||||
vk.createIndexBuffer<T>(count, buffers.back().buffer, buffers.back().bufferMemory, buffers.back().bufferSize);
|
||||
}
|
||||
else {
|
||||
static_assert(VertexType<T> or SupportedIndexType<T>, "T is not vertex or index type");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<VertexType VertexT, SupportedIndexType IndexT>
|
||||
template<util::ContiguousRange<VertexT> VertexRange, util::ContiguousRange<IndexT> IndexRange>
|
||||
std::pair<BufferInfo, BufferInfo> BufferManager<VertexT, IndexT>::addVertices(const VertexRange& vertices, const IndexRange& indices) {
|
||||
std::pair<BufferInfo, BufferInfo> bufferInfos;
|
||||
bufferInfos.first.count = vertices.size();
|
||||
// search for space for vertex buffer
|
||||
if (!searchForSpaceInBuffers(vertexBuffers, sizeof(VertexT) * vertices.size(), bufferInfos.first)) {
|
||||
bLog.log0("Allocating new vertex buffer nr.", vertexBuffers.size());
|
||||
// allocate new vertex buffer
|
||||
allocateNewBuffer<VertexT>(vertexBuffers, std::max(vertices.size(), TODO_VERTICES_COUNT), vk);
|
||||
bufferInfos.first.index = vertexBuffers.size() - 1;
|
||||
bufferInfos.first.offset = vertexBuffers.back().allocator.allocate(sizeof(VertexT) * vertices.size(), 1);
|
||||
}
|
||||
// copy vertices to vertex buffer
|
||||
Buffer& vertexBuffer = vertexBuffers.at(bufferInfos.first.index);
|
||||
vk.copyToDeviceBuffer(vertices.data(), sizeof(VertexT) * vertices.size(), vertexBuffer.buffer, vertexBuffer.bufferMemory, bufferInfos.first.offset);
|
||||
|
||||
bufferInfos.second.count = indices.size();
|
||||
// search for space for index buffer
|
||||
if (!searchForSpaceInBuffers(indexBuffers, sizeof(IndexT) * indices.size(), bufferInfos.second)) {
|
||||
bLog.log0("Allocating new index buffer nr.", indexBuffers.size());
|
||||
// allocate new index buffer
|
||||
allocateNewBuffer<IndexT>(indexBuffers, std::max(indices.size(), TODO_INDICES_COUNT), vk);
|
||||
bufferInfos.second.index = indexBuffers.size() - 1;
|
||||
bufferInfos.second.offset = indexBuffers.back().allocator.allocate(sizeof(IndexT) * indices.size(), 1);
|
||||
}
|
||||
// copy indices to index buffer
|
||||
Buffer& indexBuffer = indexBuffers.at(bufferInfos.first.index);
|
||||
vk.copyToDeviceBuffer(indices.data(), sizeof(IndexT) * indices.size(), indexBuffer.buffer, indexBuffer.bufferMemory, bufferInfos.second.offset);
|
||||
bLog.log0("addVertices: VertexBufferInfo:", bufferInfos.first, "IndexBufferInfo", bufferInfos.second);
|
||||
return bufferInfos;
|
||||
}
|
||||
template std::pair<BufferInfo, BufferInfo> BufferManager<Vertex2D, uint32_t>::addVertices(const std::vector<Vertex2D>& vertices, const std::vector<uint32_t>& indices);
|
||||
|
||||
|
||||
template<VertexType VertexT, SupportedIndexType IndexT>
|
||||
void BufferManager<VertexT, IndexT>::removeVertices(BufferInfo& vertexBufferInfo, BufferInfo& indexBufferInfo) {
|
||||
if (vertexBuffers.size() <= vertexBufferInfo.index) {
|
||||
throw VkUserError("Invalid vertex buffer index:" + gz::toString(vertexBufferInfo.index), "BufferManager::removeVertices");
|
||||
}
|
||||
Buffer& vertexBuffer = vertexBuffers.at(vertexBufferInfo.index);
|
||||
vertexBuffer.allocator.free(vertexBufferInfo.offset);
|
||||
vertexBufferInfo.index = BUFFER_NOT_INITIALIZED;
|
||||
vertexBufferInfo.offset = BUFFER_NOT_INITIALIZED;
|
||||
vertexBufferInfo.count = BUFFER_NOT_INITIALIZED;
|
||||
|
||||
if (indexBuffers.size() <= indexBufferInfo.index) {
|
||||
throw VkUserError("Invalid index buffer index:" + gz::toString(indexBufferInfo.index), "BufferManager::removeVertices");
|
||||
}
|
||||
Buffer& indexBuffer = indexBuffers.at(indexBufferInfo.index);
|
||||
indexBuffer.allocator.free(indexBufferInfo.offset);
|
||||
indexBufferInfo.index = BUFFER_NOT_INITIALIZED;
|
||||
indexBufferInfo.offset = BUFFER_NOT_INITIALIZED;
|
||||
indexBufferInfo.count = BUFFER_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
|
||||
template<VertexType VertexT, SupportedIndexType IndexT>
|
||||
void BufferManager<VertexT, IndexT>::cleanup() {
|
||||
for (auto bufferIt = vertexBuffers.begin(); bufferIt != vertexBuffers.end(); bufferIt++) {
|
||||
// check if all blocks are free
|
||||
for (auto blockIt = bufferIt->allocator.begin(); blockIt != bufferIt->allocator.end(); blockIt++) {
|
||||
if (blockIt->free) { continue; }
|
||||
bLog.warning("cleanup: vertex buffer was not removed:", *blockIt, "index:", bufferIt - vertexBuffers.begin());
|
||||
}
|
||||
vk.destroyBuffer(bufferIt->buffer, bufferIt->bufferMemory);
|
||||
}
|
||||
vertexBuffers.clear();
|
||||
for (auto bufferIt = indexBuffers.begin(); bufferIt != indexBuffers.end(); bufferIt++) {
|
||||
// check if all blocks are free
|
||||
for (auto blockIt = bufferIt->allocator.begin(); blockIt != bufferIt->allocator.end(); blockIt++) {
|
||||
if (blockIt->free) { continue; }
|
||||
bLog.warning("cleanup: index buffer was not removed:", *blockIt, "index:", bufferIt - indexBuffers.begin());
|
||||
}
|
||||
vk.destroyBuffer(bufferIt->buffer, bufferIt->bufferMemory);
|
||||
}
|
||||
indexBuffers.clear();
|
||||
}
|
||||
|
||||
|
||||
} // namespace gz::vlk
|
73
src/buffer_manager.hpp
Normal file
73
src/buffer_manager.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_allocator.hpp"
|
||||
#include "dynamic_size_block_allocator.hpp"
|
||||
#include "vertex.hpp"
|
||||
|
||||
#include <gz-util/concepts.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace gz::vlk {
|
||||
constexpr size_t TODO_VERTICES_COUNT = 100000;
|
||||
constexpr size_t TODO_INDICES_COUNT = 100000;
|
||||
constexpr uint32_t BUFFER_NOT_INITIALIZED = UINT32_MAX;
|
||||
struct BufferInfo {
|
||||
uint32_t index = BUFFER_NOT_INITIALIZED;
|
||||
uint32_t offset = BUFFER_NOT_INITIALIZED;
|
||||
uint32_t count = BUFFER_NOT_INITIALIZED;
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
struct Buffer {
|
||||
vk::Buffer buffer;
|
||||
MemoryInfo bufferMemory;
|
||||
vk::DeviceSize bufferSize;
|
||||
DynBlockAllocator allocator;
|
||||
};
|
||||
|
||||
// defined in vulkan_instance.hpp
|
||||
class VulkanInstance;
|
||||
|
||||
/**
|
||||
* @brief Manage multiple vertex and index buffers on device local memory
|
||||
* @details
|
||||
* Instantiated for Vertex2D, Vertex3D with uint16_t and uint32_t respectively.
|
||||
* Manages buffers allocated from VulkanAllocator.
|
||||
* The vertex and index buffers are then suballocated from those buffers (using DynBlockAllocator).
|
||||
*/
|
||||
template<VertexType VertexT, SupportedIndexType IndexT>
|
||||
class BufferManager {
|
||||
public:
|
||||
BufferManager(VulkanInstance& instance);
|
||||
|
||||
vk::Buffer getVertexBuffer(const BufferInfo& vertexBufferInfo) const;
|
||||
vk::Buffer getIndexBuffer(const BufferInfo& indexBufferInfo) const;
|
||||
|
||||
/**
|
||||
* @brief Copy vertices and indices to a gpu-local buffer
|
||||
* @details
|
||||
* Instantiated for std::vectors
|
||||
*/
|
||||
template<util::ContiguousRange<VertexT> VertexRange, util::ContiguousRange<IndexT> IndexRange>
|
||||
[[ nodiscard ]] std::pair<BufferInfo, BufferInfo> addVertices(const VertexRange& vertices, const IndexRange& indices);
|
||||
/**
|
||||
* @brief Free the vertex and index buffers and reset bufferInfos
|
||||
* @todo Deallocate memory if unused
|
||||
*/
|
||||
void removeVertices(BufferInfo& vertexBufferInfo, BufferInfo& indexBufferInfo);
|
||||
private:
|
||||
/**
|
||||
* @brief Deallocate all buffers
|
||||
* @details
|
||||
* Called by VulkanInstance::cleanup()
|
||||
*/
|
||||
void cleanup();
|
||||
std::vector<Buffer> vertexBuffers;
|
||||
std::vector<Buffer> indexBuffers;
|
||||
|
||||
VulkanInstance& vk;
|
||||
Log bLog;
|
||||
};
|
||||
|
||||
} // namespace gz::vlk
|
@ -1,5 +1,6 @@
|
||||
#include "shape.hpp"
|
||||
|
||||
#include "buffer_manager.hpp"
|
||||
#include "texture_manager.hpp"
|
||||
|
||||
namespace gz::vlk {
|
||||
@ -36,6 +37,13 @@ namespace gz::vlk {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Shape::initalizeBufferInfos(BufferManager<Vertex2D, uint32_t>& bufferManager) {
|
||||
assert(vertexBufferInfo.index == BUFFER_NOT_INITIALIZED);
|
||||
std::tie(vertexBufferInfo, indexBufferInfo) = bufferManager.addVertices(vertices, indices);
|
||||
}
|
||||
|
||||
|
||||
void Rectangle::setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {
|
||||
vertices[0].texCoord = topLeft;
|
||||
vertices[1].texCoord.x = bottomRight.x;
|
||||
@ -51,7 +59,13 @@ namespace gz::vlk {
|
||||
textureManager.getTexCoords(texture, vertices[1].texCoord);
|
||||
textureManager.getTexCoords(texture, vertices[2].texCoord);
|
||||
textureManager.getTexCoords(texture, vertices[3].texCoord);
|
||||
textureAtlasIndex = textureManager.getTextureAtlasIndex(texture);
|
||||
}
|
||||
else {
|
||||
textureAtlasIndex = 0;
|
||||
|
||||
}
|
||||
// must be after getTexCoords, since that might load the texture
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,18 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "buffer_manager.hpp"
|
||||
#include "vertex.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace gz::vlk {
|
||||
// defined in texture_manager.hpp
|
||||
class TextureManager;
|
||||
/**
|
||||
* @brief Base class for shapes
|
||||
* @details
|
||||
* In this implementaiton, a shape is a 2D object made of several vertices.
|
||||
* Each shape has its own vertex and index buffers, which are managed by a BufferManager.
|
||||
* Their texture is managed by a TextureManager.
|
||||
* @todo free resources and rule of 5
|
||||
*/
|
||||
class Shape {
|
||||
public:
|
||||
const std::vector<Vertex2D>& getVertices() const { return vertices; }
|
||||
const std::vector<uint32_t>& getIndices() const { return indices; }
|
||||
const std::string& getTexture() const { return texture; }
|
||||
uint32_t getTexureAtlasIndex() const { return textureAtlasIndex; }
|
||||
const BufferInfo& getVertexBufferInfo() const { return vertexBufferInfo; }
|
||||
const BufferInfo& getIndexBufferInfo() const { return indexBufferInfo; }
|
||||
|
||||
/**
|
||||
* @brief Add an offset to all indices
|
||||
* @brief Add an offset to all indices (useful when putting multiple shapes in the same vertex buffer)
|
||||
*/
|
||||
void setIndexOffset(uint32_t offset);
|
||||
/**
|
||||
@ -21,12 +34,17 @@ namespace gz::vlk {
|
||||
void normalizeVertices(float width, float height);
|
||||
virtual void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {};
|
||||
virtual void setTextureCoordinates(TextureManager& textureManager) {};
|
||||
bool buffersInitalized() const { return vertexBufferInfo.index != BUFFER_NOT_INITIALIZED; }
|
||||
void initalizeBufferInfos(BufferManager<Vertex2D, uint32_t>& bufferManager);
|
||||
virtual ~Shape() {};
|
||||
protected:
|
||||
std::string texture = "texture.png";
|
||||
uint32_t textureAtlasIndex;
|
||||
std::vector<Vertex2D> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
|
||||
BufferInfo vertexBufferInfo;
|
||||
BufferInfo indexBufferInfo;
|
||||
};
|
||||
|
||||
class Rectangle : public Shape {
|
||||
|
Loading…
Reference in New Issue
Block a user