Skip to content

Commit d3249dc

Browse files
committed
implement texture readback
1 parent e2ec602 commit d3249dc

File tree

6 files changed

+143
-23
lines changed

6 files changed

+143
-23
lines changed

src/Cafe/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,8 @@ if(ENABLE_METAL)
548548
HW/Latte/Renderer/Metal/LatteTextureMtl.h
549549
HW/Latte/Renderer/Metal/LatteTextureViewMtl.cpp
550550
HW/Latte/Renderer/Metal/LatteTextureViewMtl.h
551+
HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp
552+
HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h
551553
HW/Latte/Renderer/Metal/RendererShaderMtl.cpp
552554
HW/Latte/Renderer/Metal/RendererShaderMtl.h
553555
HW/Latte/Renderer/Metal/CachedFBOMtl.cpp

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class LatteTextureMtl : public LatteTexture
1010
{
1111
public:
12-
LatteTextureMtl(class MetalRenderer* vkRenderer, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels,
12+
LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels,
1313
uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth);
1414
~LatteTextureMtl();
1515

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
2+
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h"
3+
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h"
4+
#include "HW/Latte/Renderer/Metal/LatteToMtl.h"
5+
6+
LatteTextureReadbackInfoMtl::~LatteTextureReadbackInfoMtl()
7+
{
8+
}
9+
10+
void LatteTextureReadbackInfoMtl::StartTransfer()
11+
{
12+
cemu_assert(m_textureView);
13+
14+
auto* baseTexture = (LatteTextureMtl*)m_textureView->baseTexture;
15+
16+
cemu_assert_debug(m_textureView->firstSlice == 0);
17+
cemu_assert_debug(m_textureView->firstMip == 0);
18+
cemu_assert_debug(m_textureView->baseTexture->dim != Latte::E_DIM::DIM_3D);
19+
20+
size_t bytesPerRow = GetMtlTextureBytesPerRow(baseTexture->format, baseTexture->IsDepth(), baseTexture->width);
21+
size_t bytesPerImage = GetMtlTextureBytesPerImage(baseTexture->format, baseTexture->IsDepth(), baseTexture->height, bytesPerRow);
22+
23+
auto blitCommandEncoder = m_mtlr->GetBlitCommandEncoder();
24+
25+
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+
28+
bool LatteTextureReadbackInfoMtl::IsFinished()
29+
{
30+
// TODO: implement
31+
32+
return true;
33+
}
34+
35+
void LatteTextureReadbackInfoMtl::ForceFinish()
36+
{
37+
// TODO: implement
38+
}
39+
40+
uint8* LatteTextureReadbackInfoMtl::GetData()
41+
{
42+
return (uint8*)m_mtlr->GetTextureReadbackBuffer()->contents() + m_bufferOffset;
43+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include "Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h"
4+
5+
class LatteTextureReadbackInfoMtl : public LatteTextureReadbackInfo
6+
{
7+
public:
8+
LatteTextureReadbackInfoMtl(class MetalRenderer* mtlRenderer, LatteTextureView* textureView, uint32 bufferOffset) : LatteTextureReadbackInfo(textureView), m_mtlr{mtlRenderer}, m_bufferOffset{bufferOffset} {}
9+
~LatteTextureReadbackInfoMtl();
10+
11+
void StartTransfer() override;
12+
13+
bool IsFinished() override;
14+
void ForceFinish() override;
15+
16+
uint8* GetData() override;
17+
18+
private:
19+
class MetalRenderer* m_mtlr;
20+
21+
uint32 m_bufferOffset = 0;
22+
};

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.h"
77
#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h"
88
#include "Cafe/HW/Latte/Renderer/Metal/MetalDepthStencilCache.h"
9+
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h"
910
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
1011

1112
#include "Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h"
@@ -35,6 +36,9 @@ MetalRenderer::MetalRenderer()
3536
m_pipelineCache = new MetalPipelineCache(this);
3637
m_depthStencilCache = new MetalDepthStencilCache(this);
3738

39+
// Texture readback
40+
m_readbackBuffer = m_device->newBuffer(TEXTURE_READBACK_SIZE, MTL::StorageModeShared);
41+
3842
// Initialize state
3943
for (uint32 i = 0; i < (uint32)LatteConst::ShaderType::TotalCount; i++)
4044
{
@@ -53,6 +57,8 @@ MetalRenderer::~MetalRenderer()
5357

5458
m_nearestSampler->release();
5559

60+
m_readbackBuffer->release();
61+
5662
m_commandQueue->release();
5763
m_device->release();
5864
}
@@ -407,9 +413,17 @@ void MetalRenderer::texture_copyImageSubData(LatteTexture* src, sint32 srcMip, s
407413

408414
LatteTextureReadbackInfo* MetalRenderer::texture_createReadback(LatteTextureView* textureView)
409415
{
410-
debug_printf("MetalRenderer::texture_createReadback not implemented\n");
416+
size_t uploadSize = static_cast<LatteTextureMtl*>(textureView->baseTexture)->GetTexture()->allocatedSize();
411417

412-
return nullptr;
418+
if ((m_readbackBufferWriteOffset + uploadSize) > TEXTURE_READBACK_SIZE)
419+
{
420+
m_readbackBufferWriteOffset = 0;
421+
}
422+
423+
auto* result = new LatteTextureReadbackInfoMtl(this, textureView, m_readbackBufferWriteOffset);
424+
m_readbackBufferWriteOffset += uploadSize;
425+
426+
return result;
413427
}
414428

415429
void MetalRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* sourceTexture, sint32 srcMip, sint32 srcSlice, LatteTexture* destinationTexture, sint32 dstMip, sint32 dstSlice, sint32 width, sint32 height)
@@ -801,6 +815,9 @@ void MetalRenderer::CommitCommandBuffer()
801815
m_commandBuffer->release();
802816
m_commandBuffer = nullptr;
803817

818+
// TODO: where should this be called?
819+
LatteTextureReadback_UpdateFinishedTransfers(false);
820+
804821
// Debug
805822
m_commandQueue->insertDebugCaptureBoundary();
806823
}

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

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,36 @@ enum class MetalEncoderType
4949
Blit,
5050
};
5151

52+
class LatteQueryObjectMtl : public LatteQueryObject
53+
{
54+
public:
55+
LatteQueryObjectMtl(class MetalRenderer* mtlRenderer) : m_mtlr{mtlRenderer} {}
56+
57+
bool getResult(uint64& numSamplesPassed) override
58+
{
59+
cemuLog_log(LogType::MetalLogging, "LatteQueryObjectMtl::getResult: occlusion queries are not yet supported on Metal");
60+
return false;
61+
}
62+
63+
void begin() override
64+
{
65+
cemuLog_log(LogType::MetalLogging, "LatteQueryObjectMtl::begin: occlusion queries are not yet supported on Metal");
66+
}
67+
68+
void end() override
69+
{
70+
cemuLog_log(LogType::MetalLogging, "LatteQueryObjectMtl::end: occlusion queries are not yet supported on Metal");
71+
}
72+
73+
private:
74+
class MetalRenderer* m_mtlr;
75+
};
76+
5277
class MetalRenderer : public Renderer
5378
{
5479
public:
80+
static const inline int TEXTURE_READBACK_SIZE = 32 * 1024 * 1024; // 32 MB
81+
5582
MetalRenderer();
5683
~MetalRenderer() override;
5784

@@ -178,23 +205,43 @@ class MetalRenderer : public Renderer
178205

179206
// occlusion queries
180207
LatteQueryObject* occlusionQuery_create() override {
181-
cemuLog_log(LogType::MetalLogging, "Occlusion queries are not yet supported on Metal");
208+
cemuLog_log(LogType::MetalLogging, "MetalRenderer::occlusionQuery_create: Occlusion queries are not yet supported on Metal");
182209

183-
return nullptr;
210+
return new LatteQueryObjectMtl(this);
184211
}
185212

186213
void occlusionQuery_destroy(LatteQueryObject* queryObj) override {
187-
cemuLog_log(LogType::MetalLogging, "Occlusion queries are not yet supported on Metal");
214+
cemuLog_log(LogType::MetalLogging, "MetalRenderer::occlusionQuery_destroy: occlusion queries are not yet supported on Metal");
188215
}
189216

190217
void occlusionQuery_flush() override {
191-
cemuLog_log(LogType::MetalLogging, "Occlusion queries are not yet supported on Metal");
218+
cemuLog_log(LogType::MetalLogging, "MetalRenderer::occlusionQuery_flush: occlusion queries are not yet supported on Metal");
192219
}
193220

194221
void occlusionQuery_updateState() override {
195-
cemuLog_log(LogType::MetalLogging, "Occlusion queries are not yet supported on Metal");
222+
cemuLog_log(LogType::MetalLogging, "MetalRenderer::occlusionQuery_updateState: occlusion queries are not yet supported on Metal");
196223
}
197224

225+
// Helpers
226+
void EnsureCommandBuffer();
227+
MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate = false, bool rebindStateIfNewEncoder = true);
228+
MTL::ComputeCommandEncoder* GetComputeCommandEncoder();
229+
MTL::BlitCommandEncoder* GetBlitCommandEncoder();
230+
void EndEncoding();
231+
void CommitCommandBuffer();
232+
233+
bool AcquireNextDrawable();
234+
235+
void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader);
236+
void RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder);
237+
238+
void ClearColorTextureInternal(MTL::Texture* mtlTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a);
239+
240+
// Getters
241+
MTL::Buffer* GetTextureReadbackBuffer()
242+
{
243+
return m_readbackBuffer;
244+
}
198245

199246
private:
200247
CA::MetalLayer* m_metalLayer;
@@ -213,6 +260,10 @@ class MetalRenderer : public Renderer
213260
// Basic
214261
MTL::SamplerState* m_nearestSampler;
215262

263+
// Texture readback
264+
MTL::Buffer* m_readbackBuffer;
265+
uint32 m_readbackBufferWriteOffset = 0;
266+
216267
// Active objects
217268
MTL::CommandBuffer* m_commandBuffer = nullptr;
218269
MetalEncoderType m_encoderType = MetalEncoderType::None;
@@ -221,19 +272,4 @@ class MetalRenderer : public Renderer
221272

222273
// State
223274
MetalState m_state;
224-
225-
// Helpers
226-
void EnsureCommandBuffer();
227-
MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate = false, bool rebindStateIfNewEncoder = true);
228-
MTL::ComputeCommandEncoder* GetComputeCommandEncoder();
229-
MTL::BlitCommandEncoder* GetBlitCommandEncoder();
230-
void EndEncoding();
231-
void CommitCommandBuffer();
232-
233-
bool AcquireNextDrawable();
234-
235-
void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader);
236-
void RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder);
237-
238-
void ClearColorTextureInternal(MTL::Texture* mtlTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a);
239275
};

0 commit comments

Comments
 (0)