implement texture readback

This commit is contained in:
Samuliak 2024-08-07 18:20:09 +02:00
parent e2ec602c43
commit d3249dc324
6 changed files with 143 additions and 23 deletions

View File

@ -548,6 +548,8 @@ if(ENABLE_METAL)
HW/Latte/Renderer/Metal/LatteTextureMtl.h
HW/Latte/Renderer/Metal/LatteTextureViewMtl.cpp
HW/Latte/Renderer/Metal/LatteTextureViewMtl.h
HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp
HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h
HW/Latte/Renderer/Metal/RendererShaderMtl.cpp
HW/Latte/Renderer/Metal/RendererShaderMtl.h
HW/Latte/Renderer/Metal/CachedFBOMtl.cpp

View File

@ -9,7 +9,7 @@
class LatteTextureMtl : public LatteTexture
{
public:
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,
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,
uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth);
~LatteTextureMtl();

View File

@ -0,0 +1,43 @@
#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"
LatteTextureReadbackInfoMtl::~LatteTextureReadbackInfoMtl()
{
}
void LatteTextureReadbackInfoMtl::StartTransfer()
{
cemu_assert(m_textureView);
auto* baseTexture = (LatteTextureMtl*)m_textureView->baseTexture;
cemu_assert_debug(m_textureView->firstSlice == 0);
cemu_assert_debug(m_textureView->firstMip == 0);
cemu_assert_debug(m_textureView->baseTexture->dim != Latte::E_DIM::DIM_3D);
size_t bytesPerRow = GetMtlTextureBytesPerRow(baseTexture->format, baseTexture->IsDepth(), baseTexture->width);
size_t bytesPerImage = GetMtlTextureBytesPerImage(baseTexture->format, baseTexture->IsDepth(), baseTexture->height, bytesPerRow);
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);
}
bool LatteTextureReadbackInfoMtl::IsFinished()
{
// TODO: implement
return true;
}
void LatteTextureReadbackInfoMtl::ForceFinish()
{
// TODO: implement
}
uint8* LatteTextureReadbackInfoMtl::GetData()
{
return (uint8*)m_mtlr->GetTextureReadbackBuffer()->contents() + m_bufferOffset;
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h"
class LatteTextureReadbackInfoMtl : public LatteTextureReadbackInfo
{
public:
LatteTextureReadbackInfoMtl(class MetalRenderer* mtlRenderer, LatteTextureView* textureView, uint32 bufferOffset) : LatteTextureReadbackInfo(textureView), m_mtlr{mtlRenderer}, m_bufferOffset{bufferOffset} {}
~LatteTextureReadbackInfoMtl();
void StartTransfer() override;
bool IsFinished() override;
void ForceFinish() override;
uint8* GetData() override;
private:
class MetalRenderer* m_mtlr;
uint32 m_bufferOffset = 0;
};

View File

@ -6,6 +6,7 @@
#include "Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalDepthStencilCache.h"
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h"
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
#include "Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h"
@ -35,6 +36,9 @@ MetalRenderer::MetalRenderer()
m_pipelineCache = new MetalPipelineCache(this);
m_depthStencilCache = new MetalDepthStencilCache(this);
// Texture readback
m_readbackBuffer = m_device->newBuffer(TEXTURE_READBACK_SIZE, MTL::StorageModeShared);
// Initialize state
for (uint32 i = 0; i < (uint32)LatteConst::ShaderType::TotalCount; i++)
{
@ -53,6 +57,8 @@ MetalRenderer::~MetalRenderer()
m_nearestSampler->release();
m_readbackBuffer->release();
m_commandQueue->release();
m_device->release();
}
@ -407,9 +413,17 @@ void MetalRenderer::texture_copyImageSubData(LatteTexture* src, sint32 srcMip, s
LatteTextureReadbackInfo* MetalRenderer::texture_createReadback(LatteTextureView* textureView)
{
debug_printf("MetalRenderer::texture_createReadback not implemented\n");
size_t uploadSize = static_cast<LatteTextureMtl*>(textureView->baseTexture)->GetTexture()->allocatedSize();
return nullptr;
if ((m_readbackBufferWriteOffset + uploadSize) > TEXTURE_READBACK_SIZE)
{
m_readbackBufferWriteOffset = 0;
}
auto* result = new LatteTextureReadbackInfoMtl(this, textureView, m_readbackBufferWriteOffset);
m_readbackBufferWriteOffset += uploadSize;
return result;
}
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()
m_commandBuffer->release();
m_commandBuffer = nullptr;
// TODO: where should this be called?
LatteTextureReadback_UpdateFinishedTransfers(false);
// Debug
m_commandQueue->insertDebugCaptureBoundary();
}

View File

@ -49,9 +49,36 @@ enum class MetalEncoderType
Blit,
};
class LatteQueryObjectMtl : public LatteQueryObject
{
public:
LatteQueryObjectMtl(class MetalRenderer* mtlRenderer) : m_mtlr{mtlRenderer} {}
bool getResult(uint64& numSamplesPassed) override
{
cemuLog_log(LogType::MetalLogging, "LatteQueryObjectMtl::getResult: occlusion queries are not yet supported on Metal");
return false;
}
void begin() override
{
cemuLog_log(LogType::MetalLogging, "LatteQueryObjectMtl::begin: occlusion queries are not yet supported on Metal");
}
void end() override
{
cemuLog_log(LogType::MetalLogging, "LatteQueryObjectMtl::end: occlusion queries are not yet supported on Metal");
}
private:
class MetalRenderer* m_mtlr;
};
class MetalRenderer : public Renderer
{
public:
static const inline int TEXTURE_READBACK_SIZE = 32 * 1024 * 1024; // 32 MB
MetalRenderer();
~MetalRenderer() override;
@ -178,23 +205,43 @@ public:
// occlusion queries
LatteQueryObject* occlusionQuery_create() override {
cemuLog_log(LogType::MetalLogging, "Occlusion queries are not yet supported on Metal");
cemuLog_log(LogType::MetalLogging, "MetalRenderer::occlusionQuery_create: Occlusion queries are not yet supported on Metal");
return nullptr;
return new LatteQueryObjectMtl(this);
}
void occlusionQuery_destroy(LatteQueryObject* queryObj) override {
cemuLog_log(LogType::MetalLogging, "Occlusion queries are not yet supported on Metal");
cemuLog_log(LogType::MetalLogging, "MetalRenderer::occlusionQuery_destroy: occlusion queries are not yet supported on Metal");
}
void occlusionQuery_flush() override {
cemuLog_log(LogType::MetalLogging, "Occlusion queries are not yet supported on Metal");
cemuLog_log(LogType::MetalLogging, "MetalRenderer::occlusionQuery_flush: occlusion queries are not yet supported on Metal");
}
void occlusionQuery_updateState() override {
cemuLog_log(LogType::MetalLogging, "Occlusion queries are not yet supported on Metal");
cemuLog_log(LogType::MetalLogging, "MetalRenderer::occlusionQuery_updateState: occlusion queries are not yet supported on Metal");
}
// Helpers
void EnsureCommandBuffer();
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();
void EndEncoding();
void CommitCommandBuffer();
bool AcquireNextDrawable();
void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader);
void RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder);
void ClearColorTextureInternal(MTL::Texture* mtlTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a);
// Getters
MTL::Buffer* GetTextureReadbackBuffer()
{
return m_readbackBuffer;
}
private:
CA::MetalLayer* m_metalLayer;
@ -213,6 +260,10 @@ private:
// Basic
MTL::SamplerState* m_nearestSampler;
// Texture readback
MTL::Buffer* m_readbackBuffer;
uint32 m_readbackBufferWriteOffset = 0;
// Active objects
MTL::CommandBuffer* m_commandBuffer = nullptr;
MetalEncoderType m_encoderType = MetalEncoderType::None;
@ -221,19 +272,4 @@ private:
// State
MetalState m_state;
// Helpers
void EnsureCommandBuffer();
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();
void EndEncoding();
void CommitCommandBuffer();
bool AcquireNextDrawable();
void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader);
void RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder);
void ClearColorTextureInternal(MTL::Texture* mtlTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a);
};