fix(vk): prevent device loss when rendering to 3D BC slices #2327

Merged
lbmeng merged 1 commit from fix-gtx1080-noise-volume into main 2026-11-11 16:39:57 +00:00
lbmeng commented 2026-11-11 15:05:08 +00:00 (Migrated from github.com)

The opening cutscene crashes on a GTX 1080 with driver 581.80 when the noise pass renders to a raw color view of a BC4 volume. A standalone Vulkan probe reproduces device loss by clearing the attachment without running any game shaders.

Load each compressed slice into an uncompressed attachment with one texel per block, then copy the result back after rendering. Use the same path for texture clears and preserve mip and slice addressing, including partial blocks at the edges.

On the GTX 1080, the standalone probe read back all 16,384 blocks correctly. Hookshot now recorded all 128 noise draws and reached the Great Deku Tree and Navi dialogue without device loss. The full Windows test suite had the same seven failures as the baseline.

Refs: #996
Refs: #1001
Refs: #1338

The opening cutscene crashes on a GTX 1080 with driver 581.80 when the noise pass renders to a raw color view of a BC4 volume. A standalone Vulkan probe reproduces device loss by clearing the attachment without running any game shaders. Load each compressed slice into an uncompressed attachment with one texel per block, then copy the result back after rendering. Use the same path for texture clears and preserve mip and slice addressing, including partial blocks at the edges. On the GTX 1080, the standalone probe read back all 16,384 blocks correctly. Hookshot now recorded all 128 noise draws and reached the Great Deku Tree and Navi dialogue without device loss. The full Windows test suite had the same seven failures as the baseline. Refs: #996 Refs: #1001 Refs: #1338
hckrman101 (Migrated from github.com) approved these changes 2026-11-11 16:30:03 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, this looks right to me. A separate uncompressed 3D image per level (one texel a block), loaded from the BC volume before the pass and copied back after it, keeps the noise pass and the compressed sampling format without ever binding a raw view of the BC volume. I went through the pieces that could bite:

  • Copies: BC4 and the attachment format are both 8-byte blocks, so the compressed/uncompressed vkCmdCopyImage is legal. The extents are right both ways (source texels for compressed to raw, blocks for raw to compressed), and partial edge blocks are handled.
  • Sync: the full barriers around the loads in begin_pass and the stores in end_pass replace pass_start_barrier only when a block image is involved, and the new image's UNDEFINED to GENERAL change goes through upload_cmd() under InitRedirect, the same as other new images.
  • Pass bookkeeping: block_image[i] is zeroed for empty slots and cleared on duplicate views, so end_pass never copies a stale slot back.
  • Lifetime: the block images go to dead with the texture's last_value, and their views stay in tex->views, so they're destroyed with the texture's other views. BC textures never go to the render target pool, so no pooled view_map keeps a view of a freed block image. Compaction clears t->views, and they're made again from the kept block image. The bytes are counted in pad, so total_bytes comes back down.
  • clear_tex: volume clears now go over s.z .. s.z + s.d instead of one layer. Whole clears of 2D BC images also stop using vkCmdClearColorImage, which isn't valid on a compressed format anyway (VUID-vkCmdClearColorImage-image-00007). Both are good changes.

Non-blocking:

  • The copy path now runs on every GPU, not just Pascal, and so far it has only been tested on the GTX 1080. It's cheap (a 64x64-block slice each way per pass), but a quick check on AMD/RDNA or a recent NVIDIA card would help: trees at mid distance should still crossfade as #1151 fixed them, and no new validation errors around the copies.
  • CI hasn't run yet: the build and test workflows are waiting for maintainer approval (first-time contributor), so test_bc_raw_targets.py and test_vulkan_clear_mip.py still need a run in CI.

Good to merge once CI is green.

Thanks, this looks right to me. A separate uncompressed 3D image per level (one texel a block), loaded from the BC volume before the pass and copied back after it, keeps the noise pass and the compressed sampling format without ever binding a raw view of the BC volume. I went through the pieces that could bite: - Copies: BC4 and the attachment format are both 8-byte blocks, so the compressed/uncompressed `vkCmdCopyImage` is legal. The extents are right both ways (source texels for compressed to raw, blocks for raw to compressed), and partial edge blocks are handled. - Sync: the full barriers around the loads in `begin_pass` and the stores in `end_pass` replace `pass_start_barrier` only when a block image is involved, and the new image's UNDEFINED to GENERAL change goes through `upload_cmd()` under `InitRedirect`, the same as other new images. - Pass bookkeeping: `block_image[i]` is zeroed for empty slots and cleared on duplicate views, so `end_pass` never copies a stale slot back. - Lifetime: the block images go to `dead` with the texture's `last_value`, and their views stay in `tex->views`, so they're destroyed with the texture's other views. BC textures never go to the render target pool, so no pooled `view_map` keeps a view of a freed block image. Compaction clears `t->views`, and they're made again from the kept block image. The bytes are counted in `pad`, so `total_bytes` comes back down. - `clear_tex`: volume clears now go over `s.z .. s.z + s.d` instead of one layer. Whole clears of 2D BC images also stop using `vkCmdClearColorImage`, which isn't valid on a compressed format anyway (VUID-vkCmdClearColorImage-image-00007). Both are good changes. Non-blocking: - The copy path now runs on every GPU, not just Pascal, and so far it has only been tested on the GTX 1080. It's cheap (a 64x64-block slice each way per pass), but a quick check on AMD/RDNA or a recent NVIDIA card would help: trees at mid distance should still crossfade as #1151 fixed them, and no new validation errors around the copies. - CI hasn't run yet: the build and test workflows are waiting for maintainer approval (first-time contributor), so `test_bc_raw_targets.py` and `test_vulkan_clear_mip.py` still need a run in CI. Good to merge once CI is green.
Commenting is not possible because the repository is archived.
No description provided.