diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 8a2fed055f..249d4a9f69 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -284,6 +284,10 @@ namespace MIPSComp MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; int o = op>>26; + // 16-bit accesses can go through the depth layout (Memory::DepthMirrored16). + if (Memory::DepthMirrorsActive() && (o == 33 || o == 37 || o == 41)) { + DISABLE; + } if (((op >> 29) & 1) == 0 && rt == MIPS_REG_ZERO) { // Don't load anything into $zr return; diff --git a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp index 3b8ac2859d..f293b94329 100644 --- a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp +++ b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp @@ -279,6 +279,10 @@ namespace MIPSComp { MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; int o = op >> 26; + // 16-bit accesses can go through the depth layout (Memory::DepthMirrored16). + if (Memory::DepthMirrorsActive() && (o == 33 || o == 37 || o == 41)) { + DISABLE; + } if (((op >> 29) & 1) == 0 && rt == MIPS_REG_ZERO) { // Don't load anything into $zr return; diff --git a/Core/MIPS/IR/IRNativeCommon.cpp b/Core/MIPS/IR/IRNativeCommon.cpp index 399089b8dc..07d0787c17 100644 --- a/Core/MIPS/IR/IRNativeCommon.cpp +++ b/Core/MIPS/IR/IRNativeCommon.cpp @@ -280,10 +280,17 @@ void IRNativeBackend::CompileIRInst(IRInst inst) { CompIR_Div(inst); break; - case IROp::Load8: - case IROp::Load8Ext: case IROp::Load16: case IROp::Load16Ext: + // 16-bit accesses can go through the depth layout (Memory::DepthMirrored16). + if (Memory::DepthMirrorsActive()) + CompIR_Generic(inst); + else + CompIR_Load(inst); + break; + + case IROp::Load8: + case IROp::Load8Ext: case IROp::Load32: case IROp::Load32Linked: CompIR_Load(inst); @@ -302,8 +309,14 @@ void IRNativeBackend::CompileIRInst(IRInst inst) { CompIR_VecLoad(inst); break; - case IROp::Store8: case IROp::Store16: + if (Memory::DepthMirrorsActive()) + CompIR_Generic(inst); + else + CompIR_Store(inst); + break; + + case IROp::Store8: case IROp::Store32: CompIR_Store(inst); break; diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 11d3059dde..1961892748 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -6,6 +6,7 @@ #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Log.h" #include "Core/Config.h" +#include "Core/MemMap.h" #include "Core/MIPS/MIPSVFPUUtils.h" #include "Core/MIPS/IR/IRAnalysis.h" #include "Core/MIPS/IR/IRInterpreter.h" @@ -1357,7 +1358,10 @@ bool ReduceLoads(const IRWriter &in, IRWriter &out, const IROptions &opts) { for (int i = 0, n = (int)in.GetInstructions().size(); i < n; i++) { IRInst inst = in.GetInstructions()[i]; - if (inst.op == IROp::Load32 || inst.op == IROp::Load16 || inst.op == IROp::Load16Ext) { + // With the depth mirrors, a load's width decides whether it goes through the depth layout + // (Memory::DepthMirrored16), so 16-bit loads keep theirs, and no load becomes one. + const bool depthMirrors = Memory::DepthMirrorsActive(); + if (inst.op == IROp::Load32 || ((inst.op == IROp::Load16 || inst.op == IROp::Load16Ext) && !depthMirrors)) { int dest = IRDestGPR(GetIRMeta(inst)); for (int j = i + 1; j < n; j++) { const IRInstMeta laterInst = GetIRMeta(in.GetInstructions()[j]); @@ -1375,7 +1379,7 @@ bool ReduceLoads(const IRWriter &in, IRWriter &out, const IROptions &opts) { if (mask == 0xff) { nextSkip = j; } - } else if ((mask & 0xffff0000) == 0 && inst.op == IROp::Load32) { + } else if ((mask & 0xffff0000) == 0 && inst.op == IROp::Load32 && !depthMirrors) { inst.op = IROp::Load16; if (mask == 0xffff) { nextSkip = j; diff --git a/Core/MIPS/x86/CompLoadStore.cpp b/Core/MIPS/x86/CompLoadStore.cpp index d010cb3777..501f8f641c 100644 --- a/Core/MIPS/x86/CompLoadStore.cpp +++ b/Core/MIPS/x86/CompLoadStore.cpp @@ -302,6 +302,10 @@ namespace MIPSComp { MIPSGPReg rs = _RS; MIPSGPReg rt = _RT; int o = op>>26; + // 16-bit accesses can go through the depth layout (Memory::DepthMirrored16). + if (Memory::DepthMirrorsActive() && (o == 33 || o == 37 || o == 41)) { + DISABLE; + } if (((op >> 29) & 1) == 0 && rt == MIPS_REG_ZERO) { // Don't load anything into $zr return; diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index c925c541e2..2f2c0b40a8 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -82,6 +82,18 @@ static u8 *m_pUncachedVRAM[4]; // Required for HD Remasters to work properly. // This replaces RAM_NORMAL_SIZE at runtime. u32 g_MemorySize; +int g_depthTranslationIndex = DepthTranslationIndex(0x400); + +bool DepthMirrorsActive() { + return PSP_CoreParameter().gpuCore == GPUCORE_SOFTWARE; +} + +u32 DepthMirroredSlow(u32 address) { + if (!DepthMirrorsActive()) + return address; + const DepthLayout &layout = DEPTH_LAYOUTS[(address >> 22) & 1][g_depthTranslationIndex]; + return (address & 0x40000000) | 0x04000000 | layout.Stored(address & 0x001FFFFF); +} // Used to store the PSP model on game startup. u32 g_PSPModel; u32 g_UserPartitionSize; diff --git a/Core/MemMap.h b/Core/MemMap.h index 83fe8ec2a0..9bda44338d 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -29,6 +29,7 @@ #include "Common/CommonTypes.h" #include "Common/Swap.h" #include "Core/Opcode.h" +#include "GPU/Common/DepthSwizzle.h" // PPSSPP is very aggressive about trying to do memory accesses directly, for speed. // This can be a problem when debugging though, as stray memory reads and writes will @@ -192,7 +193,25 @@ inline float ReadUnchecked_Float(const u32 address) { #endif } -inline u16 ReadUnchecked_U16(const u32 address) { +// With the software renderer, which stores depth as the GE does, VRAM's 0x04200000 and 0x04600000 mirrors show it +// through the GE's 16-bit and 32-bit color layouts (GPU/Common/DepthSwizzle.h, ppsspp-re geprobe exp88). Only 16-bit +// accesses go through them: games read and write depth with nothing else. The layouts are fixed but for the EDRAM +// address translation, which the software renderer keeps here as DepthTranslationIndex(). +extern int g_depthTranslationIndex; + +// Whether the depth mirrors apply the layouts: only with the software renderer. Hardware renderers keep depth +// plain and handle the games that need the layout their own way. +bool DepthMirrorsActive(); +u32 DepthMirroredSlow(u32 address); + +inline u32 DepthMirrored16(u32 address) { + if ((address & 0xBFA00000) == 0x04200000) + return DepthMirroredSlow(address); + return address; +} + +inline u16 ReadUnchecked_U16(u32 address) { + address = DepthMirrored16(address); #ifdef MASKED_PSP_MEMORY return *(u16_le *)(base + (address & MEMVIEW32_MASK)); #else @@ -233,6 +252,7 @@ inline void WriteUnchecked_Float(float data, u32 address) { } inline void WriteUnchecked_U16(u16 data, u32 address) { + address = DepthMirrored16(address); #ifdef MASKED_PSP_MEMORY *(u16_le *)(base + (address & MEMVIEW32_MASK)) = data; #else diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index af5419b1e3..13456028ae 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -134,7 +134,7 @@ u8 ReadOrException_U8(const u32 address) { u16 ReadOrException_U16(const u32 address) { u16_le value = 0; - ReadMemoryOrException(value, address); + ReadMemoryOrException(value, DepthMirrored16(address)); return (u16)value; } @@ -155,7 +155,7 @@ void WriteOrException_U8(const u8 _Data, const u32 address) { } void WriteOrException_U16(const u16 _Data, const u32 address) { - WriteMemoryOrException(address, _Data); + WriteMemoryOrException(DepthMirrored16(address), _Data); } void WriteOrException_U32(const u32 _Data, const u32 address) { diff --git a/GPU/Common/DepthSwizzle.h b/GPU/Common/DepthSwizzle.h index abaf69a52c..e33fa3b6a3 100644 --- a/GPU/Common/DepthSwizzle.h +++ b/GPU/Common/DepthSwizzle.h @@ -33,13 +33,13 @@ struct DepthLayout { int rotShift = 0; uint32_t xorBits = 0; - uint32_t Stored(uint32_t offset) const { + constexpr uint32_t Stored(uint32_t offset) const { const uint32_t f = offset & rotMask; return ((offset & ~rotMask) | (((f << 1) | (f >> rotShift)) & rotMask)) ^ xorBits; } }; -inline DepthLayout GetDepthLayout(uint32_t translation, bool color32) { +constexpr DepthLayout GetDepthLayout(uint32_t translation, bool color32) { DepthLayout layout; if (translation == 0) { layout.xorBits = 0x600; @@ -60,3 +60,27 @@ inline DepthLayout GetDepthLayout(uint32_t translation, bool color32) { } return layout; } + +// The translations as an index 0 to 4: 0, 0x200, 0x400, 0x800, 0x1000. +constexpr int DepthTranslationIndex(uint32_t translation) { + int index = 0; + while (translation >= 0x200 && index < 4) { + translation >>= 1; + index++; + } + return index; +} + +constexpr uint32_t DepthTranslationFromIndex(int index) { + return index == 0 ? 0 : 0x100 << index; +} + +// Every layout there is, by [color32][translation index]. +inline constexpr DepthLayout DEPTH_LAYOUTS[2][5] = { + { GetDepthLayout(0, false), GetDepthLayout(0x200, false), GetDepthLayout(0x400, false), GetDepthLayout(0x800, false), GetDepthLayout(0x1000, false) }, + { GetDepthLayout(0, true), GetDepthLayout(0x200, true), GetDepthLayout(0x400, true), GetDepthLayout(0x800, true), GetDepthLayout(0x1000, true) }, +}; + +// A couple of exp8-exp10's measurements (TestDepthSwizzle has more). +static_assert(DEPTH_LAYOUTS[0][2].Stored(0x088000) == 0x08a040, "16-bit layout at the default translation"); +static_assert(DEPTH_LAYOUTS[1][0].Stored(0x03171e) == 0x03111e, "32-bit layout at translation 0"); diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index d90794c644..28e97ed811 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -475,6 +475,8 @@ void SoftGPU::DeviceRestore(Draw::DrawContext *draw) { } SoftGPU::~SoftGPU() { + // The firmware's default, until the next software renderer sets it. + Memory::g_depthTranslationIndex = DepthTranslationIndex(0x400); if (fbTex) { fbTex->Release(); fbTex = nullptr; @@ -1154,6 +1156,7 @@ void SoftGPU::UpdateDepthBuffer() { depthbuf.base = gstate.getDepthBufAddress() & 0x001FFFF0; depthbuf.translation = GetAddrTranslation(); depthbuf.layout = GetDepthLayout(depthbuf.translation, gstate.FrameBufFormat() == GE_FORMAT_8888); + Memory::g_depthTranslationIndex = DepthTranslationIndex(depthbuf.translation); } void SoftGPU::DoState(PointerWrap &p) { diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 84ef4c6374..f7e41bba1c 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -76,20 +76,6 @@ struct DepthBuffer { } }; -// The index PixelFuncID::depthLayout keeps a translation as (0, 0x200, 0x400, 0x800, 0x1000). -inline int DepthTranslationIndex(uint32_t translation) { - int index = 0; - while (translation >= 0x200 && index < 4) { - translation >>= 1; - index++; - } - return index; -} - -inline uint32_t DepthTranslationFromIndex(int index) { - return index == 0 ? 0 : 0x100 << index; -} - enum class SoftDirty : uint64_t { NONE = 0, diff --git a/pspautotests b/pspautotests index 6729c819b0..c662bd80cd 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 6729c819b01c84c6585b6c6e1648e04c30b4711b +Subproject commit c662bd80cdb312df62086ff87e52174ede5bd3f0