diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index ea237348..3f224491 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -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 diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h index e2187e1b..81942dfa 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h @@ -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(); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp new file mode 100644 index 00000000..608ff050 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp @@ -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; +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h new file mode 100644 index 00000000..a03bbd49 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.h @@ -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; +}; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index e4764590..852f1993 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -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(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(); } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index bee581d6..26ab52f1 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -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); };