Add true HDR output, RT shadows, and PCSS shadows for vulkan#7554
Add true HDR output, RT shadows, and PCSS shadows for vulkan#7554The-E wants to merge 18 commits into
Conversation
BMagnu
left a comment
There was a problem hiding this comment.
Check out SDR_FLAG_TONEMAPPING_LINEAR_OUT.
It's basically poor-man's HDR output in the OpenGL pass, currently only used for the OpenXR pass, since the headset swapchain seems to do its own tonemap, or at the very least expects non-SDR input (it's highly possible that proper HDR input is in fact the correct thing to forward here, instead of truly linear data).
This should very likely be merged into this work, even if it means supporting OpenGL to some degree, rather than exist as a weird, second, half-baked HDR pass. Especially since proper handling across not only gameplay but also menus will fix #7181.
I'm not yet fully familiar with most of the Vulkan PR, but I assume that before this change, there is no handling of Cmdline_window_res, which this new buffer could then do in the future as well? I also assume that this PR is going to be the proper place to mirror #7484 for the SDL3 upgrade. If so, we should at least prepare / design this in a way that makes it easy to retrofit.
Squashed snapshot of the Vulkan backend as vendored into this branch (ce2d1e8..6ce5394, authored by Mara van der Laan with build/CI fixes by Taylor Richards). This work has its own upstream PR scp-fs2open#7553 (notimaginative:vulkan-pr-new) and is not otherwise original to true-hdr.
Squashed snapshot of true-hdr's own integration work: true HDR framebuffer output, wiring the shadow overhaul into the Vulkan backend (raytraced + cascaded shadow maps), logging refactors, log filter UI, and CI/build fixes.
CI hardcodes /usr/local/bin/ccache, but macOS runners on Apple Silicon hosts install Homebrew (and ccache) under /opt/homebrew instead, causing all four Mac configs to fail with "No such file or directory".
pixel_swizzle.{h,cpp}: collapse `namespace graphics { namespace util {`
into `namespace graphics::util {` (modernize-concat-nested-namespaces).
VulkanState.h/VulkanDraw.h: getClearColor()/setClearColor() only touch
gr_screen and other globals, no instance state, so make them static
(readability-convert-member-functions-to-static) and drop the
top-level const on getClearColor()'s return type
(readability-const-return-type).
RT shadows previously only shadowed the first directional light in the scene, even though multi-sun missions push more than one directional light into the engine. The TLAS raytraced shadows query against is already light-agnostic, so this extends both the deferred lighting pass and the forward MODEL shader to shadow every directional light (capped by a new Max Raytraced Shadow Lights option, configurable via the in-game Options menu or the Lab's Scene rendering options). Cascaded shadow maps are untouched: they're built for a single light only, so the deferred pass still limits itself to one shadowed directional light whenever CSM (not raytracing) is the active method. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_016DA6JHJ3twz2oHGQcZ1Udc
… in Vulkan backend - Consolidate shader flag setup logic in VulkanPostProcessingLighting to simplify configuration. - Fix dead field usage in VulkanDraw by ensuring correct image view selection for environment maps. - Add mip chain regeneration in VulkanRenderer::endRenderTarget to ensure correct mip levels for cubemaps.
- Add mip chain generation to VulkanDraw for cubemaps with multiple mip levels. - Fix potential issue with uninitialized mip levels above level 0 in deferred-f.sdr sampling paths.
Note that this is based on @notimaginative's PR (#7553) and should only be merged afterwards.
This also adapts the work done in @BMagnu's Shadow Overhaul PR (#7529), which is therefore another path dependency.
Summary
Adds optional HDR10 (PQ / ST.2084 + BT.2020) swap chain output to the Vulkan renderer, with paper-white / peak-luminance controls and an in-game calibration screen. When disabled (the default) or when running on a non-HDR display / the OpenGL renderer, behavior is unchanged.
This PR has since grown to also bring the Vulkan renderer's shadow rendering up to par with the shadow-rendering overhaul merged in from
lafiel/shadow_overhaul_2(dynamic cascade counts, PCF/PCSS, cockpit shadow cascades, and an optional raytraced-shadow path), since both landed on this branch together. Unlike the HDR work, the shadow overhaul itself affects both the OpenGL and Vulkan backends.What's included
Swap chain & metadata (
VulkanRendererSetup.cpp)VK_EXT_swapchain_colorspace(instance) andVK_EXT_hdr_metadata(device, when available).A2B10G10R10swap chain with theHDR10_ST2084color space, falling back cleanly to SDR/sRGB otherwise.VkHdrMetadataEXT(paper white / peak luminance, BT.2020 primaries) and re-applies it on swap chain recreation (resize / fullscreen toggle).Frame composition refactor (
VulkanRenderer.cpp,VulkanRendererLoop.cpp)RGBA16Fcomposition buffer at window resolution. All rendering (scene, UI, ImGui) now targets this buffer.Tonemap / output shaders (
tonemapping-f.sdr,gamma.sdr)hdr_modeuniform:0= existing SDR tonemap,1= HDR scene tonemap (exposure + headroom clamp relative to paper white, stored as extended sRGB in the fp16 composition buffer),2= HDR10 output encode (linearize, scale to nits, BT.709 -> BT.2020, PQ encode).Scene_ldr,Scene_luminance) widen to fp16 when HDR is active so highlights above paper white survive.Post-process anti-aliasing (
VulkanPostProcessingSMAA.cpp,VulkanPostProcessingLDR.cpp,smaa-*.sdr,fxaapre-f.sdr)Graphics.AAModelisted the SMAA presets unconditionally, but selecting one while running Vulkan silently produced zero anti-aliasing with no warning. All three passes (edge detection, blending-weight calculation, neighborhood blending) are now implemented, plus the area/search lookup texture uploads; the sharedSMAA.sdralgorithm body needed no changes, only thin per-backend wrapper shaders.[0,1]LDR input, which breaks onceScene_ldrcarries extended-range values; fixed by tonemapping a second, properly-compressed proxy buffer for edge/luma detection only, while the actual blended output still reads the real extended-range colors — so anti-aliasing works without sacrificing HDR headroom.Options (
2d.cpp/2d.h)Graphics.HDR(bool, requires restart),Graphics.HDRPaperWhite(nits, live),Graphics.HDRPeakLuminance(nits, live), persisted via the options system.Gr_hdr_output_activereflects whether the renderer actually negotiated an HDR10 swap chain (distinct from the request flag).Calibration screen (
ingame_options_ui.cpp/.h)OpenGL (
gropenglpostprocessing.cpp)HDR framebuffer readback (
1c3f8b7f9)Vulkan raytraced shadows — TLAS/BLAS (
63f8fa059)VulkanRaytracingManager: BLAS caching keyed to model lifecycle, per-frame TLAS build/rebuild (VulkanRaytracingTlas.cpp,VulkanRaytracingBlas.cpp) with dynamic capacity growth.VulkanBuffer,VulkanMemory).gr_vulkan.cpp).Shadow rendering overhaul, OpenGL side (merged from
shadow_overhaul_2)model_draw_listand the newshadow_render_listnow share a commonrender_queue<Derived, DrawEntryT>CRTP base (code/graphics/render_queue.h).Vulkan shadow parity port (
f35864fda)shader_get_shadow_cascade_defines()shared between both backends' shader compilers so cascade-count defines stay in sync.VulkanDrawManager::renderShadowDraw(): builds theSDR_TYPE_SHADOW_MAP_GENpipeline, bindsShadowMapData/ShadowCascadeParamsUBOs, and does a single instanced draw across all active cascades, replacing the old "spawn N instances" hack.VulkanShadowMaprewritten: dropped the VSM color target entirely, dynamic layer count (Num_shadow_cascades + Num_cockpit_shadow_cascades), and a depth-compare sampler for hardware PCF viasampler2DArrayShadow.GlobalBinding::ShadowCascadeParams,MaterialBinding::ShadowMapData), andVulkanDeletionQueue::queueAccelerationStructure()so TLAS rebuilds defer destruction of the old acceleration structure instead of destroying it synchronously out from under an in-flight command buffer.transformToShadowMap(), matchingsampler2DArrayShadowusage, matching vertex/fragment interface blocks, sharedshadowCascadeParamsUBO layout).shadow_cascade_params_init()needed to run eagerly, not lazily, sinceshadows_start_render()indexesShadow_frustumsbefore the lazy init point is reached) and a validation error from building the shadow TLAS inside an active render pass.#ifndef VULKAN.Logging
mprintf/scopednprintfcategories for consistency with the rest of the codebase, plus a new in-game log filter UI for toggling categories and browsing session logs.Fixes picked up along the way
gr_uniform_buffer_managers_init()was only ever called from the OpenGL init path, soUniformBufferManagerstayed null under Vulkan and the first draw needing a uniform buffer (title screen rendering) dereferenced it (SIGSEGV).ccache: No such file or directory— the workflow hardcodes the Intel Homebrew ccache path (/usr/local/bin/ccache), which doesn't exist on Apple Silicon-hosted GitHub runners (Homebrew installs under/opt/homebrewthere).configure_cmake.shnow falls back to a PATH-resolvedccachewhen the configured path isn't executable.Notes / limitations