diff --git a/src/Cafe/HW/Latte/Core/Latte.h b/src/Cafe/HW/Latte/Core/Latte.h index 548944ec..b6ff6ba0 100644 --- a/src/Cafe/HW/Latte/Core/Latte.h +++ b/src/Cafe/HW/Latte/Core/Latte.h @@ -125,6 +125,7 @@ void LatteTextureReadback_StartTransfer(LatteTextureView* textureView); bool LatteTextureReadback_Update(bool forceStart = false); void LatteTextureReadback_NotifyTextureDeletion(LatteTexture* texture); void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish); +bool LatteTextureReadback_ReadbackToLinearBlocking(LatteTextureView* sourceView, uint8* dstPtr, uint32 dstWidth, uint32 dstHeight, uint32 dstPitch); // query diff --git a/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp b/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp index 7e516172..3a51eaa6 100644 --- a/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp +++ b/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp @@ -29,7 +29,6 @@ void LatteSurfaceCopy_CopyInRAM(const LatteSurfaceCopyParam& src, const LatteSur copyWidth = (copyWidth + 3) / 4; copyHeight = (copyHeight + 3) / 4; } - uint32 dstBpp = Latte::GetFormatBits(dstHwFormat); gx2SurfaceCopySoftware((uint8*)MEMPTR(src.physDataAddr).GetPtr(), src.heightInTexels, src.pitch, 1, src.sliceIndex, src.swizzle, (uint32)src.tilemode, @@ -44,10 +43,9 @@ void LatteSurfaceCopy_copySurfaceNew(const LatteSurfaceCopyParam& src, const Lat cemu_assert_debug((rect.x + rect.width) <= dst.pitch * (Latte::IsCompressedFormat(dst.surfaceFormat)?4:1)); cemu_assert_debug((rect.x + rect.width) <= src.pitch * (Latte::IsCompressedFormat(src.surfaceFormat)?4:1)); - if (src.tilemode == Latte::E_GX2TILEMODE::TM_LINEAR_SPECIAL || dst.tilemode == Latte::E_GX2TILEMODE::TM_LINEAR_SPECIAL) + if (src.tilemode == Latte::E_GX2TILEMODE::TM_LINEAR_SPECIAL) { // todo - it's technically possible for a matching linear texture to be in the texture cache already - // there is also a case of tiled to linear_special where we should trigger a readback without an actual destination texture LatteSurfaceCopy_CopyInRAM(src, dst, rect); return; } @@ -69,6 +67,13 @@ void LatteSurfaceCopy_copySurfaceNew(const LatteSurfaceCopyParam& src, const Lat LatteTexture_UpdateCacheFromDynamicTextures(sourceTexture); sourceTexture->reloadFromDynamicTextures = false; } + // special case: RAM copy from cached texture to linear special format + if (dst.tilemode == Latte::E_GX2TILEMODE::TM_LINEAR_SPECIAL) + { + cemu_assert_debug(rect.x == 0 && rect.y == 0); + LatteTextureReadback_ReadbackToLinearBlocking(sourceView, MEMPTR(dst.physDataAddr).GetPtr(), rect.width, rect.height, dst.pitch); + return; + } // look up destination texture LatteTexture* destinationTexture = nullptr; LatteTextureView* destinationView = LatteTextureViewLookupCache::lookupSliceMinSize(dst.physDataAddr, rect.x + rect.width, rect.y + rect.height, dst.pitch, 0, dst.sliceIndex, dst.surfaceFormat); diff --git a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp index 003360b6..862964ba 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp @@ -159,3 +159,25 @@ void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish) } performanceMonitor.gpuTime_waitForAsync.endMeasuring(); } + +bool LatteTextureReadback_ReadbackToLinearBlocking(LatteTextureView* sourceView, uint8* dstPtr, uint32 dstWidth, uint32 dstHeight, uint32 dstPitch) +{ + LatteTextureReadbackInfo* info = g_renderer->texture_createReadback(sourceView); + if (!info) + return false; + + info->StartTransfer(); + info->ForceFinish(); + cemu_assert(info->IsFinished()); + + uint8* data = info->GetData(); // returned pixel format should match Latte format + uint32 bpp = Latte::GetFormatBits(sourceView->baseTexture->format) / 8; + uint32 srcRowBytes = sourceView->baseTexture->width * bpp; + uint32 dstRowBytes = dstWidth * bpp; + for (uint32 y = 0; y < dstHeight; y++) + memcpy(dstPtr + y * dstPitch * bpp, data + y * srcRowBytes, dstRowBytes); + + info->ReleaseData(); + delete info; + return true; +} \ No newline at end of file