mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-01-08 08:00:44 +01:00
fix: buffer allocator leaks
This commit is contained in:
parent
b7f88d093a
commit
cd8b74ba32
@ -147,7 +147,7 @@ typedef MetalBufferAllocator<MetalBuffer> MetalDefaultBufferAllocator;
|
|||||||
struct MetalSyncedBuffer
|
struct MetalSyncedBuffer
|
||||||
{
|
{
|
||||||
MTL::Buffer* m_buffer;
|
MTL::Buffer* m_buffer;
|
||||||
std::vector<uint32> m_commandBuffers;
|
std::vector<MTL::CommandBuffer*> m_commandBuffers;
|
||||||
uint32 m_lock = 0;
|
uint32 m_lock = 0;
|
||||||
|
|
||||||
bool IsLocked() const
|
bool IsLocked() const
|
||||||
@ -156,7 +156,7 @@ struct MetalSyncedBuffer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//constexpr uint16 MAX_COMMAND_BUFFER_FRAMES = 1024;
|
constexpr uint16 MAX_COMMAND_BUFFER_FRAMES = 8;
|
||||||
|
|
||||||
class MetalTemporaryBufferAllocator : public MetalBufferAllocator<MetalSyncedBuffer>
|
class MetalTemporaryBufferAllocator : public MetalBufferAllocator<MetalSyncedBuffer>
|
||||||
{
|
{
|
||||||
@ -177,7 +177,7 @@ public:
|
|||||||
// TODO: is this really necessary?
|
// TODO: is this really necessary?
|
||||||
// Release the buffer if it wasn't released due to the lock
|
// Release the buffer if it wasn't released due to the lock
|
||||||
if (!buffer.IsLocked() && buffer.m_commandBuffers.empty())
|
if (!buffer.IsLocked() && buffer.m_commandBuffers.empty())
|
||||||
m_freeBufferRanges.push_back({bufferIndex, 0, buffer.m_buffer->length()});
|
FreeBuffer(bufferIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnlockAllBuffers()
|
void UnlockAllBuffers()
|
||||||
@ -189,7 +189,7 @@ public:
|
|||||||
if (buffer.m_lock != 0)
|
if (buffer.m_lock != 0)
|
||||||
{
|
{
|
||||||
if (buffer.m_commandBuffers.empty())
|
if (buffer.m_commandBuffers.empty())
|
||||||
m_freeBufferRanges.push_back({i, 0, buffer.m_buffer->length()});
|
FreeBuffer(i);
|
||||||
|
|
||||||
buffer.m_lock = 0;
|
buffer.m_lock = 0;
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ public:
|
|||||||
|
|
||||||
if (it->second > MAX_COMMAND_BUFFER_FRAMES)
|
if (it->second > MAX_COMMAND_BUFFER_FRAMES)
|
||||||
{
|
{
|
||||||
debug_printf("command buffer %u remained unfinished for more than %u frames\n", it->first, MAX_COMMAND_BUFFER_FRAMES);
|
debug_printf("command buffer %p remained unfinished for more than %u frames\n", it->first, MAX_COMMAND_BUFFER_FRAMES);
|
||||||
|
|
||||||
// Pretend like the command buffer has finished
|
// Pretend like the command buffer has finished
|
||||||
CommandBufferFinished(it->first, false);
|
CommandBufferFinished(it->first, false);
|
||||||
@ -218,48 +218,39 @@ public:
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetActiveCommandBuffer(uint32 commandBuffer)
|
void SetActiveCommandBuffer(MTL::CommandBuffer* commandBuffer)
|
||||||
{
|
{
|
||||||
m_activeCommandBuffer = commandBuffer;
|
m_activeCommandBuffer = commandBuffer;
|
||||||
|
|
||||||
//if (commandBuffer != INVALID_COMMAND_BUFFER_ID)
|
//if (commandBuffer)
|
||||||
// m_commandBuffersFrames[commandBuffer] = 0;
|
// m_commandBuffersFrames[commandBuffer] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBufferFinished(uint32 commandBuffer/*, bool erase = true*/)
|
void CheckForCompletedCommandBuffers(/*MTL::CommandBuffer* commandBuffer, bool erase = true*/)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < m_buffers.size(); i++)
|
for (uint32_t i = 0; i < m_buffers.size(); i++)
|
||||||
{
|
{
|
||||||
auto& buffer = m_buffers[i];
|
auto& buffer = m_buffers[i];
|
||||||
for (uint32_t j = 0; j < buffer.m_commandBuffers.size(); j++)
|
for (uint32_t j = 0; j < buffer.m_commandBuffers.size(); j++)
|
||||||
{
|
{
|
||||||
if (commandBuffer == buffer.m_commandBuffers[j])
|
if (m_mtlr->CommandBufferCompleted(buffer.m_commandBuffers[j]))
|
||||||
{
|
{
|
||||||
if (buffer.m_commandBuffers.size() == 1)
|
if (buffer.m_commandBuffers.size() == 1)
|
||||||
{
|
{
|
||||||
if (!buffer.IsLocked())
|
if (!buffer.IsLocked())
|
||||||
{
|
{
|
||||||
// First remove any free ranges that use this buffer
|
|
||||||
for (uint32 k = 0; k < m_freeBufferRanges.size(); k++)
|
|
||||||
{
|
|
||||||
if (m_freeBufferRanges[k].bufferIndex == i)
|
|
||||||
{
|
|
||||||
m_freeBufferRanges.erase(m_freeBufferRanges.begin() + k);
|
|
||||||
k--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All command buffers using it have finished execution, we can use it again
|
// All command buffers using it have finished execution, we can use it again
|
||||||
m_freeBufferRanges.push_back({i, 0, buffer.m_buffer->length()});
|
FreeBuffer(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.m_commandBuffers.clear();
|
buffer.m_commandBuffers.clear();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffer.m_commandBuffers.erase(buffer.m_commandBuffers.begin() + j);
|
buffer.m_commandBuffers.erase(buffer.m_commandBuffers.begin() + j);
|
||||||
|
j--;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,10 +261,10 @@ public:
|
|||||||
|
|
||||||
MTL::Buffer* GetBuffer(uint32 bufferIndex)
|
MTL::Buffer* GetBuffer(uint32 bufferIndex)
|
||||||
{
|
{
|
||||||
cemu_assert_debug(m_activeCommandBuffer != INVALID_COMMAND_BUFFER_ID);
|
cemu_assert_debug(m_activeCommandBuffer);
|
||||||
|
|
||||||
auto& buffer = m_buffers[bufferIndex];
|
auto& buffer = m_buffers[bufferIndex];
|
||||||
if (buffer.m_commandBuffers.empty() || buffer.m_commandBuffers.back() != m_activeCommandBuffer)
|
if (buffer.m_commandBuffers.empty() || buffer.m_commandBuffers.back() != m_activeCommandBuffer/*std::find(buffer.m_commandBuffers.begin(), buffer.m_commandBuffers.end(), m_activeCommandBuffer) == buffer.m_commandBuffers.end()*/)
|
||||||
buffer.m_commandBuffers.push_back(m_activeCommandBuffer);
|
buffer.m_commandBuffers.push_back(m_activeCommandBuffer);
|
||||||
|
|
||||||
return buffer.m_buffer;
|
return buffer.m_buffer;
|
||||||
@ -287,7 +278,6 @@ public:
|
|||||||
/*
|
/*
|
||||||
MetalBufferAllocation GetBufferAllocation(size_t size)
|
MetalBufferAllocation GetBufferAllocation(size_t size)
|
||||||
{
|
{
|
||||||
// TODO: remove this
|
|
||||||
if (!m_activeCommandBuffer)
|
if (!m_activeCommandBuffer)
|
||||||
throw std::runtime_error("No active command buffer when allocating a buffer!");
|
throw std::runtime_error("No active command buffer when allocating a buffer!");
|
||||||
|
|
||||||
@ -301,8 +291,54 @@ public:
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private:
|
/*
|
||||||
uint32 m_activeCommandBuffer = INVALID_COMMAND_BUFFER_ID;
|
void LogInfo()
|
||||||
|
{
|
||||||
|
debug_printf("BUFFERS:\n");
|
||||||
|
for (auto& buffer : m_buffers)
|
||||||
|
{
|
||||||
|
debug_printf(" %p -> size: %lu, command buffers: %zu\n", buffer.m_buffer, buffer.m_buffer->length(), buffer.m_commandBuffers.size());
|
||||||
|
uint32 same = 0;
|
||||||
|
uint32 completed = 0;
|
||||||
|
for (uint32 i = 0; i < buffer.m_commandBuffers.size(); i++)
|
||||||
|
{
|
||||||
|
if (m_mtlr->CommandBufferCompleted(buffer.m_commandBuffers[i]))
|
||||||
|
completed++;
|
||||||
|
for (uint32 j = 0; j < buffer.m_commandBuffers.size(); j++)
|
||||||
|
{
|
||||||
|
if (i != j && buffer.m_commandBuffers[i] == buffer.m_commandBuffers[j])
|
||||||
|
same++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_printf(" same: %u\n", same);
|
||||||
|
debug_printf(" completed: %u\n", completed);
|
||||||
|
}
|
||||||
|
|
||||||
//std::map<uint32, uint16> m_commandBuffersFrames;
|
debug_printf("FREE RANGES:\n");
|
||||||
|
for (auto& range : m_freeBufferRanges)
|
||||||
|
{
|
||||||
|
debug_printf(" %u -> offset: %zu, size: %zu\n", range.bufferIndex, range.offset, range.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
private:
|
||||||
|
MTL::CommandBuffer* m_activeCommandBuffer = nullptr;
|
||||||
|
|
||||||
|
//std::map<MTL::CommandBuffer*, uint16> m_commandBuffersFrames;
|
||||||
|
|
||||||
|
void FreeBuffer(uint32 bufferIndex)
|
||||||
|
{
|
||||||
|
// First remove any free ranges that use this buffer
|
||||||
|
for (uint32 k = 0; k < m_freeBufferRanges.size(); k++)
|
||||||
|
{
|
||||||
|
if (m_freeBufferRanges[k].bufferIndex == bufferIndex)
|
||||||
|
{
|
||||||
|
m_freeBufferRanges.erase(m_freeBufferRanges.begin() + k);
|
||||||
|
k--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_freeBufferRanges.push_back({bufferIndex, 0, m_buffers[bufferIndex].m_buffer->length()});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -248,8 +248,8 @@ void MetalRenderer::SwapBuffers(bool swapTV, bool swapDRC)
|
|||||||
// Release all the command buffers
|
// Release all the command buffers
|
||||||
CommitCommandBuffer();
|
CommitCommandBuffer();
|
||||||
// TODO: should this be released here?
|
// TODO: should this be released here?
|
||||||
for (uint32 i = 0; i < m_commandBuffers.size(); i++)
|
//for (uint32 i = 0; i < m_commandBuffers.size(); i++)
|
||||||
m_commandBuffers[i].m_commandBuffer->release();
|
// m_commandBuffers[i].m_commandBuffer->release();
|
||||||
m_commandBuffers.clear();
|
m_commandBuffers.clear();
|
||||||
|
|
||||||
// Release frame persistent buffers
|
// Release frame persistent buffers
|
||||||
@ -257,6 +257,9 @@ void MetalRenderer::SwapBuffers(bool swapTV, bool swapDRC)
|
|||||||
|
|
||||||
// Unlock all temporary buffers
|
// Unlock all temporary buffers
|
||||||
m_memoryManager->GetTemporaryBufferAllocator().UnlockAllBuffers();
|
m_memoryManager->GetTemporaryBufferAllocator().UnlockAllBuffers();
|
||||||
|
|
||||||
|
// Check for completed command buffers
|
||||||
|
m_memoryManager->GetTemporaryBufferAllocator().CheckForCompletedCommandBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use `shader` for drawing
|
// TODO: use `shader` for drawing
|
||||||
@ -1301,13 +1304,10 @@ MTL::CommandBuffer* MetalRenderer::GetCommandBuffer()
|
|||||||
//m_commandQueue->insertDebugCaptureBoundary();
|
//m_commandQueue->insertDebugCaptureBoundary();
|
||||||
|
|
||||||
MTL::CommandBuffer* mtlCommandBuffer = m_commandQueue->commandBuffer();
|
MTL::CommandBuffer* mtlCommandBuffer = m_commandQueue->commandBuffer();
|
||||||
MetalCommandBuffer commandBuffer = {mtlCommandBuffer, m_commandBufferID};
|
m_commandBuffers.push_back({mtlCommandBuffer});
|
||||||
m_commandBuffers.push_back(commandBuffer);
|
|
||||||
|
|
||||||
m_commandBufferID = (m_commandBufferID + 1) % 65536;
|
|
||||||
|
|
||||||
// Notify memory manager about the new command buffer
|
// Notify memory manager about the new command buffer
|
||||||
m_memoryManager->GetTemporaryBufferAllocator().SetActiveCommandBuffer(commandBuffer.m_id);
|
m_memoryManager->GetTemporaryBufferAllocator().SetActiveCommandBuffer(mtlCommandBuffer);
|
||||||
|
|
||||||
return mtlCommandBuffer;
|
return mtlCommandBuffer;
|
||||||
}
|
}
|
||||||
@ -1480,14 +1480,15 @@ void MetalRenderer::CommitCommandBuffer()
|
|||||||
auto& commandBuffer = m_commandBuffers.back();
|
auto& commandBuffer = m_commandBuffers.back();
|
||||||
if (!commandBuffer.m_commited)
|
if (!commandBuffer.m_commited)
|
||||||
{
|
{
|
||||||
commandBuffer.m_commandBuffer->addCompletedHandler(^(MTL::CommandBuffer*) {
|
// Handled differently, since it seems like Metal doesn't always call the completion handler
|
||||||
m_memoryManager->GetTemporaryBufferAllocator().CommandBufferFinished(commandBuffer.m_id);
|
//commandBuffer.m_commandBuffer->addCompletedHandler(^(MTL::CommandBuffer*) {
|
||||||
});
|
// m_memoryManager->GetTemporaryBufferAllocator().CommandBufferFinished(commandBuffer.m_commandBuffer);
|
||||||
|
//});
|
||||||
|
|
||||||
commandBuffer.m_commandBuffer->commit();
|
commandBuffer.m_commandBuffer->commit();
|
||||||
commandBuffer.m_commited = true;
|
commandBuffer.m_commited = true;
|
||||||
|
|
||||||
m_memoryManager->GetTemporaryBufferAllocator().SetActiveCommandBuffer(INVALID_COMMAND_BUFFER_ID);
|
m_memoryManager->GetTemporaryBufferAllocator().SetActiveCommandBuffer(nullptr);
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
//m_commandQueue->insertDebugCaptureBoundary();
|
//m_commandQueue->insertDebugCaptureBoundary();
|
||||||
|
@ -143,12 +143,9 @@ struct MetalState
|
|||||||
struct MetalCommandBuffer
|
struct MetalCommandBuffer
|
||||||
{
|
{
|
||||||
MTL::CommandBuffer* m_commandBuffer;
|
MTL::CommandBuffer* m_commandBuffer;
|
||||||
uint32 m_id;
|
|
||||||
bool m_commited = false;
|
bool m_commited = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32 INVALID_COMMAND_BUFFER_ID = std::numeric_limits<uint32>::max();
|
|
||||||
|
|
||||||
enum class MetalEncoderType
|
enum class MetalEncoderType
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
@ -420,8 +417,6 @@ private:
|
|||||||
|
|
||||||
MetalPerformanceMonitor m_performanceMonitor;
|
MetalPerformanceMonitor m_performanceMonitor;
|
||||||
|
|
||||||
uint32 m_commandBufferID = 0;
|
|
||||||
|
|
||||||
// Metal objects
|
// Metal objects
|
||||||
MTL::Device* m_device;
|
MTL::Device* m_device;
|
||||||
MTL::CommandQueue* m_commandQueue;
|
MTL::CommandQueue* m_commandQueue;
|
||||||
|
Loading…
Reference in New Issue
Block a user