From 0287b2be6d1edeecea26250e5cd8d3067ff614af Mon Sep 17 00:00:00 2001
From: Rodolfo Bogado <rodolfoosvaldobogado@gmail.com>
Date: Wed, 24 Oct 2018 17:09:40 -0300
Subject: [PATCH] Implement sRGB Support, including workarounds for nvidia
 driver issues and QT sRGB support

---
 src/video_core/engines/maxwell_3d.h           |  7 +-
 .../renderer_opengl/gl_rasterizer.cpp         | 13 ++-
 .../renderer_opengl/gl_rasterizer_cache.cpp   | 91 ++++++++++++++++---
 .../renderer_opengl/gl_rasterizer_cache.h     | 80 ++++++++++++----
 src/video_core/renderer_opengl/gl_state.cpp   | 13 ++-
 src/video_core/renderer_opengl/gl_state.h     | 14 ++-
 .../renderer_opengl/renderer_opengl.cpp       | 14 ++-
 src/video_core/textures/texture.h             |  5 +
 8 files changed, 197 insertions(+), 40 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index d6978162a8..443affc36d 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -723,7 +723,11 @@ public:
                 StencilOp stencil_back_op_zpass;
                 ComparisonOp stencil_back_func_func;
 
-                INSERT_PADDING_WORDS(0x17);
+                INSERT_PADDING_WORDS(0x4);
+
+                u32 framebuffer_srgb;
+
+                INSERT_PADDING_WORDS(0x12);
 
                 union {
                     BitField<2, 1, u32> coord_origin;
@@ -1086,6 +1090,7 @@ ASSERT_REG_POSITION(stencil_back_op_fail, 0x566);
 ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567);
 ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568);
 ASSERT_REG_POSITION(stencil_back_func_func, 0x569);
+ASSERT_REG_POSITION(framebuffer_srgb, 0x56E);
 ASSERT_REG_POSITION(point_coord_replace, 0x581);
 ASSERT_REG_POSITION(code_address, 0x582);
 ASSERT_REG_POSITION(draw, 0x585);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cd4216c4eb..cb180b93cf 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -418,6 +418,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
     // Bind the framebuffer surfaces
     state.draw.draw_framebuffer = framebuffer.handle;
     state.Apply();
+    state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
 
     if (using_color_fb) {
         if (single_color_target) {
@@ -429,6 +430,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
                 // Assume that a surface will be written to if it is used as a framebuffer, even if
                 // the shader doesn't actually write to it.
                 color_surface->MarkAsModified(true, res_cache);
+                // Workaround for and issue in nvidia drivers
+                // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
+                state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
             }
 
             glFramebufferTexture2D(
@@ -446,6 +450,11 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
                     // Assume that a surface will be written to if it is used as a framebuffer, even
                     // if the shader doesn't actually write to it.
                     color_surface->MarkAsModified(true, res_cache);
+                    // Enable sRGB only for supported formats
+                    // Workaround for and issue in nvidia drivers
+                    // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
+                    state.framebuffer_srgb.enabled |=
+                        color_surface->GetSurfaceParams().srgb_conversion;
                 }
 
                 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
@@ -537,7 +546,9 @@ void RasterizerOpenGL::Clear() {
 
     ConfigureFramebuffers(use_color, use_depth || use_stencil, false,
                           regs.clear_buffers.RT.Value());
-
+    // Copy the sRGB setting to the clear state to avoid problem with
+    // specific driver implementations
+    clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled;
     clear_state.Apply();
 
     if (use_color) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 04a0241374..b057e2efa3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -40,6 +40,10 @@ static bool IsPixelFormatASTC(PixelFormat format) {
     case PixelFormat::ASTC_2D_5X4:
     case PixelFormat::ASTC_2D_8X8:
     case PixelFormat::ASTC_2D_8X5:
+    case PixelFormat::ASTC_2D_4X4_SRGB:
+    case PixelFormat::ASTC_2D_5X4_SRGB:
+    case PixelFormat::ASTC_2D_8X8_SRGB:
+    case PixelFormat::ASTC_2D_8X5_SRGB:
         return true;
     default:
         return false;
@@ -56,6 +60,14 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
         return {8, 8};
     case PixelFormat::ASTC_2D_8X5:
         return {8, 5};
+    case PixelFormat::ASTC_2D_4X4_SRGB:
+        return {4, 4};
+    case PixelFormat::ASTC_2D_5X4_SRGB:
+        return {5, 4};
+    case PixelFormat::ASTC_2D_8X8_SRGB:
+        return {8, 8};
+    case PixelFormat::ASTC_2D_8X5_SRGB:
+        return {8, 5};
     default:
         LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
         UNREACHABLE();
@@ -108,8 +120,9 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
     params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
     params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
     params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
-    params.pixel_format =
-        PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value());
+    params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
+    params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
+                                                       params.srgb_conversion);
     params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
     params.type = GetFormatType(params.pixel_format);
     params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
@@ -166,6 +179,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
     params.block_height = 1 << config.memory_layout.block_height;
     params.block_depth = 1 << config.memory_layout.block_depth;
     params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
+    params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
+                             config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
     params.component_type = ComponentTypeFromRenderTarget(config.format);
     params.type = GetFormatType(params.pixel_format);
     params.width = config.width;
@@ -201,6 +216,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
     params.pixel_format = PixelFormatFromDepthFormat(format);
     params.component_type = ComponentTypeFromDepthFormat(format);
     params.type = GetFormatType(params.pixel_format);
+    params.srgb_conversion = false;
     params.width = zeta_width;
     params.height = zeta_height;
     params.unaligned_height = zeta_height;
@@ -224,6 +240,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
     params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
     params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
     params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
+    params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
+                             config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
     params.component_type = ComponentTypeFromRenderTarget(config.format);
     params.type = GetFormatType(params.pixel_format);
     params.width = config.width;
@@ -289,14 +307,29 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
     {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false},           // RG16I
     {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false},             // RG16S
     {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false},                // RGB32F
-    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8
-    {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false},                       // RG8U
-    {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false},                                // RG8S
-    {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false},              // RG32UI
-    {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false},              // R32UI
-    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
-    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
-    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
+    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm,
+     false},                                                                   // RGBA8_SRGB
+    {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false},            // RG8U
+    {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false},                     // RG8S
+    {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false},   // RG32UI
+    {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false},   // R32UI
+    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false},        // ASTC_2D_8X8
+    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false},        // ASTC_2D_8X5
+    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false},        // ASTC_2D_5X4
+    {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
+    // Compressed sRGB formats
+    {GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
+     true}, // DXT1_SRGB
+    {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
+     true}, // DXT23_SRGB
+    {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
+     true}, // DXT45_SRGB
+    {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,
+     ComponentType::UNorm, true},                                              // BC7U_SRGB
+    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB
+    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
+    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
+    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
 
     // Depth formats
     {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -361,6 +394,10 @@ static bool IsFormatBCn(PixelFormat format) {
     case PixelFormat::BC7U:
     case PixelFormat::BC6H_UF16:
     case PixelFormat::BC6H_SF16:
+    case PixelFormat::DXT1_SRGB:
+    case PixelFormat::DXT23_SRGB:
+    case PixelFormat::DXT45_SRGB:
+    case PixelFormat::BC7U_SRGB:
         return true;
     }
     return false;
@@ -432,7 +469,7 @@ static constexpr GLConversionArray morton_to_gl_fns = {
         MortonCopy<true, PixelFormat::RG16I>,
         MortonCopy<true, PixelFormat::RG16S>,
         MortonCopy<true, PixelFormat::RGB32F>,
-        MortonCopy<true, PixelFormat::SRGBA8>,
+        MortonCopy<true, PixelFormat::RGBA8_SRGB>,
         MortonCopy<true, PixelFormat::RG8U>,
         MortonCopy<true, PixelFormat::RG8S>,
         MortonCopy<true, PixelFormat::RG32UI>,
@@ -440,6 +477,15 @@ static constexpr GLConversionArray morton_to_gl_fns = {
         MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
         MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
         MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
+        MortonCopy<true, PixelFormat::BGRA8_SRGB>,
+        MortonCopy<true, PixelFormat::DXT1_SRGB>,
+        MortonCopy<true, PixelFormat::DXT23_SRGB>,
+        MortonCopy<true, PixelFormat::DXT45_SRGB>,
+        MortonCopy<true, PixelFormat::BC7U_SRGB>,
+        MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
+        MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
+        MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
+        MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
         MortonCopy<true, PixelFormat::Z32F>,
         MortonCopy<true, PixelFormat::Z16>,
         MortonCopy<true, PixelFormat::Z24S8>,
@@ -491,7 +537,7 @@ static constexpr GLConversionArray gl_to_morton_fns = {
         MortonCopy<false, PixelFormat::RG16I>,
         MortonCopy<false, PixelFormat::RG16S>,
         MortonCopy<false, PixelFormat::RGB32F>,
-        MortonCopy<false, PixelFormat::SRGBA8>,
+        MortonCopy<false, PixelFormat::RGBA8_SRGB>,
         MortonCopy<false, PixelFormat::RG8U>,
         MortonCopy<false, PixelFormat::RG8S>,
         MortonCopy<false, PixelFormat::RG32UI>,
@@ -499,6 +545,15 @@ static constexpr GLConversionArray gl_to_morton_fns = {
         nullptr,
         nullptr,
         nullptr,
+        MortonCopy<false, PixelFormat::BGRA8_SRGB>,
+        MortonCopy<false, PixelFormat::DXT1_SRGB>,
+        MortonCopy<false, PixelFormat::DXT23_SRGB>,
+        MortonCopy<false, PixelFormat::DXT45_SRGB>,
+        MortonCopy<false, PixelFormat::BC7U_SRGB>,
+        nullptr,
+        nullptr,
+        nullptr,
+        nullptr,
         MortonCopy<false, PixelFormat::Z32F>,
         MortonCopy<false, PixelFormat::Z16>,
         MortonCopy<false, PixelFormat::Z24S8>,
@@ -546,6 +601,8 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
     OpenGLState state;
     state.draw.read_framebuffer = read_fb_handle;
     state.draw.draw_framebuffer = draw_fb_handle;
+    // Set sRGB enabled if the destination surfaces need it
+    state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
     state.Apply();
 
     u32 buffers{};
@@ -881,7 +938,11 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
     case PixelFormat::ASTC_2D_4X4:
     case PixelFormat::ASTC_2D_8X8:
     case PixelFormat::ASTC_2D_8X5:
-    case PixelFormat::ASTC_2D_5X4: {
+    case PixelFormat::ASTC_2D_5X4:
+    case PixelFormat::ASTC_2D_4X4_SRGB:
+    case PixelFormat::ASTC_2D_8X8_SRGB:
+    case PixelFormat::ASTC_2D_8X5_SRGB:
+    case PixelFormat::ASTC_2D_5X4_SRGB: {
         // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
         u32 block_width{};
         u32 block_height{};
@@ -913,7 +974,9 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
     case PixelFormat::G8R8U:
     case PixelFormat::G8R8S:
     case PixelFormat::ASTC_2D_4X4:
-    case PixelFormat::ASTC_2D_8X8: {
+    case PixelFormat::ASTC_2D_8X8:
+    case PixelFormat::ASTC_2D_4X4_SRGB:
+    case PixelFormat::ASTC_2D_8X8_SRGB: {
         LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
                      static_cast<u32>(pixel_format));
         UNREACHABLE();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 181acfc68b..b4701a616f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -69,7 +69,7 @@ struct SurfaceParams {
         RG16I = 37,
         RG16S = 38,
         RGB32F = 39,
-        SRGBA8 = 40,
+        RGBA8_SRGB = 40,
         RG8U = 41,
         RG8S = 42,
         RG32UI = 43,
@@ -77,19 +77,28 @@ struct SurfaceParams {
         ASTC_2D_8X8 = 45,
         ASTC_2D_8X5 = 46,
         ASTC_2D_5X4 = 47,
+        BGRA8_SRGB = 48,
+        DXT1_SRGB = 49,
+        DXT23_SRGB = 50,
+        DXT45_SRGB = 51,
+        BC7U_SRGB = 52,
+        ASTC_2D_4X4_SRGB = 53,
+        ASTC_2D_8X8_SRGB = 54,
+        ASTC_2D_8X5_SRGB = 55,
+        ASTC_2D_5X4_SRGB = 56,
 
         MaxColorFormat,
 
         // Depth formats
-        Z32F = 48,
-        Z16 = 49,
+        Z32F = 57,
+        Z16 = 58,
 
         MaxDepthFormat,
 
         // DepthStencil formats
-        Z24S8 = 50,
-        S8Z24 = 51,
-        Z32FS8 = 52,
+        Z24S8 = 59,
+        S8Z24 = 60,
+        Z32FS8 = 61,
 
         MaxDepthStencilFormat,
 
@@ -236,7 +245,7 @@ struct SurfaceParams {
             1, // RG16I
             1, // RG16S
             1, // RGB32F
-            1, // SRGBA8
+            1, // RGBA8_SRGB
             1, // RG8U
             1, // RG8S
             1, // RG32UI
@@ -244,6 +253,15 @@ struct SurfaceParams {
             4, // ASTC_2D_8X8
             4, // ASTC_2D_8X5
             4, // ASTC_2D_5X4
+            1, // BGRA8_SRGB
+            4, // DXT1_SRGB
+            4, // DXT23_SRGB
+            4, // DXT45_SRGB
+            4, // BC7U_SRGB
+            4, // ASTC_2D_4X4_SRGB
+            4, // ASTC_2D_8X8_SRGB
+            4, // ASTC_2D_8X5_SRGB
+            4, // ASTC_2D_5X4_SRGB
             1, // Z32F
             1, // Z16
             1, // Z24S8
@@ -299,7 +317,7 @@ struct SurfaceParams {
             1, // RG16I
             1, // RG16S
             1, // RGB32F
-            1, // SRGBA8
+            1, // RGBA8_SRGB
             1, // RG8U
             1, // RG8S
             1, // RG32UI
@@ -307,6 +325,15 @@ struct SurfaceParams {
             8, // ASTC_2D_8X8
             5, // ASTC_2D_8X5
             4, // ASTC_2D_5X4
+            1, // BGRA8_SRGB
+            4, // DXT1_SRGB
+            4, // DXT23_SRGB
+            4, // DXT45_SRGB
+            4, // BC7U_SRGB
+            4, // ASTC_2D_4X4_SRGB
+            8, // ASTC_2D_8X8_SRGB
+            5, // ASTC_2D_8X5_SRGB
+            4, // ASTC_2D_5X4_SRGB
             1, // Z32F
             1, // Z16
             1, // Z24S8
@@ -362,7 +389,7 @@ struct SurfaceParams {
             32,  // RG16I
             32,  // RG16S
             96,  // RGB32F
-            32,  // SRGBA8
+            32,  // RGBA8_SRGB
             16,  // RG8U
             16,  // RG8S
             64,  // RG32UI
@@ -370,6 +397,15 @@ struct SurfaceParams {
             16,  // ASTC_2D_8X8
             32,  // ASTC_2D_8X5
             32,  // ASTC_2D_5X4
+            32,  // BGRA8_SRGB
+            64,  // DXT1_SRGB
+            128, // DXT23_SRGB
+            128, // DXT45_SRGB
+            128, // BC7U
+            32,  // ASTC_2D_4X4_SRGB
+            16,  // ASTC_2D_8X8_SRGB
+            32,  // ASTC_2D_8X5_SRGB
+            32,  // ASTC_2D_5X4_SRGB
             32,  // Z32F
             16,  // Z16
             32,  // Z24S8
@@ -408,6 +444,7 @@ struct SurfaceParams {
         // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
         // gamma.
         case Tegra::RenderTargetFormat::RGBA8_SRGB:
+            return PixelFormat::RGBA8_SRGB;
         case Tegra::RenderTargetFormat::RGBA8_UNORM:
             return PixelFormat::ABGR8U;
         case Tegra::RenderTargetFormat::RGBA8_SNORM:
@@ -415,6 +452,7 @@ struct SurfaceParams {
         case Tegra::RenderTargetFormat::RGBA8_UINT:
             return PixelFormat::ABGR8UI;
         case Tegra::RenderTargetFormat::BGRA8_SRGB:
+            return PixelFormat::BGRA8_SRGB;
         case Tegra::RenderTargetFormat::BGRA8_UNORM:
             return PixelFormat::BGRA8;
         case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
@@ -478,10 +516,14 @@ struct SurfaceParams {
     }
 
     static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
-                                                    Tegra::Texture::ComponentType component_type) {
+                                                    Tegra::Texture::ComponentType component_type,
+                                                    bool is_srgb) {
         // TODO(Subv): Properly implement this
         switch (format) {
         case Tegra::Texture::TextureFormat::A8R8G8B8:
+            if (is_srgb) {
+                return PixelFormat::RGBA8_SRGB;
+            }
             switch (component_type) {
             case Tegra::Texture::ComponentType::UNORM:
                 return PixelFormat::ABGR8U;
@@ -616,11 +658,11 @@ struct SurfaceParams {
         case Tegra::Texture::TextureFormat::Z24S8:
             return PixelFormat::Z24S8;
         case Tegra::Texture::TextureFormat::DXT1:
-            return PixelFormat::DXT1;
+            return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
         case Tegra::Texture::TextureFormat::DXT23:
-            return PixelFormat::DXT23;
+            return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23;
         case Tegra::Texture::TextureFormat::DXT45:
-            return PixelFormat::DXT45;
+            return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45;
         case Tegra::Texture::TextureFormat::DXN1:
             return PixelFormat::DXN1;
         case Tegra::Texture::TextureFormat::DXN2:
@@ -634,19 +676,19 @@ struct SurfaceParams {
                          static_cast<u32>(component_type));
             UNREACHABLE();
         case Tegra::Texture::TextureFormat::BC7U:
-            return PixelFormat::BC7U;
+            return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U;
         case Tegra::Texture::TextureFormat::BC6H_UF16:
             return PixelFormat::BC6H_UF16;
         case Tegra::Texture::TextureFormat::BC6H_SF16:
             return PixelFormat::BC6H_SF16;
         case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
-            return PixelFormat::ASTC_2D_4X4;
+            return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
         case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
-            return PixelFormat::ASTC_2D_5X4;
+            return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
         case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
-            return PixelFormat::ASTC_2D_8X8;
+            return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
         case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
-            return PixelFormat::ASTC_2D_8X5;
+            return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
         case Tegra::Texture::TextureFormat::R16_G16:
             switch (component_type) {
             case Tegra::Texture::ComponentType::FLOAT:
@@ -881,7 +923,7 @@ struct SurfaceParams {
     SurfaceTarget target;
     u32 max_mip_level;
     bool is_layered;
-
+    bool srgb_conversion;
     // Parameters used for caching
     VAddr addr;
     Tegra::GPUVAddr gpu_addr;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index f9d41ca244..d8a43cc94d 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -11,9 +11,10 @@
 namespace OpenGL {
 
 OpenGLState OpenGLState::cur_state;
-
+bool OpenGLState::s_rgb_used;
 OpenGLState::OpenGLState() {
     // These all match default OpenGL values
+    framebuffer_srgb.enabled = false;
     cull.enabled = false;
     cull.mode = GL_BACK;
     cull.front_face = GL_CCW;
@@ -89,6 +90,16 @@ OpenGLState::OpenGLState() {
 }
 
 void OpenGLState::Apply() const {
+    // sRGB
+    if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) {
+        if (framebuffer_srgb.enabled) {
+            // Track if sRGB is used
+            s_rgb_used = true;
+            glEnable(GL_FRAMEBUFFER_SRGB);
+        } else {
+            glDisable(GL_FRAMEBUFFER_SRGB);
+        }
+    }
     // Culling
     if (cull.enabled != cur_state.cull.enabled) {
         if (cull.enabled) {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 4334b0d351..9e2c573b54 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -35,6 +35,10 @@ constexpr TextureUnit ProcTexDiffLUT{9};
 
 class OpenGLState {
 public:
+    struct {
+        bool enabled; // GL_FRAMEBUFFER_SRGB
+    } framebuffer_srgb;
+
     struct {
         bool enabled;      // GL_CULL_FACE
         GLenum mode;       // GL_CULL_FACE_MODE
@@ -161,7 +165,12 @@ public:
     static OpenGLState GetCurState() {
         return cur_state;
     }
-
+    static bool GetsRGBUsed() {
+        return s_rgb_used;
+    }
+    static void ClearsRGBUsed() {
+        s_rgb_used = false;
+    }
     /// Apply this state as the current OpenGL state
     void Apply() const;
 
@@ -176,6 +185,9 @@ public:
 
 private:
     static OpenGLState cur_state;
+    // Workaround for sRGB problems caused by
+    // QT not supporting srgb output
+    static bool s_rgb_used;
 };
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 96d916b072..90b68943d1 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -283,7 +283,8 @@ void RendererOpenGL::CreateRasterizer() {
     if (rasterizer) {
         return;
     }
-
+    // Initialize sRGB Usage
+    OpenGLState::ClearsRGBUsed();
     rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info);
 }
 
@@ -356,13 +357,20 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
 
     state.texture_units[0].texture = screen_info.display_texture;
     state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
+    // Workaround brigthness problems in SMO by enabling sRGB in the final output
+    // if it has been used in the frame
+    // Needed because of this bug in QT
+    // QTBUG-50987
+    state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
     state.Apply();
-
     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
+    // restore default state
+    state.framebuffer_srgb.enabled = false;
     state.texture_units[0].texture = 0;
     state.Apply();
+    // Clear sRGB state for the next frame
+    OpenGLState::ClearsRGBUsed();
 }
 
 /**
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 5947bd2b98..d12d2ecb88 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -173,6 +173,7 @@ struct TICEntry {
     };
     union {
         BitField<0, 16, u32> width_minus_1;
+        BitField<22, 1, u32> srgb_conversion;
         BitField<23, 4, TextureType> texture_type;
     };
     union {
@@ -227,6 +228,10 @@ struct TICEntry {
         return header_version == TICHeaderVersion::BlockLinear ||
                header_version == TICHeaderVersion::BlockLinearColorKey;
     }
+
+    bool IsSrgbConversionEnabled() const {
+        return srgb_conversion != 0;
+    }
 };
 static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");