perf(vk): keep big ASTC textures as BC7 (RX_VK_ASTC_BC7) #2226
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2226
Loading…
Reference in a new issue
No description provided.
Delete branch "perf/astc-bc7-main"
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?
Big decoded ASTC textures (terrain, walls, the material arrays) are re-encoded to BC3 today to keep them at 8 bits a texel. BC7 mode 6 uses the same 8 bits with much better gradients and alpha, so the grass, paths, stone and tree textures lose visibly less.
RX_VK_ASTC_BC7=1keeps those textures as BC7. The encoder is a compute shader port of the WebGPU back end's CPU encoder (gpu/render/rx_bc7.cpp): a covariance fit, 7-bit endpoints with p-bits and 4-bit indices, 16 bytes a block, layer-major as the BC3 path writes. Off unless set; the BC3 path is unchanged, andRX_VK_ASTC_RGBA8=1still wins when both are set.Tested at 1920x1080 on an RTX 3080: Kokiri Forest holds 60 fps, the same graphics memory as BC3, and no texture artifacts. The shader source and the generated header are checked in;
scripts/make_bc7_shader.pyrebuilds the header with glslang.Thanks, this is a nice opt-in: off unless
RX_VK_ASTC_BC7=1, the BC3 path untouched, andRX_VK_ASTC_RGBA8still wins.What I checked and looks right:
view_format_ofpicks BC7_SRGB/UNORM throughlinear_twinexactly as BC3 did. The scratch holds the stored bytes, so sRGB data stays sRGB.bc_encode_intoalways pushesbc3 = 1, out_offset = 0, so the shader ignoringbc3is fine, and 16 bytes a block matches the copy.bctexture are still refused, partial uploads and CPU writes still go through thetex.bcchecks, and the AMD resized path goes throughbc_encode_intotoo. HDR ASTC is unchanged (both paths encode the decoder's RGBA8).createcounts anyx.bcimage at 1 byte a texel, so BC7 costs the same as BC3, including after #2288.What I'd like before merge:
tests/. The repo's convention (seetests/test_fg_scale_sharpen.py) is that the shader's test holds the glslang command and checks that the_spv.hwas built from this exact.comp(a hash on its first line). With a standalone script, the header can drift from the source without anyone noticing. A game-free check of the packing would also be cheap, e.g. a small CPU decode of a mode-6 block cut from the shader's logic, or a comparison againstgpu/render/rx_bc7.cpp. Please register it with run_all.py the usual way.px[16][4], a 16x4 palette and twoFits with 16 indices, which is heavy on registers and may spill on AMD and Intel iGPUs. The 90-layer material arrays are encoded one layer per run. If anyone can,vk: slow uploadlog lines for BC3 vs BC7 on an AMD or Intel card would show whether loading areas stutter more.CI: the Windows build passes. The AppImage failure is the same apt network timeout main has right now (not this PR). The Python source checks have not run yet (pending).
@ -0,0 +12,4 @@// encoder of the WebGPU back end (gpu/render/rx_bc7.cpp, RX_ASTC_BC7) as a compute shader: BC7's// mode 6 keeps gradients and alpha far better than BC3 at the same 8 bits a texel.//// Binding 0: the RGBA8 scratch (one layer per dispatch z).Nit: the other host shaders and their
_spv.hheaders start with SPDX lines and a provenance note ("NOT a Nintendo file ... compiled from ..."). It would be good to match that here and inbc7_encoder_comp_spv.h.@ -0,0 +140,4 @@for (int a = 0; a < 4; ++a)for (int b = 0; b < 4; ++b) cov[a][b] += d[a] * d[b];}float axis[4] = {cov[0][0], cov[1][1], cov[2][2], cov[3][3]};Nit (inherited from rx_bc7.cpp): starting the power iteration from the diagonal can land exactly orthogonal to the principal axis. One example is R and G anticorrelated with equal variance (a red-to-green ramp), B and A constant:
ncomes out 0, the loop breaks, every texel projects to 0, and the block becomes flat at the mean. The refit can't rescue it because every index is the same, so det = 0. Restarting from the covariance row with the largest norm when the first step collapses would avoid it.@ -712,3 +744,3 @@state = 1;logf("vk: big ASTC textures are kept as BC3 (re-encoded on the GPU; ASTC as RGBA8 or RX_VK_ASTC_RGBA8=1: as RGBA8)");logf("vk: big ASTC textures are kept as %s (re-encoded on the GPU; ASTC as RGBA8 or RX_VK_ASTC_RGBA8=1: as RGBA8)", bc7 ? "BC7" : "BC3");return true;If BC7 isn't sampleable here, or the BC7 pipeline fails at line 731,
bc_readyreturns false and every big ASTC texture becomes RGBA8, which is 4x the memory. Falling back to BC3 (clear a flag sobc7_enabled()reads false afterwards) would be safer. It's unlikely on desktop, so this is a nit.Nit: stray blank line.
Opt-in, so the risk is low, but a few things:
SHADERSlist inscripts/host_shaders.py, whichtests/test_host_shaders.pychecks. This PR adds its ownscripts/make_bc7_shader.pywith a different target (vulkan1.2), sobc7_encoder_comp_spv.hcan drift without anything failing. Please addbc7_encoderto that list and drop the private script.vk_texture.cpp:731),bc_ready()returns false and turns off BC3 too, so big ASTC textures become RGBA8 (about 4x the memory) instead of falling back to BC3.test_native_astc_texture_count.pyedit duplicates #2397 and can be dropped.Re-checked after the push. It's a rebase onto main: the PR's diff is byte-for-byte the same as the commit I reviewed, so nothing from the first round has changed yet. CI is all green now (the Python source checks were still pending last time) and it merges cleanly.
Still open:
bc7_encodertoSHADERSinscripts/host_shaders.pyand dropscripts/make_bc7_shader.py.tests/test_host_shaders.pythen recompiles it and fails on a mismatch, and the header gets the same opening comment as the others. Note thathost_shaders.pybuilds with--target-env vulkan1.1, not thevulkan1.2the private script uses, so the header has to be regenerated that way.gpu/render/rx_bc7.cpp) would still be nice to have, but with item 1 done I wouldn't hold the PR for it.vk_texture.cpp): the textures become RGBA8 rather than BC3. Joey raised it too. It's unlikely on desktop, but it's a one-flag fix.The nits (the power-iteration restart, the SPDX/provenance lines, the stray blank line) are also unchanged and still optional. Joey's note about the
test_native_astc_texture_count.pyedit doesn't apply: the PR's diff doesn't touch that file (main already has #2397).Thanks — the new head (
57e53f8b, rebased ona5b955fb) addresses the code points:bc7_encoderis now inscripts/host_shaders.py'sSHADERS; the privatescripts/make_bc7_shader.pyis gone; the header was regenerated with glslang 16.6.0 atvulkan1.1, andtests/test_host_shaders.pypasses. The shader and header have the SPDX/provenance opening lines.bc7_okturns the encoder and the textures to BC3 from then on — not RGBA8.I have local BC3-vs-BC7 comparisons at the same spot, including alpha-tested foliage; I can attach them here if game screenshots are acceptable in the thread. The packing check is not in yet; the header check covers the source/header drift, and I can add the CPU mode-6 decode if you want to hold for it.
Test report — BC7 for big ASTC textures
Machine
v2.0.11-1024-g9bc83387(source 5d0560d3),4a53c897+ BC7 + zstd; 4 GB texture budget,RX_VK_COMPACT_PCT=90,RX_VK_READBACK_NO_CATCHUP=1,RX_VK_SYNC_LIVE=1, AF 16x, HDR off, 1080p.Checks
tests/test_host_shaders.pywith glslang 16.6.0 (vulkan1.1): 4 match, includingbc7_encoder; the header is regenerated from the.comp, so it cannot drift.RX_VK_ONLYDLL build: pass, with the BC7 shader and the fallback.bc7_ok: a GPU without BC7, or a failed BC7 pipeline, falls back to BC3 (not RGBA8); the power iteration restarts from the largest covariance row when the diagonal collapses; the shader/header carry the SPDX/provenance lines.BC3 vs BC7, same save and camera
Same save, Kokiri Forest/Kakariko entrance, 1920x1080, 300-frame screenshots. The only change is
RX_VK_ASTC_BC7=0vs=1; the log says "big ASTC textures are kept as BC3/BC7" in each run.The two PNGs are local, and I have not uploaded them. I can provide sanitized copies if game screenshots are acceptable in the thread.
Limits
px[16][4]/twoFits, and the 90-layer arrays) remains unmeasured here.test_psychov35.py(scanf) andtest_pcache_header_read.py(fopen/fseeko), the same on the other PRs.github-sync referenced this pull request2026-11-12 06:22:54 +00:00