From 8a8037377fb122522c6099194c634d42189b7b94 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 16 Nov 2024 15:28:23 +0100 Subject: [PATCH] rework the command buffer system --- .../Metal/LatteTextureReadbackMtl.cpp | 2 +- .../Renderer/Metal/MetalBufferAllocator.h | 42 ++++------------- .../HW/Latte/Renderer/Metal/MetalQuery.cpp | 12 ++--- .../HW/Latte/Renderer/Metal/MetalRenderer.cpp | 47 +++++++++++++++---- .../HW/Latte/Renderer/Metal/MetalRenderer.h | 17 ++++++- 5 files changed, 66 insertions(+), 54 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp index ca4e31a7..05b579e7 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp @@ -1,7 +1,7 @@ #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.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() { diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalBufferAllocator.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalBufferAllocator.h index 7a152596..209b1395 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalBufferAllocator.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalBufferAllocator.h @@ -201,8 +201,6 @@ public: void EndFrame() { - CheckForCompletedCommandBuffers(); - // Unlock all buffers 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{})); cemu_assert_debug(result.second); m_activeCommandBufferIt = result.first; - commandBuffer->retain(); } 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 CheckForCompletedCommandBuffers(/*MTL::CommandBuffer* commandBuffer, bool erase = true*/) + void CommandBufferFinished(MTL::CommandBuffer* commandBuffer) { - bool atLeastOneCompleted = false; - for (auto it = m_executingCommandBuffers.begin(); it != m_executingCommandBuffers.end();) + auto it = m_executingCommandBuffers.find(commandBuffer); + for (auto bufferIndex : it->second) { - if (CommandBufferCompleted(it->first)) - { - for (auto bufferIndex : it->second) - { - auto& buffer = m_buffers[bufferIndex]; - buffer.m_data.m_commandBufferCount--; + auto& buffer = m_buffers[bufferIndex]; + buffer.m_data.m_commandBufferCount--; - // TODO: is this neccessary? - if (!buffer.m_data.IsLocked() && buffer.m_data.m_commandBufferCount == 0) - FreeBuffer(bufferIndex); - } - - it->first->release(); - - it = m_executingCommandBuffers.erase(it); - - atLeastOneCompleted = true; - } - else - { - ++it; - } + // TODO: is this neccessary? + if (!buffer.m_data.IsLocked() && buffer.m_data.m_commandBufferCount == 0) + FreeBuffer(bufferIndex); } - if (atLeastOneCompleted) - LatteIndices_invalidateAll(); - - //if (erase) - // m_commandBuffersFrames.erase(commandBuffer); + m_executingCommandBuffers.erase(it); } MTL::Buffer* GetBuffer(uint32 bufferIndex) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalQuery.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalQuery.cpp index 5a60d4ea..ee79f2dd 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalQuery.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalQuery.cpp @@ -3,13 +3,7 @@ bool LatteQueryObjectMtl::getResult(uint64& numSamplesPassed) { - if (!m_commandBuffer) - { - numSamplesPassed = 0; - return true; - } - - if (!CommandBufferCompleted(m_commandBuffer)) + if (m_commandBuffer && !CommandBufferCompleted(m_commandBuffer)) return false; uint64* resultPtr = m_mtlr->GetOcclusionQueryResultsPtr(); @@ -38,7 +32,7 @@ void LatteQueryObjectMtl::end() m_range.end = m_mtlr->GetOcclusionQueryIndex(); m_mtlr->EndOcclusionQuery(); - m_commandBuffer = m_mtlr->GetCurrentCommandBuffer()->retain(); - if (m_mtlr->IsCommandBufferActive()) + m_commandBuffer = m_mtlr->GetAndRetainCurrentCommandBufferIfNotCompleted(); + if (m_commandBuffer) m_mtlr->RequestSoonCommit(); } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index ef1c0d71..3f7c46da 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -20,6 +20,7 @@ #include "Cemu/Logging/CemuLogging.h" #include "Cafe/HW/Latte/Core/FetchShader.h" #include "Cafe/HW/Latte/Core/LatteConst.h" +#include "HW/Latte/Renderer/Metal/MetalCommon.h" #include "config/CemuConfig.h" #include "gui/guiWrapper.h" @@ -398,12 +399,9 @@ void MetalRenderer::Flush(bool waitIdle) { if (m_recordedDrawcalls > 0 || waitIdle) 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() @@ -1397,13 +1395,12 @@ void MetalRenderer::occlusionQuery_destroy(LatteQueryObject* queryObj) { } void MetalRenderer::occlusionQuery_flush() { - // TODO: wait for all command buffers with occlusion queries? if (m_occlusionQuery.m_lastCommandBuffer) m_occlusionQuery.m_lastCommandBuffer->waitUntilCompleted(); } void MetalRenderer::occlusionQuery_updateState() { - // TODO: implement + ProcessFinishedCommandBuffers(); } void MetalRenderer::SetBuffer(MTL::RenderCommandEncoder* renderCommandEncoder, MetalShaderType shaderType, MTL::Buffer* buffer, size_t offset, uint32 index) @@ -1686,6 +1683,9 @@ void MetalRenderer::CommitCommandBuffer() EndEncoding(); + ProcessFinishedCommandBuffers(); + + // Commit the command buffer if (!m_currentCommandBuffer.m_commited) { // Handled differently, since it seems like Metal doesn't always call the completion handler @@ -1695,12 +1695,14 @@ void MetalRenderer::CommitCommandBuffer() // Signal event 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(); - m_currentCommandBuffer.m_commandBuffer->release(); + mtlCommandBuffer->commit(); m_currentCommandBuffer.m_commited = true; + m_executingCommandBuffers.push_back(mtlCommandBuffer); + m_memoryManager->GetTemporaryBufferAllocator().SetActiveCommandBuffer(nullptr); // 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) { auto& layer = GetLayer(mainWindow); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index c272c729..2f851489 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -289,6 +289,15 @@ public: 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() { m_commitTreshold = m_recordedDrawcalls + 8; @@ -337,6 +346,7 @@ public: MTL::BlitCommandEncoder* GetBlitCommandEncoder(); void EndEncoding(); void CommitCommandBuffer(); + void ProcessFinishedCommandBuffers(); bool AcquireDrawable(bool mainWindow); @@ -428,9 +438,13 @@ public: void EndOcclusionQuery() { m_occlusionQuery.m_active = false; + + // Release the old command buffer if (m_occlusionQuery.m_lastCommandBuffer) m_occlusionQuery.m_lastCommandBuffer->release(); - m_occlusionQuery.m_lastCommandBuffer = GetCurrentCommandBuffer()->retain(); + + // Get and retain the current command buffer + m_occlusionQuery.m_lastCommandBuffer = GetAndRetainCurrentCommandBufferIfNotCompleted(); } private: @@ -491,6 +505,7 @@ private: // Active objects MetalCommandBuffer m_currentCommandBuffer{}; + std::vector m_executingCommandBuffers; MetalEncoderType m_encoderType = MetalEncoderType::None; MTL::CommandEncoder* m_commandEncoder = nullptr;