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 \