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 "shape.hpp"
|
||||||
|
|
||||||
|
#include "buffer_manager.hpp"
|
||||||
#include "texture_manager.hpp"
|
#include "texture_manager.hpp"
|
||||||
|
|
||||||
namespace gz::vlk {
|
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) {
|
void Rectangle::setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {
|
||||||
vertices[0].texCoord = topLeft;
|
vertices[0].texCoord = topLeft;
|
||||||
vertices[1].texCoord.x = bottomRight.x;
|
vertices[1].texCoord.x = bottomRight.x;
|
||||||
@ -51,7 +59,13 @@ namespace gz::vlk {
|
|||||||
textureManager.getTexCoords(texture, vertices[1].texCoord);
|
textureManager.getTexCoords(texture, vertices[1].texCoord);
|
||||||
textureManager.getTexCoords(texture, vertices[2].texCoord);
|
textureManager.getTexCoords(texture, vertices[2].texCoord);
|
||||||
textureManager.getTexCoords(texture, vertices[3].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
|
#pragma once
|
||||||
|
|
||||||
|
#include "buffer_manager.hpp"
|
||||||
#include "vertex.hpp"
|
#include "vertex.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace gz::vlk {
|
namespace gz::vlk {
|
||||||
// defined in texture_manager.hpp
|
// defined in texture_manager.hpp
|
||||||
class TextureManager;
|
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 {
|
class Shape {
|
||||||
public:
|
public:
|
||||||
const std::vector<Vertex2D>& getVertices() const { return vertices; }
|
const std::vector<Vertex2D>& getVertices() const { return vertices; }
|
||||||
const std::vector<uint32_t>& getIndices() const { return indices; }
|
const std::vector<uint32_t>& getIndices() const { return indices; }
|
||||||
const std::string& getTexture() const { return texture; }
|
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);
|
void setIndexOffset(uint32_t offset);
|
||||||
/**
|
/**
|
||||||
@ -21,12 +34,17 @@ namespace gz::vlk {
|
|||||||
void normalizeVertices(float width, float height);
|
void normalizeVertices(float width, float height);
|
||||||
virtual void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {};
|
virtual void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {};
|
||||||
virtual void setTextureCoordinates(TextureManager& textureManager) {};
|
virtual void setTextureCoordinates(TextureManager& textureManager) {};
|
||||||
|
bool buffersInitalized() const { return vertexBufferInfo.index != BUFFER_NOT_INITIALIZED; }
|
||||||
|
void initalizeBufferInfos(BufferManager<Vertex2D, uint32_t>& bufferManager);
|
||||||
virtual ~Shape() {};
|
virtual ~Shape() {};
|
||||||
protected:
|
protected:
|
||||||
std::string texture = "texture.png";
|
std::string texture = "texture.png";
|
||||||
|
uint32_t textureAtlasIndex;
|
||||||
std::vector<Vertex2D> vertices;
|
std::vector<Vertex2D> vertices;
|
||||||
std::vector<uint32_t> indices;
|
std::vector<uint32_t> indices;
|
||||||
|
|
||||||
|
BufferInfo vertexBufferInfo;
|
||||||
|
BufferInfo indexBufferInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Rectangle : public Shape {
|
class Rectangle : public Shape {
|
||||||
|
Loading…
Reference in New Issue
Block a user