Merge pull request #12452 from hrydgard/softgpu-rectangle-cleanup

Isolate most of the softgpu specialization code to RasterizerRectangle.
This commit is contained in:
Unknown W. Brackets 2019-10-30 04:03:19 -07:00 committed by GitHub
commit 4a757db571
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 369 additions and 281 deletions

View File

@ -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

View File

@ -501,6 +501,7 @@
<ClInclude Include="Software\Clipper.h" />
<ClInclude Include="Software\Lighting.h" />
<ClInclude Include="Software\Rasterizer.h" />
<ClInclude Include="Software\RasterizerRectangle.h" />
<ClInclude Include="Software\Sampler.h" />
<ClInclude Include="Software\SoftGpu.h" />
<ClInclude Include="Software\TransformUnit.h" />
@ -690,6 +691,7 @@
<ClCompile Include="Software\Clipper.cpp" />
<ClCompile Include="Software\Lighting.cpp" />
<ClCompile Include="Software\Rasterizer.cpp" />
<ClCompile Include="Software\RasterizerRectangle.cpp" />
<ClCompile Include="Software\Sampler.cpp" />
<ClCompile Include="Software\SamplerX86.cpp" />
<ClCompile Include="Software\SoftGpu.cpp" />

View File

@ -282,6 +282,9 @@
<ClInclude Include="Debugger\RecordFormat.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="Software\RasterizerRectangle.h">
<Filter>Software</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Math3D.cpp">
@ -560,5 +563,8 @@
<ClCompile Include="Debugger\Playback.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Software\RasterizerRectangle.cpp">
<Filter>Software</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -17,20 +17,14 @@
#include <algorithm>
#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<int>(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;

View File

@ -545,7 +545,7 @@ static inline u32 ApplyLogicOp(GELogicOp op, u32 old_color, u32 new_color) {
return new_color;
}
static inline Vec4<int> GetTextureFunctionOutput(const Vec4<int>& prim_color, const Vec4<int>& texcolor)
Vec4<int> GetTextureFunctionOutput(const Vec4<int>& prim_color, const Vec4<int>& texcolor)
{
Vec3<int> out_rgb;
int out_a;
@ -779,7 +779,8 @@ static inline Vec3<int> GetDestFactor(const Vec4<int>& source, const Vec4<int>&
}
}
static inline Vec3<int> AlphaBlendingResult(const Vec4<int> &source, const Vec4<int> &dst)
// Removed inline here - it was never chosen to be inlined by the compiler anyway, too complex.
Vec3<int> AlphaBlendingResult(const Vec4<int> &source, const Vec4<int> &dst)
{
// Note: These factors cannot go below 0, but they can go above 255 when doubling.
Vec3<int> srcfactor = GetSourceFactor(source, dst);
@ -935,6 +936,10 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4<in
SetPixelColor(p.x, p.y, new_color);
}
void DrawSinglePixelNonClear(const DrawingCoords &p, u16 z, u8 fog, const Vec4<int> &color_in) {
DrawSinglePixel<false>(p, z, fog, color_in);
}
static inline void ApplyTexturing(Sampler::Funcs sampler, Vec4<int> &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<int> &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<int> dst = Vec4<int>::FromRGBA(old_color);
Vec3<int> 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<int> ModulateRGBA(const Vec4<int>& prim_color, const Vec4<int>& texcolor) {
Vec3<int> 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<int>(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<int>(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<int>(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<int>::FromRGBA(tex_color));
}
s += ds;
pixel++;
}
} else {
for (int x = pos0.x; x < pos1.x; x++) {
Vec4<int> prim_color = v0.color0;
Vec4<int> tex_color = Vec4<int>::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<int> prim_color = v0.color0;
Vec4<int> tex_color = Vec4<int>::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0));
prim_color = GetTextureFunctionOutput(prim_color, tex_color);
DrawingCoords pos(x, y, z);
DrawSinglePixel<false>(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<int> 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<int> prim_color = v0.color0;
DrawingCoords pos(x, y, z);
DrawSinglePixel<false>(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)
{

View File

@ -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<int> AlphaBlendingResult(const Vec4<int> &source, const Vec4<int> &dst);
void DrawSinglePixelNonClear(const DrawingCoords &p, u16 z, u8 fog, const Vec4<int> &color_in);
Vec4<int> GetTextureFunctionOutput(const Vec4<int>& prim_color, const Vec4<int>& texcolor);
} // namespace Rasterizer

View File

@ -0,0 +1,316 @@
// See comment in header for the purpose of this.
#include <algorithm>
#include <cmath>
#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 <emmintrin.h>
#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<int> &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<int> dst = Vec4<int>::FromRGBA(old_color);
Vec3<int> 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<int> ModulateRGBA(const Vec4<int>& prim_color, const Vec4<int>& texcolor) {
Vec3<int> 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<int>(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<int>(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<int>(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<int>::FromRGBA(tex_color));
}
s += ds;
pixel++;
}
} else {
for (int x = pos0.x; x < pos1.x; x++) {
Vec4<int> prim_color = v0.color0;
Vec4<int> tex_color = Vec4<int>::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<int> prim_color = v0.color0;
Vec4<int> tex_color = Vec4<int>::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<int> 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<int> 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<int>(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

View File

@ -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]);
}

View File

@ -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;
}

View File

@ -429,6 +429,7 @@
<ClInclude Include="..\..\GPU\Software\Clipper.h" />
<ClInclude Include="..\..\GPU\Software\Lighting.h" />
<ClInclude Include="..\..\GPU\Software\Rasterizer.h" />
<ClInclude Include="..\..\GPU\Software\RasterizerRectangle.h" />
<ClInclude Include="..\..\GPU\Software\Sampler.h" />
<ClInclude Include="..\..\GPU\Software\SoftGpu.h" />
<ClInclude Include="..\..\GPU\Software\TransformUnit.h" />
@ -487,6 +488,7 @@
<ClCompile Include="..\..\GPU\Software\Clipper.cpp" />
<ClCompile Include="..\..\GPU\Software\Lighting.cpp" />
<ClCompile Include="..\..\GPU\Software\Rasterizer.cpp" />
<ClCompile Include="..\..\GPU\Software\RasterizerRectangle.cpp" />
<ClCompile Include="..\..\GPU\Software\Sampler.cpp" />
<ClCompile Include="..\..\GPU\Software\SoftGpu.cpp" />
<ClCompile Include="..\..\GPU\Software\TransformUnit.cpp" />
@ -522,4 +524,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -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 \

View File

@ -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 \