From 074fb102631021a21a9cee3eb69d6b918c797004 Mon Sep 17 00:00:00 2001
From: Magnus Norddahl <dpjudas@users.noreply.github.com>
Date: Wed, 1 May 2019 00:55:31 +0200
Subject: [PATCH] - use the exact sRGB->linear transfer function in HDR mode as
 the 2.2 gamma approximation is visibly inaccurate in this case

---
 src/rendering/gl/renderer/gl_postprocess.cpp      |  3 ++-
 src/rendering/gl/renderer/gl_stereo3d.cpp         |  1 +
 .../hwrenderer/postprocessing/hw_postprocess.h    |  3 ++-
 src/rendering/vulkan/renderer/vk_postprocess.cpp  |  6 +++++-
 wadsrc/static/shaders/glsl/present.fp             | 15 ++++++++++++++-
 5 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/rendering/gl/renderer/gl_postprocess.cpp b/src/rendering/gl/renderer/gl_postprocess.cpp
index c233b80c7a..85e802a4fb 100644
--- a/src/rendering/gl/renderer/gl_postprocess.cpp
+++ b/src/rendering/gl/renderer/gl_postprocess.cpp
@@ -225,11 +225,12 @@ void FGLRenderer::DrawPresentTexture(const IntRect &box, bool applyGamma)
 	{
 		// Full screen exclusive mode treats a rgba16f frame buffer as linear.
 		// It probably will eventually in desktop mode too, but the DWM doesn't seem to support that.
-		mPresentShader->Uniforms->InvGamma *= 2.2f;
+		mPresentShader->Uniforms->HdrMode = 1;
 		mPresentShader->Uniforms->ColorScale = (gl_dither_bpc == -1) ? 1023.0f : (float)((1 << gl_dither_bpc) - 1);
 	}
 	else
 	{
+		mPresentShader->Uniforms->HdrMode = 0;
 		mPresentShader->Uniforms->ColorScale = (gl_dither_bpc == -1) ? 255.0f : (float)((1 << gl_dither_bpc) - 1);
 	}
 	mPresentShader->Uniforms->Scale = { screen->mScreenViewport.width / (float)mBuffers->GetWidth(), screen->mScreenViewport.height / (float)mBuffers->GetHeight() };
diff --git a/src/rendering/gl/renderer/gl_stereo3d.cpp b/src/rendering/gl/renderer/gl_stereo3d.cpp
index d2bceaeaa9..9483310ba5 100644
--- a/src/rendering/gl/renderer/gl_stereo3d.cpp
+++ b/src/rendering/gl/renderer/gl_stereo3d.cpp
@@ -167,6 +167,7 @@ void FGLRenderer::prepareInterleavedPresent(FPresentShaderBase& shader)
 		shader.Uniforms->Saturation = clamp<float>(vid_saturation, -15.0f, 15.0f);
 		shader.Uniforms->GrayFormula = static_cast<int>(gl_satformula);
 	}
+	shader.Uniforms->HdrMode = 0;
 	shader.Uniforms->ColorScale = (gl_dither_bpc == -1) ? 255.0f : (float)((1 << gl_dither_bpc) - 1);
 	shader.Uniforms->Scale = {
 		screen->mScreenViewport.width / (float)mBuffers->GetWidth(),
diff --git a/src/rendering/hwrenderer/postprocessing/hw_postprocess.h b/src/rendering/hwrenderer/postprocessing/hw_postprocess.h
index 902810c7c6..61dbf47027 100644
--- a/src/rendering/hwrenderer/postprocessing/hw_postprocess.h
+++ b/src/rendering/hwrenderer/postprocessing/hw_postprocess.h
@@ -726,7 +726,7 @@ struct PresentUniforms
 	FVector2 Scale;
 	FVector2 Offset;
 	float ColorScale;
-	float Padding;
+	int HdrMode;
 
 	static std::vector<UniformFieldDesc> Desc()
 	{
@@ -741,6 +741,7 @@ struct PresentUniforms
 			{ "UVScale", UniformType::Vec2, offsetof(PresentUniforms, Scale) },
 			{ "UVOffset", UniformType::Vec2, offsetof(PresentUniforms, Offset) },
 			{ "ColorScale", UniformType::Float, offsetof(PresentUniforms, ColorScale) },
+			{ "HdrMode", UniformType::Int, offsetof(PresentUniforms, HdrMode) }
 		};
 	}
 };
diff --git a/src/rendering/vulkan/renderer/vk_postprocess.cpp b/src/rendering/vulkan/renderer/vk_postprocess.cpp
index 3bb441f190..db82eb3bf5 100644
--- a/src/rendering/vulkan/renderer/vk_postprocess.cpp
+++ b/src/rendering/vulkan/renderer/vk_postprocess.cpp
@@ -200,7 +200,11 @@ void VkPostprocess::DrawPresentTexture(const IntRect &box, bool applyGamma, bool
 
 	if (applyGamma && fb->swapChain->swapChainFormat.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT)
 	{
-		uniforms.InvGamma *= 2.2f;
+		uniforms.HdrMode = 1;
+	}
+	else
+	{
+		uniforms.HdrMode = 0;
 	}
 
 	renderstate.Clear();
diff --git a/wadsrc/static/shaders/glsl/present.fp b/wadsrc/static/shaders/glsl/present.fp
index 6a484f7a32..7069d0fae5 100644
--- a/wadsrc/static/shaders/glsl/present.fp
+++ b/wadsrc/static/shaders/glsl/present.fp
@@ -29,7 +29,20 @@ vec4 Dither(vec4 c)
 	return vec4(floor(c.rgb * ColorScale + threshold) / ColorScale, c.a);
 }
 
+vec4 sRGBtoLinear(vec4 c)
+{
+	return vec4(mix(pow((c.rgb + 0.055) / 1.055, vec3(2.4)), c.rgb / 12.92, step(c.rgb, vec3(0.04045))), c.a);
+}
+
+vec4 ApplyHdrMode(vec4 c)
+{
+	if (HdrMode == 0)
+		return c;
+	else
+		return sRGBtoLinear(c);
+}
+
 void main()
 {
-	FragColor = Dither(ApplyGamma(texture(InputTexture, UVOffset + TexCoord * UVScale)));
+	FragColor = Dither(ApplyHdrMode(ApplyGamma(texture(InputTexture, UVOffset + TexCoord * UVScale))));
 }