diff --git a/CMakeLists.txt b/CMakeLists.txt index bb71ceb193..a45433d5ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1408,6 +1408,8 @@ set(GPU_SOURCES GPU/Software/Lighting.h GPU/Software/Rasterizer.cpp GPU/Software/Rasterizer.h + GPU/Software/RasterizerRectangle.cpp + GPU/Software/RasterizerRectangle.h GPU/Software/Sampler.cpp GPU/Software/Sampler.h GPU/Software/SoftGpu.cpp diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 44da128934..34923675a7 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -501,6 +501,7 @@ + @@ -690,6 +691,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index b6f35b5a2b..3c165d4c6f 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -282,6 +282,9 @@ Debugger + + Software + @@ -560,5 +563,8 @@ Debugger + + Software + \ No newline at end of file diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index 6d22d2e7ee..2aa3ff69eb 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -17,20 +17,14 @@ #include -#include "Core/System.h" - #include "GPU/GPUState.h" #include "GPU/Software/Clipper.h" #include "GPU/Software/Rasterizer.h" +#include "GPU/Software/RasterizerRectangle.h" #include "profiler/profiler.h" - -extern bool g_DarkStalkerStretch; -// For Darkstalkers hack. Ugh. -extern bool currentDialogActive; - namespace Clipper { enum { @@ -141,11 +135,8 @@ static void RotateUVThrough(const VertexData &tl, const VertexData &br, VertexDa } } -bool needsClear = false; - void ProcessRect(const VertexData& v0, const VertexData& v1) { - g_DarkStalkerStretch = false; if (!gstate.isModeThrough()) { VertexData buf[4]; buf[0].clippos = ClipCoords(v0.clippos.x, v0.clippos.y, v1.clippos.z, v1.clippos.w); @@ -188,43 +179,10 @@ void ProcessRect(const VertexData& v0, const VertexData& v1) } else { // through mode handling - // Check for 1:1 texture mapping. In that case we can call DrawSprite. - int xdiff = v1.screenpos.x - v0.screenpos.x; - int ydiff = v1.screenpos.y - v0.screenpos.y; - int udiff = (v1.texturecoords.x - v0.texturecoords.x) * 16.0f; - int vdiff = (v1.texturecoords.y - v0.texturecoords.y) * 16.0f; - bool coord_check = - (xdiff == udiff || xdiff == -udiff) && - (ydiff == vdiff || ydiff == -vdiff); - bool state_check = !gstate.isModeClear(); // TODO: Add support for clear modes in Rasterizer::DrawSprite. - if ((coord_check || !gstate.isTextureMapEnabled()) && state_check) { - Rasterizer::DrawSprite(v0, v1); + if (Rasterizer::RectangleFastPath(v0, v1)) { return; } - // Eliminate the stretch blit in DarkStalkers. - // We compensate for that when blitting the framebuffer in SoftGpu.cpp. - if (PSP_CoreParameter().compat.flags().DarkStalkersPresentHack && v0.texturecoords.x == 64.0f && v0.texturecoords.y == 16.0f && v1.texturecoords.x == 448.0f && v1.texturecoords.y == 240.0f) { - if (v0.screenpos.x == 0x7100 && v0.screenpos.y == 0x7780 && v1.screenpos.x == 0x8f00 && v1.screenpos.y == 0x8880) { - // Also check for save/load dialog. - if (!currentDialogActive) { - g_DarkStalkerStretch = true; - if (needsClear) { - needsClear = false; - // Afterwards, we also need to clear the actual destination. Can do a fast rectfill. - gstate.textureMapEnable &= ~1; - VertexData newV0 = v0; - newV0.color0 = Vec4(0, 0, 0, 255); - Rasterizer::DrawSprite(newV0, v1); - gstate.textureMapEnable |= 1; - } - return; - } else { - needsClear = true; - } - } // else, handle the Capcom screen stretch, or the non-wide stretch? Or let's just not bother. - } - VertexData buf[4]; buf[0].screenpos = ScreenCoords(v0.screenpos.x, v0.screenpos.y, v1.screenpos.z); buf[0].texturecoords = v0.texturecoords; diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 71560776b8..ed60834075 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -545,7 +545,7 @@ static inline u32 ApplyLogicOp(GELogicOp op, u32 old_color, u32 new_color) { return new_color; } -static inline Vec4 GetTextureFunctionOutput(const Vec4& prim_color, const Vec4& texcolor) +Vec4 GetTextureFunctionOutput(const Vec4& prim_color, const Vec4& texcolor) { Vec3 out_rgb; int out_a; @@ -779,7 +779,8 @@ static inline Vec3 GetDestFactor(const Vec4& source, const Vec4& } } -static inline Vec3 AlphaBlendingResult(const Vec4 &source, const Vec4 &dst) +// Removed inline here - it was never chosen to be inlined by the compiler anyway, too complex. +inline Vec3 AlphaBlendingResult(const Vec4 &source, const Vec4 &dst) { // Note: These factors cannot go below 0, but they can go above 255 when doubling. Vec3 srcfactor = GetSourceFactor(source, dst); @@ -935,6 +936,10 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 &color_in) { + DrawSinglePixel(p, z, fog, color_in); +} + 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]; @@ -1287,200 +1292,6 @@ void DrawTriangleSlice( } } -// Through mode, with the specific Darkstalker settings. -inline void DrawSinglePixel5551(u16 *pixel, const Vec4 &color_in) { - u32 new_color; - if (color_in.a() == 255) { - new_color = color_in.ToRGBA() & 0xFFFFFF; - } else { - const u32 old_color = RGBA5551ToRGBA8888(*pixel); - const Vec4 dst = Vec4::FromRGBA(old_color); - Vec3 blended = AlphaBlendingResult(color_in, dst); - // ToRGB() always automatically clamps. - new_color = blended.ToRGB(); - } - - new_color |= (*pixel & 0x8000) ? 0xff000000 : 0x00000000; - *pixel = RGBA8888ToRGBA5551(new_color); -} - -static inline Vec4 ModulateRGBA(const Vec4& prim_color, const Vec4& texcolor) { - Vec3 out_rgb; - int out_a; - -#if defined(_M_SSE) - // We can be accurate up to 24 bit integers, should be enough. - const __m128 p = _mm_cvtepi32_ps(prim_color.ivec); - const __m128 t = _mm_cvtepi32_ps(texcolor.ivec); - const __m128 b = _mm_mul_ps(p, t); - if (gstate.isColorDoublingEnabled()) { - // We double right here, only for modulate. Other tex funcs do not color double. - const __m128 doubleColor = _mm_setr_ps(2.0f / 255.0f, 2.0f / 255.0f, 2.0f / 255.0f, 1.0f / 255.0f); - out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, doubleColor)); - } else { - out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, _mm_set_ps1(1.0f / 255.0f))); - } - return Vec4(out_rgb.ivec); -#else - if (gstate.isColorDoublingEnabled()) { - out_rgb = (prim_color.rgb() * texcolor.rgb() * 2) / 255; - } else { - out_rgb = prim_color.rgb() * texcolor.rgb() / 255; - } - out_a = (prim_color.a() * texcolor.a() / 255); -#endif - - return Vec4(out_rgb.r(), out_rgb.g(), out_rgb.b(), out_a); - -} - -void DrawSprite(const VertexData& v0, const VertexData& v1) { - const u8 *texptr = nullptr; - - GETextureFormat texfmt = gstate.getTextureFormat(); - u32 texaddr = gstate.getTextureAddress(0); - int texbufw = GetTextureBufw(0, texaddr, texfmt); - if (Memory::IsValidAddress(texaddr)) - texptr = Memory::GetPointerUnchecked(texaddr); - - ScreenCoords pprime(v0.screenpos.x, v0.screenpos.y, 0); - Sampler::NearestFunc nearestFunc = Sampler::GetNearestFunc(); // Looks at gstate. - - DrawingCoords pos0 = TransformUnit::ScreenToDrawing(v0.screenpos); - DrawingCoords pos1 = TransformUnit::ScreenToDrawing(v1.screenpos); - - DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0); - DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0); - - int z = pos0.z; - float fog = 1.0f; - - bool isWhite = v0.color0 == Vec4(255, 255, 255, 255); - - if (gstate.isTextureMapEnabled()) { - // 1:1 (but with mirror support) texture mapping! - int s_start = v0.texturecoords.x; - int t_start = v0.texturecoords.y; - int ds = v1.texturecoords.x > v0.texturecoords.x ? 1 : -1; - int dt = v1.texturecoords.y > v0.texturecoords.y ? 1 : -1; - - if (ds < 0) { - s_start += ds; - } - if (dt < 0) { - t_start += dt; - } - - // First clip the right and bottom sides, since we don't need to adjust the deltas. - if (pos1.x > scissorBR.x) pos1.x = scissorBR.x + 1; - if (pos1.y > scissorBR.y) pos1.y = scissorBR.y + 1; - // Now clip the other sides. - if (pos0.x < scissorTL.x) { - s_start += (scissorTL.x - pos0.x) * ds; - pos0.x = scissorTL.x; - } - if (pos0.y < scissorTL.y) { - t_start += (scissorTL.y - pos0.y) * dt; - pos0.y = scissorTL.y; - } - - if (!gstate.isStencilTestEnabled() && - !gstate.isDepthTestEnabled() && - !gstate.isLogicOpEnabled() && - !gstate.isColorTestEnabled() && - !gstate.isDitherEnabled() && - gstate.isAlphaTestEnabled() && - gstate.getAlphaTestRef() == 0 && - gstate.getAlphaTestMask() == 0xFF && - gstate.isAlphaBlendEnabled() && - gstate.isTextureAlphaUsed() && - gstate.getTextureFunction() == GE_TEXFUNC_MODULATE && - gstate.getColorMask() == 0x000000 && - gstate.FrameBufFormat() == GE_FORMAT_5551) { - int t = t_start; - for (int y = pos0.y; y < pos1.y; y++) { - int s = s_start; - u16 *pixel = fb.Get16Ptr(pos0.x, y, gstate.FrameBufStride()); - if (isWhite) { - for (int x = pos0.x; x < pos1.x; x++) { - u32 tex_color = nearestFunc(s, t, texptr, texbufw, 0); - if (tex_color & 0xFF000000) { - DrawSinglePixel5551(pixel, Vec4::FromRGBA(tex_color)); - } - s += ds; - pixel++; - } - } else { - for (int x = pos0.x; x < pos1.x; x++) { - Vec4 prim_color = v0.color0; - Vec4 tex_color = Vec4::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0)); - prim_color = ModulateRGBA(prim_color, tex_color); - if (prim_color.a() > 0) { - DrawSinglePixel5551(pixel, prim_color); - } - s += ds; - pixel++; - } - } - t += dt; - } - } else { - int t = t_start; - for (int y = pos0.y; y < pos1.y; y++) { - int s = s_start; - // Not really that fast but faster than triangle. - for (int x = pos0.x; x < pos1.x; x++) { - Vec4 prim_color = v0.color0; - Vec4 tex_color = Vec4::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0)); - prim_color = GetTextureFunctionOutput(prim_color, tex_color); - DrawingCoords pos(x, y, z); - DrawSinglePixel(pos, (u16)z, 1.0f, prim_color); - s += ds; - } - t += dt; - } - } - } else { - if (pos1.x > scissorBR.x) pos1.x = scissorBR.x; - if (pos1.y > scissorBR.y) pos1.y = scissorBR.y; - if (pos0.x < scissorTL.x) pos0.x = scissorTL.x; - if (pos0.y < scissorTL.y) pos0.y = scissorTL.y; - if (!gstate.isStencilTestEnabled() && - !gstate.isDepthTestEnabled() && - !gstate.isLogicOpEnabled() && - !gstate.isColorTestEnabled() && - !gstate.isDitherEnabled() && - gstate.isAlphaTestEnabled() && - gstate.getAlphaTestRef() == 0 && - gstate.getAlphaTestMask() == 0xFF && - gstate.isAlphaBlendEnabled() && - gstate.isTextureAlphaUsed() && - gstate.getTextureFunction() == GE_TEXFUNC_MODULATE && - gstate.getColorMask() == 0x000000 && - gstate.FrameBufFormat() == GE_FORMAT_5551) { - if (v0.color0.a() == 0) - return; - - for (int y = pos0.y; y < pos1.y; y++) { - u16 *pixel = fb.Get16Ptr(pos0.x, y, gstate.FrameBufStride()); - for (int x = pos0.x; x < pos1.x; x++) { - Vec4 prim_color = v0.color0; - DrawSinglePixel5551(pixel, prim_color); - pixel++; - } - } - } else { - for (int y = pos0.y; y < pos1.y; y++) { - for (int x = pos0.x; x < pos1.x; x++) { - Vec4 prim_color = v0.color0; - DrawingCoords pos(x, y, z); - DrawSinglePixel(pos, (u16)z, fog, prim_color); - } - } - } - } -} - // Draws triangle, vertices specified in counter-clockwise direction void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData& v2) { diff --git a/GPU/Software/Rasterizer.h b/GPU/Software/Rasterizer.h index df3075e3a0..1fd6407ce7 100644 --- a/GPU/Software/Rasterizer.h +++ b/GPU/Software/Rasterizer.h @@ -27,10 +27,14 @@ namespace Rasterizer { void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData& v2); void DrawPoint(const VertexData &v0); void DrawLine(const VertexData &v0, const VertexData &v1); -void DrawSprite(const VertexData &v0, const VertexData &v1); void ClearRectangle(const VertexData &v0, const VertexData &v1); bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer); bool GetCurrentTexture(GPUDebugBuffer &buffer, int level); -} +// Shared functions with RasterizerRectangle.cpp +Vec3 AlphaBlendingResult(const Vec4 &source, const Vec4 &dst); +void DrawSinglePixelNonClear(const DrawingCoords &p, u16 z, u8 fog, const Vec4 &color_in); +Vec4 GetTextureFunctionOutput(const Vec4& prim_color, const Vec4& texcolor); + +} // namespace Rasterizer diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp new file mode 100644 index 0000000000..3845c355f3 --- /dev/null +++ b/GPU/Software/RasterizerRectangle.cpp @@ -0,0 +1,316 @@ +// See comment in header for the purpose of this. + +#include +#include + +#include "base/basictypes.h" +#include "profiler/profiler.h" + +#include "Core/System.h" + +#include "Common/ColorConv.h" +#include "Core/Config.h" +#include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "GPU/GPUState.h" + +#include "Rasterizer.h" +#include "GPU/Common/TextureCacheCommon.h" +#include "GPU/Software/SoftGpu.h" +#include "GPU/Software/Rasterizer.h" +#include "GPU/Software/Sampler.h" + +#if defined(_M_SSE) +#include +#endif + +extern bool g_DarkStalkerStretch; +// For Darkstalkers hack. Ugh. +extern bool currentDialogActive; + +namespace Rasterizer { + +// Through mode, with the specific Darkstalker settings. +inline void DrawSinglePixel5551(u16 *pixel, const Vec4 &color_in) { + u32 new_color; + if (color_in.a() == 255) { + new_color = color_in.ToRGBA() & 0xFFFFFF; + } else { + const u32 old_color = RGBA5551ToRGBA8888(*pixel); + const Vec4 dst = Vec4::FromRGBA(old_color); + Vec3 blended = AlphaBlendingResult(color_in, dst); + // ToRGB() always automatically clamps. + new_color = blended.ToRGB(); + } + + new_color |= (*pixel & 0x8000) ? 0xff000000 : 0x00000000; + *pixel = RGBA8888ToRGBA5551(new_color); +} + +static inline Vec4 ModulateRGBA(const Vec4& prim_color, const Vec4& texcolor) { + Vec3 out_rgb; + int out_a; + +#if defined(_M_SSE) + // We can be accurate up to 24 bit integers, should be enough. + const __m128 p = _mm_cvtepi32_ps(prim_color.ivec); + const __m128 t = _mm_cvtepi32_ps(texcolor.ivec); + const __m128 b = _mm_mul_ps(p, t); + if (gstate.isColorDoublingEnabled()) { + // We double right here, only for modulate. Other tex funcs do not color double. + const __m128 doubleColor = _mm_setr_ps(2.0f / 255.0f, 2.0f / 255.0f, 2.0f / 255.0f, 1.0f / 255.0f); + out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, doubleColor)); + } else { + out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, _mm_set_ps1(1.0f / 255.0f))); + } + return Vec4(out_rgb.ivec); +#else + if (gstate.isColorDoublingEnabled()) { + out_rgb = (prim_color.rgb() * texcolor.rgb() * 2) / 255; + } else { + out_rgb = prim_color.rgb() * texcolor.rgb() / 255; + } + out_a = (prim_color.a() * texcolor.a() / 255); +#endif + + return Vec4(out_rgb.r(), out_rgb.g(), out_rgb.b(), out_a); + +} + +void DrawSprite(const VertexData& v0, const VertexData& v1) { + const u8 *texptr = nullptr; + + GETextureFormat texfmt = gstate.getTextureFormat(); + u32 texaddr = gstate.getTextureAddress(0); + int texbufw = GetTextureBufw(0, texaddr, texfmt); + if (Memory::IsValidAddress(texaddr)) + texptr = Memory::GetPointerUnchecked(texaddr); + + ScreenCoords pprime(v0.screenpos.x, v0.screenpos.y, 0); + Sampler::NearestFunc nearestFunc = Sampler::GetNearestFunc(); // Looks at gstate. + + DrawingCoords pos0 = TransformUnit::ScreenToDrawing(v0.screenpos); + DrawingCoords pos1 = TransformUnit::ScreenToDrawing(v1.screenpos); + + DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0); + DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0); + + int z = pos0.z; + float fog = 1.0f; + + bool isWhite = v0.color0 == Vec4(255, 255, 255, 255); + + if (gstate.isTextureMapEnabled()) { + // 1:1 (but with mirror support) texture mapping! + int s_start = v0.texturecoords.x; + int t_start = v0.texturecoords.y; + int ds = v1.texturecoords.x > v0.texturecoords.x ? 1 : -1; + int dt = v1.texturecoords.y > v0.texturecoords.y ? 1 : -1; + + if (ds < 0) { + s_start += ds; + } + if (dt < 0) { + t_start += dt; + } + + // First clip the right and bottom sides, since we don't need to adjust the deltas. + if (pos1.x > scissorBR.x) pos1.x = scissorBR.x + 1; + if (pos1.y > scissorBR.y) pos1.y = scissorBR.y + 1; + // Now clip the other sides. + if (pos0.x < scissorTL.x) { + s_start += (scissorTL.x - pos0.x) * ds; + pos0.x = scissorTL.x; + } + if (pos0.y < scissorTL.y) { + t_start += (scissorTL.y - pos0.y) * dt; + pos0.y = scissorTL.y; + } + + if (!gstate.isStencilTestEnabled() && + !gstate.isDepthTestEnabled() && + !gstate.isLogicOpEnabled() && + !gstate.isColorTestEnabled() && + !gstate.isDitherEnabled() && + gstate.isAlphaTestEnabled() && + gstate.getAlphaTestRef() == 0 && + gstate.getAlphaTestMask() == 0xFF && + gstate.isAlphaBlendEnabled() && + gstate.isTextureAlphaUsed() && + gstate.getTextureFunction() == GE_TEXFUNC_MODULATE && + gstate.getColorMask() == 0x000000 && + gstate.FrameBufFormat() == GE_FORMAT_5551) { + int t = t_start; + for (int y = pos0.y; y < pos1.y; y++) { + int s = s_start; + u16 *pixel = fb.Get16Ptr(pos0.x, y, gstate.FrameBufStride()); + if (isWhite) { + for (int x = pos0.x; x < pos1.x; x++) { + u32 tex_color = nearestFunc(s, t, texptr, texbufw, 0); + if (tex_color & 0xFF000000) { + DrawSinglePixel5551(pixel, Vec4::FromRGBA(tex_color)); + } + s += ds; + pixel++; + } + } else { + for (int x = pos0.x; x < pos1.x; x++) { + Vec4 prim_color = v0.color0; + Vec4 tex_color = Vec4::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0)); + prim_color = ModulateRGBA(prim_color, tex_color); + if (prim_color.a() > 0) { + DrawSinglePixel5551(pixel, prim_color); + } + s += ds; + pixel++; + } + } + t += dt; + } + } else { + int t = t_start; + for (int y = pos0.y; y < pos1.y; y++) { + int s = s_start; + // Not really that fast but faster than triangle. + for (int x = pos0.x; x < pos1.x; x++) { + Vec4 prim_color = v0.color0; + Vec4 tex_color = Vec4::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0)); + prim_color = GetTextureFunctionOutput(prim_color, tex_color); + DrawingCoords pos(x, y, z); + DrawSinglePixelNonClear(pos, (u16)z, 1.0f, prim_color); + s += ds; + } + t += dt; + } + } + } else { + if (pos1.x > scissorBR.x) pos1.x = scissorBR.x; + if (pos1.y > scissorBR.y) pos1.y = scissorBR.y; + if (pos0.x < scissorTL.x) pos0.x = scissorTL.x; + if (pos0.y < scissorTL.y) pos0.y = scissorTL.y; + if (!gstate.isStencilTestEnabled() && + !gstate.isDepthTestEnabled() && + !gstate.isLogicOpEnabled() && + !gstate.isColorTestEnabled() && + !gstate.isDitherEnabled() && + gstate.isAlphaTestEnabled() && + gstate.getAlphaTestRef() == 0 && + gstate.getAlphaTestMask() == 0xFF && + gstate.isAlphaBlendEnabled() && + gstate.isTextureAlphaUsed() && + gstate.getTextureFunction() == GE_TEXFUNC_MODULATE && + gstate.getColorMask() == 0x000000 && + gstate.FrameBufFormat() == GE_FORMAT_5551) { + if (v0.color0.a() == 0) + return; + + for (int y = pos0.y; y < pos1.y; y++) { + u16 *pixel = fb.Get16Ptr(pos0.x, y, gstate.FrameBufStride()); + for (int x = pos0.x; x < pos1.x; x++) { + Vec4 prim_color = v0.color0; + DrawSinglePixel5551(pixel, prim_color); + pixel++; + } + } + } else { + for (int y = pos0.y; y < pos1.y; y++) { + for (int x = pos0.x; x < pos1.x; x++) { + Vec4 prim_color = v0.color0; + DrawingCoords pos(x, y, z); + DrawSinglePixelNonClear(pos, (u16)z, fog, prim_color); + } + } + } + } +} + +bool needsClear = false; + +// Returns true if the normal path should be skipped. +bool RectangleFastPath(const VertexData &v0, const VertexData &v1) { + g_DarkStalkerStretch = false; + // Check for 1:1 texture mapping. In that case we can call DrawSprite. + int xdiff = v1.screenpos.x - v0.screenpos.x; + int ydiff = v1.screenpos.y - v0.screenpos.y; + int udiff = (v1.texturecoords.x - v0.texturecoords.x) * 16.0f; + int vdiff = (v1.texturecoords.y - v0.texturecoords.y) * 16.0f; + bool coord_check = + (xdiff == udiff || xdiff == -udiff) && + (ydiff == vdiff || ydiff == -vdiff); + bool state_check = !gstate.isModeClear(); // TODO: Add support for clear modes in Rasterizer::DrawSprite. + if ((coord_check || !gstate.isTextureMapEnabled()) && state_check) { + Rasterizer::DrawSprite(v0, v1); + return true; + } + + // Eliminate the stretch blit in DarkStalkers. + // We compensate for that when blitting the framebuffer in SoftGpu.cpp. + if (PSP_CoreParameter().compat.flags().DarkStalkersPresentHack && v0.texturecoords.x == 64.0f && v0.texturecoords.y == 16.0f && v1.texturecoords.x == 448.0f && v1.texturecoords.y == 240.0f) { + if (v0.screenpos.x == 0x7100 && v0.screenpos.y == 0x7780 && v1.screenpos.x == 0x8f00 && v1.screenpos.y == 0x8880) { + // Also check for save/load dialog. + if (!currentDialogActive) { + g_DarkStalkerStretch = true; + if (needsClear) { + needsClear = false; + // Afterwards, we also need to clear the actual destination. Can do a fast rectfill. + gstate.textureMapEnable &= ~1; + VertexData newV0 = v0; + newV0.color0 = Vec4(0, 0, 0, 255); + Rasterizer::DrawSprite(newV0, v1); + gstate.textureMapEnable |= 1; + } + return true; + } else { + needsClear = true; + } + } // else, handle the Capcom screen stretch, or the non-wide stretch? Or let's just not bother. + } + return false; +} + +bool DetectRectangleFromThroughModeStrip(const VertexData data[4]) { + // OK, now let's look at data to detect rectangles. There are a few possibilities + // but we focus on Darkstalkers for now. + if (data[0].screenpos.x == data[1].screenpos.x && + data[0].screenpos.y == data[2].screenpos.y && + data[2].screenpos.x == data[3].screenpos.x && + data[1].screenpos.y == data[3].screenpos.y && + data[1].screenpos.y > data[0].screenpos.y && // Avoid rotation handling + data[2].screenpos.x > data[0].screenpos.x && + data[0].texturecoords.x == data[1].texturecoords.x && + data[0].texturecoords.y == data[2].texturecoords.y && + data[2].texturecoords.x == data[3].texturecoords.x && + data[1].texturecoords.y == data[3].texturecoords.y && + data[1].texturecoords.y > data[0].texturecoords.y && + data[2].texturecoords.x > data[0].texturecoords.x && + data[0].color0 == data[1].color0 && + data[1].color0 == data[2].color0 && + data[2].color0 == data[3].color0) { + // It's a rectangle! + return true; + } + // There's the other vertex order too... + if (data[0].screenpos.x == data[2].screenpos.x && + data[0].screenpos.y == data[1].screenpos.y && + data[1].screenpos.x == data[3].screenpos.x && + data[2].screenpos.y == data[3].screenpos.y && + data[2].screenpos.y > data[0].screenpos.y && // Avoid rotation handling + data[1].screenpos.x > data[0].screenpos.x && + data[0].texturecoords.x == data[2].texturecoords.x && + data[0].texturecoords.y == data[1].texturecoords.y && + data[1].texturecoords.x == data[3].texturecoords.x && + data[2].texturecoords.y == data[3].texturecoords.y && + data[2].texturecoords.y > data[0].texturecoords.y && + data[1].texturecoords.x > data[0].texturecoords.x && + data[0].color0 == data[1].color0 && + data[1].color0 == data[2].color0 && + data[2].color0 == data[3].color0) { + // It's a rectangle! + return true; + } + return false; +} + + +} // namespace Rasterizer + diff --git a/GPU/Software/RasterizerRectangle.h b/GPU/Software/RasterizerRectangle.h new file mode 100644 index 0000000000..c3487340eb --- /dev/null +++ b/GPU/Software/RasterizerRectangle.h @@ -0,0 +1,20 @@ +#pragma once + +#include "TransformUnit.h" // for VertexData + +// Contains fast-paths for rectangles. Mainly for use in DarkStalkers which requires software +// rendering to work correctly, but will benefit other games as well. +// It also handles a bypass for DarkStalkers to avoid first stretching the image to 480x272. +// This greatly improves image quality and speeds things up a lot. Not happy about the grossness +// of the hack but it's just a huge waste of CPU and image quality not to do it. + +// The long term goal is to get rid of the specializations by jitting, but it still makes +// sense to specifically detect rectangles that do 1:1 texture mapping (like a sprite), because +// the JIT will then be able to eliminate UV interpolation. + +namespace Rasterizer { + // Returns true if the normal path should be skipped. + bool RectangleFastPath(const VertexData &v0, const VertexData &v1); + + bool DetectRectangleFromThroughModeStrip(const VertexData data[4]); +} diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index 82a8497684..0efb0903d4 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -27,6 +27,7 @@ #include "GPU/Software/TransformUnit.h" #include "GPU/Software/Clipper.h" #include "GPU/Software/Lighting.h" +#include "GPU/Software/RasterizerRectangle.h" #define TRANSFORM_BUF_SIZE (65536 * 48) @@ -451,44 +452,8 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy data[vtx] = ReadVertex(vreader); } - // OK, now let's look at data to detect rectangles. There are a few possibilities - // but we focus on Darkstalkers for now. - if (data[0].screenpos.x == data[1].screenpos.x && - data[0].screenpos.y == data[2].screenpos.y && - data[2].screenpos.x == data[3].screenpos.x && - data[1].screenpos.y == data[3].screenpos.y && - data[1].screenpos.y > data[0].screenpos.y && // Avoid rotation handling - data[2].screenpos.x > data[0].screenpos.x && - data[0].texturecoords.x == data[1].texturecoords.x && - data[0].texturecoords.y == data[2].texturecoords.y && - data[2].texturecoords.x == data[3].texturecoords.x && - data[1].texturecoords.y == data[3].texturecoords.y && - data[1].texturecoords.y > data[0].texturecoords.y && - data[2].texturecoords.x > data[0].texturecoords.x && - data[0].color0 == data[1].color0 && - data[1].color0 == data[2].color0 && - data[2].color0 == data[3].color0) { - // It's a rectangle! - Clipper::ProcessRect(data[0], data[3]); - break; - } - // There's the other vertex order too... - if (data[0].screenpos.x == data[2].screenpos.x && - data[0].screenpos.y == data[1].screenpos.y && - data[1].screenpos.x == data[3].screenpos.x && - data[2].screenpos.y == data[3].screenpos.y && - data[2].screenpos.y > data[0].screenpos.y && // Avoid rotation handling - data[1].screenpos.x > data[0].screenpos.x && - data[0].texturecoords.x == data[2].texturecoords.x && - data[0].texturecoords.y == data[1].texturecoords.y && - data[1].texturecoords.x == data[3].texturecoords.x && - data[2].texturecoords.y == data[3].texturecoords.y && - data[2].texturecoords.y > data[0].texturecoords.y && - data[1].texturecoords.x > data[0].texturecoords.x && - data[0].color0 == data[1].color0 && - data[1].color0 == data[2].color0 && - data[2].color0 == data[3].color0) { - // It's a rectangle! + // If a strip is effectively a rectangle, draw it as such! + if (Rasterizer::DetectRectangleFromThroughModeStrip(data)) { Clipper::ProcessRect(data[0], data[3]); break; } diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 73a9f1ae43..f524469964 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -429,6 +429,7 @@ + @@ -487,6 +488,7 @@ + @@ -522,4 +524,4 @@ - \ No newline at end of file + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 3853fa124a..00b2c74ea9 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -265,6 +265,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Software/Clipper.cpp \ $(SRC)/GPU/Software/Lighting.cpp \ $(SRC)/GPU/Software/Rasterizer.cpp.arm \ + $(SRC)/GPU/Software/RasterizerRectangle.cpp.arm \ $(SRC)/GPU/Software/Sampler.cpp \ $(SRC)/GPU/Software/SoftGpu.cpp \ $(SRC)/GPU/Software/TransformUnit.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 0d331ced8d..89f7880c1d 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -188,6 +188,7 @@ SOURCES_CXX += \ $(GPUDIR)/Software/Clipper.cpp \ $(GPUDIR)/Software/Lighting.cpp \ $(GPUDIR)/Software/Rasterizer.cpp \ + $(GPUDIR)/Software/RasterizerRectangle.cpp \ $(GPUDIR)/GLES/DepalettizeShaderGLES.cpp \ $(GPUDIR)/GLES/DepthBufferGLES.cpp \ $(GPUDIR)/GLES/VertexShaderGeneratorGLES.cpp \