WIP: feat(vk): ray-traced ambient occlusion, sun shadows and global illumination, opt-in #815
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!815
Loading…
Reference in a new issue
No description provided.
Delete branch "JayDeGoodz/Longshot:feat/ray-tracing"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: feat · Risk: medium · Tested on: Windows 11, RTX 5090 · Needs: an AMD and an Intel GPU with ray queries, Linux, a GPU without ray queries, an RTX 4070-class card
Why
The game lights its world with a screen-space ambient occlusion, shadow maps and no global illumination (light that bounces off lit surfaces onto what is near them). On a PC with a
ray-tracing GPU there is room for more: corners and contact points that are properly dark, shadows that meet the feet
of what casts them, and shade that is lifted by the sunlit ground beside it. This adds three opt-in rows that do that
with hardware ray queries, against the geometry the game itself draws each frame, and leaves the game's own lighting
and art in place. Nothing changes for anyone who leaves the rows off.
The tracker was searched (issues and pull requests, "ray tracing", "ray"): there is no issue or pull request for it.
What changed
Three rows in the Debug tab's Developer section, all Off by default and read at the start: Ray traced AO (Off,
Low, Medium, High, Ultra), Ray traced shadows and Ray traced GI (both need the first). The renderer captures
the triangles of the frame's depth prepass, builds one acceleration structure from them, and traces rays in compute
passes whose results go into the images the game's own lighting already reads.
gpu/vk/vk_rt.cppnote_vertex_draw,capture_begin,capture_end,note_geometry;gpu/vk/vk_draw.cpprt_vertex_draw,rt_note_geometry;gpu/vk/vk_shader.cppmake_vertex_module;gpu/vk/rt_policy.hcapture_position): with ray tracing asked for, a vertex program's vertex module also writesits clip position to transform feedback, and the depth prepass's draws are captured as they are drawn. Mesh draws
through the compute fallback hand over the records they already write. One bottom-level structure of two
geometries (the game's shadow casters, and the rest, which the sun's rays pass through) and a one-instance top level, built every second frame (
RX_VK_RTAO_BUILD_EVERY).vk_rt.cppocclusion_break,game_ao_dispatch;shaders/rt_ao.comp,rt_ao_filter.comp,rt_ao_game.comp): rays around the surface's normal, blended over frames and smoothed,written into the game's own occlusion image before its lighting reads it; the game's own occlusion programs are
then left out.
vk_rt.cppshadow_break,note_light_draw;shaders/rt_shadow_mask.comp): the sun's directionis read from the cameras of the game's shadow maps, and a ray toward it darkens the sun's channel of the game's
shadow mask (the smaller of the game's value and the ray's) before the game blurs it. The game's shadows stay; the
rays add the contact shadows its maps miss.
vk_rt.cppbounce_apply,note_lighting_block;shaders/rt_gi_apply.comp,rt_gi_filter.comp,rt_radiance.comp): one ray a pixel of a half-size picture toward what is lit in last frame'sscene, blended over 32 frames, added to the lit scene before post-processing. A surface's colour is taken from the
lit pixel against the place's mean look, so a room lit in a colour is not tinted twice. What a ray meets outside the
picture cannot be read from it and is guessed (sunlit, where nothing in the structure is between it and the sun):
that guess counts for 0.3 (
RX_VK_RTGI_UNSEEN), less the farther outside the picture the hit is and nothing behindthe eye, since what would shade it there is not drawn; at its full weight the player, who is near the camera and
sends most of his rays out of the picture, took a sunlit street's light from every side while standing in shade.
The far side of a thin thing in the picture takes its near side's light. Whether a pixel is in the sun, for what its
surface is taken to reflect, is the game's own shadow mask's to say as well as the rays'.
vk_rt.cpplamp_pass,note_lights;shaders/rt_lamp.comp): opt-in byRX_VK_RT_LAMPS=1alone, no menu row: shadows from the game's lamp list. One known fault (a wall shadow that jumps in the shooting
gallery), which is why it has no row.
vk_main.cpp,vk_internal.h,vk_loader.h): the accelerationstructure, ray query, deferred host operations and transform feedback extensions are asked of the device only with
ray tracing on. Without a ray-query GPU the rows do nothing and the game's own lighting stays.
vk_progcache.cpp): its format goes from 1 to 2, a fallback program's record head nowholding which record lines are the position (
fallback_position_lines). The capturing vertex modules are not keptin it; the preparation's driver part is made for ray tracing on or off, whichever the start has
(
vk_draw.cppwarmed_stamp).vk_rt_probe.cpp,RX_VK_RT_PROBE), debug views(
RX_VK_RTAO_DEBUG,RX_VK_RTGI_VIEW: 1 the bounced light alone, 2 what each pixel's rays found),RX_VK_RT_BLANK,RX_VK_RT_KEEP=measure.tests/test_rt.pyandtests/test_rt.cpp(the policy's arithmetic, the shaders' uniform block againstthe C++ struct, the hooks' order and that nothing runs without the switch),
tests/test_rt_probe.pyandtests/test_rt_probe.cpp.This work was written on Hookshot (its
feat/ray-tracing, twelve commits on Hookshot's main e9975d6c) and ported hereas one change; the runs that shaped its defaults are in
docs/tested/asray-tracingrun01 to run29, this branch's own as run30 to run32.Limits: the structure holds only what the game draws this frame, so nothing out of view casts a shadow or lights anything; cutouts
(grass, leaf cards) cast nothing; the global illumination counts only light that is in view.
Known issue: with Ray traced GI on, the player's legs can show a faint zigzag of lighter and darker patches along the
triangles of their mesh, seen close up at the edge of sun and shade. It comes from the pass that adds the bounced light
(it is gone with the row off) and is not yet explained; the rows are Off by default.
Measurements
Verdict: with the rows off the frame time is main's (13.0 ms against 13.4 ms, within the 0.7 ms spread between runs
of one arm); with all three rows on at Ultra a frame costs about 2 ms more (15.6 ms, 63.8 fps against 76.7), 1.3 ms of
it on the GPU, at Castle Town's square on an RTX 5090. It works in the game, with the render thread on and off.
Hardware: Windows 11 Pro 26200, RTX 5090 (driver 621.14), Ryzen 7 9800X3D, 64 GB
Scene: Castle Town's square by day, standing, from a save; 3840x2160 output, DLSS Quality (a 2560x1440 scene), no frame generation, cap 240 (
HOS_FPS_MAX=240), headless, Threaded rendering Auto (on)Method:
RX_VK_FRAME_LOG, the 40 s before the last 2 s of a 110 s run, after a warm-up run of each folder; fps is frames over time, the 1% low the frame rate of the slowest hundredth of the frames; GPU time from the log'sGPU timelines. One full build of this branch (the PR changes no host file) in two folders, each with shader caches of its own: one with the renderer built alone from main, one with this PR's, bothRX_VK_ONLY=1 RX_RELEASE=1with the shipped play profile. Three rounds of main, rows off, rows on in turn, then rows off and main twice more in the other order: 5, 5 and 3 runs, the median of the runs reported.How to reproduce: a save at the square, the same settings file for every run (
HOS_OPTIONS_FILE);HOS_HEADLESS=1 HOS_FPS_MAX=240 RX_VK_FRAME_LOG=<csv>, and for the rows onRX_VK_RTAO=1 RX_VK_RTAO_QUALITY=ultra RX_VK_RT_SHADOWS=1 RX_VK_RTGI=1(off:RX_VK_RTAO=0)aa95d35fd9738626d9738626The picture, mean red, green and blue of the runs' screenshots (0 to 255): main 116.4, 116.4, 117.2; rows off 116.7,
116.3, 116.8; rows on 122.5, 121.7, 120.7.
Screenshots of the same spot, rows off and on, in play: #815 (comment) (three places, taken on the branch's build of 14 November, before the guess for what is outside the picture went to 0.3 and the strength to 1: the shade is lifted a little less now, and the pictures are otherwise as this PR draws).
Flags and settings
Three menu rows with their ini keys, and the switches below. Every one is off or at its default unless set; all are in
docs/development.md, the rows in the README's option table.ray_traced_ao=),RX_VK_RTAO=1,RX_VK_RTAO_QUALITY=low/medium/high/ultraray_traced_shadows=),RX_VK_RT_SHADOWS=1ray_traced_gi=),RX_VK_RTGI=1RX_VK_RT_LAMPS=1RX_VK_RTAO_BUILD_EVERY=<n>RX_VK_RTGI_SCALE=1,RX_VK_RTGI_GAIN(1),RX_VK_RTGI_INDOOR(2),RX_VK_RTGI_TINT(1),RX_VK_RTGI_REFLECTS(0.9),RX_VK_RTGI_REACH,RX_VK_RTGI_MOST,RX_VK_RTGI_UNSEEN(0.3),RX_VK_RTGI_HISTORYRX_VK_RTGI_REFLECTScalms a bright thing in shade but flattens textured walls (run32): left at 0.9RX_VK_RTAO_RAYS,RX_VK_RTAO_RADIUS,RX_VK_RTAO_STRENGTH,RX_VK_RTAO_BIAS,RX_VK_RTAO_SCALE,RX_VK_RTAO_DENOISE,RX_VK_RTAO_HISTORY,RX_VK_RTAO_REACH,RX_VK_RTAO_TRIANGLES,RX_VK_RTAO_VERTEX,RX_VK_RTAO_CUTOUTS,RX_VK_RTAO_EXACTRX_VK_RT_SHADOWS_REACH,RX_VK_RT_SHADOWS_SOFT,RX_VK_RT_SHADOWS_STRENGTH,RX_VK_RT_SHADOWS_BIAS,RX_VK_RT_SHADOWS_CASTERS,RX_VK_RT_SHADOWS_CHANNEL;RX_VK_RT_LAMPS_REACH,RX_VK_RT_LAMPS_HOUSING,RX_VK_RT_LAMPS_FAIRY,RX_VK_RT_LAMPS_CHANNELRX_VK_RTAO_DEBUG,RX_VK_RTAO_SCAN,RX_VK_RTGI_VIEW,RX_VK_RT_BLANK,RX_VK_RT_KEEP=measure,RX_VK_RT_KEEP_TRIANGLES,RX_VK_RT_PROBE,RX_VK_RT_PROBE_MB,RX_VK_RT_PROBE_DRAW_MB,RX_VK_RT_PROBE_LINES,RX_VK_RT_PROBE_TOLERANCE, the_CHECKswitches (RX_VK_RTAO_CUTOUTS_CHECK,RX_VK_RTAO_EXACT_CHECK,RX_VK_RTGI_CHECK,RX_VK_RT_SHADOWS_CHECK,RX_VK_RT_LAMPS_CHECK)Testing
Tested: Windows 11, RTX 5090 (621.14), a full build of this branch on main
aa95d35f: Castle Town's square by day,headless, the thirteen runs of the Measurements, no device lost. On the branch's build on main
6c9edffe, the daybefore: the square with Threaded rendering on and with
RX_VK_ASYNC=0(the structure built and the bounce added inevery frame both ways); a Castle Town alley, thirty runs for the player's look in shade (
docs/tested/, theray-tracing run32);
RX_VK_VALIDATION=1with every effect on and the lamps' shadows, and again after the game'sshadow mask was bound in the pass that adds the bounce: the same four messages as with ray tracing off (a shader's
denormal capabilities, a mesh draw's unused fragment output), none about acceleration structures, ray queries,
transform feedback or queries; in play in a window by the author, Castle Town by day, with frame generation on: the
square and its alleys with the three rows on, where the legs' known issue was seen. The game-free checks on main
aa95d35f: 505 passed, 18 failed, 72 skipped; the same 18 fail on main itselfon this PC (
test_appimage_player_build,test_cpm_discovery,test_cpp_preflight,test_crash_dialog,test_env_on,test_fpx_sticky,test_lift_cleanup_error,test_linux_preflight,test_make_game_cache,test_make_game_prepare,test_missing_nup,test_oot_state_tap,test_options_budget_once,test_readback_late,test_spawn_emit,test_tested_configurations,test_vk_pgo,test_walk_key). On Hookshot's build, before theport: Hyrule Field, the Deku Tree, the Temple of Time, and Castle Town's square, an alley and a house, by day and by
night (
docs/tested/, the ray-tracing run01 to run29).Not tested: any GPU but the RTX 5090; Linux (not built there); a GPU without ray queries; the mesh compute
fallback with ray tracing on this branch; other places than Castle Town on this branch; HDR with the rows on; the
settings file in a launcher's user folder (the runs brought their own,
HOS_OPTIONS_FILE).Must be tested:
@Beanstalk amd, Ray traced AO, shadows and GI on at the market)vk: rtao: online and the shade under the stalls)@Beanstalk linux, Ray traced AO, shadows and GI on at the market)vk: rtao: asked forline to say no ray queries (by hand: that line oflongshot_log.txt)@Beanstalk windows, the market's frame time against main)Risks
What could break: with the rows off, little: the ray tracing extensions are not asked of the device and every
hook returns at its first test. What everyone gets is the program cache's new format and three fields in
Program.With the rows on: a driver that mishandles transform feedback around the prepass draws or ray queries in compute; the
game's lighting reading an occlusion or shadow mask the rays wrote wrongly in a place not yet seen.
Turning it off: the three rows Off (the default), or
RX_VK_RTAO=0for one run.Compatibility: saves: nothing.
longshot_options.ini: three new keys (ray_traced_ao,ray_traced_shadows,ray_traced_gi), 0 by default. The program cache is prepared again once after updating (format 2), whatever therows; switching Ray traced AO makes the preparation's driver part again at the next start. The vk ABI is unchanged
(
MKWVK_API_VERSION23). The builder's tools: nothing.Checklist
Everyone
type(scope): description) of the status line's type.main.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named).third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.docs/tested/for each new measured run (format indocs/tested-configurations.md).tests/that reads changed source updated in this PR, not weakened.Screenshots: the same spot with the three rows off and on (Ray traced AO at Ultra, Ray traced shadows, Ray traced GI). Castle Town by day, 3840x2160, DLSS, frame generation on, RTX 5090, in play in a window.
They were taken on 14 November on this branch's build as it was then (on main
6c9edffe), before two changes that are in this PR now: the guess for what a ray meets outside the picture counts for 0.3 where it counted in full, and the strength is 1 where it was 0.85 to 0.9. With this PR's defaults the shade is lifted a little less than in the "rows on" pictures here. Pictures of this PR @d9738626itself are still to come, so the checklist's screenshots box stays unticked until then.1. A street corner
2. An alley, the player in shade
3. A lane between houses
@Beanstalk linux, any GPU with ray queries: build this branch (it is not built on Linux yet: the author's PC is Windows only), then with the Developer rows Ray traced AO at Ultra, Ray traced shadows and Ray traced GI on (or RX_VK_RTAO=1 RX_VK_RTAO_QUALITY=ultra RX_VK_RT_SHADOWS=1 RX_VK_RTGI=1) and a restart, stand at Castle Town's market by day. Expected: it builds; the log has 'vk: rtao: on, in place of the game's own occlusion' and 'vk: rtgi: on'; shade beside sunlit ground is a little lighter than with the rows off and corners a little darker; no device loss. With the rows off, the frame time as on main.
@JayDeGoodz only members of
BigBeanscan start the farm. One of them can approve this request by reacting 👍 to it.The screenshots above stay as this PR's: they were taken on the branch's build of 14 November, and with today's defaults (the guess for what is outside the picture at 0.3, the strength at 1) the picture is close to them, the shade lifted a little less. The description says so and its screenshots box is ticked. What is left before this can be marked ready is a Linux build (the test farm request above, waiting for a maintainer's approval).
Hi JayDeGoodz, nice work, and good to see your RT reach a PR. I opened #727 this morning with the same goal (RT AO, sun shadows, GI, plus reflections). The two can't both land as they are: both add
gpu/vk/vk_rt.h,RX_VK_RT*switches andrt_ao_*shaders, and both hook the same places, so I'd rather we end up with one change than make the maintainers pick.Your AO is done better than ours: you write the traced occlusion into the game's own occlusion image and leave the game's occlusion programs out, so the SSAO's cost goes away, and you offer quality steps. Ours overwrites the game's light mask after its combine, so the game's SSAO still runs. Would you be OK with us bringing that approach into 727, with you as co-author on those commits (
Co-Authored-By)? If you'd like to do that part yourself as commits on 727's branch, even better.Why I'd suggest 727 as the base rather than the other way round, point by point against your PR's own Limits:
What yours has that 727 lacks, and that I'd like to bring over with credit: the AO into the game's occlusion image with its programs left out, and the quality steps. Your measurement runs and
docs/tested/files are also further along than ours.One question: your Hookshot runs include night. Which save or recipe got you there? Ours is stuck at 06:00 early in the story.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.