Skip to content

Commit f23eb89

Browse files
Vulkan/D3D11/D3D12: Add scRGB HDR support and fix HDR pipeline (#18759)
* D3D11/D3D12/Vulkan: Disable internal HDR10 conversion for RGBA16 output When a shader preset's last pass explicitly outputs RGBA16, disable the internal hdr10 conversion flag the same way RGB10A2 already does. This lets the user's shader fully control HDR10/PQ output rather than having the internal conversion applied on top. * Refactor HDR setting: replace EnableHDR bool + Linearize with HDRMode uint enum Replace the bool video_hdr_enable toggle and separate Linearize shader uniform with a single HDRMode setting (0=Off, 1=HDR10, 2=scRGB). Menu now shows HDR: Off/HDR10/scRGB instead of Enable HDR: ON/OFF. Slang semantic renamed from EnableHDR (float) to HDRMode (uint). Internal HDR pipeline shader updated to use HDRMode == 2u for scRGB path. * Vulkan: Fix scRGB HDR pipeline mode using emits_hdr10/hdr16 flags The runtime game_hdr_mode selection used back_buffer_format from get_pass_rt_format() to decide between mode 2 (sRGB→scRGB) and mode 3 (PQ→scRGB). However, the last pass's rt_format is overwritten with the swapchain format in shader_vulkan.cpp, so it always returned RGBA16F in scRGB mode regardless of the shader's #pragma format. Use vulkan_filter_chain_emits_hdr10/hdr16() instead, which are set before the rt_format overwrite and reliably reflect the shader's declared output format. * Vulkan/D3D: Add scRGB HDR pipeline with PQ→scRGB and sRGB→scRGB conversion Add HDRMode enum to shaders (0=passthrough, 1=HDR10, 2=sRGB→scRGB, 3=PQ→scRGB). Swapchain format selection driven by menu HDR mode: Off→B8G8R8A8, HDR10→A2B10G10R10+ST2084, scRGB→R16G16B16A16F+ExtLinearSRGB. ConvertHDR10_To_scRGB() decodes PQ, converts Rec.2020→Rec.709, and scales by 125 (10000/80 nits). GLSL and HLSL shaders updated in parallel. * D3D12: Port scRGB HDR support and fix shader pass HDRMode semantic - Port scRGB swapchain/format/colorspace selection from Vulkan driver - Fix 16-bit format: UNORM→FLOAT for R16G16B16A16 swapchain - Add HDR10↔scRGB mode change detection (triggers swapchain resize) - Set hdr_mode UBO at all 3 HDR draw points (stock, back_buffer copy, menu) - Fix back_buffer format to match shader's declared output (prevents 10-bit PQ data being quantised to 8-bit through R8G8B8A8 intermediate) - Fix shader pass semantics: enable_hdr(float)→hdr_mode(unsigned) to match SLANG_SEMANTIC_HDR (uint), and expand_gamut(float)→unsigned to match SLANG_SEMANTIC_EXPAND_GAMUT (uint) - Fix configuration.c: move video_hdr_mode from bool to uint settings section * GLSL/HLSL: Rename ConvertHDR10_To_scRGB, add kscRGBWhiteNits constant - Rename ConvertHDR10_To_scRGB() to HDR10ToscRGB() - Add kscRGBWhiteNits = 80.0 constant (scRGB reference white) - Replace all hardcoded 80.0 with kscRGBWhiteNits - Replace hardcoded 125.0 with kMaxNitsFor2084 / kscRGBWhiteNits * D3D11: Port scRGB HDR support and fix shader pass HDRMode semantic Port all scRGB HDR changes from D3D12 driver to D3D11: - Replace per-pass EnableHDR float with HDRMode uint semantic - Fix 16-bit swapchain format (R16G16B16A16_FLOAT, not UNORM) - Add swapchain bit depth selection: scRGB→16bit, HDR10→10bit - Add scRGB colorspace (DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709) - Dynamic swapchain resize on HDR mode changes (Off/HDR10/scRGB) - Full hdr_mode logic in shader preset loading and HDR pipeline - Mode-aware UBO values at all 3 upload sites (Point 1, Point 2, menu/overlay composite) with proper save/restore of hdr_mode - Per-pass HDRMode semantic uses video_info->hdr_mode (menu setting) not pipeline conversion mode, so shaders see 1 for HDR10 * Vulkan/D3D: Add colour boost (ExpandGamut) to scRGB HDR path The scRGB (HDRMode 2) non-scanlines path was ignoring the ExpandGamut setting entirely. Now applies gamut conversion matching the "Colour Boost" menu option: 0 (Accurate): k2020to709 — convert to clean Rec.709 1 (Expanded): kExpanded709to2020 * k2020to709 — slight saturation boost 2 (Wide): kP3to2020 * k2020to709 — wider boost 3 (Super): passthrough — no gamut conversion Updated in hdr.frag (Vulkan) and hdr_sm5.hlsl.h (D3D11). * GLSL/HLSL: Fix ExpandGamut matrices and scRGB scanline path Replace incorrect gamut conversion matrices that treated Rec.709 input AS the target colourspace (kP3to2020, kExpanded709to2020) with proper 709-to-target matrices (k709toP3, k709toExpanded709) computed as k709to2020 * k2020toTarget. Fix scRGB scanline path: skip InverseTonemap (HDR()) for HDRMode==2 since scRGB input is already in linear HDR, but keep LinearToSignal gamma 2.4 encode needed for CRT scanline simulation. Always apply k2020to709 back-conversion in scRGB paths regardless of ExpandGamut level, so Super mode gives maximum colour boost (matching HDR10 behaviour). * D3D12/D3D11: Fix HDR pipeline format mismatch for scRGB Fix D3D12 validation error where HDR pipeline state objects (PSOs) were hardcoded to R10G10B10A2_UNORM but the scRGB swapchain uses R16G16B16A16_FLOAT. Extract HDR PSO creation into d3d12_gfx_init_hdr_pipes() which creates both stock HDR shader pipes and sprite HDR pipes for a given render target format. Called at init and dynamically recreated whenever the swapchain format changes (e.g. switching between HDR10 and scRGB). Fix D3D11/D3D12 hidden HDR pass detection: only insert a conversion pass when the shader output has LOWER precision than the swapchain. RGBA16F writing to a 10-bit HDR10 swapchain is just a precision downsize the hardware handles naturally.
1 parent 68c878b commit f23eb89

25 files changed

Lines changed: 2732 additions & 1707 deletions

config.def.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,8 +500,8 @@
500500
/* Enable use of shaders */
501501
#define DEFAULT_SHADER_ENABLE true
502502

503-
/* Should we enable hdr when its supported */
504-
#define DEFAULT_VIDEO_HDR_ENABLE false
503+
/* HDR output mode: 0 = off, 1 = HDR10, 2 = scRGB */
504+
#define DEFAULT_VIDEO_HDR_MODE 0
505505

506506
/* The maximum nunmber of nits the actual display can show - needs to be calibrated */
507507
#define DEFAULT_VIDEO_HDR_MAX_NITS 1000.0f

configuration.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1920,7 +1920,6 @@ static struct config_bool_setting *populate_settings_bool(
19201920
SETTING_BOOL("video_font_enable", &settings->bools.video_font_enable, true, DEFAULT_FONT_ENABLE, false);
19211921
SETTING_BOOL("video_force_srgb_disable", &settings->bools.video_force_srgb_disable, true, false, false);
19221922
SETTING_BOOL("video_fullscreen", &settings->bools.video_fullscreen, true, DEFAULT_FULLSCREEN, false);
1923-
SETTING_BOOL("video_hdr_enable", &settings->bools.video_hdr_enable, true, DEFAULT_VIDEO_HDR_ENABLE, false);
19241923
SETTING_BOOL("video_hdr_scanlines", &settings->bools.video_hdr_scanlines, true, DEFAULT_VIDEO_HDR_SCANLINES, false);
19251924
SETTING_BOOL("video_vsync", &settings->bools.video_vsync, true, DEFAULT_VSYNC, false);
19261925
SETTING_BOOL("video_adaptive_vsync", &settings->bools.video_adaptive_vsync, true, DEFAULT_ADAPTIVE_VSYNC, false);
@@ -2556,6 +2555,7 @@ static struct config_uint_setting *populate_settings_uint(
25562555
SETTING_UINT("video_record_scale_factor", &settings->uints.video_record_scale_factor, true, 1, false);
25572556
SETTING_UINT("video_stream_scale_factor", &settings->uints.video_stream_scale_factor, true, 1, false);
25582557

2558+
SETTING_UINT("video_hdr_mode", &settings->uints.video_hdr_mode, true, DEFAULT_VIDEO_HDR_MODE, false);
25592559
SETTING_UINT("video_hdr_subpixel_layout", &settings->uints.video_hdr_subpixel_layout, true, DEFAULT_VIDEO_HDR_SUBPIXEL_LAYOUT, false);
25602560
SETTING_UINT("video_hdr_expand_gamut", &settings->uints.video_hdr_expand_gamut, true, DEFAULT_VIDEO_HDR_EXPAND_GAMUT, false);
25612561
#ifdef HAVE_NETWORKING

configuration.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ typedef struct settings
382382
unsigned video_bfi_dark_frames;
383383
unsigned video_shader_subframes;
384384
unsigned video_autoswitch_refresh_rate;
385+
unsigned video_hdr_mode;
385386
unsigned video_hdr_subpixel_layout;
386387
unsigned video_hdr_expand_gamut;
387388

@@ -690,7 +691,6 @@ typedef struct settings
690691
#endif
691692
bool video_wiiu_prefer_drc;
692693
bool video_notch_write_over_enable;
693-
bool video_hdr_enable;
694694
bool video_hdr_scanlines;
695695
bool video_use_metal_arg_buffers;
696696

gfx/common/dxgi_common.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ bool dxgi_check_display_hdr_support(DXGIFactory1 factory, HWND hwnd)
569569
{
570570
settings_t* settings = config_get_ptr();
571571
settings->flags |= SETTINGS_FLG_MODIFIED;
572-
settings->bools.video_hdr_enable = false;
572+
settings->uints.video_hdr_mode = 0;
573573

574574
video_driver_unset_hdr_support();
575575
}

gfx/common/dxgi_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ typedef struct ALIGN(16)
4141
unsigned expand_gamut; /* 0 */
4242
float inverse_tonemap; /* 1.0f */
4343
float hdr10; /* 1.0f */
44+
unsigned hdr_mode; /* 0 = off, 1 = HDR10, 2 = scRGB, 3 = PQ->scRGB */
4445
} dxgi_hdr_uniform_t;
4546

4647
enum dxgi_swapchain_bit_depth

gfx/common/vulkan_common.c

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,25 +2225,61 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
22252225
#ifdef VULKAN_HDR_SWAPCHAIN
22262226
if (vk->context.flags & VK_CTX_FLAG_HDR_SUPPORT)
22272227
{
2228-
if (settings->bools.video_hdr_enable)
2228+
unsigned video_hdr_mode = settings->uints.video_hdr_mode;
2229+
2230+
if (video_hdr_mode > 0)
22292231
vk->context.flags |= VK_CTX_FLAG_HDR_ENABLE;
22302232
else
22312233
vk->context.flags &= ~VK_CTX_FLAG_HDR_ENABLE;
22322234

22332235
video_driver_unset_hdr_support();
22342236

2235-
for (i = 0; i < format_count; i++)
2237+
if (video_hdr_mode == 2)
22362238
{
2237-
if ( (vulkan_is_hdr10_format(formats[i].format))
2238-
&& (formats[i].colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT))
2239+
/* scRGB mode: always use R16G16B16A16_SFLOAT + extended linear sRGB */
2240+
for (i = 0; i < format_count; i++)
22392241
{
2240-
format = formats[i];
2241-
video_driver_set_hdr_support();
2242-
break;
2242+
if ( formats[i].format == VK_FORMAT_R16G16B16A16_SFLOAT
2243+
&& formats[i].colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT)
2244+
{
2245+
format = formats[i];
2246+
video_driver_set_hdr_support();
2247+
RARCH_LOG("[Vulkan] Selecting R16G16B16A16_SFLOAT swapchain with scRGB colour space.\n");
2248+
break;
2249+
}
2250+
}
2251+
2252+
if (format.format != VK_FORMAT_R16G16B16A16_SFLOAT)
2253+
{
2254+
RARCH_WARN("[Vulkan] R16G16B16A16_SFLOAT + scRGB not available, falling back to HDR10.\n");
2255+
vk->context.flags &= ~VK_CTX_FLAG_HDR_SCRGB;
2256+
/* Fall through to HDR10 format search below */
2257+
}
2258+
}
2259+
2260+
if (video_hdr_mode == 1
2261+
|| (video_hdr_mode == 2 && format.format != VK_FORMAT_R16G16B16A16_SFLOAT))
2262+
{
2263+
/* HDR10 mode: always A2B10G10R10 + ST.2084 PQ.
2264+
* Shaders that output RGBA16F with PQ data get quantised
2265+
* to 10-bit by the swapchain — R16G16B16A16_SFLOAT is not
2266+
* paired with HDR10_ST2084 on any known implementation. */
2267+
vk->context.flags &= ~VK_CTX_FLAG_HDR_SCRGB;
2268+
2269+
for (i = 0; i < format_count; i++)
2270+
{
2271+
if ( (vulkan_is_hdr10_format(formats[i].format))
2272+
&& (formats[i].colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT))
2273+
{
2274+
format = formats[i];
2275+
video_driver_set_hdr_support();
2276+
break;
2277+
}
22432278
}
22442279
}
22452280

2246-
if (!vulkan_is_hdr10_format(format.format))
2281+
if ( !vulkan_is_hdr10_format(format.format)
2282+
&& format.format != VK_FORMAT_R16G16B16A16_SFLOAT)
22472283
vk->context.flags &= ~VK_CTX_FLAG_HDR_ENABLE;
22482284
}
22492285
else

gfx/common/vulkan_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ enum vulkan_context_flags
287287
VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN = (1 << 4),
288288
/* Whether HDR colorspaces are supported by the instance */
289289
VK_CTX_FLAG_HDR_SUPPORT = (1 << 5),
290+
/* scRGB mode: RGBA16F swapchain with extended linear sRGB colour space */
291+
VK_CTX_FLAG_HDR_SCRGB = (1 << 6),
290292
};
291293

292294
enum vulkan_emulated_mailbox_flags

0 commit comments

Comments
 (0)