Merge pull request #12603 from Sam-Belliveau/oklab_resampling
Add HDR to Metal + Perceptual HDR
This commit is contained in:
commit
03f2b99ffe
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
[configuration]
|
||||
|
||||
[OptionRangeFloat]
|
||||
GUIName = Amplificiation
|
||||
OptionName = AMPLIFICATION
|
||||
MinValue = 1.0
|
||||
MaxValue = 6.0
|
||||
StepAmount = 0.25
|
||||
DefaultValue = 2.5
|
||||
|
||||
[/configuration]
|
||||
*/
|
||||
|
||||
// ICtCP Colorspace as defined by Dolby here:
|
||||
// https://professional.dolby.com/siteassets/pdfs/ictcp_dolbywhitepaper_v071.pdf
|
||||
|
||||
/***** Transfer Function *****/
|
||||
|
||||
const float4 m_1 = float4(2610.0 / 16384.0);
|
||||
const float4 m_2 = float4(128.0 * 2523.0 / 4096.0);
|
||||
const float4 m_1_inv = float4(16384.0 / 2610.0);
|
||||
const float4 m_2_inv = float4(4096.0 / (128.0 * 2523.0));
|
||||
|
||||
const float4 c_1 = float4(3424.0 / 4096.0);
|
||||
const float4 c_2 = float4(2413.0 / 4096.0 * 32.0);
|
||||
const float4 c_3 = float4(2392.0 / 4096.0 * 32.0);
|
||||
|
||||
float4 EOTF_inv(float4 lms) {
|
||||
float4 y = pow(lms, m_1);
|
||||
return pow((c_1 + c_2 * y) / (1.0 + c_3 * y), m_2);
|
||||
}
|
||||
|
||||
float4 EOTF(float4 lms) {
|
||||
float4 x = pow(lms, m_2_inv);
|
||||
return pow(-(x - c_1) / (c_3 * x - c_2), m_1_inv);
|
||||
}
|
||||
|
||||
// This is required as scaling in EOTF space is not linear.
|
||||
float EOTF_AMPLIFICATION = EOTF_inv(float4(AMPLIFICATION)).x;
|
||||
|
||||
/***** Linear <--> ICtCp *****/
|
||||
|
||||
const mat4 RGBtoLMS = mat4(
|
||||
1688.0, 683.0, 99.0, 0.0,
|
||||
2146.0, 2951.0, 309.0, 0.0,
|
||||
262.0, 462.0, 3688.0, 0.0,
|
||||
0.0, 0.0, 0.0, 4096.0) / 4096.0;
|
||||
|
||||
const mat4 LMStoICtCp = mat4(
|
||||
+2048.0, +6610.0, +17933.0, 0.0,
|
||||
+2048.0, -13613.0, -17390.0, 0.0,
|
||||
+0.0, +7003.0, -543.0, 0.0,
|
||||
+0.0, +0.0, +0.0, 4096.0) / 4096.0;
|
||||
|
||||
float4 LinearRGBToICtCP(float4 c)
|
||||
{
|
||||
return LMStoICtCp * EOTF_inv(RGBtoLMS * c);
|
||||
}
|
||||
|
||||
/***** ICtCp <--> Linear *****/
|
||||
|
||||
mat4 ICtCptoLMS = inverse(LMStoICtCp);
|
||||
mat4 LMStoRGB = inverse(RGBtoLMS);
|
||||
|
||||
float4 ICtCpToLinearRGB(float4 c)
|
||||
{
|
||||
return LMStoRGB * EOTF(ICtCptoLMS * c);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float4 color = Sample();
|
||||
|
||||
// Nothing to do here, we are in SDR
|
||||
if (!OptionEnabled(hdr_output) || !OptionEnabled(linear_space_output)) {
|
||||
SetOutput(color);
|
||||
return;
|
||||
}
|
||||
|
||||
// Renormalize Color to be in [0.0 - 1.0] SDR Space. We will revert this later.
|
||||
const float hdr_paper_white = hdr_paper_white_nits / hdr_sdr_white_nits;
|
||||
color.rgb /= hdr_paper_white;
|
||||
|
||||
// Convert Color to Perceptual Color Space. This will allow us to do perceptual
|
||||
// scaling while also being able to use the luminance channel.
|
||||
float4 ictcp_color = LinearRGBToICtCP(color);
|
||||
|
||||
// Scale the color in perceptual space depending on the percieved luminance.
|
||||
//
|
||||
// At low luminances, ~0.0, pow(EOTF_AMPLIFICATION, ~0.0) ~= 1.0, so the
|
||||
// color will appear to be unchanged. This is important as we don't want to
|
||||
// over expose dark colors which would not have otherwise been seen.
|
||||
//
|
||||
// At high luminances, ~1.0, pow(EOTF_AMPLIFICATION, ~1.0) ~= EOTF_AMPLIFICATION,
|
||||
// which is equivilant to scaling the color by EOTF_AMPLIFICATION. This is
|
||||
// important as we want to get the most out of the display, and we want to
|
||||
// get bright colors to hit their target brightness.
|
||||
//
|
||||
// For more information, see this desmos demonstrating this scaling process:
|
||||
// https://www.desmos.com/calculator/syjyrjsj5c
|
||||
const float luminance = ictcp_color.x;
|
||||
ictcp_color *= pow(EOTF_AMPLIFICATION, luminance);
|
||||
|
||||
// Convert back to Linear RGB and output the color to the display.
|
||||
// We use hdr_paper_white to renormalize the color to the comfortable
|
||||
// SDR viewing range.
|
||||
SetOutput(hdr_paper_white * ICtCpToLinearRGB(ictcp_color));
|
||||
}
|
|
@ -164,8 +164,23 @@ void Metal::VideoBackend::PrepareWindow(WindowSystemInfo& wsi)
|
|||
return;
|
||||
NSView* view = static_cast<NSView*>(wsi.render_surface);
|
||||
CAMetalLayer* layer = [CAMetalLayer layer];
|
||||
|
||||
Util::PopulateBackendInfo(&g_Config);
|
||||
|
||||
if (g_Config.backend_info.bSupportsHDROutput && g_Config.bHDR)
|
||||
{
|
||||
[layer setWantsExtendedDynamicRangeContent:YES];
|
||||
[layer setPixelFormat:MTLPixelFormatRGBA16Float];
|
||||
|
||||
const CFStringRef name = kCGColorSpaceExtendedLinearSRGB;
|
||||
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(name);
|
||||
[layer setColorspace:colorspace];
|
||||
CGColorSpaceRelease(colorspace);
|
||||
}
|
||||
|
||||
[view setWantsLayer:YES];
|
||||
[view setLayer:layer];
|
||||
|
||||
wsi.render_surface = layer;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AppKit/NSScreen.h>
|
||||
#include <Metal/Metal.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "VideoCommon/AbstractShader.h"
|
||||
|
|
|
@ -77,6 +77,8 @@ void Metal::Util::PopulateBackendInfo(VideoConfig* config)
|
|||
config->backend_info.bSupportsPartialMultisampleResolve = false;
|
||||
config->backend_info.bSupportsDynamicVertexLoader = true;
|
||||
config->backend_info.bSupportsVSLinePointExpand = true;
|
||||
config->backend_info.bSupportsHDROutput =
|
||||
1.0 < [[NSScreen deepestScreen] maximumPotentialExtendedDynamicRangeColorComponentValue];
|
||||
}
|
||||
|
||||
void Metal::Util::PopulateBackendInfoAdapters(VideoConfig* config,
|
||||
|
|
Loading…
Reference in New Issue