fix(vk): draws with 8-bit indices are drawn on GPUs without 8-bit index buffers (RADV on GCN 1-2) #541
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/tooling
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8015c9a
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!541
Loading…
Reference in a new issue
No description provided.
Delete branch "Yeetbot90/Longshot:fix/gcn-black-screen"
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: fix · Risk: low · Tested on: Windows 11, RX 9070 XT (GCN 1-2 path forced with
RX_VK_INDEX_UINT8=0) · Needs: Linux, AMD GCN 1 or 2 on RADVWhy
On Linux, older AMD cards (GCN 1 and 2: HD 7000, R9 270 to 390, R7 260 to 360) show only a black screen from the start, while the game runs underneath with sound and controls. Their driver has no 8-bit index buffers, and Longshot left out every draw that uses them, among them the ones that put the picture on screen. A player reported it with an R9 290 on Fedora 44 and v3.0.0-rc.3; no issue on the tracker fits (searched for R9 290, GCN, black screen, uint8).
How to reproduce
Seen on: Fedora 44, Radeon R9 290 (RADV HAWAII, Mesa 26.2.6), v3.0.0-rc.3; on any GPU with
RX_VK_INDEX_UINT8=0from this PRSettings and switches: defaults
Steps: start the game and wait for the intro and the title
Expected: the intro, then the title over its movie
Actual: a black screen with the sound playing, the Esc menu working; the log shows
skips a frame by vk_draw.cpp line: 7385: 2.0(rc.3's line forif (d.indexed && d.itype == 0 && !dev.index_uint8) SKIP_HERE();)What changed
Draws with 8-bit indices are no longer left out on a GPU that has no 8-bit index buffers: their indices are copied into 16-bit ones on the CPU and drawn from those, as the quads path already does with its indices. A test switch makes any GPU take that path, a game-free test covers it, and the common-issues page no longer sends these players after a mesh shader setting that doesn't apply.
u8_indices_widened()andu8_indices_read()ingpu/vk/vk_draw.cpp. When!dev.index_uint8, an indexed draw's 8-bit indices are read as submitted and written as 16-bit ones into the recording's stream; a restart index stays one (0xFF becomes 0xFFFF where the draw restarts). An indirect draw widens as far as its records reach (first index + count; a count buffer limits the records).draw()binds the result through its existingbind_indexcalls asVK_INDEX_TYPE_UINT16, where it used toSKIP_HERE(). The first widening is logged once:vk: 8-bit indices are widened to 16 bits on the CPU (this card has no 8-bit index buffers).RX_VK_INDEX_UINT8=0ingpu/vk/vk_main.cppdoes not ask forVK_EXT_index_type_uint8..EXT_index_type_uint8 = pdev->info.gfx_level >= GFX8(the KHR extension and the feature alike) insrc/amd/vulkan/radv_physical_device.c, so GCN 1 and 2 (GFX6, GFX7) never have it. Every other driver we run on has it.tests/test_u8_indices.pycuts both functions out ofvk_draw.cppand compiles them with stubs: values, restart on and off, a draw of none, a failed read, indirect records (instances 0 left aside, a count buffer, a wider stride, past 16M indices refused). It also checks thatdraw()no longer skips these draws and binds them as 16-bit.docs/development.md. Indocs/common-issues.md: the game doesn't createlongshot_env.txt(the player looked for it after the AMD tip),RX_VK_MESH_SHADERS=computechanges nothing on Linux AMD where it is already the path, and thevulkaninfo | grep meshcheck, which matched llvmpipe'sVK_EXT_mesh_shaderon every Mesa system, now greps withdeviceName.docs/tested/: four files, the two interleaved pairs below.Measurements
Verdict: it fixes it: with the GCN 1-2 path forced, 0 of 114 screenshots show a picture without the widening and 111 of 115 with it (the other four are the first frames, before the intro), at 60 fps; it passed.
Baseline: the before column is
e072164ewith this PR'sRX_VK_INDEX_UINT8switch alone, the after columne072164ewith this PR's whole renderer diff (vk_draw.cpp,vk_main.cpp), which applies to it unchanged. main can't show the bug here: it has no switch, and no GPU on this PC lacks 8-bit index buffers. The game build on this PC is frome072164e(renderer interface 21; main is at 23 now), so the runs were made on it and not repeated after the rebase ontoaa95d35f; the widening is unchanged by the rebase.Hardware: Windows 11 Home 26200, RX 9070 XT, AMD 26.10.1 (LLPC), Ryzen 9 7900X, 31 GB RAM
Scene: from the start to the title over its movie, no input; 1280x720 headless (
HOS_HEADLESS=1), muted, fresh profile, empty caches,RX_VK_PRECOMPILE=0,RX_VK_INDEX_UINT8=0Method: 120 s per run, two interleaved pairs (before, after, before, after); a screenshot every 120 presents (
RX_VK_SHOTS), each shot's mean brightness (Rec. 709 luma, 0-255, every 8th pixel), a shot "with a picture" when over 1% of its pixels are above 16; the log's frame reports; the median of the two runsHow to reproduce: any GPU,
RX_VK_INDEX_UINT8=0inlongshot_env.txtor the environment; start the game. Without the widening the screen stays black; with it the intro and title showe072164e4b44f569, its renderer diff one072164eScreenshots, run 1 at the same presents (360, 2640, 4800, 6960), in the first comment: #541 (comment) (top row: Before, the switch alone @
e072164e, all black; bottom row: After, this PR's renderer diff one072164e).Flags and settings
This PR adds one switch, for testing only: it makes any GPU take the GCN 1-2 path.
RX_VK_INDEX_UINT80does not ask the driver for 8-bit index buffers0: draws with 8-bit indices are widened to 16-bit on the CPU, as on RADV GCN 1-2Testing
Tested:
python -I -B tests/run_all.pyon Windows 11 (llvm-mingw clang 22) after the rebase ontoaa95d35f: 494 passed, 21 failed, 79 skipped. The same 21 fail on a cleanaa95d35fon this PC (test_appimage_player_build,test_bake_portability,test_cpm_discovery,test_cpp_preflight,test_crash_dialog,test_env_on,test_fpx_sticky,test_lift_cache,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_release,test_spawn_emit,test_tested_configurations,test_vk_pgo,test_walk_key: Windows toolchain and environment, code this PR doesn't touch).tests/test_u8_indices.pypasses with clang++ and g++ and fails on main at its first check.vk_draw.cppcompiles with-Wall -Wextraand nothing on the changed lines. In the game: the four runs under Measurements, RX 9070 XT, AMD 26.10.1, withRX_VK_INDEX_UINT8=0, renderer-only builds (RX_VK_ONLY=1 RX_RELEASE=1 nx2/build.py --standalone) one072164e.Not tested: a GCN 1 or 2 card itself (none here); play past the title, where other draws may use 8-bit indices; indirect draws with 8-bit indices in the game (none at the title; the test covers them); a Linux build of this change (renderer code without OS-specific parts); the runs on
aa95d35f(this PC's game build is at renderer interface 21).Must be tested:
vk: 8-bit indices are widenedin the log with noskips a frame by vk_draw.cppline at the 8-bit index check (by hand: test request T-H95J; copy the log'svk: device 0andskips a framelines)Risks
What could break: only GPUs without 8-bit index buffers take the new path (RADV on GCN 1-2, or
RX_VK_INDEX_UINT8=0); everywhere else the draw is recorded as before. On those GPUs, indices the GPU itself writes rather than the game's CPU would be read stale, as the quads' already are, and an indirect draw whose records the GPU writes could widen too few. The two lines this changes in the indexed-indirect branch are next to the one #434 changes (extent_bindfor the index buffer): whichever merges second takes both.Turning it off: revert the commit.
Compatibility: Nothing changes: no save, options file, cache or
MKWVK_API_VERSIONchange; on GCN 1-2 the draws that were missing now show.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).mainand passing here.Screenshots from run 1, RX 9070 XT with
RX_VK_INDEX_UINT8=0, at the same presents (360, 2640, 4800, 6960). Top:e072164e+RX_VK_INDEX_UINT8only, all black (the switch-only run ended before present 6960's shot). Bottom:e072164e+ this PR's renderer diff, the intro and then the title over its movie.WIP: fix(vk): draws with 8-bit indices are drawn on GPUs without 8-bit index buffers (RADV on GCN 1-2)to fix(vk): draws with 8-bit indices are drawn on GPUs without 8-bit index buffers (RADV on GCN 1-2)fix(vk): draws with 8-bit indices are drawn on GPUs without 8-bit index buffers (RADV on GCN 1-2)to WIP: fix(vk): draws with 8-bit indices are drawn on GPUs without 8-bit index buffers (RADV on GCN 1-2)WIP: fix(vk): draws with 8-bit indices are drawn on GPUs without 8-bit index buffers (RADV on GCN 1-2)to fix(vk): draws with 8-bit indices are drawn on GPUs without 8-bit index buffers (RADV on GCN 1-2)92afaea4004b44f56960Rebased onto main @
aa95d35f(4b44f569), in place of the merge commit92afaea4, whose tree was identical. The description now follows the new template (#571). After the rebase, tests/run_all.py: 494 passed, 21 failed; the same 21 fail on a cleanaa95d35fon this PC (Windows toolchain and environment, none in code this PR touches). The in-game runs are still the ones one072164e: this PC's game build is at renderer interface 21 and main is at 23, so they weren't repeated; the renderer diff is unchanged by the rebase.Read the diff and the surrounding
draw()code. The approach is right: widen 8-bit indices to 16-bit in the stream on devices withoutindex_uint8, restart 0xFF mapped to 0xFFFF only when restart is on and the topology uses it,RX_VK_INDEX_UINT8=0to force the path. It also covers the quads exclusion that #595 makes, so 595 is a subset of this PR and they conflict on the same lines. 541 is the better fix (595 still leaves all non-quad 8-bit draws skipped, so GCN 1-2 stays black); 595 can be closed.One thing to fix before merge: for a non-indirect draw
u8_indices_readreturnsmax(d.count, 1)with no upper bound, while the indirect branch refuses more than 16M.u8_indices_widenedthen allocates astd::vector<uint8_t>of that size and astream_alloc(n * 2). The quads path guards the same case withd.count > (1 << 24)because the game's count once sized host vectors up to 16 GB. Apply the same 16M limit to the direct case (the test claims "past 16M indices refused" only for indirect).Questions, not blocking: the widening runs per draw on the CPU with no cache, so a scene with many 8-bit draws pays it every frame (fine for a fallback path). Real GCN 1-2 hardware is still untested (T-H95J), so the "must be tested" item stands.
@ -6847,0 +6847,4 @@/// (longshot) How many 8-bit indices a draw reads from its index buffer: its count, or for an indirect draw the furthest its/// records reach (first index + count), as submitted. 0: none read or more than 16Muint64_t u8_indices_read(const DrawArgs& d) {if (!d.indirect) return (uint64_t)std::max(d.count, 1);Direct draws have no upper bound here (indirect is capped at 16M below);
draw()quads cap at1 << 24. Return 0 above that.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.