From bd1d4e2d92d5c75d6a9876b8bd9a4956f5dfa4b6 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 9 Dec 2013 01:44:23 +0100 Subject: [PATCH] As if softgpu wasn't slow enough already, implement bilinear filtering. --- GPU/GLES/StateMapping.cpp | 2 +- GPU/Software/Rasterizer.cpp | 147 +++++++++++++++++++++++++++--------- 2 files changed, 114 insertions(+), 35 deletions(-) diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index f22a94762d..d03f468e64 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -182,7 +182,7 @@ void TransformDrawEngine::ApplyDrawState(int prim) { float constantAlpha = 1.0f; if (gstate.isStencilTestEnabled() && !CanReplaceAlphaWithStencil()) { if (gstate.isStencilTestEnabled() == STENCIL_VALUE_UNIFORM) { - constantAlpha = gstate.getStencilTestRef() / 255.0f; + constantAlpha = gstate.getStencilTestRef() * (1.0f / 255.0f); } } diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index c6946bfa84..f237996eda 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -96,52 +96,97 @@ static inline u32 LookupColor(unsigned int index, unsigned int level) } } -static inline void GetTexelCoordinates(int level, float s, float t, unsigned int& u, unsigned int& v) +static inline void GetTexelCoordinates(int level, float s, float t, int& out_u, int& out_v) { - s *= getFloat24(gstate.texscaleu); - t *= getFloat24(gstate.texscalev); + s = s * getFloat24(gstate.texscaleu) + getFloat24(gstate.texoffsetu); + t = t * getFloat24(gstate.texscalev) + getFloat24(gstate.texoffsetv); - s += getFloat24(gstate.texoffsetu); - t += getFloat24(gstate.texoffsetv); + int width = 1 << (gstate.texsize[level] & 0xf); + int height = 1 << ((gstate.texsize[level]>>8) & 0xf); + + int u = (int)(s * width); + int v = (int)(t * height); - // TODO: Is this really only necessary for UV mapping? if (gstate.isTexCoordClampedS()) { - if (s > 1.0) s = 1.0; - if (s < 0) s = 0; + if (u >= width - 1) + u = width - 1; + else if (u < 0) + u = 0; } else { - // Subtracting floor works for negative and positive to discard the non-fractional part. - s -= floor(s); + u &= width - 1; } if (gstate.isTexCoordClampedT()) { - if (t > 1.0) t = 1.0; - if (t < 0.0) t = 0.0; + if (v >= height - 1) + v = height - 1; + else if (v < 0) + v = 0; } else { - // Subtracting floor works for negative and positive to discard the non-fractional part. - t -= floor(t); + v &= height - 1; } - int width = 1 << (gstate.texsize[level] & 0xf); - int height = 1 << ((gstate.texsize[level]>>8) & 0xf); - - // TODO: These should really be multiplied by 256 to get fixed point coordinates - // so we can do texture filtering later. - - u = (unsigned int)(s * width); // TODO: width-1 instead? - v = (unsigned int)(t * height); // TODO: width-1 instead? + out_u = u; + out_v = v; } -static inline void GetTexelCoordinatesThrough(int level, float s, float t, unsigned int& u, unsigned int& v) +static inline void GetTexelCoordinatesQuad(int level, float in_s, float in_t, int u[4], int v[4], int &frac_u, int &frac_v) { - // Not actually sure which clamp/wrap modes should be applied. Let's just wrap for now. + in_s = in_s * getFloat24(gstate.texscaleu) + getFloat24(gstate.texoffsetu); + in_t = in_t * getFloat24(gstate.texscalev) + getFloat24(gstate.texoffsetv); + + // 8 bits of fractional UV int width = 1 << (gstate.texsize[level] & 0xf); int height = 1 << ((gstate.texsize[level]>>8) & 0xf); - // TODO: These should really be multiplied by 256 to get fixed point coordinates - // so we can do texture filtering later. + int base_u = in_s * width * 256; + int base_v = in_t * height * 256; + + frac_u = (int)(base_u) & 0xff; + frac_v = (int)(base_v) & 0xff; + + base_u >>= 8; + base_v >>= 8; + + // Need to generate and individually wrap/clamp the four sample coordinates. Ugh. + + if (gstate.isTexCoordClampedS()) { + for (int i = 0; i < 4; i++) { + int temp_u = base_u + (i & 1); + if (temp_u > width - 1) + temp_u = width - 1; + else if (temp_u < 0) + temp_u = 0; + u[i] = temp_u; + } + } else { + for (int i = 0; i < 4; i++) { + u[i] = (base_u + (i & 1)) & (width - 1); + } + } + if (gstate.isTexCoordClampedT()) { + for (int i = 0; i < 4; i++) { + int temp_v = base_v + ((i & 2) >> 1); + if (temp_v > height - 1) + temp_v = height - 1; + else if (temp_v < 0) + temp_v = 0; + v[i] = temp_v; + } + } else { + for (int i = 0; i < 4; i++) { + v[i] = (base_v + ((i & 2) >> 1)) & (height - 1); + } + } +} + +static inline void GetTexelCoordinatesThrough(int level, int s, int t, int& u, int& v) +{ + // Not actually sure which clamp/wrap modes should be applied. Let's just wrap for now. + int width = (1 << 8) << (gstate.texsize[level] & 0xf); + int height = (1 << 8) << ((gstate.texsize[level]>>8) & 0xf); // Wrap! - u = (unsigned int)(s) & (width - 1); - v = (unsigned int)(t) & (height - 1); + u = ((unsigned int)(s) & (width - 1)) >> 8; + v = ((unsigned int)(t) & (height - 1)) >> 8; } static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& v1, const VertexData& v2, int w0, int w1, int w2, float& s, float& t) @@ -166,9 +211,9 @@ static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& // projection mapping, TODO: Move this code to TransformUnit! Vec3 source; if (gstate.getUVProjMode() == GE_PROJMAP_POSITION) { - source = ((v0.modelpos * w0 + v1.modelpos * w1 + v2.modelpos * w2) / (w0+w1+w2)); + source = ((v0.modelpos * w0 + v1.modelpos * w1 + v2.modelpos * w2) / (w0+w1+w2)); } else { - ERROR_LOG_REPORT(G3D, "Software: Unsupported UV projection mode %x", gstate.getUVProjMode()); + ERROR_LOG_REPORT(G3D, "Software: Unsupported UV projection mode %x", gstate.getUVProjMode()); } Mat3x3 tgen(gstate.tgenMatrix); @@ -187,6 +232,7 @@ static inline u32 SampleNearest(int level, unsigned int u, unsigned int v, u8 *s { if (!srcptr) return 0; + GETextureFormat texfmt = gstate.getTextureFormat(); // TODO: Should probably check if textures are aligned properly... @@ -864,19 +910,52 @@ void DrawTriangleSlice( } if (gstate.isTextureMapEnabled() && !clearMode) { - unsigned int u = 0, v = 0; + int u[4] = {0}, v[4] = {0}; // 1.23.8 fixed point + int frac_u, frac_v; + + int magFilt = (gstate.texfilter>>8) & 1; + + bool bilinear = magFilt != 0; + // bilinear = false; + if (gstate.isModeThrough()) { // TODO: Is it really this simple? float s = ((v0.texturecoords.s() * w0 + v1.texturecoords.s() * w1 + v2.texturecoords.s() * w2) * wsum); float t = ((v0.texturecoords.t() * w0 + v1.texturecoords.t() * w1 + v2.texturecoords.t() * w2) * wsum); - GetTexelCoordinatesThrough(0, s, t, u, v); + + int u_texel = s * 256; + int v_texel = t * 256; + frac_u = u_texel & 0xff; + frac_v = v_texel & 0xff; + GetTexelCoordinatesThrough(0, u_texel, v_texel, u[0], v[0]); + if (bilinear) { + GetTexelCoordinatesThrough(0, u_texel + 256, v_texel, u[1], v[1]); + GetTexelCoordinatesThrough(0, u_texel, v_texel + 256, u[2], v[2]); + GetTexelCoordinatesThrough(0, u_texel + 256, v_texel + 256, u[3], v[3]); + } } else { float s = 0, t = 0; GetTextureCoordinates(v0, v1, v2, w0, w1, w2, s, t); - GetTexelCoordinates(0, s, t, u, v); + if (bilinear) { + GetTexelCoordinatesQuad(0, s, t, u, v, frac_u, frac_v); + } else { + GetTexelCoordinates(0, s, t, u[0], v[0]); + } } - Vec4 texcolor = Vec4::FromRGBA(SampleNearest(texlevel, u, v, texptr, texbufwidthbits)); + Vec4 texcolor; + if (!bilinear) { + // Nearest filtering only. Round texcoords or just chop bits? + texcolor = Vec4::FromRGBA(SampleNearest(texlevel, u[0], v[0], texptr, texbufwidthbits)); + } else { + Vec4 texcolor_tl = Vec4::FromRGBA(SampleNearest(texlevel, u[0], v[0], texptr, texbufwidthbits)); + Vec4 texcolor_tr = Vec4::FromRGBA(SampleNearest(texlevel, u[1], v[1], texptr, texbufwidthbits)); + Vec4 texcolor_bl = Vec4::FromRGBA(SampleNearest(texlevel, u[2], v[2], texptr, texbufwidthbits)); + Vec4 texcolor_br = Vec4::FromRGBA(SampleNearest(texlevel, u[3], v[3], texptr, texbufwidthbits)); + Vec4 t = texcolor_tl * (0xff - frac_u) + texcolor_tr * frac_u; + Vec4 b = texcolor_bl * (0xff - frac_u) + texcolor_br * frac_u; + texcolor = (t * (0xff - frac_v) + b * frac_v) / (256 * 256); + } Vec4 out = GetTextureFunctionOutput(prim_color_rgb, prim_color_a, texcolor); prim_color_rgb = out.rgb(); prim_color_a = out.a();