From bda380dd3394dce6b321c26685e654feb2a35dad Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 7 Sep 2026 00:03:06 +0200 Subject: [PATCH] Latte+GX2: Improve GX2Invalidate and implement basic tex invalidation --- .../HW/Latte/Core/LatteCommandProcessor.cpp | 8 +- src/Cafe/HW/Latte/Core/LatteTexture.cpp | 11 +++ src/Cafe/HW/Latte/Core/LatteTexture.h | 2 + src/Cafe/HW/Latte/ISA/LatteReg.h | 32 +++++++ src/Cafe/OS/libs/gx2/GX2_Misc.cpp | 84 ++++++++++++------- src/Cafe/OS/libs/gx2/GX2_Misc.h | 20 ++++- src/Cafe/OS/libs/proc_ui/proc_ui.cpp | 4 +- 7 files changed, 127 insertions(+), 34 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp index 82cf041c..462b5b87 100644 --- a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp +++ b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp @@ -209,7 +209,7 @@ void LatteCP_skipWords(uint32 wordsToSkip) LatteCMDPtr LatteCP_itSurfaceSync(LatteCMDPtr cmd) { - uint32 invalidationFlags = LatteReadCMD(); + Latte::E_COHER_CNTL invalidationFlags = static_cast(LatteReadCMD()); uint32 size = LatteReadCMD() << 8; MPTR addressPhys = LatteReadCMD() << 8; uint32 pollInterval = LatteReadCMD(); @@ -217,11 +217,15 @@ LatteCMDPtr LatteCP_itSurfaceSync(LatteCMDPtr cmd) if (addressPhys == MPTR_NULL || size == 0xFFFFFFFF) return cmd; // block global invalidations because they are too expensive - if (invalidationFlags & 0x800000) + // uniform invalidation needs both TC_ACTION_ENA and SH_ACTION_ENA? + if (HAS_FLAG(invalidationFlags, Latte::E_COHER_CNTL::TC_ACTION_ENA)) { // invalidate uniform or attribute buffer LatteBufferCache_invalidate(addressPhys, size); } + // texture invalidation + if (HAS_FLAG(invalidationFlags, Latte::E_COHER_CNTL::TC_ACTION_ENA)) + LatteTexture_Invalidate(addressPhys, size); return cmd; } diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.cpp b/src/Cafe/HW/Latte/Core/LatteTexture.cpp index 74b089a8..4f47e1a6 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp @@ -169,6 +169,17 @@ void LatteTexture_UnregisterTextureMemoryOccupancy(LatteTexture* texture) } } +void LatteTexture_Invalidate(uint32 physAddr, uint32 size) +{ + if (size == 0xFFFFFFFF) + return; // full cache invalidation for all textures is too expensive, so for now lets ignore it. Most likely games don't use this anyway when they are modifying a single texture + std::vector textures; + LatteTC_LookupTexturesByPhysAddr(physAddr, textures); + uint32 invalidationVal = LatteGPUState.frameCounter - 1; + for (LatteTexture* texture : textures) + texture->lastDataUpdateFrameCounter = invalidationVal; +} + // calculate the actually accessed data range // the resulting range is an estimate and may be smaller than the actual slice size (but not larger) void LatteTexture_EstimateMipSliceAccessedDataRange(LatteTexture* texture, sint32 sliceIndex, sint32 mipIndex, LatteTextureSliceMipInfo* sliceMipInfo) diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.h b/src/Cafe/HW/Latte/Core/LatteTexture.h index 8b08197f..9ea4b5f1 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.h +++ b/src/Cafe/HW/Latte/Core/LatteTexture.h @@ -348,6 +348,8 @@ void LatteTexture_InitSliceAndMipInfo(LatteTexture* texture); void LatteTexture_RegisterTextureMemoryOccupancy(LatteTexture* texture); void LatteTexture_UnregisterTextureMemoryOccupancy(LatteTexture* texture); +void LatteTexture_Invalidate(uint32 physAddr, uint32 size); + void LatteTexture_DeleteTextureRelations(LatteTexture* texture); void LatteTexture_DeleteDataOverlapTracking(LatteTexture* texture); diff --git a/src/Cafe/HW/Latte/ISA/LatteReg.h b/src/Cafe/HW/Latte/ISA/LatteReg.h index 3fecaff6..5826fd17 100644 --- a/src/Cafe/HW/Latte/ISA/LatteReg.h +++ b/src/Cafe/HW/Latte/ISA/LatteReg.h @@ -350,6 +350,38 @@ namespace Latte return dim == E_DIM::DIM_2D_MSAA || dim == E_DIM::DIM_2D_ARRAY_MSAA; } + // PM4 packet field enums + enum class E_COHER_CNTL : uint32 // for SURFACE_SYNC packet + { + DEST_BASE_0_ENA = (1<<0), + DEST_BASE_1_ENA = (1<<1), + SO0_DEST_BASE_ENA = (1<<2), + SO1_DEST_BASE_ENA = (1<<3), + SO2_DEST_BASE_ENA = (1<<4), + SO3_DEST_BASE_ENA = (1<<5), + CB0_DEST_BASE_ENA = (1<<6), + CB1_DEST_BASE_ENA = (1<<7), + CB2_DEST_BASE_ENA = (1<<8), + CB3_DEST_BASE_ENA = (1<<9), + CB4_DEST_BASE_ENA = (1<<10), + CB5_DEST_BASE_ENA = (1<<11), + CB6_DEST_BASE_ENA = (1<<12), + CB7_DEST_BASE_ENA = (1<<13), + DB_DEST_BASE_ENA = (1<<14), + + FULL_CACHE_ENA = (1<<20), + + TC_ACTION_ENA = (1<<23), // on Latte this bit seems to flush attribute buffer cache and texture cache + VC_ACTION_ENA = (1<<24), + CB_ACTION_ENA = (1<<25), + DB_ACTION_ENA = (1<<26), + SH_ACTION_ENA = (1<<27), + SX_ACTION_ENA = (1<<28), + + ENGINE_ME = (1u<<31) + }; + DEFINE_ENUM_FLAG_OPERATORS(E_COHER_CNTL); + enum GPU_LIMITS { NUM_VERTEX_BUFFERS = 16, diff --git a/src/Cafe/OS/libs/gx2/GX2_Misc.cpp b/src/Cafe/OS/libs/gx2/GX2_Misc.cpp index ada03b2d..78202693 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Misc.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Misc.cpp @@ -1,7 +1,6 @@ #include "Cafe/HW/Latte/ISA/RegDefines.h" #include "Cafe/OS/common/OSCommon.h" #include "GX2.h" -#include "config/CemuConfig.h" #include "Cafe/OS/libs/coreinit/coreinit_Time.h" #include "config/ActiveSettings.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" @@ -13,6 +12,8 @@ #include "GX2_Memory.h" #include "GX2_Texture.h" +#include "Cafe/HW/Latte/ISA/LatteReg.h" + void gx2Export_GX2SetSwapInterval(PPCInterpreter_t* hCPU) { cemuLog_log(LogType::GX2, "GX2SetSwapInterval({})", hCPU->gpr[3]); @@ -270,37 +271,64 @@ namespace GX2 return 1; } - void GX2Invalidate(uint32 invalidationFlags, MPTR invalidationAddr, uint32 invalidationSize) + void GX2Invalidate(GX2InvalidationFlag invalidationFlags, MPTR invalidationAddr, uint32 invalidationSize) { - uint32 surfaceSyncFlags = 0; + Latte::E_COHER_CNTL coherCntl{}; - if (invalidationFlags & 0x04) - { - // uniform block - surfaceSyncFlags |= 0x8800000; - } - if (invalidationFlags & 0x01) - { - // attribute data - surfaceSyncFlags |= 0x800000; - } + if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_ATTRIB) || HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_TEXTURE)) + coherCntl |= Latte::E_COHER_CNTL::TC_ACTION_ENA; - if (invalidationFlags & 0x40) - { - // CPU cache - LatteBufferCache_notifyDCFlush(invalidationAddr, invalidationSize); - } + if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_UNIFORM_BLOCK)) + coherCntl |= Latte::E_COHER_CNTL::TC_ACTION_ENA | Latte::E_COHER_CNTL::SH_ACTION_ENA; - if (surfaceSyncFlags != 0) - { - GX2ReserveCmdSpace(5); - // write PM4 command - gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SURFACE_SYNC, 4)); // IT_SURFACE_SYNC + 4 data dwords - gx2WriteGather_submitU32AsBE(surfaceSyncFlags); - gx2WriteGather_submitU32AsBE((invalidationSize + 0xFF) >> 8); // size - gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(invalidationAddr) >> 8); // base address (divided by 0x100) - gx2WriteGather_submitU32AsBE(0x00000004); // poll interval - } + if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_SHADER)) + coherCntl |= Latte::E_COHER_CNTL::SH_ACTION_ENA; + + if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_COLOR_BUFFER)) + { + coherCntl |= Latte::E_COHER_CNTL::CB_ACTION_ENA + | Latte::E_COHER_CNTL::CB0_DEST_BASE_ENA | Latte::E_COHER_CNTL::CB1_DEST_BASE_ENA + | Latte::E_COHER_CNTL::CB2_DEST_BASE_ENA | Latte::E_COHER_CNTL::CB3_DEST_BASE_ENA + | Latte::E_COHER_CNTL::CB4_DEST_BASE_ENA | Latte::E_COHER_CNTL::CB5_DEST_BASE_ENA + | Latte::E_COHER_CNTL::CB6_DEST_BASE_ENA | Latte::E_COHER_CNTL::CB7_DEST_BASE_ENA; + } + + if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_DEPTH_BUFFER)) + coherCntl |= Latte::E_COHER_CNTL::DB_ACTION_ENA | Latte::E_COHER_CNTL::DB_DEST_BASE_ENA; + + if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_STREAM_OUT)) + { + coherCntl |= Latte::E_COHER_CNTL::SX_ACTION_ENA + | Latte::E_COHER_CNTL::SO0_DEST_BASE_ENA | Latte::E_COHER_CNTL::SO1_DEST_BASE_ENA + | Latte::E_COHER_CNTL::SO2_DEST_BASE_ENA | Latte::E_COHER_CNTL::SO3_DEST_BASE_ENA; + } + + if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_EXPORT_BUFFER)) + { + coherCntl |= Latte::E_COHER_CNTL::SX_ACTION_ENA + | Latte::E_COHER_CNTL::DB_ACTION_ENA + | Latte::E_COHER_CNTL::CB_ACTION_ENA + | Latte::E_COHER_CNTL::TC_ACTION_ENA + | Latte::E_COHER_CNTL::DEST_BASE_0_ENA; + } + + if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::CPU)) + LatteBufferCache_notifyDCFlush(invalidationAddr, invalidationSize); + + if (coherCntl == static_cast(0)) + return; // no GPU-side invalidation + + coherCntl |= Latte::E_COHER_CNTL::ENGINE_ME; + + uint32 physicalAddr = memory_virtualToPhysical(invalidationAddr); + uint32 sizeUnits = (invalidationSize == 0xFFFFFFFFu) ? 0x00FFFFFFu : ((invalidationSize + 0xFF) >> 8); + + GX2ReserveCmdSpace(5); + gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_SURFACE_SYNC, 4)); + gx2WriteGather_submitU32AsBE(static_cast(coherCntl)); + gx2WriteGather_submitU32AsBE(sizeUnits); + gx2WriteGather_submitU32AsBE(physicalAddr >> 8); + gx2WriteGather_submitU32AsBE(4u); } void GX2MiscInit() diff --git a/src/Cafe/OS/libs/gx2/GX2_Misc.h b/src/Cafe/OS/libs/gx2/GX2_Misc.h index 38a728c1..b15c9e72 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Misc.h +++ b/src/Cafe/OS/libs/gx2/GX2_Misc.h @@ -14,12 +14,28 @@ namespace GX2 DOUBLE_BUFFER = 2, }; + enum class GX2InvalidationFlag : uint32 + { + GPU_ATTRIB = (1<<0), + GPU_TEXTURE = (1<<1), + GPU_UNIFORM_BLOCK = (1<<2), + GPU_SHADER = (1<<3), + GPU_COLOR_BUFFER = (1<<4), + GPU_DEPTH_BUFFER = (1<<5), + CPU = (1<<6), + GPU_STREAM_OUT = (1<<7), + GPU_EXPORT_BUFFER = (1<<8) + }; + void _GX2DriverReset(); void GX2SetTVBuffer(void* imageBuffePtr, uint32 imageBufferSize, E_TVRES tvResolutionMode, uint32 surfaceFormat, E_TVBUFFERMODE bufferMode); void GX2SetTVGamma(float gamma); - void GX2Invalidate(uint32 invalidationFlags, MPTR invalidationAddr, uint32 invalidationSize); + void GX2Invalidate(GX2InvalidationFlag invalidationFlags, MPTR invalidationAddr, uint32 invalidationSize); void GX2MiscInit(); -}; \ No newline at end of file + +}; + +ENABLE_BITMASK_OPERATORS(GX2::GX2InvalidationFlag); diff --git a/src/Cafe/OS/libs/proc_ui/proc_ui.cpp b/src/Cafe/OS/libs/proc_ui/proc_ui.cpp index 061886ca..653a0f7d 100644 --- a/src/Cafe/OS/libs/proc_ui/proc_ui.cpp +++ b/src/Cafe/OS/libs/proc_ui/proc_ui.cpp @@ -776,7 +776,7 @@ namespace proc_ui uint32be memBoundSize; OSGetMemBound(1, &memBound, &memBoundSize); OSBlockMove(memBound.GetPtr(), s_mem1StorageBasePtr, memBoundSize, true); - GX2::GX2Invalidate(0x40, s_mem1StorageBasePtr.GetMPTR(), memBoundSize); + GX2::GX2Invalidate(GX2::GX2InvalidationFlag::CPU, s_mem1StorageBasePtr.GetMPTR(), memBoundSize); } if (s_bucketStorageBasePtr) { @@ -784,7 +784,7 @@ namespace proc_ui uint32be memBoundSize; OSGetForegroundBucketFreeArea(&memBound, &memBoundSize); OSBlockMove(memBound.GetPtr(), s_bucketStorageBasePtr, memBoundSize, true); - GX2::GX2Invalidate(0x40, memBound.GetMPTR(), memBoundSize); + GX2::GX2Invalidate(GX2::GX2InvalidationFlag::CPU, memBound.GetMPTR(), memBoundSize); } }