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/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()
{

View File

@ -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<uint32>{}));
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)

View File

@ -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();
}

View File

@ -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);

View File

@ -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<MTL::CommandBuffer*> m_executingCommandBuffers;
MetalEncoderType m_encoderType = MetalEncoderType::None;
MTL::CommandEncoder* m_commandEncoder = nullptr;