feat(render3d): HDR10 output, and DLSS that stays still

HDR output: an HDR10 swapchain (A2B10G10R10, ST 2084 over BT.2020) when the
setting asks and the display offers it, with HDR metadata. The tonemap's HDR10
variant keeps the SDR picture up to a 200-nit paper white and rolls highlights
on to 1000 nits; the overlay's converts the interface to the same white. The
screen and LDR images go 10-bit with it; screenshots refuse while it is on.
OpenGL and the Vulkan SDR frame are unchanged. The instance asks for
VK_EXT_swapchain_colorspace. HDR metadata only where the loader has
vkSetHdrMetadataEXT: Streamline's interposer does not, and calling the thunk
crashed the game the moment the swapchain came up HDR10. PC 4K monitor: HDR10,
validation 0. R3D_HDR overrides the setting.

DLSS:
- the vertical jitter offset flips with Streamline's image (rows from the top):
  unflipped, Quality resolved the ground into concentric rings;
- preset K in every mode: the default M put Performance at 18 ms a frame at 4K
  on an RTX 3070 Ti (33 fps against 41 with DLSS off; with K, 60);
- the camera is jittered only while this frame holds a token and the last
  evaluate worked.
R3D_DLSS_PRESET, R3D_CAM_LOG (the camera and DLSS state a frame) and
R3D_NOGRAIN for measuring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-15 20:06:18 +03:00
parent 0224af64ab
commit 6525c11b3c
16 changed files with 273 additions and 29 deletions

View file

@ -46,6 +46,10 @@ var post_color_w: int = 0 # its size: the display's when DLSS upscaled
var post_color_h: int = 0
var post_color: int = 0 # the HDR colour the rest of post reads # the resolved depth, copied so passes can read it while drawing into the frame
var post_p_sharp: int = 0
var post_p_tone_hdr: int = 0 # the tonemap's HDR10 variant, made the first time HDR is on
var post_ldr_hdr: bool = false # post_ldr was made for HDR10 output (10-bit)
# the LDR image is 10-bit while the output is HDR10: PQ in 8 bits bands
function post_ldr_fmt() -> int { if gpu_hdr_active() { return GL_RGB10_A2 }; return GL_RGBA8 }
var post_sharpen: int = 0
var post_grain: int = 0
@ -112,7 +116,8 @@ function post_init(w: int, h: int) -> void {
post_ao = target_new(w, h, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, GL_LINEAR)
post_ao_blur = target_new(w, h, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, GL_LINEAR)
if post_p_ao == 0 { post_p_ao = r3d_program("fullscreen.vert", "ssgi.frag", ""); post_p_ao_blur = r3d_program("fullscreen.vert", "ssao_blur.frag", "") }
post_ldr = target_new(w, h, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false, GL_LINEAR)
post_ldr = target_new(w, h, post_ldr_fmt(), GL_RGBA, GL_UNSIGNED_BYTE, false, GL_LINEAR)
post_ldr_hdr = gpu_hdr_active()
post_depth_copy = target_new(w, h, GL_R8, GL_RED, GL_UNSIGNED_BYTE, true, GL_NEAREST)
post_prev = target_new(w, h, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, GL_LINEAR)
post_scene = target_new(w, h, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, GL_LINEAR)
@ -291,26 +296,37 @@ function post_bloom_pass() -> void {
}
function post_tonemap(color_tex: int) -> void {
var prog = post_p_tone
if gpu_hdr_active() {
if post_p_tone_hdr == 0 { post_p_tone_hdr = r3d_program("fullscreen.vert", "tonemap.frag", "#define HDR10\n") }
prog = post_p_tone_hdr
}
if post_ldr_hdr != gpu_hdr_active() {
let w = post_ldr.w; let h = post_ldr.h
target_free(post_ldr)
post_ldr = target_new(w, h, post_ldr_fmt(), GL_RGBA, GL_UNSIGNED_BYTE, false, GL_LINEAR)
post_ldr_hdr = gpu_hdr_active()
}
if post_auto { post_measure() }
target_bind(post_ldr)
gpu_depth_test(false)
gpu_use_program(post_p_tone)
r3d_bind_2d(post_p_tone, "u_hdr", 0, color_tex)
r3d_bind_2d(post_p_tone, "u_bloom", 1, post_bloom[0].color)
r3d_bind_2d(post_p_tone, "u_ao", 2, post_ao_blur.color)
u_f(gpu_uniform(post_p_tone, "u_ao_strength"), post_ao_strength)
u_f(gpu_uniform(post_p_tone, "u_gi_strength"), post_gi_strength)
u_f(gpu_uniform(post_p_tone, "u_exposure"), post_exposure)
gpu_use_program(prog)
r3d_bind_2d(prog, "u_hdr", 0, color_tex)
r3d_bind_2d(prog, "u_bloom", 1, post_bloom[0].color)
r3d_bind_2d(prog, "u_ao", 2, post_ao_blur.color)
u_f(gpu_uniform(prog, "u_ao_strength"), post_ao_strength)
u_f(gpu_uniform(prog, "u_gi_strength"), post_gi_strength)
u_f(gpu_uniform(prog, "u_exposure"), post_exposure)
var auto = F_ZERO
if post_auto and post_adapt_t != null { auto = F_ONE; r3d_bind_2d(post_p_tone, "u_adapt", 3, post_adapt_t[post_adapt_i].color) }
u_f(gpu_uniform(post_p_tone, "u_auto"), auto)
u_f(gpu_uniform(post_p_tone, "u_bloom_strength"), post_bloom_strength)
u_f(gpu_uniform(post_p_tone, "u_vignette"), post_vignette)
u_f(gpu_uniform(post_p_tone, "u_saturation"), post_saturation)
u_f(gpu_uniform(post_p_tone, "u_contrast"), post_contrast)
u_f3(gpu_uniform(post_p_tone, "u_wb"), fl(1.02), F_ONE, fl(0.97))
u_f3(gpu_uniform(post_p_tone, "u_lift"), fl(0.004), fl(0.004), fl(0.012))
u_f3(gpu_uniform(post_p_tone, "u_gain"), fl(0.99), fl(0.995), fl(1.0))
if post_auto and post_adapt_t != null { auto = F_ONE; r3d_bind_2d(prog, "u_adapt", 3, post_adapt_t[post_adapt_i].color) }
u_f(gpu_uniform(prog, "u_auto"), auto)
u_f(gpu_uniform(prog, "u_bloom_strength"), post_bloom_strength)
u_f(gpu_uniform(prog, "u_vignette"), post_vignette)
u_f(gpu_uniform(prog, "u_saturation"), post_saturation)
u_f(gpu_uniform(prog, "u_contrast"), post_contrast)
u_f3(gpu_uniform(prog, "u_wb"), fl(1.02), F_ONE, fl(0.97))
u_f3(gpu_uniform(prog, "u_lift"), fl(0.004), fl(0.004), fl(0.012))
u_f3(gpu_uniform(prog, "u_gain"), fl(0.99), fl(0.995), fl(1.0))
mesh_draw(post_fs)
# sharpen + grain onto the screen
gpu_fb_bind(gpu_screen_fb())
@ -319,7 +335,10 @@ function post_tonemap(color_tex: int) -> void {
r3d_bind_2d(post_p_sharp, "u_src", 0, post_ldr.color)
u_f2(gpu_uniform(post_p_sharp, "u_texel"), fr(1, post_ldr.w), fr(1, post_ldr.h))
u_f(gpu_uniform(post_p_sharp, "u_amount"), post_sharpen)
u_f(gpu_uniform(post_p_sharp, "u_grain"), post_grain)
# R3D_NOGRAIN=1: no film grain, so two frames of a still camera can be compared for what else moves
var grain = post_grain
if Os.has_env("R3D_NOGRAIN") { grain = F_ZERO }
u_f(gpu_uniform(post_p_sharp, "u_grain"), grain)
u_f(gpu_uniform(post_p_sharp, "u_time"), r3d_time)
mesh_draw(post_fs)
}