rework the command buffer system

This commit is contained in:
Samuliak 2024-11-16 15:28:23 +01:00
parent 2890819118
commit 8a8037377f
No known key found for this signature in database
5 changed files with 66 additions and 54 deletions

View File

@ -1,7 +1,7 @@
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h"
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h"
#include "HW/Latte/Renderer/Metal/LatteToMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
LatteTextureReadbackInfoMtl::~LatteTextureReadbackInfoMtl() LatteTextureReadbackInfoMtl::~LatteTextureReadbackInfoMtl()
{ {

View File

@ -201,8 +201,6 @@ public:
void EndFrame() void EndFrame()
{ {
CheckForCompletedCommandBuffers();
// Unlock all buffers // Unlock all buffers
for (uint32_t i = 0; i < m_buffers.size(); i++) for (uint32_t i = 0; i < m_buffers.size(); i++)
{ {
@ -255,7 +253,6 @@ public:
auto result = m_executingCommandBuffers.emplace(std::make_pair(m_activeCommandBuffer, std::vector<uint32>{})); auto result = m_executingCommandBuffers.emplace(std::make_pair(m_activeCommandBuffer, std::vector<uint32>{}));
cemu_assert_debug(result.second); cemu_assert_debug(result.second);
m_activeCommandBufferIt = result.first; m_activeCommandBufferIt = result.first;
commandBuffer->retain();
} }
else else
{ {
@ -263,41 +260,20 @@ public:
} }
} }
// TODO: this function should be more more lightweight and leave the checking for completion to the caller, but this works fine for now, since there is always only one instance of this class void CommandBufferFinished(MTL::CommandBuffer* commandBuffer)
void CheckForCompletedCommandBuffers(/*MTL::CommandBuffer* commandBuffer, bool erase = true*/)
{ {
bool atLeastOneCompleted = false; auto it = m_executingCommandBuffers.find(commandBuffer);
for (auto it = m_executingCommandBuffers.begin(); it != m_executingCommandBuffers.end();) for (auto bufferIndex : it->second)
{ {
if (CommandBufferCompleted(it->first)) auto& buffer = m_buffers[bufferIndex];
{ buffer.m_data.m_commandBufferCount--;
for (auto bufferIndex : it->second)
{
auto& buffer = m_buffers[bufferIndex];
buffer.m_data.m_commandBufferCount--;
// TODO: is this neccessary? // TODO: is this neccessary?
if (!buffer.m_data.IsLocked() && buffer.m_data.m_commandBufferCount == 0) if (!buffer.m_data.IsLocked() && buffer.m_data.m_commandBufferCount == 0)
FreeBuffer(bufferIndex); FreeBuffer(bufferIndex);
}
it->first->release();
it = m_executingCommandBuffers.erase(it);
atLeastOneCompleted = true;
}
else
{
++it;
}
} }
if (atLeastOneCompleted) m_executingCommandBuffers.erase(it);
LatteIndices_invalidateAll();
//if (erase)
// m_commandBuffersFrames.erase(commandBuffer);
} }
MTL::Buffer* GetBuffer(uint32 bufferIndex) MTL::Buffer* GetBuffer(uint32 bufferIndex)

View File

@ -3,13 +3,7 @@
bool LatteQueryObjectMtl::getResult(uint64& numSamplesPassed) bool LatteQueryObjectMtl::getResult(uint64& numSamplesPassed)
{ {
if (!m_commandBuffer) if (m_commandBuffer && !CommandBufferCompleted(m_commandBuffer))
{
numSamplesPassed = 0;
return true;
}
if (!CommandBufferCompleted(m_commandBuffer))
return false; return false;
uint64* resultPtr = m_mtlr->GetOcclusionQueryResultsPtr(); uint64* resultPtr = m_mtlr->GetOcclusionQueryResultsPtr();
@ -38,7 +32,7 @@ void LatteQueryObjectMtl::end()
m_range.end = m_mtlr->GetOcclusionQueryIndex(); m_range.end = m_mtlr->GetOcclusionQueryIndex();
m_mtlr->EndOcclusionQuery(); m_mtlr->EndOcclusionQuery();
m_commandBuffer = m_mtlr->GetCurrentCommandBuffer()->retain(); m_commandBuffer = m_mtlr->GetAndRetainCurrentCommandBufferIfNotCompleted();
if (m_mtlr->IsCommandBufferActive()) if (m_commandBuffer)
m_mtlr->RequestSoonCommit(); m_mtlr->RequestSoonCommit();
} }

View File

@ -20,6 +20,7 @@
#include "Cemu/Logging/CemuLogging.h" #include "Cemu/Logging/CemuLogging.h"
#include "Cafe/HW/Latte/Core/FetchShader.h" #include "Cafe/HW/Latte/Core/FetchShader.h"
#include "Cafe/HW/Latte/Core/LatteConst.h" #include "Cafe/HW/Latte/Core/LatteConst.h"
#include "HW/Latte/Renderer/Metal/MetalCommon.h"
#include "config/CemuConfig.h" #include "config/CemuConfig.h"
#include "gui/guiWrapper.h" #include "gui/guiWrapper.h"
@ -398,12 +399,9 @@ void MetalRenderer::Flush(bool waitIdle)
{ {
if (m_recordedDrawcalls > 0 || waitIdle) if (m_recordedDrawcalls > 0 || waitIdle)
CommitCommandBuffer(); CommitCommandBuffer();
if (waitIdle)
{
cemu_assert_debug(m_currentCommandBuffer.m_commited);
m_currentCommandBuffer.m_commandBuffer->waitUntilCompleted(); if (waitIdle && m_executingCommandBuffers.size() != 0)
} m_executingCommandBuffers.back()->waitUntilCompleted();
} }
void MetalRenderer::NotifyLatteCommandProcessorIdle() void MetalRenderer::NotifyLatteCommandProcessorIdle()
@ -1397,13 +1395,12 @@ void MetalRenderer::occlusionQuery_destroy(LatteQueryObject* queryObj) {
} }
void MetalRenderer::occlusionQuery_flush() { void MetalRenderer::occlusionQuery_flush() {
// TODO: wait for all command buffers with occlusion queries?
if (m_occlusionQuery.m_lastCommandBuffer) if (m_occlusionQuery.m_lastCommandBuffer)
m_occlusionQuery.m_lastCommandBuffer->waitUntilCompleted(); m_occlusionQuery.m_lastCommandBuffer->waitUntilCompleted();
} }
void MetalRenderer::occlusionQuery_updateState() { void MetalRenderer::occlusionQuery_updateState() {
// TODO: implement ProcessFinishedCommandBuffers();
} }
void MetalRenderer::SetBuffer(MTL::RenderCommandEncoder* renderCommandEncoder, MetalShaderType shaderType, MTL::Buffer* buffer, size_t offset, uint32 index) void MetalRenderer::SetBuffer(MTL::RenderCommandEncoder* renderCommandEncoder, MetalShaderType shaderType, MTL::Buffer* buffer, size_t offset, uint32 index)
@ -1686,6 +1683,9 @@ void MetalRenderer::CommitCommandBuffer()
EndEncoding(); EndEncoding();
ProcessFinishedCommandBuffers();
// Commit the command buffer
if (!m_currentCommandBuffer.m_commited) if (!m_currentCommandBuffer.m_commited)
{ {
// Handled differently, since it seems like Metal doesn't always call the completion handler // Handled differently, since it seems like Metal doesn't always call the completion handler
@ -1695,12 +1695,14 @@ void MetalRenderer::CommitCommandBuffer()
// Signal event // Signal event
m_eventValue = (m_eventValue + 1) % EVENT_VALUE_WRAP; m_eventValue = (m_eventValue + 1) % EVENT_VALUE_WRAP;
m_currentCommandBuffer.m_commandBuffer->encodeSignalEvent(m_event, m_eventValue); auto mtlCommandBuffer = m_currentCommandBuffer.m_commandBuffer;
mtlCommandBuffer->encodeSignalEvent(m_event, m_eventValue);
m_currentCommandBuffer.m_commandBuffer->commit(); mtlCommandBuffer->commit();
m_currentCommandBuffer.m_commandBuffer->release();
m_currentCommandBuffer.m_commited = true; m_currentCommandBuffer.m_commited = true;
m_executingCommandBuffers.push_back(mtlCommandBuffer);
m_memoryManager->GetTemporaryBufferAllocator().SetActiveCommandBuffer(nullptr); m_memoryManager->GetTemporaryBufferAllocator().SetActiveCommandBuffer(nullptr);
// Debug // Debug
@ -1708,6 +1710,31 @@ void MetalRenderer::CommitCommandBuffer()
} }
} }
void MetalRenderer::ProcessFinishedCommandBuffers()
{
// Check for finished command buffers
bool atLeastOneCompleted = false;
for (auto it = m_executingCommandBuffers.begin(); it != m_executingCommandBuffers.end();)
{
auto commandBuffer = *it;
if (CommandBufferCompleted(commandBuffer))
{
m_memoryManager->GetTemporaryBufferAllocator().CommandBufferFinished(commandBuffer);
commandBuffer->release();
it = m_executingCommandBuffers.erase(it);
atLeastOneCompleted = true;
}
else
{
++it;
}
}
// Invalidate indices if at least one command buffer has completed
if (atLeastOneCompleted)
LatteIndices_invalidateAll();
}
bool MetalRenderer::AcquireDrawable(bool mainWindow) bool MetalRenderer::AcquireDrawable(bool mainWindow)
{ {
auto& layer = GetLayer(mainWindow); auto& layer = GetLayer(mainWindow);

View File

@ -289,6 +289,15 @@ public:
return m_currentCommandBuffer.m_commandBuffer; return m_currentCommandBuffer.m_commandBuffer;
} }
MTL::CommandBuffer* GetAndRetainCurrentCommandBufferIfNotCompleted()
{
// The command buffer has been commited and has finished execution
if (m_currentCommandBuffer.m_commited && m_executingCommandBuffers.size() == 0)
return nullptr;
return GetCurrentCommandBuffer()->retain();
}
void RequestSoonCommit() void RequestSoonCommit()
{ {
m_commitTreshold = m_recordedDrawcalls + 8; m_commitTreshold = m_recordedDrawcalls + 8;
@ -337,6 +346,7 @@ public:
MTL::BlitCommandEncoder* GetBlitCommandEncoder(); MTL::BlitCommandEncoder* GetBlitCommandEncoder();
void EndEncoding(); void EndEncoding();
void CommitCommandBuffer(); void CommitCommandBuffer();
void ProcessFinishedCommandBuffers();
bool AcquireDrawable(bool mainWindow); bool AcquireDrawable(bool mainWindow);
@ -428,9 +438,13 @@ public:
void EndOcclusionQuery() void EndOcclusionQuery()
{ {
m_occlusionQuery.m_active = false; m_occlusionQuery.m_active = false;
// Release the old command buffer
if (m_occlusionQuery.m_lastCommandBuffer) if (m_occlusionQuery.m_lastCommandBuffer)
m_occlusionQuery.m_lastCommandBuffer->release(); m_occlusionQuery.m_lastCommandBuffer->release();
m_occlusionQuery.m_lastCommandBuffer = GetCurrentCommandBuffer()->retain();
// Get and retain the current command buffer
m_occlusionQuery.m_lastCommandBuffer = GetAndRetainCurrentCommandBufferIfNotCompleted();
} }
private: private:
@ -491,6 +505,7 @@ private:
// Active objects // Active objects
MetalCommandBuffer m_currentCommandBuffer{}; MetalCommandBuffer m_currentCommandBuffer{};
std::vector<MTL::CommandBuffer*> m_executingCommandBuffers;
MetalEncoderType m_encoderType = MetalEncoderType::None; MetalEncoderType m_encoderType = MetalEncoderType::None;
MTL::CommandEncoder* m_commandEncoder = nullptr; MTL::CommandEncoder* m_commandEncoder = nullptr;