From d5426e4360a20524c7f332aa3d46d28bfc2a2ac0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 9 May 2017 19:48:05 -0700 Subject: [PATCH 01/17] SoftGPU: Move sampler code to a dedicated file. --- CMakeLists.txt | 2 + GPU/GPU.vcxproj | 2 + GPU/GPU.vcxproj.filters | 6 + GPU/Software/Rasterizer.cpp | 205 +------------------------------ GPU/Software/Sampler.cpp | 232 ++++++++++++++++++++++++++++++++++++ GPU/Software/Sampler.h | 27 +++++ android/jni/Android.mk | 1 + 7 files changed, 276 insertions(+), 199 deletions(-) create mode 100644 GPU/Software/Sampler.cpp create mode 100644 GPU/Software/Sampler.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b6ed0e50e3..81d1d0707f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1284,6 +1284,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 diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index dce71c3d73..54785e545d 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -250,6 +250,7 @@ + @@ -350,6 +351,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 8d92e6f620..15353f1ab7 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -259,6 +259,9 @@ Common + + Software + @@ -510,5 +513,8 @@ Vulkan + + Software + \ No newline at end of file diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index e1c5e0c9d8..250dc28379 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -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 @@ -110,51 +111,6 @@ static inline Vec4 Interpolate(const float &c0, const float &c1, const fl return Interpolate(c0, c1, c2, w0.Cast(), w1.Cast(), w2.Cast(), wsum_recip); } -template -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(clut)[index + clutSharingOffset]); - - case GE_CMODE_16BIT_ABGR5551: - return RGBA5551ToRGBA8888(reinterpret_cast(clut)[index + clutSharingOffset]); - - case GE_CMODE_16BIT_ABGR4444: - return RGBA4444ToRGBA8888(reinterpret_cast(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 -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,38 +1003,6 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 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(_mm_unpacklo_epi16(res, z)); -#else - Nearest4 nearest = SampleNearest<4>(texlevel, u, v, tptr, bufw); - Vec4 texcolor_tl = Vec4::FromRGBA(nearest.v[0]); - Vec4 texcolor_tr = Vec4::FromRGBA(nearest.v[1]); - Vec4 texcolor_bl = Vec4::FromRGBA(nearest.v[2]); - Vec4 texcolor_br = Vec4::FromRGBA(nearest.v[3]); - // 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255. - Vec4 t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u; - Vec4 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 &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 &prim_color, float s, float t, int t GetTexelCoordinates(texlevel + 1, s, t, u[1], v[1]); } - texcolor0 = Vec4::FromRGBA(SampleNearest<1>(texlevel, u, v, tptr0, bufw0)); + texcolor0 = Sampler::SampleNearest(texlevel, u[0], v[0], tptr0, bufw0); if (frac_texlevel) { - texcolor1 = Vec4::FromRGBA(SampleNearest<1>(texlevel + 1, u + 1, v + 1, tptr1, bufw1)); + texcolor1 = Sampler::SampleNearest(texlevel + 1, u[1], v[1], tptr1, bufw1); } } else { GetTexelCoordinatesQuad(texlevel, s, t, u, v, frac_u[0], frac_v[0]); @@ -1224,9 +1031,9 @@ static inline void ApplyTexturing(Vec4 &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 = Sampler::SampleLinear(texlevel, u, v, frac_u[0], frac_v[0], tptr0, bufw0); if (frac_texlevel) { - texcolor1 = SampleLinear(texlevel + 1, u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1); + texcolor1 = Sampler::SampleLinear(texlevel + 1, u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1); } } @@ -1828,7 +1635,7 @@ bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) 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::SampleNearest(level, x, y, texptr, texbufw).ToRGBA(); } row += w; } diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp new file mode 100644 index 0000000000..7f1ffc001d --- /dev/null +++ b/GPU/Software/Sampler.cpp @@ -0,0 +1,232 @@ +// 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 "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 +#endif + +using namespace Math3D; + +extern u32 clut[4096]; + +namespace Sampler { + +template +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(clut)[index + clutSharingOffset]); + + case GE_CMODE_16BIT_ABGR5551: + return RGBA5551ToRGBA8888(reinterpret_cast(clut)[index + clutSharingOffset]); + + case GE_CMODE_16BIT_ABGR4444: + return RGBA4444ToRGBA8888(reinterpret_cast(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 +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; + } +} + +Vec4 SampleNearest(int level, int u, int v, const u8 *tptr, int bufw) { + return Vec4::FromRGBA(SampleNearest<1>(level, &u, &v, tptr, bufw)); +} + +Vec4 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(_mm_unpacklo_epi16(res, z)); +#else + Nearest4 nearest = SampleNearest<4>(texlevel, u, v, tptr, bufw); + Vec4 texcolor_tl = Vec4::FromRGBA(nearest.v[0]); + Vec4 texcolor_tr = Vec4::FromRGBA(nearest.v[1]); + Vec4 texcolor_bl = Vec4::FromRGBA(nearest.v[2]); + Vec4 texcolor_br = Vec4::FromRGBA(nearest.v[3]); + // 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255. + Vec4 t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u; + Vec4 b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u; + return (t * (0x100 - frac_v) + b * frac_v) / (256 * 256); +#endif +} + +}; diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h new file mode 100644 index 0000000000..768f27a0ae --- /dev/null +++ b/GPU/Software/Sampler.h @@ -0,0 +1,27 @@ +// 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 "GPU/Math3D.h" + +namespace Sampler { + +Math3D::Vec4 SampleNearest(int level, int u, int v, const u8 *tptr, int bufwbytes); +Math3D::Vec4 SampleLinear(int level, int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufwbytes); + +}; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 9f7fe881d9..a67b94d110 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -244,6 +244,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 \ From 1b491fe15612e281ac3a7f923540938be8fb5ccf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 17:31:34 -0700 Subject: [PATCH 02/17] SoftGPU: Stub a jit for texel fetch. --- CMakeLists.txt | 1 + GPU/GPU.vcxproj | 1 + GPU/GPU.vcxproj.filters | 3 + GPU/Software/Rasterizer.cpp | 30 ++++++---- GPU/Software/Sampler.cpp | 116 ++++++++++++++++++++++++++++++++---- GPU/Software/Sampler.h | 87 ++++++++++++++++++++++++++- GPU/Software/SamplerX86.cpp | 96 +++++++++++++++++++++++++++++ GPU/Software/SoftGpu.cpp | 6 +- UWP/GPU_UWP/GPU_UWP.vcxproj | 2 + android/jni/Android.mk | 6 +- 10 files changed, 321 insertions(+), 27 deletions(-) create mode 100644 GPU/Software/SamplerX86.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 81d1d0707f..98d4f79846 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 54785e545d..dcf471a173 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -352,6 +352,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 15353f1ab7..2499cfab12 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -516,5 +516,8 @@ Software + + Software + \ No newline at end of file diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 250dc28379..1fd9268271 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -1003,7 +1003,7 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 &prim_color, float s, float t, int texlevel, int frac_texlevel, bool bilinear, u8 *texptr[], int texbufw[]) { +static inline void ApplyTexturing(Sampler::NearestFunc sampler, Vec4 &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]; @@ -1021,9 +1021,9 @@ static inline void ApplyTexturing(Vec4 &prim_color, float s, float t, int t GetTexelCoordinates(texlevel + 1, s, t, u[1], v[1]); } - texcolor0 = Sampler::SampleNearest(texlevel, u[0], v[0], tptr0, bufw0); + texcolor0 = Vec4::FromRGBA(sampler(u[0], v[0], tptr0, bufw0, texlevel)); if (frac_texlevel) { - texcolor1 = Sampler::SampleNearest(texlevel + 1, u[1], v[1], tptr1, bufw1); + texcolor1 = Vec4::FromRGBA(sampler(u[1], v[1], tptr1, bufw1, texlevel + 1)); } } else { GetTexelCoordinatesQuad(texlevel, s, t, u, v, frac_u[0], frac_v[0]); @@ -1031,9 +1031,9 @@ static inline void ApplyTexturing(Vec4 &prim_color, float s, float t, int t GetTexelCoordinatesQuad(texlevel + 1, s, t, u + 4, v + 4, frac_u[1], frac_v[1]); } - texcolor0 = Sampler::SampleLinear(texlevel, u, v, frac_u[0], frac_v[0], tptr0, bufw0); + texcolor0 = Sampler::SampleLinear(sampler, u, v, frac_u[0], frac_v[0], tptr0, bufw0, texlevel); if (frac_texlevel) { - texcolor1 = Sampler::SampleLinear(texlevel + 1, u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1); + texcolor1 = Sampler::SampleLinear(sampler, u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1, texlevel + 1); } } @@ -1106,7 +1106,7 @@ static inline void CalculateSamplingParams(const float ds, const float dt, const } } -static inline void ApplyTexturing(Vec4 *prim_color, const Vec4 &s, const Vec4 &t, int maxTexLevel, u8 *texptr[], int texbufw[]) { +static inline void ApplyTexturing(Sampler::NearestFunc sampler, Vec4 *prim_color, const Vec4 &s, const Vec4 &t, int maxTexLevel, u8 *texptr[], int texbufw[]) { float ds = s[1] - s[0]; float dt = t[2] - t[0]; @@ -1116,7 +1116,7 @@ static inline void ApplyTexturing(Vec4 *prim_color, const Vec4 &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); } } @@ -1245,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::NearestFunc sampler = Sampler::GetNearestFunc(); + 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), @@ -1302,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) { @@ -1412,6 +1414,8 @@ void DrawPoint(const VertexData &v0) bool clearMode = gstate.isModeClear(); + Sampler::NearestFunc sampler = Sampler::GetNearestFunc(); + if (gstate.isTextureMapEnabled() && !clearMode) { int texbufw[8] = {0}; @@ -1449,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) @@ -1515,6 +1519,8 @@ void DrawLine(const VertexData &v0, const VertexData &v1) } } + Sampler::NearestFunc sampler = Sampler::GetNearestFunc(); + 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; @@ -1579,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) @@ -1632,10 +1638,12 @@ bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) int texbufw = GetTextureBufw(level, texaddr, texfmt); u8 *texptr = Memory::GetPointer(texaddr); + Sampler::NearestFunc sampler = Sampler::GetNearestFunc(); + u32 *row = (u32 *)buffer.GetData(); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { - row[x] = Sampler::SampleNearest(level, x, y, texptr, texbufw).ToRGBA(); + row[x] = sampler(x, y, texptr, texbufw, level); } row += w; } diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index 7f1ffc001d..2907802dcc 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -15,6 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include +#include #include "Common/ColorConv.h" #include "Core/Reporting.h" #include "GPU/Common/TextureDecoder.h" @@ -31,6 +33,95 @@ extern u32 clut[4096]; namespace Sampler { +static u32 SampleNearest(int u, int 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; +} + +NearestFunc GetNearestFunc() { + SamplerID id; + jitCache->ComputeSamplerID(&id); + NearestFunc jitted = jitCache->GetSampler(id); + if (jitted) { + return jitted; + } + + return &SampleNearest; +} + +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(); +} + +void SamplerJitCache::ComputeSamplerID(SamplerID *id_out) { + SamplerID id; + + id.texfmt = gstate.getTextureFormat(); + id.clutfmt = gstate.getClutPaletteFormat(); + id.swizzle = gstate.isTextureSwizzled(); + // Only CLUT4 can use separate CLUTs per mimap. + id.useSharedClut = gstate.isClutSharedForMipmaps() || gstate.getTextureFormat() != GE_TFMT_CLUT4; + id.hasClutMask = gstate.getClutIndexMask() != 0xFF; + id.hasClutShift = gstate.getClutIndexShift() != 0; + id.hasClutOffset = gstate.getClutIndexStartPos() != 0; + + *id_out = id; +} + +NearestFunc SamplerJitCache::GetSampler(const SamplerID &id) { + std::lock_guard 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 + NearestFunc func = Compile(id); + cache_[id] = func; + return func; +#else + return nullptr; +#endif +} + template static inline int GetPixelDataOffset(unsigned int row_pitch_bytes, unsigned int u, unsigned int v) { @@ -85,7 +176,7 @@ struct Nearest4 { }; template -inline static Nearest4 SampleNearest(int level, int u[N], int v[N], const u8 *srcptr, int texbufw) +inline static Nearest4 SampleNearest(int u[N], int v[N], const u8 *srcptr, int texbufw, int level) { Nearest4 res; if (!srcptr) { @@ -193,14 +284,18 @@ inline static Nearest4 SampleNearest(int level, int u[N], int v[N], const u8 *sr } } -Vec4 SampleNearest(int level, int u, int v, const u8 *tptr, int bufw) { - return Vec4::FromRGBA(SampleNearest<1>(level, &u, &v, tptr, bufw)); +static u32 SampleNearest(int u, int v, const u8 *tptr, int bufw, int level) { + return SampleNearest<1>(&u, &v, tptr, bufw, level); } -Vec4 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); +Vec4 SampleLinear(NearestFunc sampler, int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int texlevel) { + Nearest4 c; + c.v[0] = sampler(u[0], v[0], tptr, bufw, texlevel); + c.v[1] = sampler(u[1], v[1], tptr, bufw, texlevel); + c.v[2] = sampler(u[2], v[2], tptr, bufw, texlevel); + c.v[3] = sampler(u[3], v[3], tptr, bufw, texlevel); +#if defined(_M_SSE) const __m128i z = _mm_setzero_si128(); __m128i cvec = _mm_load_si128((const __m128i *)c.v); @@ -217,11 +312,10 @@ Vec4 SampleLinear(int texlevel, int u[4], int v[4], int frac_u, int frac_v, __m128i res = _mm_add_epi16(tmp, _mm_shuffle_epi32(tmp, _MM_SHUFFLE(3, 2, 3, 2))); return Vec4(_mm_unpacklo_epi16(res, z)); #else - Nearest4 nearest = SampleNearest<4>(texlevel, u, v, tptr, bufw); - Vec4 texcolor_tl = Vec4::FromRGBA(nearest.v[0]); - Vec4 texcolor_tr = Vec4::FromRGBA(nearest.v[1]); - Vec4 texcolor_bl = Vec4::FromRGBA(nearest.v[2]); - Vec4 texcolor_br = Vec4::FromRGBA(nearest.v[3]); + Vec4 texcolor_tl = Vec4::FromRGBA(c.v[0]); + Vec4 texcolor_tr = Vec4::FromRGBA(c.v[1]); + Vec4 texcolor_bl = Vec4::FromRGBA(c.v[2]); + Vec4 texcolor_br = Vec4::FromRGBA(c.v[3]); // 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255. Vec4 t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u; Vec4 b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u; diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index 768f27a0ae..9549b08719 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -17,11 +17,94 @@ #pragma once +#include "ppsspp_config.h" + +#include +#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 { + int8_t texfmt : 4; + int8_t clutfmt : 2; + int8_t : 2; + bool swizzle : 1; + bool useSharedClut : 1; + bool hasClutMask : 1; + bool hasClutShift : 1; + bool hasClutOffset : 1; + }; + }; + + bool operator == (const SamplerID &other) const { + return fullKey == other.fullKey; + } +}; + +namespace std { + +template <> +struct hash { + std::size_t operator()(const SamplerID &k) const { + return hash()(k.fullKey); + } +}; + +}; + namespace Sampler { -Math3D::Vec4 SampleNearest(int level, int u, int v, const u8 *tptr, int bufwbytes); -Math3D::Vec4 SampleLinear(int level, int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufwbytes); +typedef u32 (*NearestFunc)(int u, int v, const u8 *tptr, int bufw, int level); +NearestFunc GetNearestFunc(); + +void Init(); +void Shutdown(); + +Math3D::Vec4 SampleLinear(NearestFunc sampler, int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int level); + +#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); + + // Returns a pointer to the code to run. + NearestFunc GetSampler(const SamplerID &id); + void Clear(); + +private: + NearestFunc Compile(const SamplerID &id); + +#if PPSSPP_ARCH(ARM64) + Arm64Gen::ARM64FloatEmitter fp; +#endif + + std::unordered_map cache_; +}; }; diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp new file mode 100644 index 0000000000..b736c16ec4 --- /dev/null +++ b/GPU/Software/SamplerX86.cpp @@ -0,0 +1,96 @@ +// 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 +#include "Common/x64Emitter.h" +#include "GPU/Software/Sampler.h" +#include "GPU/ge_constants.h" + +using namespace Gen; + +namespace Sampler { + +#ifdef _WIN32 +static const X64Reg resultReg = RAX; +static const X64Reg tempReg1 = R10; +static const X64Reg tempReg2 = R11; +static const X64Reg uReg = RCX; +static const X64Reg vReg = RDX; +static const X64Reg srcReg = R8; +static const X64Reg bufwReg = R9; +// TODO: levelReg on stack +#else +static const X64Reg resultReg = RAX; +static const X64Reg tempReg1 = R9; +static const X64Reg tempReg2 = R10; +static const X64Reg uReg = RDI; +static const X64Reg vReg = RSI +static const X64Reg srcReg = RDX; +static const X64Reg bufwReg = RCX; +static const X64Reg levelReg = R8; +#endif + +NearestFunc SamplerJitCache::Compile(const SamplerID &id) { + BeginWrite(); + const u8 *start = this->AlignCode16(); + + SUB(PTRBITS, R(ESP), Imm8(64)); + MOVUPS(MDisp(ESP, 0), XMM4); + MOVUPS(MDisp(ESP, 16), XMM5); + MOVUPS(MDisp(ESP, 32), XMM6); + MOVUPS(MDisp(ESP, 48), XMM7); + + // Early exit on !srcPtr. + CMP(PTRBITS, R(srcReg), Imm32(0)); + FixupBranch nonZeroSrc = J_CC(CC_NZ); + XOR(32, R(RAX), R(RAX)); + FixupBranch zeroSrc = J(true); + SetJumpTarget(nonZeroSrc); + + GETextureFormat fmt = (GETextureFormat)id.texfmt; + bool success = true; + switch (fmt) { + default: + success = false; + } + + if (!success) { + EndWrite(); + SetCodePtr(const_cast(start)); + return nullptr; + } + + SetJumpTarget(zeroSrc); + + MOVUPS(XMM4, MDisp(ESP, 0)); + MOVUPS(XMM5, MDisp(ESP, 16)); + MOVUPS(XMM6, MDisp(ESP, 32)); + MOVUPS(XMM7, MDisp(ESP, 48)); + ADD(PTRBITS, R(ESP), Imm8(64)); + + RET(); + + EndWrite(); + return (NearestFunc)start; +} + +}; + +#endif diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 081d20f9fb..2c05938d5f 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -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) { diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 0200d09d61..7e797889b9 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -344,6 +344,7 @@ + @@ -397,6 +398,7 @@ + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index a67b94d110..7b3dd28a06 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -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) From 94920ec15697ac28e7f5f8fc983c5fec6d4955ea Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 17:35:16 -0700 Subject: [PATCH 03/17] SoftGPU: Implement basic jit structure. --- GPU/Software/Sampler.h | 7 ++++ GPU/Software/SamplerX86.cpp | 78 +++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index 9549b08719..dc1f4f6a85 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -100,6 +100,13 @@ public: private: NearestFunc Compile(const SamplerID &id); + bool Jit_GetTexData(const SamplerID &id, int bitsPerTexel); + bool Jit_Decode5650(); + bool Jit_Decode5551(); + bool Jit_Decode4444(); + bool Jit_TransformClutIndex(const SamplerID &id); + bool Jit_ReadClutColor(const SamplerID &id); + #if PPSSPP_ARCH(ARM64) Arm64Gen::ARM64FloatEmitter fp; #endif diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index b736c16ec4..b325ad5ef2 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -67,6 +67,60 @@ NearestFunc SamplerJitCache::Compile(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); + if (success) + success = Jit_ReadClutColor(id); + break; + + case GE_TFMT_CLUT16: + success = Jit_GetTexData(id, 16); + if (success) + success = Jit_TransformClutIndex(id); + if (success) + success = Jit_ReadClutColor(id); + break; + + case GE_TFMT_CLUT8: + success = Jit_GetTexData(id, 16); + if (success) + success = Jit_TransformClutIndex(id); + if (success) + success = Jit_ReadClutColor(id); + break; + + case GE_TFMT_CLUT4: + success = Jit_GetTexData(id, 16); + if (success) + success = Jit_TransformClutIndex(id); + if (success) + success = Jit_ReadClutColor(id); + break; + default: success = false; } @@ -91,6 +145,30 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) { return (NearestFunc)start; } +bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) { + return false; +} + +bool SamplerJitCache::Jit_Decode5650() { + return false; +} + +bool SamplerJitCache::Jit_Decode5551() { + return false; +} + +bool SamplerJitCache::Jit_Decode4444() { + return false; +} + +bool SamplerJitCache::Jit_TransformClutIndex(const SamplerID &id) { + return false; +} + +bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) { + return false; +} + }; #endif From fd4c64d43dbbfe29aace92bb9abb787ba6b6d5c9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 17:38:38 -0700 Subject: [PATCH 04/17] SoftGPU: Refactor out texture format logic. This could actually be shared, nothing x86 specific... --- GPU/Software/Sampler.h | 1 + GPU/Software/SamplerX86.cpp | 40 ++++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index dc1f4f6a85..911625bf37 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -100,6 +100,7 @@ public: private: NearestFunc Compile(const SamplerID &id); + bool Jit_ReadTextureFormat(const SamplerID &id); bool Jit_GetTexData(const SamplerID &id, int bitsPerTexel); bool Jit_Decode5650(); bool Jit_Decode5551(); diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index b325ad5ef2..e91debdd93 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -64,6 +64,27 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) { FixupBranch zeroSrc = J(true); SetJumpTarget(nonZeroSrc); + if (!Jit_ReadTextureFormat(id)) { + EndWrite(); + SetCodePtr(const_cast(start)); + return nullptr; + } + + SetJumpTarget(zeroSrc); + + MOVUPS(XMM4, MDisp(ESP, 0)); + MOVUPS(XMM5, MDisp(ESP, 16)); + MOVUPS(XMM6, MDisp(ESP, 32)); + MOVUPS(XMM7, MDisp(ESP, 48)); + ADD(PTRBITS, R(ESP), Imm8(64)); + + RET(); + + EndWrite(); + return (NearestFunc)start; +} + +bool SamplerJitCache::Jit_ReadTextureFormat(const SamplerID &id) { GETextureFormat fmt = (GETextureFormat)id.texfmt; bool success = true; switch (fmt) { @@ -125,24 +146,7 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) { success = false; } - if (!success) { - EndWrite(); - SetCodePtr(const_cast(start)); - return nullptr; - } - - SetJumpTarget(zeroSrc); - - MOVUPS(XMM4, MDisp(ESP, 0)); - MOVUPS(XMM5, MDisp(ESP, 16)); - MOVUPS(XMM6, MDisp(ESP, 32)); - MOVUPS(XMM7, MDisp(ESP, 48)); - ADD(PTRBITS, R(ESP), Imm8(64)); - - RET(); - - EndWrite(); - return (NearestFunc)start; + return success; } bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) { From 5491e1dbbb2e23d2e7e77429cd395eb485822a18 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 17:39:35 -0700 Subject: [PATCH 05/17] SoftGPU: Jit texel fetch for non-clut/non-swizzle. --- GPU/Software/SamplerX86.cpp | 54 ++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index e91debdd93..0556769b8b 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -150,7 +150,59 @@ bool SamplerJitCache::Jit_ReadTextureFormat(const SamplerID &id) { } bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) { - return false; + if (id.swizzle) { + return false; + } + + // 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: + MOV(bitsPerTexel, R(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), Imm32(0x0000000F)); + break; + } + + default: + return false; + } + + return true; } bool SamplerJitCache::Jit_Decode5650() { From c9a4679096e1681bfeb8685449b4cd93049fa9d7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 18:03:35 -0700 Subject: [PATCH 06/17] SoftGPU: Stop calling bufw pixels bytes. This was never even bits, it was just 8 * pixels before. --- GPU/Software/Sampler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index 2907802dcc..1b58504286 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -123,10 +123,10 @@ NearestFunc SamplerJitCache::GetSampler(const SamplerID &id) { } template -static inline int GetPixelDataOffset(unsigned int row_pitch_bytes, unsigned int u, unsigned int v) +static inline int GetPixelDataOffset(unsigned int row_pitch_pixels, unsigned int u, unsigned int v) { if (!gstate.isTextureSwizzled()) - return (v * (row_pitch_bytes * texel_size_bits >> 3)) + (u * texel_size_bits >> 3); + 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; @@ -136,7 +136,7 @@ static inline int GetPixelDataOffset(unsigned int row_pitch_bytes, unsigned int 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) + + (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); From a8a6b2cd8a6bdfcb0c76397f1e32f038006b1b4d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 18:04:22 -0700 Subject: [PATCH 07/17] SoftGPU: Jit swizzled texel lookup. --- GPU/Software/Sampler.h | 1 + GPU/Software/SamplerX86.cpp | 80 ++++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index 911625bf37..babdbfac7b 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -102,6 +102,7 @@ private: bool Jit_ReadTextureFormat(const SamplerID &id); bool Jit_GetTexData(const SamplerID &id, int bitsPerTexel); + bool Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTexel); bool Jit_Decode5650(); bool Jit_Decode5551(); bool Jit_Decode4444(); diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index 0556769b8b..1d3fe66faf 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -151,7 +151,7 @@ bool SamplerJitCache::Jit_ReadTextureFormat(const SamplerID &id) { bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) { if (id.swizzle) { - return false; + return Jit_GetTexDataSwizzled(id, bitsPerTexel); } // srcReg might be EDX, so let's copy that before we multiply. @@ -205,6 +205,84 @@ bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) { return true; } +bool SamplerJitCache::Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTexel) { + LEA(32, tempReg1, MScaled(vReg, SCALE_4, 0)); + AND(32, R(tempReg1), Imm32(31)); + if (bitsPerTexel != 4) { + AND(32, R(vReg), Imm32(~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; + case 4: + SHR(32, R(vReg), Imm8(3)); + SHR(32, R(tempReg2), Imm8(3)); + SHR(32, R(resultReg), Imm8(5)); + break; + default: + return false; + } + AND(32, R(tempReg2), Imm32(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), Imm32(1)); + // Multiply by two by just adding twice. + ADD(32, R(EAX), R(uReg)); + ADD(32, R(EAX), R(uReg)); + MOV(bitsPerTexel, R(resultReg), MRegSum(tempReg1, EAX)); + break; + case 8: + AND(32, R(uReg), Imm32(3)); + ADD(32, R(EAX), R(uReg)); + MOV(bitsPerTexel, R(resultReg), MRegSum(tempReg1, EAX)); + break; + case 4: { + AND(32, R(uReg), Imm32(7)); + SHR(32, R(uReg), Imm8(1)); + // Note: LEA/MOV do not affect flags (unlike ADD.) uReg may be RCX. + LEA(64, tempReg1, MRegSum(tempReg1, uReg)); + MOV(8, R(resultReg), MRegSum(tempReg1, EAX)); + FixupBranch skipNonZero = J_CC(CC_NC); + SHR(32, R(resultReg), Imm32(4)); + SetJumpTarget(skipNonZero); + // Zero out the rest. + AND(32, R(resultReg), Imm32(0x0000000F)); + break; + } + default: + return false; + } + + return true; +} + bool SamplerJitCache::Jit_Decode5650() { return false; } From cf32949183564ca2f64c859fc92c67552a234cbd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 19:22:39 -0700 Subject: [PATCH 08/17] SoftGPU: Jit CLUT lookup. --- GPU/Software/Sampler.h | 2 +- GPU/Software/SamplerX86.cpp | 98 ++++++++++++++++++++++++++++++++----- 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index babdbfac7b..fb7e34d6fb 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -106,7 +106,7 @@ private: bool Jit_Decode5650(); bool Jit_Decode5551(); bool Jit_Decode4444(); - bool Jit_TransformClutIndex(const SamplerID &id); + bool Jit_TransformClutIndex(const SamplerID &id, int bitsPerIndex); bool Jit_ReadClutColor(const SamplerID &id); #if PPSSPP_ARCH(ARM64) diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index 1d3fe66faf..f03476432d 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -20,11 +20,14 @@ #include #include "Common/x64Emitter.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 @@ -113,7 +116,7 @@ bool SamplerJitCache::Jit_ReadTextureFormat(const SamplerID &id) { case GE_TFMT_CLUT32: success = Jit_GetTexData(id, 32); if (success) - success = Jit_TransformClutIndex(id); + success = Jit_TransformClutIndex(id, 32); if (success) success = Jit_ReadClutColor(id); break; @@ -121,23 +124,23 @@ bool SamplerJitCache::Jit_ReadTextureFormat(const SamplerID &id) { case GE_TFMT_CLUT16: success = Jit_GetTexData(id, 16); if (success) - success = Jit_TransformClutIndex(id); + success = Jit_TransformClutIndex(id, 16); if (success) success = Jit_ReadClutColor(id); break; case GE_TFMT_CLUT8: - success = Jit_GetTexData(id, 16); + success = Jit_GetTexData(id, 8); if (success) - success = Jit_TransformClutIndex(id); + success = Jit_TransformClutIndex(id, 8); if (success) success = Jit_ReadClutColor(id); break; case GE_TFMT_CLUT4: - success = Jit_GetTexData(id, 16); + success = Jit_GetTexData(id, 4); if (success) - success = Jit_TransformClutIndex(id); + success = Jit_TransformClutIndex(id, 4); if (success) success = Jit_ReadClutColor(id); break; @@ -184,7 +187,7 @@ bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) { case 32: case 16: case 8: - MOV(bitsPerTexel, R(resultReg), MComplex(tempReg1, RAX, bitsPerTexel / 8, 0)); + MOVZX(32, bitsPerTexel, resultReg, MComplex(tempReg1, RAX, bitsPerTexel / 8, 0)); break; case 4: { @@ -256,12 +259,12 @@ bool SamplerJitCache::Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTex // Multiply by two by just adding twice. ADD(32, R(EAX), R(uReg)); ADD(32, R(EAX), R(uReg)); - MOV(bitsPerTexel, R(resultReg), MRegSum(tempReg1, EAX)); + MOVZX(32, bitsPerTexel, resultReg, MRegSum(tempReg1, EAX)); break; case 8: AND(32, R(uReg), Imm32(3)); ADD(32, R(EAX), R(uReg)); - MOV(bitsPerTexel, R(resultReg), MRegSum(tempReg1, EAX)); + MOVZX(32, bitsPerTexel, resultReg, MRegSum(tempReg1, EAX)); break; case 4: { AND(32, R(uReg), Imm32(7)); @@ -270,7 +273,7 @@ bool SamplerJitCache::Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTex LEA(64, tempReg1, MRegSum(tempReg1, uReg)); MOV(8, R(resultReg), MRegSum(tempReg1, EAX)); FixupBranch skipNonZero = J_CC(CC_NC); - SHR(32, R(resultReg), Imm32(4)); + SHR(8, R(resultReg), Imm8(4)); SetJumpTarget(skipNonZero); // Zero out the rest. AND(32, R(resultReg), Imm32(0x0000000F)); @@ -295,12 +298,81 @@ bool SamplerJitCache::Jit_Decode4444() { return false; } -bool SamplerJitCache::Jit_TransformClutIndex(const SamplerID &id) { - return false; +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), Imm32(0x0000001F)); + 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) { - return false; + if (!id.useSharedClut) { + // TODO: Load level, SHL 4, and add to resultReg. + return false; + } + + MOV(PTRBITS, R(tempReg1), ImmPtr(clut)); + + switch (gstate.getClutPaletteFormat()) { + 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; + } } }; From 744736b926cfcae11504abde840aa28b87334e64 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 19:50:35 -0700 Subject: [PATCH 09/17] SoftGPU: Jit 16 bit texel decode. --- GPU/Common/VertexDecoderX86.cpp | 1 - GPU/Software/SamplerX86.cpp | 83 +++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index e2b334b662..e0d65a3296 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -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)); diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index f03476432d..7a150a39a7 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -50,6 +50,11 @@ static const X64Reg bufwReg = RCX; static const X64Reg levelReg = R8; #endif +static const X64Reg fpScratchReg1 = XMM1; +static const X64Reg fpScratchReg2 = XMM2; +static const X64Reg fpScratchReg3 = XMM3; +static const X64Reg fpScratchReg4 = XMM4; + NearestFunc SamplerJitCache::Compile(const SamplerID &id) { BeginWrite(); const u8 *start = this->AlignCode16(); @@ -145,6 +150,7 @@ bool SamplerJitCache::Jit_ReadTextureFormat(const SamplerID &id) { success = Jit_ReadClutColor(id); break; + // TODO: DXT? default: success = false; } @@ -287,15 +293,86 @@ bool SamplerJitCache::Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTex } bool SamplerJitCache::Jit_Decode5650() { - return false; + 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() { - return false; + 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() { - return false; + 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) { From 2233ab073d5515a4636e854ccd9e432f8156d091 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 10 May 2017 20:36:09 -0700 Subject: [PATCH 10/17] SoftGPU: Expose jit cache for profiling. --- GPU/Software/Sampler.cpp | 76 ++++++++++++++++++++++++++++++++++++++++ GPU/Software/Sampler.h | 5 +++ GPU/Software/SoftGpu.cpp | 9 +++++ GPU/Software/SoftGpu.h | 2 ++ 4 files changed, 92 insertions(+) diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index 1b58504286..ff413f66cd 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -47,6 +47,15 @@ void Shutdown() { 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); @@ -99,6 +108,73 @@ void SamplerJitCache::ComputeSamplerID(SamplerID *id_out) { *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"; + } + return name; +} + +std::string SamplerJitCache::DescribeCodePtr(const u8 *ptr) { + int dist = 0x7FFFFFFF; + SamplerID found{}; + for (const auto &it : cache_) { + ptrdiff_t it_dist = ptr - (const u8 *)it.second; + if (it_dist >= 0 && it_dist < dist) { + found = it.first; + } + } + + return DescribeSamplerID(found); +} + NearestFunc SamplerJitCache::GetSampler(const SamplerID &id) { std::lock_guard guard(jitCacheLock); diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index fb7e34d6fb..f9c335e87c 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -75,6 +75,8 @@ NearestFunc GetNearestFunc(); void Init(); void Shutdown(); +bool DescribeCodePtr(const u8 *ptr, std::string &name); + Math3D::Vec4 SampleLinear(NearestFunc sampler, int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int level); #if PPSSPP_ARCH(ARM) @@ -97,6 +99,9 @@ public: NearestFunc GetSampler(const SamplerID &id); void Clear(); + std::string DescribeCodePtr(const u8 *ptr); + std::string DescribeSamplerID(const SamplerID &id); + private: NearestFunc Compile(const SamplerID &id); diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 2c05938d5f..94ec5b59a8 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -995,3 +995,12 @@ bool SoftGPU::GetCurrentSimpleVertices(int count, std::vector &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; +} diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 0e2a6922b7..3b0dd6b7b5 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -91,6 +91,8 @@ public: bool GetCurrentClut(GPUDebugBuffer &buffer) override; bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; + bool DescribeCodePtr(const u8 *ptr, std::string &name) override; + protected: void FastRunLoop(DisplayList &list) override; void ProcessEvent(GPUEvent ev) override; From 9a82ae34250ca9620b01ba0fe8762b0da29d4b21 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 11 May 2017 06:14:38 -0700 Subject: [PATCH 11/17] SoftGPU: Reduce overhead in sampler jit. --- GPU/Software/Sampler.cpp | 7 ++++++- GPU/Software/Sampler.h | 1 + GPU/Software/SamplerX86.cpp | 30 +++++++++++------------------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index ff413f66cd..44d4812d4c 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -94,7 +94,7 @@ void SamplerJitCache::Clear() { } void SamplerJitCache::ComputeSamplerID(SamplerID *id_out) { - SamplerID id; + SamplerID id{}; id.texfmt = gstate.getTextureFormat(); id.clutfmt = gstate.getClutPaletteFormat(); @@ -104,6 +104,11 @@ void SamplerJitCache::ComputeSamplerID(SamplerID *id_out) { id.hasClutMask = gstate.getClutIndexMask() != 0xFF; id.hasClutShift = gstate.getClutIndexShift() != 0; id.hasClutOffset = gstate.getClutIndexStartPos() != 0; + for (int i = 0; i <= gstate.getTextureMaxLevel(); ++i) { + if (gstate.getTextureAddress(i) == 0) { + id.hasInvalidPtr = true; + } + } *id_out = id; } diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index f9c335e87c..922435e5ab 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -48,6 +48,7 @@ struct SamplerID { bool hasClutMask : 1; bool hasClutShift : 1; bool hasClutOffset : 1; + bool hasInvalidPtr : 1; }; }; diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index 7a150a39a7..1f07c6cf85 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -53,24 +53,20 @@ static const X64Reg levelReg = R8; static const X64Reg fpScratchReg1 = XMM1; static const X64Reg fpScratchReg2 = XMM2; static const X64Reg fpScratchReg3 = XMM3; -static const X64Reg fpScratchReg4 = XMM4; NearestFunc SamplerJitCache::Compile(const SamplerID &id) { BeginWrite(); const u8 *start = this->AlignCode16(); - SUB(PTRBITS, R(ESP), Imm8(64)); - MOVUPS(MDisp(ESP, 0), XMM4); - MOVUPS(MDisp(ESP, 16), XMM5); - MOVUPS(MDisp(ESP, 32), XMM6); - MOVUPS(MDisp(ESP, 48), XMM7); - // Early exit on !srcPtr. - CMP(PTRBITS, R(srcReg), Imm32(0)); - FixupBranch nonZeroSrc = J_CC(CC_NZ); - XOR(32, R(RAX), R(RAX)); - FixupBranch zeroSrc = J(true); - SetJumpTarget(nonZeroSrc); + FixupBranch zeroSrc; + if (id.hasInvalidPtr) { + CMP(PTRBITS, R(srcReg), Imm32(0)); + FixupBranch nonZeroSrc = J_CC(CC_NZ); + XOR(32, R(RAX), R(RAX)); + zeroSrc = J(true); + SetJumpTarget(nonZeroSrc); + } if (!Jit_ReadTextureFormat(id)) { EndWrite(); @@ -78,13 +74,9 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) { return nullptr; } - SetJumpTarget(zeroSrc); - - MOVUPS(XMM4, MDisp(ESP, 0)); - MOVUPS(XMM5, MDisp(ESP, 16)); - MOVUPS(XMM6, MDisp(ESP, 32)); - MOVUPS(XMM7, MDisp(ESP, 48)); - ADD(PTRBITS, R(ESP), Imm8(64)); + if (id.hasInvalidPtr) { + SetJumpTarget(zeroSrc); + } RET(); From ffcfa0a82434f3e6a302eb235e40bb21c910b454 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 11 May 2017 17:57:41 -0700 Subject: [PATCH 12/17] SoftGPU: Specialize CLUT4 jit. This version is a bit faster and cleaner. --- GPU/Software/Sampler.h | 1 + GPU/Software/SamplerX86.cpp | 74 +++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index 922435e5ab..84fde02a2c 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -109,6 +109,7 @@ private: 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(); diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index 1f07c6cf85..34d456ff91 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -61,7 +61,7 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) { // Early exit on !srcPtr. FixupBranch zeroSrc; if (id.hasInvalidPtr) { - CMP(PTRBITS, R(srcReg), Imm32(0)); + CMP(PTRBITS, R(srcReg), Imm8(0)); FixupBranch nonZeroSrc = J_CC(CC_NZ); XOR(32, R(RAX), R(RAX)); zeroSrc = J(true); @@ -195,7 +195,7 @@ bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) { MOV(8, R(RCX), R(tempReg2)); SHR(8, R(resultReg), R(RCX)); // Zero out any bits not shifted off. - AND(32, R(resultReg), Imm32(0x0000000F)); + AND(32, R(resultReg), Imm8(0x0F)); break; } @@ -206,13 +206,49 @@ bool SamplerJitCache::Jit_GetTexData(const SamplerID &id, int bitsPerTexel) { 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) { - LEA(32, tempReg1, MScaled(vReg, SCALE_4, 0)); - AND(32, R(tempReg1), Imm32(31)); - if (bitsPerTexel != 4) { - AND(32, R(vReg), Imm32(~7)); + 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) { @@ -229,15 +265,10 @@ bool SamplerJitCache::Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTex SHR(32, R(tempReg2), Imm8(2)); SHR(32, R(resultReg), Imm8(4)); break; - case 4: - SHR(32, R(vReg), Imm8(3)); - SHR(32, R(tempReg2), Imm8(3)); - SHR(32, R(resultReg), Imm8(5)); - break; default: return false; } - AND(32, R(tempReg2), Imm32(3)); + AND(32, R(tempReg2), Imm8(3)); SHL(32, R(resultReg), Imm8(5)); ADD(32, R(tempReg1), R(tempReg2)); ADD(32, R(tempReg1), R(resultReg)); @@ -253,30 +284,17 @@ bool SamplerJitCache::Jit_GetTexDataSwizzled(const SamplerID &id, int bitsPerTex MOV(bitsPerTexel, R(resultReg), MRegSum(tempReg1, EAX)); break; case 16: - AND(32, R(uReg), Imm32(1)); + 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), Imm32(3)); + AND(32, R(uReg), Imm8(3)); ADD(32, R(EAX), R(uReg)); MOVZX(32, bitsPerTexel, resultReg, MRegSum(tempReg1, EAX)); break; - case 4: { - AND(32, R(uReg), Imm32(7)); - SHR(32, R(uReg), Imm8(1)); - // Note: LEA/MOV do not affect flags (unlike ADD.) uReg may be RCX. - LEA(64, tempReg1, MRegSum(tempReg1, uReg)); - MOV(8, R(resultReg), MRegSum(tempReg1, EAX)); - FixupBranch skipNonZero = J_CC(CC_NC); - SHR(8, R(resultReg), Imm8(4)); - SetJumpTarget(skipNonZero); - // Zero out the rest. - AND(32, R(resultReg), Imm32(0x0000000F)); - break; - } default: return false; } @@ -384,7 +402,7 @@ bool SamplerJitCache::Jit_TransformClutIndex(const SamplerID &id, int bitsPerInd if (id.hasClutShift) { MOV(32, R(RCX), R(tempReg1)); SHR(32, R(RCX), Imm8(2)); - AND(32, R(RCX), Imm32(0x0000001F)); + AND(32, R(RCX), Imm8(0x1F)); SHR(32, R(resultReg), R(RCX)); } From c05ecfc6f1f479895aa716d7d278dc5a78e9d60c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 11 May 2017 18:36:03 -0700 Subject: [PATCH 13/17] SoftGPU: Implement separate CLUT in samplerjit. --- GPU/Software/SamplerX86.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index 34d456ff91..0751c1efc3 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -434,8 +434,15 @@ bool SamplerJitCache::Jit_TransformClutIndex(const SamplerID &id, int bitsPerInd bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) { if (!id.useSharedClut) { - // TODO: Load level, SHL 4, and add to resultReg. - return false; +#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 + LEA(64, resultReg, MComplex(resultReg, tempReg2, SCALE_4, 0)); } MOV(PTRBITS, R(tempReg1), ImmPtr(clut)); From fa0ded4a76a6a0a1174e1a688b9ed360a332b483 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 12 May 2017 18:50:24 -0700 Subject: [PATCH 14/17] SoftGPU: Reduce sampler id fuzz. --- GPU/Software/Sampler.cpp | 15 +++++++++------ GPU/Software/Sampler.h | 6 +++--- GPU/Software/SamplerX86.cpp | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index 44d4812d4c..920a3b1e9a 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -97,14 +97,17 @@ void SamplerJitCache::ComputeSamplerID(SamplerID *id_out) { SamplerID id{}; id.texfmt = gstate.getTextureFormat(); - id.clutfmt = gstate.getClutPaletteFormat(); id.swizzle = gstate.isTextureSwizzled(); // Only CLUT4 can use separate CLUTs per mimap. - id.useSharedClut = gstate.isClutSharedForMipmaps() || gstate.getTextureFormat() != GE_TFMT_CLUT4; - id.hasClutMask = gstate.getClutIndexMask() != 0xFF; - id.hasClutShift = gstate.getClutIndexShift() != 0; - id.hasClutOffset = gstate.getClutIndexStartPos() != 0; - for (int i = 0; i <= gstate.getTextureMaxLevel(); ++i) { + 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; + } + int maxLevel = gstate.isMipmapEnabled() ? gstate.getTextureMaxLevel() : 0; + for (int i = 0; i <= maxLevel; ++i) { if (gstate.getTextureAddress(i) == 0) { id.hasInvalidPtr = true; } diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index 84fde02a2c..6e15694d66 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -40,9 +40,9 @@ struct SamplerID { union { u32 fullKey; struct { - int8_t texfmt : 4; - int8_t clutfmt : 2; - int8_t : 2; + uint8_t texfmt : 4; + uint8_t clutfmt : 2; + uint8_t : 2; bool swizzle : 1; bool useSharedClut : 1; bool hasClutMask : 1; diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index 0751c1efc3..f6820a1f5b 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -447,7 +447,7 @@ bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) { MOV(PTRBITS, R(tempReg1), ImmPtr(clut)); - switch (gstate.getClutPaletteFormat()) { + switch ((GEPaletteFormat)id.clutfmt) { case GE_CMODE_16BIT_BGR5650: MOVZX(32, 16, resultReg, MComplex(tempReg1, resultReg, SCALE_2, 0)); return Jit_Decode5650(); From cb3db559bd6c1dc7f1e4fc1d63a77df41061ce36 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 May 2017 16:16:03 -0700 Subject: [PATCH 15/17] SoftGPU: Jit the linear sampling too. For now, just reducing overhead. Could be smarter. --- Common/x64Emitter.cpp | 10 ++ Common/x64Emitter.h | 10 ++ GPU/Software/Rasterizer.cpp | 22 ++-- GPU/Software/Sampler.cpp | 69 ++++++++--- GPU/Software/Sampler.h | 24 +++- GPU/Software/SamplerX86.cpp | 226 ++++++++++++++++++++++++++++++++---- 6 files changed, 309 insertions(+), 52 deletions(-) diff --git a/Common/x64Emitter.cpp b/Common/x64Emitter.cpp index 3d9b9c4e81..aff3983bc6 100644 --- a/Common/x64Emitter.cpp +++ b/Common/x64Emitter.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) diff --git a/Common/x64Emitter.h b/Common/x64Emitter.h index bcfc592bdd..5f3e27ebc0 100644 --- a/Common/x64Emitter.h +++ b/Common/x64Emitter.h @@ -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); diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 1fd9268271..cc7eaa5df0 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -1003,7 +1003,7 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 &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 &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]; @@ -1021,9 +1021,9 @@ static inline void ApplyTexturing(Sampler::NearestFunc sampler, Vec4 &prim_ GetTexelCoordinates(texlevel + 1, s, t, u[1], v[1]); } - texcolor0 = Vec4::FromRGBA(sampler(u[0], v[0], tptr0, bufw0, texlevel)); + texcolor0 = Vec4::FromRGBA(sampler.nearest(u[0], v[0], tptr0, bufw0, texlevel)); if (frac_texlevel) { - texcolor1 = Vec4::FromRGBA(sampler(u[1], v[1], tptr1, bufw1, texlevel + 1)); + texcolor1 = Vec4::FromRGBA(sampler.nearest(u[1], v[1], tptr1, bufw1, texlevel + 1)); } } else { GetTexelCoordinatesQuad(texlevel, s, t, u, v, frac_u[0], frac_v[0]); @@ -1031,9 +1031,9 @@ static inline void ApplyTexturing(Sampler::NearestFunc sampler, Vec4 &prim_ GetTexelCoordinatesQuad(texlevel + 1, s, t, u + 4, v + 4, frac_u[1], frac_v[1]); } - texcolor0 = Sampler::SampleLinear(sampler, u, v, frac_u[0], frac_v[0], tptr0, bufw0, texlevel); + texcolor0 = Vec4::FromRGBA(sampler.linear(u, v, frac_u[0], frac_v[0], tptr0, bufw0, texlevel)); if (frac_texlevel) { - texcolor1 = Sampler::SampleLinear(sampler, u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1, texlevel + 1); + texcolor1 = Vec4::FromRGBA(sampler.linear(u + 4, v + 4, frac_u[1], frac_v[1], tptr1, bufw1, texlevel + 1)); } } @@ -1106,7 +1106,7 @@ static inline void CalculateSamplingParams(const float ds, const float dt, const } } -static inline void ApplyTexturing(Sampler::NearestFunc sampler, Vec4 *prim_color, const Vec4 &s, const Vec4 &t, int maxTexLevel, u8 *texptr[], int texbufw[]) { +static inline void ApplyTexturing(Sampler::Funcs sampler, Vec4 *prim_color, const Vec4 &s, const Vec4 &t, int maxTexLevel, u8 *texptr[], int texbufw[]) { float ds = s[1] - s[0]; float dt = t[2] - t[0]; @@ -1245,7 +1245,7 @@ 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::NearestFunc sampler = Sampler::GetNearestFunc(); + 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), @@ -1414,7 +1414,7 @@ void DrawPoint(const VertexData &v0) bool clearMode = gstate.isModeClear(); - Sampler::NearestFunc sampler = Sampler::GetNearestFunc(); + Sampler::Funcs sampler = Sampler::GetFuncs(); if (gstate.isTextureMapEnabled() && !clearMode) { int texbufw[8] = {0}; @@ -1519,7 +1519,7 @@ void DrawLine(const VertexData &v0, const VertexData &v1) } } - Sampler::NearestFunc sampler = Sampler::GetNearestFunc(); + 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; @@ -1638,12 +1638,12 @@ bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) int texbufw = GetTextureBufw(level, texaddr, texfmt); u8 *texptr = Memory::GetPointer(texaddr); - Sampler::NearestFunc sampler = Sampler::GetNearestFunc(); + 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] = sampler(x, y, texptr, texbufw, level); + row[x] = sampler.nearest(x, y, texptr, texbufw, level); } row += w; } diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index 920a3b1e9a..36afda73a4 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -34,6 +34,7 @@ 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; @@ -58,8 +59,8 @@ bool DescribeCodePtr(const u8 *ptr, std::string &name) { NearestFunc GetNearestFunc() { SamplerID id; - jitCache->ComputeSamplerID(&id); - NearestFunc jitted = jitCache->GetSampler(id); + jitCache->ComputeSamplerID(&id, false); + NearestFunc jitted = jitCache->GetNearest(id); if (jitted) { return jitted; } @@ -67,6 +68,17 @@ NearestFunc GetNearestFunc() { 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) @@ -91,9 +103,10 @@ SamplerJitCache::SamplerJitCache() void SamplerJitCache::Clear() { ClearCodeSpace(0); cache_.clear(); + addresses_.clear(); } -void SamplerJitCache::ComputeSamplerID(SamplerID *id_out) { +void SamplerJitCache::ComputeSamplerID(SamplerID *id_out, bool linear) { SamplerID id{}; id.texfmt = gstate.getTextureFormat(); @@ -106,6 +119,7 @@ void SamplerJitCache::ComputeSamplerID(SamplerID *id_out) { 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) { @@ -167,23 +181,27 @@ std::string SamplerJitCache::DescribeSamplerID(const SamplerID &id) { if (id.hasClutOffset) { name += ":COFF"; } + if (id.linear) { + name += ":LERP"; + } return name; } std::string SamplerJitCache::DescribeCodePtr(const u8 *ptr) { - int dist = 0x7FFFFFFF; + ptrdiff_t dist = 0x7FFFFFFF; SamplerID found{}; - for (const auto &it : cache_) { - ptrdiff_t it_dist = ptr - (const u8 *)it.second; + 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::GetSampler(const SamplerID &id) { +NearestFunc SamplerJitCache::GetNearest(const SamplerID &id) { std::lock_guard guard(jitCacheLock); auto it = cache_.find(id); @@ -198,6 +216,7 @@ NearestFunc SamplerJitCache::GetSampler(const SamplerID &id) { // TODO #ifdef _M_X64 + addresses_[id] = GetCodePointer(); NearestFunc func = Compile(id); cache_[id] = func; return func; @@ -206,6 +225,30 @@ NearestFunc SamplerJitCache::GetSampler(const SamplerID &id) { #endif } +LinearFunc SamplerJitCache::GetLinear(const SamplerID &id) { + std::lock_guard 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 static inline int GetPixelDataOffset(unsigned int row_pitch_pixels, unsigned int u, unsigned int v) { @@ -372,12 +415,8 @@ static u32 SampleNearest(int u, int v, const u8 *tptr, int bufw, int level) { return SampleNearest<1>(&u, &v, tptr, bufw, level); } -Vec4 SampleLinear(NearestFunc sampler, int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int texlevel) { - Nearest4 c; - c.v[0] = sampler(u[0], v[0], tptr, bufw, texlevel); - c.v[1] = sampler(u[1], v[1], tptr, bufw, texlevel); - c.v[2] = sampler(u[2], v[2], tptr, bufw, texlevel); - c.v[3] = sampler(u[3], v[3], tptr, bufw, texlevel); +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); #if defined(_M_SSE) const __m128i z = _mm_setzero_si128(); @@ -394,7 +433,7 @@ Vec4 SampleLinear(NearestFunc sampler, int u[4], int v[4], int frac_u, int // 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(_mm_unpacklo_epi16(res, z)); + return Vec4(_mm_unpacklo_epi16(res, z)).ToRGBA(); #else Vec4 texcolor_tl = Vec4::FromRGBA(c.v[0]); Vec4 texcolor_tr = Vec4::FromRGBA(c.v[1]); @@ -403,7 +442,7 @@ Vec4 SampleLinear(NearestFunc sampler, int u[4], int v[4], int frac_u, int // 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255. Vec4 t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u; Vec4 b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u; - return (t * (0x100 - frac_v) + b * frac_v) / (256 * 256); + return ((t * (0x100 - frac_v) + b * frac_v) / (256 * 256)).ToRGBA(); #endif } diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index 6e15694d66..0e7604dc63 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -49,6 +49,7 @@ struct SamplerID { bool hasClutShift : 1; bool hasClutOffset : 1; bool hasInvalidPtr : 1; + bool linear : 1; }; }; @@ -73,13 +74,25 @@ 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); -Math3D::Vec4 SampleLinear(NearestFunc sampler, int u[4], int v[4], int frac_u, int frac_v, const u8 *tptr, int bufw, int level); - #if PPSSPP_ARCH(ARM) class SamplerJitCache : public ArmGen::ARMXCodeBlock { #elif PPSSPP_ARCH(ARM64) @@ -94,10 +107,11 @@ class SamplerJitCache : public FakeGen::FakeXCodeBlock { public: SamplerJitCache(); - void ComputeSamplerID(SamplerID *id_out); + void ComputeSamplerID(SamplerID *id_out, bool linear); // Returns a pointer to the code to run. - NearestFunc GetSampler(const SamplerID &id); + NearestFunc GetNearest(const SamplerID &id); + LinearFunc GetLinear(const SamplerID &id); void Clear(); std::string DescribeCodePtr(const u8 *ptr); @@ -105,6 +119,7 @@ public: 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); @@ -121,6 +136,7 @@ private: #endif std::unordered_map cache_; + std::unordered_map addresses_; }; }; diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index f6820a1f5b..fb93987471 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -20,6 +20,7 @@ #include #include "Common/x64Emitter.h" +#include "Common/CPUDetect.h" #include "GPU/GPUState.h" #include "GPU/Software/Sampler.h" #include "GPU/ge_constants.h" @@ -31,32 +32,40 @@ 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 = RCX; -static const X64Reg vReg = RDX; -static const X64Reg srcReg = R8; -static const X64Reg bufwReg = R9; -// TODO: levelReg on stack -#else -static const X64Reg resultReg = RAX; -static const X64Reg tempReg1 = R9; -static const X64Reg tempReg2 = R10; -static const X64Reg uReg = RDI; -static const X64Reg vReg = RSI -static const X64Reg srcReg = RDX; -static const X64Reg bufwReg = RCX; -static const X64Reg levelReg = R8; -#endif + +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 = this->AlignCode16(); + const u8 *start = AlignCode16(); // Early exit on !srcPtr. FixupBranch zeroSrc; @@ -84,6 +93,174 @@ NearestFunc SamplerJitCache::Compile(const SamplerID &id) { 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(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; @@ -434,15 +611,20 @@ bool SamplerJitCache::Jit_TransformClutIndex(const SamplerID &id, int bitsPerInd bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) { if (!id.useSharedClut) { + // TODO: Need to load from RAM, always. + if (id.linear) { + return false; + } 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)); + // 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)); + // We need to multiply by 16 and add, LEA allows us to copy too. + LEA(32, tempReg2, MScaled(levelReg, SCALE_4, 0)); #endif - LEA(64, resultReg, MComplex(resultReg, tempReg2, SCALE_4, 0)); + LEA(64, resultReg, MComplex(resultReg, tempReg2, SCALE_4, 0)); + } } MOV(PTRBITS, R(tempReg1), ImmPtr(clut)); From fdf5f2ab54066a20fc83d79e07027d3b3a0568be Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 May 2017 16:19:40 -0700 Subject: [PATCH 16/17] SoftGPU: Support separate mip CLUTs with linear. --- GPU/Software/SamplerX86.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index fb93987471..38acfd72b1 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -613,7 +613,15 @@ bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) { if (!id.useSharedClut) { // TODO: Need to load from RAM, always. if (id.linear) { - return false; +#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. @@ -623,8 +631,10 @@ bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) { // We need to multiply by 16 and add, LEA allows us to copy too. LEA(32, tempReg2, MScaled(levelReg, SCALE_4, 0)); #endif - LEA(64, resultReg, MComplex(resultReg, tempReg2, SCALE_4, 0)); } + + // 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)); From 2d6b8c645ab06ac3032f8c3910e7dc0e5a01b54b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 28 May 2017 14:59:51 -0700 Subject: [PATCH 17/17] SoftGPU: Correct linear filter rounding issue. Using 16-bit slots just lost too much precision. Jit already doesn't use this. --- GPU/Software/Sampler.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index 36afda73a4..82ecaeb6e0 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -418,32 +418,13 @@ 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 texlevel) { Nearest4 c = SampleNearest<4>(u, v, tptr, bufw, texlevel); -#if defined(_M_SSE) - 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(_mm_unpacklo_epi16(res, z)).ToRGBA(); -#else Vec4 texcolor_tl = Vec4::FromRGBA(c.v[0]); Vec4 texcolor_tr = Vec4::FromRGBA(c.v[1]); Vec4 texcolor_bl = Vec4::FromRGBA(c.v[2]); Vec4 texcolor_br = Vec4::FromRGBA(c.v[3]); - // 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255. Vec4 t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u; Vec4 b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u; return ((t * (0x100 - frac_v) + b * frac_v) / (256 * 256)).ToRGBA(); -#endif } };