mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-04 22:11:18 +12:00
192 lines
5.8 KiB
C++
192 lines
5.8 KiB
C++
#include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h"
|
|
#include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h"
|
|
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
|
|
#include "Metal/MTLResource.hpp"
|
|
|
|
const size_t BUFFER_ALLOCATION_SIZE = 8 * 1024 * 1024;
|
|
|
|
MetalBufferAllocator::~MetalBufferAllocator()
|
|
{
|
|
for (auto buffer : m_buffers)
|
|
{
|
|
buffer->release();
|
|
}
|
|
}
|
|
|
|
MetalBufferAllocation MetalBufferAllocator::GetBufferAllocation(size_t size, size_t alignment)
|
|
{
|
|
// Align the size
|
|
size = Align(size, alignment);
|
|
|
|
// First, try to find a free range
|
|
for (uint32 i = 0; i < m_freeBufferRanges.size(); i++)
|
|
{
|
|
auto& range = m_freeBufferRanges[i];
|
|
if (size <= range.size)
|
|
{
|
|
MetalBufferAllocation allocation;
|
|
allocation.bufferIndex = range.bufferIndex;
|
|
allocation.bufferOffset = range.offset;
|
|
allocation.data = (uint8*)m_buffers[range.bufferIndex]->contents() + range.offset;
|
|
|
|
range.offset += size;
|
|
range.size -= size;
|
|
|
|
if (range.size == 0)
|
|
{
|
|
m_freeBufferRanges.erase(m_freeBufferRanges.begin() + i);
|
|
}
|
|
|
|
return allocation;
|
|
}
|
|
}
|
|
|
|
// If no free range was found, allocate a new buffer
|
|
MTL::Buffer* buffer = m_mtlr->GetDevice()->newBuffer(std::max(size, BUFFER_ALLOCATION_SIZE), MTL::ResourceStorageModeShared);
|
|
|
|
MetalBufferAllocation allocation;
|
|
allocation.bufferIndex = m_buffers.size();
|
|
allocation.bufferOffset = 0;
|
|
allocation.data = buffer->contents();
|
|
|
|
m_buffers.push_back(buffer);
|
|
|
|
// If the buffer is larger than the requested size, add the remaining space to the free buffer ranges
|
|
if (size < BUFFER_ALLOCATION_SIZE)
|
|
{
|
|
MetalBufferRange range;
|
|
range.bufferIndex = allocation.bufferIndex;
|
|
range.offset = size;
|
|
range.size = BUFFER_ALLOCATION_SIZE - size;
|
|
|
|
m_freeBufferRanges.push_back(range);
|
|
}
|
|
|
|
return allocation;
|
|
}
|
|
|
|
MetalVertexBufferCache::~MetalVertexBufferCache()
|
|
{
|
|
for (uint32 i = 0; i < LATTE_MAX_VERTEX_BUFFERS; i++)
|
|
{
|
|
auto vertexBufferRange = m_bufferRanges[i];
|
|
if (vertexBufferRange)
|
|
{
|
|
if (vertexBufferRange->restrideInfo.buffer)
|
|
{
|
|
vertexBufferRange->restrideInfo.buffer->release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MetalRestridedBufferRange MetalVertexBufferCache::RestrideBufferIfNeeded(MTL::Buffer* bufferCache, uint32 bufferIndex, size_t stride)
|
|
{
|
|
auto vertexBufferRange = m_bufferRanges[bufferIndex];
|
|
auto& restrideInfo = vertexBufferRange->restrideInfo;
|
|
|
|
if (stride % 4 == 0)
|
|
{
|
|
// No restride needed
|
|
return {bufferCache, vertexBufferRange->offset};
|
|
}
|
|
|
|
if (restrideInfo.memoryInvalidated || stride != restrideInfo.lastStride)
|
|
{
|
|
// TODO: use compute/void vertex function instead
|
|
size_t newStride = Align(stride, 4);
|
|
size_t newSize = vertexBufferRange->size / stride * newStride;
|
|
restrideInfo.buffer = m_mtlr->GetDevice()->newBuffer(newSize, MTL::StorageModeShared);
|
|
|
|
uint8* oldPtr = (uint8*)bufferCache->contents() + vertexBufferRange->offset;
|
|
uint8* newPtr = (uint8*)restrideInfo.buffer->contents();
|
|
|
|
for (size_t elem = 0; elem < vertexBufferRange->size / stride; elem++)
|
|
{
|
|
memcpy(newPtr + elem * newStride, oldPtr + elem * stride, stride);
|
|
}
|
|
debug_printf("Restrided vertex buffer (old stride: %zu, new stride: %zu, old size: %zu, new size: %zu)\n", stride, newStride, vertexBufferRange->size, newSize);
|
|
|
|
restrideInfo.memoryInvalidated = false;
|
|
restrideInfo.lastStride = newStride;
|
|
}
|
|
|
|
return {restrideInfo.buffer, 0};
|
|
}
|
|
|
|
void MetalVertexBufferCache::MemoryRangeChanged(size_t offset, size_t size)
|
|
{
|
|
for (uint32 i = 0; i < LATTE_MAX_VERTEX_BUFFERS; i++)
|
|
{
|
|
auto vertexBufferRange = m_bufferRanges[i];
|
|
if (vertexBufferRange)
|
|
{
|
|
if ((offset < vertexBufferRange->offset && (offset + size) < (vertexBufferRange->offset + vertexBufferRange->size)) ||
|
|
(offset > vertexBufferRange->offset && (offset + size) > (vertexBufferRange->offset + vertexBufferRange->size)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
vertexBufferRange->restrideInfo.memoryInvalidated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
MetalMemoryManager::~MetalMemoryManager()
|
|
{
|
|
if (m_bufferCache)
|
|
{
|
|
m_bufferCache->release();
|
|
}
|
|
}
|
|
|
|
void* MetalMemoryManager::GetTextureUploadBuffer(size_t size)
|
|
{
|
|
if (m_textureUploadBuffer.size() < size)
|
|
{
|
|
m_textureUploadBuffer.resize(size);
|
|
}
|
|
|
|
return m_textureUploadBuffer.data();
|
|
}
|
|
|
|
void MetalMemoryManager::InitBufferCache(size_t size)
|
|
{
|
|
if (m_bufferCache)
|
|
{
|
|
debug_printf("MetalMemoryManager::InitBufferCache: buffer cache already initialized\n");
|
|
return;
|
|
}
|
|
|
|
m_bufferCache = m_mtlr->GetDevice()->newBuffer(size, MTL::ResourceStorageModeShared);
|
|
}
|
|
|
|
void MetalMemoryManager::UploadToBufferCache(const void* data, size_t offset, size_t size)
|
|
{
|
|
if ((offset + size) > m_bufferCache->length())
|
|
{
|
|
throw std::runtime_error(std::to_string(offset) + " + " + std::to_string(size) + " > " + std::to_string(m_bufferCache->length()));
|
|
}
|
|
|
|
if (!m_bufferCache)
|
|
{
|
|
debug_printf("MetalMemoryManager::UploadToBufferCache: buffer cache not initialized\n");
|
|
return;
|
|
}
|
|
|
|
memcpy((uint8*)m_bufferCache->contents() + offset, data, size);
|
|
|
|
// Notify vertex buffer cache about the change
|
|
m_vertexBufferCache.MemoryRangeChanged(offset, size);
|
|
}
|
|
|
|
void MetalMemoryManager::CopyBufferCache(size_t srcOffset, size_t dstOffset, size_t size)
|
|
{
|
|
if (!m_bufferCache)
|
|
{
|
|
debug_printf("MetalMemoryManager::CopyBufferCache: buffer cache not initialized\n");
|
|
return;
|
|
}
|
|
|
|
memcpy((uint8*)m_bufferCache->contents() + dstOffset, (uint8*)m_bufferCache->contents() + srcOffset, size);
|
|
}
|