diff options
author | thexai <58434170+thexai@users.noreply.github.com> | 2020-10-31 08:38:42 +0100 |
---|---|---|
committer | thexai <58434170+thexai@users.noreply.github.com> | 2020-11-04 12:14:57 +0100 |
commit | 4033af79d7888b7801d27bd972a3b649a3022210 (patch) | |
tree | c24d730f7b313a22754748104b8967a502d666b6 | |
parent | 50a89a7b208f15a653622811b833807ba2c29c8e (diff) |
Tone mapping: Add new tone map methods "ACES Filmic" and "Hable"
-rw-r--r-- | addons/resource.language.en_gb/resources/strings.po | 14 | ||||
-rw-r--r-- | system/keymaps/keyboard.xml | 1 | ||||
-rw-r--r-- | system/shaders/output_d3d.fx | 103 | ||||
-rw-r--r-- | xbmc/Application.cpp | 13 | ||||
-rw-r--r-- | xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp | 69 | ||||
-rw-r--r-- | xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h | 6 | ||||
-rw-r--r-- | xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp | 14 | ||||
-rw-r--r-- | xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h | 1 | ||||
-rw-r--r-- | xbmc/cores/VideoSettings.h | 6 | ||||
-rw-r--r-- | xbmc/input/actions/ActionIDs.h | 2 | ||||
-rw-r--r-- | xbmc/input/actions/ActionTranslator.cpp | 3 | ||||
-rw-r--r-- | xbmc/video/dialogs/GUIDialogVideoSettings.cpp | 3 |
12 files changed, 195 insertions, 40 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index 7241d0cc9f..2bce2babdc 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -20549,7 +20549,19 @@ msgctxt "#36556" msgid "Tone mapping parameter" msgstr "" -#empty strings from id 36557 to 36559 +#. label of a setting, tone mapping method +#: xbmc/video/dialogs/GUIDialogVideoSettings.cpp +msgctxt "#36557" +msgid "ACES Filmic" +msgstr "" + +#. label of a setting, tone mapping method +#: xbmc/video/dialogs/GUIDialogVideoSettings.cpp +msgctxt "#36558" +msgid "Hable" +msgstr "" + +#empty string with id 36559 #: system/settings/settings.xml msgctxt "#36560" diff --git a/system/keymaps/keyboard.xml b/system/keymaps/keyboard.xml index 5cb986f1b6..694b6b10cc 100644 --- a/system/keymaps/keyboard.xml +++ b/system/keymaps/keyboard.xml @@ -398,6 +398,7 @@ <minus mod="ctrl">VolAmpDown</minus> <b mod="ctrl">CreateBookmark</b> <b mod="alt">CreateEpisodeBookmark</b> + <f11 mod="alt">CycleToneMapMethod</f11> </keyboard> </FullscreenVideo> <FullscreenGame> diff --git a/system/shaders/output_d3d.fx b/system/shaders/output_d3d.fx index 06233b692f..6a251b0723 100644 --- a/system/shaders/output_d3d.fx +++ b/system/shaders/output_d3d.fx @@ -41,58 +41,113 @@ SamplerState DitherSampler : IMMUTABLE Filter = MIN_MAG_MIP_POINT; }; #endif -#if defined(KODI_TONE_MAPPING) +#if (defined(KODI_TONE_MAPPING_ACES) || defined(KODI_TONE_MAPPING_HABLE) || defined(KODI_HLG_TO_PQ)) +const float ST2084_m1 = 2610.0f / (4096.0f * 4.0f); +const float ST2084_m2 = (2523.0f / 4096.0f) * 128.0f; +const float ST2084_c1 = 3424.0f / 4096.0f; +const float ST2084_c2 = (2413.0f / 4096.0f) * 32.0f; +const float ST2084_c3 = (2392.0f / 4096.0f) * 32.0f; +#endif +#if defined(KODI_TONE_MAPPING_REINHARD) float g_toneP1; float3 g_coefsDst; +float g_luminance; + +float reinhard(float x) +{ + return x * (1.0f + x / (g_toneP1 * g_toneP1)) / (1.0f + x); +} +#endif +#if defined(KODI_TONE_MAPPING_ACES) +float g_luminance; +float g_toneP1; -float tonemap(float val) +float3 aces(float3 x) { - return val * (1 + val/(g_toneP1*g_toneP1))/(1 + val); + const float A = 2.51f; + const float B = 0.03f; + const float C = 2.43f; + const float D = 0.59f; + const float E = 0.14f; + return (x * (A * x + B)) / (x * (C * x + D) + E); +} +#endif +#if defined(KODI_TONE_MAPPING_HABLE) +float g_luminance; +float g_toneP1; + +float3 hable(float3 x) +{ + const float A = 0.15f; + const float B = 0.5f; + const float C = 0.1f; + const float D = 0.2f; + const float E = 0.02f; + const float F = 0.3f; + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} +#endif +#if (defined(KODI_TONE_MAPPING_ACES) || defined(KODI_TONE_MAPPING_HABLE)) +float3 inversePQ(float3 x) +{ + x = pow(max(x, 0.0f), 1.0f / ST2084_m2); + x = max(x - ST2084_c1, 0.0f) / (ST2084_c2 - ST2084_c3 * x); + x = pow(x, 1.0f / ST2084_m1); + return x; } #endif #if defined(KODI_HLG_TO_PQ) float inverseHLG(float x) { - const float B67_a = 0.17883277; - const float B67_b = 0.28466892; - const float B67_c = 0.55991073; - const float B67_inv_r2 = 4.0; - if (x <= 0.5) + const float B67_a = 0.17883277f; + const float B67_b = 0.28466892f; + const float B67_c = 0.55991073f; + const float B67_inv_r2 = 4.0f; + if (x <= 0.5f) x = x * x * B67_inv_r2; else x = exp((x - B67_c) / B67_a) + B67_b; return x; } -float4 tranferPQ(float4 color) +float3 tranferPQ(float3 x) { - const float ST2084_m1 = 2610.0 / (4096.0 * 4.0); - const float ST2084_m2 = (2523.0 / 4096.0) * 128.0; - const float ST2084_c1 = 3424.0 / 4096.0; - const float ST2084_c2 = (2413.0 / 4096.0) * 32.0; - const float ST2084_c3 = (2392.0 / 4096.0) * 32.0; - color = pow(color / 1000.0, ST2084_m1); - color = (ST2084_c1 + ST2084_c2 * color) / (1 + ST2084_c3 * color); - color = pow(color, ST2084_m2); - return color; + x = pow(x / 1000.0f, ST2084_m1); + x = (ST2084_c1 + ST2084_c2 * x) / (1.0f + ST2084_c3 * x); + x = pow(x, ST2084_m2); + return x; } #endif float4 output4(float4 color, float2 uv) { -#if defined(KODI_TONE_MAPPING) +#if defined(KODI_TONE_MAPPING_REINHARD) float luma = dot(color.rgb, g_coefsDst); - color.rgb *= tonemap(luma) / luma; + color.rgb *= reinhard(luma) / luma; +#endif +#if defined(KODI_TONE_MAPPING_ACES) + color.rgb = inversePQ(color.rgb); + color.rgb *= (10000.0f / g_luminance) * (2.0f / g_toneP1); + color.rgb = aces(color.rgb); + color.rgb *= (1.24f / g_toneP1); + color.rgb = pow(color.rgb, 0.27f); +#endif +#if defined(KODI_TONE_MAPPING_HABLE) + color.rgb = inversePQ(color.rgb); + color.rgb *= g_toneP1; + float wp = g_luminance / 100.0f; + color.rgb = hable(color.rgb * wp) / hable(wp); + color.rgb = pow(color.rgb, 1.0f / 2.2f); #endif #if defined(KODI_HLG_TO_PQ) color.r = inverseHLG(color.r); color.g = inverseHLG(color.g); color.b = inverseHLG(color.b); - float3 ootf_2020 = float3(0.2627, 0.6780, 0.0593); - float ootf_ys = 2000.0 * dot(ootf_2020, color); - color = color * pow(ootf_ys, 0.200); - color = tranferPQ(color); + float3 ootf_2020 = float3(0.2627f, 0.6780f, 0.0593f); + float ootf_ys = 2000.0f * dot(ootf_2020, color.rgb); + color.rgb *= pow(ootf_ys, 0.2f); + color.rgb = tranferPQ(color.rgb); #endif #if defined(KODI_3DLUT) half3 scale = m_LUTParams.x; diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index f14d1c3627..fb4978c27c 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -1659,6 +1659,19 @@ bool CApplication::OnAction(const CAction &action) } return true; } + // Tone Mapping : switch to next tone map method + if (action.GetID() == ACTION_CYCLE_TONEMAP_METHOD) + { + if (m_appPlayer.IsPlayingVideo()) + { + CVideoSettings vs = m_appPlayer.GetVideoSettings(); + vs.m_ToneMapMethod++; + if (vs.m_ToneMapMethod >= VS_TONEMAPMETHOD_MAX) + vs.m_ToneMapMethod = VS_TONEMAPMETHOD_OFF + 1; + m_appPlayer.SetVideoSettings(vs); + } + return true; + } // built in functions : execute the built-in if (action.GetID() == ACTION_BUILT_IN_FUNCTION) { diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp index 95de271561..3c2d472ab8 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.cpp @@ -185,7 +185,7 @@ void COutputShader::ApplyEffectParameters(CD3DEffect &effect, unsigned sourceWid effect.SetResources("m_ditherMatrix", m_pDitherView.GetAddressOf(), 1); effect.SetFloatArray("m_ditherParams", ditherParams, 3); } - if (m_toneMapping) + if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_REINHARD) { const float def_param = log10(100.0f) / log10(600.0f); // 600 nits --> 0.72 float param = def_param; @@ -208,6 +208,49 @@ void COutputShader::ApplyEffectParameters(CD3DEffect &effect, unsigned sourceWid effect.SetScalar("g_toneP1", param); effect.SetFloatArray("g_coefsDst", coefs, 3); } + else if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_ACES) + { + effect.SetScalar("g_luminance", GetLuminanceValue()); + effect.SetScalar("g_toneP1", m_toneMappingParam); + } + else if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_HABLE) + { + float lumin = GetLuminanceValue(); + float param = (10000.0f / lumin) * (2.0f / m_toneMappingParam); + effect.SetScalar("g_luminance", lumin); + effect.SetScalar("g_toneP1", param); + } +} + +float COutputShader::GetLuminanceValue() const +{ + float lum1 = 400.0f; // default for bad quality HDR-PQ sources (with no metadata) + float lum2 = lum1; + float lum3 = lum1; + + if (m_hasLightMetadata) + { + uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den; + if (m_lightMetadata.MaxCLL >= lum) + { + lum1 = static_cast<float>(lum); + lum2 = static_cast<float>(m_lightMetadata.MaxCLL); + } + else + { + lum1 = static_cast<float>(m_lightMetadata.MaxCLL); + lum2 = static_cast<float>(lum); + } + lum3 = static_cast<float>(m_lightMetadata.MaxFALL); + lum1 = (lum1 * 0.5f) + (lum2 * 0.2f) + (lum3 * 0.3f); + } + else if (m_hasDisplayMetadata && m_displayMetadata.has_luminance) + { + uint16_t lum = m_displayMetadata.max_luminance.num / m_displayMetadata.max_luminance.den; + lum1 = static_cast<float>(lum); + } + + return lum1; } void COutputShader::GetDefines(DefinesMap& map) const @@ -220,9 +263,17 @@ void COutputShader::GetDefines(DefinesMap& map) const { map["KODI_DITHER"] = ""; } - if (m_toneMapping) + if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_REINHARD) { - map["KODI_TONE_MAPPING"] = ""; + map["KODI_TONE_MAPPING_REINHARD"] = ""; + } + else if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_ACES) + { + map["KODI_TONE_MAPPING_ACES"] = ""; + } + else if (m_toneMapping && m_toneMappingMethod == VS_TONEMAPMETHOD_HABLE) + { + map["KODI_TONE_MAPPING_HABLE"] = ""; } if (m_useHLGtoPQ) { @@ -230,12 +281,14 @@ void COutputShader::GetDefines(DefinesMap& map) const } } -bool COutputShader::Create(bool useLUT, bool useDithering, int ditherDepth, bool toneMapping, bool HLGtoPQ) +bool COutputShader::Create( + bool useLUT, bool useDithering, int ditherDepth, bool toneMapping, int toneMethod, bool HLGtoPQ) { m_useLut = useLUT; m_ditherDepth = ditherDepth; - m_toneMapping = toneMapping && !DX::Windowing()->IsHDROutput(); + m_toneMapping = toneMapping; m_useHLGtoPQ = HLGtoPQ; + m_toneMappingMethod = toneMethod; CWinShader::CreateVertexBuffer(4, sizeof(Vertex)); @@ -298,6 +351,12 @@ void COutputShader::SetDisplayMetadata(bool hasDisplayMetadata, AVMasteringDispl m_lightMetadata = lightMetadata; } +void COutputShader::SetToneMapParam(int method, float param) +{ + m_toneMappingMethod = method; + m_toneMappingParam = param; +} + bool COutputShader::CreateLUTView(int lutSize, uint16_t* lutData, bool isRGB, ID3D11ShaderResourceView** ppLUTView) { if (!lutSize || !lutData) diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h index 5ab850aafa..2864d8310f 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/WinVideoFilter.h @@ -60,7 +60,7 @@ public: void ApplyEffectParameters(CD3DEffect &effect, unsigned sourceWidth, unsigned sourceHeight); void GetDefines(DefinesMap &map) const; - bool Create(bool useLUT, bool useDithering, int ditherDepth, bool toneMapping, bool HLGtoPQ); + bool Create(bool useLUT, bool useDithering, int ditherDepth, bool toneMapping, int toneMethod, bool HLGtoPQ); void Render(CD3DTexture& sourceTexture, CRect sourceRect, const CPoint points[4] , CD3DTexture& target, unsigned range = 0, float contrast = 0.5f, float brightness = 0.5f); void Render(CD3DTexture& sourceTexture, CRect sourceRect, CRect destRect @@ -68,7 +68,7 @@ public: void SetLUT(int lutSize, ID3D11ShaderResourceView *pLUTView); void SetDisplayMetadata(bool hasDisplayMetadata, AVMasteringDisplayMetadata displayMetadata, bool hasLightMetadata, AVContentLightMetadata lightMetadata); - void SetToneMapParam(float param) { m_toneMappingParam = param; } + void SetToneMapParam(int method, float param); static bool CreateLUTView(int lutSize, uint16_t* lutData, bool isRGB, ID3D11ShaderResourceView** ppLUTView); @@ -83,6 +83,7 @@ private: void PrepareParameters(unsigned sourceWidth, unsigned sourceHeight, CRect sourceRect, const CPoint points[4]); void SetShaderParameters(CD3DTexture &sourceTexture, unsigned range, float contrast, float brightness); void CreateDitherView(); + float GetLuminanceValue() const; bool m_useLut = false; bool m_useDithering = false; @@ -95,6 +96,7 @@ private: unsigned m_sourceHeight = 0; int m_lutSize = 0; int m_ditherDepth = 0; + int m_toneMappingMethod = VS_TONEMAPMETHOD_REINHARD; float m_toneMappingParam = 1.0f; CRect m_sourceRect = {}; diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp index 0dbec67ac9..b888859b43 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp @@ -247,7 +247,7 @@ void CRendererBase::Render(CD3DTexture& target, const CRect& sourceRect, const C if (UseToneMapping()) { m_outputShader->SetDisplayMetadata(buf->hasDisplayMetadata, buf->displayMetadata, buf->hasLightMetadata, buf->lightMetadata); - m_outputShader->SetToneMapParam(m_videoSettings.m_ToneMapParam); + m_outputShader->SetToneMapParam(m_toneMapMethod, m_videoSettings.m_ToneMapParam); } FinalOutput(m_IntermediateTarget, target, source, dest); @@ -407,7 +407,8 @@ void CRendererBase::UpdateVideoFilters() if (!m_outputShader) { m_outputShader = std::make_shared<COutputShader>(); - if (!m_outputShader->Create(m_cmsOn, m_useDithering, m_ditherDepth, UseToneMapping(), m_useHLGtoPQ)) + if (!m_outputShader->Create(m_cmsOn, m_useDithering, m_ditherDepth, m_toneMapping, + m_toneMapMethod, m_useHLGtoPQ)) { CLog::LogF(LOGDEBUG, "unable to create output shader."); m_outputShader.reset(); @@ -422,20 +423,21 @@ void CRendererBase::UpdateVideoFilters() void CRendererBase::CheckVideoParameters() { CRenderBuffer* buf = m_renderBuffers[m_iBufferIndex]; + int method = m_videoSettings.m_ToneMapMethod; bool isHDRPQ = (buf->color_transfer == AVCOL_TRC_SMPTE2084 && buf->primaries == AVCOL_PRI_BT2020); - // HDR_TYPE::HDR_NONE_SDR is equivalent to !DX::Windowing()->IsHDROutput() using local variable - bool toneMap = (isHDRPQ && m_HdrType == HDR_TYPE::HDR_NONE_SDR && - m_videoSettings.m_ToneMapMethod != VS_TONEMAPMETHOD_OFF); + bool toneMap = (isHDRPQ && m_HdrType == HDR_TYPE::HDR_NONE_SDR && method != VS_TONEMAPMETHOD_OFF); bool hlg = (m_HdrType == HDR_TYPE::HDR_HLG); - if (toneMap != m_toneMapping || m_cmsOn != m_colorManager->IsEnabled() || hlg != m_useHLGtoPQ) + if (toneMap != m_toneMapping || m_cmsOn != m_colorManager->IsEnabled() || hlg != m_useHLGtoPQ || + method != m_toneMapMethod) { m_toneMapping = toneMap; m_cmsOn = m_colorManager->IsEnabled(); m_useHLGtoPQ = hlg; + m_toneMapMethod = method; m_outputShader.reset(); OnOutputReset(); diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h index 3dc074c385..c7a1ff8ac2 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.h @@ -158,6 +158,7 @@ protected: bool m_cmsOn = false; bool m_clutLoaded = false; bool m_useHLGtoPQ = false; + int m_toneMapMethod = 0; int m_iBufferIndex = 0; int m_iNumBuffers = 0; diff --git a/xbmc/cores/VideoSettings.h b/xbmc/cores/VideoSettings.h index 687c84a530..ba7efeb797 100644 --- a/xbmc/cores/VideoSettings.h +++ b/xbmc/cores/VideoSettings.h @@ -60,8 +60,10 @@ enum ESCALINGMETHOD enum ETONEMAPMETHOD { - VS_TONEMAPMETHOD_OFF=0, - VS_TONEMAPMETHOD_REINHARD, + VS_TONEMAPMETHOD_OFF = 0, + VS_TONEMAPMETHOD_REINHARD = 1, + VS_TONEMAPMETHOD_ACES = 2, + VS_TONEMAPMETHOD_HABLE = 3, VS_TONEMAPMETHOD_MAX }; diff --git a/xbmc/input/actions/ActionIDs.h b/xbmc/input/actions/ActionIDs.h index 0a3ffd37cf..01233668f9 100644 --- a/xbmc/input/actions/ActionIDs.h +++ b/xbmc/input/actions/ActionIDs.h @@ -430,6 +430,8 @@ #define ACTION_HDR_TOGGLE 260 //!< Toggle display HDR on/off +#define ACTION_CYCLE_TONEMAP_METHOD 261 //!< Switch to next tonemap method + // Voice actions #define ACTION_VOICE_RECOGNIZE 300 diff --git a/xbmc/input/actions/ActionTranslator.cpp b/xbmc/input/actions/ActionTranslator.cpp index 877bd3b9ab..774d225d6e 100644 --- a/xbmc/input/actions/ActionTranslator.cpp +++ b/xbmc/input/actions/ActionTranslator.cpp @@ -204,6 +204,9 @@ static const std::map<ActionName, ActionID> ActionMappings = { // HDR display support {"hdrtoggle", ACTION_HDR_TOGGLE}, + // Tone mapping + {"cycletonemapmethod", ACTION_CYCLE_TONEMAP_METHOD}, + // PVR actions {"channelup", ACTION_CHANNEL_UP}, {"channeldown", ACTION_CHANNEL_DOWN}, diff --git a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp index 3eb6957b4d..e3f99a54d4 100644 --- a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp +++ b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp @@ -412,6 +412,9 @@ void CGUIDialogVideoSettings::InitializeSettings() entries.clear(); entries.push_back(TranslatableIntegerSettingOption(36554, VS_TONEMAPMETHOD_OFF)); entries.push_back(TranslatableIntegerSettingOption(36555, VS_TONEMAPMETHOD_REINHARD)); + entries.push_back(TranslatableIntegerSettingOption(36557, VS_TONEMAPMETHOD_ACES)); + entries.push_back(TranslatableIntegerSettingOption(36558, VS_TONEMAPMETHOD_HABLE)); + AddSpinner(groupVideo, SETTING_VIDEO_TONEMAP_METHOD, 36553, SettingLevel::Basic, videoSettings.m_ToneMapMethod, entries, false, visible); AddSlider(groupVideo, SETTING_VIDEO_TONEMAP_PARAM, 36556, SettingLevel::Basic, |