From 0287b2be6d1edeecea26250e5cd8d3067ff614af Mon Sep 17 00:00:00 2001 From: Rodolfo Bogado 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 d6978162a..443affc36 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 cd4216c4e..cb180b93c 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 04a024137..b057e2efa 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 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(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 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, MortonCopy, MortonCopy, - MortonCopy, + MortonCopy, MortonCopy, MortonCopy, MortonCopy, @@ -440,6 +477,15 @@ static constexpr GLConversionArray morton_to_gl_fns = { MortonCopy, MortonCopy, MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, MortonCopy, MortonCopy, MortonCopy, @@ -491,7 +537,7 @@ static constexpr GLConversionArray gl_to_morton_fns = { MortonCopy, MortonCopy, MortonCopy, - MortonCopy, + MortonCopy, MortonCopy, MortonCopy, MortonCopy, @@ -499,6 +545,15 @@ static constexpr GLConversionArray gl_to_morton_fns = { nullptr, nullptr, nullptr, + MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, + MortonCopy, + nullptr, + nullptr, + nullptr, + nullptr, MortonCopy, MortonCopy, MortonCopy, @@ -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& 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& 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(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 181acfc68..b4701a616 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(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 f9d41ca24..d8a43cc94 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 4334b0d35..9e2c573b5 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 96d916b07..90b68943d 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(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 5947bd2b9..d12d2ecb8 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");