diff --git a/Source/Core/VideoBackends/Software/Clipper.cpp b/Source/Core/VideoBackends/Software/Clipper.cpp index f13ebb90b7..edae393350 100644 --- a/Source/Core/VideoBackends/Software/Clipper.cpp +++ b/Source/Core/VideoBackends/Software/Clipper.cpp @@ -307,7 +307,7 @@ void ProcessTriangle(OutputVertexData* v0, OutputVertexData* v1, OutputVertexDat PerspectiveDivide(v0); PerspectiveDivide(v1); PerspectiveDivide(v2); - Rasterizer::UpdateZSlope(v0, v1, v2); + Rasterizer::UpdateZSlope(v0, v1, v2, bpmem.scissorOffset.x * 2, bpmem.scissorOffset.y * 2); INCSTAT(g_stats.this_frame.num_triangles_culled) return; } @@ -320,7 +320,7 @@ void ProcessTriangle(OutputVertexData* v0, OutputVertexData* v1, OutputVertexDat PerspectiveDivide(v0); PerspectiveDivide(v2); PerspectiveDivide(v1); - Rasterizer::UpdateZSlope(v0, v2, v1); + Rasterizer::UpdateZSlope(v0, v2, v1, bpmem.scissorOffset.x * 2, bpmem.scissorOffset.y * 2); INCSTAT(g_stats.this_frame.num_triangles_culled) return; } @@ -533,10 +533,8 @@ void PerspectiveDivide(OutputVertexData* vertex) Vec3& screen = vertex->screenPosition; float wInverse = 1.0f / projected.w; - screen.x = - projected.x * wInverse * xfmem.viewport.wd + xfmem.viewport.xOrig - bpmem.scissorOffset.x * 2; - screen.y = - projected.y * wInverse * xfmem.viewport.ht + xfmem.viewport.yOrig - bpmem.scissorOffset.y * 2; + screen.x = projected.x * wInverse * xfmem.viewport.wd + xfmem.viewport.xOrig; + screen.y = projected.y * wInverse * xfmem.viewport.ht + xfmem.viewport.yOrig; screen.z = projected.z * wInverse * xfmem.viewport.zRange + xfmem.viewport.farZ; } } // namespace Clipper diff --git a/Source/Core/VideoBackends/Software/Rasterizer.cpp b/Source/Core/VideoBackends/Software/Rasterizer.cpp index 726692138c..d23b6fc558 100644 --- a/Source/Core/VideoBackends/Software/Rasterizer.cpp +++ b/Source/Core/VideoBackends/Software/Rasterizer.cpp @@ -5,11 +5,16 @@ #include #include +#include +#include "Common/Assert.h" #include "Common/CommonTypes.h" + #include "VideoBackends/Software/EfbInterface.h" #include "VideoBackends/Software/NativeVertexFormat.h" #include "VideoBackends/Software/Tev.h" +#include "VideoCommon/BPFunctions.h" +#include "VideoCommon/BPMemory.h" #include "VideoCommon/PerfQueryBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VideoCommon.h" @@ -23,14 +28,14 @@ static constexpr int BLOCK_SIZE = 2; struct SlopeContext { SlopeContext(const OutputVertexData* v0, const OutputVertexData* v1, const OutputVertexData* v2, - s32 x0, s32 y0) + s32 x0, s32 y0, s32 x_off, s32 y_off) : x0(x0), y0(y0) { // adjust a little less than 0.5 const float adjust = 0.495f; - xOff = ((float)x0 - v0->screenPosition.x) + adjust; - yOff = ((float)y0 - v0->screenPosition.y) + adjust; + xOff = ((float)x0 - (v0->screenPosition.x - x_off)) + adjust; + yOff = ((float)y0 - (v0->screenPosition.y - y_off)) + adjust; dx10 = v1->screenPosition.x - v0->screenPosition.x; dx20 = v2->screenPosition.x - v0->screenPosition.x; @@ -99,6 +104,8 @@ static Slope TexSlopes[8][3]; static Tev tev; static RasterBlock rasterBlock; +static std::vector scissors; + void Init() { tev.Init(); @@ -108,6 +115,11 @@ void Init() ZSlope = Slope(); } +void ScissorChanged() +{ + scissors = std::move(BPFunctions::ComputeScissorRects().m_result); +} + // Returns approximation of log2(f) in s28.4 // results are close enough to use for LOD static s32 FixedLog2(float f) @@ -302,37 +314,36 @@ static void BuildBlock(s32 blockX, s32 blockY) } void UpdateZSlope(const OutputVertexData* v0, const OutputVertexData* v1, - const OutputVertexData* v2) + const OutputVertexData* v2, s32 x_off, s32 y_off) { if (!bpmem.genMode.zfreeze) { - const s32 X1 = iround(16.0f * v0->screenPosition[0]) - 9; - const s32 Y1 = iround(16.0f * v0->screenPosition[1]) - 9; - const SlopeContext ctx(v0, v1, v2, (X1 + 0xF) >> 4, (Y1 + 0xF) >> 4); + const s32 X1 = iround(16.0f * (v0->screenPosition.x - x_off)) - 9; + const s32 Y1 = iround(16.0f * (v0->screenPosition.y - y_off)) - 9; + const SlopeContext ctx(v0, v1, v2, (X1 + 0xF) >> 4, (Y1 + 0xF) >> 4, x_off, y_off); ZSlope = Slope(v0->screenPosition.z, v1->screenPosition.z, v2->screenPosition.z, ctx); } } -void DrawTriangleFrontFace(const OutputVertexData* v0, const OutputVertexData* v1, - const OutputVertexData* v2) +static void DrawTriangleFrontFace(const OutputVertexData* v0, const OutputVertexData* v1, + const OutputVertexData* v2, + const BPFunctions::ScissorRect& scissor) { - INCSTAT(g_stats.this_frame.num_triangles_drawn); - // The zslope should be updated now, even if the triangle is rejected by the scissor test, as // zfreeze depends on it - UpdateZSlope(v0, v1, v2); + UpdateZSlope(v0, v1, v2, scissor.x_off, scissor.y_off); // adapted from http://devmaster.net/posts/6145/advanced-rasterization // 28.4 fixed-pou32 coordinates. rounded to nearest and adjusted to match hardware output // could also take floor and adjust -8 - const s32 Y1 = iround(16.0f * v0->screenPosition[1]) - 9; - const s32 Y2 = iround(16.0f * v1->screenPosition[1]) - 9; - const s32 Y3 = iround(16.0f * v2->screenPosition[1]) - 9; + const s32 Y1 = iround(16.0f * (v0->screenPosition.y - scissor.y_off)) - 9; + const s32 Y2 = iround(16.0f * (v1->screenPosition.y - scissor.y_off)) - 9; + const s32 Y3 = iround(16.0f * (v2->screenPosition.y - scissor.y_off)) - 9; - const s32 X1 = iround(16.0f * v0->screenPosition[0]) - 9; - const s32 X2 = iround(16.0f * v1->screenPosition[0]) - 9; - const s32 X3 = iround(16.0f * v2->screenPosition[0]) - 9; + const s32 X1 = iround(16.0f * (v0->screenPosition.x - scissor.x_off)) - 9; + const s32 X2 = iround(16.0f * (v1->screenPosition.x - scissor.x_off)) - 9; + const s32 X3 = iround(16.0f * (v2->screenPosition.x - scissor.x_off)) - 9; // Deltas const s32 DX12 = X1 - X2; @@ -359,35 +370,22 @@ void DrawTriangleFrontFace(const OutputVertexData* v0, const OutputVertexData* v s32 maxy = (std::max(std::max(Y1, Y2), Y3) + 0xF) >> 4; // scissor - s32 xoff = bpmem.scissorOffset.x * 2; - s32 yoff = bpmem.scissorOffset.y * 2; + ASSERT(scissor.rect.left >= 0); + ASSERT(scissor.rect.right <= EFB_WIDTH); + ASSERT(scissor.rect.top >= 0); + ASSERT(scissor.rect.bottom <= EFB_HEIGHT); - s32 scissorLeft = bpmem.scissorTL.x - xoff; - if (scissorLeft < 0) - scissorLeft = 0; - - s32 scissorTop = bpmem.scissorTL.y - yoff; - if (scissorTop < 0) - scissorTop = 0; - - s32 scissorRight = bpmem.scissorBR.x - xoff + 1; - if (scissorRight > s32(EFB_WIDTH)) - scissorRight = EFB_WIDTH; - - s32 scissorBottom = bpmem.scissorBR.y - yoff + 1; - if (scissorBottom > s32(EFB_HEIGHT)) - scissorBottom = EFB_HEIGHT; - - minx = std::max(minx, scissorLeft); - maxx = std::min(maxx, scissorRight); - miny = std::max(miny, scissorTop); - maxy = std::min(maxy, scissorBottom); + minx = std::max(minx, scissor.rect.left); + maxx = std::min(maxx, scissor.rect.right); + miny = std::max(miny, scissor.rect.top); + maxy = std::min(maxy, scissor.rect.bottom); if (minx >= maxx || miny >= maxy) return; // Set up the remaining slopes - const SlopeContext ctx(v0, v1, v2, (X1 + 0xF) >> 4, (Y1 + 0xF) >> 4); + const SlopeContext ctx(v0, v1, v2, (X1 + 0xF) >> 4, (Y1 + 0xF) >> 4, scissor.x_off, + scissor.y_off); float w[3] = {1.0f / v0->projectedPosition.w, 1.0f / v1->projectedPosition.w, 1.0f / v2->projectedPosition.w}; @@ -504,4 +502,13 @@ void DrawTriangleFrontFace(const OutputVertexData* v0, const OutputVertexData* v } } } + +void DrawTriangleFrontFace(const OutputVertexData* v0, const OutputVertexData* v1, + const OutputVertexData* v2) +{ + INCSTAT(g_stats.this_frame.num_triangles_drawn); + + for (const auto& scissor : scissors) + DrawTriangleFrontFace(v0, v1, v2, scissor); +} } // namespace Rasterizer diff --git a/Source/Core/VideoBackends/Software/Rasterizer.h b/Source/Core/VideoBackends/Software/Rasterizer.h index c278809966..5edd8f76af 100644 --- a/Source/Core/VideoBackends/Software/Rasterizer.h +++ b/Source/Core/VideoBackends/Software/Rasterizer.h @@ -10,9 +10,10 @@ struct OutputVertexData; namespace Rasterizer { void Init(); +void ScissorChanged(); void UpdateZSlope(const OutputVertexData* v0, const OutputVertexData* v1, - const OutputVertexData* v2); + const OutputVertexData* v2, s32 x_off, s32 y_off); void DrawTriangleFrontFace(const OutputVertexData* v0, const OutputVertexData* v1, const OutputVertexData* v2); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index e5b0f08987..76eea93068 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -12,6 +12,7 @@ #include "VideoBackends/Software/EfbCopy.h" #include "VideoBackends/Software/EfbInterface.h" +#include "VideoBackends/Software/Rasterizer.h" #include "VideoBackends/Software/SWBoundingBox.h" #include "VideoBackends/Software/SWOGLWindow.h" #include "VideoBackends/Software/SWTexture.h" @@ -179,4 +180,13 @@ SWRenderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { return std::make_unique(vtx_decl); } + +void SWRenderer::SetScissorRect(const MathUtil::Rectangle& rc) +{ + // BPFunctions calls SetScissorRect with the "best" scissor rect whenever the viewport or scissor + // changes. However, the software renderer is actually able to use multiple scissor rects (which + // is necessary in a few renderering edge cases, such as with Major Minor's Majestic March). + // Thus, we use this as a signal to update the list of scissor rects, but ignore the parameter. + Rasterizer::ScissorChanged(); +} } // namespace SW diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index dcedc1934d..8aa9aa4af5 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -58,6 +58,8 @@ public: const AbstractTexture* src_texture, const MathUtil::Rectangle& src_rect) override; + void SetScissorRect(const MathUtil::Rectangle& rc) override; + protected: std::unique_ptr CreateBoundingBox() const override;