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(); 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); 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() bool LatteTextureReadbackInfoMtl::IsFinished()
{ {
// TODO: implement // HACK: just return true for now, otherwise the game would freeze
//return m_mtlr->CommandBufferCompleted(m_commandBuffer);
return true; return true;
} }
void LatteTextureReadbackInfoMtl::ForceFinish() void LatteTextureReadbackInfoMtl::ForceFinish()
{ {
// TODO: implement m_mtlr->WaitForCommandBufferCompletion(m_commandBuffer);
} }
uint8* LatteTextureReadbackInfoMtl::GetData() uint8* LatteTextureReadbackInfoMtl::GetData()

View File

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

View File

@ -15,9 +15,12 @@
#include "Cafe/HW/Latte/Core/LatteShader.h" #include "Cafe/HW/Latte/Core/LatteShader.h"
#include "Cafe/HW/Latte/Core/LatteIndices.h" #include "Cafe/HW/Latte/Core/LatteIndices.h"
#include "Cemu/Logging/CemuDebugLogging.h" #include "Cemu/Logging/CemuDebugLogging.h"
#include "Common/precompiled.h"
#include "Metal/MTLPixelFormat.hpp" #include "Metal/MTLPixelFormat.hpp"
#include "gui/guiWrapper.h" #include "gui/guiWrapper.h"
#define COMMIT_TRESHOLD 256
extern bool hasValidFramebufferAttached; extern bool hasValidFramebufferAttached;
float supportBufferData[512 * 4]; float supportBufferData[512 * 4];
@ -113,7 +116,7 @@ MetalRenderer::~MetalRenderer()
m_device->release(); 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) void MetalRenderer::InitializeLayer(const Vector2i& size, bool mainWindow)
{ {
const auto& windowInfo = gui_getWindowInfo().window_main; const auto& windowInfo = gui_getWindowInfo().window_main;
@ -168,19 +171,23 @@ void MetalRenderer::DrawEmptyFrame(bool mainWindow)
void MetalRenderer::SwapBuffers(bool swapTV, bool swapDRC) void MetalRenderer::SwapBuffers(bool swapTV, bool swapDRC)
{ {
EndEncoding();
if (m_drawable) if (m_drawable)
{ {
EnsureCommandBuffer(); auto commandBuffer = GetCommandBuffer();
m_commandBuffer->presentDrawable(m_drawable); commandBuffer->presentDrawable(m_drawable);
} else }
else
{ {
debug_printf("skipped present!\n"); debug_printf("skipped present!\n");
} }
m_drawable = nullptr; m_drawable = nullptr;
// Release all the command buffers
CommitCommandBuffer(); CommitCommandBuffer();
for (uint32 i = 0; i < m_commandBuffers.size(); i++)
m_commandBuffers[i].m_commandBuffer->release();
m_commandBuffers.clear();
// Reset temporary buffers // Reset temporary buffers
m_memoryManager->ResetTemporaryBuffers(); m_memoryManager->ResetTemporaryBuffers();
@ -223,18 +230,20 @@ bool MetalRenderer::BeginFrame(bool mainWindow)
void MetalRenderer::Flush(bool waitIdle) void MetalRenderer::Flush(bool waitIdle)
{ {
// TODO: should we? // TODO: commit if commit on idle is requested
CommitCommandBuffer(); if (m_recordedDrawcalls > 0)
CommitCommandBuffer();
if (waitIdle) if (waitIdle)
{ {
// TODO // TODO: shouldn't we wait for all command buffers?
WaitForCommandBufferCompletion(GetCurrentCommandBuffer());
} }
} }
void MetalRenderer::NotifyLatteCommandProcessorIdle() void MetalRenderer::NotifyLatteCommandProcessorIdle()
{ {
// TODO: should we? // TODO: commit if commit on idle is requested
CommitCommandBuffer(); //CommitCommandBuffer();
} }
void MetalRenderer::AppendOverlayDebugInfo() 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) 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 // scale copy size to effective size
sint32 effectiveCopyWidth = width; sint32 effectiveCopyWidth = width;
@ -809,11 +818,15 @@ void MetalRenderer::draw_endSequence()
if (pixelShader) if (pixelShader)
LatteRenderTarget_trackUpdates(); LatteRenderTarget_trackUpdates();
bool hasReadback = LatteTextureReadback_Update(); bool hasReadback = LatteTextureReadback_Update();
//m_recordedDrawcalls++; m_recordedDrawcalls++;
//if (m_recordedDrawcalls >= m_submitThreshold || hasReadback) // The number of draw calls needs to twice as big, since we are interrupting the render pass
//{ if (m_recordedDrawcalls >= COMMIT_TRESHOLD * 2 || hasReadback)
// SubmitCommandBuffer(); {
//} CommitCommandBuffer();
// TODO: where should this be called?
LatteTextureReadback_UpdateFinishedTransfers(false);
}
} }
void* MetalRenderer::indexData_reserveIndexMemory(uint32 size, uint32& offset, uint32& bufferIndex) 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 // 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 // Debug
//m_commandQueue->insertDebugCaptureBoundary(); //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 // 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) 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 // Check if we need to begin a new render pass
if (m_commandEncoder) if (m_commandEncoder)
{ {
@ -881,6 +910,8 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
EndEncoding(); EndEncoding();
} }
auto commandBuffer = GetCommandBuffer();
// Update state // Update state
m_state.m_lastUsedFBO = m_state.m_activeFBO; m_state.m_lastUsedFBO = m_state.m_activeFBO;
for (uint8 i = 0; i < 8; i++) for (uint8 i = 0; i < 8; i++)
@ -889,7 +920,7 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
} }
m_state.m_depthRenderTarget = depthRenderTarget; m_state.m_depthRenderTarget = depthRenderTarget;
auto renderCommandEncoder = m_commandBuffer->renderCommandEncoder(renderPassDescriptor); auto renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor);
m_commandEncoder = renderCommandEncoder; m_commandEncoder = renderCommandEncoder;
m_encoderType = MetalEncoderType::Render; m_encoderType = MetalEncoderType::Render;
@ -914,7 +945,9 @@ MTL::ComputeCommandEncoder* MetalRenderer::GetComputeCommandEncoder()
EndEncoding(); EndEncoding();
} }
auto computeCommandEncoder = m_commandBuffer->computeCommandEncoder(); auto commandBuffer = GetCommandBuffer();
auto computeCommandEncoder = commandBuffer->computeCommandEncoder();
m_commandEncoder = computeCommandEncoder; m_commandEncoder = computeCommandEncoder;
m_encoderType = MetalEncoderType::Compute; m_encoderType = MetalEncoderType::Compute;
@ -933,7 +966,9 @@ MTL::BlitCommandEncoder* MetalRenderer::GetBlitCommandEncoder()
EndEncoding(); EndEncoding();
} }
auto blitCommandEncoder = m_commandBuffer->blitCommandEncoder(); auto commandBuffer = GetCommandBuffer();
auto blitCommandEncoder = commandBuffer->blitCommandEncoder();
m_commandEncoder = blitCommandEncoder; m_commandEncoder = blitCommandEncoder;
m_encoderType = MetalEncoderType::Blit; m_encoderType = MetalEncoderType::Blit;
@ -942,30 +977,35 @@ MTL::BlitCommandEncoder* MetalRenderer::GetBlitCommandEncoder()
void MetalRenderer::EndEncoding() void MetalRenderer::EndEncoding()
{ {
if (m_commandEncoder) if (m_encoderType != MetalEncoderType::None)
{ {
m_commandEncoder->endEncoding(); m_commandEncoder->endEncoding();
m_commandEncoder->release(); m_commandEncoder->release();
m_commandEncoder = nullptr;
m_encoderType = MetalEncoderType::None; m_encoderType = MetalEncoderType::None;
// Commit the command buffer if enough draw calls have been recorded
if (m_recordedDrawcalls >= COMMIT_TRESHOLD)
CommitCommandBuffer();
} }
} }
void MetalRenderer::CommitCommandBuffer() void MetalRenderer::CommitCommandBuffer()
{ {
EndEncoding(); m_recordedDrawcalls = 0;
if (m_commandBuffer) if (m_commandBuffers.size() != 0)
{ {
m_commandBuffer->commit(); EndEncoding();
m_commandBuffer->release();
m_commandBuffer = nullptr;
// TODO: where should this be called? auto& commandBuffer = m_commandBuffers.back();
LatteTextureReadback_UpdateFinishedTransfers(false); if (!commandBuffer.m_commited)
{
commandBuffer.m_commandBuffer->commit();
commandBuffer.m_commited = true;
// Debug // Debug
//m_commandQueue->insertDebugCaptureBoundary(); //m_commandQueue->insertDebugCaptureBoundary();
}
} }
} }

View File

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