From d962fb35d33a71f5355fe4e9b76183d3b51b42ef Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 11 Jan 2022 19:42:40 -0800 Subject: [PATCH] softgpu: Centralize more prim drawing state. --- GPU/Software/Rasterizer.cpp | 99 +++++++++++++++------------- GPU/Software/Rasterizer.h | 16 +++++ GPU/Software/RasterizerRectangle.cpp | 12 ++-- 3 files changed, 74 insertions(+), 53 deletions(-) diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 22e86b1fbe..7e2d94071a 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -116,10 +116,12 @@ void ComputeRasterizerState(RasterizerState *state) { state->linear = state->nearest; } - int maxTexLevel = state->samplerID.hasAnyMips ? gstate.getTextureMaxLevel() : 0; - if (gstate.isTextureMapEnabled() && !state->pixelID.clearMode) { + state->maxTexLevel = state->samplerID.hasAnyMips ? gstate.getTextureMaxLevel() : 0; + state->enableTextures = gstate.isTextureMapEnabled(); + + if (state->enableTextures && !state->pixelID.clearMode) { GETextureFormat texfmt = state->samplerID.TexFmt(); - for (int i = 0; i <= maxTexLevel; i++) { + for (uint8_t i = 0; i <= state->maxTexLevel; i++) { u32 texaddr = gstate.getTextureAddress(i); state->texbufw[i] = GetTextureBufw(i, texaddr, texfmt); if (Memory::IsValidAddress(texaddr)) @@ -128,6 +130,14 @@ void ComputeRasterizerState(RasterizerState *state) { state->texptr[i] = nullptr; } } + + state->texLevelMode = gstate.getTexLevelMode(); + state->texLevelOffset = (int8_t)gstate.getTexLevelOffset16(); + state->shadeGouraud = gstate.getShadeMode() == GE_SHADE_GOURAUD; + state->throughMode = gstate.isModeThrough(); + state->mipFilt = gstate.isMipmapFilteringEnabled(); + state->minFilt = gstate.isMinifyFilteringEnabled(); + state->magFilt = gstate.isMagnifyFilteringEnabled(); } static inline u8 ClampFogDepth(float fogdepth) { @@ -506,9 +516,9 @@ Vec3 AlphaBlendingResult(const PixelFuncID &pixelID, const Vec4 &sourc } } -static inline Vec4IntResult SOFTRAST_CALL ApplyTexturing(float s, float t, int x, int y, Vec4IntArg prim_color, u8 *const texptr[], const int texbufw[], int texlevel, int frac_texlevel, bool bilinear, const RasterizerState &state) { - const u8 **tptr0 = const_cast(&texptr[texlevel]); - const int *bufw0 = &texbufw[texlevel]; +static inline Vec4IntResult SOFTRAST_CALL ApplyTexturing(float s, float t, int x, int y, Vec4IntArg prim_color, int texlevel, int frac_texlevel, bool bilinear, const RasterizerState &state) { + const u8 **tptr0 = const_cast(&state.texptr[texlevel]); + const int *bufw0 = &state.texbufw[texlevel]; if (!bilinear) { return state.nearest(s, t, x, y, prim_color, tptr0, bufw0, texlevel, frac_texlevel); @@ -516,8 +526,8 @@ static inline Vec4IntResult SOFTRAST_CALL ApplyTexturing(float s, float t, int x return state.linear(s, t, x, y, prim_color, tptr0, bufw0, texlevel, frac_texlevel); } -static inline Vec4IntResult SOFTRAST_CALL ApplyTexturingSingle(float s, float t, int x, int y, Vec4IntArg prim_color, u8 *const texptr[], const int texbufw[], int texlevel, int frac_texlevel, bool bilinear, const RasterizerState &state) { - return ApplyTexturing(s, t, ((x & 15) + 1) / 2, ((y & 15) + 1) / 2, prim_color, texptr, texbufw, texlevel, frac_texlevel, bilinear, state); +static inline Vec4IntResult SOFTRAST_CALL ApplyTexturingSingle(float s, float t, int x, int y, Vec4IntArg prim_color, int texlevel, int frac_texlevel, bool bilinear, const RasterizerState &state) { + return ApplyTexturing(s, t, ((x & 15) + 1) / 2, ((y & 15) + 1) / 2, prim_color, texlevel, frac_texlevel, bilinear, state); } // Produces a signed 1.27.4 value. @@ -535,13 +545,13 @@ static int TexLog2(float delta) { return useful - 127 * 16; } -static inline void CalculateSamplingParams(const float ds, const float dt, const int maxTexLevel, int &level, int &levelFrac, bool &filt) { - const int width = gstate.getTextureWidth(0); - const int height = gstate.getTextureHeight(0); +static inline void CalculateSamplingParams(const float ds, const float dt, const RasterizerState &state, int &level, int &levelFrac, bool &filt) { + const int width = 1 << state.samplerID.width0Shift; + const int height = 1 << state.samplerID.height0Shift; // With 8 bits of fraction (because texslope can be fairly precise.) int detail; - switch (gstate.getTexLevelMode()) { + switch (state.TexLevelMode()) { case GE_TEXLEVEL_MODE_AUTO: detail = TexLog2(std::max(ds * width, dt * height)); break; @@ -556,13 +566,13 @@ static inline void CalculateSamplingParams(const float ds, const float dt, const break; } - // Add in the bias (used in all modes), expanding to 8 bits of fraction. - detail += gstate.getTexLevelOffset16(); + // Add in the bias (used in all modes), with 4 bits of fraction. + detail += state.texLevelOffset; - if (detail > 0 && maxTexLevel > 0) { - bool mipFilt = gstate.isMipmapFilteringEnabled(); + if (detail > 0 && state.maxTexLevel > 0) { + bool mipFilt = state.mipFilt; - int level8 = std::min(detail, maxTexLevel * 16); + int level8 = std::min(detail, state.maxTexLevel * 16); if (!mipFilt) { // Round up at 1.5. level8 += 8; @@ -575,24 +585,24 @@ static inline void CalculateSamplingParams(const float ds, const float dt, const } if (detail > 0) - filt = gstate.isMinifyFilteringEnabled(); + filt = state.minFilt; else - filt = gstate.isMagnifyFilteringEnabled(); + filt = state.magFilt; } -static inline void ApplyTexturing(const RasterizerState &state, Vec4 *prim_color, const Vec4 &mask, const Vec4 &s, const Vec4 &t, int maxTexLevel, u8 *const texptr[], const int texbufw[], int x, int y) { +static inline void ApplyTexturing(const RasterizerState &state, Vec4 *prim_color, const Vec4 &mask, const Vec4 &s, const Vec4 &t, int x, int y) { float ds = s[1] - s[0]; float dt = t[2] - t[0]; int level; int levelFrac; bool bilinear; - CalculateSamplingParams(ds, dt, maxTexLevel, level, levelFrac, bilinear); + CalculateSamplingParams(ds, dt, state, level, levelFrac, bilinear); PROFILE_THIS_SCOPE("sampler"); for (int i = 0; i < 4; ++i) { if (mask[i] >= 0) - prim_color[i] = ApplyTexturing(s[i], t[i], ((x & 15) + 1) / 2, ((y & 15) + 1) / 2, ToVec4IntArg(prim_color[i]), texptr, texbufw, level, levelFrac, bilinear, state); + prim_color[i] = ApplyTexturing(s[i], t[i], ((x & 15) + 1) / 2, ((y & 15) + 1) / 2, ToVec4IntArg(prim_color[i]), level, levelFrac, bilinear, state); } } @@ -775,7 +785,6 @@ void DrawTriangleSlice( Vec4 bias2 = Vec4::AssignToAll(IsRightSideOrFlatBottomLine(v2.screenpos.xy(), v0.screenpos.xy(), v1.screenpos.xy()) ? -1 : 0); const PixelFuncID &pixelID = state.pixelID; - int maxTexLevel = state.samplerID.hasAnyMips ? gstate.getTextureMaxLevel() : 0; TriangleEdge e0; TriangleEdge e1; @@ -791,7 +800,7 @@ void DrawTriangleSlice( // All the z values are the same, no interpolation required. // 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; - const bool flatColorAll = clearMode || gstate.getShadeMode() != GE_SHADE_GOURAUD; + const bool flatColorAll = clearMode || !state.shadeGouraud; const bool flatColor0 = flatColorAll || (v0.color0 == v1.color0 && v0.color0 == v2.color0); const bool flatColor1 = flatColorAll || (v0.color1 == v1.color1 && v0.color1 == v2.color1); const bool noFog = clearMode || !pixelID.applyFog || (v0.fogdepth >= 1.0f && v1.fogdepth >= 1.0f && v2.fogdepth >= 1.0f); @@ -867,21 +876,21 @@ void DrawTriangleSlice( } } - if (gstate.isTextureMapEnabled() && !clearMode) { + if (state.enableTextures && !clearMode) { Vec4 s, t; - if (gstate.isModeThrough()) { + if (state.throughMode) { s = Interpolate(v0.texturecoords.s(), v1.texturecoords.s(), v2.texturecoords.s(), w0, w1, w2, wsum_recip); t = Interpolate(v0.texturecoords.t(), v1.texturecoords.t(), v2.texturecoords.t(), w0, w1, w2, wsum_recip); // For levels > 0, mipmapping is always based on level 0. Simpler to scale first. - s *= 1.0f / (float)gstate.getTextureWidth(0); - t *= 1.0f / (float)gstate.getTextureHeight(0); + s *= 1.0f / (float)(1 << state.samplerID.width0Shift); + t *= 1.0f / (float)(1 << state.samplerID.height0Shift); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v1, v2, w0, w1, w2, wsum_recip, s, t); } - ApplyTexturing(state, prim_color, mask, s, t, maxTexLevel, state.texptr, state.texbufw, curX, curY); + ApplyTexturing(state, prim_color, mask, s, t, curX, curY); } if (!clearMode) { @@ -1037,14 +1046,12 @@ void DrawPoint(const VertexData &v0, const RasterizerState &state) { auto &pixelID = state.pixelID; auto &samplerID = state.samplerID; - if (gstate.isTextureMapEnabled() && !pixelID.clearMode) { - int maxTexLevel = samplerID.hasAnyMips ? gstate.getTextureMaxLevel() : 0; - + if (state.enableTextures && !pixelID.clearMode) { float s = v0.texturecoords.s(); float t = v0.texturecoords.t(); - if (gstate.isModeThrough()) { - s *= 1.0f / (float)gstate.getTextureWidth(0); - t *= 1.0f / (float)gstate.getTextureHeight(0); + if (state.throughMode) { + s *= 1.0f / (float)(1 << state.samplerID.width0Shift); + t *= 1.0f / (float)(1 << state.samplerID.height0Shift); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v0, 0.0f, s, t); @@ -1053,9 +1060,9 @@ void DrawPoint(const VertexData &v0, const RasterizerState &state) { int texLevel; int texLevelFrac; bool bilinear; - CalculateSamplingParams(0.0f, 0.0f, maxTexLevel, texLevel, texLevelFrac, bilinear); + CalculateSamplingParams(0.0f, 0.0f, state, texLevel, texLevelFrac, bilinear); PROFILE_THIS_SCOPE("sampler"); - prim_color = ApplyTexturingSingle(s, t, pos.x, pos.y, ToVec4IntArg(prim_color), state.texptr, state.texbufw, texLevel, texLevelFrac, bilinear, state); + prim_color = ApplyTexturingSingle(s, t, pos.x, pos.y, ToVec4IntArg(prim_color), texLevel, texLevelFrac, bilinear, state); } if (!pixelID.clearMode) @@ -1308,8 +1315,6 @@ void DrawLine(const VertexData &v0, const VertexData &v1, const RasterizerState scissorBR.x += 15; scissorBR.y += 15; - int maxTexLevel = state.samplerID.hasAnyMips ? gstate.getTextureMaxLevel() : 0; - auto &pixelID = state.pixelID; auto &samplerID = state.samplerID; @@ -1349,17 +1354,17 @@ void DrawLine(const VertexData &v0, const VertexData &v1, const RasterizerState prim_color.a() = 0x7F; } - if (gstate.isTextureMapEnabled() && !pixelID.clearMode) { + if (state.enableTextures && !pixelID.clearMode) { float s, s1; float t, t1; - if (gstate.isModeThrough()) { + if (state.throughMode) { Vec2 tc = (v0.texturecoords * (float)(steps - i) + v1.texturecoords * (float)i) / steps1; Vec2 tc1 = (v0.texturecoords * (float)(steps - i - 1) + v1.texturecoords * (float)(i + 1)) / steps1; - s = tc.s() * (1.0f / (float)gstate.getTextureWidth(0)); - s1 = tc1.s() * (1.0f / (float)gstate.getTextureWidth(0)); - t = tc.t() * (1.0f / (float)gstate.getTextureHeight(0)); - t1 = tc1.t() * (1.0f / (float)gstate.getTextureHeight(0)); + s = tc.s() * (1.0f / (float)(1 << state.samplerID.width0Shift)); + s1 = tc1.s() * (1.0f / (float)(1 << state.samplerID.width0Shift)); + t = tc.t() * (1.0f / (float)(1 << state.samplerID.height0Shift)); + t1 = tc1.t() * (1.0f / (float)(1 << state.samplerID.height0Shift)); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v1, (float)(steps - i) / steps1, s, t); @@ -1373,7 +1378,7 @@ void DrawLine(const VertexData &v0, const VertexData &v1, const RasterizerState int texLevel; int texLevelFrac; bool texBilinear; - CalculateSamplingParams(ds, dt, maxTexLevel, texLevel, texLevelFrac, texBilinear); + CalculateSamplingParams(ds, dt, state, texLevel, texLevelFrac, texBilinear); if (gstate.isAntiAliasEnabled()) { // TODO: This is a niave and wrong implementation. @@ -1385,7 +1390,7 @@ void DrawLine(const VertexData &v0, const VertexData &v1, const RasterizerState } PROFILE_THIS_SCOPE("sampler"); - prim_color = ApplyTexturingSingle(s, t, x, y, ToVec4IntArg(prim_color), state.texptr, state.texbufw, texLevel, texLevelFrac, texBilinear, state); + prim_color = ApplyTexturingSingle(s, t, x, y, ToVec4IntArg(prim_color), texLevel, texLevelFrac, texBilinear, state); } if (!pixelID.clearMode) diff --git a/GPU/Software/Rasterizer.h b/GPU/Software/Rasterizer.h index 59e2d390c9..2a1478351c 100644 --- a/GPU/Software/Rasterizer.h +++ b/GPU/Software/Rasterizer.h @@ -40,6 +40,22 @@ struct RasterizerState { Sampler::NearestFunc nearest; int texbufw[8]{}; u8 *texptr[8]{}; + + struct { + uint8_t maxTexLevel : 3; + bool enableTextures : 1; + uint8_t texLevelMode : 2; + bool shadeGouraud : 1; + bool throughMode : 1; + int8_t texLevelOffset : 8; + bool mipFilt : 1; + bool minFilt : 1; + bool magFilt : 1; + }; + + GETexLevelMode TexLevelMode() const { + return GETexLevelMode(texLevelMode); + } }; void ComputeRasterizerState(RasterizerState *state); diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index 795674c5e1..889c6531e1 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -102,7 +102,7 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const RasterizerStat constexpr int MIN_LINES_PER_THREAD = 32; - if (gstate.isTextureMapEnabled()) { + if (state.enableTextures) { // 1:1 (but with mirror support) texture mapping! int s_start = v0.texturecoords.x; int t_start = v0.texturecoords.y; @@ -184,10 +184,10 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const RasterizerStat int xoff = ((v0.screenpos.x & 15) + 1) / 2; int yoff = ((v0.screenpos.y & 15) + 1) / 2; - float dsf = ds * (1.0f / (float)gstate.getTextureWidth(0)); - float dtf = dt * (1.0f / (float)gstate.getTextureHeight(0)); - float sf_start = s_start * (1.0f / (float)gstate.getTextureWidth(0)); - float tf_start = t_start * (1.0f / (float)gstate.getTextureHeight(0)); + float dsf = ds * (1.0f / (float)(1 << state.samplerID.width0Shift)); + float dtf = dt * (1.0f / (float)(1 << state.samplerID.height0Shift)); + float sf_start = s_start * (1.0f / (float)(1 << state.samplerID.width0Shift)); + float tf_start = t_start * (1.0f / (float)(1 << state.samplerID.height0Shift)); ParallelRangeLoop(&g_threadManager, [=](int y1, int y2) { float t = tf_start + (y1 - pos0.y) * dtf; @@ -291,7 +291,7 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, const Rasteri // We already have a fast path for clear in ClearRectangle. bool state_check = !state.pixelID.clearMode && NoClampOrWrap(v0.texturecoords) && NoClampOrWrap(v1.texturecoords); // TODO: No mipmap levels? Might be a font at level 1... - if ((coord_check || !gstate.isTextureMapEnabled()) && orient_check && state_check) { + if ((coord_check || !state.enableTextures) && orient_check && state_check) { Rasterizer::DrawSprite(v0, v1, state); return true; }