use multiple command buffers per frame

This commit is contained in:
Samuliak 2024-08-12 14:27:28 +02:00
parent 34d8076ab6
commit 0c73ff8452
4 changed files with 105 additions and 40 deletions

View File

@ -23,18 +23,22 @@ void LatteTextureReadbackInfoMtl::StartTransfer()
auto blitCommandEncoder = m_mtlr->GetBlitCommandEncoder();
blitCommandEncoder->copyFromTexture(baseTexture->GetTexture(), 0, 0, MTL::Origin{0, 0, 0}, MTL::Size{(uint32)baseTexture->width, (uint32)baseTexture->height, 1}, m_mtlr->GetTextureReadbackBuffer(), m_bufferOffset, bytesPerRow, bytesPerImage);
m_commandBuffer = m_mtlr->GetCurrentCommandBuffer();
// TODO: uncomment
//m_mtlr->RequestSoonCommit();
}
bool LatteTextureReadbackInfoMtl::IsFinished()
{
// TODO: implement
// HACK: just return true for now, otherwise the game would freeze
//return m_mtlr->CommandBufferCompleted(m_commandBuffer);
return true;
}
void LatteTextureReadbackInfoMtl::ForceFinish()
{
// TODO: implement
m_mtlr->WaitForCommandBufferCompletion(m_commandBuffer);
}
uint8* LatteTextureReadbackInfoMtl::GetData()

View File

@ -1,5 +1,6 @@
#pragma once
#include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h"
#include "Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h"
class LatteTextureReadbackInfoMtl : public LatteTextureReadbackInfo
@ -18,5 +19,7 @@ public:
private:
class MetalRenderer* m_mtlr;
MTL::CommandBuffer* m_commandBuffer = nullptr;
uint32 m_bufferOffset = 0;
};

View File

@ -15,9 +15,12 @@
#include "Cafe/HW/Latte/Core/LatteShader.h"
#include "Cafe/HW/Latte/Core/LatteIndices.h"
#include "Cemu/Logging/CemuDebugLogging.h"
#include "Common/precompiled.h"
#include "Metal/MTLPixelFormat.hpp"
#include "gui/guiWrapper.h"
#define COMMIT_TRESHOLD 256
extern bool hasValidFramebufferAttached;
float supportBufferData[512 * 4];
@ -113,7 +116,7 @@ MetalRenderer::~MetalRenderer()
m_device->release();
}
// TODO: don't ignore "mainWindow" argument
// TODO: don't ignore "mainWindow" argument and respect size
void MetalRenderer::InitializeLayer(const Vector2i& size, bool mainWindow)
{
const auto& windowInfo = gui_getWindowInfo().window_main;
@ -168,19 +171,23 @@ void MetalRenderer::DrawEmptyFrame(bool mainWindow)
void MetalRenderer::SwapBuffers(bool swapTV, bool swapDRC)
{
EndEncoding();
if (m_drawable)
{
EnsureCommandBuffer();
m_commandBuffer->presentDrawable(m_drawable);
} else
auto commandBuffer = GetCommandBuffer();
commandBuffer->presentDrawable(m_drawable);
}
else
{
debug_printf("skipped present!\n");
}
m_drawable = nullptr;
// Release all the command buffers
CommitCommandBuffer();
for (uint32 i = 0; i < m_commandBuffers.size(); i++)
m_commandBuffers[i].m_commandBuffer->release();
m_commandBuffers.clear();
// Reset temporary buffers
m_memoryManager->ResetTemporaryBuffers();
@ -223,18 +230,20 @@ bool MetalRenderer::BeginFrame(bool mainWindow)
void MetalRenderer::Flush(bool waitIdle)
{
// TODO: should we?
// TODO: commit if commit on idle is requested
if (m_recordedDrawcalls > 0)
CommitCommandBuffer();
if (waitIdle)
{
// TODO
// TODO: shouldn't we wait for all command buffers?
WaitForCommandBufferCompletion(GetCurrentCommandBuffer());
}
}
void MetalRenderer::NotifyLatteCommandProcessorIdle()
{
// TODO: should we?
CommitCommandBuffer();
// TODO: commit if commit on idle is requested
//CommitCommandBuffer();
}
void MetalRenderer::AppendOverlayDebugInfo()
@ -452,7 +461,7 @@ LatteTextureReadbackInfo* MetalRenderer::texture_createReadback(LatteTextureView
void MetalRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* sourceTexture, sint32 srcMip, sint32 srcSlice, LatteTexture* destinationTexture, sint32 dstMip, sint32 dstSlice, sint32 width, sint32 height)
{
EnsureCommandBuffer();
GetCommandBuffer();
// scale copy size to effective size
sint32 effectiveCopyWidth = width;
@ -809,11 +818,15 @@ void MetalRenderer::draw_endSequence()
if (pixelShader)
LatteRenderTarget_trackUpdates();
bool hasReadback = LatteTextureReadback_Update();
//m_recordedDrawcalls++;
//if (m_recordedDrawcalls >= m_submitThreshold || hasReadback)
//{
// SubmitCommandBuffer();
//}
m_recordedDrawcalls++;
// The number of draw calls needs to twice as big, since we are interrupting the render pass
if (m_recordedDrawcalls >= COMMIT_TRESHOLD * 2 || hasReadback)
{
CommitCommandBuffer();
// TODO: where should this be called?
LatteTextureReadback_UpdateFinishedTransfers(false);
}
}
void* MetalRenderer::indexData_reserveIndexMemory(uint32 size, uint32& offset, uint32& bufferIndex)
@ -830,22 +843,38 @@ void MetalRenderer::indexData_uploadIndexMemory(uint32 offset, uint32 size)
// Do nothing, since the buffer has shared storage mode
}
void MetalRenderer::EnsureCommandBuffer()
MTL::CommandBuffer* MetalRenderer::GetCommandBuffer()
{
if (!m_commandBuffer)
bool needsNewCommandBuffer = (m_commandBuffers.empty() || m_commandBuffers.back().m_commited);
if (needsNewCommandBuffer)
{
// Debug
//m_commandQueue->insertDebugCaptureBoundary();
m_commandBuffer = m_commandQueue->commandBuffer();
MTL::CommandBuffer* mtlCommandBuffer = m_commandQueue->commandBuffer();
m_commandBuffers.push_back({mtlCommandBuffer});
return mtlCommandBuffer;
}
else
{
return m_commandBuffers.back().m_commandBuffer;
}
}
bool MetalRenderer::CommandBufferCompleted(MTL::CommandBuffer* commandBuffer)
{
return commandBuffer->status() == MTL::CommandBufferStatusCompleted;
}
void MetalRenderer::WaitForCommandBufferCompletion(MTL::CommandBuffer* commandBuffer)
{
commandBuffer->waitUntilCompleted();
}
// Some render passes clear the attachments, forceRecreate is supposed to be used in those cases
MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate, bool rebindStateIfNewEncoder)
{
EnsureCommandBuffer();
// Check if we need to begin a new render pass
if (m_commandEncoder)
{
@ -881,6 +910,8 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
EndEncoding();
}
auto commandBuffer = GetCommandBuffer();
// Update state
m_state.m_lastUsedFBO = m_state.m_activeFBO;
for (uint8 i = 0; i < 8; i++)
@ -889,7 +920,7 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
}
m_state.m_depthRenderTarget = depthRenderTarget;
auto renderCommandEncoder = m_commandBuffer->renderCommandEncoder(renderPassDescriptor);
auto renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor);
m_commandEncoder = renderCommandEncoder;
m_encoderType = MetalEncoderType::Render;
@ -914,7 +945,9 @@ MTL::ComputeCommandEncoder* MetalRenderer::GetComputeCommandEncoder()
EndEncoding();
}
auto computeCommandEncoder = m_commandBuffer->computeCommandEncoder();
auto commandBuffer = GetCommandBuffer();
auto computeCommandEncoder = commandBuffer->computeCommandEncoder();
m_commandEncoder = computeCommandEncoder;
m_encoderType = MetalEncoderType::Compute;
@ -933,7 +966,9 @@ MTL::BlitCommandEncoder* MetalRenderer::GetBlitCommandEncoder()
EndEncoding();
}
auto blitCommandEncoder = m_commandBuffer->blitCommandEncoder();
auto commandBuffer = GetCommandBuffer();
auto blitCommandEncoder = commandBuffer->blitCommandEncoder();
m_commandEncoder = blitCommandEncoder;
m_encoderType = MetalEncoderType::Blit;
@ -942,32 +977,37 @@ MTL::BlitCommandEncoder* MetalRenderer::GetBlitCommandEncoder()
void MetalRenderer::EndEncoding()
{
if (m_commandEncoder)
if (m_encoderType != MetalEncoderType::None)
{
m_commandEncoder->endEncoding();
m_commandEncoder->release();
m_commandEncoder = nullptr;
m_encoderType = MetalEncoderType::None;
// Commit the command buffer if enough draw calls have been recorded
if (m_recordedDrawcalls >= COMMIT_TRESHOLD)
CommitCommandBuffer();
}
}
void MetalRenderer::CommitCommandBuffer()
{
m_recordedDrawcalls = 0;
if (m_commandBuffers.size() != 0)
{
EndEncoding();
if (m_commandBuffer)
auto& commandBuffer = m_commandBuffers.back();
if (!commandBuffer.m_commited)
{
m_commandBuffer->commit();
m_commandBuffer->release();
m_commandBuffer = nullptr;
// TODO: where should this be called?
LatteTextureReadback_UpdateFinishedTransfers(false);
commandBuffer.m_commandBuffer->commit();
commandBuffer.m_commited = true;
// Debug
//m_commandQueue->insertDebugCaptureBoundary();
}
}
}
bool MetalRenderer::AcquireNextDrawable(bool mainWindow)
{

View File

@ -7,6 +7,8 @@
#include "Cafe/HW/Latte/Renderer/Renderer.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h"
#include "Common/precompiled.h"
#include "Metal/MTLCommandBuffer.hpp"
#define MAX_MTL_BUFFERS 31
#define GET_MTL_VERTEX_BUFFER_INDEX(index) (MAX_MTL_BUFFERS - index - 2)
@ -47,6 +49,12 @@ struct MetalState
MTL::ScissorRect m_scissor = {0, 0, 0, 0};
};
struct MetalCommandBuffer
{
MTL::CommandBuffer* m_commandBuffer;
bool m_commited = false;
};
enum class MetalEncoderType
{
None,
@ -231,7 +239,16 @@ public:
}
// Helpers
void EnsureCommandBuffer();
MTL::CommandBuffer* GetCurrentCommandBuffer()
{
cemu_assert_debug(m_commandBuffers.size() != 0);
return m_commandBuffers[m_commandBuffers.size() - 1].m_commandBuffer;
}
MTL::CommandBuffer* GetCommandBuffer();
bool CommandBufferCompleted(MTL::CommandBuffer* commandBuffer);
void WaitForCommandBufferCompletion(MTL::CommandBuffer* commandBuffer);
MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate = false, bool rebindStateIfNewEncoder = true);
MTL::ComputeCommandEncoder* GetComputeCommandEncoder();
MTL::BlitCommandEncoder* GetBlitCommandEncoder();
@ -280,7 +297,8 @@ private:
MTL::Buffer* m_xfbRingBuffer;
// Active objects
MTL::CommandBuffer* m_commandBuffer = nullptr;
std::vector<MetalCommandBuffer> m_commandBuffers;
uint32 m_recordedDrawcalls = 0;
MetalEncoderType m_encoderType = MetalEncoderType::None;
MTL::CommandEncoder* m_commandEncoder = nullptr;
CA::MetalDrawable* m_drawable = nullptr;