vlc | branch: master | Steve Lhomme <[email protected]> | Thu Mar  2 17:28:38 
2017 +0100| [8b5530e5cf09306dfc8e3132d63392f6188cbe5c] | committer: 
Jean-Baptiste Kempf

direct3d11: fix HDR rendering on LDR screens

The wide contrast provided by HDR results in washed out colors when displayed
on an LDR (non-HDR) screen. To fix this we need to do some tone mapping in
linear space. The tone mapping used here comes from [1].

The pixel shader source is constructed based on the video input characteristics.
When the source is LDR no tone mapping or transfer adjustment is done.

For now we assume the screen is using sRGB transfer.

1: http://filmicworlds.com/blog/filmic-tonemapping-operators/

Signed-off-by: Jean-Baptiste Kempf <[email protected]>

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=8b5530e5cf09306dfc8e3132d63392f6188cbe5c
---

 modules/video_output/win32/direct3d11.c | 99 ++++++++++++++++++++++++++++++---
 1 file changed, 92 insertions(+), 7 deletions(-)

diff --git a/modules/video_output/win32/direct3d11.c 
b/modules/video_output/win32/direct3d11.c
index e24d9a0..836efa8 100644
--- a/modules/video_output/win32/direct3d11.c
+++ b/modules/video_output/win32/direct3d11.c
@@ -110,6 +110,8 @@ struct vout_display_sys_t
 {
     vout_display_sys_win32_t sys;
 
+    video_transfer_func_t    display_transfer; /* TODO may go in 
vout_display_info_t */
+
 #if !VLC_WINSTORE_APP
     HINSTANCE                hdxgi_dll;        /* handle of the opened dxgi 
dll */
     HINSTANCE                hd3d11_dll;       /* handle of the opened d3d11 
dll */
@@ -298,6 +300,31 @@ static const char* globPixelShaderDefault = "\
     float4 Texture    : TEXCOORD0;\
   };\
   \
+  /* see http://filmicworlds.com/blog/filmic-tonemapping-operators/ */\
+  inline float hable(float x) {\
+      const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30;\
+      return ((x * (A*x + (C*B))+(D*E))/(x * (A*x + B) + (D*F))) - E/F;\
+  }\
+  \
+  inline float3 hable(float3 x) {\
+      x.r = hable(x.r);\
+      x.g = hable(x.g);\
+      x.b = hable(x.b);\
+      return x;\
+  }\
+  \
+  inline float3 sourceToLinear(float3 rgb) {\
+      %s;\
+  }\
+  \
+  inline float3 linearToDisplay(float3 rgb) {\
+      %s;\
+  }\
+  \
+  inline float3 toneMapping(float3 rgb) {\
+      %s;\
+  }\
+  \
   float4 main( PS_INPUT In ) : SV_TARGET\
   {\
     float4 sample;\
@@ -308,7 +335,13 @@ static const char* globPixelShaderDefault = "\
     rgba.y = sample.y + WhitePointY;\
     rgba.z = sample.z + WhitePointZ;\
     rgba.a = sample.a * Opacity;\
-    return saturate(mul(rgba, Colorspace));\
+    rgba = mul(rgba, Colorspace);\
+    float opacity = rgba.a * Opacity;\
+    float3 rgb = float3(rgba.r, rgba.g, rgba.b);\
+    rgb = sourceToLinear(rgb);\
+    rgb = toneMapping(rgb);\
+    rgb = linearToDisplay(saturate(rgb));\
+    return float4(rgb, saturate(opacity));\
   }\
 ";
 
@@ -1385,6 +1418,8 @@ static int Direct3D11Open(vout_display_t *vd, 
video_format_t *fmt)
        return VLC_EGENERIC;
     }
 #endif
+    /* TODO adjust the swapchain transfer function based on the source */
+    sys->display_transfer = TRANSFER_FUNC_SRGB;
 
     // look for the requested pixel format first
     sys->picQuadConfig = GetOutputFormat(vd, fmt->i_chroma, 0, true, false);
@@ -1531,11 +1566,16 @@ static ID3DBlob* CompileShader(vout_display_t *vd, 
const char *psz_shader, bool
     return pShaderBlob;
 }
 
-static HRESULT CompilePixelShader(vout_display_t *vd, const d3d_format_t 
*format, ID3D11PixelShader **output)
+static HRESULT CompilePixelShader(vout_display_t *vd, const d3d_format_t 
*format,
+                                  video_transfer_func_t transfer, 
ID3D11PixelShader **output)
 {
     vout_display_sys_t *sys = vd->sys;
 
     const char *psz_sampler;
+    const char *psz_src_transform     = "return rgb";
+    const char *psz_display_transform = "return rgb";
+    const char *psz_tone_mapping      = "return rgb";
+
     switch (format->formatTexture)
     {
     case DXGI_FORMAT_NV12:
@@ -1560,16 +1600,61 @@ static HRESULT CompilePixelShader(vout_display_t *vd, 
const d3d_format_t *format
         break;
     default:
         vlc_assert_unreachable();
+    }
 
+    video_transfer_func_t src_transfer;
+    switch (transfer)
+    {
+        case TRANSFER_FUNC_SMPTE_ST2084:
+            /* ST2084 to Linear */
+            psz_src_transform =
+                    "rgb = pow(rgb, 1.0/78.843750);\
+                    rgb = max(rgb - 0.835938, 0.0) / (18.851562 - 18.687500 * 
rgb);\
+                    rgb = pow(rgb, 1.0/0.159302);\
+                    return rgb";
+            src_transfer = TRANSFER_FUNC_LINEAR;
+            break;
+        default:
+            src_transfer = transfer;
+            break;
+    }
+
+    if (src_transfer != sys->display_transfer)
+    {
+        switch (sys->display_transfer)
+        {
+            case TRANSFER_FUNC_SRGB:
+                if (src_transfer == TRANSFER_FUNC_LINEAR)
+                {
+                    /* Linear to sRGB */
+                    psz_display_transform = "return pow(rgb, 1.0 / 2.2)";
+
+                    if (transfer == TRANSFER_FUNC_SMPTE_ST2084)
+                    {
+                        /* HDR tone mapping */
+                        psz_tone_mapping =
+                            "static const float3 HABLE_DIV = hable(11.2);\
+                            rgb = hable(32.0 * rgb) / HABLE_DIV;\
+                            return rgb";
+                    }
+                    break;
+                }
+            default:
+                msg_Warn(vd, "don't know how to transfer from %d to %d", 
src_transfer, sys->display_transfer);
+                break;
+        }
     }
 
-    char *shader = malloc(strlen(globPixelShaderDefault) + 32 + 
strlen(psz_sampler));
+    char *shader = malloc(strlen(globPixelShaderDefault) + 32 + 
strlen(psz_sampler) +
+                          strlen(psz_src_transform) + 
strlen(psz_display_transform) +
+                          strlen(psz_tone_mapping));
     if (!shader)
     {
         msg_Err(vd, "no room for the Pixel Shader");
         return E_OUTOFMEMORY;
     }
-    sprintf(shader, globPixelShaderDefault, sys->legacy_shader ? "" : "Array", 
psz_sampler);
+    sprintf(shader, globPixelShaderDefault, sys->legacy_shader ? "" : "Array", 
psz_src_transform,
+            psz_display_transform, psz_tone_mapping, psz_sampler);
 
     ID3DBlob *pPSBlob = CompileShader(vd, shader, true);
     free(shader);
@@ -1720,7 +1805,7 @@ static int Direct3D11CreateResources(vout_display_t *vd, 
video_format_t *fmt)
     sys->legacy_shader = !CanUseTextureArray(vd);
     vd->info.is_slow = !is_d3d11_opaque(fmt->i_chroma);
 
-    hr = CompilePixelShader(vd, sys->picQuadConfig, &sys->picQuadPixelShader);
+    hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, 
&sys->picQuadPixelShader);
     if (FAILED(hr))
     {
 #ifdef HAVE_ID3D11VIDEODECODER
@@ -1728,7 +1813,7 @@ static int Direct3D11CreateResources(vout_display_t *vd, 
video_format_t *fmt)
         {
             sys->legacy_shader = true;
             msg_Dbg(vd, "fallback to legacy shader mode");
-            hr = CompilePixelShader(vd, sys->picQuadConfig, 
&sys->picQuadPixelShader);
+            hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, 
&sys->picQuadPixelShader);
         }
 #endif
         if (FAILED(hr))
@@ -1740,7 +1825,7 @@ static int Direct3D11CreateResources(vout_display_t *vd, 
video_format_t *fmt)
 
     if (sys->d3dregion_format != NULL)
     {
-        hr = CompilePixelShader(vd, sys->d3dregion_format, 
&sys->pSPUPixelShader);
+        hr = CompilePixelShader(vd, sys->d3dregion_format, TRANSFER_FUNC_SRGB, 
&sys->pSPUPixelShader);
         if (FAILED(hr))
         {
             ID3D11PixelShader_Release(sys->picQuadPixelShader);

_______________________________________________
vlc-commits mailing list
[email protected]
https://mailman.videolan.org/listinfo/vlc-commits

Reply via email to