From e4bc3c33426d6e0968d64ddb84a41966b3c37f19 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 24 Feb 2020 19:43:57 -0300
Subject: [PATCH] gl_rasterizer: Implement polygon modes and fill rectangles

---
 src/video_core/engines/maxwell_3d.h           | 22 +++++++++-
 .../renderer_opengl/gl_rasterizer.cpp         | 40 +++++++++++++++++++
 .../renderer_opengl/gl_rasterizer.h           |  3 ++
 .../renderer_opengl/gl_state_tracker.cpp      | 10 +++++
 .../renderer_opengl/gl_state_tracker.h        | 11 +++++
 .../renderer_opengl/maxwell_to_gl.h           | 13 ++++++
 .../renderer_opengl/renderer_opengl.cpp       |  2 +
 7 files changed, 99 insertions(+), 2 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 491cff3707..ed7fc8fdd9 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -524,6 +524,12 @@ public:
             FractionalEven = 2,
         };
 
+        enum class PolygonMode : u32 {
+            Point = 0x1b00,
+            Line = 0x1b01,
+            Fill = 0x1b02,
+        };
+
         struct RenderTargetConfig {
             u32 address_high;
             u32 address_low;
@@ -705,7 +711,12 @@ public:
 
                 s32 clear_stencil;
 
-                INSERT_UNION_PADDING_WORDS(0x7);
+                INSERT_UNION_PADDING_WORDS(0x2);
+
+                PolygonMode polygon_mode_front;
+                PolygonMode polygon_mode_back;
+
+                INSERT_UNION_PADDING_WORDS(0x3);
 
                 u32 polygon_offset_point_enable;
                 u32 polygon_offset_line_enable;
@@ -764,7 +775,11 @@ public:
                     BitField<12, 4, u32> viewport;
                 } clear_flags;
 
-                INSERT_UNION_PADDING_WORDS(0x19);
+                INSERT_UNION_PADDING_WORDS(0x10);
+
+                u32 fill_rectangle;
+
+                INSERT_UNION_PADDING_WORDS(0x8);
 
                 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
 
@@ -1422,6 +1437,8 @@ ASSERT_REG_POSITION(depth_mode, 0x35F);
 ASSERT_REG_POSITION(clear_color[0], 0x360);
 ASSERT_REG_POSITION(clear_depth, 0x364);
 ASSERT_REG_POSITION(clear_stencil, 0x368);
+ASSERT_REG_POSITION(polygon_mode_front, 0x36B);
+ASSERT_REG_POSITION(polygon_mode_back, 0x36C);
 ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370);
 ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371);
 ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372);
@@ -1435,6 +1452,7 @@ ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
 ASSERT_REG_POSITION(depth_bounds, 0x3E7);
 ASSERT_REG_POSITION(zeta, 0x3F8);
 ASSERT_REG_POSITION(clear_flags, 0x43E);
+ASSERT_REG_POSITION(fill_rectangle, 0x44F);
 ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
 ASSERT_REG_POSITION(rt_control, 0x487);
 ASSERT_REG_POSITION(zeta_width, 0x48a);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 55324e6d5a..90124e1149 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -487,6 +487,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
 
     SyncViewport();
     SyncRasterizeEnable();
+    SyncPolygonModes();
     SyncColorMask();
     SyncFragmentColorClampState();
     SyncMultiSampleState();
@@ -1097,6 +1098,45 @@ void RasterizerOpenGL::SyncRasterizeEnable() {
     oglEnable(GL_RASTERIZER_DISCARD, gpu.regs.rasterize_enable == 0);
 }
 
+void RasterizerOpenGL::SyncPolygonModes() {
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::PolygonModes]) {
+        return;
+    }
+    flags[Dirty::PolygonModes] = false;
+
+    if (gpu.regs.fill_rectangle) {
+        if (!GLAD_GL_NV_fill_rectangle) {
+            LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported");
+            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+            return;
+        }
+
+        flags[Dirty::PolygonModeFront] = true;
+        flags[Dirty::PolygonModeBack] = true;
+        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL_RECTANGLE_NV);
+        return;
+    }
+
+    if (gpu.regs.polygon_mode_front == gpu.regs.polygon_mode_back) {
+        flags[Dirty::PolygonModeFront] = false;
+        flags[Dirty::PolygonModeBack] = false;
+        glPolygonMode(GL_FRONT_AND_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front));
+        return;
+    }
+
+    if (flags[Dirty::PolygonModeFront]) {
+        flags[Dirty::PolygonModeFront] = false;
+        glPolygonMode(GL_FRONT, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front));
+    }
+
+    if (flags[Dirty::PolygonModeBack]) {
+        flags[Dirty::PolygonModeBack] = false;
+        glPolygonMode(GL_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_back));
+    }
+}
+
 void RasterizerOpenGL::SyncColorMask() {
     auto& gpu = system.GPU().Maxwell3D();
     auto& flags = gpu.dirty.flags;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b24c6661b4..e5681d6df6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -178,6 +178,9 @@ private:
     /// Syncs the rasterizer enable state to match the guest state
     void SyncRasterizeEnable();
 
+    /// Syncs polygon modes to match the guest state
+    void SyncPolygonModes();
+
     /// Syncs Color Mask
     void SyncColorMask();
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 1e43c9ec00..3f3bdf812c 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -94,6 +94,15 @@ void SetupDirtyShaders(Tables& tables) {
               Shaders);
 }
 
+void SetupDirtyPolygonModes(Tables& tables) {
+    tables[0][OFF(polygon_mode_front)] = PolygonModeFront;
+    tables[0][OFF(polygon_mode_back)] = PolygonModeBack;
+
+    tables[1][OFF(polygon_mode_front)] = PolygonModes;
+    tables[1][OFF(polygon_mode_back)] = PolygonModes;
+    tables[0][OFF(fill_rectangle)] = PolygonModes;
+}
+
 void SetupDirtyDepthTest(Tables& tables) {
     auto& table = tables[0];
     table[OFF(depth_test_enable)] = DepthTest;
@@ -211,6 +220,7 @@ void StateTracker::Initialize() {
     SetupDirtyVertexArrays(tables);
     SetupDirtyVertexFormat(tables);
     SetupDirtyShaders(tables);
+    SetupDirtyPolygonModes(tables);
     SetupDirtyDepthTest(tables);
     SetupDirtyStencilTest(tables);
     SetupDirtyAlphaTest(tables);
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index e084829111..b882d75c3a 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -59,6 +59,10 @@ enum : u8 {
     Shaders,
     ClipDistances,
 
+    PolygonModes,
+    PolygonModeFront,
+    PolygonModeBack,
+
     ColorMask,
     FrontFace,
     CullTest,
@@ -111,6 +115,13 @@ public:
         flags[OpenGL::Dirty::VertexInstance0 + 1] = true;
     }
 
+    void NotifyPolygonModes() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::PolygonModes] = true;
+        flags[OpenGL::Dirty::PolygonModeFront] = true;
+        flags[OpenGL::Dirty::PolygonModeBack] = true;
+    }
+
     void NotifyViewport0() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[OpenGL::Dirty::Viewports] = true;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 494e38e7a0..89f0e04ef5 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -488,5 +488,18 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) {
     return GL_COPY;
 }
 
+inline GLenum PolygonMode(Maxwell::PolygonMode polygon_mode) {
+    switch (polygon_mode) {
+    case Maxwell::PolygonMode::Point:
+        return GL_POINT;
+    case Maxwell::PolygonMode::Line:
+        return GL_LINE;
+    case Maxwell::PolygonMode::Fill:
+        return GL_FILL;
+    }
+    UNREACHABLE_MSG("Invalid polygon mode={}", static_cast<int>(polygon_mode));
+    return GL_FILL;
+}
+
 } // namespace MaxwellToGL
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index a51410660d..d03b29c22e 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -577,6 +577,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
 
     // TODO: Signal state tracker about these changes
     state_tracker.NotifyScreenDrawVertexArray();
+    state_tracker.NotifyPolygonModes();
     state_tracker.NotifyViewport0();
     state_tracker.NotifyScissor0();
     state_tracker.NotifyColorMask0();
@@ -612,6 +613,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glDisable(GL_ALPHA_TEST);
     glDisablei(GL_BLEND, 0);
     glDisablei(GL_SCISSOR_TEST, 0);
+    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);
     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);