From 7b9cbc08d71a07f19cf79f396095dc1be5398c0d Mon Sep 17 00:00:00 2001 From: Autechre Date: Fri, 3 Sep 2021 06:15:25 +0200 Subject: [PATCH] Add HDR support for D3D12 (rebased PR from MajorPainTheCactus) (#12917) * Add HDR support * Attempt to fix Mingw build and Metal builds * (D3D12) Fix relative header includes * Add missing hdr_sm5.hlsl.h * (d3d12_common.c) Some C89 build fixes * Fix MSVC build * - Attempt to fix build on mingw/msys unix with dirty hack - Fix shader compilation of hdr_sm5.hlsl.h on MSVC/Visual Studio - the define was seen as an error and was causing the first pipeline to error out - Make sure we manually set handle of backBuffer to NULL * Moving the release of the texture above the freeing of desc.srv_heap and desc.rtv_heap solves the hard crashes on teardown/setup in RA - it was crashing hard in d3d12_release_texture before * Add HAVE_D3D12_HDR ifdef - needs to be disabled for WinRT for now because of several things that are Windows desktop-specific right now (GetWindowRect) * Add dirty GUID hack - should work for both mingw/msys on Windows/Linux as well as MSVC/Visual Studio (hopefully) * Change HAVE_D3D12_HDR to HAVE_DXGI_HDR * Move away from camelcase named variables * Fix RARCH_ERR logs - they need a newline at the end * d3d12_check_display_hdr_support - make it return a bool on return and set d3d12->hdr.support and d3d12->hdr.enable outside of the function * (DXGI) Remove D3D12 dependencies from dxgi_check_display_hdr_support and move it to dxgi_common.c instead * (DXGI) move d3d12_swapchain_color_space over to dxgi_common.c and rename it dxgi_swapchain_color_space * (DXGI) move d3d12_set_hdr_metadata to dxgi_common.c and rename it dxgi_set_hdr_metadata * (DXGI) dxgi_check_display_hdr_support - better error handling? * Fix typo * Remove video_force_resolution * (D3D12) Address TODO/FIXME * (D3D12) Backport https://github.com/libretro/RetroArch/pull/12916/commits/c1b6c0bff2aa33cde035b43cb31ac7e78ff2a07a - Fixed resource transition for present when HDR is off Fixed cel shader displaying all black as blending was enabled when the hdr shader was being applied - turned off blending during this shader * Move d3d12_hdr_uniform_t to dxgi_common.h and rename it dxgi_hdr_uniform_t * (D3D11) Add HDR support * Add TODO/FIXME notes * Cache hdr_enable in video_frame_info_t * Update comment --- .vscode/c_cpp_properties.json | 297 ++++++------- config.def.h | 15 + configuration.c | 8 +- configuration.h | 5 + gfx/common/d3d11_common.h | 23 + gfx/common/d3d12_common.c | 121 +++++- gfx/common/d3d12_common.h | 28 +- gfx/common/d3dcompiler_common.c | 3 +- gfx/common/dxgi_common.c | 269 ++++++++++++ gfx/common/dxgi_common.h | 75 +++- gfx/common/win32_common.c | 6 + gfx/drivers/caca_gfx.c | 4 + gfx/drivers/ctr_gfx.c | 6 +- gfx/drivers/d3d10.c | 6 +- gfx/drivers/d3d11.c | 392 ++++++++++++++++-- gfx/drivers/d3d12.c | 553 +++++++++++++++++++++---- gfx/drivers/d3d8.c | 6 +- gfx/drivers/d3d9.c | 6 +- gfx/drivers/d3d_shaders/hdr_sm5.hlsl.h | 120 ++++++ gfx/drivers/dispmanx_gfx.c | 6 +- gfx/drivers/drm_gfx.c | 6 +- gfx/drivers/exynos_gfx.c | 6 +- gfx/drivers/fpga_gfx.c | 4 + gfx/drivers/gdi_gfx.c | 6 +- gfx/drivers/gl.c | 6 +- gfx/drivers/gl1.c | 6 +- gfx/drivers/gl_core.c | 4 + gfx/drivers/gx2_gfx.c | 4 + gfx/drivers/gx_gfx.c | 6 +- gfx/drivers/metal.m | 38 +- gfx/drivers/omap_gfx.c | 6 +- gfx/drivers/ps2_gfx.c | 6 +- gfx/drivers/psp1_gfx.c | 6 +- gfx/drivers/rsx_gfx.c | 6 +- gfx/drivers/sdl_dingux_gfx.c | 6 +- gfx/drivers/sdl_gfx.c | 6 +- gfx/drivers/sdl_rs90_gfx.c | 6 +- gfx/drivers/sixel_gfx.c | 4 + gfx/drivers/sunxi_gfx.c | 6 +- gfx/drivers/switch_gfx.c | 4 + gfx/drivers/switch_nx_gfx.c | 4 + gfx/drivers/vga_gfx.c | 6 +- gfx/drivers/vita2d_gfx.c | 6 +- gfx/drivers/vulkan.c | 4 + gfx/video_thread_wrapper.c | 86 +++- gfx/video_thread_wrapper.h | 13 + intl/msg_hash_lbl.h | 8 + intl/msg_hash_us.c | 30 ++ intl/msg_hash_us.h | 51 +++ menu/cbs/menu_cbs_deferred_push.c | 5 + menu/cbs/menu_cbs_ok.c | 5 + menu/cbs/menu_cbs_sublabel.c | 4 + menu/cbs/menu_cbs_title.c | 2 + menu/drivers/materialui.c | 1 + menu/menu_cbs.h | 1 + menu/menu_displaylist.c | 37 ++ menu/menu_displaylist.h | 1 + menu/menu_setting.c | 153 +++++++ msg_hash.h | 8 + retroarch.c | 60 +++ retroarch.cfg | 7 + retroarch.h | 21 +- retroarch_data.h | 5 + tools/com-parser/com-parse.cpp | 2 +- ui/drivers/qt/qt_options.cpp | 12 + 65 files changed, 2303 insertions(+), 320 deletions(-) create mode 100644 gfx/drivers/d3d_shaders/hdr_sm5.hlsl.h diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 679cbeddab..e3beb93367 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,155 +1,156 @@ { - "configurations": [ - { - "name": "Mac", - "includePath": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}", - "${workspaceRoot}/libretro-common/include" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" + "configurations": [ + { + "name": "Mac", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}", + "${workspaceRoot}/libretro-common/include" ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - }, - "macFrameworkPath": [ - "/System/Library/Frameworks", - "/Library/Frameworks" - ] - }, - { - "name": "Linux", - "includePath": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}", - "${workspaceFolder}/libretro-common/include", - "${workspaceRoot}/libretro-common/include" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "macFrameworkPath": [ + "/System/Library/Frameworks", + "/Library/Frameworks" + ] + }, + { + "name": "Linux", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}", + "${workspaceFolder}/libretro-common/include", + "${workspaceRoot}/libretro-common/include" ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "Win32", - "includePath": [ - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", - "${workspaceRoot}", - "${workspaceFolder}/libretro-common/include" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", - "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", - "${workspaceRoot}" + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "Win32", + "includePath": [ + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", + "${workspaceRoot}", + "${workspaceFolder}/libretro-common/include" ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - }, - "cStandard": "c11", - "cppStandard": "c++17" - }, - { - "name": "msys2-mingw32", - "includePath": [ - "C:/msys64/mingw32/include", - "C:/msys64/mingw32/i686-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/msys64/mingw32/include", - "C:/msys64/mingw32/i686-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" + "defines": [ + "_DEBUG", + "UNICODE" ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "msys2-mingw64", - "includePath": [ - "C:/msys64/mingw64/include", - "C:/msys64/mingw64/x86_64-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/msys64/mingw64/include", - "C:/msys64/mingw64/x86_64-w64-mingw32/include", - "${workspaceRoot}/libretro-common/include", - "${workspaceRoot}/include", - "${workspaceRoot}" + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "cStandard": "c11", + "cppStandard": "c++17", + "configurationProvider": "ms-vscode.makefile-tools" + }, + { + "name": "msys2-mingw32", + "includePath": [ + "C:/msys64/mingw32/include", + "C:/msys64/mingw32/i686-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "Switch", - "includePath": [ - "/opt/devkitpro/devkitA64/aarch64-none-elf/include", - "/opt/devkitpro/devkitA64/lib/gcc/aarch64-none-elf/8.3.0/include", - "/opt/devkitpro/libnx/include", - "/opt/devkitpro/portlibs/switch/include", - "/opt/devkitpro/portlibs/switch/include/freetype2", - "${workspaceFolder}/**" - ], - "defines": [ - "_DEBUG", - "UNICODE", - "_UNICODE", - "__aarch64__", - "__SWITCH__", - "HAVE_LIBNX" - ], - "windowsSdkVersion": "10.0.17763.0", - "compilerPath": "/opt/devkitpro/devkitA64/bin/aarch64-none-elf-gcc", - "cStandard": "c11", - "cppStandard": "c++11", - "intelliSenseMode": "gcc-x64" - } - ], - "version": 4 + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/msys64/mingw32/include", + "C:/msys64/mingw32/i686-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "msys2-mingw64", + "includePath": [ + "C:/msys64/mingw64/include", + "C:/msys64/mingw64/x86_64-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/msys64/mingw64/include", + "C:/msys64/mingw64/x86_64-w64-mingw32/include", + "${workspaceRoot}/libretro-common/include", + "${workspaceRoot}/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "Switch", + "includePath": [ + "/opt/devkitpro/devkitA64/aarch64-none-elf/include", + "/opt/devkitpro/devkitA64/lib/gcc/aarch64-none-elf/8.3.0/include", + "/opt/devkitpro/libnx/include", + "/opt/devkitpro/portlibs/switch/include", + "/opt/devkitpro/portlibs/switch/include/freetype2", + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "__aarch64__", + "__SWITCH__", + "HAVE_LIBNX" + ], + "windowsSdkVersion": "10.0.17763.0", + "compilerPath": "/opt/devkitpro/devkitA64/bin/aarch64-none-elf-gcc", + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "gcc-x64" + } + ], + "version": 4 } \ No newline at end of file diff --git a/config.def.h b/config.def.h index 80b96ee352..bd0fd0e9aa 100644 --- a/config.def.h +++ b/config.def.h @@ -412,6 +412,21 @@ #define DEFAULT_SHADER_ENABLE false #endif +/* Should we enable hdr when its supported*/ +#define DEFAULT_VIDEO_HDR_ENABLE false + +/* The maximum nunmber of nits the actual display can show - needs to be calibrated */ +#define DEFAULT_VIDEO_HDR_MAX_NITS 1000.0f + +/* The number of nits that paper white is at */ +#define DEFAULT_VIDEO_HDR_PAPER_WHITE_NITS 200.0f + +/* The contrast setting for hdr used to calculate the display gamma by dividing gamma 2.2 by this value */ +#define DEFAULT_VIDEO_HDR_CONTRAST 1.0f + +/* Should we expand the colour gamut when using hdr */ +#define DEFAULT_VIDEO_HDR_EXPAND_GAMUT true + /* When presets are saved they will be saved using the #reference * directive by default */ #define DEFAULT_VIDEO_SHADER_PRESET_SAVE_REFERENCE_ENABLE true diff --git a/configuration.c b/configuration.c index 63fb5bbca5..cb3365c0d8 100644 --- a/configuration.c +++ b/configuration.c @@ -1656,7 +1656,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("video_scale_integer", &settings->bools.video_scale_integer, true, DEFAULT_SCALE_INTEGER, false); SETTING_BOOL("video_scale_integer_overscale", &settings->bools.video_scale_integer_overscale, true, DEFAULT_SCALE_INTEGER_OVERSCALE, false); SETTING_BOOL("video_smooth", &settings->bools.video_smooth, true, DEFAULT_VIDEO_SMOOTH, false); - SETTING_BOOL("video_ctx_scaling", &settings->bools.video_ctx_scaling, true, DEFAULT_VIDEO_CTX_SCALING, false); + SETTING_BOOL("video_ctx_scaling", &settings->bools.video_ctx_scaling, true, DEFAULT_VIDEO_CTX_SCALING, false); SETTING_BOOL("video_force_aspect", &settings->bools.video_force_aspect, true, DEFAULT_FORCE_ASPECT, false); #if defined(DINGUX) SETTING_BOOL("video_dingux_ipu_keep_aspect", &settings->bools.video_dingux_ipu_keep_aspect, true, DEFAULT_DINGUX_IPU_KEEP_ASPECT, false); @@ -1666,6 +1666,8 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("auto_screenshot_filename", &settings->bools.auto_screenshot_filename, true, DEFAULT_AUTO_SCREENSHOT_FILENAME, false); SETTING_BOOL("video_force_srgb_disable", &settings->bools.video_force_srgb_disable, true, false, false); SETTING_BOOL("video_fullscreen", &settings->bools.video_fullscreen, true, DEFAULT_FULLSCREEN, false); + SETTING_BOOL("video_hdr_enable", &settings->bools.video_hdr_enable, true, DEFAULT_VIDEO_HDR_ENABLE, false); + SETTING_BOOL("video_hdr_expand_gamut", &settings->bools.video_hdr_expand_gamut, true, DEFAULT_VIDEO_HDR_EXPAND_GAMUT, false); SETTING_BOOL("bundle_assets_extract_enable", &settings->bools.bundle_assets_extract_enable, true, DEFAULT_BUNDLE_ASSETS_EXTRACT_ENABLE, false); SETTING_BOOL("video_vsync", &settings->bools.video_vsync, true, DEFAULT_VSYNC, false); SETTING_BOOL("video_adaptive_vsync", &settings->bools.video_adaptive_vsync, true, DEFAULT_ADAPTIVE_VSYNC, false); @@ -2039,6 +2041,10 @@ static struct config_float_setting *populate_settings_float( SETTING_FLOAT("input_analog_sensitivity", &settings->floats.input_analog_sensitivity, true, DEFAULT_ANALOG_SENSITIVITY, false); SETTING_FLOAT("video_msg_bgcolor_opacity", &settings->floats.video_msg_bgcolor_opacity, true, message_bgcolor_opacity, false); + SETTING_FLOAT("video_hdr_max_nits", &settings->floats.video_hdr_max_nits, true, DEFAULT_VIDEO_HDR_MAX_NITS, false); + SETTING_FLOAT("video_hdr_paper_white_nits", &settings->floats.video_hdr_paper_white_nits, true, DEFAULT_VIDEO_HDR_PAPER_WHITE_NITS, false); + SETTING_FLOAT("video_hdr_contrast", &settings->floats.video_hdr_contrast, true, DEFAULT_VIDEO_HDR_CONTRAST, false); + *size = count; return tmp; diff --git a/configuration.h b/configuration.h index 88b9fb992c..3effb395cc 100644 --- a/configuration.h +++ b/configuration.h @@ -345,6 +345,9 @@ typedef struct settings float video_msg_color_g; float video_msg_color_b; float video_msg_bgcolor_opacity; + float video_hdr_max_nits; + float video_hdr_paper_white_nits; + float video_hdr_contrast; float menu_scale_factor; float menu_widget_scale_factor; @@ -553,6 +556,8 @@ typedef struct settings #ifdef HAVE_VIDEO_LAYOUT bool video_layout_enable; #endif + bool video_hdr_enable; + bool video_hdr_expand_gamut; /* Accessibility */ bool accessibility_enable; diff --git a/gfx/common/d3d11_common.h b/gfx/common/d3d11_common.h index fae19e398f..0928e91ce4 100644 --- a/gfx/common/d3d11_common.h +++ b/gfx/common/d3d11_common.h @@ -2529,6 +2529,9 @@ typedef struct D3D11RasterizerState scissor_disabled; D3D11Buffer ubo; d3d11_uniform_t ubo_values; +#ifdef HAVE_DXGI_HDR + d3d11_texture_t back_buffer; +#endif D3D11SamplerState samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX]; D3D11BlendState blend_enable; D3D11BlendState blend_disable; @@ -2549,6 +2552,12 @@ typedef struct bool has_flip_model; bool has_allow_tearing; d3d11_shader_t shaders[GFX_MAX_SHADERS]; +#ifdef HAVE_DXGI_HDR + enum dxgi_swapchain_bit_depth + chain_bit_depth; + DXGI_COLOR_SPACE_TYPE chain_color_space; + DXGI_FORMAT chain_formats[DXGI_SWAPCHAIN_BIT_DEPTH_COUNT]; +#endif #ifdef __WINRT__ DXGIFactory2 factory; #else @@ -2562,6 +2571,20 @@ typedef struct struct retro_hw_render_interface_d3d11 iface; } hw; +#ifdef HAVE_DXGI_HDR + struct + { + dxgi_hdr_uniform_t ubo_values; + D3D11Buffer ubo; + float max_output_nits; + float min_output_nits; + float max_cll; + float max_fall; + bool support; + bool enable; + } hdr; +#endif + struct { d3d11_shader_t shader; diff --git a/gfx/common/d3d12_common.c b/gfx/common/d3d12_common.c index 8f194f1860..f05f9dd4a6 100644 --- a/gfx/common/d3d12_common.c +++ b/gfx/common/d3d12_common.c @@ -174,7 +174,6 @@ bool d3d12_init_base(d3d12_video_t* d3d12) #else DXGICreateFactory(&d3d12->factory); #endif - { int i = 0; settings_t *settings = config_get_ptr(); @@ -200,7 +199,6 @@ bool d3d12_init_base(d3d12_video_t* d3d12) if (FAILED(DXGIEnumAdapters(d3d12->factory, i, &adapter))) break; #endif - IDXGIAdapter_GetDesc(adapter, &desc); utf16_to_char_string((const uint16_t*)desc.Description, str, sizeof(str)); @@ -284,13 +282,30 @@ bool d3d12_init_swapchain(d3d12_video_t* d3d12, { unsigned i; HRESULT hr; + HWND hwnd; #ifdef __WINRT__ - DXGI_SWAP_CHAIN_DESC1 desc; - memset(&desc, 0, sizeof(DXGI_SWAP_CHAIN_DESC1)); + DXGI_SWAP_CHAIN_DESC1 desc = {{0}}; #else - DXGI_SWAP_CHAIN_DESC desc; - HWND hwnd = (HWND)corewindow; - memset(&desc, 0, sizeof(DXGI_SWAP_CHAIN_DESC)); + DXGI_SWAP_CHAIN_DESC desc = {{0}}; +#endif +#ifdef HAVE_DXGI_HDR + DXGI_COLOR_SPACE_TYPE color_space; + + d3d12->chain.formats[DXGI_SWAPCHAIN_BIT_DEPTH_8] = DXGI_FORMAT_R8G8B8A8_UNORM; + d3d12->chain.formats[DXGI_SWAPCHAIN_BIT_DEPTH_10] = DXGI_FORMAT_R10G10B10A2_UNORM; + d3d12->chain.formats[DXGI_SWAPCHAIN_BIT_DEPTH_16] = DXGI_FORMAT_R16G16B16A16_UNORM; +#endif + + hwnd = (HWND)corewindow; + +#ifdef HAVE_DXGI_HDR + if (!(d3d12->hdr.support = + dxgi_check_display_hdr_support(d3d12->factory, hwnd))) + d3d12->hdr.enable = false; + + d3d12->chain.bit_depth = d3d12->hdr.enable + ? DXGI_SWAPCHAIN_BIT_DEPTH_10 + : DXGI_SWAPCHAIN_BIT_DEPTH_8; #endif desc.BufferCount = countof(d3d12->chain.renderTargets); @@ -298,24 +313,37 @@ bool d3d12_init_swapchain(d3d12_video_t* d3d12, #ifdef __WINRT__ desc.Width = width; desc.Height = height; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; #else desc.BufferDesc.Width = width; desc.BufferDesc.Height = height; - desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.BufferDesc.RefreshRate.Numerator = 0; desc.BufferDesc.RefreshRate.Denominator = 1; #endif + +#ifdef HAVE_DXGI_HDR +#ifdef __WINRT__ + desc.Format = d3d12->chain.formats[d3d12->chain.bit_depth]; +#else + desc.BufferDesc.Format = d3d12->chain.formats[d3d12->chain.bit_depth]; +#endif +#else +#ifdef __WINRT__ + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; +#else + desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; +#endif +#endif + desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; #ifdef HAVE_WINDOW - desc.OutputWindow = hwnd; - desc.Windowed = TRUE; + desc.OutputWindow = hwnd; + desc.Windowed = TRUE; #endif #if 0 - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; #else - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; #endif desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; @@ -334,6 +362,34 @@ bool d3d12_init_swapchain(d3d12_video_t* d3d12, DXGIMakeWindowAssociation(d3d12->factory, hwnd, DXGI_MWA_NO_ALT_ENTER); #endif +#ifdef HAVE_DXGI_HDR + /* Check display HDR support and + initialize ST.2084 support to match + the display's support. */ +#if 0 + d3d12->hdr.max_output_nits = 300.0f; + d3d12->hdr.min_output_nits = 0.001f; + d3d12->hdr.max_cll = 0.0f; + d3d12->hdr.max_fall = 0.0f; +#endif + color_space = + d3d12->hdr.enable + ? DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 + : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + + dxgi_swapchain_color_space(d3d12->chain.handle, + &d3d12->chain.color_space, color_space); + dxgi_set_hdr_metadata( + d3d12->chain.handle, + d3d12->hdr.support, + d3d12->chain.bit_depth, + d3d12->chain.color_space, + d3d12->hdr.max_output_nits, + d3d12->hdr.min_output_nits, + d3d12->hdr.max_cll, + d3d12->hdr.max_fall); +#endif + d3d12->chain.frame_index = DXGIGetCurrentBackBufferIndex(d3d12->chain.handle); for (i = 0; i < countof(d3d12->chain.renderTargets); i++) @@ -343,10 +399,28 @@ bool d3d12_init_swapchain(d3d12_video_t* d3d12, d3d12->device, d3d12->chain.renderTargets[i], NULL, d3d12->chain.desc_handles[i]); } - d3d12->chain.viewport.Width = width; - d3d12->chain.viewport.Height = height; - d3d12->chain.scissorRect.right = width; - d3d12->chain.scissorRect.bottom = height; +#ifdef HAVE_DXGI_HDR + memset(&d3d12->chain.back_buffer, + 0, sizeof(d3d12->chain.back_buffer)); + d3d12->chain.back_buffer.desc.Width = width; + d3d12->chain.back_buffer.desc.Height = height; + d3d12->chain.back_buffer.desc.Format = + DXGI_FORMAT_R8G8B8A8_UNORM; + d3d12->chain.back_buffer.desc.Flags = + D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + d3d12->chain.back_buffer.srv_heap = + &d3d12->desc.srv_heap; + d3d12->chain.back_buffer.rt_view.ptr = + d3d12->desc.rtv_heap.cpu.ptr + + (countof(d3d12->chain.renderTargets)) + * d3d12->desc.rtv_heap.stride; + d3d12_init_texture(d3d12->device, &d3d12->chain.back_buffer); +#endif + + d3d12->chain.viewport.Width = width; + d3d12->chain.viewport.Height = height; + d3d12->chain.scissorRect.right = width; + d3d12->chain.scissorRect.bottom = height; return true; } @@ -619,6 +693,19 @@ D3D12_RENDER_TARGET_BLEND_DESC d3d12_blend_enable_desc = { D3D12_COLOR_WRITE_ENABLE_ALL, }; +D3D12_RENDER_TARGET_BLEND_DESC d3d12_blend_disable_desc = { + FALSE, + FALSE, + D3D12_BLEND_SRC_ALPHA, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_SRC_ALPHA, + D3D12_BLEND_INV_SRC_ALPHA, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, +}; + bool d3d12_init_pipeline( D3D12Device device, D3DBlob vs_code, diff --git a/gfx/common/d3d12_common.h b/gfx/common/d3d12_common.h index f3c9368567..80c56ba1ff 100644 --- a/gfx/common/d3d12_common.h +++ b/gfx/common/d3d12_common.h @@ -1385,6 +1385,9 @@ typedef struct { DXGISwapChain handle; D3D12Resource renderTargets[2]; +#ifdef HAVE_DXGI_HDR + d3d12_texture_t back_buffer; +#endif D3D12_CPU_DESCRIPTOR_HANDLE desc_handles[2]; D3D12_VIEWPORT viewport; D3D12_RECT scissorRect; @@ -1392,6 +1395,11 @@ typedef struct int frame_index; bool vsync; unsigned swap_interval; +#ifdef HAVE_DXGI_HDR + enum dxgi_swapchain_bit_depth bit_depth; + DXGI_COLOR_SPACE_TYPE color_space; + DXGI_FORMAT formats[DXGI_SWAPCHAIN_BIT_DEPTH_COUNT]; +#endif } chain; struct @@ -1407,6 +1415,21 @@ typedef struct int rotation; } frame; +#ifdef HAVE_DXGI_HDR + struct + { + dxgi_hdr_uniform_t ubo_values; + D3D12Resource ubo; + D3D12_CONSTANT_BUFFER_VIEW_DESC ubo_view; + float max_output_nits; + float min_output_nits; + float max_cll; + float max_fall; + bool support; + bool enable; + } hdr; +#endif + struct { D3D12Resource vbo; @@ -1490,19 +1513,20 @@ typedef enum { ROOT_ID_SAMPLER_T, ROOT_ID_UBO, ROOT_ID_PC, - ROOT_ID_MAX, + ROOT_ID_MAX } root_signature_parameter_index_t; typedef enum { CS_ROOT_ID_TEXTURE_T = 0, CS_ROOT_ID_UAV_T, CS_ROOT_ID_CONSTANTS, - CS_ROOT_ID_MAX, + CS_ROOT_ID_MAX } compute_root_index_t; RETRO_BEGIN_DECLS extern D3D12_RENDER_TARGET_BLEND_DESC d3d12_blend_enable_desc; +extern D3D12_RENDER_TARGET_BLEND_DESC d3d12_blend_disable_desc; bool d3d12_init_base(d3d12_video_t* d3d12); diff --git a/gfx/common/d3dcompiler_common.c b/gfx/common/d3dcompiler_common.c index a408ac7b4f..7206857b8a 100644 --- a/gfx/common/d3dcompiler_common.c +++ b/gfx/common/d3dcompiler_common.c @@ -140,7 +140,8 @@ bool d3d_compile(const char* src, size_t size, LPCSTR src_name, LPCSTR entrypoin { if (error_msg) { - RARCH_ERR("D3DCompile failed :\n%s\n", (const char*)D3DGetBufferPointer(error_msg)); + const char* msg = (const char*)D3DGetBufferPointer(error_msg); + RARCH_ERR("D3DCompile failed :\n%s\n", msg); /* Place a breakpoint here, if you want, to see shader compilation issues */ Release(error_msg); } return false; diff --git a/gfx/common/dxgi_common.c b/gfx/common/dxgi_common.c index de282eb368..0e8c0e78ff 100644 --- a/gfx/common/dxgi_common.c +++ b/gfx/common/dxgi_common.c @@ -31,6 +31,18 @@ #include "../frontend/frontend_driver.h" #include "win32_common.h" +const GUID DECLSPEC_SELECTANY libretro_IID_IDXGIOutput6 = { 0x068346e8,0xaaec, +0x4b84, {0xad,0xd7,0x13,0x7f,0x51,0x3f,0x77,0xa1 } }; + +#ifdef HAVE_DXGI_HDR +typedef enum hdr_root_constants +{ + HDR_ROOT_CONSTANTS_REFERENCE_WHITE_NITS = 0, + HDR_ROOT_CONSTANTS_DISPLAY_CURVE, + HDR_ROOT_CONSTANTS_COUNT +} hdr_root_constants_t; +#endif + #if defined(HAVE_DYNAMIC) && !defined(__WINRT__) #include @@ -347,3 +359,260 @@ DXGI_FORMAT glslang_format_to_dxgi(glslang_format fmt) return DXGI_FORMAT_UNKNOWN; } + +#ifdef HAVE_DXGI_HDR +typedef struct display_chromaticities +{ + float red_x; + float red_y; + float green_x; + float green_y; + float blue_x; + float blue_y; + float white_x; + float white_y; +} display_chromaticities_t; + +inline static int dxgi_compute_intersection_area( + int ax1, int ay1, int ax2, int ay2, + int bx1, int by1, int bx2, int by2) +{ + return max(0, min(ax2, bx2) - + max(ax1, bx1)) + * max(0, min(ay2, by2) - max(ay1, by1)); +} + +#ifdef __WINRT__ +bool dxgi_check_display_hdr_support(DXGIFactory2 factory, HWND hwnd) +#else +bool dxgi_check_display_hdr_support(DXGIFactory factory, HWND hwnd) +#endif +{ + DXGIOutput6 output6 = NULL; + DXGIOutput best_output = NULL; + DXGIOutput current_output = NULL; + DXGIAdapter dxgi_adapter = NULL; + UINT i = 0; + bool supported = false; + float best_intersect_area = -1; + + if (!DXGIIsCurrent(factory)) + { + if (FAILED(DXGICreateFactory(&factory))) + { + RARCH_ERR("[DXGI]: Failed to create DXGI factory\n"); + return false; + } + } + + if (FAILED(DXGIEnumAdapters(factory, 0, &dxgi_adapter))) + { + RARCH_ERR("[DXGI]: Failed to enumerate adapters\n"); + return false; + } + + while ( DXGIEnumOutputs(dxgi_adapter, i, ¤t_output) + != DXGI_ERROR_NOT_FOUND) + { + RECT r, rect; + DXGI_OUTPUT_DESC desc; + int intersect_area; + int bx1, by1, bx2, by2; + int ax1 = 0; + int ay1 = 0; + int ax2 = 0; + int ay2 = 0; + + if (GetWindowRect(hwnd, &rect)) /* TODO/FIXME - won't work for WinRT */ + { + ax1 = rect.left; + ay1 = rect.top; + ax2 = rect.right; + ay2 = rect.bottom; + } + + /* Get the rectangle bounds of current output */ + if (FAILED(DXGIGetOutputDesc(current_output, &desc))) + { + RARCH_ERR("[DXGI]: Failed to get DXGI output description\n"); + goto error; + } + + /* TODO/FIXME - DesktopCoordinates won't work for WinRT */ + r = desc.DesktopCoordinates; + bx1 = r.left; + by1 = r.top; + bx2 = r.right; + by2 = r.bottom; + + /* Compute the intersection */ + intersect_area = dxgi_compute_intersection_area( + ax1, ay1, ax2, ay2, bx1, by1, bx2, by2); + + if (intersect_area > best_intersect_area) + { + best_output = current_output; + AddRef(best_output); + best_intersect_area = (float)intersect_area; + } + + i++; + } + + if (SUCCEEDED(best_output->lpVtbl->QueryInterface( + best_output, + &libretro_IID_IDXGIOutput6, (void**)&output6))) + { + DXGI_OUTPUT_DESC1 desc1; + if (SUCCEEDED(DXGIGetOutputDesc1(output6, &desc1))) + { + supported = (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020); + + if (supported) + video_driver_set_hdr_support(); + else + { + settings_t* settings = config_get_ptr(); + settings->modified = true; + settings->bools.video_hdr_enable = false; + + video_driver_unset_hdr_support(); + } + } + else + { + RARCH_ERR("[DXGI]: Failed to get DXGI Output 6 description\n"); + } + Release(output6); + } + else + { + RARCH_ERR("[DXGI]: Failed to get DXGI Output 6 from best output\n"); + } + +error: + Release(best_output); + Release(current_output); + Release(dxgi_adapter); + + return supported; +} + +void dxgi_swapchain_color_space( + DXGISwapChain chain_handle, + DXGI_COLOR_SPACE_TYPE *chain_color_space, + DXGI_COLOR_SPACE_TYPE color_space) +{ + if (*chain_color_space != color_space) + { + UINT color_space_support = 0; + if (SUCCEEDED(DXGICheckColorSpaceSupport( + chain_handle, color_space, + &color_space_support)) + && ((color_space_support & + DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) + == DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) + { + if (FAILED(DXGISetColorSpace1(chain_handle, color_space))) + { + RARCH_ERR("[DXGI]: Failed to set DXGI swapchain colour space\n"); + /* TODO/FIXME/CLARIFICATION: Was this fall-through intentional? + * Should chain color space still be set even when this fails? + * Going to assume this was wrong and early return instead + */ + return; + } + + *chain_color_space = color_space; + } + } +} + +void dxgi_set_hdr_metadata( + DXGISwapChain handle, + bool hdr_supported, + enum dxgi_swapchain_bit_depth chain_bit_depth, + DXGI_COLOR_SPACE_TYPE chain_color_space, + float max_output_nits, + float min_output_nits, + float max_cll, + float max_fall +) +{ + static const display_chromaticities_t + display_chromaticity_list[] = + { + { 0.64000f, 0.33000f, 0.30000f, 0.60000f, 0.15000f, 0.06000f, 0.31270f, 0.32900f }, /* Rec709 */ + { 0.70800f, 0.29200f, 0.17000f, 0.79700f, 0.13100f, 0.04600f, 0.31270f, 0.32900f }, /* Rec2020 */ + }; + const display_chromaticities_t* chroma = NULL; + DXGI_HDR_METADATA_HDR10 hdr10_meta_data = {0}; + int selected_chroma = 0; + + if (!handle) + return; + + /* Clear the hdr meta data if the monitor does not support HDR */ + if (!hdr_supported) + { + if (FAILED(DXGISetHDRMetaData(handle, + DXGI_HDR_METADATA_TYPE_NONE, 0, NULL))) + { + RARCH_ERR("[DXGI]: Failed to set HDR meta data to none\n"); + } + return; + } + + + /* Now select the chromacity based on colour space */ + if ( chain_bit_depth == DXGI_SWAPCHAIN_BIT_DEPTH_10 + && chain_color_space == + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) + selected_chroma = 1; + else + { + if (FAILED(DXGISetHDRMetaData(handle, + DXGI_HDR_METADATA_TYPE_NONE, 0, NULL))) + { + RARCH_ERR("[DXGI]: Failed to set HDR meta data to none\n"); + } + + return; + } + + /* Set the HDR meta data */ + chroma = + &display_chromaticity_list[selected_chroma]; + hdr10_meta_data.RedPrimary[0] = + (UINT16)(chroma->red_x * 50000.0f); + hdr10_meta_data.RedPrimary[1] = + (UINT16)(chroma->red_y * 50000.0f); + hdr10_meta_data.GreenPrimary[0] = + (UINT16)(chroma->green_x * 50000.0f); + hdr10_meta_data.GreenPrimary[1] = + (UINT16)(chroma->green_y * 50000.0f); + hdr10_meta_data.BluePrimary[0] = + (UINT16)(chroma->blue_x * 50000.0f); + hdr10_meta_data.BluePrimary[1] = + (UINT16)(chroma->blue_y * 50000.0f); + hdr10_meta_data.WhitePoint[0] = + (UINT16)(chroma->white_x * 50000.0f); + hdr10_meta_data.WhitePoint[1] = + (UINT16)(chroma->white_y * 50000.0f); + hdr10_meta_data.MaxMasteringLuminance = + (UINT)(max_output_nits * 10000.0f); + hdr10_meta_data.MinMasteringLuminance = + (UINT)(min_output_nits * 10000.0f); + hdr10_meta_data.MaxContentLightLevel = + (UINT16)(max_cll); + hdr10_meta_data.MaxFrameAverageLightLevel = + (UINT16)(max_fall); + + if (FAILED(DXGISetHDRMetaData(handle, + DXGI_HDR_METADATA_TYPE_HDR10, + sizeof(DXGI_HDR_METADATA_HDR10), &hdr10_meta_data))) + { + RARCH_ERR("[DXGI]: Failed to set HDR meta data for HDR10\n"); + } +} +#endif diff --git a/gfx/common/dxgi_common.h b/gfx/common/dxgi_common.h index 366bde0282..4ac0f86c36 100644 --- a/gfx/common/dxgi_common.h +++ b/gfx/common/dxgi_common.h @@ -2,6 +2,41 @@ #include +#ifndef __WINRT__ +#ifndef HAVE_DXGI_HDR +#define HAVE_DXGI_HDR +#endif +#endif + +#ifdef HAVE_DXGI_HDR +#ifndef ALIGN +#ifdef _MSC_VER +#define ALIGN(x) __declspec(align(x)) +#else +#define ALIGN(x) __attribute__((aligned(x))) +#endif +#endif + +#include + +typedef struct ALIGN(16) +{ + math_matrix_4x4 mvp; + float contrast; /* 2.0f */ + float paperWhiteNits; /* 200.0f */ + float maxNits; /* 1000.0f */ + float expandGamut; /* 1.0f */ +} dxgi_hdr_uniform_t; + +enum dxgi_swapchain_bit_depth +{ + DXGI_SWAPCHAIN_BIT_DEPTH_8 = 0, + DXGI_SWAPCHAIN_BIT_DEPTH_10, + DXGI_SWAPCHAIN_BIT_DEPTH_16, + DXGI_SWAPCHAIN_BIT_DEPTH_COUNT +}; +#endif + #ifdef __MINGW32__ #define __REQUIRED_RPCNDR_H_VERSION__ 475 /* Pointer parameters */ @@ -233,7 +268,7 @@ #endif #include -#include +#include #ifndef countof #define countof(a) (sizeof(a) / sizeof(*a)) @@ -281,6 +316,7 @@ typedef IDXGIResource* DXGIResource; typedef IDXGIKeyedMutex* DXGIKeyedMutex; typedef IDXGISurface1* DXGISurface; typedef IDXGIOutput* DXGIOutput; +typedef IDXGIOutput6* DXGIOutput6; typedef IDXGIDevice* DXGIDevice; typedef IDXGIFactory1* DXGIFactory; #ifdef __WINRT__ @@ -292,7 +328,7 @@ typedef IDXGIOutputDuplication* DXGIOutputDuplication; typedef IDXGIDecodeSwapChain* DXGIDecodeSwapChain; typedef IDXGIFactoryMedia* DXGIFactoryMedia; typedef IDXGISwapChainMedia* DXGISwapChainMedia; -typedef IDXGISwapChain3* DXGISwapChain; +typedef IDXGISwapChain4* DXGISwapChain; #if !defined(__cplusplus) || defined(CINTERFACE) static INLINE ULONG DXGIReleaseDeviceSubObject(DXGIDeviceSubObject device_sub_object) @@ -392,6 +428,14 @@ static INLINE HRESULT DXGIGetDisplaySurfaceData(DXGIOutput output, DXGISurface d { return output->lpVtbl->GetDisplaySurfaceData(output, (IDXGISurface*)destination); } +static INLINE HRESULT DXGIGetOutputDesc(DXGIOutput output, DXGI_OUTPUT_DESC* desc) +{ + return output->lpVtbl->GetDesc(output, desc); +} +static INLINE HRESULT DXGIGetOutputDesc1(DXGIOutput6 output, DXGI_OUTPUT_DESC1* desc) +{ + return output->lpVtbl->GetDesc1(output, desc); +} static INLINE ULONG DXGIReleaseDevice(DXGIDevice device) { return device->lpVtbl->Release(device); } static INLINE HRESULT DXGICreateSurface( DXGIDevice device, @@ -769,11 +813,14 @@ static INLINE HRESULT DXGICheckColorSpaceSupport( { return swap_chain->lpVtbl->CheckColorSpaceSupport(swap_chain, color_space, color_space_support); } -static INLINE HRESULT -DXGISetColorSpace1(DXGISwapChain swap_chain, DXGI_COLOR_SPACE_TYPE color_space) +static INLINE HRESULT DXGISetColorSpace1(DXGISwapChain swap_chain, DXGI_COLOR_SPACE_TYPE color_space) { return swap_chain->lpVtbl->SetColorSpace1(swap_chain, color_space); } +static INLINE HRESULT DXGISetHDRMetaData(DXGISwapChain swap_chain, DXGI_HDR_METADATA_TYPE type, UINT size, void *metaData) +{ + return swap_chain->lpVtbl->SetHDRMetaData(swap_chain, type, size, metaData); +} #endif /* end of auto-generated */ @@ -821,6 +868,26 @@ void dxgi_copy( int dst_pitch, void* dst_data); +#ifdef HAVE_DXGI_HDR +#ifdef __WINRT__ +bool dxgi_check_display_hdr_support(DXGIFactory2 factory, HWND hwnd); +#else +bool dxgi_check_display_hdr_support(DXGIFactory factory, HWND hwnd); +#endif +void dxgi_swapchain_color_space(DXGISwapChain handle, DXGI_COLOR_SPACE_TYPE +*chain_color_space, DXGI_COLOR_SPACE_TYPE color_space); +void dxgi_set_hdr_metadata( + DXGISwapChain handle, + bool hdr_supported, + enum dxgi_swapchain_bit_depth chain_bit_depth, + DXGI_COLOR_SPACE_TYPE chain_color_space, + float max_output_nits, + float min_output_nits, + float max_cll, + float max_fall +); +#endif + DXGI_FORMAT glslang_format_to_dxgi(glslang_format fmt); RETRO_END_DECLS diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index 583f05d72f..ec5a90beda 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -932,6 +932,12 @@ static LRESULT CALLBACK wnd_proc_common( /* fall-through */ case WM_MOVE: win32_save_position(); +#if 0 +#if !defined(_XBOX) + if(d3d12) + d3d12_check_display_hdr_support(d3d12, hwnd); +#endif +#endif break; case WM_SIZE: /* Do not send resize message if we minimize. */ diff --git a/gfx/drivers/caca_gfx.c b/gfx/drivers/caca_gfx.c index dcf27a0f1c..7615326214 100644 --- a/gfx/drivers/caca_gfx.c +++ b/gfx/drivers/caca_gfx.c @@ -270,6 +270,10 @@ static const video_poke_interface_t caca_poke_interface = { NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void caca_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/ctr_gfx.c b/gfx/drivers/ctr_gfx.c index 8604036d85..db2f202078 100644 --- a/gfx/drivers/ctr_gfx.c +++ b/gfx/drivers/ctr_gfx.c @@ -1535,7 +1535,11 @@ static const video_poke_interface_t ctr_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void ctr_get_poke_interface(void* data, diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 07182da906..fffca21c2c 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -1008,7 +1008,7 @@ static void *d3d10_gfx_init(const video_info_t* video, #else DXGICreateFactory(&d3d10->factory); #endif - + { int i = 0; int gpu_index = settings->ints.d3d10_gpu_index; @@ -1800,6 +1800,10 @@ static const video_poke_interface_t d3d10_poke_interface = { #else NULL, /* get_hw_render_interface */ #endif + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void d3d10_gfx_get_poke_interface(void* data, const video_poke_interface_t** iface) diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 351b5cf877..e9339a0094 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -255,6 +255,79 @@ static void d3d11_get_overlay_interface( } #endif +#ifdef HAVE_DXGI_HDR +static void d3d11_set_hdr_max_nits(void *data, float max_nits) +{ + D3D11_MAPPED_SUBRESOURCE mapped_ubo; + d3d11_video_t* d3d11 = (d3d11_video_t*)data; + + d3d11->hdr.max_output_nits = max_nits; + d3d11->hdr.ubo_values.maxNits = max_nits; + + D3D11MapBuffer(d3d11->context, d3d11->hdr.ubo, + 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mapped_ubo); + { + dxgi_hdr_uniform_t *ubo = (dxgi_hdr_uniform_t*)mapped_ubo.pData; + *ubo = d3d11->hdr.ubo_values; + } + D3D11UnmapBuffer(d3d11->context, d3d11->hdr.ubo, 0); + + dxgi_set_hdr_metadata( + d3d11->swapChain, + d3d11->hdr.support, + d3d11->chain_bit_depth, + d3d11->chain_color_space, + d3d11->hdr.max_output_nits, + d3d11->hdr.min_output_nits, + d3d11->hdr.max_cll, + d3d11->hdr.max_fall); +} + +static void d3d11_set_hdr_paper_white_nits(void* data, float paper_white_nits) +{ + D3D11_MAPPED_SUBRESOURCE mapped_ubo; + dxgi_hdr_uniform_t *ubo = NULL; + d3d11_video_t *d3d11 = (d3d11_video_t*)data; + + d3d11->hdr.ubo_values.paperWhiteNits = paper_white_nits; + + D3D11MapBuffer(d3d11->context, d3d11->hdr.ubo, + 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_ubo); + ubo = (dxgi_hdr_uniform_t*)mapped_ubo.pData; + *ubo = d3d11->hdr.ubo_values; + D3D11UnmapBuffer(d3d11->context, d3d11->hdr.ubo, 0); +} + +static void d3d11_set_hdr_contrast(void* data, float contrast) +{ + D3D11_MAPPED_SUBRESOURCE mapped_ubo; + dxgi_hdr_uniform_t *ubo = NULL; + d3d11_video_t* d3d11 = (d3d11_video_t*)data; + + d3d11->hdr.ubo_values.contrast = contrast; + + D3D11MapBuffer(d3d11->context, d3d11->hdr.ubo, + 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_ubo); + ubo = (dxgi_hdr_uniform_t*)mapped_ubo.pData; + *ubo = d3d11->hdr.ubo_values; + D3D11UnmapBuffer(d3d11->context, d3d11->hdr.ubo, 0); +} + +static void d3d11_set_hdr_expand_gamut(void* data, bool expand_gamut) +{ + D3D11_MAPPED_SUBRESOURCE mapped_ubo; + dxgi_hdr_uniform_t *ubo = NULL; + d3d11_video_t* d3d11 = (d3d11_video_t*)data; + + d3d11->hdr.ubo_values.expandGamut = expand_gamut; + + D3D11MapBuffer(d3d11->context, d3d11->hdr.ubo, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_ubo); + ubo = (dxgi_hdr_uniform_t*)mapped_ubo.pData; + *ubo = d3d11->hdr.ubo_values; + D3D11UnmapBuffer(d3d11->context, d3d11->hdr.ubo, 0); +} +#endif + static void d3d11_set_filtering(void* data, unsigned index, bool smooth, bool ctx_scaling) { @@ -559,6 +632,10 @@ static void d3d11_gfx_free(void* data) d3d11_release_texture(&d3d11->menu.texture); Release(d3d11->menu.vbo); +#ifdef HAVE_DXGI_HDR + Release(d3d11->hdr.ubo); +#endif + d3d11_release_shader(&d3d11->sprites.shader); d3d11_release_shader(&d3d11->sprites.shader_font); Release(d3d11->sprites.vbo); @@ -580,6 +657,10 @@ static void d3d11_gfx_free(void* data) Release(d3d11->samplers[RARCH_FILTER_NEAREST][i]); } +#ifdef HAVE_DXGI_HDR + d3d11_release_texture(&d3d11->back_buffer); +#endif + Release(d3d11->scissor_enabled); Release(d3d11->scissor_disabled); Release(d3d11->swapChain); @@ -607,6 +688,10 @@ static void d3d11_gfx_free(void* data) } } +#ifdef HAVE_DXGI_HDR + video_driver_unset_hdr_support(); +#endif + #ifdef HAVE_MONITOR win32_monitor_from_window(); #endif @@ -622,6 +707,7 @@ static bool d3d11_init_swapchain(d3d11_video_t* d3d11, D3D11DeviceContext *cached_context, void *corewindow) { + HWND hwnd; #ifdef __WINRT__ IDXGIFactory2* dxgiFactory = NULL; #else @@ -646,19 +732,51 @@ static bool d3d11_init_swapchain(d3d11_video_t* d3d11, #endif UINT number_feature_levels = ARRAY_SIZE(requested_feature_levels); +#ifdef HAVE_DXGI_HDR + DXGI_COLOR_SPACE_TYPE color_space; + + d3d11->chain_formats[DXGI_SWAPCHAIN_BIT_DEPTH_8] = DXGI_FORMAT_R8G8B8A8_UNORM; + d3d11->chain_formats[DXGI_SWAPCHAIN_BIT_DEPTH_10] = DXGI_FORMAT_R10G10B10A2_UNORM; + d3d11->chain_formats[DXGI_SWAPCHAIN_BIT_DEPTH_16] = DXGI_FORMAT_R16G16B16A16_UNORM; +#endif + + hwnd = (HWND)corewindow; + +#ifdef HAVE_DXGI_HDR + if (!(d3d11->hdr.support = + dxgi_check_display_hdr_support(d3d11->factory, hwnd))) + d3d11->hdr.enable = false; + + d3d11->chain_bit_depth = d3d11->hdr.enable + ? DXGI_SWAPCHAIN_BIT_DEPTH_10 + : DXGI_SWAPCHAIN_BIT_DEPTH_8; +#endif + #ifdef __WINRT__ /* Flip model forces us to do double-buffering */ desc.BufferCount = 2; desc.Width = width; desc.Height = height; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; +#ifdef HAVE_DXGI_HDR + if (d3d11->hdr.support) + desc.Format = d3d11->chain_formats[ + d3d11->chain_bit_depth]; + else +#endif + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; #else desc.BufferCount = 2; desc.BufferDesc.Width = width; desc.BufferDesc.Height = height; - desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; +#ifdef HAVE_DXGI_HDR + if (d3d11->hdr.support) + desc.BufferDesc.Format = d3d11->chain_formats[ + d3d11->chain_bit_depth]; + else +#endif + desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.BufferDesc.RefreshRate.Numerator = 60; desc.BufferDesc.RefreshRate.Denominator = 1; #endif @@ -777,7 +895,44 @@ static bool d3d11_init_swapchain(d3d11_video_t* d3d11, } #endif -#endif // __WINRT__ +#endif /* __WINRT__ */ + +#ifdef HAVE_DXGI_HDR + /* Check display HDR support and + initialize ST.2084 support to match + the display's support. */ +#if 0 + d3d11->hdr.max_output_nits = 300.0f; + d3d11->hdr.min_output_nits = 0.001f; + d3d11->hdr.max_cll = 0.0f; + d3d11->hdr.max_fall = 0.0f; +#endif + color_space = + d3d11->hdr.enable + ? DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 + : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + + dxgi_swapchain_color_space( + d3d11->swapChain, + &d3d11->chain_color_space, + color_space); + dxgi_set_hdr_metadata( + d3d11->swapChain, + d3d11->hdr.support, + d3d11->chain_bit_depth, + d3d11->chain_color_space, + d3d11->hdr.max_output_nits, + d3d11->hdr.min_output_nits, + d3d11->hdr.max_cll, + d3d11->hdr.max_fall); + + memset(&d3d11->back_buffer, 0, sizeof(d3d11->back_buffer)); + d3d11->back_buffer.desc.Width = width; + d3d11->back_buffer.desc.Height = height; + d3d11->back_buffer.desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + d3d11->back_buffer.desc.BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_init_texture(d3d11->device, &d3d11->back_buffer); +#endif dxgiFactory->lpVtbl->Release(dxgiFactory); adapter->lpVtbl->Release(adapter); @@ -840,6 +995,19 @@ static void *d3d11_gfx_init(const video_info_t* video, d3d_input_driver(settings->arrays.input_driver, settings->arrays.input_joypad_driver, input, input_data); +#ifdef __WINRT__ + DXGICreateFactory2(&d3d11->factory); +#else + DXGICreateFactory(&d3d11->factory); +#endif +#ifdef HAVE_DXGI_HDR + d3d11->hdr.enable = settings->bools.video_hdr_enable; + d3d11->hdr.max_output_nits = settings->floats.video_hdr_max_nits; + d3d11->hdr.min_output_nits = 0.001f; + d3d11->hdr.max_cll = 0.0f; + d3d11->hdr.max_fall = 0.0f; +#endif + #ifdef __WINRT__ if (!d3d11_init_swapchain(d3d11, d3d11->vp.full_width, @@ -905,6 +1073,39 @@ static void *d3d11_gfx_init(const video_info_t* video, d3d11_gfx_set_rotation(d3d11, 0); +#ifdef HAVE_DXGI_HDR + if (d3d11->hdr.enable) + { + D3D11_BUFFER_DESC desc; + D3D11_SUBRESOURCE_DATA ubo_data; + matrix_4x4_ortho(d3d11->mvp_no_rot, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); + + d3d11->hdr.ubo_values.mvp = + d3d11->mvp_no_rot; + d3d11->hdr.ubo_values.maxNits = + settings->floats.video_hdr_max_nits; + d3d11->hdr.ubo_values.paperWhiteNits = + settings->floats.video_hdr_paper_white_nits; + d3d11->hdr.ubo_values.contrast = + settings->floats.video_hdr_contrast; + d3d11->hdr.ubo_values.expandGamut = + settings->bools.video_hdr_expand_gamut; + + desc.ByteWidth = sizeof(dxgi_hdr_uniform_t); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + + ubo_data.pSysMem = &d3d11->hdr.ubo_values.mvp; + ubo_data.SysMemPitch = 0; + ubo_data.SysMemSlicePitch = 0; + + D3D11CreateBuffer(d3d11->device, &desc, &ubo_data, &d3d11->hdr.ubo); + } +#endif + { D3D11_SAMPLER_DESC desc = { D3D11_FILTER_MIN_MAG_MIP_POINT }; desc.MaxAnisotropy = 1; @@ -974,6 +1175,33 @@ static void *d3d11_gfx_init(const video_info_t* video, D3D11CreateBuffer(d3d11->device, &desc, NULL, &d3d11->sprites.vbo); } +#ifdef HAVE_DXGI_HDR + if (d3d11->hdr.enable) + { + D3D11_INPUT_ELEMENT_DESC desc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, + offsetof(d3d11_vertex_t, position), + D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, + offsetof(d3d11_vertex_t, texcoord), + D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, + offsetof(d3d11_vertex_t, color), + D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + + static const char shader[] = +#include "d3d_shaders/hdr_sm5.hlsl.h" + ; + + if (!d3d11_init_shader( + d3d11->device, shader, sizeof(shader), + NULL, "VSMain", "PSMain", NULL, desc, + countof(desc), &d3d11->shaders[VIDEO_SHADER_STOCK_HDR])) + goto error; + } +#endif + { D3D11_INPUT_ELEMENT_DESC desc[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d11_vertex_t, position), @@ -1156,12 +1384,6 @@ static void *d3d11_gfx_init(const video_info_t* video, d3d11->hw.iface.D3DCompile = D3DCompile; } -#ifdef __WINRT__ - DXGICreateFactory2(&d3d11->factory); -#else - DXGICreateFactory(&d3d11->factory); -#endif - { int i = 0; int gpu_index = settings->ints.d3d11_gpu_index; @@ -1384,32 +1606,91 @@ static bool d3d11_gfx_frame( #ifdef HAVE_GFX_WIDGETS bool widgets_active = video_info->widgets_active; #endif +#ifdef HAVE_DXGI_HDR + bool video_hdr_enable = video_info->hdr_enable; + if ( d3d11->resize_chain || + (d3d11->hdr.enable != video_hdr_enable)) +#else if (d3d11->resize_chain) +#endif { - UINT swapchain_flags = d3d11->has_allow_tearing + UINT swapchain_flags = d3d11->has_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; - DXGIResizeBuffers(d3d11->swapChain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, - swapchain_flags); +#ifdef HAVE_DXGI_HDR + d3d11->hdr.enable = video_hdr_enable; - d3d11->viewport.Width = video_width; - d3d11->viewport.Height = video_height; - d3d11->scissor.right = video_width; - d3d11->scissor.bottom = video_height; + if(d3d11->hdr.enable) + d3d11_release_texture(&d3d11->back_buffer); + DXGIResizeBuffers(d3d11->swapChain, 0, 0, 0, + d3d11->chain_formats[d3d11->chain_bit_depth], + swapchain_flags); +#else + DXGIResizeBuffers(d3d11->swapChain, 0, 0, 0, + DXGI_FORMAT_UNKNOWN, + swapchain_flags); +#endif + + d3d11->viewport.Width = video_width; + d3d11->viewport.Height = video_height; + d3d11->scissor.right = video_width; + d3d11->scissor.bottom = video_height; d3d11->ubo_values.OutputSize.width = d3d11->viewport.Width; d3d11->ubo_values.OutputSize.height = d3d11->viewport.Height; - d3d11->resize_chain = false; - d3d11->resize_viewport = true; + d3d11->resize_chain = false; + d3d11->resize_viewport = true; video_driver_set_size(video_width, video_height); + +#ifdef HAVE_DXGI_HDR +#ifdef __WINRT__ + if (!(d3d11->hdr.support = + dxgi_check_display_hdr_support(d3d11->factory, uwp_get_corewindow()))) + d3d11->hdr.enable = false; +#else + if (!(d3d11->hdr.support = + dxgi_check_display_hdr_support(d3d11->factory, main_window.hwnd))) + d3d11->hdr.enable = false; +#endif + + if(d3d11->hdr.enable) + { + memset(&d3d11->back_buffer, 0, sizeof(d3d11->back_buffer)); + d3d11->back_buffer.desc.Width = video_width; + d3d11->back_buffer.desc.Height = video_height; + d3d11->back_buffer.desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + d3d11->back_buffer.desc.BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_init_texture(d3d11->device, &d3d11->back_buffer); + + dxgi_swapchain_color_space( + d3d11->swapChain, + &d3d11->chain_color_space, + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020); + } + else + dxgi_swapchain_color_space( + d3d11->swapChain, + &d3d11->chain_color_space, + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709); + + dxgi_set_hdr_metadata( + d3d11->swapChain, + d3d11->hdr.support, + d3d11->chain_bit_depth, + d3d11->chain_color_space, + d3d11->hdr.max_output_nits, + d3d11->hdr.min_output_nits, + d3d11->hdr.max_cll, + d3d11->hdr.max_fall); +#endif } { - D3D11Texture2D backBuffer; - DXGIGetSwapChainBufferD3D11(d3d11->swapChain, 0, &backBuffer); - D3D11CreateTexture2DRenderTargetView(d3d11->device, backBuffer, NULL, &rtv); - Release(backBuffer); + D3D11Texture2D back_buffer; + DXGIGetSwapChainBufferD3D11(d3d11->swapChain, 0, &back_buffer); + D3D11CreateTexture2DRenderTargetView(d3d11->device, back_buffer, NULL, &rtv); + Release(back_buffer); } /* custom viewport doesn't call apply_state_changes, so we can't rely on this for now */ @@ -1603,8 +1884,25 @@ static bool d3d11_gfx_frame( } } - D3D11SetRenderTargets(context, 1, &rtv, NULL); - D3D11ClearRenderTargetView(context, rtv, d3d11->clearcolor); + +#ifdef HAVE_DXGI_HDR + if(d3d11->hdr.enable) + { + /* TODO/FIXME - + * following D3D11 warnings are spammed in Debug mode - + * Forcing PS shader resource slot 0 to NULL. [ STATE_SETTING WARNING #7: DEVICE_PSSETSHADERRESOURCES_HAZARD] + * Resource being set to OM RenderTarget slot 0 is still bound on input! [ STATE_SETTING WARNING #9: DEVICE_OMSETRENDERTARGETS_HAZARD] + */ + D3D11SetRenderTargets(context, 1, &d3d11->back_buffer.rt_view, NULL); + D3D11ClearRenderTargetView(context, d3d11->back_buffer.rt_view, d3d11->clearcolor); + } + else +#endif + { + D3D11SetRenderTargets(context, 1, &rtv, NULL); + D3D11ClearRenderTargetView(context, rtv, d3d11->clearcolor); + } + D3D11SetViewports(context, 1, &d3d11->frame.viewport); if (texture) @@ -1702,6 +2000,41 @@ static bool d3d11_gfx_frame( #if defined(_WIN32) && !defined(__WINRT__) win32_update_title(); #endif + +#ifdef HAVE_DXGI_HDR + /* Copy over back buffer to swap chain render targets */ + if(d3d11->hdr.enable) + { + D3D11SetRenderTargets(context, 1, &rtv, NULL); + D3D11ClearRenderTargetView(context, rtv, + d3d11->clearcolor); + D3D11SetViewports(context, 1, + &d3d11->viewport); + D3D11SetScissorRects(context, 1, + &d3d11->scissor); + + d3d11_set_shader(context, + &d3d11->shaders[VIDEO_SHADER_STOCK_HDR]); + D3D11SetVShaderConstantBuffer(context, 0, + d3d11->hdr.ubo); + D3D11SetPShaderResources(context, 0, 1, + &d3d11->back_buffer.view); + D3D11SetPShaderSamplers(context, 0, 1, + &d3d11->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); + D3D11SetPShaderConstantBuffer(context, 0, d3d11->hdr.ubo); + D3D11SetVertexBuffer(context, 0, d3d11->frame.vbo, + sizeof(d3d11_vertex_t), 0); + + D3D11SetRasterizerState(context, d3d11->scissor_disabled); + D3D11SetBlendState(context, d3d11->blend_disable, NULL, + D3D11_DEFAULT_SAMPLE_MASK); + D3D11SetPrimitiveTopology(context, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + D3D11Draw(context, 4, 0); + } +#endif + DXGIPresent(d3d11->swapChain, !!vsync, present_flags); Release(rtv); @@ -1957,6 +2290,17 @@ static const video_poke_interface_t d3d11_poke_interface = { d3d11_gfx_get_current_shader, NULL, /* get_current_software_framebuffer */ d3d11_get_hw_render_interface, +#ifdef HAVE_DXGI_HDR + d3d11_set_hdr_max_nits, + d3d11_set_hdr_paper_white_nits, + d3d11_set_hdr_contrast, + d3d11_set_hdr_expand_gamut +#else + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ +#endif }; static void d3d11_gfx_get_poke_interface(void* data, diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 882cba9a38..b62898c23c 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -238,6 +238,78 @@ static void d3d12_get_overlay_interface(void* data, const video_overlay_interfac } #endif +#if 0 + d3d12->hdr.max_output_nits = settings->floats.video_hdr_max_nits; + d3d12->hdr.ubo_values.maxNits = settings->floats.video_hdr_max_nits; + d3d12->hdr.ubo_values.paperWhiteNits = settings->floats.video_hdr_paper_white_nits; + d3d12->hdr.ubo_values.contrast = settings->floats.video_hdr_contrast; + d3d12->hdr.ubo_values.expandGamut = settings->bools.video_hdr_expand_gamut; +#endif + +#ifdef HAVE_DXGI_HDR +static void d3d12_set_hdr_max_nits(void* data, float max_nits) +{ + dxgi_hdr_uniform_t *mapped_ubo = NULL; + D3D12_RANGE read_range = { 0, 0 }; + d3d12_video_t *d3d12 = (d3d12_video_t*)data; + + d3d12->hdr.max_output_nits = max_nits; + d3d12->hdr.ubo_values.maxNits = max_nits; + + D3D12Map(d3d12->hdr.ubo, 0, &read_range, (void**)&mapped_ubo); + *mapped_ubo = d3d12->hdr.ubo_values; + D3D12Unmap(d3d12->hdr.ubo, 0, NULL); + + dxgi_set_hdr_metadata( + d3d12->chain.handle, + d3d12->hdr.support, + d3d12->chain.bit_depth, + d3d12->chain.color_space, + d3d12->hdr.max_output_nits, + d3d12->hdr.min_output_nits, + d3d12->hdr.max_cll, + d3d12->hdr.max_fall); +} + +static void d3d12_set_hdr_paper_white_nits(void* data, float paper_white_nits) +{ + D3D12_RANGE read_range = { 0, 0 }; + dxgi_hdr_uniform_t *mapped_ubo = NULL; + d3d12_video_t *d3d12 = (d3d12_video_t*)data; + + d3d12->hdr.ubo_values.paperWhiteNits = paper_white_nits; + + D3D12Map(d3d12->hdr.ubo, 0, &read_range, (void**)&mapped_ubo); + *mapped_ubo = d3d12->hdr.ubo_values; + D3D12Unmap(d3d12->hdr.ubo, 0, NULL); +} + +static void d3d12_set_hdr_contrast(void* data, float contrast) +{ + D3D12_RANGE read_range = { 0, 0 }; + d3d12_video_t *d3d12 = (d3d12_video_t*)data; + dxgi_hdr_uniform_t *mapped_ubo = NULL; + + d3d12->hdr.ubo_values.contrast = contrast; + + D3D12Map(d3d12->hdr.ubo, 0, &read_range, (void**)&mapped_ubo); + *mapped_ubo = d3d12->hdr.ubo_values; + D3D12Unmap(d3d12->hdr.ubo, 0, NULL); +} + +static void d3d12_set_hdr_expand_gamut(void* data, bool expand_gamut) +{ + D3D12_RANGE read_range = { 0, 0 }; + dxgi_hdr_uniform_t *mapped_ubo = NULL; + d3d12_video_t *d3d12 = (d3d12_video_t*)data; + + d3d12->hdr.ubo_values.expandGamut = expand_gamut; + D3D12Map(d3d12->hdr.ubo, 0, &read_range, (void**)&mapped_ubo); + *mapped_ubo = d3d12->hdr.ubo_values; + D3D12Unmap(d3d12->hdr.ubo, 0, NULL); +} +#endif + static void d3d12_set_filtering(void* data, unsigned index, bool smooth, bool ctx_scaling) { int i; @@ -494,9 +566,18 @@ static bool d3d12_gfx_set_shader(void* data, enum rarch_shader_type type, const if (!d3d12->pass[i].pipe) goto error; +#ifdef HAVE_DXGI_HDR d3d12->pass[i].rt.rt_view.ptr = - d3d12->desc.rtv_heap.cpu.ptr + - (countof(d3d12->chain.renderTargets) + (2 * i)) * d3d12->desc.rtv_heap.stride; + d3d12->desc.rtv_heap.cpu.ptr + + (countof(d3d12->chain.renderTargets) + 1 + (2 * i)) + * d3d12->desc.rtv_heap.stride; +#else + d3d12->pass[i].rt.rt_view.ptr = + d3d12->desc.rtv_heap.cpu.ptr + + (countof(d3d12->chain.renderTargets) + (2 * i)) + * d3d12->desc.rtv_heap.stride; +#endif + d3d12->pass[i].feedback.rt_view.ptr = d3d12->pass[i].rt.rt_view.ptr + d3d12->desc.rtv_heap.stride; d3d12->pass[i].textures.ptr = @@ -562,12 +643,51 @@ static bool d3d12_gfx_init_pipelines(d3d12_video_t* d3d12) settings_t * settings = config_get_ptr(); D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = { d3d12->desc.rootSignature }; + desc.BlendState.RenderTarget[0] = d3d12_blend_disable_desc; +#ifdef HAVE_DXGI_HDR + desc.RTVFormats[0] = DXGI_FORMAT_R10G10B10A2_UNORM; + + { + static const char shader[] = +#include "d3d_shaders/hdr_sm5.hlsl.h" + ; + + static const D3D12_INPUT_ELEMENT_DESC inputElementDesc[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d12_vertex_t, position), + D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(d3d12_vertex_t, texcoord), + D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(d3d12_vertex_t, color), + D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; + + if (!d3d_compile(shader, sizeof(shader), NULL, "VSMain", "vs_5_0", &vs_code)) + goto error; + if (!d3d_compile(shader, sizeof(shader), NULL, "PSMain", "ps_5_0", &ps_code)) + goto error; + + desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + desc.InputLayout.pInputElementDescs = inputElementDesc; + desc.InputLayout.NumElements = countof(inputElementDesc); + + if (!d3d12_init_pipeline( + d3d12->device, vs_code, ps_code, NULL, &desc, + &d3d12->pipes[VIDEO_SHADER_STOCK_HDR])) + goto error; + + Release(vs_code); + Release(ps_code); + vs_code = NULL; + ps_code = NULL; + } +#endif + desc.BlendState.RenderTarget[0] = d3d12_blend_enable_desc; desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; { static const char shader[] = -#include "../drivers/d3d_shaders/opaque_sm5.hlsl.h" +#include "d3d_shaders/opaque_sm5.hlsl.h" ; static const D3D12_INPUT_ELEMENT_DESC inputElementDesc[] = { @@ -839,6 +959,10 @@ static void d3d12_gfx_free(void* data) Release(d3d12->sprites.vbo); Release(d3d12->menu_pipeline_vbo); +#ifdef HAVE_DXGI_HDR + Release(d3d12->hdr.ubo); +#endif + Release(d3d12->frame.ubo); Release(d3d12->frame.vbo); Release(d3d12->frame.texture[0].handle); @@ -847,6 +971,10 @@ static void d3d12_gfx_free(void* data) Release(d3d12->menu.texture.handle); Release(d3d12->menu.texture.upload_buffer); +#ifdef HAVE_DXGI_HDR + d3d12_release_texture(&d3d12->chain.back_buffer); + d3d12->chain.back_buffer.handle = NULL; +#endif free(d3d12->desc.sampler_heap.map); free(d3d12->desc.srv_heap.map); free(d3d12->desc.rtv_heap.map); @@ -890,6 +1018,10 @@ static void d3d12_gfx_free(void* data) } } +#ifdef HAVE_DXGI_HDR + video_driver_unset_hdr_support(); +#endif + #ifdef HAVE_MONITOR win32_monitor_from_window(); #endif @@ -951,6 +1083,14 @@ static void *d3d12_gfx_init(const video_info_t* video, goto error; } +#ifdef HAVE_DXGI_HDR + d3d12->hdr.enable = settings->bools.video_hdr_enable; + d3d12->hdr.max_output_nits = settings->floats.video_hdr_max_nits; + d3d12->hdr.min_output_nits = 0.001f; + d3d12->hdr.max_cll = 0.0f; + d3d12->hdr.max_fall = 0.0f; +#endif + d3d_input_driver(settings->arrays.input_driver, settings->arrays.input_joypad_driver, input, input_data); if (!d3d12_init_base(d3d12)) @@ -1007,6 +1147,26 @@ static void *d3d12_gfx_init(const video_info_t* video, D3D12Unmap(d3d12->ubo, 0, NULL); } +#ifdef HAVE_DXGI_HDR + d3d12->hdr.ubo_view.SizeInBytes = sizeof(dxgi_hdr_uniform_t); + d3d12->hdr.ubo_view.BufferLocation = + d3d12_create_buffer(d3d12->device, d3d12->hdr.ubo_view.SizeInBytes, &d3d12->hdr.ubo); + + d3d12->hdr.ubo_values.mvp = d3d12->mvp_no_rot; + d3d12->hdr.ubo_values.maxNits = settings->floats.video_hdr_max_nits; + d3d12->hdr.ubo_values.paperWhiteNits = settings->floats.video_hdr_paper_white_nits; + d3d12->hdr.ubo_values.contrast = settings->floats.video_hdr_contrast; + d3d12->hdr.ubo_values.expandGamut = settings->bools.video_hdr_expand_gamut; + + { + dxgi_hdr_uniform_t* mapped_ubo; + D3D12_RANGE read_range = { 0, 0 }; + D3D12Map(d3d12->hdr.ubo, 0, &read_range, (void**)&mapped_ubo); + *mapped_ubo = d3d12->hdr.ubo_values; + D3D12Unmap(d3d12->hdr.ubo, 0, NULL); + } +#endif + d3d12_gfx_set_rotation(d3d12, 0); video_driver_set_size(d3d12->vp.full_width, d3d12->vp.full_height); d3d12->chain.viewport.Width = d3d12->vp.full_width; @@ -1197,53 +1357,132 @@ static bool d3d12_gfx_frame( #ifdef HAVE_GFX_WIDGETS bool widgets_active = video_info->widgets_active; #endif - +#ifdef HAVE_DXGI_HDR + bool video_hdr_enable = video_info->hdr_enable; + if (d3d12->resize_chain || (d3d12->hdr.enable != video_hdr_enable)) +#else if (d3d12->resize_chain) +#endif { - unsigned i; +#ifdef HAVE_DXGI_HDR + d3d12->hdr.enable = video_hdr_enable; +#endif for (i = 0; i < countof(d3d12->chain.renderTargets); i++) Release(d3d12->chain.renderTargets[i]); - DXGIResizeBuffers(d3d12->chain.handle, 0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING); +#ifdef HAVE_DXGI_HDR + if (d3d12->hdr.enable) + { + d3d12_release_texture(&d3d12->chain.back_buffer); + d3d12->chain.back_buffer.handle = NULL; + } + DXGIResizeBuffers(d3d12->chain.handle, + countof(d3d12->chain.renderTargets), + video_width, + video_height, + d3d12->chain.formats[d3d12->chain.bit_depth], + DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING); +#else + DXGIResizeBuffers(d3d12->chain.handle, + 0, + 0, + 0, + DXGI_FORMAT_UNKNOWN, + DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING); +#endif for (i = 0; i < countof(d3d12->chain.renderTargets); i++) { - DXGIGetSwapChainBuffer(d3d12->chain.handle, i, &d3d12->chain.renderTargets[i]); + DXGIGetSwapChainBuffer(d3d12->chain.handle, i, + &d3d12->chain.renderTargets[i]); D3D12CreateRenderTargetView( - d3d12->device, d3d12->chain.renderTargets[i], NULL, d3d12->chain.desc_handles[i]); + d3d12->device, d3d12->chain.renderTargets[i], + NULL, d3d12->chain.desc_handles[i]); } - d3d12->chain.viewport.Width = video_width; - d3d12->chain.viewport.Height = video_height; - d3d12->chain.scissorRect.right = video_width; - d3d12->chain.scissorRect.bottom = video_height; - d3d12->resize_chain = false; - d3d12->resize_viewport = true; + d3d12->chain.viewport.Width = video_width; + d3d12->chain.viewport.Height = video_height; + d3d12->chain.scissorRect.right = video_width; + d3d12->chain.scissorRect.bottom = video_height; + d3d12->resize_chain = false; + d3d12->resize_viewport = true; d3d12->ubo_values.OutputSize.width = d3d12->chain.viewport.Width; d3d12->ubo_values.OutputSize.height = d3d12->chain.viewport.Height; video_driver_set_size(video_width, video_height); + +#ifdef HAVE_DXGI_HDR +#ifdef __WINRT__ + if (!(d3d12->hdr.support = + dxgi_check_display_hdr_support(d3d12->factory, uwp_get_corewindow()))) + d3d12->hdr.enable = false; +#else + if (!(d3d12->hdr.support = + dxgi_check_display_hdr_support(d3d12->factory, main_window.hwnd))) + d3d12->hdr.enable = false; +#endif + + if(d3d12->hdr.enable) + { + memset(&d3d12->chain.back_buffer, + 0, sizeof(d3d12->chain.back_buffer)); + d3d12->chain.back_buffer.desc.Width = video_width; + d3d12->chain.back_buffer.desc.Height = video_height; + d3d12->chain.back_buffer.desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + d3d12->chain.back_buffer.desc.Flags = + D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + d3d12->chain.back_buffer.srv_heap = &d3d12->desc.srv_heap; + d3d12->chain.back_buffer.rt_view.ptr = + d3d12->desc.rtv_heap.cpu.ptr + + countof(d3d12->chain.renderTargets) + * d3d12->desc.rtv_heap.stride; + d3d12_init_texture(d3d12->device, &d3d12->chain.back_buffer); + + dxgi_swapchain_color_space(d3d12->chain.handle, + &d3d12->chain.color_space, + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020); + } + else + dxgi_swapchain_color_space(d3d12->chain.handle, + &d3d12->chain.color_space, + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709); + + dxgi_set_hdr_metadata( + d3d12->chain.handle, + d3d12->hdr.support, + d3d12->chain.bit_depth, + d3d12->chain.color_space, + d3d12->hdr.max_output_nits, + d3d12->hdr.min_output_nits, + d3d12->hdr.max_cll, + d3d12->hdr.max_fall); +#endif } D3D12ResetCommandAllocator(d3d12->queue.allocator); D3D12ResetGraphicsCommandList( - d3d12->queue.cmd, d3d12->queue.allocator, d3d12->pipes[VIDEO_SHADER_STOCK_BLEND]); + d3d12->queue.cmd, d3d12->queue.allocator, + d3d12->pipes[VIDEO_SHADER_STOCK_BLEND]); { D3D12DescriptorHeap desc_heaps[] = { d3d12->desc.srv_heap.handle, d3d12->desc.sampler_heap.handle }; - D3D12SetDescriptorHeaps(d3d12->queue.cmd, countof(desc_heaps), desc_heaps); + D3D12SetDescriptorHeaps(d3d12->queue.cmd, + countof(desc_heaps), desc_heaps); } -#if 0 /* custom viewport doesn't call apply_state_changes, so we can't rely on this for now */ +#if 0 + /* Custom viewport doesn't call apply_state_changes, + so we can't rely on this for now */ if (d3d12->resize_viewport) #endif d3d12_update_viewport(d3d12, false); - D3D12IASetPrimitiveTopology(d3d12->queue.cmd, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + D3D12IASetPrimitiveTopology(d3d12->queue.cmd, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); if (frame && width && height) { @@ -1260,7 +1499,8 @@ static bool d3d12_gfx_frame( if (d3d12->resize_render_targets) { - /* release all render targets first to avoid memory fragmentation */ + /* Release all render targets first + to avoid memory fragmentation */ for (i = 0; i < d3d12->shader_preset->passes; i++) { d3d12_release_texture(&d3d12->pass[i].rt); @@ -1277,9 +1517,10 @@ static bool d3d12_gfx_frame( else { int k; - /* todo: what about frame-duping ? + /* TODO/FIXME: what about frame-duping ? * maybe clone d3d12_texture_t with AddRef */ - d3d12_texture_t tmp = d3d12->frame.texture[d3d12->shader_preset->history_size]; + d3d12_texture_t tmp = + d3d12->frame.texture[d3d12->shader_preset->history_size]; for (k = d3d12->shader_preset->history_size; k > 0; k--) d3d12->frame.texture[k] = d3d12->frame.texture[k - 1]; d3d12->frame.texture[0] = tmp; @@ -1287,8 +1528,9 @@ static bool d3d12_gfx_frame( } } - /* either no history, or we moved a texture of a different size in the front slot */ - if (d3d12->frame.texture[0].desc.Width != width || + /* Either no history, or we moved a texture + of a different size in the front slot */ + if (d3d12->frame.texture[0].desc.Width != width || d3d12->frame.texture[0].desc.Height != height) { d3d12->frame.texture[0].desc.Width = width; @@ -1300,7 +1542,8 @@ static bool d3d12_gfx_frame( if (d3d12->resize_render_targets) d3d12_init_render_targets(d3d12, width, height); - d3d12_update_texture(width, height, pitch, d3d12->format, frame, &d3d12->frame.texture[0]); + d3d12_update_texture(width, height, pitch, d3d12->format, + frame, &d3d12->frame.texture[0]); d3d12_upload_texture(d3d12->queue.cmd, &d3d12->frame.texture[0], d3d12); @@ -1311,7 +1554,8 @@ static bool d3d12_gfx_frame( if (d3d12->shader_preset) { - D3D12SetGraphicsRootSignature(d3d12->queue.cmd, d3d12->desc.sl_rootSignature); + D3D12SetGraphicsRootSignature(d3d12->queue.cmd, + d3d12->desc.sl_rootSignature); for (i = 0; i < d3d12->shader_preset->passes; i++) { @@ -1330,16 +1574,17 @@ static bool d3d12_gfx_frame( D3D12SetPipelineState(d3d12->queue.cmd, d3d12->pass[i].pipe); if (d3d12->shader_preset->pass[i].frame_count_mod) - d3d12->pass[i].frame_count = - frame_count % d3d12->shader_preset->pass[i].frame_count_mod; + d3d12->pass[i].frame_count = frame_count + % d3d12->shader_preset->pass[i].frame_count_mod; else d3d12->pass[i].frame_count = frame_count; #ifdef HAVE_REWIND - d3d12->pass[i].frame_direction = state_manager_frame_is_reversed() ? -1 : 1; -#else - d3d12->pass[i].frame_direction = 1; + if (state_manager_frame_is_reversed()) + d3d12->pass[i].frame_direction = -1; + else #endif + d3d12->pass[i].frame_direction = 1; for (j = 0; j < SLANG_CBUFFER_MAX; j++) { @@ -1351,17 +1596,21 @@ static bool d3d12_gfx_frame( uint8_t* mapped_data = NULL; uniform_sem_t* uniform = buffer_sem->uniforms; - D3D12Map(d3d12->pass[i].buffers[j], 0, &range, (void**)&mapped_data); + D3D12Map(d3d12->pass[i].buffers[j], 0, &range, + (void**)&mapped_data); while (uniform->size) { if (uniform->data) - memcpy(mapped_data + uniform->offset, uniform->data, uniform->size); + memcpy(mapped_data + uniform->offset, + uniform->data, uniform->size); uniform++; } D3D12Unmap(d3d12->pass[i].buffers[j], 0, NULL); D3D12SetGraphicsRootConstantBufferView( - d3d12->queue.cmd, j == SLANG_CBUFFER_UBO ? ROOT_ID_UBO : ROOT_ID_PC, + d3d12->queue.cmd, j == SLANG_CBUFFER_UBO + ? ROOT_ID_UBO + : ROOT_ID_PC, d3d12->pass[i].buffer_view[j].BufferLocation); } } @@ -1375,15 +1624,19 @@ static bool d3d12_gfx_frame( { { D3D12_CPU_DESCRIPTOR_HANDLE handle = { - d3d12->pass[i].textures.ptr - d3d12->desc.srv_heap.gpu.ptr + - d3d12->desc.srv_heap.cpu.ptr + - texture_sem->binding * d3d12->desc.srv_heap.stride + d3d12->pass[i].textures.ptr + - d3d12->desc.srv_heap.gpu.ptr + + d3d12->desc.srv_heap.cpu.ptr + + texture_sem->binding * d3d12->desc.srv_heap.stride }; - d3d12_texture_t* tex = (d3d12_texture_t*)texture_sem->texture_data; + d3d12_texture_t* tex = + (d3d12_texture_t*)texture_sem->texture_data; D3D12_SHADER_RESOURCE_VIEW_DESC desc = { tex->desc.Format }; - desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + desc.Shader4ComponentMapping = + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + desc.ViewDimension = + D3D12_SRV_DIMENSION_TEXTURE2D; desc.Texture2D.MipLevels = tex->desc.MipLevels; D3D12CreateShaderResourceView(d3d12->device, @@ -1392,9 +1645,11 @@ static bool d3d12_gfx_frame( { D3D12_CPU_DESCRIPTOR_HANDLE handle = { - d3d12->pass[i].samplers.ptr - d3d12->desc.sampler_heap.gpu.ptr + - d3d12->desc.sampler_heap.cpu.ptr + - texture_sem->binding * d3d12->desc.sampler_heap.stride + d3d12->pass[i].samplers.ptr + - d3d12->desc.sampler_heap.gpu.ptr + + d3d12->desc.sampler_heap.cpu.ptr + + texture_sem->binding + * d3d12->desc.sampler_heap.stride }; D3D12_SAMPLER_DESC desc = { D3D12_FILTER_MIN_MAG_MIP_LINEAR }; @@ -1435,29 +1690,37 @@ static bool d3d12_gfx_frame( } D3D12SetGraphicsRootDescriptorTable( - d3d12->queue.cmd, ROOT_ID_TEXTURE_T, d3d12->pass[i].textures); + d3d12->queue.cmd, ROOT_ID_TEXTURE_T, + d3d12->pass[i].textures); D3D12SetGraphicsRootDescriptorTable( - d3d12->queue.cmd, ROOT_ID_SAMPLER_T, d3d12->pass[i].samplers); + d3d12->queue.cmd, ROOT_ID_SAMPLER_T, + d3d12->pass[i].samplers); } if (d3d12->pass[i].rt.handle) { d3d12_resource_transition( d3d12->queue.cmd, d3d12->pass[i].rt.handle, - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET); + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_STATE_RENDER_TARGET); - D3D12OMSetRenderTargets(d3d12->queue.cmd, 1, &d3d12->pass[i].rt.rt_view, FALSE, NULL); + D3D12OMSetRenderTargets(d3d12->queue.cmd, 1, + &d3d12->pass[i].rt.rt_view, FALSE, NULL); #if 0 D3D12ClearRenderTargetView( - d3d12->queue.cmd, d3d12->pass[i].rt.rt_view, d3d12->chain.clearcolor, 0, NULL); + d3d12->queue.cmd, d3d12->pass[i].rt.rt_view, + d3d12->chain.clearcolor, 0, NULL); #endif - D3D12RSSetViewports(d3d12->queue.cmd, 1, &d3d12->pass[i].viewport); - D3D12RSSetScissorRects(d3d12->queue.cmd, 1, &d3d12->pass[i].scissorRect); + D3D12RSSetViewports(d3d12->queue.cmd, 1, + &d3d12->pass[i].viewport); + D3D12RSSetScissorRects(d3d12->queue.cmd, 1, + &d3d12->pass[i].scissorRect); D3D12DrawInstanced(d3d12->queue.cmd, 4, 1, 0, 0); d3d12_resource_transition( - d3d12->queue.cmd, d3d12->pass[i].rt.handle, D3D12_RESOURCE_STATE_RENDER_TARGET, + d3d12->queue.cmd, d3d12->pass[i].rt.handle, + D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); texture = &d3d12->pass[i].rt; } @@ -1471,31 +1734,68 @@ static bool d3d12_gfx_frame( if (texture) { - D3D12SetPipelineState(d3d12->queue.cmd, d3d12->pipes[VIDEO_SHADER_STOCK_BLEND]); - D3D12SetGraphicsRootSignature(d3d12->queue.cmd, d3d12->desc.rootSignature); + D3D12SetPipelineState(d3d12->queue.cmd, + d3d12->pipes[VIDEO_SHADER_STOCK_BLEND]); + D3D12SetGraphicsRootSignature(d3d12->queue.cmd, + d3d12->desc.rootSignature); d3d12_set_texture(d3d12->queue.cmd, &d3d12->frame.texture[0]); - d3d12_set_sampler(d3d12->queue.cmd, d3d12->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); + d3d12_set_sampler(d3d12->queue.cmd, + d3d12->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); D3D12SetGraphicsRootConstantBufferView( - d3d12->queue.cmd, ROOT_ID_UBO, d3d12->frame.ubo_view.BufferLocation); + d3d12->queue.cmd, ROOT_ID_UBO, + d3d12->frame.ubo_view.BufferLocation); } - d3d12->chain.frame_index = DXGIGetCurrentBackBufferIndex(d3d12->chain.handle); - d3d12_resource_transition( - d3d12->queue.cmd, d3d12->chain.renderTargets[d3d12->chain.frame_index], - D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET); + d3d12->chain.frame_index = DXGIGetCurrentBackBufferIndex( + d3d12->chain.handle); - D3D12OMSetRenderTargets( - d3d12->queue.cmd, 1, &d3d12->chain.desc_handles[d3d12->chain.frame_index], FALSE, NULL); - D3D12ClearRenderTargetView( - d3d12->queue.cmd, d3d12->chain.desc_handles[d3d12->chain.frame_index], - d3d12->chain.clearcolor, 0, NULL); +#ifdef HAVE_DXGI_HDR + if(d3d12->hdr.enable) + { + d3d12_resource_transition( + d3d12->queue.cmd, d3d12->chain.back_buffer.handle, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_STATE_RENDER_TARGET); + + D3D12OMSetRenderTargets( + d3d12->queue.cmd, 1, + &d3d12->chain.back_buffer.rt_view, FALSE, NULL); + /* TODO/FIXME - fix this warning that shows up with Debug logging + * EXECUTIONWARNING #820: CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE + * We need to set clear value during resource creation to NULL for + * D3D12_RESOURCE_DIMENSION_BUFFER, yet we get spammed with this + * warning + */ + D3D12ClearRenderTargetView( + d3d12->queue.cmd, d3d12->chain.back_buffer.rt_view, + d3d12->chain.clearcolor, 0, NULL); + } + else +#endif + { + d3d12_resource_transition( + d3d12->queue.cmd, + d3d12->chain.renderTargets[d3d12->chain.frame_index], + D3D12_RESOURCE_STATE_PRESENT, + D3D12_RESOURCE_STATE_RENDER_TARGET); + + D3D12OMSetRenderTargets( + d3d12->queue.cmd, 1, + &d3d12->chain.desc_handles[d3d12->chain.frame_index], + FALSE, NULL); + D3D12ClearRenderTargetView( + d3d12->queue.cmd, + d3d12->chain.desc_handles[d3d12->chain.frame_index], + d3d12->chain.clearcolor, 0, NULL); + } D3D12RSSetViewports(d3d12->queue.cmd, 1, &d3d12->frame.viewport); D3D12RSSetScissorRects(d3d12->queue.cmd, 1, &d3d12->frame.scissorRect); D3D12DrawInstanced(d3d12->queue.cmd, 4, 1, 0, 0); - D3D12SetPipelineState(d3d12->queue.cmd, d3d12->pipes[VIDEO_SHADER_STOCK_BLEND]); + D3D12SetPipelineState(d3d12->queue.cmd, + d3d12->pipes[VIDEO_SHADER_STOCK_BLEND]); D3D12SetGraphicsRootSignature(d3d12->queue.cmd, d3d12->desc.rootSignature); if (d3d12->menu.enabled && d3d12->menu.texture.handle) @@ -1520,7 +1820,8 @@ static bool d3d12_gfx_frame( d3d12->sprites.pipe = d3d12->sprites.pipe_noblend; D3D12SetPipelineState(d3d12->queue.cmd, d3d12->sprites.pipe); - D3D12IASetPrimitiveTopology(d3d12->queue.cmd, D3D_PRIMITIVE_TOPOLOGY_POINTLIST); + D3D12IASetPrimitiveTopology(d3d12->queue.cmd, + D3D_PRIMITIVE_TOPOLOGY_POINTLIST); d3d12->sprites.enabled = true; @@ -1529,9 +1830,12 @@ static bool d3d12_gfx_frame( if (d3d12->menu.enabled) #endif { - D3D12RSSetViewports(d3d12->queue.cmd, 1, &d3d12->chain.viewport); - D3D12RSSetScissorRects(d3d12->queue.cmd, 1, &d3d12->chain.scissorRect); - D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, &d3d12->sprites.vbo_view); + D3D12RSSetViewports(d3d12->queue.cmd, 1, + &d3d12->chain.viewport); + D3D12RSSetScissorRects(d3d12->queue.cmd, 1, + &d3d12->chain.scissorRect); + D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, + &d3d12->sprites.vbo_view); } #endif @@ -1544,10 +1848,14 @@ static bool d3d12_gfx_frame( { if (osd_params) { - D3D12SetPipelineState(d3d12->queue.cmd, d3d12->sprites.pipe_blend); - D3D12RSSetViewports(d3d12->queue.cmd, 1, &d3d12->chain.viewport); - D3D12RSSetScissorRects(d3d12->queue.cmd, 1, &d3d12->chain.scissorRect); - D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, &d3d12->sprites.vbo_view); + D3D12SetPipelineState(d3d12->queue.cmd, + d3d12->sprites.pipe_blend); + D3D12RSSetViewports(d3d12->queue.cmd, 1, + &d3d12->chain.viewport); + D3D12RSSetScissorRects(d3d12->queue.cmd, 1, + &d3d12->chain.scissorRect); + D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, + &d3d12->sprites.vbo_view); font_driver_render_msg(d3d12, stat_text, (const struct font_params*)osd_params, NULL); } @@ -1557,17 +1865,21 @@ static bool d3d12_gfx_frame( { if (d3d12->overlays.fullscreen) { - D3D12RSSetViewports(d3d12->queue.cmd, 1, &d3d12->chain.viewport); - D3D12RSSetScissorRects(d3d12->queue.cmd, 1, &d3d12->chain.scissorRect); + D3D12RSSetViewports(d3d12->queue.cmd, 1, + &d3d12->chain.viewport); + D3D12RSSetScissorRects(d3d12->queue.cmd, 1, + &d3d12->chain.scissorRect); } else { - D3D12RSSetViewports(d3d12->queue.cmd, 1, &d3d12->frame.viewport); - D3D12RSSetScissorRects(d3d12->queue.cmd, 1, &d3d12->frame.scissorRect); + D3D12RSSetViewports(d3d12->queue.cmd, 1, + &d3d12->frame.viewport); + D3D12RSSetScissorRects(d3d12->queue.cmd, 1, + &d3d12->frame.scissorRect); } - D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, &d3d12->overlays.vbo_view); - + D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, + &d3d12->overlays.vbo_view); D3D12SetPipelineState(d3d12->queue.cmd, d3d12->sprites.pipe_blend); D3D12SetGraphicsRootDescriptorTable( @@ -1582,7 +1894,8 @@ static bool d3d12_gfx_frame( d3d12); D3D12SetGraphicsRootDescriptorTable( - d3d12->queue.cmd, ROOT_ID_TEXTURE_T, d3d12->overlays.textures[i].gpu_descriptor[0]); + d3d12->queue.cmd, ROOT_ID_TEXTURE_T, + d3d12->overlays.textures[i].gpu_descriptor[0]); D3D12DrawInstanced(d3d12->queue.cmd, 1, 1, i, 0); } } @@ -1595,21 +1908,78 @@ static bool d3d12_gfx_frame( if (msg && *msg) { - D3D12SetPipelineState(d3d12->queue.cmd, d3d12->sprites.pipe_blend); - D3D12RSSetViewports(d3d12->queue.cmd, 1, &d3d12->chain.viewport); - D3D12RSSetScissorRects(d3d12->queue.cmd, 1, &d3d12->chain.scissorRect); - D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, &d3d12->sprites.vbo_view); + D3D12SetPipelineState(d3d12->queue.cmd, + d3d12->sprites.pipe_blend); + D3D12RSSetViewports(d3d12->queue.cmd, 1, + &d3d12->chain.viewport); + D3D12RSSetScissorRects(d3d12->queue.cmd, 1, + &d3d12->chain.scissorRect); + D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, + &d3d12->sprites.vbo_view); font_driver_render_msg(d3d12, msg, NULL, NULL); } d3d12->sprites.enabled = false; +#ifdef HAVE_DXGI_HDR + /* Copy over back buffer to swap chain render targets */ + if (d3d12->hdr.enable) + { + d3d12_resource_transition( + d3d12->queue.cmd, + d3d12->chain.renderTargets[d3d12->chain.frame_index], + D3D12_RESOURCE_STATE_PRESENT, + D3D12_RESOURCE_STATE_RENDER_TARGET); + + d3d12_resource_transition( + d3d12->queue.cmd, d3d12->chain.back_buffer.handle, + D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + D3D12SetPipelineState(d3d12->queue.cmd, + d3d12->pipes[VIDEO_SHADER_STOCK_HDR]); + + D3D12OMSetRenderTargets( + d3d12->queue.cmd, 1, + &d3d12->chain.desc_handles[d3d12->chain.frame_index], + FALSE, NULL); + D3D12ClearRenderTargetView( + d3d12->queue.cmd, + d3d12->chain.desc_handles[d3d12->chain.frame_index], + d3d12->chain.clearcolor, 0, NULL); + + D3D12SetGraphicsRootSignature(d3d12->queue.cmd, + d3d12->desc.rootSignature); + d3d12_set_texture(d3d12->queue.cmd, &d3d12->chain.back_buffer); + d3d12_set_sampler(d3d12->queue.cmd, + d3d12->samplers[RARCH_FILTER_UNSPEC][RARCH_WRAP_DEFAULT]); + D3D12SetGraphicsRootConstantBufferView( + d3d12->queue.cmd, ROOT_ID_UBO, + d3d12->hdr.ubo_view.BufferLocation); + D3D12IASetVertexBuffers(d3d12->queue.cmd, 0, 1, + &d3d12->frame.vbo_view); + + D3D12IASetPrimitiveTopology(d3d12->queue.cmd, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + D3D12RSSetViewports(d3d12->queue.cmd, 1, + &d3d12->chain.viewport); + D3D12RSSetScissorRects(d3d12->queue.cmd, 1, + &d3d12->chain.scissorRect); + + D3D12DrawInstanced(d3d12->queue.cmd, 4, 1, 0, 0); + } +#endif + d3d12_resource_transition( - d3d12->queue.cmd, d3d12->chain.renderTargets[d3d12->chain.frame_index], - D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT); + d3d12->queue.cmd, + d3d12->chain.renderTargets[d3d12->chain.frame_index], + D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PRESENT); + D3D12CloseGraphicsCommandList(d3d12->queue.cmd); - D3D12ExecuteGraphicsCommandLists(d3d12->queue.handle, 1, &d3d12->queue.cmd); + D3D12ExecuteGraphicsCommandLists(d3d12->queue.handle, 1, + &d3d12->queue.cmd); #if defined(_WIN32) && !defined(__WINRT__) win32_update_title(); @@ -1890,6 +2260,17 @@ static const video_poke_interface_t d3d12_poke_interface = { d3d12_gfx_get_current_shader, NULL, /* get_current_software_framebuffer */ NULL, /* get_hw_render_interface */ +#ifdef HAVE_DXGI_HDR + d3d12_set_hdr_max_nits, + d3d12_set_hdr_paper_white_nits, + d3d12_set_hdr_contrast, + d3d12_set_hdr_expand_gamut, +#else + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ +#endif }; static void d3d12_gfx_get_poke_interface(void* data, const video_poke_interface_t** iface) diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index ba37565927..2bdabe305b 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1831,7 +1831,11 @@ static const video_poke_interface_t d3d_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void d3d8_get_poke_interface(void *data, diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 8fd4cd551a..c4f1a688be 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -1976,7 +1976,11 @@ static const video_poke_interface_t d3d9_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void d3d9_get_poke_interface(void *data, diff --git a/gfx/drivers/d3d_shaders/hdr_sm5.hlsl.h b/gfx/drivers/d3d_shaders/hdr_sm5.hlsl.h new file mode 100644 index 0000000000..26f7d8fc64 --- /dev/null +++ b/gfx/drivers/d3d_shaders/hdr_sm5.hlsl.h @@ -0,0 +1,120 @@ + +#define SRC(...) #__VA_ARGS__ +SRC( + struct UBO + { + float4x4 modelViewProj; + float contrast; /* 2.0f; */ + float paperWhiteNits; /* 200.0f; */ + float maxNits; /* 1000.0f; */ + float expandGamut; /* 1.0f; */ + }; + uniform UBO global; + + struct PSInput + { + float4 position : SV_POSITION; + float2 texcoord : TEXCOORD0; + float4 color : COLOR; + }; + PSInput VSMain(float4 position : POSITION, float2 texcoord : TEXCOORD0, float4 color : COLOR) + { + PSInput result; + result.position = mul(global.modelViewProj, position); + result.texcoord = texcoord; + result.color = color; + return result; + } + uniform sampler s0; + uniform Texture2D t0; + + static const float kMaxNitsFor2084 = 10000.0f; + static const float kEpsilon = 0.0001f; + static const float kLumaChannelRatio = 0.25f; + + static const float3x3 k709to2020 = + { + { 0.6274040f, 0.3292820f, 0.0433136f }, + { 0.0690970f, 0.9195400f, 0.0113612f }, + { 0.0163916f, 0.0880132f, 0.8955950f } + }; + + static const float3x3 kP3to2020 = + { + { 0.753845f, 0.198593f, 0.047562f }, + { 0.0457456f, 0.941777f, 0.0124772f }, + { -0.00121055f, 0.0176041f, 0.983607f } + }; + + /* START Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.) https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */ + static const float3x3 kExpanded709to2020 = + { + { 0.6274040f, 0.3292820f, 0.0433136f }, + { 0.0457456, 0.941777, 0.0124772 }, + { -0.00121055, 0.0176041, 0.983607 } + }; + + float3 LinearToST2084(float3 normalizedLinearValue) + { + float3 ST2084 = pow((0.8359375f + 18.8515625f * pow(abs(normalizedLinearValue), 0.1593017578f)) / (1.0f + 18.6875f * pow(abs(normalizedLinearValue), 0.1593017578f)), 78.84375f); + return ST2084; /* Don't clamp between [0..1], so we can still perform operations on scene values higher than 10,000 nits */ + } + /* END Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.) https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */ + + float3 SRGBToLinear(float3 color) + { + float3 scale = color / 12.92f; + float3 gamma = pow(abs(color + 0.055f) / 1.055f, 2.4f); + + return float3( color.x < 0.04045f ? scale.x : gamma.x, + color.y < 0.04045f ? scale.y : gamma.y, + color.z < 0.04045f ? scale.z : gamma.z); + } + + float4 Hdr(float4 sdr) + { + sdr.xyz = pow(abs(sdr.xyz), 2.2f / global.contrast); /* Display Gamma - needs to be determined by calibration screen but should be in the 0.8 - 1.4 range */ + + float luma = dot(sdr.xyz, float3(0.2126, 0.7152, 0.0722)); /* Rec BT.709 luma coefficients - https://en.wikipedia.org/wiki/Luma_(video) */ + + /* Inverse reinhard tonemap */ + float maxValue = (global.maxNits / global.paperWhiteNits) + kEpsilon; + float elbow = maxValue / (maxValue - 1.0f); /* Convert (1.0 + epsilon) to infinite to range 1001 -> 1.0 */ + float offset = 1.0f - ((0.5f * elbow) / (elbow - 0.5f)); /* Convert 1001 to 1.0 to range 0.5 -> 1.0 */ + + float hdrLumaInvTonemap = offset + ((luma * elbow) / (elbow - luma)); + float sdrLumaInvTonemap = luma / ((1.0f + kEpsilon) - luma); /* Convert the srd < 0.5 to 0.0 -> 1.0 range */ + + float lumaInvTonemap = (luma > 0.5f) ? hdrLumaInvTonemap : sdrLumaInvTonemap; + float3 perLuma = sdr.xyz / (luma + kEpsilon) * lumaInvTonemap; + + float3 hdrInvTonemap = offset + ((sdr.xyz * elbow) / (elbow - sdr.xyz)); + float3 sdrInvTonemap = sdr.xyz / ((1.0f + kEpsilon) - sdr.xyz); /* Convert the srd < 0.5 to 0.0 -> 1.0 range */ + + float3 perChannel = float3(sdr.x > 0.5f ? hdrInvTonemap.x : sdrInvTonemap.x, + sdr.y > 0.5f ? hdrInvTonemap.y : sdrInvTonemap.y, + sdr.z > 0.5f ? hdrInvTonemap.z : sdrInvTonemap.z); + + float3 hdr = lerp(perLuma, perChannel, kLumaChannelRatio); + + /* Now convert into HDR10 */ + float3 rec2020 = mul(k709to2020, hdr); + + if(global.expandGamut > 0.0f) + { + rec2020 = mul( kExpanded709to2020, hdr); + } + + float3 linearColour = rec2020 * (global.paperWhiteNits / kMaxNitsFor2084); + float3 hdr10 = LinearToST2084(linearColour); + + return float4(hdr10, sdr.w); + } + + float4 PSMain(PSInput input) : SV_TARGET + { + float4 sdr = input.color * t0.Sample(s0, input.texcoord); + + return Hdr(sdr); + }; +) diff --git a/gfx/drivers/dispmanx_gfx.c b/gfx/drivers/dispmanx_gfx.c index 20fbfaaec4..333fe79aa7 100644 --- a/gfx/drivers/dispmanx_gfx.c +++ b/gfx/drivers/dispmanx_gfx.c @@ -600,7 +600,11 @@ static const video_poke_interface_t dispmanx_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void dispmanx_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/drm_gfx.c b/gfx/drivers/drm_gfx.c index 0758ad348d..b0b380c698 100644 --- a/gfx/drivers/drm_gfx.c +++ b/gfx/drivers/drm_gfx.c @@ -956,7 +956,11 @@ static const video_poke_interface_t drm_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void drm_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/exynos_gfx.c b/gfx/drivers/exynos_gfx.c index bde479b25c..8dc001083d 100644 --- a/gfx/drivers/exynos_gfx.c +++ b/gfx/drivers/exynos_gfx.c @@ -1498,7 +1498,11 @@ static const video_poke_interface_t exynos_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void exynos_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/fpga_gfx.c b/gfx/drivers/fpga_gfx.c index cea518aea3..5578912913 100644 --- a/gfx/drivers/fpga_gfx.c +++ b/gfx/drivers/fpga_gfx.c @@ -386,6 +386,10 @@ static const video_poke_interface_t fpga_poke_interface = { #ifdef HAVE_MENU NULL, #endif + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void fpga_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/gdi_gfx.c b/gfx/drivers/gdi_gfx.c index f5aaca133b..2e6a8b1a02 100644 --- a/gfx/drivers/gdi_gfx.c +++ b/gfx/drivers/gdi_gfx.c @@ -724,7 +724,11 @@ static const video_poke_interface_t gdi_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void gdi_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index ba16cac452..904ff44168 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -4588,7 +4588,11 @@ static const video_poke_interface_t gl2_poke_interface = { NULL, gl2_get_current_shader, NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void gl2_get_poke_interface(void *data, diff --git a/gfx/drivers/gl1.c b/gfx/drivers/gl1.c index 2bc373e1cd..165119675b 100644 --- a/gfx/drivers/gl1.c +++ b/gfx/drivers/gl1.c @@ -1448,7 +1448,11 @@ static const video_poke_interface_t gl1_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void gl1_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/gl_core.c b/gfx/drivers/gl_core.c index 2d2e782212..7c4bc3eff8 100644 --- a/gfx/drivers/gl_core.c +++ b/gfx/drivers/gl_core.c @@ -2287,6 +2287,10 @@ static const video_poke_interface_t gl_core_poke_interface = { gl_core_get_current_shader, NULL, NULL, + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void gl_core_get_poke_interface(void *data, diff --git a/gfx/drivers/gx2_gfx.c b/gfx/drivers/gx2_gfx.c index 88c3125c47..0aee3063b4 100644 --- a/gfx/drivers/gx2_gfx.c +++ b/gfx/drivers/gx2_gfx.c @@ -1735,6 +1735,10 @@ static const video_poke_interface_t wiiu_poke_interface = { wiiu_gfx_get_current_shader, NULL, /* get_current_software_framebuffer */ NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void wiiu_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/gx_gfx.c b/gfx/drivers/gx_gfx.c index b14f1bfb03..0f7aedfc1f 100644 --- a/gfx/drivers/gx_gfx.c +++ b/gfx/drivers/gx_gfx.c @@ -1372,7 +1372,11 @@ static const video_poke_interface_t gx_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void gx_get_poke_interface(void *data, diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index 2e7f789419..2993f8302e 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -399,19 +399,31 @@ static uint32_t metal_get_flags(void *data) } static const video_poke_interface_t metal_poke_interface = { - .get_flags = metal_get_flags, - .load_texture = metal_load_texture, - .unload_texture = metal_unload_texture, - .set_video_mode = metal_set_video_mode, - .get_refresh_rate = metal_get_refresh_rate, - .set_filtering = metal_set_filtering, - .set_aspect_ratio = metal_set_aspect_ratio, - .apply_state_changes = metal_apply_state_changes, - .set_texture_frame = metal_set_texture_frame, - .set_texture_enable = metal_set_texture_enable, - .set_osd_msg = font_driver_render_msg, - .show_mouse = metal_show_mouse, - .get_current_shader = metal_get_current_shader, + metal_get_flags, + metal_load_texture, + metal_unload_texture, + metal_set_video_mode, + metal_get_refresh_rate, + metal_set_filtering, + NULL, /* get_video_output_size */ + NULL, /* get_video_output_prev */ + NULL, /* get_video_output_next */ + NULL, /* get_current_framebuffer */ + NULL, /* get_proc_address */ + metal_set_aspect_ratio, + metal_apply_state_changes, + metal_set_texture_frame, + metal_set_texture_enable, + font_driver_render_msg, + metal_show_mouse, + NULL, /* grab_mouse_toggle */ + metal_get_current_shader, + NULL, /* get_current_software_framebuffer */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void metal_get_poke_interface(void *data, diff --git a/gfx/drivers/omap_gfx.c b/gfx/drivers/omap_gfx.c index 8300cf9131..ba0a86c165 100644 --- a/gfx/drivers/omap_gfx.c +++ b/gfx/drivers/omap_gfx.c @@ -1123,7 +1123,11 @@ static const video_poke_interface_t omap_gfx_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void omap_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/ps2_gfx.c b/gfx/drivers/ps2_gfx.c index 010ccbe088..f4d32b1e50 100644 --- a/gfx/drivers/ps2_gfx.c +++ b/gfx/drivers/ps2_gfx.c @@ -465,7 +465,11 @@ static const video_poke_interface_t ps2_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - ps2_get_hw_render_interface /* get_hw_render_interface */ + ps2_get_hw_render_interface, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void ps2_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/psp1_gfx.c b/gfx/drivers/psp1_gfx.c index ffdf5e4233..753c971279 100644 --- a/gfx/drivers/psp1_gfx.c +++ b/gfx/drivers/psp1_gfx.c @@ -802,7 +802,11 @@ static const video_poke_interface_t psp_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void psp_get_poke_interface(void *data, diff --git a/gfx/drivers/rsx_gfx.c b/gfx/drivers/rsx_gfx.c index 01ea8183e7..899b76949d 100644 --- a/gfx/drivers/rsx_gfx.c +++ b/gfx/drivers/rsx_gfx.c @@ -720,7 +720,11 @@ static const video_poke_interface_t rsx_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void rsx_get_poke_interface(void* data, diff --git a/gfx/drivers/sdl_dingux_gfx.c b/gfx/drivers/sdl_dingux_gfx.c index 607e3de340..0e1988967e 100644 --- a/gfx/drivers/sdl_dingux_gfx.c +++ b/gfx/drivers/sdl_dingux_gfx.c @@ -1106,7 +1106,11 @@ static const video_poke_interface_t sdl_dingux_poke_interface = { NULL, /* sdl_grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void sdl_dingux_get_poke_interface(void *data, const video_poke_interface_t **iface) diff --git a/gfx/drivers/sdl_gfx.c b/gfx/drivers/sdl_gfx.c index 3e9f4a7342..9bec6706a6 100644 --- a/gfx/drivers/sdl_gfx.c +++ b/gfx/drivers/sdl_gfx.c @@ -547,7 +547,11 @@ static const video_poke_interface_t sdl_poke_interface = { sdl_grab_mouse_toggle, NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void sdl_get_poke_interface(void *data, const video_poke_interface_t **iface) diff --git a/gfx/drivers/sdl_rs90_gfx.c b/gfx/drivers/sdl_rs90_gfx.c index 0da790ebd0..20186c115b 100644 --- a/gfx/drivers/sdl_rs90_gfx.c +++ b/gfx/drivers/sdl_rs90_gfx.c @@ -1393,7 +1393,11 @@ static const video_poke_interface_t sdl_rs90_poke_interface = { NULL, /* sdl_grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void sdl_rs90_get_poke_interface(void *data, const video_poke_interface_t **iface) diff --git a/gfx/drivers/sixel_gfx.c b/gfx/drivers/sixel_gfx.c index 8274a4aee8..09b9f79b9b 100644 --- a/gfx/drivers/sixel_gfx.c +++ b/gfx/drivers/sixel_gfx.c @@ -551,6 +551,10 @@ static const video_poke_interface_t sixel_poke_interface = { NULL, NULL, NULL, + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void sixel_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/sunxi_gfx.c b/gfx/drivers/sunxi_gfx.c index ae2bf8b691..2be9324999 100644 --- a/gfx/drivers/sunxi_gfx.c +++ b/gfx/drivers/sunxi_gfx.c @@ -951,7 +951,11 @@ static const video_poke_interface_t sunxi_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void sunxi_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/switch_gfx.c b/gfx/drivers/switch_gfx.c index e5b5cb48d1..d491934eed 100644 --- a/gfx/drivers/switch_gfx.c +++ b/gfx/drivers/switch_gfx.c @@ -397,6 +397,10 @@ static const video_poke_interface_t switch_poke_interface = { NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void switch_get_poke_interface(void *data, diff --git a/gfx/drivers/switch_nx_gfx.c b/gfx/drivers/switch_nx_gfx.c index c903e3a840..49759b71a4 100644 --- a/gfx/drivers/switch_nx_gfx.c +++ b/gfx/drivers/switch_nx_gfx.c @@ -682,6 +682,10 @@ static const video_poke_interface_t switch_poke_interface = { NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void switch_get_poke_interface(void *data, diff --git a/gfx/drivers/vga_gfx.c b/gfx/drivers/vga_gfx.c index 67d1000b16..d776b53b52 100644 --- a/gfx/drivers/vga_gfx.c +++ b/gfx/drivers/vga_gfx.c @@ -385,7 +385,11 @@ static const video_poke_interface_t vga_poke_interface = { NULL, /* grab_mouse_toggle */ NULL, /* get_current_shader */ NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void vga_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/vita2d_gfx.c b/gfx/drivers/vita2d_gfx.c index 11bb3a2154..91c9d95f54 100644 --- a/gfx/drivers/vita2d_gfx.c +++ b/gfx/drivers/vita2d_gfx.c @@ -824,7 +824,11 @@ static const video_poke_interface_t vita_poke_interface = { NULL, NULL, vita_get_current_sw_framebuffer, - NULL + NULL, + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void vita2d_gfx_get_poke_interface(void *data, diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 8afd897fa9..928e515065 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -2638,6 +2638,10 @@ static const video_poke_interface_t vulkan_poke_interface = { vulkan_get_current_shader, vulkan_get_current_sw_framebuffer, vulkan_get_hw_render_interface, + NULL, /* set_hdr_max_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_contrast */ + NULL /* set_hdr_expand_gamut */ }; static void vulkan_get_poke_interface(void *data, diff --git a/gfx/video_thread_wrapper.c b/gfx/video_thread_wrapper.c index 0336ddb65c..d663eed6e1 100644 --- a/gfx/video_thread_wrapper.c +++ b/gfx/video_thread_wrapper.c @@ -366,6 +366,34 @@ static bool video_thread_handle_packet( /* Never reply on no command. Possible deadlock if * thread sends command right after frame update. */ break; + + case CMD_POKE_SET_HDR_MAX_NITS: + if (thr->poke && thr->poke->set_hdr_max_nits) + thr->poke->set_hdr_max_nits(thr->driver_data, + pkt.data.hdr.max_nits); + video_thread_reply(thr, &pkt); + break; + + case CMD_POKE_SET_HDR_PAPER_WHITE_NITS: + if (thr->poke && thr->poke->set_hdr_paper_white_nits) + thr->poke->set_hdr_paper_white_nits(thr->driver_data, + pkt.data.hdr.paper_white_nits); + video_thread_reply(thr, &pkt); + break; + + case CMD_POKE_SET_HDR_CONTRAST: + if (thr->poke && thr->poke->set_hdr_contrast) + thr->poke->set_hdr_contrast(thr->driver_data, + pkt.data.hdr.contrast); + video_thread_reply(thr, &pkt); + break; + + case CMD_POKE_SET_HDR_EXPAND_GAMUT: + if (thr->poke && thr->poke->set_hdr_expand_gamut) + thr->poke->set_hdr_expand_gamut(thr->driver_data, + pkt.data.hdr.expand_gamut); + video_thread_reply(thr, &pkt); + break; default: video_thread_reply(thr, &pkt); break; @@ -931,6 +959,58 @@ static void thread_set_filtering(void *data, unsigned idx, bool smooth, bool ctx video_thread_send_and_wait_user_to_thread(thr, &pkt); } +static void thread_set_hdr_max_nits(void *data, float max_nits) +{ + thread_packet_t pkt; + thread_video_t *thr = (thread_video_t*)data; + + if (!thr) + return; + pkt.type = CMD_POKE_SET_HDR_MAX_NITS; + pkt.data.hdr.max_nits = max_nits; + + video_thread_send_and_wait_user_to_thread(thr, &pkt); +} + +static void thread_set_hdr_paper_white_nits(void *data, float paper_white_nits) +{ + thread_packet_t pkt; + thread_video_t *thr = (thread_video_t*)data; + + if (!thr) + return; + pkt.type = CMD_POKE_SET_HDR_PAPER_WHITE_NITS; + pkt.data.hdr.paper_white_nits = paper_white_nits; + + video_thread_send_and_wait_user_to_thread(thr, &pkt); +} + +static void thread_set_hdr_contrast(void *data, float contrast) +{ + thread_packet_t pkt; + thread_video_t *thr = (thread_video_t*)data; + + if (!thr) + return; + pkt.type = CMD_POKE_SET_HDR_CONTRAST; + pkt.data.hdr.contrast = contrast; + + video_thread_send_and_wait_user_to_thread(thr, &pkt); +} + +static void thread_set_hdr_expand_gamut(void *data, bool expand_gamut) +{ + thread_packet_t pkt; + thread_video_t *thr = (thread_video_t*)data; + + if (!thr) + return; + pkt.type = CMD_POKE_SET_HDR_EXPAND_GAMUT; + pkt.data.hdr.expand_gamut = expand_gamut; + + video_thread_send_and_wait_user_to_thread(thr, &pkt); +} + static void thread_get_video_output_size(void *data, unsigned *width, unsigned *height) { @@ -1132,7 +1212,11 @@ static const video_poke_interface_t thread_poke = { thread_get_current_shader, NULL, /* get_current_software_framebuffer */ - NULL /* get_hw_render_interface */ + NULL, /* get_hw_render_interface */ + thread_set_hdr_max_nits, + thread_set_hdr_paper_white_nits, + thread_set_hdr_contrast, + thread_set_hdr_expand_gamut }; static void video_thread_get_poke_interface( diff --git a/gfx/video_thread_wrapper.h b/gfx/video_thread_wrapper.h index 739c6d2bca..d378698541 100644 --- a/gfx/video_thread_wrapper.h +++ b/gfx/video_thread_wrapper.h @@ -57,6 +57,11 @@ enum thread_cmd CMD_POKE_SHOW_MOUSE, CMD_POKE_GRAB_MOUSE_TOGGLE, + CMD_POKE_SET_HDR_MAX_NITS, + CMD_POKE_SET_HDR_PAPER_WHITE_NITS, + CMD_POKE_SET_HDR_CONTRAST, + CMD_POKE_SET_HDR_EXPAND_GAMUT, + CMD_DUMMY = INT_MAX }; @@ -150,6 +155,14 @@ struct thread_packet bool is_threaded; enum font_driver_render_api api; } font_init; + + struct + { + float max_nits; + float paper_white_nits; + float contrast; + bool expand_gamut; + } hdr; } data; enum thread_cmd type; }; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index baa893610b..9f81cbd6ae 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1072,6 +1072,10 @@ MSG_HASH( MENU_ENUM_LABEL_DEFERRED_VIDEO_SCALING_SETTINGS_LIST, "deferred_video_scaling_settings_list" ) +MSG_HASH( + MENU_ENUM_LABEL_DEFERRED_VIDEO_HDR_SETTINGS_LIST, + "deferred_video_hdr_settings_list" + ) MSG_HASH( MENU_ENUM_LABEL_DEFERRED_VIDEO_SYNCHRONIZATION_SETTINGS_LIST, "deferred_video_synchronization_settings_list" @@ -3356,6 +3360,10 @@ MSG_HASH( MENU_ENUM_LABEL_VIDEO_SCALING_SETTINGS, "video_scaling_settings" ) +MSG_HASH( + MENU_ENUM_LABEL_VIDEO_HDR_SETTINGS, + "video_hdr_settings" + ) MSG_HASH( MENU_ENUM_LABEL_VIDEO_SYNCHRONIZATION_SETTINGS, "video_synchronization_settings" diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 9eda1c4ad6..e7975566ce 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -1316,6 +1316,36 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) "Resolution of 0 uses the \n" "resolution of the environment.\n"); break; + case MENU_ENUM_LABEL_VIDEO_HDR_ENABLE: + snprintf(s, len, + "Enable HDR.\n" + " \n" + "If supported this enables hdr \n"); + break; + case MENU_ENUM_LABEL_VIDEO_HDR_MAX_NITS: + snprintf(s, len, + "Max Nits\n" + " \n" + "Set the maximum no. nits the display can reproduce\n"); + break; + case MENU_ENUM_LABEL_VIDEO_HDR_PAPER_WHITE_NITS: + snprintf(s, len, + "Paper White Nits\n" + " \n" + "Set the no. nits at which paper white should be\n"); + break; + case MENU_ENUM_LABEL_VIDEO_HDR_CONTRAST: + snprintf(s, len, + "Contrast\n" + " \n" + "The constrast setting for HDR\n"); + break; + case MENU_ENUM_LABEL_VIDEO_HDR_EXPAND_GAMUT: + snprintf(s, len, + "Expand Gamut\n" + " \n" + "Once converted to linear space should we use an expanded gamut to get to HDR10\n"); + break; case MENU_ENUM_LABEL_FASTFORWARD_RATIO: snprintf(s, len, "Fastforward ratio.\n" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 7257de54aa..381aa1867b 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1304,6 +1304,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_SCALING_SETTINGS, "Change video scaling settings." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SETTINGS, + "HDR" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_HDR_SETTINGS, + "Change video hdr settings." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_SYNCHRONIZATION_SETTINGS, "Synchronization" @@ -1748,6 +1756,49 @@ MSG_HASH( "Cut off a few pixels around the edges of the image customarily left blank by developers which sometimes also contain garbage pixels." ) +/* Settings > Video > HDR */ + +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_ENABLE, + "Enable HDR" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_HDR_ENABLE, + "Enable HDR if the display supports it" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_MAX_NITS, + "Max Nits" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_HDR_MAX_NITS, + "" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_PAPER_WHITE_NITS, + "Paper White Nits" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_HDR_PAPER_WHITE_NITS, + "" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_CONTRAST, + "Contrast" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_HDR_CONTRAST, + "" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_EXPAND_GAMUT, + "Expand Gamut" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_HDR_EXPAND_GAMUT, + "" + ) + /* Settings > Video > Synchronization */ MSG_HASH( diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index 9dbc2207d3..95a8f0bd6a 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -167,6 +167,7 @@ GENERIC_DEFERRED_PUSH(deferred_push_video_windowed_mode_settings_list, DISPLA GENERIC_DEFERRED_PUSH(deferred_push_video_synchronization_settings_list, DISPLAYLIST_VIDEO_SYNCHRONIZATION_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_video_output_settings_list, DISPLAYLIST_VIDEO_OUTPUT_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_video_scaling_settings_list, DISPLAYLIST_VIDEO_SCALING_SETTINGS_LIST) +GENERIC_DEFERRED_PUSH(deferred_push_video_hdr_settings_list, DISPLAYLIST_VIDEO_HDR_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_crt_switchres_settings_list, DISPLAYLIST_CRT_SWITCHRES_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_configuration_settings_list, DISPLAYLIST_CONFIGURATION_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_saving_settings_list, DISPLAYLIST_SAVING_SETTINGS_LIST) @@ -766,6 +767,7 @@ static int menu_cbs_init_bind_deferred_push_compare_label( {MENU_ENUM_LABEL_DEFERRED_VIDEO_SYNCHRONIZATION_SETTINGS_LIST, deferred_push_video_synchronization_settings_list}, {MENU_ENUM_LABEL_DEFERRED_VIDEO_OUTPUT_SETTINGS_LIST, deferred_push_video_output_settings_list}, {MENU_ENUM_LABEL_DEFERRED_VIDEO_SCALING_SETTINGS_LIST, deferred_push_video_scaling_settings_list}, + {MENU_ENUM_LABEL_DEFERRED_VIDEO_HDR_SETTINGS_LIST, deferred_push_video_hdr_settings_list}, {MENU_ENUM_LABEL_DEFERRED_CRT_SWITCHRES_SETTINGS_LIST, deferred_push_crt_switchres_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST, deferred_push_audio_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, deferred_push_audio_synchronization_settings_list}, @@ -1209,6 +1211,9 @@ static int menu_cbs_init_bind_deferred_push_compare_label( case MENU_ENUM_LABEL_DEFERRED_VIDEO_OUTPUT_SETTINGS_LIST: BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_video_output_settings_list); break; + case MENU_ENUM_LABEL_DEFERRED_VIDEO_HDR_SETTINGS_LIST: + BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_video_hdr_settings_list); + break; case MENU_ENUM_LABEL_DEFERRED_VIDEO_SCALING_SETTINGS_LIST: BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_video_scaling_settings_list); break; diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 3d4ebaebf5..486b1f9d7f 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -330,6 +330,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MENU_ENUM_LABEL_DEFERRED_VIDEO_WINDOWED_MODE_SETTINGS_LIST; case ACTION_OK_DL_VIDEO_SCALING_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_VIDEO_SCALING_SETTINGS_LIST; + case ACTION_OK_DL_VIDEO_HDR_SETTINGS_LIST: + return MENU_ENUM_LABEL_DEFERRED_VIDEO_HDR_SETTINGS_LIST; case ACTION_OK_DL_VIDEO_OUTPUT_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_VIDEO_OUTPUT_SETTINGS_LIST; case ACTION_OK_DL_CRT_SWITCHRES_SETTINGS_LIST: @@ -1458,6 +1460,7 @@ int generic_action_ok_displaylist_push(const char *path, case ACTION_OK_DL_CORE_SETTINGS_LIST: case ACTION_OK_DL_CORE_INFORMATION_LIST: case ACTION_OK_DL_VIDEO_SETTINGS_LIST: + case ACTION_OK_DL_VIDEO_HDR_SETTINGS_LIST: case ACTION_OK_DL_VIDEO_SYNCHRONIZATION_SETTINGS_LIST: case ACTION_OK_DL_VIDEO_FULLSCREEN_MODE_SETTINGS_LIST: case ACTION_OK_DL_VIDEO_WINDOWED_MODE_SETTINGS_LIST: @@ -5609,6 +5612,7 @@ DEFAULT_ACTION_OK_FUNC(action_ok_push_video_fullscreen_mode_settings_list, ACTIO DEFAULT_ACTION_OK_FUNC(action_ok_push_video_synchronization_settings_list, ACTION_OK_DL_VIDEO_SYNCHRONIZATION_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_video_windowed_mode_settings_list, ACTION_OK_DL_VIDEO_WINDOWED_MODE_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_video_scaling_settings_list, ACTION_OK_DL_VIDEO_SCALING_SETTINGS_LIST) +DEFAULT_ACTION_OK_FUNC(action_ok_push_video_hdr_settings_list, ACTION_OK_DL_VIDEO_HDR_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_video_output_settings_list, ACTION_OK_DL_VIDEO_OUTPUT_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_configuration_settings_list, ACTION_OK_DL_CONFIGURATION_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_core_settings_list, ACTION_OK_DL_CORE_SETTINGS_LIST) @@ -7688,6 +7692,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_VIDEO_FULLSCREEN_MODE_SETTINGS, action_ok_push_video_fullscreen_mode_settings_list}, {MENU_ENUM_LABEL_VIDEO_WINDOWED_MODE_SETTINGS, action_ok_push_video_windowed_mode_settings_list}, {MENU_ENUM_LABEL_VIDEO_SCALING_SETTINGS, action_ok_push_video_scaling_settings_list}, + {MENU_ENUM_LABEL_VIDEO_HDR_SETTINGS, action_ok_push_video_hdr_settings_list}, {MENU_ENUM_LABEL_VIDEO_OUTPUT_SETTINGS, action_ok_push_video_output_settings_list}, {MENU_ENUM_LABEL_CRT_SWITCHRES_SETTINGS, action_ok_push_crt_switchres_settings_list}, {MENU_ENUM_LABEL_AUDIO_SETTINGS, action_ok_push_audio_settings_list}, diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 13256795c8..720fbfc7fe 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -203,6 +203,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_synchronization_settings_list, DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_fullscreen_mode_settings_list, MENU_ENUM_SUBLABEL_VIDEO_FULLSCREEN_MODE_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_windowed_mode_settings_list, MENU_ENUM_SUBLABEL_VIDEO_WINDOWED_MODE_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_scaling_settings_list, MENU_ENUM_SUBLABEL_VIDEO_SCALING_SETTINGS) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_hdr_settings_list, MENU_ENUM_SUBLABEL_VIDEO_HDR_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_output_settings_list, MENU_ENUM_SUBLABEL_VIDEO_OUTPUT_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_crt_switchres_settings_list, MENU_ENUM_SUBLABEL_CRT_SWITCHRES_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_suspend_screensaver_enable, MENU_ENUM_SUBLABEL_SUSPEND_SCREENSAVER_ENABLE) @@ -3878,6 +3879,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_VIDEO_SCALING_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_scaling_settings_list); break; + case MENU_ENUM_LABEL_VIDEO_HDR_SETTINGS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_hdr_settings_list); + break; case MENU_ENUM_LABEL_VIDEO_OUTPUT_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_output_settings_list); break; diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index 640442e12f..d87f849943 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -589,6 +589,7 @@ DEFAULT_TITLE_MACRO(action_get_core_settings_list, MENU_ENUM_LABEL_ DEFAULT_TITLE_MACRO(action_get_video_settings_list, MENU_ENUM_LABEL_VALUE_VIDEO_SETTINGS) DEFAULT_TITLE_MACRO(action_get_video_fullscreen_mode_settings_list, MENU_ENUM_LABEL_VALUE_VIDEO_FULLSCREEN_MODE_SETTINGS) DEFAULT_TITLE_MACRO(action_get_video_windowed_mode_settings_list, MENU_ENUM_LABEL_VALUE_VIDEO_WINDOWED_MODE_SETTINGS) +DEFAULT_TITLE_MACRO(action_get_video_hdr_settings_list, MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SETTINGS) DEFAULT_TITLE_MACRO(action_get_video_scaling_settings_list, MENU_ENUM_LABEL_VALUE_VIDEO_SCALING_SETTINGS) DEFAULT_TITLE_MACRO(action_get_video_output_settings_list, MENU_ENUM_LABEL_VALUE_VIDEO_OUTPUT_SETTINGS) DEFAULT_TITLE_MACRO(action_get_video_synchronization_settings_list, MENU_ENUM_LABEL_VALUE_VIDEO_SYNCHRONIZATION_SETTINGS) @@ -1000,6 +1001,7 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_ONLINE_UPDATER, action_get_online_updater_list}, {MENU_ENUM_LABEL_DEFERRED_RECORDING_SETTINGS_LIST, action_get_recording_settings_list}, {MENU_ENUM_LABEL_DEFERRED_VIDEO_SCALING_SETTINGS_LIST, action_get_video_scaling_settings_list}, + {MENU_ENUM_LABEL_DEFERRED_VIDEO_HDR_SETTINGS_LIST, action_get_video_hdr_settings_list}, {MENU_ENUM_LABEL_DEFERRED_VIDEO_OUTPUT_SETTINGS_LIST, action_get_video_output_settings_list}, {MENU_ENUM_LABEL_DEFERRED_VIDEO_SYNCHRONIZATION_SETTINGS_LIST, action_get_video_synchronization_settings_list}, {MENU_ENUM_LABEL_DEFERRED_INPUT_MENU_SETTINGS_LIST, action_get_input_menu_settings_list}, diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 822d8f4bb1..2918ef544e 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -10383,6 +10383,7 @@ static void materialui_list_insert( string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_FULLSCREEN_MODE_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_WINDOWED_MODE_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_SCALING_SETTINGS)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_HDR_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_RESAMPLER_SETTINGS)) || diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 133f1c1351..81213cdaa4 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -107,6 +107,7 @@ enum ACTION_OK_DL_VIDEO_SYNCHRONIZATION_SETTINGS_LIST, ACTION_OK_DL_VIDEO_OUTPUT_SETTINGS_LIST, ACTION_OK_DL_VIDEO_SCALING_SETTINGS_LIST, + ACTION_OK_DL_VIDEO_HDR_SETTINGS_LIST, ACTION_OK_DL_CRT_SWITCHRES_SETTINGS_LIST, ACTION_OK_DL_AUDIO_SETTINGS_LIST, ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 699f7cdd2f..14e0207e95 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6095,6 +6095,11 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_VIDEO_SCALING_SETTINGS, PARSE_ACTION, false) == 0) count++; + /* if (video_driver_supports_hdr()) */ + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_VIDEO_HDR_SETTINGS, + PARSE_ACTION, false) == 0) + count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_VIDEO_SYNCHRONIZATION_SETTINGS, PARSE_ACTION, false) == 0) @@ -7917,6 +7922,37 @@ unsigned menu_displaylist_build_list( count++; } break; + case DISPLAYLIST_VIDEO_HDR_SETTINGS_LIST: + { + if (video_driver_supports_hdr()) + { + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_VIDEO_HDR_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; + + /* if (settings->bools.video_hdr_enable) */ + { + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_VIDEO_HDR_MAX_NITS, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_VIDEO_HDR_PAPER_WHITE_NITS, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_VIDEO_HDR_CONTRAST, + PARSE_ONLY_FLOAT, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_VIDEO_HDR_EXPAND_GAMUT, + PARSE_ONLY_BOOL, false) == 0) + count++; + } + } + } + break; case DISPLAYLIST_VIDEO_SCALING_SETTINGS_LIST: { #if defined(DINGUX) @@ -11637,6 +11673,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, case DISPLAYLIST_VIDEO_FULLSCREEN_MODE_SETTINGS_LIST: case DISPLAYLIST_VIDEO_WINDOWED_MODE_SETTINGS_LIST: case DISPLAYLIST_VIDEO_OUTPUT_SETTINGS_LIST: + case DISPLAYLIST_VIDEO_HDR_SETTINGS_LIST: case DISPLAYLIST_VIDEO_SYNCHRONIZATION_SETTINGS_LIST: case DISPLAYLIST_VIDEO_SCALING_SETTINGS_LIST: case DISPLAYLIST_OPTIONS_DISK: diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index f724a02093..daff826351 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -156,6 +156,7 @@ enum menu_displaylist_ctl_state DISPLAYLIST_VIDEO_SYNCHRONIZATION_SETTINGS_LIST, DISPLAYLIST_VIDEO_OUTPUT_SETTINGS_LIST, DISPLAYLIST_VIDEO_SCALING_SETTINGS_LIST, + DISPLAYLIST_VIDEO_HDR_SETTINGS_LIST, DISPLAYLIST_CRT_SWITCHRES_SETTINGS_LIST, DISPLAYLIST_VIDEO_SETTINGS_LIST, DISPLAYLIST_CONFIGURATION_SETTINGS_LIST, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index cefc63a7d9..c5e58c86ae 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -7711,6 +7711,51 @@ static void general_write_handler(rarch_setting_t *setting) rarch_cmd = CMD_EVENT_REINIT; } break; + case MENU_ENUM_LABEL_VIDEO_HDR_ENABLE: + { + settings_t *settings = config_get_ptr(); + settings->modified = true; + settings->bools.video_hdr_enable = *setting->value.target.boolean; + + rarch_cmd = CMD_EVENT_REINIT; + } + break; + case MENU_ENUM_LABEL_VIDEO_HDR_MAX_NITS: + { + settings_t *settings = config_get_ptr(); + settings->modified = true; + settings->floats.video_hdr_max_nits = roundf(*setting->value.target.fraction); + + video_driver_set_hdr_max_nits(settings->floats.video_hdr_max_nits); + } + break; + case MENU_ENUM_LABEL_VIDEO_HDR_PAPER_WHITE_NITS: + { + settings_t *settings = config_get_ptr(); + settings->modified = true; + settings->floats.video_hdr_paper_white_nits = roundf(*setting->value.target.fraction); + + video_driver_set_hdr_paper_white_nits(settings->floats.video_hdr_paper_white_nits); + } + break; + case MENU_ENUM_LABEL_VIDEO_HDR_CONTRAST: + { + settings_t *settings = config_get_ptr(); + settings->modified = true; + settings->floats.video_hdr_contrast = *setting->value.target.fraction; + + video_driver_set_hdr_contrast(settings->floats.video_hdr_contrast); + } + break; + case MENU_ENUM_LABEL_VIDEO_HDR_EXPAND_GAMUT: + { + settings_t *settings = config_get_ptr(); + settings->modified = true; + settings->bools.video_hdr_expand_gamut = *setting->value.target.boolean;; + + video_driver_set_hdr_expand_gamut(settings->bools.video_hdr_expand_gamut); + } + break; case MENU_ENUM_LABEL_INPUT_MAX_USERS: command_event(CMD_EVENT_CONTROLLER_INIT, NULL); break; @@ -11783,6 +11828,114 @@ static bool setting_append_list( SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); END_SUB_GROUP(list, list_info, parent_group); + + if(video_driver_supports_hdr()) + { + START_SUB_GROUP(list, list_info, "HDR", &group_info, &subgroup_info, parent_group); + + CONFIG_ACTION( + list, list_info, + MENU_ENUM_LABEL_VIDEO_HDR_SETTINGS, + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SETTINGS, + &group_info, + &subgroup_info, + parent_group); + + CONFIG_BOOL( + list, list_info, + &settings->bools.video_hdr_enable, + MENU_ENUM_LABEL_VIDEO_HDR_ENABLE, + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_ENABLE, + DEFAULT_VIDEO_HDR_ENABLE, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); + (*list)[list_info->index - 1].action_ok = setting_bool_action_left_with_refresh; + (*list)[list_info->index - 1].action_left = setting_bool_action_left_with_refresh; + (*list)[list_info->index - 1].action_right = setting_bool_action_right_with_refresh; + MENU_SETTINGS_LIST_CURRENT_ADD_CMD( + list, + list_info, + CMD_EVENT_VIDEO_APPLY_STATE_CHANGES); + + /* if (settings->bools.video_hdr_enable) */ + { + CONFIG_FLOAT( + list, list_info, + &settings->floats.video_hdr_max_nits, + MENU_ENUM_LABEL_VIDEO_HDR_MAX_NITS, + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_MAX_NITS, + DEFAULT_VIDEO_HDR_MAX_NITS, + "%.1fx", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, 0.0, 10000.0, 10.0, true, true); + + CONFIG_FLOAT( + list, list_info, + &settings->floats.video_hdr_paper_white_nits, + MENU_ENUM_LABEL_VIDEO_HDR_PAPER_WHITE_NITS, + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_PAPER_WHITE_NITS, + DEFAULT_VIDEO_HDR_PAPER_WHITE_NITS, + "%.1fx", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, 0.0, 10000.0, 10.0, true, true); + + CONFIG_FLOAT( + list, list_info, + &settings->floats.video_hdr_contrast, + MENU_ENUM_LABEL_VIDEO_HDR_CONTRAST, + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_CONTRAST, + DEFAULT_VIDEO_HDR_CONTRAST, + "%.2fx", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, 0.1, 3.0, 0.01, true, true); + + CONFIG_BOOL( + list, list_info, + &settings->bools.video_hdr_expand_gamut, + MENU_ENUM_LABEL_VIDEO_HDR_EXPAND_GAMUT, + MENU_ENUM_LABEL_VALUE_VIDEO_HDR_EXPAND_GAMUT, + DEFAULT_VIDEO_HDR_EXPAND_GAMUT, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); + (*list)[list_info->index - 1].action_ok = setting_bool_action_left_with_refresh; + (*list)[list_info->index - 1].action_left = setting_bool_action_left_with_refresh; + (*list)[list_info->index - 1].action_right = setting_bool_action_right_with_refresh; + MENU_SETTINGS_LIST_CURRENT_ADD_CMD( + list, + list_info, + CMD_EVENT_VIDEO_APPLY_STATE_CHANGES); + } + + END_SUB_GROUP(list, list_info, parent_group); + } + START_SUB_GROUP( list, list_info, diff --git a/msg_hash.h b/msg_hash.h index f855bc70a5..dc5ac135e0 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1097,6 +1097,12 @@ enum msg_hash_enums MENU_LABEL(VIDEO_REFRESH_RATE_AUTO), MENU_LABEL(VIDEO_REFRESH_RATE_POLLED), + MENU_LABEL(VIDEO_HDR_ENABLE), + MENU_LABEL(VIDEO_HDR_MAX_NITS), + MENU_LABEL(VIDEO_HDR_PAPER_WHITE_NITS), + MENU_LABEL(VIDEO_HDR_CONTRAST), + MENU_LABEL(VIDEO_HDR_EXPAND_GAMUT), + MENU_LABEL(VIDEO_LAYOUT_ENABLE), MENU_LABEL(VIDEO_LAYOUT_PATH), MENU_LABEL(VIDEO_LAYOUT_SELECTED_VIEW), @@ -1538,6 +1544,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_DEFERRED_VIDEO_FULLSCREEN_MODE_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_VIDEO_OUTPUT_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_VIDEO_SYNCHRONIZATION_SETTINGS_LIST, + MENU_ENUM_LABEL_DEFERRED_VIDEO_HDR_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_VIDEO_SCALING_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_CRT_SWITCHRES_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_CONFIGURATION_SETTINGS_LIST, @@ -2345,6 +2352,7 @@ enum msg_hash_enums MENU_LABEL(VIDEO_OUTPUT_SETTINGS), MENU_LABEL(VIDEO_SYNCHRONIZATION_SETTINGS), MENU_LABEL(VIDEO_SCALING_SETTINGS), + MENU_LABEL(VIDEO_HDR_SETTINGS), MENU_LABEL(CRT_SWITCHRES_SETTINGS), MENU_LABEL(AUDIO_SETTINGS), MENU_LABEL(AUDIO_RESAMPLER_SETTINGS), diff --git a/retroarch.c b/retroarch.c index e05b7e5c17..a95d4f8cf1 100644 --- a/retroarch.c +++ b/retroarch.c @@ -27718,6 +27718,38 @@ void video_driver_set_filtering(unsigned index, bool smooth, bool ctx_scaling) index, smooth, ctx_scaling); } +void video_driver_set_hdr_max_nits(float max_nits) +{ + struct rarch_state *p_rarch = &rarch_st; + if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->set_hdr_max_nits) + p_rarch->video_driver_poke->set_hdr_max_nits(p_rarch->video_driver_data, + max_nits); +} + +void video_driver_set_hdr_paper_white_nits(float paper_white_nits) +{ + struct rarch_state *p_rarch = &rarch_st; + if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->set_hdr_paper_white_nits) + p_rarch->video_driver_poke->set_hdr_paper_white_nits(p_rarch->video_driver_data, + paper_white_nits); +} + +void video_driver_set_hdr_contrast(float contrast) +{ + struct rarch_state *p_rarch = &rarch_st; + if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->set_hdr_contrast) + p_rarch->video_driver_poke->set_hdr_contrast(p_rarch->video_driver_data, + contrast); +} + +void video_driver_set_hdr_expand_gamut(bool expand_gamut) +{ + struct rarch_state *p_rarch = &rarch_st; + if (p_rarch->video_driver_poke && p_rarch->video_driver_poke->set_hdr_expand_gamut) + p_rarch->video_driver_poke->set_hdr_expand_gamut(p_rarch->video_driver_data, + expand_gamut); +} + void video_driver_cached_frame_set(const void *data, unsigned width, unsigned height, size_t pitch) { @@ -28068,6 +28100,32 @@ bool video_driver_supports_rgba(void) return tmp; } +void video_driver_set_hdr_support(void) +{ + struct rarch_state *p_rarch = &rarch_st; + VIDEO_DRIVER_LOCK(); + p_rarch->video_driver_hdr_support = true; + VIDEO_DRIVER_UNLOCK(); +} + +void video_driver_unset_hdr_support(void) +{ + struct rarch_state *p_rarch = &rarch_st; + VIDEO_DRIVER_LOCK(); + p_rarch->video_driver_hdr_support = false; + VIDEO_DRIVER_UNLOCK(); +} + +bool video_driver_supports_hdr(void) +{ + bool tmp; + struct rarch_state *p_rarch = &rarch_st; + VIDEO_DRIVER_LOCK(); + tmp = p_rarch->video_driver_hdr_support; + VIDEO_DRIVER_UNLOCK(); + return tmp; +} + bool video_driver_get_next_video_out(void) { struct rarch_state *p_rarch = &rarch_st; @@ -29152,6 +29210,7 @@ void video_driver_build_info(video_frame_info_t *video_info) video_info->height = p_rarch->video_driver_height; video_info->use_rgba = p_rarch->video_driver_use_rgba; + video_info->hdr_enable = settings->bools.video_hdr_enable; video_info->libretro_running = false; video_info->msg_bgcolor_enable = @@ -30527,6 +30586,7 @@ static void retroarch_deinit_drivers( video_display_server_destroy(); p_rarch->video_driver_use_rgba = false; + p_rarch->video_driver_hdr_support = false; p_rarch->video_driver_active = false; p_rarch->video_driver_cache_context = false; p_rarch->video_driver_cache_context_ack = false; diff --git a/retroarch.cfg b/retroarch.cfg index 8c55924f23..2ccd895d3e 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -283,6 +283,13 @@ # The angle is * 90 degrees counter-clockwise. # screen_orientation = 0 +# HDR settings +# video_hdr_enable = false +# video_hdr_max_nits = 1000.0f +# video_hdr_paper_white_nits = 200.0f +# video_hdr_contrast = 1.0f +# video_hdr_expand_gamut = true + #### Audio # Enable audio. diff --git a/retroarch.h b/retroarch.h index 7236065618..4d46f39b5c 100644 --- a/retroarch.h +++ b/retroarch.h @@ -799,6 +799,7 @@ void recording_driver_update_streaming_url(void); #define VIDEO_SHADER_MENU_4 (GFX_MAX_SHADERS - 5) #define VIDEO_SHADER_MENU_5 (GFX_MAX_SHADERS - 6) #define VIDEO_SHADER_MENU_6 (GFX_MAX_SHADERS - 7) +#define VIDEO_SHADER_STOCK_HDR (GFX_MAX_SHADERS - 8) #if defined(_XBOX360) #define DEFAULT_SHADER_TYPE RARCH_SHADER_HLSL @@ -1233,6 +1234,7 @@ typedef struct video_frame_info bool fullscreen; bool font_enable; bool use_rgba; + bool hdr_support; bool libretro_running; bool xmb_shadows_enable; bool battery_level_enable; @@ -1243,7 +1245,7 @@ typedef struct video_frame_info bool menu_screensaver_active; bool msg_bgcolor_enable; bool crt_switch_hires_menu; - + bool hdr_enable; } video_frame_info_t; typedef void (*update_window_title_cb)(void*); @@ -1452,6 +1454,12 @@ typedef struct video_poke_interface struct retro_framebuffer *framebuffer); bool (*get_hw_render_interface)(void *data, const struct retro_hw_render_interface **iface); + + /* hdr settings */ + void (*set_hdr_max_nits)(void *data, float max_nits); + void (*set_hdr_paper_white_nits)(void *data, float paper_white_nits); + void (*set_hdr_contrast)(void *data, float contrast); + void (*set_hdr_expand_gamut)(void *data, bool expand_gamut); } video_poke_interface_t; /* msg is for showing a message on the screen @@ -1579,6 +1587,12 @@ void video_driver_unset_rgba(void); bool video_driver_supports_rgba(void); +void video_driver_set_hdr_support(void); + +void video_driver_unset_hdr_support(void); + +bool video_driver_supports_hdr(void); + bool video_driver_get_next_video_out(void); bool video_driver_get_prev_video_out(void); @@ -1655,6 +1669,11 @@ void * video_driver_read_frame_raw(unsigned *width, void video_driver_set_filtering(unsigned index, bool smooth, bool ctx_scaling); +void video_driver_set_hdr_max_nits(float max_nits); +void video_driver_set_hdr_paper_white_nits(float paper_white_nits); +void video_driver_set_hdr_contrast(float contrast); +void video_driver_set_hdr_expand_gamut(bool expand_gamut); + const char *video_driver_get_ident(void); void video_driver_set_viewport(unsigned width, unsigned height, diff --git a/retroarch_data.h b/retroarch_data.h index 28a73adc46..40c804b9e3 100644 --- a/retroarch_data.h +++ b/retroarch_data.h @@ -1844,6 +1844,11 @@ struct rarch_state * TODO: Refactor this better. */ bool video_driver_use_rgba; + /* Graphics driver supports HDR displays + * Currently only D3D11/D3D12 supports HDR displays and + * whether we've enabled it */ + bool video_driver_hdr_support; + /* If set during context deinit, the driver should keep * graphics context alive to avoid having to reset all * context state. */ diff --git a/tools/com-parser/com-parse.cpp b/tools/com-parser/com-parse.cpp index 1252aa25e7..966f43da6e 100644 --- a/tools/com-parser/com-parse.cpp +++ b/tools/com-parser/com-parse.cpp @@ -173,7 +173,7 @@ vector derived_types_list = "IDXGIFactory1", "IDXGIAdapter1", "IDXGISurface1", - "IDXGISwapChain3", + "IDXGISwapChain4", "IDXGIOutput", "IDXGIDevice", }; diff --git a/ui/drivers/qt/qt_options.cpp b/ui/drivers/qt/qt_options.cpp index 9e1688663f..336c9e46e1 100644 --- a/ui/drivers/qt/qt_options.cpp +++ b/ui/drivers/qt/qt_options.cpp @@ -1235,6 +1235,9 @@ QWidget *VideoPage::widget() CheckableSettingsGroup *savePosGroup = new CheckableSettingsGroup(MENU_ENUM_LABEL_VIDEO_WINDOW_CUSTOM_SIZE_ENABLE); #endif + SettingsGroup *hdrGroup = new SettingsGroup("HDR"); + QHBoxLayout *hdrLayout = new QHBoxLayout; + SettingsGroup *syncGroup = new SettingsGroup("Synchronization"); CheckableSettingsGroup *vSyncGroup = new CheckableSettingsGroup(MENU_ENUM_LABEL_VIDEO_VSYNC); @@ -1307,6 +1310,12 @@ QWidget *VideoPage::widget() windowedGroup->add(MENU_ENUM_LABEL_VIDEO_WINDOW_SHOW_DECORATIONS); + hdrGroup->add(MENU_ENUM_LABEL_VIDEO_HDR_ENABLE); + hdrGroup->add(MENU_ENUM_LABEL_VIDEO_HDR_MAX_NITS); + hdrGroup->add(MENU_ENUM_LABEL_VIDEO_HDR_PAPER_WHITE_NITS); + hdrGroup->add(MENU_ENUM_LABEL_VIDEO_HDR_CONTRAST); + hdrGroup->add(MENU_ENUM_LABEL_VIDEO_HDR_EXPAND_GAMUT); + vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_SWAP_INTERVAL); vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_ADAPTIVE_VSYNC); vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_FRAME_DELAY); @@ -1333,6 +1342,8 @@ QWidget *VideoPage::widget() miscGroup->add(MENU_ENUM_LABEL_VIDEO_CTX_SCALING); miscGroup->add(MENU_ENUM_LABEL_VIDEO_SHADER_DELAY); + hdrLayout->addWidget(hdrGroup); + syncMiscLayout->addWidget(syncGroup); syncMiscLayout->addWidget(miscGroup); @@ -1348,6 +1359,7 @@ QWidget *VideoPage::widget() layout->addLayout(outputScalingLayout); layout->addLayout(modeLayout); + layout->addLayout(hdrLayout); layout->addLayout(syncMiscLayout); layout->addWidget(filterGroup);