mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-30 19:24:23 +01:00
Merge pull request #15275 from unknownbrackets/samplerjit-avx2
Use AVX2 gather for samplerjit
This commit is contained in:
7 files changed
+549
-246
No files matched your search
+19
-13
@@ -1701,16 +1701,15 @@ void XEmitter::PSRLQ(X64Reg reg, int shift)
|
||||
Write8(shift);
|
||||
}
|
||||
|
||||
void XEmitter::PSRLQ(X64Reg reg, OpArg arg)
|
||||
{
|
||||
WriteSSEOp(0x66, 0xd3, reg, arg);
|
||||
}
|
||||
|
||||
void XEmitter::PSRLDQ(X64Reg reg, int shift) {
|
||||
WriteSSEOp(0x66, 0x73, (X64Reg)3, R(reg));
|
||||
Write8(shift);
|
||||
}
|
||||
|
||||
void XEmitter::PSRLW(X64Reg reg, OpArg arg) { WriteSSEOp(0x66, 0xD1, reg, arg); }
|
||||
void XEmitter::PSRLD(X64Reg reg, OpArg arg) { WriteSSEOp(0x66, 0xD2, reg, arg); }
|
||||
void XEmitter::PSRLQ(X64Reg reg, OpArg arg) { WriteSSEOp(0x66, 0xD3, reg, arg); }
|
||||
|
||||
void XEmitter::PSLLW(X64Reg reg, int shift)
|
||||
{
|
||||
WriteSSEOp(0x66, 0x71, (X64Reg)6, R(reg));
|
||||
@@ -1734,6 +1733,10 @@ void XEmitter::PSLLDQ(X64Reg reg, int shift) {
|
||||
Write8(shift);
|
||||
}
|
||||
|
||||
void XEmitter::PSLLW(X64Reg reg, OpArg arg) { WriteSSEOp(0x66, 0xF1, reg, arg); }
|
||||
void XEmitter::PSLLD(X64Reg reg, OpArg arg) { WriteSSEOp(0x66, 0xF2, reg, arg); }
|
||||
void XEmitter::PSLLQ(X64Reg reg, OpArg arg) { WriteSSEOp(0x66, 0xF3, reg, arg); }
|
||||
|
||||
void XEmitter::PSRAW(X64Reg reg, int shift)
|
||||
{
|
||||
WriteSSEOp(0x66, 0x71, (X64Reg)4, R(reg));
|
||||
@@ -1746,6 +1749,9 @@ void XEmitter::PSRAD(X64Reg reg, int shift)
|
||||
Write8(shift);
|
||||
}
|
||||
|
||||
void XEmitter::PSRAW(X64Reg reg, OpArg arg) { WriteSSEOp(0x66, 0xE1, reg, arg); }
|
||||
void XEmitter::PSRAD(X64Reg reg, OpArg arg) { WriteSSEOp(0x66, 0xE2, reg, arg); }
|
||||
|
||||
void XEmitter::PMULLW(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0xD5, dest, arg);}
|
||||
void XEmitter::PMULHW(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0xE5, dest, arg);}
|
||||
void XEmitter::PMULHUW(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0xE4, dest, arg);}
|
||||
@@ -2240,20 +2246,20 @@ void XEmitter::VGATHERQPD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VGATHER cannot have overlapped registers");
|
||||
WriteAVX2Op(bits, 0x66, 0x3893, regOp1, regOp2, arg);
|
||||
}
|
||||
void XEmitter::VGATHERDD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VGATHER cannot have overlapped registers");
|
||||
void XEmitter::VPGATHERDD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VPGATHER cannot have overlapped registers");
|
||||
WriteAVX2Op(bits, 0x66, 0x3890, regOp1, regOp2, arg);
|
||||
}
|
||||
void XEmitter::VGATHERQD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VGATHER cannot have overlapped registers");
|
||||
void XEmitter::VPGATHERQD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VPGATHER cannot have overlapped registers");
|
||||
WriteAVX2Op(bits, 0x66, 0x3891, regOp1, regOp2, arg);
|
||||
}
|
||||
void XEmitter::VGATHERDQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VGATHER cannot have overlapped registers");
|
||||
void XEmitter::VPGATHERDQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VPGATHER cannot have overlapped registers");
|
||||
WriteAVX2Op(bits, 0x66, 0x3890, regOp1, regOp2, arg, 0, 1);
|
||||
}
|
||||
void XEmitter::VGATHERQQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VGATHER cannot have overlapped registers");
|
||||
void XEmitter::VPGATHERQQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2) {
|
||||
_assert_msg_(regOp1 != regOp2 && !arg.IsIndexedReg(regOp1) && !arg.IsIndexedReg(regOp2), "VPGATHER cannot have overlapped registers");
|
||||
WriteAVX2Op(bits, 0x66, 0x3891, regOp1, regOp2, arg, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
+20
-5
@@ -847,16 +847,31 @@ public:
|
||||
void PSRLW(X64Reg reg, int shift);
|
||||
void PSRLD(X64Reg reg, int shift);
|
||||
void PSRLQ(X64Reg reg, int shift);
|
||||
void PSRLQ(X64Reg reg, OpArg arg);
|
||||
void PSRLDQ(X64Reg reg, int shift);
|
||||
// Note: all values shifted by lowest 64-bit in XMM arg.
|
||||
void PSRLW(X64Reg reg, OpArg arg);
|
||||
// Note: all values shifted by lowest 64-bit in XMM arg.
|
||||
void PSRLD(X64Reg reg, OpArg arg);
|
||||
// Note: both values shifted by lowest 64-bit in XMM arg.
|
||||
void PSRLQ(X64Reg reg, OpArg arg);
|
||||
|
||||
void PSLLW(X64Reg reg, int shift);
|
||||
void PSLLD(X64Reg reg, int shift);
|
||||
void PSLLQ(X64Reg reg, int shift);
|
||||
void PSLLDQ(X64Reg reg, int shift);
|
||||
// Note: all values shifted by lowest 64-bit in XMM arg.
|
||||
void PSLLW(X64Reg reg, OpArg arg);
|
||||
// Note: all values shifted by lowest 64-bit in XMM arg.
|
||||
void PSLLD(X64Reg reg, OpArg arg);
|
||||
// Note: both values shifted by lowest 64-bit in XMM arg.
|
||||
void PSLLQ(X64Reg reg, OpArg arg);
|
||||
|
||||
void PSRAW(X64Reg reg, int shift);
|
||||
void PSRAD(X64Reg reg, int shift);
|
||||
// Note: all values shifted by lowest 64-bit in XMM arg.
|
||||
void PSRAW(X64Reg reg, OpArg arg);
|
||||
// Note: all values shifted by lowest 64-bit in XMM arg.
|
||||
void PSRAD(X64Reg reg, OpArg arg);
|
||||
|
||||
void PMULLW(X64Reg dest, const OpArg &arg);
|
||||
void PMULHW(X64Reg dest, const OpArg &arg);
|
||||
@@ -1239,10 +1254,10 @@ public:
|
||||
void VGATHERDPD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VGATHERQPS(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VGATHERQPD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VGATHERDD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VGATHERQD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VGATHERDQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VGATHERQQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VPGATHERDD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VPGATHERQD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VPGATHERDQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
void VPGATHERQQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
||||
|
||||
void VPSLLVD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
||||
void VPSLLVQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
||||
|
||||
+20
-11
@@ -17,8 +17,10 @@
|
||||
|
||||
#include "Common/Data/Convert/ColorConv.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "GPU/Software/FuncId.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/Software/FuncId.h"
|
||||
|
||||
static_assert(sizeof(SamplerID) == sizeof(SamplerID::fullKey), "Bad sampler ID size");
|
||||
#ifdef SOFTPIXEL_USE_CACHE
|
||||
@@ -361,18 +363,24 @@ void ComputeSamplerID(SamplerID *id_out) {
|
||||
SamplerID id{};
|
||||
|
||||
id.useStandardBufw = true;
|
||||
id.hasStandardMips = true;
|
||||
id.overReadSafe = true;
|
||||
int maxLevel = gstate.isMipmapEnabled() ? gstate.getTextureMaxLevel() : 0;
|
||||
int lastWidth = -1;
|
||||
for (int i = 0; i <= maxLevel; ++i) {
|
||||
if (gstate.getTextureAddress(i) == 0)
|
||||
uint32_t addr = gstate.getTextureAddress(i);
|
||||
if (!Memory::IsValidAddress(addr))
|
||||
id.hasInvalidPtr = true;
|
||||
|
||||
int bufw = GetTextureBufw(i, addr, gstate.getTextureFormat());
|
||||
int bitspp = textureBitsPerPixel[gstate.getTextureFormat()];
|
||||
// We use a 16 byte minimum for all small bufws, so allow those as standard.
|
||||
int w = gstate.getTextureWidth(i);
|
||||
if (w != (gstate.texbufwidth[i] & 0x00001FFF))
|
||||
if (w != bufw && w * bitspp > 128)
|
||||
id.useStandardBufw = false;
|
||||
if (lastWidth != -1 && lastWidth != w * 2)
|
||||
id.hasStandardMips = false;
|
||||
lastWidth = w;
|
||||
|
||||
int h = gstate.getTextureHeight(i);
|
||||
int bytes = h * (bufw * bitspp) / 8;
|
||||
if (bitspp < 32 && !Memory::IsValidAddress(addr + bytes + (32 - bitspp) / 8))
|
||||
id.overReadSafe = false;
|
||||
}
|
||||
id.hasAnyMips = maxLevel != 0;
|
||||
|
||||
@@ -458,9 +466,10 @@ std::string DescribeSamplerID(const SamplerID &id) {
|
||||
if (!id.useStandardBufw) {
|
||||
name += ":BUFW";
|
||||
}
|
||||
if (!id.hasStandardMips) {
|
||||
name += ":XMIP";
|
||||
} else if (id.hasAnyMips) {
|
||||
if (!id.overReadSafe) {
|
||||
name += ":XRD";
|
||||
}
|
||||
if (id.hasAnyMips) {
|
||||
name += ":MIP";
|
||||
}
|
||||
if (id.linear) {
|
||||
|
||||
@@ -176,8 +176,8 @@ struct SamplerID {
|
||||
uint8_t texFunc : 3;
|
||||
bool useTextureAlpha : 1;
|
||||
bool useColorDoubling : 1;
|
||||
bool hasStandardMips : 1;
|
||||
bool hasAnyMips : 1;
|
||||
bool overReadSafe : 1;
|
||||
bool fetch : 1;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -103,6 +103,7 @@ struct RegCache {
|
||||
VEC_RESULT1 = 0x0002,
|
||||
VEC_U1 = 0x0003,
|
||||
VEC_V1 = 0x0004,
|
||||
VEC_INDEX = 0x0005,
|
||||
|
||||
GEN_SRC_ALPHA = 0x0100,
|
||||
GEN_GSTATE = 0x0101,
|
||||
|
||||
@@ -101,6 +101,10 @@ private:
|
||||
bool Jit_PrepareDataOffsets(const SamplerID &id, Rasterizer::RegCache::Reg uReg, Rasterizer::RegCache::Reg vReg, bool level1);
|
||||
bool Jit_PrepareDataDirectOffsets(const SamplerID &id, Rasterizer::RegCache::Reg uReg, Rasterizer::RegCache::Reg vReg, bool level1, int bitsPerTexel);
|
||||
bool Jit_PrepareDataSwizzledOffsets(const SamplerID &id, Rasterizer::RegCache::Reg uReg, Rasterizer::RegCache::Reg vReg, bool level1, int bitsPerTexel);
|
||||
bool Jit_FetchQuad(const SamplerID &id, bool level1);
|
||||
bool Jit_GetDataQuad(const SamplerID &id, bool level1, int bitsPerTexel);
|
||||
bool Jit_TransformClutIndexQuad(const SamplerID &id, int bitsPerIndex);
|
||||
bool Jit_ReadClutQuad(const SamplerID &id, bool level1);
|
||||
bool Jit_BlendQuad(const SamplerID &id, bool level1);
|
||||
bool Jit_DecodeQuad(const SamplerID &id, bool level1);
|
||||
bool Jit_Decode5650Quad(const SamplerID &id, Rasterizer::RegCache::Reg quadReg);
|
||||
|
||||
+484
-216
@@ -411,77 +411,71 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
|
||||
BeginWrite();
|
||||
Describe("Init");
|
||||
|
||||
// Set the stackArgPos_ so we can use it in the nearest part too.
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
// RET + shadow space + 8 byte space for color arg (the Win32 ABI is kinda ugly.)
|
||||
stackArgPos_ = 8 + 32 + 8;
|
||||
// Plus 32 for R12-R15.
|
||||
stackArgPos_ += 32;
|
||||
// Plus XMM6-XMM9 and 8 to align.
|
||||
stackArgPos_ += 16 * 4 + 8;
|
||||
#else
|
||||
stackArgPos_ = 32;
|
||||
#endif
|
||||
|
||||
regCache_.SetupABI({
|
||||
RegCache::GEN_ARG_U,
|
||||
RegCache::GEN_ARG_V,
|
||||
RegCache::GEN_ARG_TEXPTR,
|
||||
RegCache::GEN_ARG_BUFW,
|
||||
RegCache::GEN_ARG_LEVEL,
|
||||
// Avoid clobber.
|
||||
RegCache::GEN_ARG_LEVELFRAC,
|
||||
});
|
||||
regCache_.ChangeReg(RAX, RegCache::GEN_RESULT);
|
||||
auto lockReg = [&](X64Reg r, RegCache::Purpose p) {
|
||||
regCache_.ChangeReg(r, p);
|
||||
regCache_.ForceRetain(p);
|
||||
};
|
||||
lockReg(XMM0, RegCache::VEC_ARG_U);
|
||||
lockReg(XMM1, RegCache::VEC_ARG_V);
|
||||
lockReg(XMM5, RegCache::VEC_RESULT);
|
||||
#if !PPSSPP_PLATFORM(WINDOWS)
|
||||
if (id.hasAnyMips) {
|
||||
lockReg(XMM6, RegCache::VEC_U1);
|
||||
lockReg(XMM7, RegCache::VEC_V1);
|
||||
lockReg(XMM8, RegCache::VEC_RESULT1);
|
||||
}
|
||||
lockReg(XMM9, RegCache::VEC_ARG_COLOR);
|
||||
#endif
|
||||
// We don't use stackArgPos_ here, this is just for DXT.
|
||||
stackArgPos_ = -1;
|
||||
|
||||
// Let's drop some helpful constants here.
|
||||
WriteConstantPool(id);
|
||||
|
||||
// We'll first write the nearest sampler, which we will CALL.
|
||||
// This may differ slightly based on the "linear" flag.
|
||||
const u8 *nearest = AlignCode16();
|
||||
const u8 *nearest = nullptr;
|
||||
if (id.TexFmt() >= GE_TFMT_DXT1) {
|
||||
regCache_.SetupABI({
|
||||
RegCache::GEN_ARG_U,
|
||||
RegCache::GEN_ARG_V,
|
||||
RegCache::GEN_ARG_TEXPTR,
|
||||
RegCache::GEN_ARG_BUFW,
|
||||
RegCache::GEN_ARG_LEVEL,
|
||||
// Avoid clobber.
|
||||
RegCache::GEN_ARG_LEVELFRAC,
|
||||
});
|
||||
regCache_.ChangeReg(RAX, RegCache::GEN_RESULT);
|
||||
auto lockReg = [&](X64Reg r, RegCache::Purpose p) {
|
||||
regCache_.ChangeReg(r, p);
|
||||
regCache_.ForceRetain(p);
|
||||
};
|
||||
lockReg(XMM0, RegCache::VEC_ARG_U);
|
||||
lockReg(XMM1, RegCache::VEC_ARG_V);
|
||||
lockReg(XMM5, RegCache::VEC_RESULT);
|
||||
#if !PPSSPP_PLATFORM(WINDOWS)
|
||||
if (id.hasAnyMips) {
|
||||
lockReg(XMM6, RegCache::VEC_U1);
|
||||
lockReg(XMM7, RegCache::VEC_V1);
|
||||
lockReg(XMM8, RegCache::VEC_RESULT1);
|
||||
}
|
||||
lockReg(XMM9, RegCache::VEC_ARG_COLOR);
|
||||
#endif
|
||||
|
||||
if (!Jit_ReadTextureFormat(id)) {
|
||||
regCache_.Reset(false);
|
||||
EndWrite();
|
||||
ResetCodePtr(GetOffset(nearest));
|
||||
return nullptr;
|
||||
// We'll first write the nearest sampler, which we will CALL.
|
||||
// This may differ slightly based on the "linear" flag.
|
||||
const u8 *nearest = AlignCode16();
|
||||
|
||||
if (!Jit_ReadTextureFormat(id)) {
|
||||
regCache_.Reset(false);
|
||||
EndWrite();
|
||||
ResetCodePtr(GetOffset(nearest));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Describe("Init");
|
||||
RET();
|
||||
|
||||
regCache_.ForceRelease(RegCache::VEC_ARG_U);
|
||||
regCache_.ForceRelease(RegCache::VEC_ARG_V);
|
||||
regCache_.ForceRelease(RegCache::VEC_RESULT);
|
||||
|
||||
auto unlockOptReg = [&](RegCache::Purpose p) {
|
||||
if (regCache_.Has(p))
|
||||
regCache_.ForceRelease(p);
|
||||
};
|
||||
unlockOptReg(RegCache::GEN_ARG_LEVEL);
|
||||
unlockOptReg(RegCache::GEN_ARG_LEVELFRAC);
|
||||
unlockOptReg(RegCache::VEC_U1);
|
||||
unlockOptReg(RegCache::VEC_V1);
|
||||
unlockOptReg(RegCache::VEC_RESULT1);
|
||||
unlockOptReg(RegCache::VEC_ARG_COLOR);
|
||||
regCache_.Reset(true);
|
||||
}
|
||||
|
||||
Describe("Init");
|
||||
RET();
|
||||
|
||||
regCache_.ForceRelease(RegCache::VEC_ARG_U);
|
||||
regCache_.ForceRelease(RegCache::VEC_ARG_V);
|
||||
regCache_.ForceRelease(RegCache::VEC_RESULT);
|
||||
|
||||
auto unlockOptReg = [&](RegCache::Purpose p) {
|
||||
if (regCache_.Has(p))
|
||||
regCache_.ForceRelease(p);
|
||||
};
|
||||
unlockOptReg(RegCache::GEN_ARG_LEVEL);
|
||||
unlockOptReg(RegCache::GEN_ARG_LEVELFRAC);
|
||||
unlockOptReg(RegCache::VEC_U1);
|
||||
unlockOptReg(RegCache::VEC_V1);
|
||||
unlockOptReg(RegCache::VEC_RESULT1);
|
||||
unlockOptReg(RegCache::VEC_ARG_COLOR);
|
||||
regCache_.Reset(true);
|
||||
|
||||
// Now the actual linear func, which is exposed externally.
|
||||
const u8 *start = AlignCode16();
|
||||
Describe("Init");
|
||||
@@ -549,13 +543,15 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_LEVELFRAC);
|
||||
}
|
||||
|
||||
// Save prim color for later in a different XMM too.
|
||||
X64Reg primColorReg = regCache_.Find(RegCache::VEC_ARG_COLOR);
|
||||
MOVDQA(XMM9, R(primColorReg));
|
||||
regCache_.Unlock(primColorReg, RegCache::VEC_ARG_COLOR);
|
||||
regCache_.ForceRelease(RegCache::VEC_ARG_COLOR);
|
||||
regCache_.ChangeReg(XMM9, RegCache::VEC_ARG_COLOR);
|
||||
regCache_.ForceRetain(RegCache::VEC_ARG_COLOR);
|
||||
// Save prim color for later in a different XMM too if we're using the nearest helper.
|
||||
if (nearest != nullptr) {
|
||||
X64Reg primColorReg = regCache_.Find(RegCache::VEC_ARG_COLOR);
|
||||
MOVDQA(XMM9, R(primColorReg));
|
||||
regCache_.Unlock(primColorReg, RegCache::VEC_ARG_COLOR);
|
||||
regCache_.ForceRelease(RegCache::VEC_ARG_COLOR);
|
||||
regCache_.ChangeReg(XMM9, RegCache::VEC_ARG_COLOR);
|
||||
regCache_.ForceRetain(RegCache::VEC_ARG_COLOR);
|
||||
}
|
||||
|
||||
// We also want to save src and bufw for later. Might be in a reg already.
|
||||
if (regCache_.Has(RegCache::GEN_ARG_TEXPTR_PTR)) {
|
||||
@@ -686,11 +682,15 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
|
||||
regCache_.Unlock(vecResultReg, level1 ? RegCache::VEC_RESULT1 : RegCache::VEC_RESULT);
|
||||
};
|
||||
|
||||
Describe("Calls");
|
||||
doNearestCall(0, false);
|
||||
doNearestCall(4, false);
|
||||
doNearestCall(8, false);
|
||||
doNearestCall(12, false);
|
||||
if (nearest != nullptr) {
|
||||
Describe("Calls");
|
||||
doNearestCall(0, false);
|
||||
doNearestCall(4, false);
|
||||
doNearestCall(8, false);
|
||||
doNearestCall(12, false);
|
||||
} else {
|
||||
success = success && Jit_FetchQuad(id, false);
|
||||
}
|
||||
|
||||
if (id.hasAnyMips) {
|
||||
Describe("MipsCalls");
|
||||
@@ -713,10 +713,15 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
|
||||
ADD(32, MDisp(RSP, stackArgPos_ + 16), Imm8(1));
|
||||
}
|
||||
|
||||
doNearestCall(0, true);
|
||||
doNearestCall(4, true);
|
||||
doNearestCall(8, true);
|
||||
doNearestCall(12, true);
|
||||
if (nearest != nullptr) {
|
||||
Describe("MipsCalls");
|
||||
doNearestCall(0, true);
|
||||
doNearestCall(4, true);
|
||||
doNearestCall(8, true);
|
||||
doNearestCall(12, true);
|
||||
} else {
|
||||
success = success && Jit_FetchQuad(id, true);
|
||||
}
|
||||
|
||||
SetJumpTarget(skip);
|
||||
}
|
||||
@@ -931,6 +936,368 @@ RegCache::Reg SamplerJitCache::GetGState() {
|
||||
return regCache_.Find(RegCache::GEN_GSTATE);
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_FetchQuad(const SamplerID &id, bool level1) {
|
||||
bool success = true;
|
||||
switch (id.TexFmt()) {
|
||||
case GE_TFMT_5650:
|
||||
case GE_TFMT_5551:
|
||||
case GE_TFMT_4444:
|
||||
success = Jit_GetDataQuad(id, level1, 16);
|
||||
// Mask away the high bits, if loaded via AVX2.
|
||||
if (cpu_info.bAVX2) {
|
||||
X64Reg destReg = regCache_.Find(level1 ? RegCache::VEC_RESULT1 : RegCache::VEC_RESULT);
|
||||
PSLLD(destReg, 16);
|
||||
PSRLD(destReg, 16);
|
||||
regCache_.Unlock(destReg, level1 ? RegCache::VEC_RESULT1 : RegCache::VEC_RESULT);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_TFMT_8888:
|
||||
success = Jit_GetDataQuad(id, level1, 32);
|
||||
break;
|
||||
|
||||
case GE_TFMT_CLUT32:
|
||||
success = Jit_GetDataQuad(id, level1, 32);
|
||||
if (success)
|
||||
success = Jit_TransformClutIndexQuad(id, 32);
|
||||
if (success)
|
||||
success = Jit_ReadClutQuad(id, level1);
|
||||
break;
|
||||
|
||||
case GE_TFMT_CLUT16:
|
||||
success = Jit_GetDataQuad(id, level1, 16);
|
||||
if (success)
|
||||
success = Jit_TransformClutIndexQuad(id, 16);
|
||||
if (success)
|
||||
success = Jit_ReadClutQuad(id, level1);
|
||||
break;
|
||||
|
||||
case GE_TFMT_CLUT8:
|
||||
success = Jit_GetDataQuad(id, level1, 8);
|
||||
if (success)
|
||||
success = Jit_TransformClutIndexQuad(id, 8);
|
||||
if (success)
|
||||
success = Jit_ReadClutQuad(id, level1);
|
||||
break;
|
||||
|
||||
case GE_TFMT_CLUT4:
|
||||
success = Jit_GetDataQuad(id, level1, 4);
|
||||
if (success)
|
||||
success = Jit_TransformClutIndexQuad(id, 4);
|
||||
if (success)
|
||||
success = Jit_ReadClutQuad(id, level1);
|
||||
break;
|
||||
|
||||
case GE_TFMT_DXT1:
|
||||
case GE_TFMT_DXT3:
|
||||
case GE_TFMT_DXT5:
|
||||
// No SIMD version currently, should use nearest helper path.
|
||||
success = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_GetDataQuad(const SamplerID &id, bool level1, int bitsPerTexel) {
|
||||
Describe("DataQuad");
|
||||
bool success = true;
|
||||
|
||||
X64Reg baseReg = regCache_.Alloc(RegCache::GEN_ARG_TEXPTR);
|
||||
X64Reg srcReg = regCache_.Find(RegCache::GEN_ARG_TEXPTR_PTR);
|
||||
MOV(64, R(baseReg), MDisp(srcReg, level1 ? 8 : 0));
|
||||
regCache_.Unlock(srcReg, RegCache::GEN_ARG_TEXPTR_PTR);
|
||||
|
||||
X64Reg destReg = INVALID_REG;
|
||||
if (id.TexFmt() >= GE_TFMT_CLUT4 && id.TexFmt() <= GE_TFMT_CLUT32)
|
||||
destReg = regCache_.Alloc(RegCache::VEC_INDEX);
|
||||
else
|
||||
destReg = regCache_.Find(level1 ? RegCache::VEC_RESULT1 : RegCache::VEC_RESULT);
|
||||
|
||||
X64Reg byteOffsetReg = regCache_.Find(level1 ? RegCache::VEC_V1 : RegCache::VEC_ARG_V);
|
||||
if (cpu_info.bAVX2 && id.overReadSafe) {
|
||||
// We have to set a mask for which values to load. Load all 4.
|
||||
// Note this is overwritten with zeroes by the gather instruction.
|
||||
X64Reg maskReg = regCache_.Alloc(RegCache::VEC_TEMP0);
|
||||
PCMPEQD(maskReg, R(maskReg));
|
||||
VPGATHERDD(128, destReg, MComplex(baseReg, byteOffsetReg, SCALE_1, 0), maskReg);
|
||||
regCache_.Release(maskReg, RegCache::VEC_TEMP0);
|
||||
} else {
|
||||
if (bitsPerTexel != 32)
|
||||
PXOR(destReg, R(destReg));
|
||||
|
||||
// Grab each value separately... try to use the right memory access size.
|
||||
X64Reg temp2Reg = regCache_.Alloc(RegCache::GEN_TEMP2);
|
||||
if (cpu_info.bSSE4_1) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
PEXTRD(R(temp2Reg), byteOffsetReg, i);
|
||||
if (bitsPerTexel <= 8)
|
||||
PINSRB(destReg, MComplex(baseReg, temp2Reg, SCALE_1, 0), i * 4);
|
||||
else if (bitsPerTexel == 16)
|
||||
PINSRW(destReg, MComplex(baseReg, temp2Reg, SCALE_1, 0), i * 2);
|
||||
else if (bitsPerTexel == 32)
|
||||
PINSRD(destReg, MComplex(baseReg, temp2Reg, SCALE_1, 0), i);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
MOVD_xmm(R(temp2Reg), byteOffsetReg);
|
||||
if (i != 3)
|
||||
PSRLDQ(byteOffsetReg, 4);
|
||||
if (bitsPerTexel <= 8) {
|
||||
MOVZX(32, 8, temp2Reg, MComplex(baseReg, temp2Reg, SCALE_2, 0));
|
||||
PINSRW(destReg, R(temp2Reg), i * 2);
|
||||
} else if (bitsPerTexel == 16) {
|
||||
PINSRW(destReg, MComplex(baseReg, temp2Reg, SCALE_2, 0), i * 2);
|
||||
} else if (bitsPerTexel == 32) {
|
||||
if (i == 0) {
|
||||
MOVD_xmm(destReg, MComplex(baseReg, temp2Reg, SCALE_2, 0));
|
||||
} else {
|
||||
// Maybe a temporary would be better, but this path should be rare.
|
||||
PINSRW(destReg, MComplex(baseReg, temp2Reg, SCALE_2, 0), i * 2);
|
||||
PINSRW(destReg, MComplex(baseReg, temp2Reg, SCALE_2, 2), i * 2 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
regCache_.Release(temp2Reg, RegCache::GEN_TEMP2);
|
||||
}
|
||||
regCache_.Unlock(byteOffsetReg, level1 ? RegCache::VEC_V1 : RegCache::VEC_ARG_V);
|
||||
regCache_.Release(baseReg, RegCache::GEN_ARG_TEXPTR);
|
||||
|
||||
if (bitsPerTexel == 4) {
|
||||
// Take only lowest bit, multiply by 4 with shifting.
|
||||
X64Reg uReg = regCache_.Find(level1 ? RegCache::VEC_U1 : RegCache::VEC_ARG_U);
|
||||
// Next, shift away based on the odd U bits.
|
||||
if (cpu_info.bAVX2) {
|
||||
// This is really convenient with AVX. Just make the bit into a shift amount.
|
||||
PSLLD(uReg, 31);
|
||||
PSRLD(uReg, 29);
|
||||
VPSRLVD(128, destReg, destReg, R(uReg));
|
||||
} else {
|
||||
// This creates a mask - FFFFFFFF to shift, zero otherwise.
|
||||
PSLLD(uReg, 31);
|
||||
PSRAD(uReg, 31);
|
||||
|
||||
X64Reg unshiftedReg = regCache_.Alloc(RegCache::VEC_TEMP0);
|
||||
MOVDQA(unshiftedReg, R(destReg));
|
||||
PSRLD(destReg, 4);
|
||||
// Mask destReg (shifted) and reverse uReg to unshifted masked.
|
||||
PAND(destReg, R(uReg));
|
||||
PANDN(uReg, R(unshiftedReg));
|
||||
// Now combine.
|
||||
POR(destReg, R(uReg));
|
||||
regCache_.Release(unshiftedReg, RegCache::VEC_TEMP0);
|
||||
}
|
||||
regCache_.Unlock(uReg, level1 ? RegCache::VEC_U1 : RegCache::VEC_ARG_U);
|
||||
}
|
||||
|
||||
if (id.TexFmt() >= GE_TFMT_CLUT4 && id.TexFmt() <= GE_TFMT_CLUT32)
|
||||
regCache_.Unlock(destReg, RegCache::VEC_INDEX);
|
||||
else
|
||||
regCache_.Unlock(destReg, level1 ? RegCache::VEC_RESULT1 : RegCache::VEC_RESULT);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_TransformClutIndexQuad(const SamplerID &id, int bitsPerIndex) {
|
||||
Describe("TrCLUTQuad");
|
||||
GEPaletteFormat fmt = id.ClutFmt();
|
||||
if (!id.hasClutShift && !id.hasClutMask && !id.hasClutOffset) {
|
||||
// This is simple - just mask.
|
||||
X64Reg indexReg = regCache_.Find(RegCache::VEC_INDEX);
|
||||
// Mask to 8 bits for CLUT8/16/32, 4 bits for CLUT4.
|
||||
PSLLD(indexReg, bitsPerIndex >= 8 ? 24 : 28);
|
||||
PSRLD(indexReg, bitsPerIndex >= 8 ? 24 : 28);
|
||||
regCache_.Unlock(indexReg, RegCache::VEC_INDEX);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
X64Reg indexReg = regCache_.Find(RegCache::VEC_INDEX);
|
||||
bool maskedIndex = false;
|
||||
|
||||
// Okay, first load the actual gstate clutformat bits we'll use.
|
||||
X64Reg formatReg = regCache_.Alloc(RegCache::VEC_TEMP0);
|
||||
X64Reg gstateReg = GetGState();
|
||||
if (cpu_info.bAVX2 && !id.hasClutShift)
|
||||
VPBROADCASTD(128, formatReg, MDisp(gstateReg, offsetof(GPUgstate, clutformat)));
|
||||
else
|
||||
MOVD_xmm(formatReg, MDisp(gstateReg, offsetof(GPUgstate, clutformat)));
|
||||
regCache_.Unlock(gstateReg, RegCache::GEN_GSTATE);
|
||||
|
||||
// Shift = (clutformat >> 2) & 0x1F
|
||||
if (id.hasClutShift) {
|
||||
// Before shifting, let's mask if needed (we always read 32 bits.)
|
||||
// We have to do this here, because the bits should be zero even if F is used as a mask.
|
||||
if (bitsPerIndex < 32) {
|
||||
PSLLD(indexReg, 32 - bitsPerIndex);
|
||||
PSRLD(indexReg, 32 - bitsPerIndex);
|
||||
maskedIndex = true;
|
||||
}
|
||||
|
||||
X64Reg shiftReg = regCache_.Alloc(RegCache::VEC_TEMP1);
|
||||
// Shift against walls to get 5 bits after the rightmost 2.
|
||||
if (cpu_info.bAVX) {
|
||||
VPSLLD(128, shiftReg, formatReg, 32 - 7);
|
||||
} else {
|
||||
MOVDQA(shiftReg, R(formatReg));
|
||||
PSLLD(shiftReg, 32 - 7);
|
||||
}
|
||||
PSRLD(shiftReg, 32 - 5);
|
||||
// The other lanes are zero, so we can use PSRLD.
|
||||
PSRLD(indexReg, R(shiftReg));
|
||||
regCache_.Release(shiftReg, RegCache::VEC_TEMP1);
|
||||
}
|
||||
|
||||
// With shifting done, we need the format in each lane.
|
||||
if (!cpu_info.bAVX2 || id.hasClutShift)
|
||||
PSHUFD(formatReg, R(formatReg), _MM_SHUFFLE(0, 0, 0, 0));
|
||||
|
||||
// Mask = (clutformat >> 8) & 0xFF
|
||||
if (id.hasClutMask) {
|
||||
X64Reg maskReg = regCache_.Alloc(RegCache::VEC_TEMP1);
|
||||
// If it was CLUT4, grab only 4 bits of the mask.
|
||||
if (cpu_info.bAVX) {
|
||||
VPSLLD(128, maskReg, formatReg, bitsPerIndex == 4 ? 20 : 16);
|
||||
} else {
|
||||
MOVDQA(maskReg, R(formatReg));
|
||||
PSLLD(maskReg, bitsPerIndex == 4 ? 20 : 16);
|
||||
}
|
||||
PSRLD(maskReg, bitsPerIndex == 4 ? 28 : 24);
|
||||
|
||||
PAND(indexReg, R(maskReg));
|
||||
regCache_.Release(maskReg, RegCache::VEC_TEMP1);
|
||||
} else if (!maskedIndex || bitsPerIndex > 8) {
|
||||
// Apply the fixed 8 bit mask (or the CLUT4 mask if we didn't shift.)
|
||||
PSLLD(indexReg, maskedIndex || bitsPerIndex >= 8 ? 24 : 28);
|
||||
PSRLD(indexReg, maskedIndex || bitsPerIndex >= 8 ? 24 : 28);
|
||||
}
|
||||
|
||||
// Offset = (clutformat >> 12) & 0x01F0
|
||||
if (id.hasClutOffset) {
|
||||
// Use walls to extract the 5 bits at 16, and then put them shifted left by 4.
|
||||
int offsetBits = fmt == GE_CMODE_32BIT_ABGR8888 ? 4 : 5;
|
||||
PSRLD(formatReg, 16);
|
||||
PSLLD(formatReg, 32 - offsetBits);
|
||||
PSRLD(formatReg, 32 - offsetBits - 4);
|
||||
|
||||
POR(indexReg, R(formatReg));
|
||||
}
|
||||
|
||||
regCache_.Release(formatReg, RegCache::VEC_TEMP0);
|
||||
regCache_.Unlock(indexReg, RegCache::VEC_INDEX);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_ReadClutQuad(const SamplerID &id, bool level1) {
|
||||
Describe("ReadCLUTQuad");
|
||||
X64Reg indexReg = regCache_.Find(RegCache::VEC_INDEX);
|
||||
|
||||
if (!id.useSharedClut) {
|
||||
X64Reg vecLevelReg = regCache_.Alloc(RegCache::VEC_TEMP0);
|
||||
|
||||
if (regCache_.Has(RegCache::GEN_ARG_LEVEL)) {
|
||||
X64Reg levelReg = regCache_.Find(RegCache::GEN_ARG_LEVEL);
|
||||
MOVD_xmm(vecLevelReg, R(levelReg));
|
||||
regCache_.Unlock(levelReg, RegCache::GEN_ARG_LEVEL);
|
||||
} else {
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
if (cpu_info.bAVX2) {
|
||||
VPBROADCASTD(128, vecLevelReg, MDisp(RSP, stackArgPos_ + 16));
|
||||
} else {
|
||||
MOVD_xmm(vecLevelReg, MDisp(RSP, stackArgPos_ + 16));
|
||||
PSHUFD(vecLevelReg, R(vecLevelReg), _MM_SHUFFLE(0, 0, 0, 0));
|
||||
}
|
||||
#else
|
||||
_assert_(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Now we multiply by 16, and add.
|
||||
PSLLD(vecLevelReg, 4);
|
||||
PADDD(indexReg, R(vecLevelReg));
|
||||
regCache_.Release(vecLevelReg, RegCache::VEC_TEMP0);
|
||||
}
|
||||
|
||||
X64Reg clutBaseReg = regCache_.Alloc(RegCache::GEN_TEMP1);
|
||||
MOV(PTRBITS, R(clutBaseReg), ImmPtr(clut));
|
||||
|
||||
X64Reg resultReg = regCache_.Find(level1 ? RegCache::VEC_RESULT1 : RegCache::VEC_RESULT);
|
||||
X64Reg maskReg = regCache_.Alloc(RegCache::VEC_TEMP0);
|
||||
if (cpu_info.bAVX2 && id.overReadSafe)
|
||||
PCMPEQD(maskReg, R(maskReg));
|
||||
|
||||
switch (id.ClutFmt()) {
|
||||
case GE_CMODE_16BIT_BGR5650:
|
||||
case GE_CMODE_16BIT_ABGR5551:
|
||||
case GE_CMODE_16BIT_ABGR4444:
|
||||
if (cpu_info.bAVX2 && id.overReadSafe) {
|
||||
VPGATHERDD(128, resultReg, MComplex(clutBaseReg, indexReg, SCALE_2, 0), maskReg);
|
||||
// Clear out the top 16 bits.
|
||||
PCMPEQD(maskReg, R(maskReg));
|
||||
PSRLD(maskReg, 16);
|
||||
PAND(resultReg, R(maskReg));
|
||||
} else {
|
||||
PXOR(resultReg, R(resultReg));
|
||||
|
||||
X64Reg temp2Reg = regCache_.Alloc(RegCache::GEN_TEMP2);
|
||||
if (cpu_info.bSSE4_1) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
PEXTRD(R(temp2Reg), indexReg, i);
|
||||
PINSRW(resultReg, MComplex(clutBaseReg, temp2Reg, SCALE_2, 0), i * 2);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
MOVD_xmm(R(temp2Reg), indexReg);
|
||||
if (i != 3)
|
||||
PSRLDQ(indexReg, 4);
|
||||
PINSRW(resultReg, MComplex(clutBaseReg, temp2Reg, SCALE_2, 0), i * 2);
|
||||
}
|
||||
}
|
||||
regCache_.Release(temp2Reg, RegCache::GEN_TEMP2);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMODE_32BIT_ABGR8888:
|
||||
if (cpu_info.bAVX2 && id.overReadSafe) {
|
||||
VPGATHERDD(128, resultReg, MComplex(clutBaseReg, indexReg, SCALE_4, 0), maskReg);
|
||||
} else {
|
||||
X64Reg temp2Reg = regCache_.Alloc(RegCache::GEN_TEMP2);
|
||||
if (cpu_info.bSSE4_1) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
PEXTRD(R(temp2Reg), indexReg, i);
|
||||
PINSRD(resultReg, MComplex(clutBaseReg, temp2Reg, SCALE_4, 0), i);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
MOVD_xmm(R(temp2Reg), indexReg);
|
||||
if (i != 3)
|
||||
PSRLDQ(indexReg, 4);
|
||||
|
||||
if (i == 0) {
|
||||
MOVD_xmm(resultReg , MComplex(clutBaseReg, temp2Reg, SCALE_4, 0));
|
||||
} else {
|
||||
MOVD_xmm(maskReg, MComplex(clutBaseReg, temp2Reg, SCALE_4, 0));
|
||||
PSLLDQ(maskReg, 4 * i);
|
||||
POR(resultReg, R(maskReg));
|
||||
}
|
||||
}
|
||||
}
|
||||
regCache_.Release(temp2Reg, RegCache::GEN_TEMP2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
regCache_.Release(maskReg, RegCache::VEC_TEMP0);
|
||||
regCache_.Unlock(resultReg, level1 ? RegCache::VEC_RESULT1 : RegCache::VEC_RESULT);
|
||||
|
||||
regCache_.Release(clutBaseReg, RegCache::GEN_TEMP1);
|
||||
regCache_.Release(indexReg, RegCache::VEC_INDEX);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_BlendQuad(const SamplerID &id, bool level1) {
|
||||
Describe(level1 ? "BlendQuadMips" : "BlendQuad");
|
||||
|
||||
@@ -1003,11 +1370,16 @@ bool SamplerJitCache::Jit_BlendQuad(const SamplerID &id, bool level1) {
|
||||
regCache_.Release(multLRReg, RegCache::VEC_TEMP1);
|
||||
|
||||
// Shrink to 16-bit, it's more convenient for later.
|
||||
PACKSSDW(quadReg, R(quadReg));
|
||||
if (level1) {
|
||||
PACKSSDW(quadReg, R(quadReg));
|
||||
regCache_.Unlock(quadReg, RegCache::VEC_RESULT1);
|
||||
} else {
|
||||
MOVDQA(XMM0, R(quadReg));
|
||||
if (cpu_info.bAVX) {
|
||||
VPACKSSDW(128, XMM0, quadReg, R(quadReg));
|
||||
} else {
|
||||
PACKSSDW(quadReg, R(quadReg));
|
||||
MOVDQA(XMM0, R(quadReg));
|
||||
}
|
||||
regCache_.Unlock(quadReg, RegCache::VEC_RESULT);
|
||||
|
||||
regCache_.ForceRelease(RegCache::VEC_RESULT);
|
||||
@@ -1324,19 +1696,19 @@ bool SamplerJitCache::Jit_ReadTextureFormat(const SamplerID &id) {
|
||||
switch (fmt) {
|
||||
case GE_TFMT_5650:
|
||||
success = Jit_GetTexData(id, 16);
|
||||
if (success && !id.linear)
|
||||
if (success)
|
||||
success = Jit_Decode5650();
|
||||
break;
|
||||
|
||||
case GE_TFMT_5551:
|
||||
success = Jit_GetTexData(id, 16);
|
||||
if (success && !id.linear)
|
||||
if (success)
|
||||
success = Jit_Decode5551();
|
||||
break;
|
||||
|
||||
case GE_TFMT_4444:
|
||||
success = Jit_GetTexData(id, 16);
|
||||
if (success && !id.linear)
|
||||
if (success)
|
||||
success = Jit_Decode4444();
|
||||
break;
|
||||
|
||||
@@ -1790,55 +2162,8 @@ bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) {
|
||||
if (id.swizzle) {
|
||||
return Jit_GetTexDataSwizzled(id, bitsPerTexel);
|
||||
}
|
||||
if (id.linear) {
|
||||
Describe("TexDataL");
|
||||
// We can throw away bufw immediately. Maybe even earlier?
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_BUFW);
|
||||
|
||||
X64Reg resultReg = regCache_.Find(RegCache::GEN_RESULT);
|
||||
|
||||
X64Reg srcReg = regCache_.Find(RegCache::GEN_ARG_TEXPTR);
|
||||
X64Reg byteIndexReg = regCache_.Find(RegCache::GEN_ARG_V);
|
||||
bool success = true;
|
||||
switch (bitsPerTexel) {
|
||||
case 32:
|
||||
case 16:
|
||||
case 8:
|
||||
MOVZX(32, bitsPerTexel, resultReg, MRegSum(srcReg, byteIndexReg));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
MOV(8, R(resultReg), MRegSum(srcReg, byteIndexReg));
|
||||
break;
|
||||
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
// Okay, srcReg and byteIndexReg have done their jobs.
|
||||
regCache_.Unlock(srcReg, RegCache::GEN_ARG_TEXPTR);
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_TEXPTR);
|
||||
regCache_.Unlock(byteIndexReg, RegCache::GEN_ARG_V);
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_V);
|
||||
|
||||
if (bitsPerTexel == 4) {
|
||||
X64Reg uReg = regCache_.Find(RegCache::GEN_ARG_U);
|
||||
|
||||
SHR(32, R(uReg), Imm8(1));
|
||||
FixupBranch skip = J_CC(CC_NC);
|
||||
SHR(32, R(resultReg), Imm8(4));
|
||||
SetJumpTarget(skip);
|
||||
// Zero out any bits not shifted off.
|
||||
AND(32, R(resultReg), Imm8(0x0F));
|
||||
|
||||
regCache_.Unlock(uReg, RegCache::GEN_ARG_U);
|
||||
}
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_U);
|
||||
|
||||
regCache_.Unlock(resultReg, RegCache::GEN_RESULT);
|
||||
return success;
|
||||
}
|
||||
|
||||
_assert_msg_(!id.linear, "Should not use this path for linear")
|
||||
Describe("TexData");
|
||||
X64Reg temp1Reg = regCache_.Alloc(RegCache::GEN_TEMP1);
|
||||
X64Reg temp2Reg = regCache_.Alloc(RegCache::GEN_TEMP2);
|
||||
@@ -1922,39 +2247,8 @@ bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) {
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_GetTexDataSwizzled4(const SamplerID &id) {
|
||||
if (id.linear) {
|
||||
Describe("TexDataS4L");
|
||||
// We can throw away bufw immediately. Maybe even earlier?
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_BUFW);
|
||||
|
||||
X64Reg uReg = regCache_.Find(RegCache::GEN_ARG_U);
|
||||
X64Reg byteIndexReg = regCache_.Find(RegCache::GEN_ARG_V);
|
||||
X64Reg srcReg = regCache_.Find(RegCache::GEN_ARG_TEXPTR);
|
||||
|
||||
X64Reg resultReg = regCache_.Find(RegCache::GEN_RESULT);
|
||||
MOV(8, R(resultReg), MRegSum(srcReg, byteIndexReg));
|
||||
|
||||
SHR(32, R(uReg), Imm8(1));
|
||||
FixupBranch skipNonZero = J_CC(CC_NC);
|
||||
// If the horizontal offset was odd, take the upper 4.
|
||||
SHR(8, R(resultReg), Imm8(4));
|
||||
SetJumpTarget(skipNonZero);
|
||||
// Zero out the rest of the bits.
|
||||
AND(32, R(resultReg), Imm8(0x0F));
|
||||
regCache_.Unlock(resultReg, RegCache::GEN_RESULT);
|
||||
|
||||
// We're all done with each of these regs, now.
|
||||
regCache_.Unlock(srcReg, RegCache::GEN_ARG_TEXPTR);
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_TEXPTR);
|
||||
regCache_.Unlock(uReg, RegCache::GEN_ARG_U);
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_U);
|
||||
regCache_.Unlock(byteIndexReg, RegCache::GEN_ARG_V);
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_V);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Describe("TexDataS4");
|
||||
_assert_msg_(!id.linear, "Should not use this path for linear")
|
||||
X64Reg temp1Reg = regCache_.Alloc(RegCache::GEN_TEMP1);
|
||||
X64Reg temp2Reg = regCache_.Alloc(RegCache::GEN_TEMP2);
|
||||
X64Reg uReg = regCache_.Find(RegCache::GEN_ARG_U);
|
||||
@@ -2018,41 +2312,7 @@ bool SamplerJitCache::Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTex
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
if (id.linear) {
|
||||
Describe("TexDataSL");
|
||||
// We can throw away bufw immediately. Maybe even earlier?
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_BUFW);
|
||||
// We've also baked uReg into vReg.
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_U);
|
||||
|
||||
X64Reg byteIndexReg = regCache_.Find(RegCache::GEN_ARG_V);
|
||||
X64Reg srcReg = regCache_.Find(RegCache::GEN_ARG_TEXPTR);
|
||||
|
||||
X64Reg resultReg = regCache_.Find(RegCache::GEN_RESULT);
|
||||
switch (bitsPerTexel) {
|
||||
case 32:
|
||||
MOV(bitsPerTexel, R(resultReg), MRegSum(srcReg, byteIndexReg));
|
||||
break;
|
||||
case 16:
|
||||
MOVZX(32, bitsPerTexel, resultReg, MRegSum(srcReg, byteIndexReg));
|
||||
break;
|
||||
case 8:
|
||||
MOVZX(32, bitsPerTexel, resultReg, MRegSum(srcReg, byteIndexReg));
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
regCache_.Unlock(resultReg, RegCache::GEN_RESULT);
|
||||
|
||||
// The pointer and offset have done their duty.
|
||||
regCache_.Unlock(srcReg, RegCache::GEN_ARG_TEXPTR);
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_TEXPTR);
|
||||
regCache_.Unlock(byteIndexReg, RegCache::GEN_ARG_V);
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_V);
|
||||
|
||||
return success;
|
||||
}
|
||||
_assert_msg_(!id.linear, "Should not use this path for linear")
|
||||
|
||||
Describe("TexDataS");
|
||||
X64Reg resultReg = regCache_.Find(RegCache::GEN_RESULT);
|
||||
@@ -2657,8 +2917,12 @@ bool SamplerJitCache::Jit_PrepareDataSwizzledOffsets(const SamplerID &id, RegCac
|
||||
|
||||
// Divide vvec by 8 in a temp.
|
||||
X64Reg vMultReg = regCache_.Alloc(RegCache::VEC_TEMP1);
|
||||
MOVDQA(vMultReg, R(vReg));
|
||||
PSRLD(vMultReg, 3);
|
||||
if (cpu_info.bAVX) {
|
||||
VPSRLD(128, vMultReg, vReg, 3);
|
||||
} else {
|
||||
MOVDQA(vMultReg, R(vReg));
|
||||
PSRLD(vMultReg, 3);
|
||||
}
|
||||
|
||||
// And now multiply by bufw. May be able to use a shift in a common case.
|
||||
int shiftAmount = 32 - clz32_nonzero(bitsPerTexel - 1);
|
||||
@@ -2700,16 +2964,24 @@ bool SamplerJitCache::Jit_PrepareDataSwizzledOffsets(const SamplerID &id, RegCac
|
||||
|
||||
// Now get ((uvec / texels_per_tile) / 4) * 32 * 4 aka (uvec / (128 / bitsPerTexel)) << 7.
|
||||
X64Reg uCopyReg = regCache_.Alloc(RegCache::VEC_TEMP0);
|
||||
MOVDQA(uCopyReg, R(uReg));
|
||||
PSRLD(uCopyReg, 7 + clz32_nonzero(bitsPerTexel - 1) - 32);
|
||||
if (cpu_info.bAVX) {
|
||||
VPSRLD(128, uCopyReg, uReg, 7 + clz32_nonzero(bitsPerTexel - 1) - 32);
|
||||
} else {
|
||||
MOVDQA(uCopyReg, R(uReg));
|
||||
PSRLD(uCopyReg, 7 + clz32_nonzero(bitsPerTexel - 1) - 32);
|
||||
}
|
||||
PSLLD(uCopyReg, 7);
|
||||
// Add it in to our running total.
|
||||
PADDD(vReg, R(uCopyReg));
|
||||
|
||||
if (bitsPerTexel == 4) {
|
||||
// Finally, we want (uvec & 31) / 2. Use a 16-bit wall.
|
||||
MOVDQA(uCopyReg, R(uReg));
|
||||
PSLLW(uCopyReg, 11);
|
||||
if (cpu_info.bAVX) {
|
||||
VPSLLW(128, uCopyReg, uReg, 11);
|
||||
} else {
|
||||
MOVDQA(uCopyReg, R(uReg));
|
||||
PSLLW(uCopyReg, 11);
|
||||
}
|
||||
PSRLD(uCopyReg, 12);
|
||||
// With that, this is our byte offset. uvec & 1 has which half.
|
||||
PADDD(vReg, R(uCopyReg));
|
||||
@@ -3116,6 +3388,7 @@ bool SamplerJitCache::Jit_TransformClutIndex(const SamplerID &id, int bitsPerInd
|
||||
bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) {
|
||||
Describe("ReadCLUT");
|
||||
X64Reg resultReg = regCache_.Find(RegCache::GEN_RESULT);
|
||||
_assert_msg_(!id.linear, "Should not use this path for linear");
|
||||
|
||||
if (!id.useSharedClut) {
|
||||
X64Reg temp2Reg = regCache_.Alloc(RegCache::GEN_TEMP2);
|
||||
@@ -3130,13 +3403,8 @@ bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) {
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_LEVEL);
|
||||
} else {
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
if (id.linear) {
|
||||
// Extra 8 to account for call.
|
||||
MOV(32, R(temp2Reg), MDisp(RSP, stackArgPos_ + 8 + 16));
|
||||
} else {
|
||||
// The argument was saved on the stack.
|
||||
MOV(32, R(temp2Reg), MDisp(RSP, 40));
|
||||
}
|
||||
// The argument was saved on the stack.
|
||||
MOV(32, R(temp2Reg), MDisp(RSP, 40));
|
||||
#else
|
||||
_assert_(false);
|
||||
#endif
|
||||
@@ -3168,13 +3436,13 @@ bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) {
|
||||
|
||||
switch (id.ClutFmt()) {
|
||||
case GE_CMODE_16BIT_BGR5650:
|
||||
return id.linear || Jit_Decode5650();
|
||||
return Jit_Decode5650();
|
||||
|
||||
case GE_CMODE_16BIT_ABGR5551:
|
||||
return id.linear || Jit_Decode5551();
|
||||
return Jit_Decode5551();
|
||||
|
||||
case GE_CMODE_16BIT_ABGR4444:
|
||||
return id.linear || Jit_Decode4444();
|
||||
return Jit_Decode4444();
|
||||
|
||||
case GE_CMODE_32BIT_ABGR8888:
|
||||
return true;
|
||||
|
||||
Reference in new issue
Block a user