From 73001002e0f4b4c8e27ecddfdd15a9b7f16b381e Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:24:50 +0200 Subject: [PATCH] Latte: Add support for texture readback with non-zero slice index --- src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp | 6 +----- src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp | 4 ++-- src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h | 3 ++- .../Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp | 3 +-- .../HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp | 10 +++++----- .../HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp | 3 +-- 6 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp b/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp index 589b3bd0..7e196881 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp @@ -704,7 +704,7 @@ void optimizedLinearReadbackWriteLoop(LatteTextureLoaderCtx* textureLoader, uint sint32 yc = y; sint32 pixelOffset = yc * pitch; copyType* rowPixelData = (copyType*)(linearPixelData + pixelOffset * sizeof(copyType)); - copyType* blockData = (copyType*)LatteTextureLoader_getInputLinearOptimized_(textureLoader, 0, y, 1, 1, sizeof(copyType) * 8, 0, 1, 0, textureLoader->pitch, textureLoader->height); + copyType* blockData = (copyType*)LatteTextureLoader_getInputLinearOptimized_(textureLoader, 0, y, 1, 1, sizeof(copyType) * 8, textureLoader->sliceIndex, 1, 0, textureLoader->pitch, textureLoader->surfaceInfoHeight); if constexpr (sizeof(copyType) == 4) { memcpy_dwords(blockData, rowPixelData, textureLoader->width); @@ -726,10 +726,6 @@ void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* tex LatteTextureLoaderCtx textureLoader = { 0 }; LatteTextureLoader_begin(&textureLoader, sliceIndex, mipIndex, textureData->physAddress, textureData->physMipAddress, textureData->format, textureData->dim, textureData->width, textureData->height, textureData->depth, textureData->mipLevels, textureData->pitch, textureData->tileMode, textureData->swizzle); -#ifdef CEMU_DEBUG_ASSERT - if (textureData->depth != 1) - cemuLog_log(LogType::Force, "_writeReadbackTextureToMemory(): Texture has multiple slices (not supported)"); -#endif if (textureLoader.physAddress == MPTR_NULL) { cemuLog_log(LogType::Force, "_writeReadbackTextureToMemory(): Texture has invalid address"); diff --git a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp index 862964ba..db1b0a3b 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp @@ -145,7 +145,7 @@ void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish) } #endif uint8* pixelData = readbackInfo->GetData(); - LatteTextureLoader_writeReadbackTextureToMemory(&readbackInfo->hostTextureCopy, 0, 0, pixelData); + LatteTextureLoader_writeReadbackTextureToMemory(&readbackInfo->hostTextureCopy, readbackInfo->m_firstSlice, 0, pixelData); readbackInfo->ReleaseData(); // get the original texture if it still exists and invalidate the current data hash LatteTextureView* origTexView = LatteTextureViewLookupCache::lookupSlice(readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.pitch, 0, 0, readbackInfo->hostTextureCopy.format); @@ -180,4 +180,4 @@ bool LatteTextureReadback_ReadbackToLinearBlocking(LatteTextureView* sourceView, info->ReleaseData(); delete info; return true; -} \ No newline at end of file +} diff --git a/src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h b/src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h index 535e9442..d076c840 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h +++ b/src/Cafe/HW/Latte/Core/LatteTextureReadbackInfo.h @@ -7,7 +7,7 @@ class LatteTextureReadbackInfo { public: LatteTextureReadbackInfo(LatteTextureView* textureView) - : hostTextureCopy(textureView->baseTexture), m_textureView(textureView) + : hostTextureCopy(textureView->baseTexture), m_firstSlice(textureView->firstSlice), m_textureView(textureView) {} virtual ~LatteTextureReadbackInfo() = default; @@ -24,6 +24,7 @@ public: bool forceFinish{ false }; // set to true if not finished in time for dependent operation // texture info LatteTextureDefinition hostTextureCopy{}; + sint32 m_firstSlice{}; protected: LatteTextureView* m_textureView; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp index 405c49df..683cfe34 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureReadbackMtl.cpp @@ -15,7 +15,6 @@ void LatteTextureReadbackInfoMtl::StartTransfer() 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); @@ -24,7 +23,7 @@ void LatteTextureReadbackInfoMtl::StartTransfer() 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(), m_firstSlice, 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()->retain(); // TODO: uncomment? diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp index b2966706..f978a598 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp @@ -1,7 +1,5 @@ -#include "Cafe/HW/Latte/Renderer/Renderer.h" -#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" #include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLTextureReadback.h" -#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h" +#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h" LatteTextureReadbackInfoGL::LatteTextureReadbackInfoGL(LatteTextureView* textureView) : LatteTextureReadbackInfo(textureView) @@ -94,13 +92,15 @@ LatteTextureReadbackInfoGL::~LatteTextureReadbackInfoGL() void LatteTextureReadbackInfoGL::StartTransfer() { cemu_assert(m_textureView); - ((OpenGLRenderer*)g_renderer.get())->texture_bindAndActivate(m_textureView, 0); + LatteTextureGL* baseTexture = (LatteTextureGL*)m_textureView->baseTexture; + cemu_assert_debug(m_textureView->firstMip == 0); + cemu_assert_debug(m_textureView->baseTexture->dim != Latte::E_DIM::DIM_3D); // create unsynchronized buffer glGenBuffers(1, &texImageBufferGL); glBindBuffer(GL_PIXEL_PACK_BUFFER, texImageBufferGL); glBufferData(GL_PIXEL_PACK_BUFFER, m_image_size, NULL, GL_DYNAMIC_READ); // request texture read into buffer - glGetTexImage(((LatteTextureViewGL*)m_textureView)->glTexTarget, 0, m_texFormatGL, m_texDataTypeGL, NULL); + glGetTextureSubImage(baseTexture->glId_texture, 0, 0, 0, m_firstSlice, baseTexture->width, baseTexture->height, 1, m_texFormatGL, m_texDataTypeGL, m_image_size, NULL); glFlush(); // create fence sync (so we can check if the image copy operation finished) imageCopyFinSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp index 81281ebc..a4556c45 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp @@ -102,7 +102,6 @@ void LatteTextureReadbackInfoVk::StartTransfer() auto* baseTexture = (LatteTextureVk*)m_textureView->baseTexture; baseTexture->GetImageObj()->flagForCurrentCommandBuffer(); - 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); @@ -112,7 +111,7 @@ void LatteTextureReadbackInfoVk::StartTransfer() region.bufferImageHeight = baseTexture->height; region.imageSubresource.aspectMask = baseTexture->GetImageAspect(); - region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.baseArrayLayer = m_firstSlice; region.imageSubresource.layerCount = 1; region.imageSubresource.mipLevel = 0;