diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj index f968942ec..4fe90d87c 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj @@ -35,8 +35,8 @@ Windows Store 10.0 true - 10.0.17134.0 - 10.0.17134.0 + 10.0.17763.0 + 10.0.17763.0 D2DAdvancedColorImages 78009E8D6FA6B7A714605247C1C6E95F0CDF94CC D2DAdvancedColorImages_StoreKey.pfx @@ -144,14 +144,12 @@ :: Parse the Visual Studio macro into a form usable by fxc.exe. set INCLUDEPATHS="$(WindowsSDK_IncludePath)" set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "% -fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% @@ -160,10 +158,10 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main Generate shader-linking compatible pixel shaders for D2D custom effects - $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h + $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h - ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl + SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl true @@ -187,26 +185,24 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main :: Parse the Visual Studio macro into a form usable by fxc.exe. set INCLUDEPATHS="$(WindowsSDK_IncludePath)" set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "% -fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% Generate shader-linking compatible pixel shaders for D2D custom effects - $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h + $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h - ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl + SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl true @@ -230,14 +226,12 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main :: Parse the Visual Studio macro into a form usable by fxc.exe. set INCLUDEPATHS="$(WindowsSDK_IncludePath)" set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "% -fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% @@ -246,10 +240,10 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main Generate shader-linking compatible pixel shaders for D2D custom effects - $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h + $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h - ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl + SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl true @@ -273,26 +267,24 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main :: Parse the Visual Studio macro into a form usable by fxc.exe. set INCLUDEPATHS="$(WindowsSDK_IncludePath)" set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "% -fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% Generate shader-linking compatible pixel shaders for D2D custom effects - $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h + $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h - ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl + SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl true @@ -316,14 +308,12 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main :: Parse the Visual Studio macro into a form usable by fxc.exe. set INCLUDEPATHS="$(WindowsSDK_IncludePath)" set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "% -fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% @@ -332,10 +322,10 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main Generate shader-linking compatible pixel shaders for D2D custom effects - $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h + $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h - ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl + SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl true @@ -359,26 +349,24 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main :: Parse the Visual Studio macro into a form usable by fxc.exe. set INCLUDEPATHS="$(WindowsSDK_IncludePath)" set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "% -fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS% fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS% fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS% -fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% -fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% +fxc /T lib_4_0 SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS% +fxc /T ps_4_0 SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS% Generate shader-linking compatible pixel shaders for D2D custom effects - $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h + $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h - ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl + SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl true @@ -432,12 +420,12 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main DirectXPage.xaml - - + + @@ -451,7 +439,6 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main DirectXPage.xaml - Create @@ -462,7 +449,7 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main Create - + @@ -479,16 +466,6 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main Designer - - - Document - - - - - Document - - Document @@ -511,6 +488,11 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main + + + Document + + diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj.filters b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj.filters index e11b2e684..cacbc4119 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj.filters +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj.filters @@ -29,12 +29,6 @@ Common - - RenderEffects - - - RenderEffects - RenderEffects @@ -47,6 +41,9 @@ RenderEffects + + RenderEffects + @@ -63,12 +60,6 @@ - - RenderEffects - - - RenderEffects - RenderEffects @@ -81,6 +72,10 @@ RenderEffects + + + RenderEffects + @@ -196,12 +191,6 @@ - - RenderEffects - - - RenderEffects - RenderEffects @@ -211,6 +200,9 @@ RenderEffects + + RenderEffects + diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.cpp b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.cpp index d6482c892..4506afa9f 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.cpp +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.cpp @@ -14,6 +14,7 @@ #include "DirectXPage.xaml.h" #include "DirectXHelper.h" #include "DirectXTex.h" +#include "SimpleTonemapEffect.h" #include "DirectXTex\DirectXTexEXR.h" using namespace D2DAdvancedColorImages; @@ -31,6 +32,10 @@ using namespace Windows::Storage::Streams; using namespace Windows::UI::Input; using namespace Windows::UI::Xaml; +static const float sc_DefaultHdrDispMaxNits = 1499.0f; // Experimentally chosen for compatibility with 2018 TVs. +static const float sc_DefaultSdrDispMaxNits = 270.0f; // Experimentally chosen based on typical SDR displays. +static const float sc_DefaultImageMaxCLL = 1000.0f; // Needs more tuning based on real world content. +static const float sc_DefaultImageMedCLL = 200.0f; // Needs more tuning based on real world content. static const float sc_MaxZoom = 1.0f; // Restrict max zoom to 1:1 scale. static const unsigned int sc_MaxBytesPerPixel = 16; // Covers all supported image formats. static const float sc_MinZoomSphereMap = 0.25f; @@ -51,7 +56,7 @@ D2DAdvancedColorImagesRenderer::D2DAdvancedColorImagesRenderer( m_minZoom(1.0f), // Dynamically calculated on window size. m_imageOffset(), m_pointerPos(), - m_maxCLL(-1.0f), + m_imageCLL{ -1.0f, -1.0f }, m_brightnessAdjust(1.0f), m_imageInfo{}, m_isComputeSupported(false) @@ -74,11 +79,7 @@ void D2DAdvancedColorImagesRenderer::CreateDeviceIndependentResources() { // Register the custom render effects. DX::ThrowIfFailed( - ReinhardEffect::Register(m_deviceResources->GetD2DFactory()) - ); - - DX::ThrowIfFailed( - FilmicEffect::Register(m_deviceResources->GetD2DFactory()) + SimpleTonemapEffect::Register(m_deviceResources->GetD2DFactory()) ); DX::ThrowIfFailed( @@ -109,7 +110,8 @@ void D2DAdvancedColorImagesRenderer::ReleaseDeviceDependentResources() // to update the app's sizing and advanced color state. void D2DAdvancedColorImagesRenderer::CreateWindowSizeDependentResources() { - FitImageToWindow(); + // Window size changes don't require recomputing image HDR metadata. + FitImageToWindow(false); } // White level scale is used to multiply the color values in the image; allows the user to @@ -133,15 +135,20 @@ void D2DAdvancedColorImagesRenderer::SetRenderOptions( // after the effect as their numerical output is affected by any luminance boost. switch (m_renderEffectKind) { - // Effect graph: ImageSource > ColorManagement > WhiteScale > Reinhard - case RenderEffectKind::ReinhardTonemap: - m_finalOutput = m_reinhardEffect.Get(); - m_whiteScaleEffect->SetInputEffect(0, m_colorManagementEffect.Get()); - break; + // Effect graph: ImageSource > ColorManagement > WhiteScale > HDRTonemap > WhiteScale2* + case RenderEffectKind::HdrTonemap: + if (m_dispInfo->CurrentAdvancedColorKind != AdvancedColorKind::HighDynamicRange) + { + // *Second white scale is needed as an integral part of using the Direct2D HDR + // tonemapper on SDR/WCG displays to stay within [0, 1] numeric range. + m_finalOutput = m_sdrWhiteScaleEffect.Get(); + } + else + { + m_finalOutput = m_hdrTonemapEffect.Get(); + } - // Effect graph: ImageSource > ColorManagement > WhiteScale > FilmicTonemap - case RenderEffectKind::FilmicTonemap: - m_finalOutput = m_filmicEffect.Get(); + m_sdrWhiteScaleEffect->SetInputEffect(0, m_hdrTonemapEffect.Get()); m_whiteScaleEffect->SetInputEffect(0, m_colorManagementEffect.Get()); break; @@ -174,11 +181,48 @@ void D2DAdvancedColorImagesRenderer::SetRenderOptions( break; } + float targetMaxNits = GetBestDispMaxLuminance(); + + // Update HDR tonemappers with display information. + // The 1803 custom tonemapper uses mostly the same property definitions as the 1809 Direct2D tonemapper, for simplicity. + DX::ThrowIfFailed(m_hdrTonemapEffect->SetValue(D2D1_HDRTONEMAP_PROP_OUTPUT_MAX_LUMINANCE, targetMaxNits)); + + float maxCLL = m_imageCLL.maxNits != -1.0f ? m_imageCLL.maxNits : sc_DefaultImageMaxCLL; + maxCLL *= m_brightnessAdjust; + + // Very low input max luminance can produce unexpected rendering behavior. Restrict to + // a reasonable level - the Direct2D tonemapper performs nearly a no-op if input < output max nits. + maxCLL = max(maxCLL, 0.5f * targetMaxNits); + + DX::ThrowIfFailed(m_hdrTonemapEffect->SetValue(D2D1_HDRTONEMAP_PROP_INPUT_MAX_LUMINANCE, maxCLL)); + + // The 1809 Direct2D tonemapper also optimizes for HDR or SDR displays; the 1803 custom tonemapper does not. + if (DX::CheckPlatformSupport(DX::Win1809)) + { + D2D1_HDRTONEMAP_DISPLAY_MODE mode = + m_dispInfo->CurrentAdvancedColorKind == AdvancedColorKind::HighDynamicRange ? + D2D1_HDRTONEMAP_DISPLAY_MODE_HDR : D2D1_HDRTONEMAP_DISPLAY_MODE_SDR; + + DX::ThrowIfFailed(m_hdrTonemapEffect->SetValue(D2D1_HDRTONEMAP_PROP_DISPLAY_MODE, mode)); + } + + // If an HDR tonemapper is used on an SDR or WCG display, perform additional white level correction. + if (m_dispInfo->CurrentAdvancedColorKind != AdvancedColorKind::HighDynamicRange) + { + // Both the D2D and custom HDR tonemappers output values in scRGB using scene-referred luminance - a typical SDR display will + // be around numeric range [0.0, 3.0] corresponding to [0, 240 nits]. To encode correctly for an SDR/WCG display + // output, we must reinterpret the scene-referred input content (80 nits) as display-referred (targetMaxNits). + DX::ThrowIfFailed( + m_sdrWhiteScaleEffect->SetValue(D2D1_WHITELEVELADJUSTMENT_PROP_INPUT_WHITE_LEVEL, D2D1_SCENE_REFERRED_SDR_WHITE_LEVEL)); + + DX::ThrowIfFailed( + m_sdrWhiteScaleEffect->SetValue(D2D1_WHITELEVELADJUSTMENT_PROP_OUTPUT_WHITE_LEVEL, targetMaxNits)); + } + Draw(); } -// Reads the provided data stream and decodes an image from it using WIC. These resources are device- -// independent. +// Reads the provided data stream and decodes an image from it using WIC. These resources are device-independent. ImageInfo D2DAdvancedColorImagesRenderer::LoadImageFromWic(_In_ IStream* imageStream) { auto wicFactory = m_deviceResources->GetWicImagingFactory(); @@ -368,14 +412,31 @@ void D2DAdvancedColorImagesRenderer::CreateImageDependentResources() // Set the actual matrix in SetRenderOptions. // Instantiate and cache all of the tonemapping/render effects. - // Each effect is implemented as a Direct2D custom effect; see the RenderEffects filter in the + // Some effects are implemented as Direct2D custom effects; see the RenderEffects filter in the // Solution Explorer. + + GUID sdrWhiteScale = {}; + GUID tonemapper = {}; + if (DX::CheckPlatformSupport(DX::Win1809)) + { + // HDR tonemapper and white level adjust are only available in 1809 and above. + tonemapper = CLSID_D2D1HdrToneMap; + sdrWhiteScale = CLSID_D2D1WhiteLevelAdjustment; + } + else + { + tonemapper = CLSID_CustomSimpleTonemapEffect; + + // For 1803, this effect should never actually be rendered. Invert is a good "sentinel". + sdrWhiteScale = CLSID_D2D1Invert; + } + DX::ThrowIfFailed( - context->CreateEffect(CLSID_CustomReinhardEffect, &m_reinhardEffect) + context->CreateEffect(tonemapper, &m_hdrTonemapEffect) ); DX::ThrowIfFailed( - context->CreateEffect(CLSID_CustomFilmicEffect, &m_filmicEffect) + context->CreateEffect(sdrWhiteScale, &m_sdrWhiteScaleEffect) ); DX::ThrowIfFailed( @@ -401,8 +462,7 @@ void D2DAdvancedColorImagesRenderer::CreateImageDependentResources() border->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP); border->SetInputEffect(0, m_whiteScaleEffect.Get()); - m_reinhardEffect->SetInputEffect(0, m_whiteScaleEffect.Get()); - m_filmicEffect->SetInputEffect(0, m_whiteScaleEffect.Get()); + m_hdrTonemapEffect->SetInputEffect(0, m_whiteScaleEffect.Get()); m_sphereMapEffect->SetInputEffect(0, border.Get()); // SphereMap needs to know the pixel size of the image. @@ -460,8 +520,8 @@ void D2DAdvancedColorImagesRenderer::CreateHistogramResources() // HDR10 image data is encoded as [0, 1] UNORM values, which represents [0, 10000] nits. // This should be converted to scRGB [0, 125] FP16 values (10000 / 80 nits reference), but // instead remains as scRGB [0, 1] FP16 values, or [0, 80] nits. - // This is fixed in Windows 10 version 1809 (UniversalApiContract version 7). - if (m_imageInfo.isXboxHdrScreenshot && !Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7)) + // This is fixed in Windows 10 version 1809. + if (m_imageInfo.isXboxHdrScreenshot && !DX::CheckPlatformSupport(DX::Win1809)) { scale /= 125.0f; } @@ -591,8 +651,8 @@ void D2DAdvancedColorImagesRenderer::ReleaseImageDependentResources() m_scaledImage.Reset(); m_colorManagementEffect.Reset(); m_whiteScaleEffect.Reset(); - m_reinhardEffect.Reset(); - m_filmicEffect.Reset(); + m_sdrWhiteScaleEffect.Reset(); + m_hdrTonemapEffect.Reset(); m_sdrOverlayEffect.Reset(); m_heatmapEffect.Reset(); m_histogramPrescale.Reset(); @@ -658,8 +718,9 @@ void D2DAdvancedColorImagesRenderer::UpdateManipulationState(_In_ ManipulationUp } // Overrides any pan/zoom state set by the user to fit image to the window size. -// Returns the computed MaxCLL of the image in nits. -float D2DAdvancedColorImagesRenderer::FitImageToWindow() +// Returns the computed content light level (CLL) of the image in nits. +// Recomputing the HDR metadata is only needed when loading a new image. +ImageCLL D2DAdvancedColorImagesRenderer::FitImageToWindow(bool computeMetadata) { if (m_imageSource) { @@ -691,7 +752,7 @@ float D2DAdvancedColorImagesRenderer::FitImageToWindow() ComputeHdrMetadata(); } - return m_maxCLL; + return m_imageCLL; } // After initial decode, obtain image information and do common setup. @@ -905,8 +966,8 @@ void D2DAdvancedColorImagesRenderer::UpdateWhiteLevelScale(float brightnessAdjus // HDR10 image data is encoded as [0, 1] UNORM values, which represents [0, 10000] nits. // This should be converted to scRGB [0, 125] FP16 values (10000 / 80 nits reference), but // instead remains as scRGB [0, 1] FP16 values, or [0, 80] nits. - // This is fixed in Windows 10 version 1809 (UniversalApiContract version 7). - if (m_imageInfo.isXboxHdrScreenshot && !Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7)) + // This is fixed in Windows 10 version 1809. + if (m_imageInfo.isXboxHdrScreenshot && !DX::CheckPlatformSupport(DX::Win1809)) { scale *= 125.0f; } @@ -957,14 +1018,15 @@ void D2DAdvancedColorImagesRenderer::UpdateImageTransformState() } } -// Uses a histogram to compute a modified version of MaxCLL (ST.2086 max content light level). +// Uses a histogram to compute a modified version of maximum content light level/ST.2086 MaxCLL +// and average content light level. // Performs Begin/EndDraw on the D2D context. void D2DAdvancedColorImagesRenderer::ComputeHdrMetadata() { // Initialize with a sentinel value. - m_maxCLL = -1.0f; + m_imageCLL = { -1.0f, -1.0f }; - // MaxCLL is not meaningful for SDR or WCG images. + // HDR metadata is not meaningful for SDR or WCG images. if ((!m_isComputeSupported) || (m_imageInfo.imageKind != AdvancedColorKind::HighDynamicRange)) { @@ -999,23 +1061,41 @@ void D2DAdvancedColorImagesRenderer::ComputeHdrMetadata() ); unsigned int maxCLLbin = 0; + unsigned int avgCLLbin = 0; // Average is defined as 50th percentile. float runningSum = 0.0f; // Cumulative sum of values in histogram is 1.0. for (int i = sc_histNumBins - 1; i >= 0; i--) { runningSum += histogramData[i]; - maxCLLbin = i; - if (runningSum >= 1.0f - maxCLLPercent) + // Note the inequality (<) is the opposite of the next if block. + if (runningSum < 1.0f - maxCLLPercent) { + maxCLLbin = i; + } + + if (runningSum > 0.5f) + { + // Note if the entire histogram is 0, avgCLLbin remains at -1. + avgCLLbin = i; break; } } - float binNorm = static_cast(maxCLLbin) / static_cast(sc_histNumBins); - m_maxCLL = powf(binNorm, 1 / sc_histGamma) * sc_histMaxNits; + float binNormMax = static_cast(maxCLLbin) / static_cast(sc_histNumBins); + m_imageCLL.maxNits = powf(binNormMax, 1 / sc_histGamma) * sc_histMaxNits; + + float binNormAvg = static_cast(avgCLLbin) / static_cast(sc_histNumBins); + m_imageCLL.medNits = powf(binNormAvg, 1 / sc_histGamma) * sc_histMaxNits; + + // Some drivers have a bug where histogram will always return 0. Or some images are pure black. + // Treat these cases as unknown. + if (m_imageCLL.maxNits == 0.0f) + { + m_imageCLL = { -1.0f, -1.0f }; + } - // Some drivers have a bug where histogram will always return 0. Treat this as unknown. - m_maxCLL = (m_maxCLL == 0.0f) ? -1.0f : m_maxCLL; + // HDR metadata computation is completed before the app rendering options are known, so don't + // attempt to draw yet. } // Set HDR10 metadata to allow HDR displays to optimize behavior based on our content. @@ -1046,7 +1126,7 @@ void D2DAdvancedColorImagesRenderer::EmitHdrMetadata() // Currently only the "None" render effect results in pixel values that exceed // the OS-specified SDR white level, as it just passes through HDR color values. case RenderEffectKind::None: - effectiveMaxCLL = max(m_maxCLL, 0.0f) * m_brightnessAdjust; + effectiveMaxCLL = max(m_imageCLL.maxNits, 0.0f) * m_brightnessAdjust; break; default: @@ -1069,6 +1149,29 @@ void D2DAdvancedColorImagesRenderer::EmitHdrMetadata() } } +// If AdvancedColorInfo does not have valid data, picks an appropriate default value. +float D2DAdvancedColorImagesRenderer::GetBestDispMaxLuminance() +{ + float val = m_dispInfo->MaxLuminanceInNits; + + if (val == 0.0f) + { + if (m_dispInfo->CurrentAdvancedColorKind == AdvancedColorKind::HighDynamicRange) + { + // HDR TVs generally don't report metadata, but monitors do. + val = sc_DefaultHdrDispMaxNits; + } + else + { + // Almost no SDR displays report HDR metadata. WCG displays generally should report HDR metadata. + // We assume both SDR and WCG displays have similar peak luminances and use the same constants. + val = sc_DefaultSdrDispMaxNits; + } + } + + return val; +} + // Renders the loaded image with user-specified options. void D2DAdvancedColorImagesRenderer::Draw() { diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.h index b9f7a38b4..2b1076b62 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.h +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.h @@ -12,8 +12,6 @@ #pragma once #include "DeviceResources.h" -#include "ReinhardEffect.h" -#include "FilmicEffect.h" #include "SdrOverlayEffect.h" #include "LuminanceHeatmapEffect.h" #include "SphereMapEffect.h" @@ -33,6 +31,12 @@ namespace D2DAdvancedColorImages bool isValid; }; + struct ImageCLL + { + float maxNits; + float medNits; + }; + class D2DAdvancedColorImagesRenderer : public DX::IDeviceNotify { public: @@ -51,10 +55,11 @@ namespace D2DAdvancedColorImages void UpdateManipulationState(_In_ Windows::UI::Input::ManipulationUpdatedEventArgs^ args); - // Returns the computed MaxCLL of the image in nits. While HDR metadata is a - // property of the image (and independent of rendering), our implementation + // Returns the computed MaxCLL and AvgCLL of the image in nits. While HDR metadata is a + // property of the image (and is independent of rendering), our implementation // can't compute it until this point. - float FitImageToWindow(); + ImageCLL FitImageToWindow(bool computeMetadata); + void SetRenderOptions( RenderEffectKind effect, float brightnessAdjustment, @@ -89,6 +94,8 @@ namespace D2DAdvancedColorImages void ComputeHdrMetadata(); void EmitHdrMetadata(); + float GetBestDispMaxLuminance(); + // Cached pointer to device resources. std::shared_ptr m_deviceResources; @@ -99,8 +106,8 @@ namespace D2DAdvancedColorImages Microsoft::WRL::ComPtr m_scaledImage; Microsoft::WRL::ComPtr m_colorManagementEffect; Microsoft::WRL::ComPtr m_whiteScaleEffect; - Microsoft::WRL::ComPtr m_reinhardEffect; - Microsoft::WRL::ComPtr m_filmicEffect; + Microsoft::WRL::ComPtr m_sdrWhiteScaleEffect; + Microsoft::WRL::ComPtr m_hdrTonemapEffect; Microsoft::WRL::ComPtr m_sdrOverlayEffect; Microsoft::WRL::ComPtr m_heatmapEffect; Microsoft::WRL::ComPtr m_sphereMapEffect; @@ -114,7 +121,7 @@ namespace D2DAdvancedColorImages float m_minZoom; D2D1_POINT_2F m_imageOffset; D2D1_POINT_2F m_pointerPos; - float m_maxCLL; // In nits. + ImageCLL m_imageCLL; float m_brightnessAdjust; Windows::Graphics::Display::AdvancedColorInfo^ m_dispInfo; ImageInfo m_imageInfo; diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXHelper.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXHelper.h index bdfccd627..047613790 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXHelper.h +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXHelper.h @@ -24,6 +24,37 @@ namespace DX } } + enum OSVer + { + Win1803, + Win1809 + }; + + // Check for OS version. Common helper function makes it easier to do testing/mockups. + // DirectX doesn't allow us to check for a specific feature's presence, so instead + // we check the OS version (API contract). + inline bool CheckPlatformSupport(OSVer version) + { + int apiLevel = 0; + switch (version) + { + case Win1803: + apiLevel = 6; + break; + + case Win1809: + apiLevel = 7; + break; + + default: + throw ref new Platform::InvalidArgumentException(); + break; + } + + return Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent( + "Windows.Foundation.UniversalApiContract", apiLevel); + } + // Function that reads from a binary file asynchronously. inline Concurrency::task> ReadDataAsync(const std::wstring& filename) { diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml index f88f64cff..0060c2a76 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml @@ -39,6 +39,7 @@ + - + @@ -68,7 +70,7 @@ - + Render Effect: @@ -82,7 +84,18 @@ Adjust brightness: - + Image information @@ -91,6 +104,7 @@ Bit depth: Floating point: Estimated MaxCLL: + Estimated MedCLL: Display information Kind: diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.cpp b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.cpp index 30b197c64..6fc399c79 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.cpp +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.cpp @@ -41,7 +41,7 @@ DirectXPage::DirectXPage() : m_isWindowVisible(true), m_imageInfo{}, m_isImageValid(false), - m_imageMaxCLL(-1.0f) + m_imageCLL{ -1.0f, -1.0f } { InitializeComponent(); @@ -194,7 +194,7 @@ void DirectXPage::LoadImage(_In_ StorageFile^ imageFile) m_imageInfo = info; m_renderer->CreateImageDependentResources(); - m_imageMaxCLL = m_renderer->FitImageToWindow(); + m_imageCLL = m_renderer->FitImageToWindow(true); // On first load of image, need to generate HDR metadata. ApplicationView::GetForCurrentView()->Title = imageFile->Name; ImageACKind->Text = L"Kind: " + ConvertACKindToString(m_imageInfo.imageKind); @@ -204,17 +204,30 @@ void DirectXPage::LoadImage(_In_ StorageFile^ imageFile) std::wstringstream cllStr; cllStr << L"Estimated MaxCLL: "; - if (m_imageMaxCLL < 0.0f) + if (m_imageCLL.maxNits < 0.0f) { cllStr << L"N/A"; } else { - cllStr << std::to_wstring(static_cast(m_imageMaxCLL)) << L" nits"; + cllStr << std::to_wstring(static_cast(m_imageCLL.maxNits)) << L" nits"; } ImageMaxCLL->Text = ref new String(cllStr.str().c_str()); + std::wstringstream avgStr; + avgStr << L"Estimated MedCLL: "; + if (m_imageCLL.medNits < 0.0f) + { + avgStr << L"N/A"; + } + else + { + avgStr << std::to_wstring(static_cast(m_imageCLL.medNits)) << L" nits"; + } + + ImageAvgCLL->Text = ref new String(avgStr.str().c_str()); + // Image loading is done at this point. m_isImageValid = true; BrightnessAdjustSlider->IsEnabled = true; @@ -323,32 +336,17 @@ void DirectXPage::UpdateDefaultRenderOptions() case AdvancedColorKind::WideColorGamut: default: // SDR and WCG images don't need to be tonemapped. - RenderEffectCombo->SelectedIndex = 2; // See RenderOptions.h for which value this indicates. + RenderEffectCombo->SelectedIndex = 0; // See RenderOptions.h for which value this indicates. // Manual brightness adjustment is only useful for HDR content. // SDR and WCG content is adjusted by the OS-provided AdvancedColorInfo::SdrWhiteLevel parameter. - BrightnessAdjustSlider->Value = 1.0f; + BrightnessAdjustSlider->Value = SdrBrightnessFormatter::BrightnessToSlider(1.0f); BrightnessAdjustPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed; break; case AdvancedColorKind::HighDynamicRange: - auto acKind = m_dispInfo ? m_dispInfo->CurrentAdvancedColorKind : AdvancedColorKind::StandardDynamicRange; - - switch (acKind) - { - case AdvancedColorKind::StandardDynamicRange: - case AdvancedColorKind::WideColorGamut: - default: - // HDR content + non-HDR display: HDR tonemapping is needed for correct rendering. - RenderEffectCombo->SelectedIndex = 0; // See RenderOptions.h for which value this indicates. - break; - - case AdvancedColorKind::HighDynamicRange: - // HDR content + HDR display: HDR tonemapping is needed for best results, but this sample's - // tonemappers are very simple and not suitable, so just disable it. - RenderEffectCombo->SelectedIndex = 2; // See RenderOptions.h for which value this indicates. - break; - } + // HDR images need to be tonemapped regardless of display kind. + RenderEffectCombo->SelectedIndex = 1; // See RenderOptions.h for which value this indicates. // Manual brightness adjustment is useful for any HDR content. BrightnessAdjustPanel->Visibility = Windows::UI::Xaml::Visibility::Visible; @@ -367,7 +365,7 @@ void DirectXPage::UpdateRenderOptions() m_renderer->SetRenderOptions( tm->Kind, - static_cast(BrightnessAdjustSlider->Value), + SdrBrightnessFormatter::SliderToBrightness(BrightnessAdjustSlider->Value), m_dispInfo ); } diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.h index 2ef1060e1..fcba5f302 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.h +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.h @@ -16,6 +16,7 @@ #include "DeviceResources.h" #include "D2DAdvancedColorImagesRenderer.h" #include "RenderOptions.h" +#include "SdrBrightnessFormatter.h" namespace D2DAdvancedColorImages { @@ -88,7 +89,7 @@ namespace D2DAdvancedColorImages // Cached information for UI. D2DAdvancedColorImages::ImageInfo m_imageInfo; - float m_imageMaxCLL; + D2DAdvancedColorImages::ImageCLL m_imageCLL; bool m_isImageValid; Windows::Graphics::Display::AdvancedColorInfo^ m_dispInfo; RenderOptionsViewModel^ m_renderOptionsViewModel; diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.cpp b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.cpp deleted file mode 100644 index ef8bf8a6c..000000000 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.cpp +++ /dev/null @@ -1,268 +0,0 @@ -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY -// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR -// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. -// -//********************************************************* - -#include "pch.h" -#include -#include "FilmicEffect.h" -#include "BasicReaderWriter.h" - -#define XML(X) TEXT(#X) - -FilmicEffect::FilmicEffect() : - m_refCount(1) -{ -} - -HRESULT __stdcall FilmicEffect::CreateFilmicImpl(_Outptr_ IUnknown** ppEffectImpl) -{ - // Since the object's refcount is initialized to 1, we don't need to AddRef here. - *ppEffectImpl = static_cast(new (std::nothrow) FilmicEffect()); - - if (*ppEffectImpl == nullptr) - { - return E_OUTOFMEMORY; - } - else - { - return S_OK; - } -} - -HRESULT FilmicEffect::Register(_In_ ID2D1Factory1* pFactory) -{ - // The inspectable metadata of an effect is defined in XML. This can be passed in from an external source - // as well, however for simplicity we just inline the XML. - PCWSTR pszXml = - XML( - - - - - - - - - - - - - ); - - // This registers the effect with the factory, which will make the effect - // instantiatable. - return pFactory->RegisterEffectFromString( - CLSID_CustomFilmicEffect, - pszXml, - nullptr, - 0, - CreateFilmicImpl - ); -} - -IFACEMETHODIMP FilmicEffect::Initialize( - _In_ ID2D1EffectContext* pEffectContext, - _In_ ID2D1TransformGraph* pTransformGraph -) -{ - // To maintain consistency across different DPIs, this effect needs to cover more pixels at - // higher than normal DPIs. The context is saved here so the effect can later retrieve the DPI. - m_effectContext = pEffectContext; - - BasicReaderWriter^ reader = ref new BasicReaderWriter(); - Platform::Array^ data; - - try - { - data = reader->ReadData("FilmicEffect.cso"); - } - catch (Platform::Exception^ e) - { - // Return error if file can not be read. - return e->HResult; - } - - HRESULT hr = pEffectContext->LoadPixelShader(GUID_FilmicEffectPixelShader, data->Data, data->Length); - - // This loads the shader into the Direct2D image effects system and associates it with the GUID passed in. - // If this method is called more than once (say by other instances of the effect) with the same GUID, - // the system will simply do nothing, ensuring that only one instance of a shader is stored regardless of how - // many time it is used. - if (SUCCEEDED(hr)) - { - // The graph consists of a single transform. In fact, this class is the transform, - // reducing the complexity of implementing an effect when all we need to - // do is use a single pixel shader. - hr = pTransformGraph->SetSingleTransformNode(this); - } - - return hr; -} - -HRESULT FilmicEffect::UpdateConstants() -{ - // Update the DPI if it has changed. This allows the effect to scale across different DPIs automatically. - m_effectContext->GetDpi(&m_dpi, &m_dpi); - m_constants.dpi = m_dpi; - - return m_drawInfo->SetPixelShaderConstantBuffer(reinterpret_cast(&m_constants), sizeof(m_constants)); -} - -IFACEMETHODIMP FilmicEffect::PrepareForRender(D2D1_CHANGE_TYPE changeType) -{ - return UpdateConstants(); -} - -// SetGraph is only called when the number of inputs changes. This never happens as we publish this effect -// as a single input effect. -IFACEMETHODIMP FilmicEffect::SetGraph(_In_ ID2D1TransformGraph* pGraph) -{ - return E_NOTIMPL; -} - -// Called to assign a new render info class, which is used to inform D2D on -// how to set the state of the GPU. -IFACEMETHODIMP FilmicEffect::SetDrawInfo(_In_ ID2D1DrawInfo* pDrawInfo) -{ - m_drawInfo = pDrawInfo; - - return m_drawInfo->SetPixelShader(GUID_FilmicEffectPixelShader); -} - -// Calculates the mapping between the output and input rects. -IFACEMETHODIMP FilmicEffect::MapOutputRectToInputRects( - _In_ const D2D1_RECT_L* pOutputRect, - _Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects, - UINT32 inputRectCount - ) const -{ - // This effect has exactly one input, so if there is more than one input rect, - // something is wrong. - if (inputRectCount != 1) - { - return E_INVALIDARG; - } - - pInputRects[0].left = pOutputRect->left; - pInputRects[0].top = pOutputRect->top; - pInputRects[0].right = pOutputRect->right; - pInputRects[0].bottom = pOutputRect->bottom; - - return S_OK; -} - -IFACEMETHODIMP FilmicEffect::MapInputRectsToOutputRect( - _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputRects, - _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputOpaqueSubRects, - UINT32 inputRectCount, - _Out_ D2D1_RECT_L* pOutputRect, - _Out_ D2D1_RECT_L* pOutputOpaqueSubRect - ) -{ - // This effect has exactly one input, so if there is more than one input rect, - // something is wrong. - if (inputRectCount != 1) - { - return E_INVALIDARG; - } - - *pOutputRect = pInputRects[0]; - m_inputRect = pInputRects[0]; - - // Indicate that entire output might contain transparency. - ZeroMemory(pOutputOpaqueSubRect, sizeof(*pOutputOpaqueSubRect)); - - return S_OK; -} - -IFACEMETHODIMP FilmicEffect::MapInvalidRect( - UINT32 inputIndex, - D2D1_RECT_L invalidInputRect, - _Out_ D2D1_RECT_L* pInvalidOutputRect - ) const -{ - HRESULT hr = S_OK; - - // Indicate that the entire output may be invalid. - *pInvalidOutputRect = m_inputRect; - - return hr; -} - -IFACEMETHODIMP_(UINT32) FilmicEffect::GetInputCount() const -{ - return 1; -} - -// D2D ensures that that effects are only referenced from one thread at a time. -// To improve performance, we simply increment/decrement our reference count -// rather than use atomic InterlockedIncrement()/InterlockedDecrement() functions. -IFACEMETHODIMP_(ULONG) FilmicEffect::AddRef() -{ - m_refCount++; - return m_refCount; -} - -IFACEMETHODIMP_(ULONG) FilmicEffect::Release() -{ - m_refCount--; - - if (m_refCount == 0) - { - delete this; - return 0; - } - else - { - return m_refCount; - } -} - -// This enables the stack of parent interfaces to be queried. -IFACEMETHODIMP FilmicEffect::QueryInterface( - _In_ REFIID riid, - _Outptr_ void** ppOutput - ) -{ - *ppOutput = nullptr; - HRESULT hr = S_OK; - - if (riid == __uuidof(ID2D1EffectImpl)) - { - *ppOutput = reinterpret_cast(this); - } - else if (riid == __uuidof(ID2D1DrawTransform)) - { - *ppOutput = static_cast(this); - } - else if (riid == __uuidof(ID2D1Transform)) - { - *ppOutput = static_cast(this); - } - else if (riid == __uuidof(ID2D1TransformNode)) - { - *ppOutput = static_cast(this); - } - else if (riid == __uuidof(IUnknown)) - { - *ppOutput = this; - } - else - { - hr = E_NOINTERFACE; - } - - if (*ppOutput != nullptr) - { - AddRef(); - } - - return hr; -} diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.hlsl b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.hlsl deleted file mode 100644 index 8fc254b3f..000000000 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.hlsl +++ /dev/null @@ -1,63 +0,0 @@ - -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY -// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR -// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. -// -//********************************************************* - -// Custom effects using pixel shaders should use HLSL helper functions defined in -// d2d1effecthelpers.hlsli to make use of effect shader linking. -#define D2D_INPUT_COUNT 1 // The pixel shader takes 1 input texture. -#define D2D_INPUT0_SIMPLE - -// Note that the custom build step must provide the correct path to find d2d1effecthelpers.hlsli when calling fxc.exe. -#include "d2d1effecthelpers.hlsli" - -cbuffer constants : register(b0) -{ - float dpi : packoffset(c1.z); -}; - -static const float3x3 ACESInputMat = -{ - { 0.59719, 0.35458, 0.04823 }, - { 0.07600, 0.90834, 0.01566 }, - { 0.02840, 0.13383, 0.83777 } -}; - -// ODT_SAT => XYZ => D60_2_D65 => sRGB -static const float3x3 ACESOutputMat = -{ - { 1.60475, -0.53108, -0.07367 }, - { -0.10208, 1.10813, -0.00605 }, - { -0.00327, -0.07276, 1.07602 } -}; - -float3 RRTAndODTFit(float3 v) -{ - float3 a = v * (v + 0.0245786f) - 0.000090537f; - float3 b = v * (0.983729f * v + 0.4329510f) + 0.238081f; - return a / b; -} - -D2D_PS_ENTRY(main) -{ - float4 color = D2DGetInput(0); - float3 filmic = mul(ACESInputMat, color.rgb); - - // Apply RRT and ODT - filmic = RRTAndODTFit(filmic); - - filmic = mul(ACESOutputMat, filmic); - - // Clamp to [0, 1] - filmic = saturate(filmic); - - return float4(filmic, color.a); -} - diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.h deleted file mode 100644 index ace683248..000000000 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.h +++ /dev/null @@ -1,105 +0,0 @@ -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY -// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR -// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. -// -//********************************************************* - -DEFINE_GUID(GUID_ReinhardEffectPixelShader, 0x6716FB29, 0x06A0, 0x460F, 0xAA, 0x9C, 0x8B, 0x8F, 0xF0, 0x33, 0x5E, 0xA6); -DEFINE_GUID(CLSID_CustomReinhardEffect, 0xB7B36C92, 0x3498, 0x4A94, 0x9E, 0x95, 0x9F, 0x24, 0x6F, 0x92, 0x45, 0xBF); - -// Our effect contains one transform, which is simply a wrapper around a pixel shader. As such, -// we can simply make the effect itself act as the transform. -class ReinhardEffect : public ID2D1EffectImpl, public ID2D1DrawTransform -{ -public: - // Declare effect registration methods. - static HRESULT Register(_In_ ID2D1Factory1* pFactory); - - static HRESULT __stdcall CreateReinhardImpl(_Outptr_ IUnknown** ppEffectImpl); - - // Declare ID2D1EffectImpl implementation methods. - IFACEMETHODIMP Initialize( - _In_ ID2D1EffectContext* pContextInternal, - _In_ ID2D1TransformGraph* pTransformGraph - ); - - IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType); - - IFACEMETHODIMP SetGraph(_In_ ID2D1TransformGraph* pGraph); - - // Declare ID2D1DrawTransform implementation methods. - IFACEMETHODIMP SetDrawInfo(_In_ ID2D1DrawInfo* pRenderInfo); - - // Declare ID2D1Transform implementation methods. - IFACEMETHODIMP MapOutputRectToInputRects( - _In_ const D2D1_RECT_L* pOutputRect, - _Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects, - UINT32 inputRectCount - ) const; - - IFACEMETHODIMP MapInputRectsToOutputRect( - _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputRects, - _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputOpaqueSubRects, - UINT32 inputRectCount, - _Out_ D2D1_RECT_L* pOutputRect, - _Out_ D2D1_RECT_L* pOutputOpaqueSubRect - ); - - IFACEMETHODIMP MapInvalidRect( - UINT32 inputIndex, - D2D1_RECT_L invalidInputRect, - _Out_ D2D1_RECT_L* pInvalidOutputRect - ) const; - - // Declare ID2D1TransformNode implementation methods. - IFACEMETHODIMP_(UINT32) GetInputCount() const; - - // Declare IUnknown implementation methods. - IFACEMETHODIMP_(ULONG) AddRef(); - IFACEMETHODIMP_(ULONG) Release(); - IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppOutput); - -private: - ReinhardEffect(); - HRESULT UpdateConstants(); - - inline static float Clamp(float v, float low, float high) - { - return (v < low) ? low : (v > high) ? high : v; - } - - inline static float Round(float v) - { - return floor(v + 0.5f); - } - - // Prevents over/underflows when adding longs. - inline static long SafeAdd(long base, long valueToAdd) - { - if (valueToAdd >= 0) - { - return ((base + valueToAdd) >= base) ? (base + valueToAdd) : LONG_MAX; - } - else - { - return ((base + valueToAdd) <= base) ? (base + valueToAdd) : LONG_MIN; - } - } - - // This struct defines the constant buffer of our pixel shader. - struct - { - float dpi; - } m_constants; - - Microsoft::WRL::ComPtr m_drawInfo; - Microsoft::WRL::ComPtr m_effectContext; - LONG m_refCount; - D2D1_RECT_L m_inputRect; - float m_dpi; -}; diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.hlsl b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.hlsl deleted file mode 100644 index b95fa80d0..000000000 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.hlsl +++ /dev/null @@ -1,30 +0,0 @@ -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY -// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR -// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. -// -//********************************************************* - -// Custom effects using pixel shaders should use HLSL helper functions defined in -// d2d1effecthelpers.hlsli to make use of effect shader linking. -#define D2D_INPUT_COUNT 1 // The pixel shader takes 1 input texture. -#define D2D_INPUT0_SIMPLE - -// Note that the custom build step must provide the correct path to find d2d1effecthelpers.hlsli when calling fxc.exe. -#include "d2d1effecthelpers.hlsli" - -// Implements an extremely rudimentary HDR-to-SDR tone mapping effect. This is a -// simplified version of the Reinhard tone mapping algorithm, applied directly -// to the RGB channels. It successfully maps any content into the SDR range -// (i.e. 0.0f - 1.0f), but highlights will tend to be compressed, and color -// shifting can occur. -D2D_PS_ENTRY(main) -{ - // Basic formula: for each color value x of each pixel, return (x / (x+1)). - float4 color = D2DGetInput(0); - return (color / (color + 1.0f)); -} \ No newline at end of file diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/RenderOptions.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/RenderOptions.h index 5425423a2..cb95b7d74 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/RenderOptions.h +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/RenderOptions.h @@ -22,8 +22,7 @@ namespace D2DAdvancedColorImages /// public enum class RenderEffectKind { - ReinhardTonemap, - FilmicTonemap, + HdrTonemap, None, SdrOverlay, LuminanceHeatmap, @@ -68,11 +67,10 @@ namespace D2DAdvancedColorImages // The first index is chosen by default. Ensure this is in sync with DirectXPage. renderEffects = ref new Platform::Collections::VectorView { - ref new EffectOption(L"ACES Filmic Tonemap", RenderEffectKind::FilmicTonemap), - ref new EffectOption(L"Reinhard Tonemap", RenderEffectKind::ReinhardTonemap), ref new EffectOption(L"No effect", RenderEffectKind::None), + ref new EffectOption(L"HDR tonemap", RenderEffectKind::HdrTonemap), ref new EffectOption(L"Draw SDR as grayscale", RenderEffectKind::SdrOverlay), - ref new EffectOption(L"Draw luminance as heatmap", RenderEffectKind::LuminanceHeatmap), + ref new EffectOption(L"Luminance heatmap", RenderEffectKind::LuminanceHeatmap), // TODO: Temporarily disable sphere map in UI for the upcoming app release. //ref new EffectOption(L"Draw as spheremap", RenderEffectKind::SphereMap) }; diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SdrBrightnessFormatter.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SdrBrightnessFormatter.h new file mode 100644 index 000000000..e2c2bba00 --- /dev/null +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SdrBrightnessFormatter.h @@ -0,0 +1,58 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "pch.h" + +namespace D2DAdvancedColorImages +{ + // The brightness adjustment slider maps the UI linear value to an exponential (power of 2) multiplier factor. + // The user sees the multiplier as the tooltip text, and the renderer uses this multiplier internally. + public ref class SdrBrightnessFormatter sealed : Windows::UI::Xaml::Data::IValueConverter + { + public: + virtual Platform::Object^ Convert(Platform::Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, + Platform::Object^ parameter, Platform::String^ language) + { + auto type = value->GetType()->FullName; + + double dbl = static_cast(value); + + float percent = SdrBrightnessFormatter::SliderToBrightness(dbl) * 100.0f; + + Platform::String^ text = ref new Platform::String(std::to_wstring(static_cast(percent)).c_str()) + L"%"; + + return text; + } + + // No need to implement converting back on a one-way binding + virtual Platform::Object^ ConvertBack(Platform::Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, + Platform::Object^ parameter, Platform::String^ language) + { + throw ref new Platform::NotImplementedException(); + } + + // Convert slider UI value (linear) to brightness multiplier (2^x) + static float SliderToBrightness(double slider) + { + return powf(2.0f, static_cast(slider)); + } + + // Convert brightness multiplier (2^x) to slider UI value (linear) + static double BrightnessToSlider(float multiplier) + { + multiplier = max(FLT_MIN, multiplier); + + return logf(multiplier) / logf(2.0f); + } + }; +} \ No newline at end of file diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.cpp b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.cpp similarity index 64% rename from Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.cpp rename to Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.cpp index fc59c88f1..b48513c5b 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.cpp +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.cpp @@ -11,20 +11,20 @@ #include "pch.h" #include -#include "ReinhardEffect.h" +#include "SimpleTonemapEffect.h" #include "BasicReaderWriter.h" #define XML(X) TEXT(#X) -ReinhardEffect::ReinhardEffect() : +SimpleTonemapEffect::SimpleTonemapEffect() : m_refCount(1) { } -HRESULT __stdcall ReinhardEffect::CreateReinhardImpl(_Outptr_ IUnknown** ppEffectImpl) +HRESULT __stdcall SimpleTonemapEffect::CreateSimpleTonemapImpl(_Outptr_ IUnknown** ppEffectImpl) { // Since the object's refcount is initialized to 1, we don't need to AddRef here. - *ppEffectImpl = static_cast(new (std::nothrow) ReinhardEffect()); + *ppEffectImpl = static_cast(new (std::nothrow) SimpleTonemapEffect()); if (*ppEffectImpl == nullptr) { @@ -36,7 +36,41 @@ HRESULT __stdcall ReinhardEffect::CreateReinhardImpl(_Outptr_ IUnknown** ppEffec } } -HRESULT ReinhardEffect::Register(_In_ ID2D1Factory1* pFactory) +HRESULT SimpleTonemapEffect::SetInputMaxLuminance(float nits) +{ + if (nits < 0.0f) + { + return E_INVALIDARG; + } + + m_constants.inputMaxLum = nits / 80.0f; // scRGB 1.0 == 80 nits. + + return S_OK; +} + +float SimpleTonemapEffect::GetInputMaxLuminance() const +{ + return m_constants.inputMaxLum * 80.0f; +} + +HRESULT SimpleTonemapEffect::SetOutputMaxLuminance(float nits) +{ + if (nits < 0.0f || nits > 10000.0f) + { + return E_INVALIDARG; + } + + m_constants.outputMaxLum = nits / 80.0f; // scRGB 1.0 == 80 nits. + + return S_OK; +} + +float SimpleTonemapEffect::GetOutputMaxLuminance() const +{ + return m_constants.outputMaxLum * 80.0f; +} + +HRESULT SimpleTonemapEffect::Register(_In_ ID2D1Factory1* pFactory) { // The inspectable metadata of an effect is defined in XML. This can be passed in from an external source // as well, however for simplicity we just inline the XML. @@ -45,29 +79,51 @@ HRESULT ReinhardEffect::Register(_In_ ID2D1Factory1* pFactory) - + - + + + + + + + + + + ); + // This defines the bindings from specific properties to the callback functions + // on the class that ID2D1Effect::SetValue() & GetValue() will call. + const D2D1_PROPERTY_BINDING bindings[] = + { + // When accessing by index, use D2D1_HDRTONEMAP_PROP_INPUT_MAX_LUMINANCE = 0. + D2D1_VALUE_TYPE_BINDING(L"InputMaxLuminance", &SetInputMaxLuminance, &GetInputMaxLuminance), + + // When accessing by index, use D2D1_HDRTONEMAP_PROP_OUTPUT_MAX_LUMINANCE = 1. + D2D1_VALUE_TYPE_BINDING(L"OutputMaxLuminance", &SetOutputMaxLuminance, &GetOutputMaxLuminance), + + // Note that D2D1_HDRTONEMAP_DISPLAY_MODE = 2 is NOT IMPLEMENTED by this effect. + }; + // This registers the effect with the factory, which will make the effect // instantiatable. return pFactory->RegisterEffectFromString( - CLSID_CustomReinhardEffect, + CLSID_CustomSimpleTonemapEffect, pszXml, - nullptr, - 0, - CreateReinhardImpl + bindings, + ARRAYSIZE(bindings), + CreateSimpleTonemapImpl ); } -IFACEMETHODIMP ReinhardEffect::Initialize( +IFACEMETHODIMP SimpleTonemapEffect::Initialize( _In_ ID2D1EffectContext* pEffectContext, _In_ ID2D1TransformGraph* pTransformGraph ) @@ -80,7 +136,7 @@ IFACEMETHODIMP ReinhardEffect::Initialize( try { - data = reader->ReadData("ReinhardEffect.cso"); + data = reader->ReadData("SimpleTonemapEffect.cso"); } catch (Platform::Exception^ e) { @@ -88,7 +144,7 @@ IFACEMETHODIMP ReinhardEffect::Initialize( return e->HResult; } - HRESULT hr = pEffectContext->LoadPixelShader(GUID_ReinhardEffectPixelShader, data->Data, data->Length); + HRESULT hr = pEffectContext->LoadPixelShader(GUID_SimpleTonemapEffectPixelShader, data->Data, data->Length); // This loads the shader into the Direct2D image effects system and associates it with the GUID passed in. // If this method is called more than once (say by other instances of the effect) with the same GUID, @@ -105,38 +161,37 @@ IFACEMETHODIMP ReinhardEffect::Initialize( return hr; } -HRESULT ReinhardEffect::UpdateConstants() +HRESULT SimpleTonemapEffect::UpdateConstants() { // Update the DPI if it has changed. This allows the effect to scale across different DPIs automatically. - m_effectContext->GetDpi(&m_dpi, &m_dpi); - m_constants.dpi = m_dpi; + m_effectContext->GetDpi(&m_dpi, &m_dpi); // DPI is never used right now. return m_drawInfo->SetPixelShaderConstantBuffer(reinterpret_cast(&m_constants), sizeof(m_constants)); } -IFACEMETHODIMP ReinhardEffect::PrepareForRender(D2D1_CHANGE_TYPE changeType) +IFACEMETHODIMP SimpleTonemapEffect::PrepareForRender(D2D1_CHANGE_TYPE changeType) { return UpdateConstants(); } // SetGraph is only called when the number of inputs changes. This never happens as we publish this effect // as a single input effect. -IFACEMETHODIMP ReinhardEffect::SetGraph(_In_ ID2D1TransformGraph* pGraph) +IFACEMETHODIMP SimpleTonemapEffect::SetGraph(_In_ ID2D1TransformGraph* pGraph) { return E_NOTIMPL; } // Called to assign a new render info class, which is used to inform D2D on // how to set the state of the GPU. -IFACEMETHODIMP ReinhardEffect::SetDrawInfo(_In_ ID2D1DrawInfo* pDrawInfo) +IFACEMETHODIMP SimpleTonemapEffect::SetDrawInfo(_In_ ID2D1DrawInfo* pDrawInfo) { m_drawInfo = pDrawInfo; - return m_drawInfo->SetPixelShader(GUID_ReinhardEffectPixelShader); + return m_drawInfo->SetPixelShader(GUID_SimpleTonemapEffectPixelShader); } // Calculates the mapping between the output and input rects. -IFACEMETHODIMP ReinhardEffect::MapOutputRectToInputRects( +IFACEMETHODIMP SimpleTonemapEffect::MapOutputRectToInputRects( _In_ const D2D1_RECT_L* pOutputRect, _Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects, UINT32 inputRectCount @@ -157,7 +212,7 @@ IFACEMETHODIMP ReinhardEffect::MapOutputRectToInputRects( return S_OK; } -IFACEMETHODIMP ReinhardEffect::MapInputRectsToOutputRect( +IFACEMETHODIMP SimpleTonemapEffect::MapInputRectsToOutputRect( _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputRects, _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputOpaqueSubRects, UINT32 inputRectCount, @@ -181,7 +236,7 @@ IFACEMETHODIMP ReinhardEffect::MapInputRectsToOutputRect( return S_OK; } -IFACEMETHODIMP ReinhardEffect::MapInvalidRect( +IFACEMETHODIMP SimpleTonemapEffect::MapInvalidRect( UINT32 inputIndex, D2D1_RECT_L invalidInputRect, _Out_ D2D1_RECT_L* pInvalidOutputRect @@ -195,7 +250,7 @@ IFACEMETHODIMP ReinhardEffect::MapInvalidRect( return hr; } -IFACEMETHODIMP_(UINT32) ReinhardEffect::GetInputCount() const +IFACEMETHODIMP_(UINT32) SimpleTonemapEffect::GetInputCount() const { return 1; } @@ -203,13 +258,13 @@ IFACEMETHODIMP_(UINT32) ReinhardEffect::GetInputCount() const // D2D ensures that that effects are only referenced from one thread at a time. // To improve performance, we simply increment/decrement our reference count // rather than use atomic InterlockedIncrement()/InterlockedDecrement() functions. -IFACEMETHODIMP_(ULONG) ReinhardEffect::AddRef() +IFACEMETHODIMP_(ULONG) SimpleTonemapEffect::AddRef() { m_refCount++; return m_refCount; } -IFACEMETHODIMP_(ULONG) ReinhardEffect::Release() +IFACEMETHODIMP_(ULONG) SimpleTonemapEffect::Release() { m_refCount--; @@ -225,7 +280,7 @@ IFACEMETHODIMP_(ULONG) ReinhardEffect::Release() } // This enables the stack of parent interfaces to be queried. -IFACEMETHODIMP ReinhardEffect::QueryInterface( +IFACEMETHODIMP SimpleTonemapEffect::QueryInterface( _In_ REFIID riid, _Outptr_ void** ppOutput ) diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.h similarity index 79% rename from Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.h rename to Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.h index a32415e5f..80d149af5 100644 --- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.h +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.h @@ -8,19 +8,29 @@ // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* +// {4587A15E-425A-4311-A591-0B2E89D02E75} +DEFINE_GUID(GUID_SimpleTonemapEffectPixelShader, +0x4587a15e, 0x425a, 0x4311, 0xa5, 0x91, 0xb, 0x2e, 0x89, 0xd0, 0x2e, 0x75); -DEFINE_GUID(GUID_FilmicEffectPixelShader, 0x4214db42, 0x3bc5, 0x4f17, 0xb8, 0x59, 0x9d, 0x29, 0x15, 0x27, 0x4a, 0x6c); -DEFINE_GUID(CLSID_CustomFilmicEffect, 0x40e0e874, 0x562c, 0x480f, 0x9b, 0x3e, 0x69, 0xf8, 0x42, 0x25, 0x59, 0xc9); +// {0273FF16-6CA6-4AC4-BD14-77704DAD5391} +DEFINE_GUID(CLSID_CustomSimpleTonemapEffect, 0x273ff16, 0x6ca6, 0x4ac4, 0xbd, 0x14, 0x77, 0x70, 0x4d, 0xad, 0x53, 0x91); // Our effect contains one transform, which is simply a wrapper around a pixel shader. As such, // we can simply make the effect itself act as the transform. -class FilmicEffect : public ID2D1EffectImpl, public ID2D1DrawTransform +class SimpleTonemapEffect : public ID2D1EffectImpl, public ID2D1DrawTransform { public: // Declare effect registration methods. static HRESULT Register(_In_ ID2D1Factory1* pFactory); - static HRESULT __stdcall CreateFilmicImpl(_Outptr_ IUnknown** ppEffectImpl); + static HRESULT __stdcall CreateSimpleTonemapImpl(_Outptr_ IUnknown** ppEffectImpl); + + // Declare property getter/setters + HRESULT SetInputMaxLuminance(float nits); + float GetInputMaxLuminance() const; + + HRESULT SetOutputMaxLuminance(float nits); + float GetOutputMaxLuminance() const; // Declare ID2D1EffectImpl implementation methods. IFACEMETHODIMP Initialize( @@ -65,7 +75,7 @@ class FilmicEffect : public ID2D1EffectImpl, public ID2D1DrawTransform IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppOutput); private: - FilmicEffect(); + SimpleTonemapEffect(); HRESULT UpdateConstants(); inline static float Clamp(float v, float low, float high) @@ -94,7 +104,8 @@ class FilmicEffect : public ID2D1EffectImpl, public ID2D1DrawTransform // This struct defines the constant buffer of our pixel shader. struct { - float dpi; + float inputMaxLum; // In scRGB values. + float outputMaxLum; // In scRGB values. } m_constants; Microsoft::WRL::ComPtr m_drawInfo; diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.hlsl b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.hlsl new file mode 100644 index 000000000..ef4a84388 --- /dev/null +++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.hlsl @@ -0,0 +1,96 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +// Custom effects using pixel shaders should use HLSL helper functions defined in +// d2d1effecthelpers.hlsli to make use of effect shader linking. +#define D2D_INPUT_COUNT 1 // The pixel shader takes 1 input texture. +#define D2D_INPUT0_SIMPLE + +#define MIDTONE_MAX 0.8 // Fraction of outputMax reserved for passthrough linear section. +#define DELTA 0.00001 + +// Note that the custom build step must provide the correct path to find d2d1effecthelpers.hlsli when calling fxc.exe. +#include "d2d1effecthelpers.hlsli" + +cbuffer constants : register(b0) +{ + float inputMax; // In scRGB values. + float outputMax; // In scRGB values. +}; + +// Rational, second-order, weighted bezier. t is defined over [0, 1]. +float rb2(float t, float p0, float p1, float p2, float w0, float w1, float w2) +{ + float tN1 = 1 - t; + float tN1_2 = tN1 * tN1; + float t_2 = t * t; + + // w0 * (1 - t)^2 * p0 + + // w1 * 2t(1 - t) * p1 + + // w2 * t^2 * p2 + float num = w0 * tN1_2 * p0 + w1 * 2 * t * tN1 * p1 + w2 * t_2 * p2; + + // w0 * (1 - t)^2 + + // w1 * 2t(1 - t) + + // w2 * t^2 + float den = w0 * tN1_2 + w1 * 2 * t * tN1 + w2 * t_2; + + return num / den; +} + +// This tonemapper operates directly on each R, G and B channel. A more sophisticated tonemapper +// would first convert to a colorspace like ICtCp that isolates luminance. +float channelTonemap(float input) +{ + // Tonemapper consists of 3 segments: + // 1: Midtones and shadows are passed through/preserved. + // 2: Highlights that exceed display max luminance are compressed using bezier. + // 3: Highlights that exceed content max (i.e. bad metadata) are clipped. + if (input < MIDTONE_MAX * outputMax) + { + return input; + } + else if (input < inputMax) + { + float midLimit = MIDTONE_MAX * outputMax; + float w0 = outputMax / midLimit; + float w1 = inputMax / outputMax; + float t = (input - midLimit) / (inputMax - midLimit + DELTA); + + // Choose weights for smooth transition with linear segments. + return rb2( + t, + midLimit, outputMax, outputMax, // p0, p1, p2 + w0 , w1 , w1); + } + else + { + return outputMax; + } +} + +// Implements a rudimentary HDR tonemapper. In linear RGB (scRGB) space, preserve colors which +// the display can reproduce, and use a rational bezier to provide a "soft knee" to compress +// colors above the display (target) max luminance. + +// Like the 1809 Direct2D HDR tonemapper, this effect outputs values in scRGB scene-referred +// luminance space. Therefore, when outputting to an SDR or WCG display, the app must perform +// white level adjustment to bring the numeric range of the output to [0, 1]. +D2D_PS_ENTRY(main) +{ + float4 color = D2DGetInput(0); + + color.r = channelTonemap(color.r); + color.g = channelTonemap(color.g); + color.b = channelTonemap(color.b); + + return color; +} \ No newline at end of file