Skip to content

GPU hang on Steam Deck running CachyOS (RADV): gfx ring timeout, ring reset fails, MODE2 reset — v2.0.1 / v2.0.3 / v2.0.4 #817

Closed as duplicate

Description

@thelinuxgmr

First off, thank you for this project — it's been getting smoother and going further with every version today. Filing this in case the data helps; happy to test builds or pull more logs.

Environment

  • Steam Deck running CachyOS (Deckify/handheld edition, Arch-based — not SteamOS)
  • Kernel 7.2.11-1-cachyos-deckify
  • Mesa 26.2.6 — engine reports: AMD Custom GPU 0932 (RADV VANGOGH), Vulkan 1.4, driver 0x6802006
  • Game: own dump, Ocarina of Time (Switch 2) [0400486024DEC000] v131072, built locally with the Linux AppImage builder
  • Launch: Steam non-Steam shortcut, HOS_HANDHELD=1 (swapchain 1280x720)
  • Builds tested: v2.0.1, v2.0.3, v2.0.4 (have not run v2.0.5 yet — it dropped while I was testing 2.0.4)

What happens

Mid-gameplay the picture freezes and the GPU hangs hard: gfx ring timeout, the ring reset fails, and the driver falls back to a full MODE2 reset. The game process dies in the reset. Each version runs smoother and gets further before it happens:

  • v2.0.1: hung early
  • v2.0.3: ~4 minutes of gameplay (13:30 PT today)
  • v2.0.4: ~10 minutes of gameplay (crashed 14:21 PT today) — best run yet

LSFG/frame generation was ruled out: the hang happens with it fully out of the launch path.

v2.0.4 run, with RX_VK_CHECKPOINTS=1

Checkpoints were armed at startup:

vk: RX_VK_CHECKPOINTS: VK_AMD_buffer_marker, a marker before each draw and dispatch (memory type 2)

Last engine output before the freeze (14:21:00–14:21:09, crash at 14:21:16) — repeated warnings, then a burst of new pipelines:

vk: storage buffer NOT bound (reads zeros): program 68642bb810ee3e7c stage 7 c5+0x60 addr 0x10001c10a size 0x40800000
vk: storage buffer NOT bound (reads zeros): program 43ec86f35fd937b9 stage 7 c5+0x100 addr 0x42ecff4dc2383ff3 size 0x417855b0 (written)
vk: in play: graphics 1a7482c0ec9991ea ... (a burst of ~10 "in play: graphics" lines within one second)

After that the log goes silent — no "device lost" line and no fault-info readout was printed before the process died in the reset, so I don't have a checkpoint/marker name for the hanging draw. If that readout lands somewhere else, tell me where and I'll fetch it.

Kernel side (amdgpu, 2026-11-08 14:21:16 PT):

ring gfx_0.0.0 timeout, signaled seq=401594, emitted seq=401596
Process hookshot pid 19756 thread sead::MainThrea pid 19761
Starting gfx_0.0.0 ring reset
Ring gfx_0.0.0 reset failed
GPU reset begin!. Source: 1
MODE2 reset
GPU reset succeeded, trying to resume
[drm] device wedged, but no recovery needed

…followed at 14:21:19 by ring comp_1.3.0 timeout (that ring reset succeeded). The kernel did create a devcoredump both times, but it expired before I could copy it — same story on the v2.0.3 crash.

v2.0.3's kernel signature was the same family: gfx_0.0.0 timeout → ring reset failed → MODE2 reset, then a comp ring timeout. I have full kernel excerpts and the Steam console log window for both crashes if you want them.

Activity

  1. thelinuxgmr commented on Nov 8, 2026

    @thelinuxgmr
    Author

    Repro detail from the player: the crash hits at a specific story beat — right after the intro, when Link first reaches the Great Deku Tree and it starts speaking (the opening dialogue in Kokiri Forest). The ~10 minutes before that is the intro and first-boot preparation. After the freeze the game completely exits (process gone, not left frozen on screen). This also looks like the same spot that stopped v2.0.1 early.

  2. omgitsgela commented on Nov 9, 2026

    @omgitsgela

    Confirming the same mid-gameplay GPU hang on SteamOS (not CachyOS), and adding a data point for v2.0.5, which you had not tested when you filed this.

    Setup

    • Steam Deck (VanGogh, RDNA2), 3 GiB VRAM + 6 GiB shared, 14 GiB RAM
    • SteamOS 3.8.28 (build 20260922.1), kernel 6.18.50-valve2-1-neptune-618-gc7289a96b14d
    • Mesa 26.2.0-devel (git-035ae2f854), vulkan-radeon 26.2.0_devel.222131
    • Builds tested: ultrawide (source 50b3813b) and v2.0.5 (c5895377)

    Same kernel signature, three times in one session

    ring gfx_0.0.0 timeout, signaled seq=916081, emitted seq=916082
    Process hookshot pid 21773 thread Presentation Th pid 21776
    Ring gfx_0.0.0 reset failed
    GPU reset begin!. Source: 1
    VRAM is lost due to GPU reset!
    GPU reset(1|2|3) succeeded!
    [drm] device wedged, but recovered through reset
    

    Always signaled seq=N, emitted seq=N+1 — the GPU is exactly one submission behind, and a single command buffer never completes. Always on the Presentation Th thread. The screen shows a rainbow/garbage frame immediately before the reset. Visually it correlates with fast motion — new shaders being hit in quick succession.

    Where the time goes before a hang (may be relevant)

    During a new scene the GPU sits at 0% busy while the process uses ~210% CPU, so the stutter is CPU-side pipeline compilation in the driver, not GPU rendering. Not swap either: /proc/pressure/memory read avg60 ≈ 0.18, and zram was barely touched. So the hang is not obviously a memory-pressure event.

    Things that did not help (on the ultrawide build)

    • A renderer built from source at HEAD 5facf993, which includes the "under 6 GB VRAM → BC3" change (confirmed the .so hash differs): still hangs. VRAM pressure at 3 GiB is therefore probably not the trigger.
    • RX_VK_PRECOMPILE=1: preparation completes fine, hang still occurs.

    v2.0.5 on this device does not reach gameplay at all — it segfaults in libvulkan_radeon.so right after shader preparation. That is a separate report (see the RDNA2 SIGSEGV issue), not the hang described here.

    Given the one-submission-deep stall, a checkpoint/marker name for the hanging draw would be the most useful next artefact — you noted the readout did not print before the process died in the reset. If I can get one out of a run on this device I will add it here.

  3. RufflezAU commented on Nov 9, 2026

    @RufflezAU

    Same hang on a Steam Deck OLED (VanGogh, SteamOS), and this one comes with the marker data from RX_VK_CHECKPOINTS=1.

    • build: upstream 15ec28d + a 10-line local eviction patch (millisecond age instead of presents), RX_VK_PRECOMPILE=0 (the prepare is 166 s here and sometimes faults in the driver), 50 % resolution, 4x frame generation
    • it boots, loads a save, runs the starting room, and survives walking out into Kokiri Forest
    • a few seconds after that (frame 1062, 498 draws in the frame), the picture stutters and the process dies:
    radv/amdgpu: The CS has been cancelled because the context is lost. This context is guilty of a hard recovery.
    vk: batched submit 5046 failed (-4)
    vk: device lost: 206505 markers set, 61520 reached; the last whose earlier work all finished: 202476
    vk:   finished before: marker 202470 (draw of program 41deb0da8e1d169b, frame 1062)
    ...
    

    No OOM: VRAM was 3116 of 5867 MB, textures 2340 MB, GPU 52 ms a frame. RX_VK_KEEP_FINALIZED_MB / the occlusion value make no difference to it.

    The older base (4d7d2f1 + the PR #448 udmabuf branch + the retired-import giveback) does not hang on this machine in the same place — it runs the forest for minutes (slow, ~10 fps, and with the old flicker). So whatever the newer code does differently, this Deck's RADV does not survive it.

    If the mesh path is the common thread: RX_VK_NO_MESH=1 avoided the startup fault in 15ec28d, but I have not yet run the forest long enough with mesh draws skipped to say whether it avoids this hang too. I can do that, or test PR #792's RX_VK_FORCE_MESH_FALLBACK=1 here — that one boots without the startup fault but needs a 5.9 GB program cache, which is its own problem on a 14 GB machine.

  4. hardicd commented on Nov 9, 2026

    @hardicd

    This answers the two open questions above, which hanging draw it is and whether leaving mesh draws out avoids it, with a workaround that makes the game stable on the Deck.

    Same hang here on a Steam Deck OLED, SteamOS 3.9.5 (RADV VANGOGH, Mesa 26.2-devel), v2.0.4 built with the AppImage toolchain. I traced it to one task shader, and skipping the draws that use it makes the game stable on the Deck.

    Repro: loading a save inside the Great Deku Tree hangs the GPU within ~30 s: ring gfx_0.0.0 timeout, ring reset failed, MODE2 reset. Not memory: 6 GB free at the hang.

    Getting the marker readout: the process is SIGKILLed in the reset before device lost can read the buffer markers. I added a watchdog thread that reads marker_words while the GPU is still stuck: when no marker has finished for 3 s while later ones were set, and again at 6 and 9 s, before the 10 s ring timeout. Patch below.

    What it shows (headless runs, same save, scripted input):

    Run Switches Stuck in
    1 none draws of 6f9142def3b6a978 (code 1:9bcda2a743ed9704 6:2c56dc1006942f74 7:4797e4cd2b870a69), reset
    2 RX_VK_SKIP_PROG=…,6f9142def3b6a978 draw of 636eed4d5000d1c0, reset
    3 both skipped 9 s in 155b02322a3ee490, then it finished
    4 RX_VK_MESH_OPT=1 38cdd9d8563b16eb, same point
    5 RX_VK_NO_MESH=1 nothing: 0 stalls in 4+ min
    6 RX_VK_SKIP_CODE=2c56dc1006942f74 (below) nothing: 0 stalls, also without markers

    Every stuck program is a task + mesh program sharing one task shader, NVN code 2c56dc1006942f74; about 30 programs in the Deku Tree and Kokiri Forest use it. Their mesh shaders have no loops. The task shader has two nested loops over a 2D grid, with bounds SMin(SMax(0, F2I(...)), cbuf3[3088] - 1): a culling pass over each object's screen footprint. On RDNA 2 some of these draws run for seconds (one took 9 s and then finished), and the kernel resets the GPU when one passes 10 s. With the optimizer on (RX_VK_MESH_OPT=1) it still stalls.

    Workaround: a new RX_VK_SKIP_CODE=<stage code key prefix,...> switch leaves out the draws of every program that has a stage of that code, decided once per program. With RX_VK_SKIP_CODE=2c56dc1006942f74:

    • Deku Tree: no stalls in several 4-minute runs (it hung within 30 s before).
    • Game Mode on the Deck (launched from Steam, 720p handheld): 20 min in Kokiri Forest with no stall and no device lost.
    • I couldn't see anything missing in the Deku Tree or Kokiri Forest.

    Deck settings that worked best for me, for other Deck players (hookshot_env.txt):

    RX_VK_LOW_SPEC=1
    HOS_HANDHELD=1
    RX_VK_SKIP_CODE=2c56dc1006942f74
    RX_VK_MESH_OPT=1
    RX_VK_VRAM_MB=1200
    RX_VK_TEX_MAX_MB=128
    RX_VK_TEX_MAX_DIM=1024
    RX_VK_MIRROR_MB=256
    
    • Frame generation: with 2x set in the menu, Kokiri Forest runs at ~28 fps game / ~57 shown. Without it the game runs at ~24 fps. Measured with a 90 s camera turn from the same spot after a fresh load.
    • Memory caps: the Minimum preset's memory budgets don't apply on the Deck, because it reports ~6 GB and preset_caps_memory() only applies them on cards of 4.75 GB or less. Hence the explicit caps: GTT 5.6 vs 6.4 GB after 5 min, about half the swap.
    • No help: forcing the GPU to 1600 MHz, the performance CPU governor and the LAVD scheduler made no difference or made it worse, since the CPU and GPU share 15 W.

    Also: PR #792 with RX_VK_FORCE_MESH_FALLBACK=1 crashes inside libvulkan_radeon.so (null read) from fallback_record at the first mesh draw on this GPU.

    Patch: marker watchdog + RX_VK_SKIP_CODE (gpu/vk/vk_draw.cpp only; applies to v2.0.4 and to current main d3659c5)
    diff --git a/gpu/vk/vk_draw.cpp b/gpu/vk/vk_draw.cpp
    index b0aac0f..d373a14 100644
    --- a/gpu/vk/vk_draw.cpp
    +++ b/gpu/vk/vk_draw.cpp
    @@ -2334,6 +2334,7 @@ std::atomic<uint32_t> checkpoint_n{0};
     
     // (VK_AMD_buffer_marker, where the NV checkpoints are missing: marker i's sequence number goes to word 2i when the GPU
     // reaches it and to word 2i + 1 once all the work before it is done. Host memory, not VRAM: a GPU reset loses VRAM.)
    +void marker_watchdog();   // (deck-fix, below)
     VkBuffer marker_buffer = VK_NULL_HANDLE;
     volatile uint32_t* marker_words = nullptr;
     
    @@ -2472,11 +2473,13 @@ void buffer_markers_init() {
         marker_buffer = buffer;
         marker_words = (volatile uint32_t*)words;
         logf("vk: RX_VK_CHECKPOINTS: VK_AMD_buffer_marker, a marker before each draw and dispatch (memory type %u)", type);
    +    std::thread(marker_watchdog).detach();   // (deck-fix)
    +    logf("vk: RX_VK_CHECKPOINTS: markers watched while the GPU runs (a stall of 3 s is reported)");
     }
     
     namespace {
     // (the markers the GPU reached whose earlier work never finished: the hang is in the work after the last marker that did)
    -void buffer_markers_report() {
    +void buffer_markers_report(const char* why = "device lost") {
         const uint32_t n = checkpoint_n.load(), first = n > 0xFFFF ? n - 0xFFFF : 1;
         uint32_t reached = 0, last_done = 0;
         std::vector<uint32_t> stuck;
    @@ -2487,7 +2490,7 @@ void buffer_markers_report() {
             if (d) last_done = s;
             else if (r) stuck.push_back(s);
         }
    -    logf("vk: device lost: %u markers set, %u reached; the last whose earlier work all finished: %u", n, reached, last_done);
    +    logf("vk: %s: %u markers set, %u reached; the last whose earlier work all finished: %u", why, n, reached, last_done);
         const auto line = [](const char* what, uint32_t s) {
             const Checkpoint& k = checkpoints[(s - 1) & 0xFFFF];
             logf("vk:   %s: marker %u (%s %016llx, frame %llu)", what, s, checkpoint_kind(k.kind), (unsigned long long)k.prog,
    @@ -2500,6 +2503,36 @@ void buffer_markers_report() {
         for (uint32_t s = start; s && s <= end && s <= n; s++) line(s == last_done ? "RUNNING after" : "running", s);
         for (size_t j = 0; j < stuck.size() && j < 12; j++) line("reached, earlier work unfinished", stuck[j]);
     }
    +// (deck-fix) RX_VK_CHECKPOINTS on AMD: the markers watched while the GPU still runs. A hang on RDNA 2 (Steam Deck,
    +// RX 6000) ends in a ring timeout and a full GPU reset that kills the process before "device lost" reads them (#817),
    +// so they are read when no marker has finished for 3 s while later ones were set: at 3, 6 and 9 s of the stall (the
    +// kernel resets the ring at 10 s).
    +void marker_watchdog() {
    +    uint32_t last_done = 0;
    +    auto since = std::chrono::steady_clock::now();
    +    int told = 0;
    +    for (;;) {
    +        std::this_thread::sleep_for(std::chrono::milliseconds(250));
    +        const uint32_t n = checkpoint_n.load(), first = n > 0xFFFF ? n - 0xFFFF : 1;
    +        uint32_t done = 0;
    +        for (uint32_t s = n; s >= first && s; s--)
    +            if (marker_words[2 * ((s - 1) & 0xFFFF) + 1] == s) { done = s; break; }
    +        const auto now = std::chrono::steady_clock::now();
    +        if (done != last_done || done == n) {
    +            last_done = done;
    +            since = now;
    +            told = 0;
    +            continue;
    +        }
    +        const double stalled = std::chrono::duration<double>(now - since).count();
    +        if (told < 3 && stalled >= 3.0 * (told + 1)) {
    +            told++;
    +            char why[96];
    +            snprintf(why, sizeof why, "GPU stalled for %.1f s (marker watch %d of 3)", stalled, told);
    +            buffer_markers_report(why);
    +        }
    +    }
    +}
     } // namespace
     
     void draw_records_log(uint64_t lo, uint64_t hi);
    @@ -4675,6 +4708,39 @@ void draw(VkCommandBuffer cmd, NvnState& s, const DrawArgs& d) {
                     return;
                 }
         }
    +    {   // (deck-fix) RX_VK_SKIP_CODE=<stage code key prefix,...>: the draws of programs that have a stage of that code left
    +        // out, decided once a program (RX_VK_SKIP_PROG names whole programs; on RDNA 2 the GPU stalls for seconds in task/mesh
    +        // programs that share one task shader, Hookshot issue 817)
    +        static const char* skip_code = getenv("RX_VK_SKIP_CODE");
    +        if (skip_code && *skip_code) {
    +            static std::mutex memo_mu;
    +            static std::unordered_map<uint64_t, bool> memo;
    +            bool skip = false;
    +            {
    +                std::lock_guard lk(memo_mu);
    +                const auto it = memo.find(cp->key);
    +                if (it != memo.end()) {
    +                    skip = it->second;
    +                } else {
    +                    const std::string codes = program_code_keys(s);
    +                    for (const char* q = skip_code; *q;) {
    +                        const char* e = q;
    +                        while (*e && *e != ',') e++;
    +                        if (e > q && codes.find(":" + std::string(q, e)) != std::string::npos) skip = true;
    +                        q = *e ? e + 1 : e;
    +                    }
    +                    memo.emplace(cp->key, skip);
    +                    if (skip) {
    +                        static std::atomic<int> told{0};
    +                        if (told++ < 32)
    +                            logf("vk: draws of program %016llx left out (RX_VK_SKIP_CODE); its code:%s",
    +                                 (unsigned long long)cp->key, codes.c_str());
    +                    }
    +                }
    +            }
    +            if (skip) return;
    +        }
    +    }
         // Not ready (translating, or its pipeline building): left out of the frame when its targets hold an earlier
         // frame's picture already, which shows for a frame without it; waited for otherwise (a pass drawn once, a target
         // made this frame), as totk-pc-recomp's TOTK_PIPE_SKIP rule. RX_VK_PIPE_WAIT=1: always waited for.

    This is a workaround, not a fix: what makes that task shader run for seconds on RDNA 2 is still open. I have the IR dumps of the three programs and the logs if they help.

  5. RufflezAU commented on Nov 9, 2026

    @RufflezAU

    Confirming hardicd's workaround on a second Deck (Steam Deck OLED, RADV VANGOGH, SteamOS).

    Build: upstream 15ec28d + a 10-line millisecond-eviction patch, RX_VK_PRECOMPILE=0, plus their RX_VK_SKIP_CODE=2c56dc1006942f74 patch, HOS_HANDHELD=1, RX_VK_LOW_SPEC=1, RX_VK_MESH_OPT=1, VRAM 1200 / TEX 128 / 1024.

    • before: device lost within seconds, sometimes in the starting room (frame 902, RUNNING after marker on a draw), sometimes in Kokiri Forest — same hard recovery as reported above;
    • with RX_VK_SKIP_CODE: 2952 draws in Kokiri Forest, 16.9–17.6 fps, GPU 47.9 ms, no device lost over a 5-minute run, and no stall reported by their watchdog.

    The only remaining visual issue on this build is the icon/object flashing in #798, which is separate. Thanks for the trace and the switch — it turned an unusable build into a playable one here.

  6. NotRexed commented on Nov 9, 2026

    @NotRexed
    Owner

    Tracked in #1000, follow there. That issue collects every report of Linux / Steam Deck GPU hangs, crashes, low fps and build failures, and this one is linked from it with its GPU, version and log details. If it still happens on v2.0.6 (https://github.com/NotRexed/Hookshot/releases/tag/v2.0.6; Update next to Play in the launcher), please add a comment there with your hookshot_log.txt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions