diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp index 8c528989e..2822a3035 100644 --- a/src/video_core/pica.cpp +++ b/src/video_core/pica.cpp @@ -28,6 +28,7 @@ std::string Regs::GetCommandName(int index) { ADD_FIELD(viewport_size_y); ADD_FIELD(viewport_depth_range); ADD_FIELD(viewport_depth_far_plane); + ADD_FIELD(scissor_test); ADD_FIELD(viewport_corner); ADD_FIELD(texture0_enable); ADD_FIELD(texture0); diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 2f1b2dec4..6fc8b1bc5 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -112,7 +112,36 @@ struct Regs { BitField<24, 5, Semantic> map_w; } vs_output_attributes[7]; - INSERT_PADDING_WORDS(0x11); + INSERT_PADDING_WORDS(0xe); + + enum class ScissorMode : u32 { + Disabled = 0, + Exclude = 1, // Exclude pixels inside the scissor box + + Include = 3 // Exclude pixels outside the scissor box + }; + + struct { + BitField<0, 2, ScissorMode> mode; + + union { + BitField< 0, 16, u32> right; + BitField<16, 16, u32> bottom; + }; + + union { + BitField< 0, 16, u32> left; + BitField<16, 16, u32> top; + }; + + u32 GetWidth() const { + return left - right + 1; + } + + u32 GetHeight() const { + return top - bottom + 1; + } + } scissor_test; union { BitField< 0, 16, u32> x; @@ -986,6 +1015,7 @@ ASSERT_REG_POSITION(viewport_depth_range, 0x4d); ASSERT_REG_POSITION(viewport_depth_far_plane, 0x4e); ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); +ASSERT_REG_POSITION(scissor_test, 0x65); ASSERT_REG_POSITION(viewport_corner, 0x68); ASSERT_REG_POSITION(texture0_enable, 0x80); ASSERT_REG_POSITION(texture0, 0x81); diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index ecfdbc9e8..4249ae235 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -334,12 +334,25 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, return; } - // TODO: Proper scissor rect test! u16 min_x = std::min({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x}); u16 min_y = std::min({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); u16 max_x = std::max({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x}); u16 max_y = std::max({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); + // Convert the scissor box coordinates to 12.4 fixed point + u16 scissor_width = (u16)(regs.scissor_test.GetWidth() << 4); + u16 scissor_height = (u16)(regs.scissor_test.GetHeight() << 4); + u16 scissor_x = (u16)(regs.scissor_test.right << 4); + u16 scissor_y = (u16)(regs.scissor_test.bottom << 4); + + if (regs.scissor_test.mode == Regs::ScissorMode::Include) { + // Calculate the new bounds + min_x = std::max(min_x, scissor_x); + min_y = std::max(min_y, scissor_y); + max_x = std::min(max_x, (u16)(scissor_x + scissor_width)); + max_y = std::min(max_y, (u16)(scissor_y + scissor_height)); + } + min_x &= Fix12P4::IntMask(); min_y &= Fix12P4::IntMask(); max_x = ((max_x + Fix12P4::FracMask()) & Fix12P4::IntMask()); @@ -379,6 +392,13 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, for (u16 y = min_y + 8; y < max_y; y += 0x10) { for (u16 x = min_x + 8; x < max_x; x += 0x10) { + // Do not process the pixel if it's inside the scissor box and the scissor mode is set to Exclude + if (regs.scissor_test.mode == Regs::ScissorMode::Exclude) { + if (x >= scissor_x && x <= scissor_x + scissor_width && + y >= scissor_y && y <= scissor_y + scissor_height) + continue; + } + // Calculate the barycentric coordinates w0, w1 and w2 int w0 = bias0 + SignedArea(vtxpos[1].xy(), vtxpos[2].xy(), {x, y}); int w1 = bias1 + SignedArea(vtxpos[2].xy(), vtxpos[0].xy(), {x, y});