samplerjit: Pass u/v coords as vector.

This commit is contained in:
Unknown W. Brackets committed 2021-12-27 11:37:32 -08:00
1 parent cc767622d0
commit b00a66e34c
5 files changed
+109 -52

No files matched your search

+66 -29
View File
@@ -149,26 +149,56 @@ static inline void ApplyTexelClamp(int out_u[N], int out_v[N], const int u[N], c
}
}
template <int N>
static inline void ApplyTexelClampQuad(int out_u[N * 4], int out_v[N * 4], const int u[N], const int v[N], int width, int height) {
static inline Vec4IntResult SOFTRAST_CALL ApplyTexelClampQuad(bool clamp, Vec4IntArg vec, int width) {
Vec4<int> result = vec;
#ifdef _M_SSE
if (clamp) {
// First, clamp to zero.
__m128i negmask = _mm_cmpgt_epi32(_mm_setzero_si128(), result.ivec);
result.ivec = _mm_andnot_si128(negmask, result.ivec);
// Now the high bound.
__m128i bound = _mm_set1_epi32(width - 1);
__m128i goodmask = _mm_cmpgt_epi32(bound, result.ivec);
// Clear the ones that were too high, then or in the high bound to those.
result.ivec = _mm_and_si128(goodmask, result.ivec);
result.ivec = _mm_or_si128(result.ivec, _mm_andnot_si128(goodmask, bound));
} else {
result.ivec = _mm_and_si128(result.ivec, _mm_set1_epi32(width - 1));
}
#else
if (gstate.isTexCoordClampedS()) {
for (int i = 0; i < N * 4; ++i) {
out_u[i] = ClampUV(u[i >> 2] + (i & 1), width);
for (int i = 0; i < 4; ++i) {
result[i] = ClampUV(result[i], width);
}
} else {
for (int i = 0; i < N * 4; ++i) {
out_u[i] = WrapUV(u[i >> 2] + (i & 1), width);
}
}
if (gstate.isTexCoordClampedT()) {
for (int i = 0; i < N * 4; ++i) {
out_v[i] = ClampUV(v[i >> 2] + ((i >> 1) & 1), height);
}
} else {
for (int i = 0; i < N * 4; ++i) {
out_v[i] = WrapUV(v[i >> 2] + ((i >> 1) & 1), height);
for (int i = 0; i < 4; ++i) {
result[i] = WrapUV(result[i], width);
}
}
#endif
return ToVec4IntResult(result);
}
static inline Vec4IntResult SOFTRAST_CALL ApplyTexelClampQuadS(bool clamp, int u, int width) {
#ifdef _M_SSE
__m128i uvec = _mm_add_epi32(_mm_set1_epi32(u), _mm_set_epi32(1, 0, 1, 0));
return ApplyTexelClampQuad(clamp, uvec, width);
#else
Vec4<int> result = Vec4<int>::AssignToAll(u) + Vec4<int>(0, 1, 0, 1);
return ApplyTexelClampQuad(clamp, ToVec4IntArg(result), width);
#endif
}
static inline Vec4IntResult SOFTRAST_CALL ApplyTexelClampQuadT(bool clamp, int v, int height) {
#ifdef _M_SSE
__m128i vvec = _mm_add_epi32(_mm_set1_epi32(v), _mm_set_epi32(1, 1, 0, 0));
return ApplyTexelClampQuad(clamp, vvec, height);
#else
Vec4<int> result = Vec4<int>::AssignToAll(v) + Vec4<int>(0, 0, 1, 1);
return ApplyTexelClampQuad(clamp, ToVec4IntArg(result), height);
#endif
}
static inline void GetTexelCoordinates(int level, float s, float t, int& out_u, int& out_v)
@@ -185,23 +215,26 @@ static inline void GetTexelCoordinates(int level, float s, float t, int& out_u,
ApplyTexelClamp<1>(&out_u, &out_v, &base_u, &base_v, width, height);
}
static inline void GetTexelCoordinatesQuad(int level, float in_s, float in_t, int u[4], int v[4], int &frac_u, int &frac_v)
{
// 8 bits of fractional UV
static inline Vec4IntResult SOFTRAST_CALL GetTexelCoordinatesQuadS(int level, float in_s, int &frac_u) {
int width = gstate.getTextureWidth(level);
int height = gstate.getTextureHeight(level);
int base_u = (int)(in_s * width * 256.0f + 0.375f) - 128;
int base_v = (int)(in_t * height * 256.0f + 0.375f) - 128;
frac_u = (int)(base_u) & 0xff;
frac_v = (int)(base_v) & 0xff;
base_u >>= 8;
// Need to generate and individually wrap/clamp the four sample coordinates. Ugh.
return ApplyTexelClampQuadS(gstate.isTexCoordClampedS(), base_u, width);
}
static inline Vec4IntResult SOFTRAST_CALL GetTexelCoordinatesQuadT(int level, float in_t, int &frac_v) {
int height = gstate.getTextureHeight(level);
int base_v = (int)(in_t * height * 256.0f + 0.375f) - 128;
frac_v = (int)(base_v) & 0xff;
base_v >>= 8;
// Need to generate and individually wrap/clamp the four sample coordinates. Ugh.
ApplyTexelClampQuad<1>(u, v, &base_u, &base_v, width, height);
return ApplyTexelClampQuadT(gstate.isTexCoordClampedT(), base_v, height);
}
static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& v1, const float p, float &s, float &t) {
@@ -562,7 +595,6 @@ Vec3<int> AlphaBlendingResult(const PixelFuncID &pixelID, const Vec4<int> &sourc
template <bool mayHaveMipLevels>
static inline Vec4IntResult SOFTRAST_CALL ApplyTexturing(Sampler::Funcs sampler, Vec4IntArg prim_color, float s, float t, int texlevel, int frac_texlevel, bool bilinear, u8 *texptr[], int texbufw[]) {
int u[8] = {0}, v[8] = {0}; // 1.23.8 fixed point
int frac_u[2], frac_v[2];
Vec4<int> texcolor0;
@@ -573,6 +605,8 @@ static inline Vec4IntResult SOFTRAST_CALL ApplyTexturing(Sampler::Funcs sampler,
int bufw1 = texbufw[mayHaveMipLevels ? texlevel + 1 : 0];
if (!bilinear) {
int u[8] = { 0 }, v[8] = { 0 }; // 1.23.8 fixed point
// Nearest filtering only. Round texcoords.
GetTexelCoordinates(mayHaveMipLevels ? texlevel : 0, s, t, u[0], v[0]);
if (mayHaveMipLevels && frac_texlevel) {
@@ -584,14 +618,17 @@ static inline Vec4IntResult SOFTRAST_CALL ApplyTexturing(Sampler::Funcs sampler,
texcolor1 = Vec4<int>(sampler.nearest(u[1], v[1], tptr1, bufw1, texlevel + 1));
}
} else {
GetTexelCoordinatesQuad(mayHaveMipLevels ? texlevel : 0, s, t, u, v, frac_u[0], frac_v[0]);
Vec4IntResult u = GetTexelCoordinatesQuadS(mayHaveMipLevels ? texlevel : 0, s, frac_u[0]);
Vec4IntResult v = GetTexelCoordinatesQuadT(mayHaveMipLevels ? texlevel : 0, t, frac_v[0]);
Vec4IntResult u1, v1;
if (mayHaveMipLevels && frac_texlevel) {
GetTexelCoordinatesQuad(texlevel + 1, s, t, u + 4, v + 4, frac_u[1], frac_v[1]);
u1 = GetTexelCoordinatesQuadS(texlevel + 1, s, frac_u[1]);
v1 = GetTexelCoordinatesQuadT(texlevel + 1, t, frac_v[1]);
}
texcolor0 = Vec4<int>(sampler.linear(u, v, frac_u[0], frac_v[0], tptr0, bufw0, mayHaveMipLevels ? texlevel : 0));
texcolor0 = Vec4<int>(sampler.linear(ToVec4IntArg(u), ToVec4IntArg(v), frac_u[0], frac_v[0], tptr0, bufw0, mayHaveMipLevels ? texlevel : 0));
if (mayHaveMipLevels && frac_texlevel) {
texcolor1 = Vec4<int>(sampler.linear(u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1, texlevel + 1));
texcolor1 = Vec4<int>(sampler.linear(ToVec4IntArg(u1), ToVec4IntArg(v1), frac_u[1], frac_v[1], tptr1, bufw1, texlevel + 1));
}
}
+4
View File
@@ -67,6 +67,7 @@ typedef int32x4_t Vec4IntArg;
typedef int32x4_t Vec4IntResult;
typedef float32x4_t Vec4FloatArg;
static inline Vec4IntArg ToVec4IntArg(const Math3D::Vec4<int> &a) { return vld1q_s32(a.AsArray()); }
static inline Vec4IntArg ToVec4IntArg(const Vec4IntResult &a) { return a; }
static inline Vec4IntResult ToVec4IntResult(const Math3D::Vec4<int> &a) { return vld1q_s32(a.AsArray()); }
static inline Vec4FloatArg ToVec4FloatArg(const Math3D::Vec4<float> &a) { return vld1q_f32(a.AsArray()); }
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
@@ -74,6 +75,7 @@ typedef __m128i Vec4IntArg;
typedef __m128i Vec4IntResult;
typedef __m128 Vec4FloatArg;
static inline Vec4IntArg ToVec4IntArg(const Math3D::Vec4<int> &a) { return a.ivec; }
static inline Vec4IntArg ToVec4IntArg(const Vec4IntResult &a) { return a; }
static inline Vec4IntResult ToVec4IntResult(const Math3D::Vec4<int> &a) { return a.ivec; }
static inline Vec4FloatArg ToVec4FloatArg(const Math3D::Vec4<float> &a) { return a.vec; }
#else
@@ -124,6 +126,8 @@ struct RegCache {
GEN_ARG_FRAC_V = 0x018D,
VEC_ARG_COLOR = 0x0080,
VEC_ARG_MASK = 0x0081,
VEC_ARG_U = 0x0082,
VEC_ARG_V = 0x0083,
VEC_TEMP0 = 0x1000,
VEC_TEMP1 = 0x1001,
+6 -4
View File
@@ -38,7 +38,7 @@ extern u32 clut[4096];
namespace Sampler {
static Vec4IntResult SOFTRAST_CALL SampleNearest(int u, int v, const u8 *tptr, int bufw, int level);
static Vec4IntResult SOFTRAST_CALL SampleLinear(int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int level);
static Vec4IntResult SOFTRAST_CALL SampleLinear(Vec4IntArg u, Vec4IntArg v, int frac_u, int frac_v, const u8 *tptr, int bufw, int level);
std::mutex jitCacheLock;
SamplerJitCache *jitCache = nullptr;
@@ -308,7 +308,7 @@ struct Nearest4 {
};
template <int N>
inline static Nearest4 SOFTRAST_CALL SampleNearest(int u[N], int v[N], const u8 *srcptr, int texbufw, int level) {
inline static Nearest4 SOFTRAST_CALL SampleNearest(const int u[N], const int v[N], const u8 *srcptr, int texbufw, int level) {
Nearest4 res;
if (!srcptr) {
memset(res.v, 0, sizeof(res.v));
@@ -414,8 +414,10 @@ static Vec4IntResult SOFTRAST_CALL SampleNearest(int u, int v, const u8 *tptr, i
return ToVec4IntResult(Vec4<int>::FromRGBA(c.v[0]));
}
static Vec4IntResult SOFTRAST_CALL SampleLinear(int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int texlevel) {
Nearest4 c = SampleNearest<4>(u, v, tptr, bufw, texlevel);
static Vec4IntResult SOFTRAST_CALL SampleLinear(Vec4IntArg u_in, Vec4IntArg v_in, int frac_u, int frac_v, const u8 *tptr, int bufw, int texlevel) {
const Vec4<int> u = u_in;
const Vec4<int> v = v_in;
Nearest4 c = SampleNearest<4>(u.AsArray(), v.AsArray(), tptr, bufw, texlevel);
Vec4<int> texcolor_tl = Vec4<int>::FromRGBA(c.v[0]);
Vec4<int> texcolor_tr = Vec4<int>::FromRGBA(c.v[1]);
+1 -1
View File
@@ -36,7 +36,7 @@ namespace Sampler {
typedef Rasterizer::Vec4IntResult (SOFTRAST_CALL *NearestFunc)(int u, int v, const u8 *tptr, int bufw, int level);
NearestFunc GetNearestFunc();
typedef Rasterizer::Vec4IntResult (SOFTRAST_CALL *LinearFunc)(int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int level);
typedef Rasterizer::Vec4IntResult (SOFTRAST_CALL *LinearFunc)(Rasterizer::Vec4IntArg u, Rasterizer::Vec4IntArg v, int frac_u, int frac_v, const u8 *tptr, int bufw, int level);
LinearFunc GetLinearFunc();
struct Funcs {
+32 -18
View File
@@ -131,8 +131,13 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
RegCache::GEN_ARG_V,
RegCache::GEN_ARG_TEXPTR,
RegCache::GEN_ARG_BUFW,
RegCache::GEN_ARG_LEVEL,
});
regCache_.ChangeReg(RAX, RegCache::GEN_RESULT);
regCache_.ChangeReg(XMM0, RegCache::VEC_ARG_U);
regCache_.ForceRetain(RegCache::VEC_ARG_U);
regCache_.ChangeReg(XMM1, RegCache::VEC_ARG_V);
regCache_.ForceRetain(RegCache::VEC_ARG_V);
// We'll first write the nearest sampler, which we will CALL.
// This may differ slightly based on the "linear" flag.
@@ -147,41 +152,46 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
RET();
regCache_.ForceRelease(RegCache::VEC_ARG_U);
regCache_.ForceRelease(RegCache::VEC_ARG_V);
if (regCache_.Has(RegCache::GEN_ARG_LEVEL))
regCache_.ForceRelease(RegCache::GEN_ARG_LEVEL);
regCache_.Reset(true);
// Now the actual linear func, which is exposed externally.
const u8 *start = AlignCode16();
// NOTE: This doesn't use the general register mapping.
// POSIX: arg1=uptr, arg2=vptr, arg3=frac_u, arg4=frac_v, arg5=src, arg6=bufw, stack+8=level
// Win64: arg1=uptr, arg2=vptr, arg3=frac_u, arg4=frac_v, stack+40=src, stack+48=bufw, stack+56=level
// POSIX: XMM0=uvec, XMM1=vvec, arg1=frac_u, arg2=frac_v, arg3=src, arg4=bufw, arg5=level
// Win64: XMM0=uvec, XMM1=vvec, arg3=frac_u, arg4=frac_v, stack+40=src, stack+48=bufw, stack+56=level
//
// We map these to nearest CALLs, with order: u, v, src, bufw, level
// Let's start by saving a bunch of registers.
PUSH(R15);
PUSH(R14);
PUSH(R13);
PUSH(R12);
// Won't need frac_u/frac_v for a while.
#ifdef _WIN32
PUSH(arg4Reg);
PUSH(arg3Reg);
#else
PUSH(arg2Reg);
PUSH(arg1Reg);
#endif
// Extra space to restore alignment and save resultReg for lerp.
// TODO: Maybe use XMMs instead?
SUB(64, R(RSP), Imm8(24));
MOV(64, R(R12), R(arg1Reg));
MOV(64, R(R13), R(arg2Reg));
#ifdef _WIN32
// First arg now starts at 24 (extra space) + 48 (pushed stack) + 8 (ret address) + 32 (shadow space)
const int argOffset = 24 + 48 + 8 + 32;
// First arg now starts at 24 (extra space) + 32 (pushed stack) + 8 (ret address) + 32 (shadow space)
const int argOffset = 24 + 32 + 8 + 32;
MOV(64, R(R14), MDisp(RSP, argOffset));
MOV(32, R(R15), MDisp(RSP, argOffset + 8));
// level is at argOffset + 16.
#else
MOV(64, R(R14), R(arg5Reg));
MOV(32, R(R15), R(arg6Reg));
// level is at 24 + 48 + 8.
MOV(64, R(R14), R(arg3Reg));
MOV(32, R(R15), R(arg4Reg));
// level is in arg5Reg, which is convenient.
#endif
// Early exit on !srcPtr.
@@ -195,7 +205,7 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
}
// At this point:
// R12=uptr, R13=vptr, stack+24=frac_u, stack+32=frac_v, R14=src, R15=bufw, stack+X=level
// XMM0=uvec, XMM1=vvec, stack+24=frac_u, stack+32=frac_v, R14=src, R15=bufw, stack+X=level
// This stores the result on the stack for later processing.
auto doNearestCall = [&](int off) {
@@ -205,12 +215,16 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
static const X64Reg bufwReg = arg4Reg;
static const X64Reg resultReg = RAX;
MOV(32, R(uReg), MDisp(R12, off));
MOV(32, R(vReg), MDisp(R13, off));
MOVD_xmm(R(uReg), XMM0);
MOVD_xmm(R(vReg), XMM1);
MOV(64, R(srcReg), R(R14));
MOV(32, R(bufwReg), R(R15));
// Leave level, we just always load from RAM. Separate CLUTs is uncommon.
PSRLDQ(XMM0, 4);
PSRLDQ(XMM1, 4);
CALL(nearest);
MOV(32, MDisp(RSP, off), R(resultReg));
};
@@ -316,8 +330,6 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
ADD(64, R(RSP), Imm8(24));
POP(arg3Reg);
POP(arg4Reg);
POP(R12);
POP(R13);
POP(R14);
POP(R15);
@@ -1203,11 +1215,13 @@ bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) {
// We need to multiply by 16 and add, LEA allows us to copy too.
LEA(32, temp2Reg, MScaled(levelReg, SCALE_4, 0));
regCache_.Unlock(levelReg, RegCache::GEN_ARG_LEVEL);
regCache_.ForceRelease(RegCache::GEN_ARG_LEVEL);
// Don't release if we're reusing it.
if (!regCache_.Has(RegCache::VEC_ARG_U))
regCache_.ForceRelease(RegCache::GEN_ARG_LEVEL);
} else {
if (id.linear) {
#ifdef _WIN32
const int argOffset = 24 + 48 + 8 + 32;
const int argOffset = 24 + 32 + 8 + 32;
// Extra 8 to account for CALL.
MOV(32, R(temp2Reg), MDisp(RSP, argOffset + 16 + 8));
#else