Skip to content

Commit 0c73ff8

Browse files
committed
use multiple command buffers per frame
1 parent 34d8076 commit 0c73ff8

File tree

4 files changed

+105
-40
lines changed

4 files changed

+105
-40
lines changed

src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,22 @@ void LatteTextureReadbackInfoMtl::StartTransfer()
2323
auto blitCommandEncoder = m_mtlr->GetBlitCommandEncoder();
2424

2525
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);
26+
27+
m_commandBuffer = m_mtlr->GetCurrentCommandBuffer();
28+
// TODO: uncomment
29+
//m_mtlr->RequestSoonCommit();
2630
}
2731

2832
bool LatteTextureReadbackInfoMtl::IsFinished()
2933
{
30-
// TODO: implement
31-
34+
// HACK: just return true for now, otherwise the game would freeze
35+
//return m_mtlr->CommandBufferCompleted(m_commandBuffer);
3236
return true;
3337
}
3438

3539
void LatteTextureReadbackInfoMtl::ForceFinish()
3640
{
37-
// TODO: implement
41+
m_mtlr->WaitForCommandBufferCompletion(m_commandBuffer);
3842
}
3943

4044
uint8* LatteTextureReadbackInfoMtl::GetData()

src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h"
34
#include "Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h"
45

56
class LatteTextureReadbackInfoMtl : public LatteTextureReadbackInfo
@@ -18,5 +19,7 @@ class LatteTextureReadbackInfoMtl : public LatteTextureReadbackInfo
1819
private:
1920
class MetalRenderer* m_mtlr;
2021

22+
MTL::CommandBuffer* m_commandBuffer = nullptr;
23+
2124
uint32 m_bufferOffset = 0;
2225
};

src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
#include "Cafe/HW/Latte/Core/LatteShader.h"
1616
#include "Cafe/HW/Latte/Core/LatteIndices.h"
1717
#include "Cemu/Logging/CemuDebugLogging.h"
18+
#include "Common/precompiled.h"
1819
#include "Metal/MTLPixelFormat.hpp"
1920
#include "gui/guiWrapper.h"
2021

22+
#define COMMIT_TRESHOLD 256
23+
2124
extern bool hasValidFramebufferAttached;
2225

2326
float supportBufferData[512 * 4];
@@ -113,7 +116,7 @@ MetalRenderer::~MetalRenderer()
113116
m_device->release();
114117
}
115118

116-
// TODO: don't ignore "mainWindow" argument
119+
// TODO: don't ignore "mainWindow" argument and respect size
117120
void MetalRenderer::InitializeLayer(const Vector2i& size, bool mainWindow)
118121
{
119122
const auto& windowInfo = gui_getWindowInfo().window_main;
@@ -168,19 +171,23 @@ void MetalRenderer::DrawEmptyFrame(bool mainWindow)
168171

169172
void MetalRenderer::SwapBuffers(bool swapTV, bool swapDRC)
170173
{
171-
EndEncoding();
172174

173175
if (m_drawable)
174176
{
175-
EnsureCommandBuffer();
176-
m_commandBuffer->presentDrawable(m_drawable);
177-
} else
177+
auto commandBuffer = GetCommandBuffer();
178+
commandBuffer->presentDrawable(m_drawable);
179+
}
180+
else
178181
{
179182
debug_printf("skipped present!\n");
180183
}
181184
m_drawable = nullptr;
182185

186+
// Release all the command buffers
183187
CommitCommandBuffer();
188+
for (uint32 i = 0; i < m_commandBuffers.size(); i++)
189+
m_commandBuffers[i].m_commandBuffer->release();
190+
m_commandBuffers.clear();
184191

185192
// Reset temporary buffers
186193
m_memoryManager->ResetTemporaryBuffers();
@@ -223,18 +230,20 @@ bool MetalRenderer::BeginFrame(bool mainWindow)
223230

224231
void MetalRenderer::Flush(bool waitIdle)
225232
{
226-
// TODO: should we?
227-
CommitCommandBuffer();
233+
// TODO: commit if commit on idle is requested
234+
if (m_recordedDrawcalls > 0)
235+
CommitCommandBuffer();
228236
if (waitIdle)
229237
{
230-
// TODO
238+
// TODO: shouldn't we wait for all command buffers?
239+
WaitForCommandBufferCompletion(GetCurrentCommandBuffer());
231240
}
232241
}
233242

234243
void MetalRenderer::NotifyLatteCommandProcessorIdle()
235244
{
236-
// TODO: should we?
237-
CommitCommandBuffer();
245+
// TODO: commit if commit on idle is requested
246+
//CommitCommandBuffer();
238247
}
239248

240249
void MetalRenderer::AppendOverlayDebugInfo()
@@ -452,7 +461,7 @@ LatteTextureReadbackInfo* MetalRenderer::texture_createReadback(LatteTextureView
452461

453462
void MetalRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* sourceTexture, sint32 srcMip, sint32 srcSlice, LatteTexture* destinationTexture, sint32 dstMip, sint32 dstSlice, sint32 width, sint32 height)
454463
{
455-
EnsureCommandBuffer();
464+
GetCommandBuffer();
456465

457466
// scale copy size to effective size
458467
sint32 effectiveCopyWidth = width;
@@ -809,11 +818,15 @@ void MetalRenderer::draw_endSequence()
809818
if (pixelShader)
810819
LatteRenderTarget_trackUpdates();
811820
bool hasReadback = LatteTextureReadback_Update();
812-
//m_recordedDrawcalls++;
813-
//if (m_recordedDrawcalls >= m_submitThreshold || hasReadback)
814-
//{
815-
// SubmitCommandBuffer();
816-
//}
821+
m_recordedDrawcalls++;
822+
// The number of draw calls needs to twice as big, since we are interrupting the render pass
823+
if (m_recordedDrawcalls >= COMMIT_TRESHOLD * 2 || hasReadback)
824+
{
825+
CommitCommandBuffer();
826+
827+
// TODO: where should this be called?
828+
LatteTextureReadback_UpdateFinishedTransfers(false);
829+
}
817830
}
818831

819832
void* MetalRenderer::indexData_reserveIndexMemory(uint32 size, uint32& offset, uint32& bufferIndex)
@@ -830,22 +843,38 @@ void MetalRenderer::indexData_uploadIndexMemory(uint32 offset, uint32 size)
830843
// Do nothing, since the buffer has shared storage mode
831844
}
832845

833-
void MetalRenderer::EnsureCommandBuffer()
846+
MTL::CommandBuffer* MetalRenderer::GetCommandBuffer()
834847
{
835-
if (!m_commandBuffer)
848+
bool needsNewCommandBuffer = (m_commandBuffers.empty() || m_commandBuffers.back().m_commited);
849+
if (needsNewCommandBuffer)
836850
{
837851
// Debug
838852
//m_commandQueue->insertDebugCaptureBoundary();
839853

840-
m_commandBuffer = m_commandQueue->commandBuffer();
854+
MTL::CommandBuffer* mtlCommandBuffer = m_commandQueue->commandBuffer();
855+
m_commandBuffers.push_back({mtlCommandBuffer});
856+
857+
return mtlCommandBuffer;
858+
}
859+
else
860+
{
861+
return m_commandBuffers.back().m_commandBuffer;
841862
}
842863
}
843864

865+
bool MetalRenderer::CommandBufferCompleted(MTL::CommandBuffer* commandBuffer)
866+
{
867+
return commandBuffer->status() == MTL::CommandBufferStatusCompleted;
868+
}
869+
870+
void MetalRenderer::WaitForCommandBufferCompletion(MTL::CommandBuffer* commandBuffer)
871+
{
872+
commandBuffer->waitUntilCompleted();
873+
}
874+
844875
// Some render passes clear the attachments, forceRecreate is supposed to be used in those cases
845876
MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate, bool rebindStateIfNewEncoder)
846877
{
847-
EnsureCommandBuffer();
848-
849878
// Check if we need to begin a new render pass
850879
if (m_commandEncoder)
851880
{
@@ -881,6 +910,8 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
881910
EndEncoding();
882911
}
883912

913+
auto commandBuffer = GetCommandBuffer();
914+
884915
// Update state
885916
m_state.m_lastUsedFBO = m_state.m_activeFBO;
886917
for (uint8 i = 0; i < 8; i++)
@@ -889,7 +920,7 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
889920
}
890921
m_state.m_depthRenderTarget = depthRenderTarget;
891922

892-
auto renderCommandEncoder = m_commandBuffer->renderCommandEncoder(renderPassDescriptor);
923+
auto renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor);
893924
m_commandEncoder = renderCommandEncoder;
894925
m_encoderType = MetalEncoderType::Render;
895926

@@ -914,7 +945,9 @@ MTL::ComputeCommandEncoder* MetalRenderer::GetComputeCommandEncoder()
914945
EndEncoding();
915946
}
916947

917-
auto computeCommandEncoder = m_commandBuffer->computeCommandEncoder();
948+
auto commandBuffer = GetCommandBuffer();
949+
950+
auto computeCommandEncoder = commandBuffer->computeCommandEncoder();
918951
m_commandEncoder = computeCommandEncoder;
919952
m_encoderType = MetalEncoderType::Compute;
920953

@@ -933,7 +966,9 @@ MTL::BlitCommandEncoder* MetalRenderer::GetBlitCommandEncoder()
933966
EndEncoding();
934967
}
935968

936-
auto blitCommandEncoder = m_commandBuffer->blitCommandEncoder();
969+
auto commandBuffer = GetCommandBuffer();
970+
971+
auto blitCommandEncoder = commandBuffer->blitCommandEncoder();
937972
m_commandEncoder = blitCommandEncoder;
938973
m_encoderType = MetalEncoderType::Blit;
939974

@@ -942,30 +977,35 @@ MTL::BlitCommandEncoder* MetalRenderer::GetBlitCommandEncoder()
942977

943978
void MetalRenderer::EndEncoding()
944979
{
945-
if (m_commandEncoder)
980+
if (m_encoderType != MetalEncoderType::None)
946981
{
947982
m_commandEncoder->endEncoding();
948983
m_commandEncoder->release();
949-
m_commandEncoder = nullptr;
950984
m_encoderType = MetalEncoderType::None;
985+
986+
// Commit the command buffer if enough draw calls have been recorded
987+
if (m_recordedDrawcalls >= COMMIT_TRESHOLD)
988+
CommitCommandBuffer();
951989
}
952990
}
953991

954992
void MetalRenderer::CommitCommandBuffer()
955993
{
956-
EndEncoding();
994+
m_recordedDrawcalls = 0;
957995

958-
if (m_commandBuffer)
996+
if (m_commandBuffers.size() != 0)
959997
{
960-
m_commandBuffer->commit();
961-
m_commandBuffer->release();
962-
m_commandBuffer = nullptr;
998+
EndEncoding();
963999

964-
// TODO: where should this be called?
965-
LatteTextureReadback_UpdateFinishedTransfers(false);
1000+
auto& commandBuffer = m_commandBuffers.back();
1001+
if (!commandBuffer.m_commited)
1002+
{
1003+
commandBuffer.m_commandBuffer->commit();
1004+
commandBuffer.m_commited = true;
9661005

967-
// Debug
968-
//m_commandQueue->insertDebugCaptureBoundary();
1006+
// Debug
1007+
//m_commandQueue->insertDebugCaptureBoundary();
1008+
}
9691009
}
9701010
}
9711011

src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "Cafe/HW/Latte/Renderer/Renderer.h"
88

99
#include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h"
10+
#include "Common/precompiled.h"
11+
#include "Metal/MTLCommandBuffer.hpp"
1012

1113
#define MAX_MTL_BUFFERS 31
1214
#define GET_MTL_VERTEX_BUFFER_INDEX(index) (MAX_MTL_BUFFERS - index - 2)
@@ -47,6 +49,12 @@ struct MetalState
4749
MTL::ScissorRect m_scissor = {0, 0, 0, 0};
4850
};
4951

52+
struct MetalCommandBuffer
53+
{
54+
MTL::CommandBuffer* m_commandBuffer;
55+
bool m_commited = false;
56+
};
57+
5058
enum class MetalEncoderType
5159
{
5260
None,
@@ -231,7 +239,16 @@ class MetalRenderer : public Renderer
231239
}
232240

233241
// Helpers
234-
void EnsureCommandBuffer();
242+
MTL::CommandBuffer* GetCurrentCommandBuffer()
243+
{
244+
cemu_assert_debug(m_commandBuffers.size() != 0);
245+
246+
return m_commandBuffers[m_commandBuffers.size() - 1].m_commandBuffer;
247+
}
248+
249+
MTL::CommandBuffer* GetCommandBuffer();
250+
bool CommandBufferCompleted(MTL::CommandBuffer* commandBuffer);
251+
void WaitForCommandBufferCompletion(MTL::CommandBuffer* commandBuffer);
235252
MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate = false, bool rebindStateIfNewEncoder = true);
236253
MTL::ComputeCommandEncoder* GetComputeCommandEncoder();
237254
MTL::BlitCommandEncoder* GetBlitCommandEncoder();
@@ -280,7 +297,8 @@ class MetalRenderer : public Renderer
280297
MTL::Buffer* m_xfbRingBuffer;
281298

282299
// Active objects
283-
MTL::CommandBuffer* m_commandBuffer = nullptr;
300+
std::vector<MetalCommandBuffer> m_commandBuffers;
301+
uint32 m_recordedDrawcalls = 0;
284302
MetalEncoderType m_encoderType = MetalEncoderType::None;
285303
MTL::CommandEncoder* m_commandEncoder = nullptr;
286304
CA::MetalDrawable* m_drawable = nullptr;

0 commit comments

Comments
 (0)