Forráskód Böngészése

Merge pull request #1693 from Tinob/master

Missing ogl states
bunnei 7 éve
szülő
commit
6dc33fb812

+ 5 - 2
src/video_core/engines/maxwell_3d.cpp

@@ -34,8 +34,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
     // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
     // needed for ARMS.
     for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
-        regs.viewport[viewport].depth_range_near = 0.0f;
-        regs.viewport[viewport].depth_range_far = 1.0f;
+        regs.viewports[viewport].depth_range_near = 0.0f;
+        regs.viewports[viewport].depth_range_far = 1.0f;
     }
     // Doom and Bomberman seems to use the uninitialized registers and just enable blend
     // so initialize blend registers with sane values
@@ -66,6 +66,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
     regs.stencil_back_func_func = Regs::ComparisonOp::Always;
     regs.stencil_back_func_mask = 0xFFFFFFFF;
     regs.stencil_back_mask = 0xFFFFFFFF;
+    // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
+    // register carrying a default value. Assume it's OpenGL's default (1).
+    regs.point_size = 1.0f;
 }
 
 void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {

+ 79 - 63
src/video_core/engines/maxwell_3d.h

@@ -480,6 +480,67 @@ public:
             };
         };
 
+        struct ViewportTransform {
+            f32 scale_x;
+            f32 scale_y;
+            f32 scale_z;
+            f32 translate_x;
+            f32 translate_y;
+            f32 translate_z;
+            INSERT_PADDING_WORDS(2);
+
+            MathUtil::Rectangle<s32> GetRect() const {
+                return {
+                    GetX(),               // left
+                    GetY() + GetHeight(), // top
+                    GetX() + GetWidth(),  // right
+                    GetY()                // bottom
+                };
+            };
+
+            s32 GetX() const {
+                return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
+            }
+
+            s32 GetY() const {
+                return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
+            }
+
+            s32 GetWidth() const {
+                return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
+            }
+
+            s32 GetHeight() const {
+                return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
+            }
+        };
+
+        struct ScissorTest {
+            u32 enable;
+            union {
+                BitField<0, 16, u32> min_x;
+                BitField<16, 16, u32> max_x;
+            };
+            union {
+                BitField<0, 16, u32> min_y;
+                BitField<16, 16, u32> max_y;
+            };
+            u32 fill;
+        };
+
+        struct ViewPort {
+            union {
+                BitField<0, 16, u32> x;
+                BitField<16, 16, u32> width;
+            };
+            union {
+                BitField<0, 16, u32> y;
+                BitField<16, 16, u32> height;
+            };
+            float depth_range_near;
+            float depth_range_far;
+        };
+
         bool IsShaderConfigEnabled(std::size_t index) const {
             // The VertexB is always enabled.
             if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -505,55 +566,11 @@ public:
 
                 INSERT_PADDING_WORDS(0x2E);
 
-                RenderTargetConfig rt[NumRenderTargets];
-
-                struct {
-                    f32 scale_x;
-                    f32 scale_y;
-                    f32 scale_z;
-                    f32 translate_x;
-                    f32 translate_y;
-                    f32 translate_z;
-                    INSERT_PADDING_WORDS(2);
-
-                    MathUtil::Rectangle<s32> GetRect() const {
-                        return {
-                            GetX(),               // left
-                            GetY() + GetHeight(), // top
-                            GetX() + GetWidth(),  // right
-                            GetY()                // bottom
-                        };
-                    };
-
-                    s32 GetX() const {
-                        return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
-                    }
+                std::array<RenderTargetConfig, NumRenderTargets> rt;
 
-                    s32 GetY() const {
-                        return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
-                    }
+                std::array<ViewportTransform, NumViewports> viewport_transform;
 
-                    s32 GetWidth() const {
-                        return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
-                    }
-
-                    s32 GetHeight() const {
-                        return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
-                    }
-                } viewport_transform[NumViewports];
-
-                struct {
-                    union {
-                        BitField<0, 16, u32> x;
-                        BitField<16, 16, u32> width;
-                    };
-                    union {
-                        BitField<0, 16, u32> y;
-                        BitField<16, 16, u32> height;
-                    };
-                    float depth_range_near;
-                    float depth_range_far;
-                } viewport[NumViewports];
+                std::array<ViewPort, NumViewports> viewports;
 
                 INSERT_PADDING_WORDS(0x1D);
 
@@ -571,19 +588,9 @@ public:
 
                 INSERT_PADDING_WORDS(0x17);
 
-                struct {
-                    u32 enable;
-                    union {
-                        BitField<0, 16, u32> min_x;
-                        BitField<16, 16, u32> max_x;
-                    };
-                    union {
-                        BitField<0, 16, u32> min_y;
-                        BitField<16, 16, u32> max_y;
-                    };
-                } scissor_test;
+                std::array<ScissorTest, NumViewports> scissor_test;
 
-                INSERT_PADDING_WORDS(0x52);
+                INSERT_PADDING_WORDS(0x15);
 
                 s32 stencil_back_func_ref;
                 u32 stencil_back_mask;
@@ -700,7 +707,9 @@ public:
                 u32 stencil_front_func_mask;
                 u32 stencil_front_mask;
 
-                INSERT_PADDING_WORDS(0x3);
+                INSERT_PADDING_WORDS(0x2);
+
+                u32 frag_color_clamp;
 
                 union {
                     BitField<4, 1, u32> triangle_rast_flip;
@@ -718,7 +727,12 @@ public:
 
                 u32 zeta_enable;
 
-                INSERT_PADDING_WORDS(0x8);
+                union {
+                    BitField<0, 1, u32> alpha_to_coverage;
+                    BitField<4, 1, u32> alpha_to_one;
+                } multisample_control;
+
+                INSERT_PADDING_WORDS(0x7);
 
                 struct {
                     u32 tsc_address_high;
@@ -1100,8 +1114,8 @@ private:
 ASSERT_REG_POSITION(macros, 0x45);
 ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
 ASSERT_REG_POSITION(rt, 0x200);
-ASSERT_REG_POSITION(viewport_transform[0], 0x280);
-ASSERT_REG_POSITION(viewport, 0x300);
+ASSERT_REG_POSITION(viewport_transform, 0x280);
+ASSERT_REG_POSITION(viewports, 0x300);
 ASSERT_REG_POSITION(vertex_buffer, 0x35D);
 ASSERT_REG_POSITION(clear_color[0], 0x360);
 ASSERT_REG_POSITION(clear_depth, 0x364);
@@ -1136,10 +1150,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
 ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
 ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
 ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
+ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
 ASSERT_REG_POSITION(screen_y_control, 0x4EB);
 ASSERT_REG_POSITION(vb_element_base, 0x50D);
 ASSERT_REG_POSITION(point_size, 0x546);
 ASSERT_REG_POSITION(zeta_enable, 0x54E);
+ASSERT_REG_POSITION(multisample_control, 0x54F);
 ASSERT_REG_POSITION(tsc, 0x557);
 ASSERT_REG_POSITION(tic, 0x55D);
 ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);

+ 94 - 65
src/video_core/renderer_opengl/gl_rasterizer.cpp

@@ -580,6 +580,8 @@ void RasterizerOpenGL::DrawArrays() {
 
     ConfigureFramebuffers(state);
     SyncColorMask();
+    SyncFragmentColorClampState();
+    SyncMultiSampleState();
     SyncDepthTestState();
     SyncStencilTestState();
     SyncBlendState();
@@ -640,7 +642,7 @@ void RasterizerOpenGL::DrawArrays() {
     params.DispatchDraw();
 
     // Disable scissor test
-    state.scissor.enabled = false;
+    state.viewports[0].scissor.enabled = false;
 
     accelerate_draw = AccelDraw::Disabled;
 
@@ -731,9 +733,8 @@ void RasterizerOpenGL::SamplerInfo::Create() {
     glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
 }
 
-void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) {
+void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
     const GLuint s = sampler.handle;
-    const Tegra::Texture::TSCEntry& config = info.tsc;
     if (mag_filter != config.mag_filter) {
         mag_filter = config.mag_filter;
         glSamplerParameteri(
@@ -775,30 +776,50 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTex
                             MaxwellToGL::DepthCompareFunc(depth_compare_func));
     }
 
-    if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
-        wrap_p == Tegra::Texture::WrapMode::Border) {
-        const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
-                                          config.border_color_b, config.border_color_a}};
-        if (border_color != new_border_color) {
-            border_color = new_border_color;
-            glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
-        }
+    GLvec4 new_border_color;
+    if (config.srgb_conversion) {
+        new_border_color[0] = config.srgb_border_color_r / 255.0f;
+        new_border_color[1] = config.srgb_border_color_g / 255.0f;
+        new_border_color[2] = config.srgb_border_color_g / 255.0f;
+    } else {
+        new_border_color[0] = config.border_color_r;
+        new_border_color[1] = config.border_color_g;
+        new_border_color[2] = config.border_color_b;
     }
-    if (info.tic.use_header_opt_control == 0) {
+    new_border_color[3] = config.border_color_a;
+
+    if (border_color != new_border_color) {
+        border_color = new_border_color;
+        glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
+    }
+
+    const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value());
+    if (anisotropic_max != max_anisotropic) {
+        max_anisotropic = anisotropic_max;
         if (GLAD_GL_ARB_texture_filter_anisotropic) {
-            glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY,
-                                static_cast<float>(1 << info.tic.max_anisotropy.Value()));
+            glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
         } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
-            glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
-                                static_cast<float>(1 << info.tic.max_anisotropy.Value()));
+            glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
         }
-        glSamplerParameterf(s, GL_TEXTURE_MIN_LOD,
-                            static_cast<float>(info.tic.res_min_mip_level.Value()));
-        glSamplerParameterf(s, GL_TEXTURE_MAX_LOD,
-                            static_cast<float>(info.tic.res_max_mip_level.Value() == 0
-                                                   ? 16
-                                                   : info.tic.res_max_mip_level.Value()));
-        glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f);
+    }
+    const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f;
+    if (lod_min != min_lod) {
+        min_lod = lod_min;
+        glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod);
+    }
+
+    const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f;
+    if (lod_max != max_lod) {
+        max_lod = lod_max;
+        glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod);
+    }
+    const u32 bias = config.mip_lod_bias.Value();
+    // Sign extend the 13-bit value.
+    const u32 mask = 1U << (13 - 1);
+    const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
+    if (lod_bias != bias_lod) {
+        lod_bias = bias_lod;
+        glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias);
     }
 }
 
@@ -897,7 +918,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
             continue;
         }
 
-        texture_samplers[current_bindpoint].SyncWithConfig(texture);
+        texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
         Surface surface = res_cache.GetTextureSurface(texture, entry);
         if (surface != nullptr) {
             state.texture_units[current_bindpoint].texture = surface->Texture().handle;
@@ -921,15 +942,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
 
 void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
-    for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
+    for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
         const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
         auto& viewport = current_state.viewports[i];
         viewport.x = viewport_rect.left;
         viewport.y = viewport_rect.bottom;
         viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
         viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
-        viewport.depth_range_far = regs.viewport[i].depth_range_far;
-        viewport.depth_range_near = regs.viewport[i].depth_range_near;
+        viewport.depth_range_far = regs.viewports[i].depth_range_far;
+        viewport.depth_range_near = regs.viewports[i].depth_range_near;
     }
 }
 
@@ -1020,7 +1041,9 @@ void RasterizerOpenGL::SyncStencilTestState() {
 
 void RasterizerOpenGL::SyncColorMask() {
     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
-    for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
+    const std::size_t count =
+        regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
+    for (std::size_t i = 0; i < count; i++) {
         const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
         auto& dest = state.color_mask[i];
         dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
@@ -1030,6 +1053,17 @@ void RasterizerOpenGL::SyncColorMask() {
     }
 }
 
+void RasterizerOpenGL::SyncMultiSampleState() {
+    const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+    state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
+    state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
+}
+
+void RasterizerOpenGL::SyncFragmentColorClampState() {
+    const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+    state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
+}
+
 void RasterizerOpenGL::SyncBlendState() {
     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
 
@@ -1041,43 +1075,40 @@ void RasterizerOpenGL::SyncBlendState() {
     state.independant_blend.enabled = regs.independent_blend_enable;
     if (!state.independant_blend.enabled) {
         auto& blend = state.blend[0];
-        blend.enabled = regs.blend.enable[0] != 0;
-        blend.separate_alpha = regs.blend.separate_alpha;
-        blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb);
-        blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb);
-        blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb);
-        if (blend.separate_alpha) {
-            blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a);
-            blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a);
-            blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a);
+        const auto& src = regs.blend;
+        blend.enabled = src.enable[0] != 0;
+        if (blend.enabled) {
+            blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
+            blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
+            blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
+            blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
+            blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
+            blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
         }
-        for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
+        for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
             state.blend[i].enabled = false;
         }
         return;
     }
 
-    for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
+    for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
         auto& blend = state.blend[i];
+        const auto& src = regs.independent_blend[i];
         blend.enabled = regs.blend.enable[i] != 0;
         if (!blend.enabled)
             continue;
-        blend.separate_alpha = regs.independent_blend[i].separate_alpha;
-        blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb);
-        blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb);
-        blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb);
-        if (blend.separate_alpha) {
-            blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a);
-            blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a);
-            blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a);
-        }
+        blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
+        blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
+        blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
+        blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
+        blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
+        blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
     }
 }
 
 void RasterizerOpenGL::SyncLogicOpState() {
     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
 
-    // TODO(Subv): Support more than just render target 0.
     state.logic_op.enabled = regs.logic_op.enable != 0;
 
     if (!state.logic_op.enabled)
@@ -1090,19 +1121,21 @@ void RasterizerOpenGL::SyncLogicOpState() {
 }
 
 void RasterizerOpenGL::SyncScissorTest() {
-    // TODO: what is the correct behavior here, a single scissor for all targets
-    // or scissor disabled for the rest of the targets?
     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
-    state.scissor.enabled = (regs.scissor_test.enable != 0);
-    if (regs.scissor_test.enable == 0) {
-        return;
+    for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
+        const auto& src = regs.scissor_test[i];
+        auto& dst = state.viewports[i].scissor;
+        dst.enabled = (src.enable != 0);
+        if (dst.enabled == 0) {
+            return;
+        }
+        const u32 width = src.max_x - src.min_x;
+        const u32 height = src.max_y - src.min_y;
+        dst.x = src.min_x;
+        dst.y = src.min_y;
+        dst.width = width;
+        dst.height = height;
     }
-    const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
-    const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
-    state.scissor.x = regs.scissor_test.min_x;
-    state.scissor.y = regs.scissor_test.min_y;
-    state.scissor.width = width;
-    state.scissor.height = height;
 }
 
 void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1116,11 +1149,7 @@ void RasterizerOpenGL::SyncTransformFeedback() {
 
 void RasterizerOpenGL::SyncPointState() {
     const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
-
-    // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
-    // register carrying a default value. For now, if the point size is zero, assume it's
-    // OpenGL's default (1).
-    state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
+    state.point.size = regs.point_size;
 }
 
 void RasterizerOpenGL::CheckAlphaTests() {

+ 11 - 1
src/video_core/renderer_opengl/gl_rasterizer.h

@@ -88,7 +88,7 @@ private:
         /// SamplerInfo struct.
         void Create();
         /// Syncs the sampler object with the config, updating any necessary state.
-        void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info);
+        void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
 
     private:
         Tegra::Texture::TextureFilter mag_filter;
@@ -100,6 +100,10 @@ private:
         bool uses_depth_compare;
         Tegra::Texture::DepthCompareFunc depth_compare_func;
         GLvec4 border_color;
+        float min_lod;
+        float max_lod;
+        float lod_bias;
+        float max_anisotropic;
     };
 
     /**
@@ -160,6 +164,12 @@ private:
     /// Syncs the LogicOp state to match the guest state
     void SyncLogicOpState();
 
+    /// Syncs the the color clamp state
+    void SyncFragmentColorClampState();
+
+    /// Syncs the alpha coverage and alpha to one
+    void SyncMultiSampleState();
+
     /// Syncs the scissor test state to match the guest state
     void SyncScissorTest();
 

+ 1 - 0
src/video_core/renderer_opengl/gl_shader_manager.h

@@ -67,6 +67,7 @@ public:
         glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
         state.draw.shader_program = 0;
         state.draw.program_pipeline = pipeline.handle;
+        state.geometry_shaders.enabled = (gs != 0);
     }
 
 private:

+ 84 - 68
src/video_core/renderer_opengl/gl_state.cpp

@@ -14,7 +14,10 @@ OpenGLState OpenGLState::cur_state;
 bool OpenGLState::s_rgb_used;
 OpenGLState::OpenGLState() {
     // These all match default OpenGL values
+    geometry_shaders.enabled = false;
     framebuffer_srgb.enabled = false;
+    multisample_control.alpha_to_coverage = false;
+    multisample_control.alpha_to_one = false;
     cull.enabled = false;
     cull.mode = GL_BACK;
     cull.front_face = GL_CCW;
@@ -50,12 +53,12 @@ OpenGLState::OpenGLState() {
         item.height = 0;
         item.depth_range_near = 0.0f;
         item.depth_range_far = 1.0f;
+        item.scissor.enabled = false;
+        item.scissor.x = 0;
+        item.scissor.y = 0;
+        item.scissor.width = 0;
+        item.scissor.height = 0;
     }
-    scissor.enabled = false;
-    scissor.x = 0;
-    scissor.y = 0;
-    scissor.width = 0;
-    scissor.height = 0;
     for (auto& item : blend) {
         item.enabled = true;
         item.rgb_equation = GL_FUNC_ADD;
@@ -88,6 +91,7 @@ OpenGLState::OpenGLState() {
     clip_distance = {};
 
     point.size = 1;
+    fragment_color_clamp.enabled = false;
 }
 
 void OpenGLState::ApplyDefaultState() {
@@ -136,7 +140,7 @@ void OpenGLState::ApplyCulling() const {
 }
 
 void OpenGLState::ApplyColorMask() const {
-    if (GLAD_GL_ARB_viewport_array) {
+    if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) {
         for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
             const auto& updated = color_mask[i];
             const auto& current = cur_state.color_mask[i];
@@ -230,26 +234,10 @@ void OpenGLState::ApplyStencilTest() const {
     }
 }
 
-void OpenGLState::ApplyScissor() const {
-    const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
-    if (scissor_changed) {
-        if (scissor.enabled) {
-            glEnable(GL_SCISSOR_TEST);
-        } else {
-            glDisable(GL_SCISSOR_TEST);
-        }
-    }
-    if (scissor.enabled &&
-        (scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
-         scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) {
-        glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
-    }
-}
-
 void OpenGLState::ApplyViewport() const {
-    if (GLAD_GL_ARB_viewport_array) {
-        for (GLuint i = 0;
-             i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) {
+    if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
+        for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports);
+             i++) {
             const auto& current = cur_state.viewports[i];
             const auto& updated = viewports[i];
             if (updated.x != current.x || updated.y != current.y ||
@@ -260,6 +248,22 @@ void OpenGLState::ApplyViewport() const {
                 updated.depth_range_far != current.depth_range_far) {
                 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
             }
+            const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
+            if (scissor_changed) {
+                if (updated.scissor.enabled) {
+                    glEnablei(GL_SCISSOR_TEST, i);
+                } else {
+                    glDisablei(GL_SCISSOR_TEST, i);
+                }
+            }
+            if (updated.scissor.enabled &&
+                (scissor_changed || updated.scissor.x != current.scissor.x ||
+                 updated.scissor.y != current.scissor.y ||
+                 updated.scissor.width != current.scissor.width ||
+                 updated.scissor.height != current.scissor.height)) {
+                glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
+                                 updated.scissor.height);
+            }
         }
     } else {
         const auto& current = cur_state.viewports[0];
@@ -273,6 +277,21 @@ void OpenGLState::ApplyViewport() const {
             updated.depth_range_far != current.depth_range_far) {
             glDepthRange(updated.depth_range_near, updated.depth_range_far);
         }
+        const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
+        if (scissor_changed) {
+            if (updated.scissor.enabled) {
+                glEnable(GL_SCISSOR_TEST);
+            } else {
+                glDisable(GL_SCISSOR_TEST);
+            }
+        }
+        if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x ||
+                                        updated.scissor.y != current.scissor.y ||
+                                        updated.scissor.width != current.scissor.width ||
+                                        updated.scissor.height != current.scissor.height)) {
+            glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
+                      updated.scissor.height);
+        }
     }
 }
 
@@ -290,27 +309,16 @@ void OpenGLState::ApplyGlobalBlending() const {
     if (!updated.enabled) {
         return;
     }
-    if (updated.separate_alpha) {
-        if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
-            updated.dst_rgb_func != current.dst_rgb_func ||
-            updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) {
-            glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
-                                updated.dst_a_func);
-        }
-
-        if (blend_changed || updated.rgb_equation != current.rgb_equation ||
-            updated.a_equation != current.a_equation) {
-            glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
-        }
-    } else {
-        if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
-            updated.dst_rgb_func != current.dst_rgb_func) {
-            glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func);
-        }
+    if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
+        updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
+        updated.dst_a_func != current.dst_a_func) {
+        glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
+                            updated.dst_a_func);
+    }
 
-        if (blend_changed || updated.rgb_equation != current.rgb_equation) {
-            glBlendEquation(updated.rgb_equation);
-        }
+    if (blend_changed || updated.rgb_equation != current.rgb_equation ||
+        updated.a_equation != current.a_equation) {
+        glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
     }
 }
 
@@ -328,29 +336,17 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
     if (!updated.enabled) {
         return;
     }
-    if (updated.separate_alpha) {
-        if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
-            updated.dst_rgb_func != current.dst_rgb_func ||
-            updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) {
-            glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
-                                    updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
-        }
-
-        if (blend_changed || updated.rgb_equation != current.rgb_equation ||
-            updated.a_equation != current.a_equation) {
-            glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
-                                        updated.a_equation);
-        }
-    } else {
-        if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
-            updated.dst_rgb_func != current.dst_rgb_func) {
-            glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func,
-                            updated.dst_rgb_func);
-        }
+    if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
+        updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
+        updated.dst_a_func != current.dst_a_func) {
+        glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
+                                updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
+    }
 
-        if (blend_changed || updated.rgb_equation != current.rgb_equation) {
-            glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation);
-        }
+    if (blend_changed || updated.rgb_equation != current.rgb_equation ||
+        updated.a_equation != current.a_equation) {
+        glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
+                                    updated.a_equation);
     }
 }
 
@@ -481,9 +477,29 @@ void OpenGLState::Apply() const {
     if (point.size != cur_state.point.size) {
         glPointSize(point.size);
     }
+    if (GLAD_GL_ARB_color_buffer_float) {
+        if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
+            glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
+                         fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
+        }
+    }
+    if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
+        if (multisample_control.alpha_to_coverage) {
+            glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+        } else {
+            glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+        }
+    }
+    if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
+        if (multisample_control.alpha_to_one) {
+            glEnable(GL_SAMPLE_ALPHA_TO_ONE);
+        } else {
+            glDisable(GL_SAMPLE_ALPHA_TO_ONE);
+        }
+    }
+
     ApplyColorMask();
     ApplyViewport();
-    ApplyScissor();
     ApplyStencilTest();
     ApplySRgb();
     ApplyCulling();

+ 21 - 11
src/video_core/renderer_opengl/gl_state.h

@@ -39,6 +39,19 @@ public:
         bool enabled; // GL_FRAMEBUFFER_SRGB
     } framebuffer_srgb;
 
+    struct {
+        bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE
+        bool alpha_to_one;      // GL_ALPHA_TO_ONE
+    } multisample_control;
+
+    struct {
+        bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB
+    } fragment_color_clamp;
+
+    struct {
+        bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
+    } geometry_shaders;
+
     struct {
         bool enabled;      // GL_CULL_FACE
         GLenum mode;       // GL_CULL_FACE_MODE
@@ -79,7 +92,6 @@ public:
 
     struct Blend {
         bool enabled;        // GL_BLEND
-        bool separate_alpha; // Independent blend enabled
         GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
         GLenum a_equation;   // GL_BLEND_EQUATION_ALPHA
         GLenum src_rgb_func; // GL_BLEND_SRC_RGB
@@ -150,16 +162,15 @@ public:
         GLfloat height;
         GLfloat depth_range_near; // GL_DEPTH_RANGE
         GLfloat depth_range_far;  // GL_DEPTH_RANGE
+        struct {
+            bool enabled; // GL_SCISSOR_TEST
+            GLint x;
+            GLint y;
+            GLsizei width;
+            GLsizei height;
+        } scissor;
     };
-    std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports;
-
-    struct {
-        bool enabled; // GL_SCISSOR_TEST
-        GLint x;
-        GLint y;
-        GLsizei width;
-        GLsizei height;
-    } scissor;
+    std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
 
     struct {
         float size; // GL_POINT_SIZE
@@ -214,7 +225,6 @@ private:
     void ApplyLogicOp() const;
     void ApplyTextures() const;
     void ApplySamplers() const;
-    void ApplyScissor() const;
 };
 
 } // namespace OpenGL

+ 6 - 0
src/video_core/renderer_opengl/maxwell_to_gl.h

@@ -180,6 +180,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
         return GL_CLAMP_TO_BORDER;
     case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
         return GL_MIRROR_CLAMP_TO_EDGE;
+    case Tegra::Texture::WrapMode::MirrorOnceBorder:
+        if (GL_EXT_texture_mirror_clamp) {
+            return GL_MIRROR_CLAMP_TO_BORDER_EXT;
+        } else {
+            return GL_MIRROR_CLAMP_TO_EDGE;
+        }
     }
     LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
     return GL_REPEAT;

+ 14 - 1
src/video_core/textures/texture.h

@@ -190,6 +190,7 @@ struct TICEntry {
     union {
         BitField<0, 4, u32> res_min_mip_level;
         BitField<4, 4, u32> res_max_mip_level;
+        BitField<12, 12, u32> min_lod_clamp;
     };
 
     GPUVAddr Address() const {
@@ -284,13 +285,25 @@ struct TSCEntry {
         BitField<6, 3, WrapMode> wrap_p;
         BitField<9, 1, u32> depth_compare_enabled;
         BitField<10, 3, DepthCompareFunc> depth_compare_func;
+        BitField<13, 1, u32> srgb_conversion;
+        BitField<20, 3, u32> max_anisotropy;
     };
     union {
         BitField<0, 2, TextureFilter> mag_filter;
         BitField<4, 2, TextureFilter> min_filter;
         BitField<6, 2, TextureMipmapFilter> mip_filter;
+        BitField<9, 1, u32> cubemap_interface_filtering;
+        BitField<12, 13, u32> mip_lod_bias;
+    };
+    union {
+        BitField<0, 12, u32> min_lod_clamp;
+        BitField<12, 12, u32> max_lod_clamp;
+        BitField<24, 8, u32> srgb_border_color_r;
+    };
+    union {
+        BitField<12, 8, u32> srgb_border_color_g;
+        BitField<20, 8, u32> srgb_border_color_b;
     };
-    INSERT_PADDING_BYTES(8);
     float border_color_r;
     float border_color_g;
     float border_color_b;