The offscreen targets backing the scene texture and post-processing were
allocated once from gr_screen at gr_init() and never revisited. Anything drawn
while gr_screen was larger got clipped to their edge and stretched back over
the viewport. The game hits this on the SDL window-resize path
(osapi.cpp calls gr_screen_resize()); qtFRED hits it constantly, since its 3D
viewport is a resizable dock widget.
Add gf_resize_render_targets, called from gr_screen_resize(), and implement it
for OpenGL: grow the targets to cover the new gr_screen, rebuilding only the
resolution-dependent resources. The post-processing table, the compiled
shaders and the SMAA lookup textures are resolution-independent and stay
alive, which is what makes this cheap enough to run off a window drag. This
mirrors what the Vulkan backend already does in VulkanPostProcessor::resize();
Vulkan leaves the hook unset because recreateSwapChain() owns it there.
The allocation only ever grows: gr_screen_resize() is called every frame by
qtFRED and repeatedly by the briefing map widget, so tracking the high-water
mark avoids thrashing, and the shrunk state is already handled by
Scene_texture_u_scale/v_scale. The size is also clamped to the hardware limit
up front, so a viewport past that limit stops asking to be resized instead of
rebuilding every frame. If the larger allocation fails outright -- most likely
precisely when growing -- the resize stops before rebuilding the
post-processing targets on top of scene textures that no longer exist.
This replaces Gr_min_render_target_w/h, which sized the targets for the
largest attached display up front -- on a 4K display at 2x scaling that was
over a gigabyte of VRAM, allocated at launch, for a feature that is off by
default and may never be switched on.
Deletions now go through GL_state.Texture.Delete(): a freed texture name the
state cache still holds would make a later Enable() of the recycled name a
silent no-op. This mattered little when teardown only ran at shutdown. While
there, the scene teardown now releases everything setup allocates -- it was
leaking Scene_ldr/composite/luminance/Cockpit_depth and all six MSAA targets --
and post-processing shutdown releases the SMAA lookup textures.
Separately, fix the sampling extents that the above makes reachable:
- deferred-f.sdr turns gl_FragCoord into a G-buffer coordinate using
invScreenWidth/Height, which described gr_screen rather than the G-buffer.
Fixed in both backends; it is currently a no-op under Vulkan, where
resize() keeps the extent equal to gr_screen, but states the requirement
instead of relying on that.
- the MSAA scene-colour copy, the MSAA resolve and the fog pass sampled the
full [0,1] range of targets that are only filled to the u/v scale.
- fxaa-v.sdr derived its texcoord from vertPosition, ignoring the
sub-rectangle the draw call asked for.
Those extents now come from opengl_draw_full_screen_scene_texture() rather
than being open-coded, so a new pass cannot quietly reintroduce the bug. The
volumetric nebula pass is deliberately left unscaled and commented: it uses
fragTexCoord both to reconstruct a ray direction and to sample, which needs a
shader change to separate.
Co-Authored-By: Claude Opus 5 <[email protected]>
The shadow-mapped path's penumbra width was a fixed per-cascade value
(shadow_smoothness_scale()'s ceiling), so every occluder blurred its
shadow by the same amount regardless of how close it actually was to
the receiver -- unlike the raytraced path, which gets that for free
from traceShadowRayCone()'s real occluder distance. Close that gap
with PCSS: search nearby texels for occluders, then scale the filter
radius by how far away they were.
The blocker search (pcssBlockerSearch() in shadows.sdr) needs raw,
uncompared depth reads, which the existing PCF sampler can't do -- it's
bound with a compare mode for the hardware shadow-compare instructions
the filtered sampling itself relies on. So shadow maps now get a second
sampler, shadow_map_raw, bound to the same texture with compare mode
off; on OpenGL that needs a second sampler object and GL 3.3
(shadow_contact_hardening_supported()), Vulkan can always bind it.
Where the raw sampler isn't available, or the new Graphics.ShadowContactHardening
option is off, shadows_start_render() sets Shadow_penumbra_scale[cascade]
to -1 as a sentinel and pcssPenumbraRadius() (shadows.sdr) falls straight
back to the fixed radius, bit-identical to the pre-contact-hardening
behavior and skipping the extra texture reads.
When it is on, shadows_start_render() computes each cascade's penumbra
scale from its own frustum extents and the sun's tanθ; the shader turns
that plus the blocker search's average depth into a per-pixel radius,
clamped to [1 texel, the old fixed ceiling]. A blocker search that finds
nothing doesn't fall back to "no shadow" -- a sparse 8-tap search can
miss a thin occluder, so it instead runs the full filter at the hardest
(1-texel) radius, trading a touch of extra blur for not popping a real
shadow to nothing.
Drops RT_SHADOW_SUN_SIZE_CALIBRATION accordingly: that constant existed
only to fake a blocker-distance-driven penumbra on the shadow-mapped path
by pre-scaling the raytraced cone to match it, and the shadow-mapped path
now has a real blocker search of its own, so the raytraced cone goes back
to its physically correct size.
Raytraced shadow penumbras needed a hand-authored $SunAngularSize: in
stars.tbl, which no shipped content sets -- so in practice nothing ever
got a penumbra, and the shadow-mapped path's $Shadow Smoothness Factor:
had no relation to it at all. Derive a size from the sun bitmap instead
and drive both methods from the same number.
g3_render_rect_screen_aligned_2d() sizes the sun quad so its rad is the
tangent of the half-angle it subtends, making tan(angular radius) equal
to 0.05 * the mission's +Scale: * the emitting disc's fraction of the
bitmap. That fraction is the area of the pixels at or above 90% of the
brightest one, converted to an equivalent radius. Across 90 sun bitmaps
from retail, the MediaVPs, Blue Planet and BtA it lands at a median of
0.261 (retail's chunkier art at 0.487), and the threshold acts as a
constant factor rather than a per-bitmap judgement call -- Spearman rho
0.96 between a 90% and a 50% cutoff.
Taken literally that yields suns 3.9-8.5x Sol's apparent diameter once
+Scale: is folded in (median 1.55, up to 5.0 in retail's own missions),
so the measurement is scaled by 0.25 and clamped to 1.5 degrees, putting
typical content near Sol. Derivation is the default: $SunAngularSize: 0
asks for hard shadows, an explicit value still wins, and missions can
also set +AngularSize: on an individual sun to override both, with FRED
and QtFRED UI (validation and fallback included) to author it. Bitmaps
that can't be read -- and the deliberately blank sun bitmaps mods ship
to get a light source with no visible disc -- fall back to hard shadows
rather than to the widest possible penumbra.
That angular size now sizes the raytraced penumbra cone directly
(traceShadowRayCone() in shadows.sdr), with sample count following the
same Shadow_quality tier that picks the shadow map's resolution (1 ray
at Low, up to 16 at Ultra) rather than being a separate setting. The
shadow-mapped path's tabled smoothness values were tuned by eye against
Sol-sized retail/MediaVPs art, so shadow_smoothness_scale() in
shadows.cpp scales them by the active sun's size relative to Sol,
clamped to what the shadow map can represent (shadow_clamp_smoothness()).
The two methods still disagree about occluder distance -- the raytracer
uses the real one, the shadow map's tabled values stand in for a blocker
distance pinned to the cascade's own extent -- so RT_SHADOW_SUN_SIZE_CALIBRATION
in shadows.sdr is a constant factor that makes a given $SunAngularSize:
read as roughly the same softness either way; it's the number to revisit
if the shadow map ever grows a real blocker search.
Also adds RTAO (raytraced ambient occlusion) sharing the same TLAS as
the shadow rays, with lighting-profile-driven radius/strength, and lab
controls for RT shadow quality (Low/High), the local light cap, RT
shadow samples, RTAO, and an angular-size override for comparing both
shadow methods against the same sun. All are safe to change live: they
only gate which lights are picked as shadow casters or scale existing
uniforms while filling the per-frame light data.
Co-Authored-By: Claude Opus 5 <[email protected]>
- Move the overlay option strings to XSTR 1940 and 1941. The touch
overlay on master uses 1936 to 1939.
- Give FrameProfiler the main thread ID when it is made. Before, it
used the thread of the first event, which was a race.
- Let mouse motion go to the game while the pointer is over the
overlay, so that mouse flight does not stop there.
- Find the dynamic bindings of the PerDraw key with
dynamic_offset_slot(), as the other call sites do.
- Make disable_inside_open_scope count the GPU timestamp queries, so
that it fails if end() does not close the GPU event.
- Make the copy test copy the category, not bind a reference.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Skip complete::end for a scope that did not start. This fixes a crash
when Lua turns the overlay on while a scope is open. Track the GPU begin
event so that turning the overlay off mid-scope still closes it.
Use XSTR 1936 and 1937 for the overlay option. Master now uses 1932
and 1933.
Remove the default value of dynamicOffsets in bindDescriptorSet. Use the
PerDraw binding constants in the PerDraw key.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- Introduced `DYNAMIC_SLOT_INVALID` sentinel to replace the throw in `dynamic_offset_slot` for MSVC compatibility.
- Added `static_assert` checks to validate binding constants, ensuring compile-time validation for dynamic offsets.
- Updated comments to clarify design rationale and avoid potential misuse.
- Added `topologySupportsPrimitiveRestart` to determine restart eligibility for each topology.
- Updated `primitiveRestartEnable` to be conditionally set based on topology, addressing compatibility issues with Metal/MoltenVK.
- Prevented crash scenarios on macOS by ensuring pipelines are valid for strip/fan topologies requiring primitive restart.
- Consolidated dynamic descriptor offset logic into `DescriptorWriter::bindSets`, replacing repetitive `vkCmdBindDescriptorSets` patterns across Vulkan modules.
- Eliminated redundant descriptor binding operations for Material and PerDraw sets by centralizing logic in `bindSets`.
- Simplified descriptors by directly splitting offset changes from descriptor contents, avoiding unnecessary updates.
- Updated comments and documentation to align with the new approach while maintaining reference consistency.
- Applied optimizations across Vulkan modules (`VulkanPostProcessing`, `VulkanDescriptorManager`, etc.) to reduce per-draw overhead and improve readability.
The Material and PerDraw descriptor sets were reallocated and fully rewritten on
essentially every draw call. model_draw_list::render_buffer rebinds ModelData with
a fresh uniform buffer offset for each queued draw, and that offset was part of the
Material set's memoization key, so the cache in VulkanDrawManager::applyMaterial
missed every time - taking a frame-pool allocation, a full template write, the
16-entry material texture resolution and a vkUpdateDescriptorSets with it.
GenericData/Matrices did the same to the PerDraw set. renderShadowDraw was worse
still: all three sets allocated and rewritten per shadow draw, with no memoization
at all.
A large asteroid field (Solaris m21) measured 2566 descriptor sets and 19107
descriptor writes per frame at 1606 model draws, and grew an extra descriptor pool
chunk every frame.
Declare the bindings whose offset moves per draw - MaterialBinding::ModelData,
MaterialBinding::ShadowMapData, PerDrawBinding::GenericData and
PerDrawBinding::Matrices - as eUniformBufferDynamic. DescriptorWriter::setBuffer
now splits a dynamic binding's offset out of the descriptor, writing
{buffer, 0, range} and stashing the offset for vkCmdBindDescriptorSets'
pDynamicOffsets, so an offset-only change leaves the set's contents identical and
the memoization caches start hitting. Shader sources are unchanged.
VulkanStateTracker::bindDescriptorSet now compares the dynamic offsets as well as
the set handle. This is required, not an optimization: the common case is now the
same set rebound with a new offset, and the old handle-only redundancy check would
have skipped that bind and fed every draw the previous draw's uniforms.
renderShadowDraw memoizes its three sets the way applyMaterial does. It does not
invalidate applyMaterial's caches - those record what a set contains, the shadow
pass allocates its own sets rather than overwriting theirs, and it binds through
the state tracker, so the next material cache hit still rebinds correctly.
Also switch allocateFrameSet to the non-allocating allocateDescriptorSets overload;
the vector-returning one heap-allocated once per set, thousands of times a frame.
MAX_SETS_PER_POOL stays at 1024 - the per-frame pool growth it used to log was a
symptom of the churn, and one chunk now covers the scene outright.
Measured on that mission in a Debug build, A/B on the same tree with and without
this change:
descriptor sets/frame @ ~1370 draws 2566 -> 250-345
descriptor writes/frame 19107 -> ~1700-2300
median frametime 92.9/83.6 ms -> 72.0/66.5 ms
Render Buffer (profiler overlay) 24.68 ms (30.8%) -> 12.73 ms (18.4%)
Build Shadow Map 5.44 ms -> off the top-5 entirely
Release builds already inlined most of this away and are unchanged, as expected.
Verified with the validation layers (sync validation enabled): no new VUIDs versus
the same build without the change.
Co-Authored-By: Claude Opus 5 <[email protected]>
- Integrated per-backend diagnostic counters into the ImGui frame profiler overlay, triggered by `-gr_debug`.
- Consolidated and cleaned up Vulkan rendering state and descriptor management for compatibility with ImGui's Vulkan pipeline.
- Enabled uniform buffer and draw statistics reporting for enhanced performance insights during debugging.
The Vulkan light shaft pass reuses OpenGL's sun position formula, whose
texture V grows upward. The Vulkan fullscreen triangle in post-v.sdr has
V = 0 at the top of the screen, and ls-f.sdr steps from fragTexCoord
toward sun_pos in that space, so the shafts radiated from a point mirrored
vertically from the sun. Flip the vertical coordinate before upload.
Co-authored-by: Saga port <saga-port@invalid>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* android build support
* Update es_compatibility.h
* Support reporting flags in android
* include pref_path in json v2
* add vulkan headers
* shaderc includes
* remove headers
* force use prebuilt vulkan
* fix vulkan inverted screen
* fix find sdl3 precompiled android
* Update CMakeLists.txt
* replace sdl2 android function names for sdl3 versions
* change sdl includes in osapi
* fix sdl3 includes, again
* rename function to sdl3 name
* remove unused definition
* copy libshaderc_shared.so to output in android
* do not look for vulkan-loader in android
* revert changes to SDL2.cmake
* add comment to vulkan pretransform setup
* cleanup maxsize function
* rename new android functions in osapi
* android trap back button
* Remove calculate_max_resize_max_size
* touch ui opacity control
* change opacity control to int
* fix opacity slider
* Corrected android working folder fallback and set borderless mode as default
* Implement shared helper for JNI calls
* Add version tag and sha256 to android prebuilt libs
* vulkan android check eSuboptimal
* An already requested rebuild also avoids redundant Android surface queries
* add cache bool to cmakelist
* set string ids
OpenGL's shadow pass calls gr_zbias(-1024), which sets a polygon offset of
factor 1, units 1024 (gropengltnl.cpp, gropengl.cpp). The Vulkan shadow pass
set a constant bias of -1024 with no slope term: the opposite sign, on a
depth buffer that uses the same less-than test. Use OpenGL's values.
Co-authored-by: Dustinspace217 <[email protected]>
Each VulkanRenderFrame signaled its own rendering-finished semaphore and
presented with it. The frame's fence only says the submit has finished, not
that the presentation engine has consumed the semaphore, so the next submit
on that frame could signal a semaphore that is still pending, which the
validation layer reports as VUID-vkQueueSubmit-pSignalSemaphores-00067.
Move the semaphore to the renderer, one per swap chain image, recreated with
the swap chain, and pass the one for the acquired image to submitAndPresent.
Acquiring an image again means its previous present has been consumed.
Co-authored-by: Dustinspace217 <[email protected]>
irrmap-f.sdr declares the Material set's texture binding as a samplerCube,
but vulkan_calculate_irrmap filled the array with the 2D fallback and only
replaced element 0 with the environment map, which the validation layer
reports as VUID-vkCmdDraw-viewType-07752 on every irradiance pass. Fill it
with the cube fallback instead.
Co-authored-by: Dustinspace217 <[email protected]>
VulkanDistortion creates a linear/repeat sampler for its noise textures,
matching OpenGL's GL_REPEAT on Distortion_texture, but getTextureInfo
handed out the post-processing linearSampler instead, which clamps. The
sampler that was built for it is now the one used.
Co-authored-by: Dustinspace217 <[email protected]>
Squad logos are drawn with a material depth bias of 1 so they sit on top
of the hull. Under Vulkan the bias was written to the dynamic state but
the pipeline was built with depth bias disabled, so it never applied.
applyMaterial now calls vulkan_zbias with the material's bias before
building the pipeline config, the same way opengl_tnl_set_material calls
gr_zbias.
vulkan_zbias also passed its two values in the wrong order:
glPolygonOffset takes (slope factor, constant units) while
setDepthBias here takes (constant, slope).
Co-authored-by: Dustinspace217 <[email protected]>
vulkan_calculate_irrmap ended the swap chain render pass first and then
had four early returns (pipeline, UBO create, UBO allocate, UBO map)
before resumeSwapChainPass, so any of those failures left the rest of
the frame recording draws outside a render pass. End the pass only after
the setup that can fail.
Co-authored-by: Dustinspace217 <[email protected]>
A BLAS was destroyed immediately when its model was unloaded or when the
vertex heap grew and the BLAS had to be rebuilt, while the TLAS of a
frame still on the GPU could reference it by device address. Queue the
acceleration structure and its buffer on the deletion queue instead, the
same way ensureTlasCapacity already retires the TLAS. The deletion queue
is flushed after ray tracing shuts down, so shutdown defers too.
A built TLAS also kept pointing at retired BLASes for later frames: the
live shadow TLAS binding is only refreshed when a mission frame rebuilds
it, and buildTlas keeps the previous TLAS when it has no instances. With
ray-traced shadows on, flying a mission and then leaving and re-entering
the tech room (which frees every model on exit) bound such a TLAS on
every tech room frame. So when BLASes are retired, the built frame TLASes
are retired with them and the binding goes back to the empty fallback
TLAS until the next rebuild. onModelUnloaded reports whether it retired
anything, so the binding refresh only runs then.
Co-authored-by: Dustinspace217 <[email protected]>
The runtime shader compiler cached SPIR-V on disk without checking the
write, and accepted any cached file whose size was a multiple of four.
A file cut short by a crash or a full disk was handed to the driver as a
shader module. With half-length cache files in place, master crashes in
the NVIDIA driver on entering SM1-01 under -vulkan.
Cached files are now checked before use: the SPIR-V magic word, and the
instruction word counts must end exactly at the end of the file on an
OpFunctionEnd. A file that fails is logged, recompiled and rewritten.
Fresh compiler output is checked the same way before it is cached, so a
module the check would reject is never written, and the log says so.
New cache files are written to a temporary file unique to the writer and
renamed over the old .spv only after a complete write and close, so a
game crash, or a second game writing the same shader, never leaves a
partial .spv. A power loss still can; the read check catches that.
purgeOldCache now also removes old temporary files.
Co-authored-by: Dustinspace217 <[email protected]>
The default-material shader transforms each vertex by the model matrix
before testing it against the clip plane. Vulkan always passed the
identity, so a clip plane given in world space (for example the warp
plane on a squad logo) was tested against model-space positions. OpenGL
passes gr_model_matrix_stack.get_transform(); do the same here.
Co-authored-by: Dustinspace217 <[email protected]>
Under Vulkan a mission's $Environment Map was never uploaded: the
irradiance and lighting passes only use ENVMAP when its texture slot
already has an image view, and nothing created one for a bitmap that was
only bm_load'ed. OpenGL uploads it the first time it binds it as a
cubemap.
Add VulkanTextureManager::ensureCubemapUploaded(), called from both
passes. It locks the map the way OpenGL does for TCACHE_TYPE_CUBEMAP.
The lock uploads through bm_data(), which now sends uncompressed DDS
cubemaps (bm_is_compressed() returns 0 for them; comp_type identifies
them) to uploadCubemap() as DXT ones already went. If the slot still
holds no cube after the lock, because the data was already resident, it
uploads it directly. A map that cannot be used is remembered by bitmap
signature (handles are reused across missions) and warned about once in
debug builds, and the irradiance pass is skipped instead of sampling a
non-cube view.
uploadCubemap() also assumed uncompressed cubemaps had one mip level and
read the six faces back to back. DDS stores face0[mip0..mipN],
face1[...], so faces 1-5 read the tail of face 0's mip chain. It now
uses the file's mip count, expands 24-bit data over the whole chain,
generates a chain for a one-level uncompressed cubemap as OpenGL does
(the lighting pass samples by roughness), copies mip 0 for every array
layer, cleans up the image when the staging map fails, and clears
isCubemap as soon as it retires the old image.
Co-authored-by: Dustinspace217 <[email protected]>
* Vulkan: bind all four model-matrix attribute locations for decals
The Vulkan shader input mask had one bit per listed attribute, so the
pipeline dropped the MATRIX4 attributes at locations 9-11 and the decal
vertex shader read three of its four instance-matrix columns from unbound
inputs. No decal was ever drawn in Vulkan.
The mask now covers four locations for VATTRIB_MODEL_MATRIX. It is built
by shader_vertex_input_mask() in shader_types so both Vulkan call sites
share it and a unit test covers it without a Vulkan build.
Fixes#7866
* Vulkan: build the decal pipeline with the material's shader flags
The decal pipeline config set the shader type but not the flags, so the
decal shader variant was always compiled without USE_NORMAL_MAP and fell
back to screen-space derivative normals for angle rejection. Far more of
the decal volume passed the angle test than in OpenGL, which lit the
decals brighter and wider. The pipeline now takes the flags the material
reports, as the other draw paths already do.
* Review fixes: exact input-mask test, decal flag polarity test, mask width check
Pin the full mask per shader type instead of the presence of the matrix
bits, so an expansion of any other attribute fails; pin that a decal
without a normal map is the one that reads the scene normal buffer; add a
compile-time bound on the mask width; trim two comments.
---------
Co-authored-by: Dustinspace217 <[email protected]>
* GLES Shaders and Vulkan Clip fallback
* clip fallback: change defined name, set from engine side and invert usage
* Proposed changes and fixes
Fixed:
-The OpenGL ES option stays on when OpenGL is off (issue #7984)
-Missing precision in copy-f, volumetric-f and smaa-resolve-f
-msaa-f including z-compress before precision quantification
-missing float literals in volumetric-f
-Fixed decals blending
-Restored glBindFragDataLocation() for use in legacy external shaders
-Enabled GL_ARB_explicit_attrib_location ext for GLSL version below 330 in GL
-Guarded enabling/disabling of GL_CLIP_DISTANCE0_EXT and GL_DEPTH_CLAMP
-Restored PI in lighting.sdr
-removed "External shaders only" indication from cmakelists.txt
Vulkan never read the material's texture addressing, so MR_FORCE_CLAMP
skyboxes were sampled with a wrapping sampler, producing seams. Set it
on every material draw, as OpenGL does in opengl_tnl_set_material().
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Introduced `shadow_ray_params` to simplify ray origin and cull mask management.
- Shifted shadow ray origins to TLAS-relative space for improved precision and consistency, especially for cockpit rendering.
- Refactored cascade bind functions, adding `shadow_cascade_params_bind_deferred()` for consolidated offset/count handling in deferred lighting.
- Expanded TLAS to include cockpits and additional object types, ensuring parity with rasterized shadows.
- Introduced per-instance cull masks to address cockpit self-shadowing issues.
- Improved Vulkan depth isolation for cockpit rendering via backup/restore mechanism, ensuring correct depth sampling in later passes.
- Extracted reusable helper functions for cockpit rendering state logic.
Mission flags are saved to the mission file as the full 64-bit flagset value, but three issues prevented anything larger than 32 bits from being saved as intended:
* `stuff_flagset` parsed the value with `stuff_long`, where `atol` clamps anything above `LONG_MAX`
* `flagset::from_u64` cast the incoming value to `unsigned long`, truncating bits 32-63
* `flagset::to_u64` used `bitset::to_ulong` where it should use `bitset::to_ullong`
Fix these issues and add `static_assert` in the flagset functions to defend against future issues. Also use the proper UINT64_T_ARG where it should be used.
This was first triggered by `Limited_support_rearm_pool`, the first mission flag to occupy bit 31. Only mission flags are affected; the other flagset serialization paths were audited and are safe.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Refactor shadow flag handling in shaders to improve compatibility with small-shader mode. Add support for Vulkan's `-no_large_shaders` switch to split model ubershaders for better driver compatibility. Adjust related comments and preprocessor checks for clarity.
* Fix Vulkan render target readback orientation to match OpenGL behavior
* Add render target pass resumption after Vulkan shadow rendering
Ensure that Vulkan render targets resume rendering properly after shadow passes, particularly for off-screen targets like SCPUI's 3D previews, to prevent unintended content leakage onto the screen.
* Skip alpha writes for additive batched bitmaps in render targets to preserve coverage and prevent opaque artifacts.
* Add external subpass dependencies to Vulkan render passes to fix read/write hazards
Introduce explicit memory dependencies for color attachments sampled between passes to prevent flickering and graphical artifacts caused by read-after-write and write-after-read hazards.
* Support fractional alpha values in createColor function and preserve 0-255 compatibility
Address FXAA/SMAA shader selection logic by aligning it with the current anti-aliasing mode. Fix undefined behavior for discarded non-edge pixels during the FXAA main pass by transitioning Scene_ldr to a load-preserving layout. Optimize compatibility across IMR and tiled GPUs, preventing visual artifacts.
Introduce load-variant render passes to Vulkan textures, enabling content preservation during mid-frame readbacks. Changes include creating `renderPassLoad`, flushing command buffers for readbacks, and resuming rendering into targets with `loadOp=eLoad` to avoid clearing previously rendered content.
This commit introduces optional depth/stencil attachments for Vulkan render targets, enabling z-testing and addressing earlier limitations where depth-tested geometry rendered in submission order without depth attachments. Changes include resource allocation, attachment handling, and layout transitions.
* Shadow overhaul round 2: PCSS, dynamic cascades, cockpit/instanced shadows
Squashed snapshot of lafiel/shadow_overhaul_2's continuation work
(d3922aa87..010f9d2d9), which has no separate upstream PR of its own.
* Add Vulkan rendering backend
Squashed snapshot of the Vulkan backend as vendored into this branch
(ce2d1e85f..6ce539463, authored by Mara van der Laan with build/CI
fixes by Taylor Richards). This work has its own upstream PR #7553
(notimaginative:vulkan-pr-new) and is not otherwise original to true-hdr.
* Add true HDR output, RT shadows, and PCSS shadows for Vulkan
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.
* refactor: update Vulkan logging calls to use `nprintf` for scoped logging
* fix: correct shadow cascade count in Vulkan logging calls
* fix: fall back to PATH-resolved ccache when CCACHE_PATH is stale
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".
* Fix vulkan texture readback to correctly handle transparent pipelines
* Put the pixel swizzles into a util file
* OOB safety check
* Fix fog handling
* Fix MSAA in Vulkan
* fix: enable SMAA for vulkan
* adapt the post-processing AA shaders to hdr
* fix clang-tidy warnings: concatenate nested namespaces, static methods
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).
* fix clang-tidy warning
* Extend raytraced shadows to all directional lights
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
* refactor: improve shader flag handling and render target finalization 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.
* fix: ensure correct mip chain initialization in cubemap rendering
- 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.
* fix: don't crash on startup when environment locale is unavailable
std::locale("") pulls in the process's environment locale categories
(LC_CTYPE, LC_MONETARY, LC_PAPER, etc.), and throws std::runtime_error
if any of them names a locale not installed on the host (e.g.
mismatched regional-format overrides). Since SCP_default_locale is
constructed during static initialization, before main() and before any
of our own exception handling exists, an uncaught throw here aborted
the whole process before the game even started. Fall back to the
classic "C" locale instead, which is sufficient since this locale is
only ever used for ASCII case-folding (SCP_toupper/tolower/isdigit).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
* `feat: add raytraced shadows for local lights and optimize TLAS management`
* Make the Commandline authoritative for -res and -vulkan / -opengl
* address BMagnu's comments on the original PR
* update prebuilt version hash to 62c89ef6
* refactor: standardize graphics API handling and document Vulkan limitations in qtFRED
- Updated `AboutDialogModel.cpp` to use `GraphicsAPI` enum for better clarity.
- Ensured qtFRED falls back to OpenGL when Vulkan is requested.
- Added detailed documentation in `qtfred/README.md` regarding Vulkan backend limitations and requirements.
* chore: include cmdline library in main and mission management modules
* docs: add known issue for blank render viewport under Wayland in README.md
* fix CI issues
* chore: add `GR_DEBUG_SCOPE` markers across Vulkan post-processing and rendering paths for improved debugging
* feat: improve Vulkan HDR pipeline with separate encode paths for SDR and HDR10
- Added an HDR10-specific output encode pass in `VulkanRenderer` and `VulkanPostProcessor` that applies PQ/BT.2020 transfer.
- Introduced an optimized SDR fallback path with direct blit support for devices that can skip shader draws.
- Updated `m_sceneLdrCompressed` to provide [0,1]-clamped proxies for HDR edge detection.
- Refactored `copyImageToImage` and added `blitImageToImage` to allow format conversions and optimized transfers.
- Replaced `hdr_mode` with explicit HDR10 flags and data structures for clearer state handling.
* refactor: encapsulate SMAA processing and refactor VulkanPostProcessor
- Moved SMAA-specific logic into `VulkanSMAA` class for better separation of concerns.
- Simplified `initSMAA`, `shutdownSMAA`, and `executeSMAA` logic by leveraging `VulkanLDR` and `PostProcessContext` abstractions.
- Improved initialization and cleanup code with clearer resource lifecycle management.
- Updated debug markers and messages to reference `VulkanSMAA`.
- Enhanced flexibility of fullscreen triangle rendering API with additional parameters (`sampleCount`, `bindGlobalSet`).
* Update Vulkan porting credits in AboutDialogModel
* fix CI issues again
* fix: correct Y-flip in viewport setup for Vulkan light volumes
- Adjusted viewport height to negative value to account for G-buffer render pass's negative Y-axis convention.
- Ensured proper alignment of point and tube light volumes with actual light positions.
* feat: add user-adjustable gamma correction to SDR and HDR10 output paths
- Added gamma correction to HDR10 path, applying user settings before PQ/BT.2020 encode.
- Replaced the SDR blit-only path with an encode pass that applies gamma correction, ensuring consistent behavior across devices.
- Refactored VulkanPostProcessor and associated shaders to support gamma slider adjustments for both SDR and HDR output.
* fix: ensure proper uniform and cull mode setup for Vulkan sphere rendering
- Added uniform buffer setup and default material uniforms in `vulkan_draw_sphere`.
- Disabled face culling in material definition for sphere rendering to align behavior with OpenGL.
* Ensure memory flushes after UBO updates and add explicit pipeline barrier for MoltenVK compatibility.
* fix: add clear pass for SMAA edge detection to prevent persistent artifacts
* feat: add Vulkan debug-utils messenger, opt-in sync validation, frame diagnostics
Groundwork for the renderer-review fix series (.agents/plans/
vulkan-renderer-review-fixes.md, Phase 0):
- Register a VK_EXT_debug_utils messenger (preferred) instead of only the
deprecated VK_EXT_debug_report callback, which is kept as a fallback for
loaders without debug-utils. The messenger config is also chained into
instance creation so vkCreateInstance-time messages are captured.
Validation errors/warnings now go to the main log via mprintf.
- New -gr_sync_validation cmdline flag (implies -gr_debug): chains
VkValidationFeaturesEXT with synchronization validation into instance
creation when VK_LAYER_KHRONOS_validation is available. This is the
regression net for the synchronization fixes in the following commits.
- Frame stats now report descriptor sets/writes allocated per frame and the
total pipeline count (descriptor-manager counters; DescriptorWriter::flush
moved out-of-line to report into the manager).
Co-Authored-By: Claude Fable 5 <[email protected]>
* refactor: unify Vulkan render-pass begins, clamp in-pass clears, add per-frame UBO ring
Phase 1 of the renderer-review fix series: shared helpers that the
correctness fixes build on.
- New VulkanRenderer::beginTrackedRenderPass(PassBeginDesc): every render-pass
begin on the frame command buffer now records vkCmdBeginRenderPass and
updates the state tracker (pass, color attachment count, sample count,
render area, viewport) in one place. Converted: setupFrame,
beginSceneRendering (both branches), resumeSceneRendering,
endSceneRendering, beginRenderTarget (viewport kept for gr_set_viewport),
resumeSwapChainPass, plus new resumeScenePassAfterCopy() shared by
copyEffectTexture/copySceneDepthForParticles and
rebeginSwapChainPassAfterReadback() shared by both readbackFramebuffer
exits (~200 lines of duplicated begin/clear/viewport blocks removed).
- The state tracker records the active render area; all four
vkCmdClearAttachments sites clamp their rects against it and skip empty
results. Previously gr_screen-sized clear rects could exceed a smaller
off-screen render target's render area, which is invalid API usage.
- New PerFrameUboRing utility (VulkanPerFrameUbo.h/.cpp): a persistently
mapped uniform buffer with one slot region per frame in flight and a
self-flushing alloc(). Used by the next commit to fix the cross-frame
UBO races.
Deliberate behavior deltas (all safe-direction): the helper always sets
colorAttachmentCount/sampleCount (some sites previously inherited stale
values), and resumeSwapChainPass/resumeSceneRendering now also reset the
standard flipped viewport instead of leaving the previous one.
Co-Authored-By: Claude Fable 5 <[email protected]>
* fix: eliminate Vulkan cross-frame UBO/buffer races, make gr_sync honest
Phase 2 of the renderer-review fix series: with two frames in flight, several
host-visible resources were rewritten by the CPU while the previous frame's
GPU work could still be reading them.
- The post-processing scratch UBO is now a PerFrameUboRing (per-frame-in-
flight slot regions, persistently mapped, self-flushing alloc). All the
per-pass map/unmap/cursor bookkeeping in bloom/LDR/fog/SMAA is deleted;
the cursor resets exactly once per frame via the new
VulkanPostProcessor::beginFrame() hook in setupFrame.
This also fixes two latent bugs: (a) fog consumed scratch slots mid-scene
and bloom's later same-frame cursor reset overwrote them before submission
whenever fog+bloom were both active; (b) volumetric_fog_data (288 bytes)
overflowed the 256-byte slots - the memcpy spilled into the neighboring
slot and the shader's bound range ended before the aspect/fov/noise
fields. Slots are now 512 bytes (static_assert at the fog call site) and
the per-frame budget is 32 slots.
- The dedicated tonemap, output-encode, and MSAA-resolve UBOs (each a single
instance rewritten per frame, i.e. racy; the MSAA one also never flushed)
are deleted entirely - those passes build their structs on the stack and
allocate from the scratch ring.
- Static-buffer rewrites orphan instead of racing: getVkBuffer() stamps a
last-used frame number, and a full-content updateBufferData() on a Static
buffer used within the last MAX_FRAMES_IN_FLIGHT frames defers the old
VkBuffer to the deletion queue and writes a fresh one. Offset writes
(GPU-heap appends to fresh regions) and PersistentMapping buffers (the
engine holds their mapped pointer and syncs via gr_sync) are exempt.
- gr_sync_wait now honors its contract: VulkanRenderFrame::waitForFinish
takes a timeout and reports expiry without consuming the fence;
VulkanRenderer::waitForFrame returns false for fences taken during the
still-recording frame (previously it waited on a stale or idle slot and
reported success) and resolves the frame->slot mapping correctly. This
makes UniformBufferManager's segment-fence throttling actually protective
on Vulkan for the first time.
Co-Authored-By: Claude Fable 5 <[email protected]>
* Refactor framebuffer and target lifecycle: centralized creation/resizing logic, improved resource management across Vulkan post-processing subsystems.
* Improve Vulkan cross-frame sync with explicit access masks and validation updates
- Unified Vulkan validation flag handling in setup, separating engine debug output from synchronization validation.
- Specified access masks and stage flags to address cross-frame WRITE_AFTER_WRITE vulnerabilities, notably in G-buffer, post-processing, and MSAA resolve paths.
- Adjusted subpass dependencies and image layout transitions to prevent read-after-write and write-after-write hazards, detected using `-gr_sync_validation`.
- Fixed uniform buffer and barrier transitions to ensure proper ordering in render passes across frames.
* Refactor Vulkan render-pass dependencies and sync stages for clarity and future testing
- Clarified dependency logic for cross-pass and cross-frame synchronization, ensuring explicit stage and access flag comments match observed behavior.
- Adjusted subpass dependencies to decouple render-pass compatibility definitions from dependency assumptions.
- Highlighted MoltenVK-specific issues and retained workarounds with notes for re-testing on macOS hardware.
- Updated synchronization barriers and validation notes to address historical inconsistencies across platforms.
* Remove transfer queue support; consolidate all operations into the graphics queue
- Removed transfer queue handling, simplifying buffer and queue logic to use the graphics queue for all transfer operations.
- Updated buffer sharing modes and removed related code paths and parameters for transfer queue families.
- Introduced exclusive buffer access, noting async transfer and queue ownership transfers are future work.
- Limited enabled device features to only those actively used by the renderer, reducing unnecessary feature requests (e.g., robustBufferAccess).
* Refactor frame advancement and descriptor management in VulkanRenderer
- Removed `endFrame()` from `VulkanDescriptorManager`, replacing it with `setCurrentFrame(frameIndex)` for explicit frame synchronization.
- Centralized frame index advancement within `VulkanRenderer` to ensure consistency across components.
- Invalidated cached descriptor set bindings at render-pass boundaries to handle raw recorder staleness correctly.
- Optimized pipeline descriptor rebind logic by removing redundant clears during pipeline changes.
* Refactor `PipelineConfig::hash` and Global descriptor set management for improved hashing and memoization efficiency
- Replaced inefficient shift-based hashing in `PipelineConfig::hash` with a robust `hashCombine` strategy to minimize bucket collisions.
- Introduced `maskBits` helper to streamline bitmask creation for boolean fields.
- Implemented memoization for Global (Set 0) descriptor sets, reducing redundant rebuilds across frames and improving performance.
- Added dirty state tracking (`m_globalSetDirty`) and invalidation logic for Global set updates following relevant input changes (e.g., TLAS or UBO modifications).
* Refactor Vulkan post-processing output logic: extracted shared `encodeToSwapChainPass` method to streamline HDR and SDR output paths.
* Optimize Vulkan descriptor set management with per-frame memoization
- Added memoization for Material (Set 1) and PerDraw (Set 2) descriptor sets to reuse cached sets across frames if inputs remain unchanged.
- Implemented precise input tracking for Material and PerDraw descriptor sets to detect and rebuild only when necessary.
- Reduced redundant Vulkan descriptor allocations and writes, improving performance on repeated or batched draw calls.
* Fix Vulkan access masks for G-buffer and scene pass transitions
- Added `eColorAttachmentRead` to access masks in multiple barriers to ensure proper synchronization for load operations (e.g., `loadOp=eLoad`) across render passes.
- Prevented `READ_AFTER_WRITE` synchronization validation issues in deferred and multi-pass rendering scenarios.
- Updated comments to clarify the reasoning behind the access mask changes and load dependencies.
* Refactor Vulkan resource management and indexed draw logic
- Consolidated destruction queue methods in `VulkanDeletionQueue` using a shared `queueHandle` template to reduce redundancy.
- Introduced `ModelDrawParams` structure and `computeModelDrawParams` function to unify indexed draw parameters for models and shadows.
- Simplified draw logic by eliminating repeated calculation of indexed draw parameters and improving code reuse.
* Add `releaseAnimationSlotRef` for ref-count handling of shared animation texture arrays
- Introduced `releaseAnimationSlotRef` to manage reference counting for shared animation texture-array slots, preventing premature destruction of shared resources.
- Updated `flushTextures` and related methods to integrate `releaseAnimationSlotRef` for shared slot cleanup.
- Refactored staging buffer creation into `createStagingBuffer` for 3D texture uploads and shared path consistency.
- Enhanced texture upload logic with improved tracking and logging for re-upload cases, aiding preloading gap analysis.
- Added instrumentation for on-demand mid-frame texture uploads in `FrameStats`.
* Refactor Vulkan resource and texture management, extend debug logging
- Updated smart pointer usage for consistency across Vulkan resource managers (`std::make_unique`).
- Removed redundant `cubeImageView` member and related cleanup logic; unified sampling view management.
- Added per-instance debug log counters (avoiding static state) for Vulkan texture uploads and bindings, improving visibility during initialization and run-time.
- Optimized `VulkanRenderer` property/feature getters by caching physical device properties and features during device creation.
- Improved memory region flushing logic for out-of-date offsets, aligning with Vulkan Memory Allocator behavior.
- Refactored render pass state tracking to reapply dynamic state reliably after mid-frame render passes.
- Enhanced validation and error handling for mismatched update texture formats.
* fix CI build warning
* Fix on-demand YUV texture locking and set alpha uniform for movie rendering in Vulkan
- Corrected bitmap lock type from `BMP_TEX_OTHER` to `BMP_AABITMAP` for YUV textures during on-demand loading.
- Updated movie rendering to bind alpha from the `MovieData` UBO (set 2, binding 4) for Vulkan, aligning behavior with OpenGL implementation.
* revert inadvertent changes introduced by a squash commit
* address ci issues
* remove wip stuff from code comments that make no sense to people who aren't me
* Remove unused LD_LIBRARY_PATH export and fix shader shadow map sampling.
* Refactor batched rendering uniform setup in Vulkan
- Removed unused fields from `genericData` uniform block, simplifying shader definitions.
- Updated Vulkan material uniform handling to align with OpenGL's batched rendering logic.
- Introduced `batched_data` struct for streamlined UBO updates, improving maintainability and clarity.
* Add `-hdr` command-line option for HDR nits configuration
- Introduced `-hdr` parameter to set paper white and peak luminance in nits, overriding in-game HDR settings.
- Added input validation with error feedback for incorrect format.
* Remove unused `MODEL_SDR_FLAG_SHADOW_MAP` macro from shader flags
* clean up unrelated changes
* Invalidate Vulkan state tracker bindings after external command buffer modifications
- Added `invalidateExternalBindings()` to `VulkanStateTracker` to handle stale pipelines, descriptors, and dynamic states caused by external Vulkan commands (e.g., ImGui renders mid-pass).
- Updated ImGui rendering flow to call `invalidateExternalBindings()` post-draw, ensuring consistent pipeline state for subsequent engine renders.
- Adjusted image usage flags to account for `eTransferSrc` in texture readbacks.
- Fixed barrier `dstAccessMask` and pipeline stage flags to ensure proper synchronization for image transitions.
* Add a few more generically useful lines to the log
* Simplify Qt deployment logic and improve handling for system-installed Qt on Linux
* add cmdline headers to make qtFred compile
* use VK_KHR_synchronization2 for sync and barrier purposes
* Simplify and clean up Qt deployment logic in CMake for qtfred
* update get_bitmap_from_texture to match the new API
* Enhance `-hdr` command-line option to force HDR output and support optional nits configuration with improved input validation and overrides.
* Improve Vulkan robustness, add device feature checks, and refine memory/logging.
- Added depth clamping support gated by device feature checks, with fallback where unsupported.
- Improved deferred lighting UBO overflow handling with user-accessible warnings and buffer size adjustments.
- Enhanced Vulkan logging for validation, allocation, and debugging:
- Automatic logging for descriptor pool exhaustion/fragmentation.
- Vertex format and resource destruction checks.
- Notifications for generic layout transitions and volumetric texture uploads.
- Consolidated shader logic to ensure Vulkan and OpenGL parity.
* Remove HDR10 encode shader and consolidate gamma correction logic for SDR and HDR10. Replace `Verify` assertions with `Assert`
* Add braces to conditional in object lighting logic for clarity
* Refine Vulkan memory allocation checks with `Verification` for improved error handling and messaging.
* Refine VulkanBuffer resize logic to preserve contents with CPU-side copy and add safeguards for non-coherent memory scenarios.
* Add detailed comments explaining descriptor array capacity constants in VulkanDescriptorManager
* Gate shadow support on depth clamp feature availability and clarify its necessity with improved comments.
---------
Co-authored-by: BMagnu <[email protected]>
Co-authored-by: Mara van der Laan <[email protected]>
Co-authored-by: Claude <[email protected]>
* Fix heap overrun in texture readback for the model transparency scan
gr_opengl_get_bitmap_from_texture() sized its readback buffer from the
texture cache slot dimensions and the bmpman frame count, but
glGetTexImage() returns whatever the bound GL texture actually contains.
When texture detail below maximum culls textures on upload, the two can
disagree, and the readback overruns the buffer (observed as heap
corruption when loading Zeus.pof with Texture Detail: Minimum). Size
the readback from the dimensions and layer count OpenGL itself reports
for the texture, and pad the staging buffer to 4x4 block granularity in
case a driver writes tiny compressed mips back in whole blocks.
The readback now also allocates the returned buffer and reports the
dimensions of the data it returns, because the second half of the bug
was on the consumer side: bitmap_lookup indexed the readback data by
the bitmap's own dimensions, so with any culling in effect the
transparency scan read mostly uninitialized memory. bitmap_lookup (and
its cache) now carries the actual readback dimensions, and it skips the
texture cleanly if the readback fails.
Also pairs the cache's vm_malloc allocations with vm_free instead of
free, and no longer ignores a failed tcache_set before reading back.
Co-Authored-By: Claude Fable 5 <[email protected]>
* Harden the transparency-scan texture readback
Return fully opaque from bitmap_lookup::get_channel_alpha() when the
bitmap has no alpha channel, since indexing channel 3 of a 3-channel
buffer reads out of bounds (currently unreachable because the only
caller checks bm_has_alpha_channel() first, but no longer a trap).
Per review, also assert at maximum texture detail, where no culling
occurs on upload, that the readback dimensions OpenGL reports match the
texture cache slot, so any future slot mismatch is caught immediately
in debug builds instead of surfacing as corruption.
Co-Authored-By: Claude Fable 5 <[email protected]>
* Fix inverted dimension check in opengl_export_render_target
The validation rejected the export only if both the width and the
height mismatched the texture cache slot, so a texture wrong in one
dimension passed through into opengl_get_texture, which trusts the
caller-supplied dimensions for its mipmap loop.
Co-Authored-By: Claude Fable 5 <[email protected]>
---------
Co-authored-by: Claude Fable 5 <[email protected]>
* Extract shadows into own shader and render pass
# Conflicts:
# code/def_files/data/effects/main-g.sdr
* Consolidate render queue code
* More cleanup
* Fix missing clear of shadow buffer inter frame
* Request capability for viewport layer rendering
* instanced shadows
* Fix issues
* Switch to PCF+PCSS and proper textures for it
* Smooth PCSS toggle
* Fix for windows
* Fix OGL error
* Cleanup of old flag
* Expose shadow smoothness
* Fix missing uniform
* Make number of shadow cascades dynamic
* Progress on cockpit shadow integration
* Wrangle Cockpit and show-ship shadows into correct behaviour
* Cleanup
* make player ship shadow table options more intuitive
* Cleanup
* Only read from correct set of shadow maps, also stretch first main-scene map to 0
* Remove now non-necessary validation
* Make shadow cascade buffer more typesafe and less wasteful
* Don't allocate cockpit cascades if not used
* Cleanup
* Final fixes
* Fix Warnings
* clang tidy
* Fix missing fallback for shader precaching
* That weird FOV multiplier that always ruins your day
* Avoid shadow geometry artifacts by using hardware depth clamping
* Correctly handle shadow cascade overrides with hardcoded length
* GCC9 and its sad state of C++17 support.
* Address Feedback
# Conflicts:
# code/graphics/shadows.cpp
This fixes two build issues:
- `vk::PFN_DebugReportCallbackEXT` takes `vk::DebugReport*EXT` not C
type `VkDebugReport*EXT` since version 1.3.304.
(KhronosGroup/Vulkan-Hpp@d5a18dc87e)
- `vk::ObjectDestroy` was moved to namespace `vk::detail` in version
1.3.301.
(KhronosGroup/Vulkan-Hpp@6e5489fcd9)
Guard them with version checks, so that compiling with older verions
of Vulkan keeps working.
Closes#6416.
Because these functions can return statuses that aren't strictly an
error (and thus don't raise an exception), but might have to be handled,
they return a `vk::Result` that's `[[nodiscard]]`. Ideally some of the
result statuses would be handled here, but to get it to compile, cast
the result to void.
In 133b033ea0, SCP_vector was made into a
subclass, so direct assignment from the vulkan-hpp result isn't possible
anymore. Use an intermediate vector.