From f019817f8f6452dec784d8217ef102231319d26c Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 30 Oct 2019 00:23:09 -0300
Subject: [PATCH 1/5] gl_rasterizer: Emulate viewport flipping with
 ARB_clip_control

Emulates negative y viewports with ARB_clip_control. This allows us to
more easily emulated pipelines with tessellation and/or geometry shader
stages. It also avoids corrupting games with transform feedbacks and
negative viewports (gl_Position.y was being modified).
---
 .../renderer_opengl/gl_rasterizer.cpp         | 48 +++++++++++--------
 .../renderer_opengl/gl_shader_decompiler.cpp  |  8 +---
 .../renderer_opengl/gl_shader_gen.cpp         | 24 ++--------
 .../renderer_opengl/gl_shader_manager.cpp     | 19 +-------
 .../renderer_opengl/gl_shader_manager.h       | 13 ++---
 src/video_core/renderer_opengl/gl_state.cpp   |  7 +++
 src/video_core/renderer_opengl/gl_state.h     |  5 ++
 src/yuzu/main.cpp                             |  3 ++
 .../emu_window/emu_window_sdl2_gl.cpp         |  2 +
 9 files changed, 55 insertions(+), 74 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e560d70d55..53ee754ff9 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -257,10 +257,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
             continue;
         }
 
-        const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
-
         GLShader::MaxwellUniformData ubo{};
-        ubo.SetFromRegs(gpu, stage);
+        ubo.SetFromRegs(gpu);
         const auto [buffer, offset] =
             buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment());
 
@@ -269,10 +267,11 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
 
         Shader shader{shader_cache.GetStageProgram(program)};
 
-        const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
-        SetupDrawConstBuffers(stage_enum, shader);
-        SetupDrawGlobalMemory(stage_enum, shader);
-        const auto texture_buffer_usage{SetupDrawTextures(stage_enum, shader, base_bindings)};
+        // Stage indices are 0 - 5
+        const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1);
+        SetupDrawConstBuffers(stage, shader);
+        SetupDrawGlobalMemory(stage, shader);
+        const auto texture_buffer_usage{SetupDrawTextures(stage, shader, base_bindings)};
 
         const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage};
         const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
@@ -1055,6 +1054,15 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
     }
     state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0;
     state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0;
+
+    bool flip_y = false;
+    if (regs.viewport_transform[0].scale_y < 0.0) {
+        flip_y = !flip_y;
+    }
+    if (regs.screen_y_control.y_negate != 0) {
+        flip_y = !flip_y;
+    }
+    state.clip_control.origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT;
 }
 
 void RasterizerOpenGL::SyncClipEnabled(
@@ -1077,26 +1085,24 @@ void RasterizerOpenGL::SyncClipCoef() {
 }
 
 void RasterizerOpenGL::SyncCullMode() {
-    auto& maxwell3d = system.GPU().Maxwell3D();
-
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = system.GPU().Maxwell3D().regs;
 
     state.cull.enabled = regs.cull.enabled != 0;
     if (state.cull.enabled) {
-        state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
         state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face);
+    }
 
-        const bool flip_triangles{regs.screen_y_control.triangle_rast_flip == 0 ||
-                                  regs.viewport_transform[0].scale_y < 0.0f};
+    state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
 
-        // If the GPU is configured to flip the rasterized triangles, then we need to flip the
-        // notion of front and back. Note: We flip the triangles when the value of the register is 0
-        // because OpenGL already does it for us.
-        if (flip_triangles) {
-            if (state.cull.front_face == GL_CCW)
-                state.cull.front_face = GL_CW;
-            else if (state.cull.front_face == GL_CW)
-                state.cull.front_face = GL_CCW;
+    // If the GPU is configured to flip the rasterized triangles, then we need to flip the
+    // notion of front and back.
+    const bool flip_triangles{regs.screen_y_control.triangle_rast_flip != 0 &&
+                              regs.viewport_transform[0].scale_y > 0.0f};
+    if (flip_triangles) {
+        if (state.cull.front_face == GL_CCW) {
+            state.cull.front_face = GL_CW;
+        } else if (state.cull.front_face == GL_CW) {
+            state.cull.front_face = GL_CCW;
         }
     }
 }
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 92ee8459e4..8dcbeefc09 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1872,10 +1872,6 @@ private:
     Expression EmitVertex(Operation operation) {
         ASSERT_MSG(stage == ProgramType::Geometry,
                    "EmitVertex is expected to be used in a geometry shader.");
-
-        // If a geometry shader is attached, it will always flip (it's the last stage before
-        // fragment). For more info about flipping, refer to gl_shader_gen.cpp.
-        code.AddLine("gl_Position.xy *= viewport_flip.xy;");
         code.AddLine("EmitVertex();");
         return {};
     }
@@ -1883,14 +1879,12 @@ private:
     Expression EndPrimitive(Operation operation) {
         ASSERT_MSG(stage == ProgramType::Geometry,
                    "EndPrimitive is expected to be used in a geometry shader.");
-
         code.AddLine("EndPrimitive();");
         return {};
     }
 
     Expression YNegate(Operation operation) {
-        // Config pack's third value is Y_NEGATE's state.
-        return {"config_pack[2]", Type::Uint};
+        return {"y_negate", Type::Float};
     }
 
     template <u32 element>
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 0e22eede90..af17216bdb 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -20,8 +20,7 @@ std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const
     std::string out = GetCommonDeclarations();
     out += R"(
 layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
-    vec4 viewport_flip;
-    uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
+    float y_direction;
 };
 
 )";
@@ -35,23 +34,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
 void main() {
     execute_vertex();
 )";
-
     if (ir_b) {
         out += "    execute_vertex_b();";
     }
-
-    out += R"(
-
-    // Set Position Y direction
-    gl_Position.y *= utof(config_pack[2]);
-    // Check if the flip stage is VertexB
-    // Config pack's second value is flip_stage
-    if (config_pack[1] == 1) {
-        // Viewport can be flipped, which is unsupported by glViewport
-        gl_Position.xy *= viewport_flip.xy;
-    }
-}
-)";
+    out += "}\n";
     return out;
 }
 
@@ -59,8 +45,7 @@ std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir) {
     std::string out = GetCommonDeclarations();
     out += R"(
 layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
-    vec4 viewport_flip;
-    uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
+    float y_direction;
 };
 
 )";
@@ -87,8 +72,7 @@ layout (location = 6) out vec4 FragColor6;
 layout (location = 7) out vec4 FragColor7;
 
 layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
-    vec4 viewport_flip;
-    uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
+    float y_direction;
 };
 
 )";
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index b05f90f20d..cd729d9786 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -40,27 +40,12 @@ void ProgramManager::UpdatePipeline() {
     old_state = current_state;
 }
 
-void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) {
+void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell) {
     const auto& regs = maxwell.regs;
     const auto& state = maxwell.state;
 
-    // TODO(bunnei): Support more than one viewport
-    viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
-    viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
-
-    instance_id = state.current_instance;
-
-    // Assign in which stage the position has to be flipped
-    // (the last stage before the fragment shader).
-    constexpr u32 geometry_index = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
-    if (maxwell.regs.shader_config[geometry_index].enable) {
-        flip_stage = geometry_index;
-    } else {
-        flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
-    }
-
     // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value.
-    y_direction = regs.screen_y_control.y_negate == 0 ? 1.f : -1.f;
+    y_direction = regs.screen_y_control.y_negate == 0 ? 1.0f : -1.0f;
 }
 
 } // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 6961e702a2..3703e7018d 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -18,17 +18,12 @@ namespace OpenGL::GLShader {
 /// @note Always keep a vec4 at the end. The GL spec is not clear whether the alignment at
 ///       the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
 ///       Not following that rule will cause problems on some AMD drivers.
-struct MaxwellUniformData {
-    void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell, std::size_t shader_stage);
+struct alignas(16) MaxwellUniformData {
+    void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell);
 
-    alignas(16) GLvec4 viewport_flip;
-    struct alignas(16) {
-        GLuint instance_id;
-        GLuint flip_stage;
-        GLfloat y_direction;
-    };
+    GLfloat y_direction;
 };
-static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect");
+static_assert(sizeof(MaxwellUniformData) == 16, "MaxwellUniformData structure size is incorrect");
 static_assert(sizeof(MaxwellUniformData) < 16384,
               "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index f25148362f..ccbe5912ea 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -410,6 +410,12 @@ void OpenGLState::ApplyAlphaTest() {
     }
 }
 
+void OpenGLState::ApplyClipControl() {
+    if (UpdateValue(cur_state.clip_control.origin, clip_control.origin)) {
+        glClipControl(clip_control.origin, GL_NEGATIVE_ONE_TO_ONE);
+    }
+}
+
 void OpenGLState::ApplyTextures() {
     if (const auto update = UpdateArray(cur_state.textures, textures)) {
         glBindTextures(update->first, update->second, textures.data() + update->first);
@@ -453,6 +459,7 @@ void OpenGLState::Apply() {
     ApplyImages();
     ApplyPolygonOffset();
     ApplyAlphaTest();
+    ApplyClipControl();
 }
 
 void OpenGLState::EmulateViewportWithScissor() {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index cca25206b2..eaff22bda0 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -146,6 +146,10 @@ public:
 
     std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
 
+    struct {
+        GLenum origin = GL_LOWER_LEFT;
+    } clip_control;
+
     OpenGLState();
 
     /// Get the currently active OpenGL state
@@ -182,6 +186,7 @@ public:
     void ApplyDepthClamp();
     void ApplyPolygonOffset();
     void ApplyAlphaTest();
+    void ApplyClipControl();
 
     /// Resets any references to the given resource
     OpenGLState& UnbindTexture(GLuint handle);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 160613ee15..867f8e9130 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -817,6 +817,9 @@ QStringList GMainWindow::GetUnsupportedGLExtensions() {
     if (!GLAD_GL_ARB_multi_bind) {
         unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
     }
+    if (!GLAD_GL_ARB_clip_control) {
+        unsupported_ext.append(QStringLiteral("ARB_clip_control"));
+    }
 
     // Extensions required to support some texture formats.
     if (!GLAD_GL_EXT_texture_compression_s3tc) {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index f91b071bf5..075a7074f1 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -62,6 +62,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
         unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
     if (!GLAD_GL_ARB_multi_bind)
         unsupported_ext.push_back("ARB_multi_bind");
+    if (!GLAD_GL_ARB_clip_control)
+        unsupported_ext.push_back("ARB_clip_control");
 
     // Extensions required to support some texture formats.
     if (!GLAD_GL_EXT_texture_compression_s3tc)

From 028b1a34a99a03babb464834f15d069fe2074d55 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 30 Oct 2019 00:32:05 -0300
Subject: [PATCH 2/5] yuzu_cmd: Use string_view instead of string for
 extensions

Avoids potential allocations due to the usage of std::string on strings
that we know at compile time. Most of these might fit in SSO, but it
adds complexity that can be easily avoided with string views.
---
 src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 075a7074f1..6fde694a2a 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -50,7 +50,7 @@ private:
 };
 
 bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
-    std::vector<std::string> unsupported_ext;
+    std::vector<std::string_view> unsupported_ext;
 
     if (!GLAD_GL_ARB_buffer_storage)
         unsupported_ext.push_back("ARB_buffer_storage");
@@ -73,8 +73,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
     if (!GLAD_GL_ARB_depth_buffer_float)
         unsupported_ext.push_back("ARB_depth_buffer_float");
 
-    for (const std::string& ext : unsupported_ext)
-        LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
+    for (const auto& extension : unsupported_ext)
+        LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", extension);
 
     return unsupported_ext.empty();
 }

From e2ea0c3e11d877ab9c93aaf8a8dc694b93a0aeb6 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 30 Oct 2019 00:40:56 -0300
Subject: [PATCH 3/5] gl_shader_manager: Remove unused variable in SetFromRegs

---
 src/video_core/renderer_opengl/gl_shader_manager.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index cd729d9786..75d3fac042 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -42,7 +42,6 @@ void ProgramManager::UpdatePipeline() {
 
 void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell) {
     const auto& regs = maxwell.regs;
-    const auto& state = maxwell.state;
 
     // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value.
     y_direction = regs.screen_y_control.y_negate == 0 ? 1.0f : -1.0f;

From f1facaeaef2d62b576c2b03590b76bf1025088e4 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 3 Nov 2019 14:46:53 -0300
Subject: [PATCH 4/5] gl_shader_decompiler: Fix typo "y_negate"->"y_direction"

---
 src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 8dcbeefc09..ce857d8dcb 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1884,7 +1884,7 @@ private:
     }
 
     Expression YNegate(Operation operation) {
-        return {"y_negate", Type::Float};
+        return {"y_direction", Type::Float};
     }
 
     template <u32 element>

From e9d2fad9849ff7fba8d63a16c838b79a3e752327 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 4 Nov 2019 01:54:35 -0300
Subject: [PATCH 5/5] gl_rasterizer: Remove front facing hack

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 53ee754ff9..0e9338ab67 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1093,18 +1093,6 @@ void RasterizerOpenGL::SyncCullMode() {
     }
 
     state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
-
-    // If the GPU is configured to flip the rasterized triangles, then we need to flip the
-    // notion of front and back.
-    const bool flip_triangles{regs.screen_y_control.triangle_rast_flip != 0 &&
-                              regs.viewport_transform[0].scale_y > 0.0f};
-    if (flip_triangles) {
-        if (state.cull.front_face == GL_CCW) {
-            state.cull.front_face = GL_CW;
-        } else if (state.cull.front_face == GL_CW) {
-            state.cull.front_face = GL_CCW;
-        }
-    }
 }
 
 void RasterizerOpenGL::SyncPrimitiveRestart() {