From 720882efeb10c086944524e399d17bb423b0c92e Mon Sep 17 00:00:00 2001 From: Samuliak <samuliak77@gmail.com> Date: Wed, 3 Jul 2024 14:31:15 +0200 Subject: [PATCH] store vertices in 1 big buffer --- .../renderer_mtl/mtl_vertex_buffer_cache.hpp | 71 +++++++++++++++++++ include/renderer_mtl/renderer_mtl.hpp | 2 + src/core/renderer_mtl/renderer_mtl.cpp | 11 ++- 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 include/renderer_mtl/mtl_vertex_buffer_cache.hpp diff --git a/include/renderer_mtl/mtl_vertex_buffer_cache.hpp b/include/renderer_mtl/mtl_vertex_buffer_cache.hpp new file mode 100644 index 00000000..778499cf --- /dev/null +++ b/include/renderer_mtl/mtl_vertex_buffer_cache.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include <map> +#include "pica_to_mtl.hpp" + +using namespace PICA; + +namespace Metal { + +struct BufferHandle { + MTL::Buffer* buffer; + size_t offset; +}; + +// 64MB buffer for caching vertex data +#define CACHE_BUFFER_SIZE 64 * 1024 * 1024 + +class VertexBufferCache { +public: + VertexBufferCache() = default; + + ~VertexBufferCache() { + clear(); + } + + void set(MTL::Device* dev) { + device = dev; + buffer = device->newBuffer(CACHE_BUFFER_SIZE, MTL::ResourceStorageModeShared); + } + + void endFrame() { + ptr = 0; + for (auto buffer : additionalAllocations) { + buffer->release(); + } + additionalAllocations.clear(); + } + + BufferHandle get(const std::span<const PICA::Vertex>& vertices) { + // If the vertex buffer is too large, just create a new one + if (ptr + vertices.size_bytes() > CACHE_BUFFER_SIZE) { + MTL::Buffer* newBuffer = device->newBuffer(vertices.data(), vertices.size_bytes(), MTL::ResourceStorageModeShared); + additionalAllocations.push_back(newBuffer); + Helpers::warn("Vertex buffer doesn't have enough space, creating a new buffer"); + + return BufferHandle{newBuffer, 0}; + } + + // Copy the data into the buffer + memcpy((char*)buffer->contents() + ptr, vertices.data(), vertices.size_bytes()); + + size_t oldPtr = ptr; + ptr += vertices.size_bytes(); + + return BufferHandle{buffer, oldPtr}; + } + + void clear() { + endFrame(); + buffer->release(); + } + +private: + MTL::Buffer* buffer; + size_t ptr = 0; + std::vector<MTL::Buffer*> additionalAllocations; + + MTL::Device* device; +}; + +} // namespace Metal diff --git a/include/renderer_mtl/renderer_mtl.hpp b/include/renderer_mtl/renderer_mtl.hpp index 51e3f97e..8ac3f2c2 100644 --- a/include/renderer_mtl/renderer_mtl.hpp +++ b/include/renderer_mtl/renderer_mtl.hpp @@ -6,6 +6,7 @@ #include "render_target.hpp" #include "mtl_pipeline_cache.hpp" #include "mtl_depth_stencil_cache.hpp" +#include "mtl_vertex_buffer_cache.hpp" // HACK: use the OpenGL cache #include "../renderer_gl/surface_cache.hpp" @@ -43,6 +44,7 @@ class RendererMTL final : public Renderer { Metal::PipelineCache blitPipelineCache; Metal::PipelineCache drawPipelineCache; Metal::DepthStencilCache depthStencilCache; + Metal::VertexBufferCache vertexBufferCache; // Helpers MTL::SamplerState* basicSampler; diff --git a/src/core/renderer_mtl/renderer_mtl.cpp b/src/core/renderer_mtl/renderer_mtl.cpp index c2fe9f1e..01a40f0a 100644 --- a/src/core/renderer_mtl/renderer_mtl.cpp +++ b/src/core/renderer_mtl/renderer_mtl.cpp @@ -78,6 +78,9 @@ void RendererMTL::display() { commandBuffer->presentDrawable(drawable); commitCommandBuffer(); + + // Inform the vertex buffer cache that the frame ended + vertexBufferCache.endFrame(); } void RendererMTL::initGraphicsContext(SDL_Window* window) { @@ -191,6 +194,9 @@ void RendererMTL::initGraphicsContext(SDL_Window* window) { // Depth stencil cache depthStencilCache.set(device); + + // Vertex buffer cache + vertexBufferCache.set(device); } void RendererMTL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { @@ -382,9 +388,8 @@ void RendererMTL::drawVertices(PICA::PrimType primType, std::span<const PICA::Ve if (vertices.size_bytes() < 4 * 1024) { renderCommandEncoder->setVertexBytes(vertices.data(), vertices.size_bytes(), VERTEX_BUFFER_BINDING_INDEX); } else { - // TODO: cache this buffer - MTL::Buffer* vertexBuffer = device->newBuffer(vertices.data(), vertices.size_bytes(), MTL::ResourceStorageModeShared); - renderCommandEncoder->setVertexBuffer(vertexBuffer, 0, VERTEX_BUFFER_BINDING_INDEX); + Metal::BufferHandle buffer = vertexBufferCache.get(vertices); + renderCommandEncoder->setVertexBuffer(buffer.buffer, buffer.offset, VERTEX_BUFFER_BINDING_INDEX); } // Bind resources