Latte+GX2: Improve GX2Invalidate and implement basic tex invalidation

This commit is contained in:
Exzap
2026-09-07 00:03:06 +02:00
parent c25dbdf86d
commit bda380dd33
7 changed files with 127 additions and 34 deletions

View File

@@ -209,7 +209,7 @@ void LatteCP_skipWords(uint32 wordsToSkip)
LatteCMDPtr LatteCP_itSurfaceSync(LatteCMDPtr cmd)
{
uint32 invalidationFlags = LatteReadCMD();
Latte::E_COHER_CNTL invalidationFlags = static_cast<Latte::E_COHER_CNTL>(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;
}

View File

@@ -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<LatteTexture*> 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)

View File

@@ -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);

View File

@@ -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,

View File

@@ -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<Latte::E_COHER_CNTL>(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<uint32>(coherCntl));
gx2WriteGather_submitU32AsBE(sizeUnits);
gx2WriteGather_submitU32AsBE(physicalAddr >> 8);
gx2WriteGather_submitU32AsBE(4u);
}
void GX2MiscInit()

View File

@@ -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();
};
};
ENABLE_BITMASK_OPERATORS(GX2::GX2InvalidationFlag);

View File

@@ -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);
}
}