mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 00:59:41 +01:00
Merge pull request #9735 from unknownbrackets/samplerjit
SoftGPU: Sampler jit
This commit is contained in:
commit
eff60ca575
14 files changed
+1316
-209
No files matched your search
@@ -1089,6 +1089,7 @@ list(APPEND CoreExtra
|
||||
Core/MIPS/x86/RegCacheFPU.cpp
|
||||
Core/MIPS/x86/RegCacheFPU.h
|
||||
GPU/Common/VertexDecoderX86.cpp
|
||||
GPU/Software/SamplerX86.cpp
|
||||
)
|
||||
|
||||
list(APPEND CoreExtra
|
||||
@@ -1284,6 +1285,8 @@ set(GPU_SOURCES
|
||||
GPU/Software/Lighting.h
|
||||
GPU/Software/Rasterizer.cpp
|
||||
GPU/Software/Rasterizer.h
|
||||
GPU/Software/Sampler.cpp
|
||||
GPU/Software/Sampler.h
|
||||
GPU/Software/SoftGpu.cpp
|
||||
GPU/Software/SoftGpu.h
|
||||
GPU/Software/TransformUnit.cpp
|
||||
|
||||
@@ -1673,6 +1673,11 @@ void XEmitter::PUNPCKLWD(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x61,
|
||||
void XEmitter::PUNPCKLDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x62, dest, arg);}
|
||||
void XEmitter::PUNPCKLQDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x6C, dest, arg);}
|
||||
|
||||
void XEmitter::PUNPCKHBW(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x68, dest, arg);}
|
||||
void XEmitter::PUNPCKHWD(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x69, dest, arg);}
|
||||
void XEmitter::PUNPCKHDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x6A, dest, arg);}
|
||||
void XEmitter::PUNPCKHQDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0x6D, dest, arg);}
|
||||
|
||||
void XEmitter::PSRLW(X64Reg reg, int shift)
|
||||
{
|
||||
WriteSSEOp(0x66, 0x71, (X64Reg)2, R(reg));
|
||||
@@ -1736,6 +1741,11 @@ void XEmitter::PSRAD(X64Reg reg, int shift)
|
||||
Write8(shift);
|
||||
}
|
||||
|
||||
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);}
|
||||
void XEmitter::PMULUDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(0x66, 0xF4, dest, arg);}
|
||||
|
||||
void XEmitter::WriteSSSE3Op(u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes)
|
||||
{
|
||||
if (!cpu_info.bSSSE3)
|
||||
|
||||
@@ -756,6 +756,11 @@ public:
|
||||
void PUNPCKLDQ(X64Reg dest, const OpArg &arg);
|
||||
void PUNPCKLQDQ(X64Reg dest, const OpArg &arg);
|
||||
|
||||
void PUNPCKHBW(X64Reg dest, const OpArg &arg);
|
||||
void PUNPCKHWD(X64Reg dest, const OpArg &arg);
|
||||
void PUNPCKHDQ(X64Reg dest, const OpArg &arg);
|
||||
void PUNPCKHQDQ(X64Reg dest, const OpArg &arg);
|
||||
|
||||
void PTEST(X64Reg dest, OpArg arg);
|
||||
void PAND(X64Reg dest, OpArg arg);
|
||||
void PANDN(X64Reg dest, OpArg arg);
|
||||
@@ -834,6 +839,11 @@ public:
|
||||
void PSRAW(X64Reg reg, int shift);
|
||||
void PSRAD(X64Reg reg, int shift);
|
||||
|
||||
void PMULLW(X64Reg dest, const OpArg &arg);
|
||||
void PMULHW(X64Reg dest, const OpArg &arg);
|
||||
void PMULHUW(X64Reg dest, const OpArg &arg);
|
||||
void PMULUDQ(X64Reg dest, const OpArg &arg);
|
||||
|
||||
// SSE4: data type conversions
|
||||
void PMOVSXBW(X64Reg dest, OpArg arg);
|
||||
void PMOVSXBD(X64Reg dest, OpArg arg);
|
||||
|
||||
@@ -1075,7 +1075,6 @@ static const float MEMORY_ALIGNED16(byColor565[4]) = { 255.0f / 31.0f, 255.0f /
|
||||
|
||||
void VertexDecoderJitCache::Jit_Color565Morph() {
|
||||
MOV(PTRBITS, R(tempReg1), ImmPtr(&gstate_c.morphWeights[0]));
|
||||
MOV(32, R(tempReg2), Imm32(1));
|
||||
MOVDQA(XMM5, M(color565Mask));
|
||||
MOVAPS(XMM6, M(byColor565));
|
||||
|
||||
|
||||
@@ -250,6 +250,7 @@
|
||||
<ClInclude Include="Software\Clipper.h" />
|
||||
<ClInclude Include="Software\Lighting.h" />
|
||||
<ClInclude Include="Software\Rasterizer.h" />
|
||||
<ClInclude Include="Software\Sampler.h" />
|
||||
<ClInclude Include="Software\SoftGpu.h" />
|
||||
<ClInclude Include="Software\TransformUnit.h" />
|
||||
<ClInclude Include="Common\TextureDecoder.h" />
|
||||
@@ -350,6 +351,8 @@
|
||||
<ClCompile Include="Software\Clipper.cpp" />
|
||||
<ClCompile Include="Software\Lighting.cpp" />
|
||||
<ClCompile Include="Software\Rasterizer.cpp" />
|
||||
<ClCompile Include="Software\Sampler.cpp" />
|
||||
<ClCompile Include="Software\SamplerX86.cpp" />
|
||||
<ClCompile Include="Software\SoftGpu.cpp" />
|
||||
<ClCompile Include="Software\TransformUnit.cpp" />
|
||||
<ClCompile Include="Common\TextureDecoder.cpp" />
|
||||
|
||||
@@ -259,6 +259,9 @@
|
||||
<ClInclude Include="Common\ShaderTranslation.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Software\Sampler.h">
|
||||
<Filter>Software</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Math3D.cpp">
|
||||
@@ -510,5 +513,11 @@
|
||||
<ClCompile Include="Vulkan\FramebufferVulkan.cpp">
|
||||
<Filter>Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Software\Sampler.cpp">
|
||||
<Filter>Software</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Software\SamplerX86.cpp">
|
||||
<Filter>Software</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+20
-205
@@ -31,6 +31,7 @@
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Software/SoftGpu.h"
|
||||
#include "GPU/Software/Rasterizer.h"
|
||||
#include "GPU/Software/Sampler.h"
|
||||
|
||||
#if defined(_M_SSE)
|
||||
#include <emmintrin.h>
|
||||
@@ -110,51 +111,6 @@ static inline Vec4<float> Interpolate(const float &c0, const float &c1, const fl
|
||||
return Interpolate(c0, c1, c2, w0.Cast<float>(), w1.Cast<float>(), w2.Cast<float>(), wsum_recip);
|
||||
}
|
||||
|
||||
template <unsigned int texel_size_bits>
|
||||
static inline int GetPixelDataOffset(unsigned int row_pitch_bytes, unsigned int u, unsigned int v)
|
||||
{
|
||||
if (!gstate.isTextureSwizzled())
|
||||
return (v * (row_pitch_bytes * texel_size_bits >> 3)) + (u * texel_size_bits >> 3);
|
||||
|
||||
const int tile_size_bits = 32;
|
||||
const int tiles_in_block_horizontal = 4;
|
||||
const int tiles_in_block_vertical = 8;
|
||||
|
||||
int texels_per_tile = tile_size_bits / texel_size_bits;
|
||||
int tile_u = u / texels_per_tile;
|
||||
int tile_idx = (v % tiles_in_block_vertical) * (tiles_in_block_horizontal) +
|
||||
// TODO: not sure if the *texel_size_bits/8 factor is correct
|
||||
(v / tiles_in_block_vertical) * ((row_pitch_bytes*texel_size_bits/(tile_size_bits))*tiles_in_block_vertical) +
|
||||
(tile_u % tiles_in_block_horizontal) +
|
||||
(tile_u / tiles_in_block_horizontal) * (tiles_in_block_horizontal*tiles_in_block_vertical);
|
||||
|
||||
return tile_idx * (tile_size_bits / 8) + ((u % texels_per_tile) * texel_size_bits) / 8;
|
||||
}
|
||||
|
||||
static inline u32 LookupColor(unsigned int index, unsigned int level)
|
||||
{
|
||||
const bool mipmapShareClut = gstate.isClutSharedForMipmaps();
|
||||
const int clutSharingOffset = mipmapShareClut ? 0 : level * 16;
|
||||
|
||||
switch (gstate.getClutPaletteFormat()) {
|
||||
case GE_CMODE_16BIT_BGR5650:
|
||||
return RGB565ToRGBA8888(reinterpret_cast<u16*>(clut)[index + clutSharingOffset]);
|
||||
|
||||
case GE_CMODE_16BIT_ABGR5551:
|
||||
return RGBA5551ToRGBA8888(reinterpret_cast<u16*>(clut)[index + clutSharingOffset]);
|
||||
|
||||
case GE_CMODE_16BIT_ABGR4444:
|
||||
return RGBA4444ToRGBA8888(reinterpret_cast<u16*>(clut)[index + clutSharingOffset]);
|
||||
|
||||
case GE_CMODE_32BIT_ABGR8888:
|
||||
return clut[index + clutSharingOffset];
|
||||
|
||||
default:
|
||||
ERROR_LOG_REPORT(G3D, "Software: Unsupported palette format: %x", gstate.getClutPaletteFormat());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u8 ClampFogDepth(float fogdepth) {
|
||||
if (fogdepth <= 0.0f)
|
||||
return 0;
|
||||
@@ -374,123 +330,6 @@ static inline void GetTextureCoordinates(const VertexData& v0, const VertexData&
|
||||
}
|
||||
}
|
||||
|
||||
struct Nearest4 {
|
||||
MEMORY_ALIGNED16(u32 v[4]);
|
||||
|
||||
operator u32() const {
|
||||
return v[0];
|
||||
}
|
||||
};
|
||||
|
||||
template <int N>
|
||||
inline static Nearest4 SampleNearest(int level, int u[N], int v[N], const u8 *srcptr, int texbufw)
|
||||
{
|
||||
Nearest4 res;
|
||||
if (!srcptr) {
|
||||
memset(res.v, 0, sizeof(res.v));
|
||||
return res;
|
||||
}
|
||||
|
||||
GETextureFormat texfmt = gstate.getTextureFormat();
|
||||
|
||||
// TODO: Should probably check if textures are aligned properly...
|
||||
|
||||
switch (texfmt) {
|
||||
case GE_TFMT_4444:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<16>(texbufw, u[i], v[i]);
|
||||
res.v[i] = RGBA4444ToRGBA8888(*(const u16 *)src);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_5551:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<16>(texbufw, u[i], v[i]);
|
||||
res.v[i] = RGBA5551ToRGBA8888(*(const u16 *)src);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_5650:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<16>(texbufw, u[i], v[i]);
|
||||
res.v[i] = RGB565ToRGBA8888(*(const u16 *)src);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_8888:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<32>(texbufw, u[i], v[i]);
|
||||
res.v[i] = *(const u32 *)src;
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_CLUT32:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<32>(texbufw, u[i], v[i]);
|
||||
u32 val = src[0] + (src[1] << 8) + (src[2] << 16) + (src[3] << 24);
|
||||
res.v[i] = LookupColor(gstate.transformClutIndex(val), 0);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_CLUT16:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<16>(texbufw, u[i], v[i]);
|
||||
u16 val = src[0] + (src[1] << 8);
|
||||
res.v[i] = LookupColor(gstate.transformClutIndex(val), 0);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_CLUT8:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<8>(texbufw, u[i], v[i]);
|
||||
u8 val = *src;
|
||||
res.v[i] = LookupColor(gstate.transformClutIndex(val), 0);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_CLUT4:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<4>(texbufw, u[i], v[i]);
|
||||
u8 val = (u[i] & 1) ? (src[0] >> 4) : (src[0] & 0xF);
|
||||
// Only CLUT4 uses separate mipmap palettes.
|
||||
res.v[i] = LookupColor(gstate.transformClutIndex(val), level);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_DXT1:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const DXT1Block *block = (const DXT1Block *)srcptr + (v[i] / 4) * (texbufw / 4) + (u[i] / 4);
|
||||
u32 data[4 * 4];
|
||||
DecodeDXT1Block(data, block, 4, 4, false);
|
||||
res.v[i] = data[4 * (v[i] % 4) + (u[i] % 4)];
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_DXT3:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const DXT3Block *block = (const DXT3Block *)srcptr + (v[i] / 4) * (texbufw / 4) + (u[i] / 4);
|
||||
u32 data[4 * 4];
|
||||
DecodeDXT3Block(data, block, 4, 4);
|
||||
res.v[i] = data[4 * (v[i] % 4) + (u[i] % 4)];
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_DXT5:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const DXT5Block *block = (const DXT5Block *)srcptr + (v[i] / 4) * (texbufw / 4) + (u[i] / 4);
|
||||
u32 data[4 * 4];
|
||||
DecodeDXT5Block(data, block, 4, 4);
|
||||
res.v[i] = data[4 * (v[i] % 4) + (u[i] % 4)];
|
||||
}
|
||||
return res;
|
||||
|
||||
default:
|
||||
ERROR_LOG_REPORT(G3D, "Software: Unsupported texture format: %x", texfmt);
|
||||
memset(res.v, 0, sizeof(res.v));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: These likely aren't endian safe
|
||||
static inline u32 GetPixelColor(int x, int y)
|
||||
{
|
||||
@@ -1164,39 +1003,7 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4<in
|
||||
SetPixelColor(p.x, p.y, new_color);
|
||||
}
|
||||
|
||||
static inline Vec4<int> SampleLinear(int texlevel, int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw) {
|
||||
#if defined(_M_SSE)
|
||||
Nearest4 c = SampleNearest<4>(texlevel, u, v, tptr, bufw);
|
||||
|
||||
const __m128i z = _mm_setzero_si128();
|
||||
|
||||
__m128i cvec = _mm_load_si128((const __m128i *)c.v);
|
||||
__m128i tvec = _mm_unpacklo_epi8(cvec, z);
|
||||
tvec = _mm_mullo_epi16(tvec, _mm_set1_epi16(0x100 - frac_v));
|
||||
__m128i bvec = _mm_unpackhi_epi8(cvec, z);
|
||||
bvec = _mm_mullo_epi16(bvec, _mm_set1_epi16(frac_v));
|
||||
|
||||
// This multiplies the left and right sides. We shift right after, although this may round down...
|
||||
__m128i rowmult = _mm_set_epi16(frac_u, frac_u, frac_u, frac_u, 0x100 - frac_u, 0x100 - frac_u, 0x100 - frac_u, 0x100 - frac_u);
|
||||
__m128i tmp = _mm_mulhi_epu16(_mm_add_epi16(tvec, bvec), rowmult);
|
||||
|
||||
// Now we need to add the left and right sides together.
|
||||
__m128i res = _mm_add_epi16(tmp, _mm_shuffle_epi32(tmp, _MM_SHUFFLE(3, 2, 3, 2)));
|
||||
return Vec4<int>(_mm_unpacklo_epi16(res, z));
|
||||
#else
|
||||
Nearest4 nearest = SampleNearest<4>(texlevel, u, v, tptr, bufw);
|
||||
Vec4<int> texcolor_tl = Vec4<int>::FromRGBA(nearest.v[0]);
|
||||
Vec4<int> texcolor_tr = Vec4<int>::FromRGBA(nearest.v[1]);
|
||||
Vec4<int> texcolor_bl = Vec4<int>::FromRGBA(nearest.v[2]);
|
||||
Vec4<int> texcolor_br = Vec4<int>::FromRGBA(nearest.v[3]);
|
||||
// 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255.
|
||||
Vec4<int> t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u;
|
||||
Vec4<int> b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u;
|
||||
return (t * (0x100 - frac_v) + b * frac_v) / (256 * 256);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void ApplyTexturing(Vec4<int> &prim_color, float s, float t, int texlevel, int frac_texlevel, bool bilinear, u8 *texptr[], int texbufw[]) {
|
||||
static inline void ApplyTexturing(Sampler::Funcs sampler, Vec4<int> &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];
|
||||
|
||||
@@ -1214,9 +1021,9 @@ static inline void ApplyTexturing(Vec4<int> &prim_color, float s, float t, int t
|
||||
GetTexelCoordinates(texlevel + 1, s, t, u[1], v[1]);
|
||||
}
|
||||
|
||||
texcolor0 = Vec4<int>::FromRGBA(SampleNearest<1>(texlevel, u, v, tptr0, bufw0));
|
||||
texcolor0 = Vec4<int>::FromRGBA(sampler.nearest(u[0], v[0], tptr0, bufw0, texlevel));
|
||||
if (frac_texlevel) {
|
||||
texcolor1 = Vec4<int>::FromRGBA(SampleNearest<1>(texlevel + 1, u + 1, v + 1, tptr1, bufw1));
|
||||
texcolor1 = Vec4<int>::FromRGBA(sampler.nearest(u[1], v[1], tptr1, bufw1, texlevel + 1));
|
||||
}
|
||||
} else {
|
||||
GetTexelCoordinatesQuad(texlevel, s, t, u, v, frac_u[0], frac_v[0]);
|
||||
@@ -1224,9 +1031,9 @@ static inline void ApplyTexturing(Vec4<int> &prim_color, float s, float t, int t
|
||||
GetTexelCoordinatesQuad(texlevel + 1, s, t, u + 4, v + 4, frac_u[1], frac_v[1]);
|
||||
}
|
||||
|
||||
texcolor0 = SampleLinear(texlevel, u, v, frac_u[0], frac_v[0], tptr0, bufw0);
|
||||
texcolor0 = Vec4<int>::FromRGBA(sampler.linear(u, v, frac_u[0], frac_v[0], tptr0, bufw0, texlevel));
|
||||
if (frac_texlevel) {
|
||||
texcolor1 = SampleLinear(texlevel + 1, u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1);
|
||||
texcolor1 = Vec4<int>::FromRGBA(sampler.linear(u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1, texlevel + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1299,7 +1106,7 @@ static inline void CalculateSamplingParams(const float ds, const float dt, const
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyTexturing(Vec4<int> *prim_color, const Vec4<float> &s, const Vec4<float> &t, int maxTexLevel, u8 *texptr[], int texbufw[]) {
|
||||
static inline void ApplyTexturing(Sampler::Funcs sampler, Vec4<int> *prim_color, const Vec4<float> &s, const Vec4<float> &t, int maxTexLevel, u8 *texptr[], int texbufw[]) {
|
||||
float ds = s[1] - s[0];
|
||||
float dt = t[2] - t[0];
|
||||
|
||||
@@ -1309,7 +1116,7 @@ static inline void ApplyTexturing(Vec4<int> *prim_color, const Vec4<float> &s, c
|
||||
CalculateSamplingParams(ds, dt, maxTexLevel, level, levelFrac, bilinear);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
ApplyTexturing(prim_color[i], s[i], t[i], level, levelFrac, bilinear, texptr, texbufw);
|
||||
ApplyTexturing(sampler, prim_color[i], s[i], t[i], level, levelFrac, bilinear, texptr, texbufw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1438,6 +1245,8 @@ void DrawTriangleSlice(
|
||||
// This is common, and when we interpolate, we lose accuracy.
|
||||
const bool flatZ = v0.screenpos.z == v1.screenpos.z && v0.screenpos.z == v2.screenpos.z;
|
||||
|
||||
Sampler::Funcs sampler = Sampler::GetFuncs();
|
||||
|
||||
for (pprime.y = minY + hy1 * 32; pprime.y < minY + hy2 * 32; pprime.y += 32,
|
||||
w0_base = e0.StepY(w0_base),
|
||||
w1_base = e1.StepY(w1_base),
|
||||
@@ -1495,7 +1304,7 @@ void DrawTriangleSlice(
|
||||
GetTextureCoordinates(v0, v1, v2, w0, w1, w2, wsum_recip, s, t);
|
||||
}
|
||||
|
||||
ApplyTexturing(prim_color, s, t, maxTexLevel, texptr, texbufw);
|
||||
ApplyTexturing(sampler, prim_color, s, t, maxTexLevel, texptr, texbufw);
|
||||
}
|
||||
|
||||
if (!clearMode) {
|
||||
@@ -1605,6 +1414,8 @@ void DrawPoint(const VertexData &v0)
|
||||
|
||||
bool clearMode = gstate.isModeClear();
|
||||
|
||||
Sampler::Funcs sampler = Sampler::GetFuncs();
|
||||
|
||||
if (gstate.isTextureMapEnabled() && !clearMode) {
|
||||
int texbufw[8] = {0};
|
||||
|
||||
@@ -1642,7 +1453,7 @@ void DrawPoint(const VertexData &v0)
|
||||
int texLevelFrac;
|
||||
bool bilinear;
|
||||
CalculateSamplingParams(0.0f, 0.0f, maxTexLevel, texLevel, texLevelFrac, bilinear);
|
||||
ApplyTexturing(prim_color, s, t, texLevel, texLevelFrac, bilinear, texptr, texbufw);
|
||||
ApplyTexturing(sampler, prim_color, s, t, texLevel, texLevelFrac, bilinear, texptr, texbufw);
|
||||
}
|
||||
|
||||
if (!clearMode)
|
||||
@@ -1708,6 +1519,8 @@ void DrawLine(const VertexData &v0, const VertexData &v1)
|
||||
}
|
||||
}
|
||||
|
||||
Sampler::Funcs sampler = Sampler::GetFuncs();
|
||||
|
||||
float x = a.x > b.x ? a.x - 1 : a.x;
|
||||
float y = a.y > b.y ? a.y - 1 : a.y;
|
||||
float z = a.z;
|
||||
@@ -1772,7 +1585,7 @@ void DrawLine(const VertexData &v0, const VertexData &v1)
|
||||
texBilinear = true;
|
||||
}
|
||||
|
||||
ApplyTexturing(prim_color, s, t, texLevel, texLevelFrac, texBilinear, texptr, texbufw);
|
||||
ApplyTexturing(sampler, prim_color, s, t, texLevel, texLevelFrac, texBilinear, texptr, texbufw);
|
||||
}
|
||||
|
||||
if (!clearMode)
|
||||
@@ -1825,10 +1638,12 @@ bool GetCurrentTexture(GPUDebugBuffer &buffer, int level)
|
||||
int texbufw = GetTextureBufw(level, texaddr, texfmt);
|
||||
u8 *texptr = Memory::GetPointer(texaddr);
|
||||
|
||||
Sampler::Funcs sampler = Sampler::GetFuncs();
|
||||
|
||||
u32 *row = (u32 *)buffer.GetData();
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
row[x] = SampleNearest<1>(level, &x, &y, texptr, texbufw);
|
||||
row[x] = sampler.nearest(x, y, texptr, texbufw, level);
|
||||
}
|
||||
row += w;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
// Copyright (c) 2017- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include "Common/ColorConv.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/Software/Sampler.h"
|
||||
|
||||
#if defined(_M_SSE)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
using namespace Math3D;
|
||||
|
||||
extern u32 clut[4096];
|
||||
|
||||
namespace Sampler {
|
||||
|
||||
static u32 SampleNearest(int u, int v, const u8 *tptr, int bufw, int level);
|
||||
static u32 SampleLinear(int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int level);
|
||||
|
||||
std::mutex jitCacheLock;
|
||||
SamplerJitCache *jitCache = nullptr;
|
||||
|
||||
void Init() {
|
||||
jitCache = new SamplerJitCache();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
delete jitCache;
|
||||
jitCache = nullptr;
|
||||
}
|
||||
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
if (!jitCache->IsInSpace(ptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
name = jitCache->DescribeCodePtr(ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
NearestFunc GetNearestFunc() {
|
||||
SamplerID id;
|
||||
jitCache->ComputeSamplerID(&id, false);
|
||||
NearestFunc jitted = jitCache->GetNearest(id);
|
||||
if (jitted) {
|
||||
return jitted;
|
||||
}
|
||||
|
||||
return &SampleNearest;
|
||||
}
|
||||
|
||||
LinearFunc GetLinearFunc() {
|
||||
SamplerID id;
|
||||
jitCache->ComputeSamplerID(&id, true);
|
||||
LinearFunc jitted = jitCache->GetLinear(id);
|
||||
if (jitted) {
|
||||
return jitted;
|
||||
}
|
||||
|
||||
return &SampleLinear;
|
||||
}
|
||||
|
||||
SamplerJitCache::SamplerJitCache()
|
||||
#if PPSSPP_ARCH(ARM64)
|
||||
: fp(this)
|
||||
#endif
|
||||
{
|
||||
// 256k should be enough.
|
||||
AllocCodeSpace(1024 * 64 * 4);
|
||||
|
||||
// Add some random code to "help" MSVC's buggy disassembler :(
|
||||
#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64))
|
||||
using namespace Gen;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
MOV(32, R(EAX), R(EBX));
|
||||
RET();
|
||||
}
|
||||
#elif defined(ARM)
|
||||
BKPT(0);
|
||||
BKPT(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SamplerJitCache::Clear() {
|
||||
ClearCodeSpace(0);
|
||||
cache_.clear();
|
||||
addresses_.clear();
|
||||
}
|
||||
|
||||
void SamplerJitCache::ComputeSamplerID(SamplerID *id_out, bool linear) {
|
||||
SamplerID id{};
|
||||
|
||||
id.texfmt = gstate.getTextureFormat();
|
||||
id.swizzle = gstate.isTextureSwizzled();
|
||||
// Only CLUT4 can use separate CLUTs per mimap.
|
||||
id.useSharedClut = gstate.getTextureFormat() != GE_TFMT_CLUT4 || !gstate.isMipmapEnabled() || gstate.isClutSharedForMipmaps();
|
||||
if (gstate.isTextureFormatIndexed()) {
|
||||
id.clutfmt = gstate.getClutPaletteFormat();
|
||||
id.hasClutMask = gstate.getClutIndexMask() != 0xFF;
|
||||
id.hasClutShift = gstate.getClutIndexShift() != 0;
|
||||
id.hasClutOffset = gstate.getClutIndexStartPos() != 0;
|
||||
}
|
||||
id.linear = linear;
|
||||
int maxLevel = gstate.isMipmapEnabled() ? gstate.getTextureMaxLevel() : 0;
|
||||
for (int i = 0; i <= maxLevel; ++i) {
|
||||
if (gstate.getTextureAddress(i) == 0) {
|
||||
id.hasInvalidPtr = true;
|
||||
}
|
||||
}
|
||||
|
||||
*id_out = id;
|
||||
}
|
||||
|
||||
std::string SamplerJitCache::DescribeSamplerID(const SamplerID &id) {
|
||||
std::string name;
|
||||
switch ((GETextureFormat)id.texfmt) {
|
||||
case GE_TFMT_5650: name = "5650"; break;
|
||||
case GE_TFMT_5551: name = "5551"; break;
|
||||
case GE_TFMT_4444: name = "4444"; break;
|
||||
case GE_TFMT_8888: name = "8888"; break;
|
||||
case GE_TFMT_CLUT4: name = "CLUT4"; break;
|
||||
case GE_TFMT_CLUT8: name = "CLUT8"; break;
|
||||
case GE_TFMT_CLUT16: name = "CLUT16"; break;
|
||||
case GE_TFMT_CLUT32: name = "CLUT32"; break;
|
||||
case GE_TFMT_DXT1: name = "DXT1"; break;
|
||||
case GE_TFMT_DXT3: name = "DXT3"; break;
|
||||
case GE_TFMT_DXT5: name = "DXT5"; break;
|
||||
}
|
||||
switch ((GEPaletteFormat)id.clutfmt) {
|
||||
case GE_CMODE_16BIT_BGR5650:
|
||||
switch ((GETextureFormat)id.texfmt) {
|
||||
case GE_TFMT_CLUT4:
|
||||
case GE_TFMT_CLUT8:
|
||||
case GE_TFMT_CLUT16:
|
||||
case GE_TFMT_CLUT32:
|
||||
name += ":C5650";
|
||||
break;
|
||||
default:
|
||||
// Ignore 0 clutfmt when no clut.
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GE_CMODE_16BIT_ABGR5551: name += ":C5551"; break;
|
||||
case GE_CMODE_16BIT_ABGR4444: name += ":C4444"; break;
|
||||
case GE_CMODE_32BIT_ABGR8888: name += ":C8888"; break;
|
||||
}
|
||||
if (id.swizzle) {
|
||||
name += ":SWZ";
|
||||
}
|
||||
if (!id.useSharedClut) {
|
||||
name += ":MIP";
|
||||
}
|
||||
if (id.hasInvalidPtr) {
|
||||
name += ":INV";
|
||||
}
|
||||
if (id.hasClutMask) {
|
||||
name += ":CMASK";
|
||||
}
|
||||
if (id.hasClutShift) {
|
||||
name += ":CSHF";
|
||||
}
|
||||
if (id.hasClutOffset) {
|
||||
name += ":COFF";
|
||||
}
|
||||
if (id.linear) {
|
||||
name += ":LERP";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string SamplerJitCache::DescribeCodePtr(const u8 *ptr) {
|
||||
ptrdiff_t dist = 0x7FFFFFFF;
|
||||
SamplerID found{};
|
||||
for (const auto &it : addresses_) {
|
||||
ptrdiff_t it_dist = ptr - it.second;
|
||||
if (it_dist >= 0 && it_dist < dist) {
|
||||
found = it.first;
|
||||
dist = it_dist;
|
||||
}
|
||||
}
|
||||
|
||||
return DescribeSamplerID(found);
|
||||
}
|
||||
|
||||
NearestFunc SamplerJitCache::GetNearest(const SamplerID &id) {
|
||||
std::lock_guard<std::mutex> guard(jitCacheLock);
|
||||
|
||||
auto it = cache_.find(id);
|
||||
if (it != cache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// TODO: What should be the min size? Can we even hit this?
|
||||
if (GetSpaceLeft() < 16384) {
|
||||
Clear();
|
||||
}
|
||||
|
||||
// TODO
|
||||
#ifdef _M_X64
|
||||
addresses_[id] = GetCodePointer();
|
||||
NearestFunc func = Compile(id);
|
||||
cache_[id] = func;
|
||||
return func;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
LinearFunc SamplerJitCache::GetLinear(const SamplerID &id) {
|
||||
std::lock_guard<std::mutex> guard(jitCacheLock);
|
||||
|
||||
auto it = cache_.find(id);
|
||||
if (it != cache_.end()) {
|
||||
return (LinearFunc)it->second;
|
||||
}
|
||||
|
||||
// TODO: What should be the min size? Can we even hit this?
|
||||
if (GetSpaceLeft() < 16384) {
|
||||
Clear();
|
||||
}
|
||||
|
||||
// TODO
|
||||
#ifdef _M_X64
|
||||
addresses_[id] = GetCodePointer();
|
||||
LinearFunc func = CompileLinear(id);
|
||||
cache_[id] = (NearestFunc)func;
|
||||
return func;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <unsigned int texel_size_bits>
|
||||
static inline int GetPixelDataOffset(unsigned int row_pitch_pixels, unsigned int u, unsigned int v)
|
||||
{
|
||||
if (!gstate.isTextureSwizzled())
|
||||
return (v * (row_pitch_pixels * texel_size_bits >> 3)) + (u * texel_size_bits >> 3);
|
||||
|
||||
const int tile_size_bits = 32;
|
||||
const int tiles_in_block_horizontal = 4;
|
||||
const int tiles_in_block_vertical = 8;
|
||||
|
||||
int texels_per_tile = tile_size_bits / texel_size_bits;
|
||||
int tile_u = u / texels_per_tile;
|
||||
int tile_idx = (v % tiles_in_block_vertical) * (tiles_in_block_horizontal) +
|
||||
// TODO: not sure if the *texel_size_bits/8 factor is correct
|
||||
(v / tiles_in_block_vertical) * ((row_pitch_pixels*texel_size_bits/(tile_size_bits))*tiles_in_block_vertical) +
|
||||
(tile_u % tiles_in_block_horizontal) +
|
||||
(tile_u / tiles_in_block_horizontal) * (tiles_in_block_horizontal*tiles_in_block_vertical);
|
||||
|
||||
return tile_idx * (tile_size_bits / 8) + ((u % texels_per_tile) * texel_size_bits) / 8;
|
||||
}
|
||||
|
||||
static inline u32 LookupColor(unsigned int index, unsigned int level)
|
||||
{
|
||||
const bool mipmapShareClut = gstate.isClutSharedForMipmaps();
|
||||
const int clutSharingOffset = mipmapShareClut ? 0 : level * 16;
|
||||
|
||||
switch (gstate.getClutPaletteFormat()) {
|
||||
case GE_CMODE_16BIT_BGR5650:
|
||||
return RGB565ToRGBA8888(reinterpret_cast<u16*>(clut)[index + clutSharingOffset]);
|
||||
|
||||
case GE_CMODE_16BIT_ABGR5551:
|
||||
return RGBA5551ToRGBA8888(reinterpret_cast<u16*>(clut)[index + clutSharingOffset]);
|
||||
|
||||
case GE_CMODE_16BIT_ABGR4444:
|
||||
return RGBA4444ToRGBA8888(reinterpret_cast<u16*>(clut)[index + clutSharingOffset]);
|
||||
|
||||
case GE_CMODE_32BIT_ABGR8888:
|
||||
return clut[index + clutSharingOffset];
|
||||
|
||||
default:
|
||||
ERROR_LOG_REPORT(G3D, "Software: Unsupported palette format: %x", gstate.getClutPaletteFormat());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct Nearest4 {
|
||||
MEMORY_ALIGNED16(u32 v[4]);
|
||||
|
||||
operator u32() const {
|
||||
return v[0];
|
||||
}
|
||||
};
|
||||
|
||||
template <int N>
|
||||
inline static Nearest4 SampleNearest(int u[N], int v[N], const u8 *srcptr, int texbufw, int level)
|
||||
{
|
||||
Nearest4 res;
|
||||
if (!srcptr) {
|
||||
memset(res.v, 0, sizeof(res.v));
|
||||
return res;
|
||||
}
|
||||
|
||||
GETextureFormat texfmt = gstate.getTextureFormat();
|
||||
|
||||
// TODO: Should probably check if textures are aligned properly...
|
||||
|
||||
switch (texfmt) {
|
||||
case GE_TFMT_4444:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<16>(texbufw, u[i], v[i]);
|
||||
res.v[i] = RGBA4444ToRGBA8888(*(const u16 *)src);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_5551:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<16>(texbufw, u[i], v[i]);
|
||||
res.v[i] = RGBA5551ToRGBA8888(*(const u16 *)src);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_5650:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<16>(texbufw, u[i], v[i]);
|
||||
res.v[i] = RGB565ToRGBA8888(*(const u16 *)src);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_8888:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<32>(texbufw, u[i], v[i]);
|
||||
res.v[i] = *(const u32 *)src;
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_CLUT32:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<32>(texbufw, u[i], v[i]);
|
||||
u32 val = src[0] + (src[1] << 8) + (src[2] << 16) + (src[3] << 24);
|
||||
res.v[i] = LookupColor(gstate.transformClutIndex(val), 0);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_CLUT16:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<16>(texbufw, u[i], v[i]);
|
||||
u16 val = src[0] + (src[1] << 8);
|
||||
res.v[i] = LookupColor(gstate.transformClutIndex(val), 0);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_CLUT8:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<8>(texbufw, u[i], v[i]);
|
||||
u8 val = *src;
|
||||
res.v[i] = LookupColor(gstate.transformClutIndex(val), 0);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_CLUT4:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const u8 *src = srcptr + GetPixelDataOffset<4>(texbufw, u[i], v[i]);
|
||||
u8 val = (u[i] & 1) ? (src[0] >> 4) : (src[0] & 0xF);
|
||||
// Only CLUT4 uses separate mipmap palettes.
|
||||
res.v[i] = LookupColor(gstate.transformClutIndex(val), level);
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_DXT1:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const DXT1Block *block = (const DXT1Block *)srcptr + (v[i] / 4) * (texbufw / 4) + (u[i] / 4);
|
||||
u32 data[4 * 4];
|
||||
DecodeDXT1Block(data, block, 4, 4, false);
|
||||
res.v[i] = data[4 * (v[i] % 4) + (u[i] % 4)];
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_DXT3:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const DXT3Block *block = (const DXT3Block *)srcptr + (v[i] / 4) * (texbufw / 4) + (u[i] / 4);
|
||||
u32 data[4 * 4];
|
||||
DecodeDXT3Block(data, block, 4, 4);
|
||||
res.v[i] = data[4 * (v[i] % 4) + (u[i] % 4)];
|
||||
}
|
||||
return res;
|
||||
|
||||
case GE_TFMT_DXT5:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const DXT5Block *block = (const DXT5Block *)srcptr + (v[i] / 4) * (texbufw / 4) + (u[i] / 4);
|
||||
u32 data[4 * 4];
|
||||
DecodeDXT5Block(data, block, 4, 4);
|
||||
res.v[i] = data[4 * (v[i] % 4) + (u[i] % 4)];
|
||||
}
|
||||
return res;
|
||||
|
||||
default:
|
||||
ERROR_LOG_REPORT(G3D, "Software: Unsupported texture format: %x", texfmt);
|
||||
memset(res.v, 0, sizeof(res.v));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 SampleNearest(int u, int v, const u8 *tptr, int bufw, int level) {
|
||||
return SampleNearest<1>(&u, &v, tptr, bufw, level);
|
||||
}
|
||||
|
||||
static u32 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);
|
||||
|
||||
Vec4<int> texcolor_tl = Vec4<int>::FromRGBA(c.v[0]);
|
||||
Vec4<int> texcolor_tr = Vec4<int>::FromRGBA(c.v[1]);
|
||||
Vec4<int> texcolor_bl = Vec4<int>::FromRGBA(c.v[2]);
|
||||
Vec4<int> texcolor_br = Vec4<int>::FromRGBA(c.v[3]);
|
||||
Vec4<int> t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u;
|
||||
Vec4<int> b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u;
|
||||
return ((t * (0x100 - frac_v) + b * frac_v) / (256 * 256)).ToRGBA();
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) 2017- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#if PPSSPP_ARCH(ARM)
|
||||
#include "Common/ArmEmitter.h"
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
#include "Common/Arm64Emitter.h"
|
||||
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
#include "Common/x64Emitter.h"
|
||||
#elif PPSSPP_ARCH(MIPS)
|
||||
#include "Common/MipsEmitter.h"
|
||||
#else
|
||||
#include "Common/FakeEmitter.h"
|
||||
#endif
|
||||
#include "GPU/Math3D.h"
|
||||
|
||||
struct SamplerID {
|
||||
SamplerID() : fullKey(0) {
|
||||
}
|
||||
|
||||
union {
|
||||
u32 fullKey;
|
||||
struct {
|
||||
uint8_t texfmt : 4;
|
||||
uint8_t clutfmt : 2;
|
||||
uint8_t : 2;
|
||||
bool swizzle : 1;
|
||||
bool useSharedClut : 1;
|
||||
bool hasClutMask : 1;
|
||||
bool hasClutShift : 1;
|
||||
bool hasClutOffset : 1;
|
||||
bool hasInvalidPtr : 1;
|
||||
bool linear : 1;
|
||||
};
|
||||
};
|
||||
|
||||
bool operator == (const SamplerID &other) const {
|
||||
return fullKey == other.fullKey;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<SamplerID> {
|
||||
std::size_t operator()(const SamplerID &k) const {
|
||||
return hash<u32>()(k.fullKey);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
namespace Sampler {
|
||||
|
||||
typedef u32 (*NearestFunc)(int u, int v, const u8 *tptr, int bufw, int level);
|
||||
NearestFunc GetNearestFunc();
|
||||
|
||||
typedef u32 (*LinearFunc)(int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int level);
|
||||
LinearFunc GetLinearFunc();
|
||||
|
||||
struct Funcs {
|
||||
NearestFunc nearest;
|
||||
LinearFunc linear;
|
||||
};
|
||||
static inline Funcs GetFuncs() {
|
||||
Funcs f;
|
||||
f.nearest = GetNearestFunc();
|
||||
f.linear = GetLinearFunc();
|
||||
return f;
|
||||
}
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name);
|
||||
|
||||
#if PPSSPP_ARCH(ARM)
|
||||
class SamplerJitCache : public ArmGen::ARMXCodeBlock {
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
class SamplerJitCache : public Arm64Gen::ARM64CodeBlock {
|
||||
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
class SamplerJitCache : public Gen::XCodeBlock {
|
||||
#elif PPSSPP_ARCH(MIPS)
|
||||
class SamplerJitCache : public MIPSGen::MIPSCodeBlock {
|
||||
#else
|
||||
class SamplerJitCache : public FakeGen::FakeXCodeBlock {
|
||||
#endif
|
||||
public:
|
||||
SamplerJitCache();
|
||||
|
||||
void ComputeSamplerID(SamplerID *id_out, bool linear);
|
||||
|
||||
// Returns a pointer to the code to run.
|
||||
NearestFunc GetNearest(const SamplerID &id);
|
||||
LinearFunc GetLinear(const SamplerID &id);
|
||||
void Clear();
|
||||
|
||||
std::string DescribeCodePtr(const u8 *ptr);
|
||||
std::string DescribeSamplerID(const SamplerID &id);
|
||||
|
||||
private:
|
||||
NearestFunc Compile(const SamplerID &id);
|
||||
LinearFunc CompileLinear(const SamplerID &id);
|
||||
|
||||
bool Jit_ReadTextureFormat(const SamplerID &id);
|
||||
bool Jit_GetTexData(const SamplerID &id, int bitsPerTexel);
|
||||
bool Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTexel);
|
||||
bool Jit_GetTexDataSwizzled4();
|
||||
bool Jit_Decode5650();
|
||||
bool Jit_Decode5551();
|
||||
bool Jit_Decode4444();
|
||||
bool Jit_TransformClutIndex(const SamplerID &id, int bitsPerIndex);
|
||||
bool Jit_ReadClutColor(const SamplerID &id);
|
||||
|
||||
#if PPSSPP_ARCH(ARM64)
|
||||
Arm64Gen::ARM64FloatEmitter fp;
|
||||
#endif
|
||||
|
||||
std::unordered_map<SamplerID, NearestFunc> cache_;
|
||||
std::unordered_map<SamplerID, const u8 *> addresses_;
|
||||
};
|
||||
|
||||
};
|
||||
@@ -0,0 +1,666 @@
|
||||
// Copyright (c) 2017- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
|
||||
#include <emmintrin.h>
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/Software/Sampler.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
extern u32 clut[4096];
|
||||
|
||||
namespace Sampler {
|
||||
|
||||
#ifdef _WIN32
|
||||
static const X64Reg arg1Reg = RCX;
|
||||
static const X64Reg arg2Reg = RDX;
|
||||
static const X64Reg arg3Reg = R8;
|
||||
static const X64Reg arg4Reg = R9;
|
||||
// 5 and 6 are on the stack.
|
||||
#else
|
||||
static const X64Reg arg1Reg = RDI;
|
||||
static const X64Reg arg2Reg = RSI;
|
||||
static const X64Reg arg3Reg = RDX;
|
||||
static const X64Reg arg4Reg = RCX;
|
||||
static const X64Reg arg5Reg = R8;
|
||||
static const X64Reg arg6Reg = R9;
|
||||
|
||||
static const X64Reg levelReg = arg5Reg;
|
||||
#endif
|
||||
|
||||
static const X64Reg resultReg = RAX;
|
||||
static const X64Reg tempReg1 = R10;
|
||||
static const X64Reg tempReg2 = R11;
|
||||
|
||||
static const X64Reg uReg = arg1Reg;
|
||||
static const X64Reg vReg = arg2Reg;
|
||||
static const X64Reg srcReg = arg3Reg;
|
||||
static const X64Reg bufwReg = arg4Reg;
|
||||
|
||||
static const X64Reg fpScratchReg1 = XMM1;
|
||||
static const X64Reg fpScratchReg2 = XMM2;
|
||||
static const X64Reg fpScratchReg3 = XMM3;
|
||||
static const X64Reg fpScratchReg4 = XMM4;
|
||||
static const X64Reg fpScratchReg5 = XMM5;
|
||||
|
||||
NearestFunc SamplerJitCache::Compile(const SamplerID &id) {
|
||||
BeginWrite();
|
||||
const u8 *start = AlignCode16();
|
||||
|
||||
// Early exit on !srcPtr.
|
||||
FixupBranch zeroSrc;
|
||||
if (id.hasInvalidPtr) {
|
||||
CMP(PTRBITS, R(srcReg), Imm8(0));
|
||||
FixupBranch nonZeroSrc = J_CC(CC_NZ);
|
||||
XOR(32, R(RAX), R(RAX));
|
||||
zeroSrc = J(true);
|
||||
SetJumpTarget(nonZeroSrc);
|
||||
}
|
||||
|
||||
if (!Jit_ReadTextureFormat(id)) {
|
||||
EndWrite();
|
||||
SetCodePtr(const_cast<u8 *>(start));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (id.hasInvalidPtr) {
|
||||
SetJumpTarget(zeroSrc);
|
||||
}
|
||||
|
||||
RET();
|
||||
|
||||
EndWrite();
|
||||
return (NearestFunc)start;
|
||||
}
|
||||
|
||||
static const float MEMORY_ALIGNED16(by256[4]) = { 1.0f / 256.0f, 1.0f / 256.0f, 1.0f / 256.0f, 1.0f / 256.0f, };
|
||||
static const float MEMORY_ALIGNED16(ones[4]) = { 1.0f, 1.0f, 1.0f, 1.0f, };
|
||||
|
||||
LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
|
||||
_assert_msg_(G3D, id.linear, "Linear should be set on sampler id");
|
||||
BeginWrite();
|
||||
|
||||
// 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)) {
|
||||
EndWrite();
|
||||
SetCodePtr(const_cast<u8 *>(nearest));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RET();
|
||||
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
PUSH(arg4Reg);
|
||||
PUSH(arg3Reg);
|
||||
// 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;
|
||||
MOV(64, R(R14), MDisp(RSP, argOffset));
|
||||
MOV(32, R(R15), MDisp(RSP, argOffset + 8));
|
||||
// level is at argOffset + 12.
|
||||
#else
|
||||
MOV(64, R(R14), R(arg5Reg));
|
||||
MOV(32, R(R15), R(arg6Reg));
|
||||
// level is at 24 + 48 + 8.
|
||||
#endif
|
||||
|
||||
// Early exit on !srcPtr.
|
||||
FixupBranch zeroSrc;
|
||||
if (id.hasInvalidPtr) {
|
||||
CMP(PTRBITS, R(R14), Imm8(0));
|
||||
FixupBranch nonZeroSrc = J_CC(CC_NZ);
|
||||
XOR(32, R(RAX), R(RAX));
|
||||
zeroSrc = J(true);
|
||||
SetJumpTarget(nonZeroSrc);
|
||||
}
|
||||
|
||||
// At this point:
|
||||
// R12=uptr, R13=vptr, stack+24=frac_u, stack+32=frac_v, R14=src, R15=bufw, stack+X=level
|
||||
|
||||
auto doNearestCall = [&](int off) {
|
||||
MOV(32, R(uReg), MDisp(R12, off));
|
||||
MOV(32, R(vReg), MDisp(R13, off));
|
||||
MOV(64, R(srcReg), R(R14));
|
||||
MOV(32, R(bufwReg), R(R15));
|
||||
// Leave level, we just always load from RAM. Separate CLUTs is uncommon.
|
||||
|
||||
CALL(nearest);
|
||||
MOV(32, MDisp(RSP, off), R(resultReg));
|
||||
};
|
||||
|
||||
doNearestCall(0);
|
||||
doNearestCall(4);
|
||||
doNearestCall(8);
|
||||
doNearestCall(12);
|
||||
|
||||
// Convert TL, TR, BL, BR to floats for easier blending.
|
||||
if (!cpu_info.bSSE4_1) {
|
||||
PXOR(XMM0, R(XMM0));
|
||||
}
|
||||
|
||||
MOVD_xmm(fpScratchReg1, MDisp(RSP, 0));
|
||||
MOVD_xmm(fpScratchReg2, MDisp(RSP, 4));
|
||||
MOVD_xmm(fpScratchReg3, MDisp(RSP, 8));
|
||||
MOVD_xmm(fpScratchReg4, MDisp(RSP, 12));
|
||||
|
||||
if (cpu_info.bSSE4_1) {
|
||||
PMOVZXBD(fpScratchReg1, R(fpScratchReg1));
|
||||
PMOVZXBD(fpScratchReg2, R(fpScratchReg2));
|
||||
PMOVZXBD(fpScratchReg3, R(fpScratchReg3));
|
||||
PMOVZXBD(fpScratchReg4, R(fpScratchReg4));
|
||||
} else {
|
||||
PUNPCKLBW(fpScratchReg1, R(XMM0));
|
||||
PUNPCKLBW(fpScratchReg2, R(XMM0));
|
||||
PUNPCKLBW(fpScratchReg3, R(XMM0));
|
||||
PUNPCKLBW(fpScratchReg4, R(XMM0));
|
||||
PUNPCKLWD(fpScratchReg1, R(XMM0));
|
||||
PUNPCKLWD(fpScratchReg2, R(XMM0));
|
||||
PUNPCKLWD(fpScratchReg3, R(XMM0));
|
||||
PUNPCKLWD(fpScratchReg4, R(XMM0));
|
||||
}
|
||||
CVTDQ2PS(fpScratchReg1, R(fpScratchReg1));
|
||||
CVTDQ2PS(fpScratchReg2, R(fpScratchReg2));
|
||||
CVTDQ2PS(fpScratchReg3, R(fpScratchReg3));
|
||||
CVTDQ2PS(fpScratchReg4, R(fpScratchReg4));
|
||||
|
||||
// Okay, now multiply the R sides by frac_u, and L by (256 - frac_u)...
|
||||
MOVD_xmm(fpScratchReg5, MDisp(RSP, 24));
|
||||
CVTDQ2PS(fpScratchReg5, R(fpScratchReg5));
|
||||
SHUFPS(fpScratchReg5, R(fpScratchReg5), _MM_SHUFFLE(0, 0, 0, 0));
|
||||
MULPS(fpScratchReg5, M(by256));
|
||||
MOVAPS(XMM0, M(ones));
|
||||
SUBPS(XMM0, R(fpScratchReg5));
|
||||
|
||||
MULPS(fpScratchReg1, R(XMM0));
|
||||
MULPS(fpScratchReg2, R(fpScratchReg5));
|
||||
MULPS(fpScratchReg3, R(XMM0));
|
||||
MULPS(fpScratchReg4, R(fpScratchReg5));
|
||||
|
||||
// Now set top=fpScratchReg1, bottom=fpScratchReg3.
|
||||
ADDPS(fpScratchReg1, R(fpScratchReg2));
|
||||
ADDPS(fpScratchReg3, R(fpScratchReg4));
|
||||
|
||||
// Next, time for frac_v.
|
||||
MOVD_xmm(fpScratchReg5, MDisp(RSP, 32));
|
||||
CVTDQ2PS(fpScratchReg5, R(fpScratchReg5));
|
||||
SHUFPS(fpScratchReg5, R(fpScratchReg5), _MM_SHUFFLE(0, 0, 0, 0));
|
||||
MULPS(fpScratchReg5, M(by256));
|
||||
MOVAPS(XMM0, M(ones));
|
||||
SUBPS(XMM0, R(fpScratchReg5));
|
||||
|
||||
MULPS(fpScratchReg1, R(XMM0));
|
||||
MULPS(fpScratchReg3, R(fpScratchReg5));
|
||||
|
||||
// Still at the 255 scale, now we're interpolated.
|
||||
ADDPS(fpScratchReg1, R(fpScratchReg3));
|
||||
|
||||
// Time to convert back to a single 32 bit value.
|
||||
CVTPS2DQ(fpScratchReg1, R(fpScratchReg1));
|
||||
PACKSSDW(fpScratchReg1, R(fpScratchReg1));
|
||||
PACKUSWB(fpScratchReg1, R(fpScratchReg1));
|
||||
MOVD_xmm(R(resultReg), fpScratchReg1);
|
||||
|
||||
if (id.hasInvalidPtr) {
|
||||
SetJumpTarget(zeroSrc);
|
||||
}
|
||||
|
||||
ADD(64, R(RSP), Imm8(24));
|
||||
POP(arg3Reg);
|
||||
POP(arg4Reg);
|
||||
POP(R12);
|
||||
POP(R13);
|
||||
POP(R14);
|
||||
POP(R15);
|
||||
|
||||
RET();
|
||||
|
||||
EndWrite();
|
||||
return (LinearFunc)start;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_ReadTextureFormat(const SamplerID &id) {
|
||||
GETextureFormat fmt = (GETextureFormat)id.texfmt;
|
||||
bool success = true;
|
||||
switch (fmt) {
|
||||
case GE_TFMT_5650:
|
||||
success = Jit_GetTexData(id, 16);
|
||||
if (success)
|
||||
success = Jit_Decode5650();
|
||||
break;
|
||||
|
||||
case GE_TFMT_5551:
|
||||
success = Jit_GetTexData(id, 16);
|
||||
if (success)
|
||||
success = Jit_Decode5551();
|
||||
break;
|
||||
|
||||
case GE_TFMT_4444:
|
||||
success = Jit_GetTexData(id, 16);
|
||||
if (success)
|
||||
success = Jit_Decode4444();
|
||||
break;
|
||||
|
||||
case GE_TFMT_8888:
|
||||
success = Jit_GetTexData(id, 32);
|
||||
break;
|
||||
|
||||
case GE_TFMT_CLUT32:
|
||||
success = Jit_GetTexData(id, 32);
|
||||
if (success)
|
||||
success = Jit_TransformClutIndex(id, 32);
|
||||
if (success)
|
||||
success = Jit_ReadClutColor(id);
|
||||
break;
|
||||
|
||||
case GE_TFMT_CLUT16:
|
||||
success = Jit_GetTexData(id, 16);
|
||||
if (success)
|
||||
success = Jit_TransformClutIndex(id, 16);
|
||||
if (success)
|
||||
success = Jit_ReadClutColor(id);
|
||||
break;
|
||||
|
||||
case GE_TFMT_CLUT8:
|
||||
success = Jit_GetTexData(id, 8);
|
||||
if (success)
|
||||
success = Jit_TransformClutIndex(id, 8);
|
||||
if (success)
|
||||
success = Jit_ReadClutColor(id);
|
||||
break;
|
||||
|
||||
case GE_TFMT_CLUT4:
|
||||
success = Jit_GetTexData(id, 4);
|
||||
if (success)
|
||||
success = Jit_TransformClutIndex(id, 4);
|
||||
if (success)
|
||||
success = Jit_ReadClutColor(id);
|
||||
break;
|
||||
|
||||
// TODO: DXT?
|
||||
default:
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) {
|
||||
if (id.swizzle) {
|
||||
return Jit_GetTexDataSwizzled(id, bitsPerTexel);
|
||||
}
|
||||
|
||||
// srcReg might be EDX, so let's copy that before we multiply.
|
||||
switch (bitsPerTexel) {
|
||||
case 32:
|
||||
case 16:
|
||||
case 8:
|
||||
LEA(64, tempReg1, MComplex(srcReg, uReg, bitsPerTexel / 8, 0));
|
||||
break;
|
||||
|
||||
case 4: {
|
||||
XOR(32, R(tempReg2), R(tempReg2));
|
||||
SHR(32, R(uReg), Imm8(1));
|
||||
FixupBranch skip = J_CC(CC_NC);
|
||||
// Track whether we shifted a 1 off or not.
|
||||
MOV(32, R(tempReg2), Imm32(4));
|
||||
SetJumpTarget(skip);
|
||||
LEA(64, tempReg1, MRegSum(srcReg, uReg));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
MOV(32, R(EAX), R(vReg));
|
||||
MUL(32, R(bufwReg));
|
||||
|
||||
switch (bitsPerTexel) {
|
||||
case 32:
|
||||
case 16:
|
||||
case 8:
|
||||
MOVZX(32, bitsPerTexel, resultReg, MComplex(tempReg1, RAX, bitsPerTexel / 8, 0));
|
||||
break;
|
||||
|
||||
case 4: {
|
||||
SHR(32, R(RAX), Imm8(1));
|
||||
MOV(8, R(resultReg), MRegSum(tempReg1, RAX));
|
||||
// RCX is now free.
|
||||
MOV(8, R(RCX), R(tempReg2));
|
||||
SHR(8, R(resultReg), R(RCX));
|
||||
// Zero out any bits not shifted off.
|
||||
AND(32, R(resultReg), Imm8(0x0F));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_GetTexDataSwizzled4() {
|
||||
// Get the horizontal tile pos into tempReg1.
|
||||
LEA(32, tempReg1, MScaled(uReg, SCALE_4, 0));
|
||||
// Note: imm8 sign extends negative.
|
||||
AND(32, R(tempReg1), Imm8(~127));
|
||||
|
||||
// Add vertical offset inside tile to tempReg1.
|
||||
LEA(32, tempReg2, MScaled(vReg, SCALE_4, 0));
|
||||
AND(32, R(tempReg2), Imm8(31));
|
||||
LEA(32, tempReg1, MComplex(tempReg1, tempReg2, SCALE_4, 0));
|
||||
// Add srcReg, since we'll need it at some point.
|
||||
ADD(64, R(tempReg1), R(srcReg));
|
||||
|
||||
// Now find the vertical tile pos, and add to tempReg1.
|
||||
SHR(32, R(vReg), Imm8(3));
|
||||
LEA(32, EAX, MScaled(bufwReg, SCALE_4, 0));
|
||||
MUL(32, R(vReg));
|
||||
ADD(64, R(tempReg1), R(EAX));
|
||||
|
||||
// Last and possible also least, the horizontal offset inside the tile.
|
||||
AND(32, R(uReg), Imm8(31));
|
||||
SHR(32, R(uReg), Imm8(1));
|
||||
MOV(8, R(resultReg), MRegSum(tempReg1, uReg));
|
||||
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));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTexel) {
|
||||
if (bitsPerTexel == 4) {
|
||||
// Specialized implementation.
|
||||
return Jit_GetTexDataSwizzled4();
|
||||
}
|
||||
|
||||
LEA(32, tempReg1, MScaled(vReg, SCALE_4, 0));
|
||||
AND(32, R(tempReg1), Imm8(31));
|
||||
AND(32, R(vReg), Imm8(~7));
|
||||
|
||||
MOV(32, R(tempReg2), R(uReg));
|
||||
MOV(32, R(resultReg), R(uReg));
|
||||
switch (bitsPerTexel) {
|
||||
case 32:
|
||||
SHR(32, R(resultReg), Imm8(2));
|
||||
break;
|
||||
case 16:
|
||||
SHR(32, R(vReg), Imm8(1));
|
||||
SHR(32, R(tempReg2), Imm8(1));
|
||||
SHR(32, R(resultReg), Imm8(3));
|
||||
break;
|
||||
case 8:
|
||||
SHR(32, R(vReg), Imm8(2));
|
||||
SHR(32, R(tempReg2), Imm8(2));
|
||||
SHR(32, R(resultReg), Imm8(4));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
AND(32, R(tempReg2), Imm8(3));
|
||||
SHL(32, R(resultReg), Imm8(5));
|
||||
ADD(32, R(tempReg1), R(tempReg2));
|
||||
ADD(32, R(tempReg1), R(resultReg));
|
||||
|
||||
// We may clobber srcReg in the MUL, so let's grab it now.
|
||||
LEA(64, tempReg1, MComplex(srcReg, tempReg1, SCALE_4, 0));
|
||||
|
||||
LEA(32, EAX, MScaled(bufwReg, SCALE_4, 0));
|
||||
MUL(32, R(vReg));
|
||||
|
||||
switch (bitsPerTexel) {
|
||||
case 32:
|
||||
MOV(bitsPerTexel, R(resultReg), MRegSum(tempReg1, EAX));
|
||||
break;
|
||||
case 16:
|
||||
AND(32, R(uReg), Imm8(1));
|
||||
// Multiply by two by just adding twice.
|
||||
ADD(32, R(EAX), R(uReg));
|
||||
ADD(32, R(EAX), R(uReg));
|
||||
MOVZX(32, bitsPerTexel, resultReg, MRegSum(tempReg1, EAX));
|
||||
break;
|
||||
case 8:
|
||||
AND(32, R(uReg), Imm8(3));
|
||||
ADD(32, R(EAX), R(uReg));
|
||||
MOVZX(32, bitsPerTexel, resultReg, MRegSum(tempReg1, EAX));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_Decode5650() {
|
||||
MOV(32, R(tempReg2), R(resultReg));
|
||||
AND(32, R(tempReg2), Imm32(0x0000001F));
|
||||
|
||||
// B (we do R and B at the same time, they're both 5.)
|
||||
MOV(32, R(tempReg1), R(resultReg));
|
||||
AND(32, R(tempReg1), Imm32(0x0000F800));
|
||||
SHL(32, R(tempReg1), Imm8(5));
|
||||
OR(32, R(tempReg2), R(tempReg1));
|
||||
|
||||
// Expand 5 -> 8. At this point we have 00BB00RR.
|
||||
MOV(32, R(tempReg1), R(tempReg2));
|
||||
SHL(32, R(tempReg2), Imm8(3));
|
||||
SHR(32, R(tempReg1), Imm8(2));
|
||||
OR(32, R(tempReg2), R(tempReg1));
|
||||
AND(32, R(tempReg2), Imm32(0x00FF00FF));
|
||||
|
||||
// Now's as good a time to put in A as any.
|
||||
OR(32, R(tempReg2), Imm32(0xFF000000));
|
||||
|
||||
// Last, we need to align, extract, and expand G.
|
||||
// 3 to align to G, and then 2 to expand to 8.
|
||||
SHL(32, R(resultReg), Imm8(3 + 2));
|
||||
AND(32, R(resultReg), Imm32(0x0000FC00));
|
||||
MOV(32, R(tempReg1), R(resultReg));
|
||||
// 2 to account for resultReg being preshifted, 4 for expansion.
|
||||
SHR(32, R(tempReg1), Imm8(2 + 4));
|
||||
OR(32, R(resultReg), R(tempReg1));
|
||||
AND(32, R(resultReg), Imm32(0x0000FF00));
|
||||
OR(32, R(resultReg), R(tempReg2));;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_Decode5551() {
|
||||
MOV(32, R(tempReg2), R(resultReg));
|
||||
MOV(32, R(tempReg1), R(resultReg));
|
||||
AND(32, R(tempReg2), Imm32(0x0000001F));
|
||||
AND(32, R(tempReg1), Imm32(0x000003E0));
|
||||
SHL(32, R(tempReg1), Imm8(3));
|
||||
OR(32, R(tempReg2), R(tempReg1));
|
||||
|
||||
MOV(32, R(tempReg1), R(resultReg));
|
||||
AND(32, R(tempReg1), Imm32(0x00007C00));
|
||||
SHL(32, R(tempReg1), Imm8(6));
|
||||
OR(32, R(tempReg2), R(tempReg1));
|
||||
|
||||
// Expand 5 -> 8. After this is just A.
|
||||
MOV(32, R(tempReg1), R(tempReg2));
|
||||
SHL(32, R(tempReg2), Imm8(3));
|
||||
SHR(32, R(tempReg1), Imm8(2));
|
||||
// Chop off the bits that were shifted out.
|
||||
AND(32, R(tempReg1), Imm32(0x00070707));
|
||||
OR(32, R(tempReg2), R(tempReg1));
|
||||
|
||||
// For A, we shift it to a single bit, and then subtract and XOR.
|
||||
// That's probably the simplest way to expand it...
|
||||
SHR(32, R(resultReg), Imm8(15));
|
||||
// If it was 0, it's now -1, otherwise it's 0. Easy.
|
||||
SUB(32, R(resultReg), Imm8(1));
|
||||
XOR(32, R(resultReg), Imm32(0xFF000000));
|
||||
AND(32, R(resultReg), Imm32(0xFF000000));
|
||||
OR(32, R(resultReg), R(tempReg2));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const u32 MEMORY_ALIGNED16(color4444mask[4]) = { 0xf00ff00f, 0xf00ff00f, 0xf00ff00f, 0xf00ff00f, };
|
||||
|
||||
bool SamplerJitCache::Jit_Decode4444() {
|
||||
MOVD_xmm(fpScratchReg1, R(resultReg));
|
||||
PUNPCKLBW(fpScratchReg1, R(fpScratchReg1));
|
||||
PAND(fpScratchReg1, M(color4444mask));
|
||||
MOVSS(fpScratchReg2, R(fpScratchReg1));
|
||||
MOVSS(fpScratchReg3, R(fpScratchReg1));
|
||||
PSRLW(fpScratchReg2, 4);
|
||||
PSLLW(fpScratchReg3, 4);
|
||||
POR(fpScratchReg1, R(fpScratchReg2));
|
||||
POR(fpScratchReg1, R(fpScratchReg3));
|
||||
MOVD_xmm(R(resultReg), fpScratchReg1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_TransformClutIndex(const SamplerID &id, int bitsPerIndex) {
|
||||
GEPaletteFormat fmt = (GEPaletteFormat)id.clutfmt;
|
||||
if (!id.hasClutShift && !id.hasClutMask && !id.hasClutOffset) {
|
||||
// This is simple - just mask if necessary.
|
||||
if (bitsPerIndex > 8) {
|
||||
AND(32, R(resultReg), Imm32(0x000000FF));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MOV(PTRBITS, R(tempReg1), ImmPtr(&gstate.clutformat));
|
||||
MOV(32, R(tempReg1), MatR(tempReg1));
|
||||
|
||||
// Shift = (clutformat >> 2) & 0x1F
|
||||
if (id.hasClutShift) {
|
||||
MOV(32, R(RCX), R(tempReg1));
|
||||
SHR(32, R(RCX), Imm8(2));
|
||||
AND(32, R(RCX), Imm8(0x1F));
|
||||
SHR(32, R(resultReg), R(RCX));
|
||||
}
|
||||
|
||||
// Mask = (clutformat >> 8) & 0xFF
|
||||
if (id.hasClutMask) {
|
||||
MOV(32, R(tempReg2), R(tempReg1));
|
||||
SHR(32, R(tempReg2), Imm8(8));
|
||||
AND(32, R(resultReg), R(tempReg2));
|
||||
}
|
||||
|
||||
// We need to wrap any entries beyond the first 1024 bytes.
|
||||
u32 offsetMask = fmt == GE_CMODE_32BIT_ABGR8888 ? 0x00FF : 0x01FF;
|
||||
|
||||
// We must mask to 0xFF before ORing 0x100 in 16 bit CMODEs.
|
||||
// But skip if we'll mask 0xFF after offset anyway.
|
||||
if (bitsPerIndex > 8 && (!id.hasClutOffset || offsetMask != 0x00FF)) {
|
||||
AND(32, R(resultReg), Imm32(0x000000FF));
|
||||
}
|
||||
|
||||
// Offset = (clutformat >> 12) & 0x01F0
|
||||
if (id.hasClutOffset) {
|
||||
SHR(32, R(tempReg1), Imm8(16));
|
||||
SHL(32, R(tempReg1), Imm8(4));
|
||||
OR(32, R(resultReg), R(tempReg1));
|
||||
AND(32, R(resultReg), Imm32(offsetMask));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) {
|
||||
if (!id.useSharedClut) {
|
||||
// TODO: Need to load from RAM, always.
|
||||
if (id.linear) {
|
||||
#ifdef _WIN32
|
||||
const int argOffset = 24 + 48 + 8 + 32;
|
||||
// Extra 8 to account for CALL.
|
||||
MOV(32, R(tempReg2), MDisp(RSP, argOffset + 12 + 8));
|
||||
#else
|
||||
// Extra 8 to account for CALL.
|
||||
MOV(32, R(tempReg2), MDisp(RSP, 24 + 48 + 8 + 8));
|
||||
#endif
|
||||
LEA(32, tempReg2, MScaled(tempReg2, SCALE_4, 0));
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
// The argument was saved on the stack.
|
||||
MOV(32, R(tempReg2), MDisp(RSP, 40));
|
||||
LEA(32, tempReg2, MScaled(tempReg2, SCALE_4, 0));
|
||||
#else
|
||||
// We need to multiply by 16 and add, LEA allows us to copy too.
|
||||
LEA(32, tempReg2, MScaled(levelReg, SCALE_4, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Second step of the multiply by 16 (since we only multiplied by 4 before.)
|
||||
LEA(64, resultReg, MComplex(resultReg, tempReg2, SCALE_4, 0));
|
||||
}
|
||||
|
||||
MOV(PTRBITS, R(tempReg1), ImmPtr(clut));
|
||||
|
||||
switch ((GEPaletteFormat)id.clutfmt) {
|
||||
case GE_CMODE_16BIT_BGR5650:
|
||||
MOVZX(32, 16, resultReg, MComplex(tempReg1, resultReg, SCALE_2, 0));
|
||||
return Jit_Decode5650();
|
||||
|
||||
case GE_CMODE_16BIT_ABGR5551:
|
||||
MOVZX(32, 16, resultReg, MComplex(tempReg1, resultReg, SCALE_2, 0));
|
||||
return Jit_Decode5551();
|
||||
|
||||
case GE_CMODE_16BIT_ABGR4444:
|
||||
MOVZX(32, 16, resultReg, MComplex(tempReg1, resultReg, SCALE_2, 0));
|
||||
return Jit_Decode4444();
|
||||
|
||||
case GE_CMODE_32BIT_ABGR8888:
|
||||
MOV(32, R(resultReg), MComplex(tempReg1, resultReg, SCALE_4, 0));
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -31,9 +31,10 @@
|
||||
#include "profiler/profiler.h"
|
||||
#include "thin3d/thin3d.h"
|
||||
|
||||
#include "GPU/Software/Rasterizer.h"
|
||||
#include "GPU/Software/Sampler.h"
|
||||
#include "GPU/Software/SoftGpu.h"
|
||||
#include "GPU/Software/TransformUnit.h"
|
||||
#include "GPU/Software/Rasterizer.h"
|
||||
#include "GPU/Common/DrawEngineCommon.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
|
||||
@@ -99,6 +100,7 @@ SoftGPU::SoftGPU(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
displayStride_ = 512;
|
||||
displayFormat_ = GE_FORMAT_8888;
|
||||
|
||||
Sampler::Init();
|
||||
drawEngine_ = new SoftwareDrawEngine();
|
||||
drawEngineCommon_ = drawEngine_;
|
||||
}
|
||||
@@ -127,6 +129,8 @@ SoftGPU::~SoftGPU() {
|
||||
samplerNearest = nullptr;
|
||||
samplerLinear->Release();
|
||||
samplerLinear = nullptr;
|
||||
|
||||
Sampler::Shutdown();
|
||||
}
|
||||
|
||||
void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
|
||||
@@ -991,3 +995,12 @@ bool SoftGPU::GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &v
|
||||
{
|
||||
return drawEngine_->transformUnit.GetCurrentSimpleVertices(count, vertices, indices);
|
||||
}
|
||||
|
||||
bool SoftGPU::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
std::string subname;
|
||||
if (Sampler::DescribeCodePtr(ptr, subname)) {
|
||||
name = "SamplerJit:" + subname;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -91,6 +91,8 @@ public:
|
||||
bool GetCurrentClut(GPUDebugBuffer &buffer) override;
|
||||
bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) override;
|
||||
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
|
||||
|
||||
protected:
|
||||
void FastRunLoop(DisplayList &list) override;
|
||||
void ProcessEvent(GPUEvent ev) override;
|
||||
|
||||
@@ -344,6 +344,7 @@
|
||||
<ClInclude Include="..\..\GPU\Software\Clipper.h" />
|
||||
<ClInclude Include="..\..\GPU\Software\Lighting.h" />
|
||||
<ClInclude Include="..\..\GPU\Software\Rasterizer.h" />
|
||||
<ClInclude Include="..\..\GPU\Software\Sampler.h" />
|
||||
<ClInclude Include="..\..\GPU\Software\SoftGpu.h" />
|
||||
<ClInclude Include="..\..\GPU\Software\TransformUnit.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -397,6 +398,7 @@
|
||||
<ClCompile Include="..\..\GPU\Software\Clipper.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Software\Lighting.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Software\Rasterizer.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Software\Sampler.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Software\SoftGpu.cpp" />
|
||||
<ClCompile Include="..\..\GPU\Software\TransformUnit.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
|
||||
@@ -28,7 +28,8 @@ ARCH_FILES := \
|
||||
$(SRC)/Core/MIPS/x86/JitSafeMem.cpp \
|
||||
$(SRC)/Core/MIPS/x86/RegCache.cpp \
|
||||
$(SRC)/Core/MIPS/x86/RegCacheFPU.cpp \
|
||||
$(SRC)/GPU/Common/VertexDecoderX86.cpp
|
||||
$(SRC)/GPU/Common/VertexDecoderX86.cpp \
|
||||
$(SRC)/GPU/Software/SamplerX86.cpp
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH_ABI),x86_64)
|
||||
@@ -48,7 +49,8 @@ ARCH_FILES := \
|
||||
$(SRC)/Core/MIPS/x86/JitSafeMem.cpp \
|
||||
$(SRC)/Core/MIPS/x86/RegCache.cpp \
|
||||
$(SRC)/Core/MIPS/x86/RegCacheFPU.cpp \
|
||||
$(SRC)/GPU/Common/VertexDecoderX86.cpp
|
||||
$(SRC)/GPU/Common/VertexDecoderX86.cpp \
|
||||
$(SRC)/GPU/Software/SamplerX86.cpp
|
||||
endif
|
||||
|
||||
ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a)
|
||||
@@ -244,6 +246,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/GPU/Software/Clipper.cpp \
|
||||
$(SRC)/GPU/Software/Lighting.cpp \
|
||||
$(SRC)/GPU/Software/Rasterizer.cpp.arm \
|
||||
$(SRC)/GPU/Software/Sampler.cpp \
|
||||
$(SRC)/GPU/Software/SoftGpu.cpp \
|
||||
$(SRC)/GPU/Software/TransformUnit.cpp \
|
||||
$(SRC)/Core/ELF/ElfReader.cpp \
|
||||
|
||||
Reference in new issue
Block a user