mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-27 04:08:37 +01:00
samplerjit: Pass u/v coords as vector.
This commit is contained in:
1 parent
cc767622d0
commit
b00a66e34c
5 files changed
+109
-52
No files matched your search
+66
-29
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user