From 4e91a3d05302e1d365d0c0a2c95e89915ec2bc0c Mon Sep 17 00:00:00 2001 From: seedds Date: Sat, 24 Oct 2026 01:36:50 +0800 Subject: [PATCH] Readable-tile variants compile again for pixel shaders with an integer-output entry point (main0_u), from PR #83 Since 626e6a6 a pixel shader whose outputs look like integer writes gets a second entry point, main0_u, appended after main0. dmsh_fetch.variant, which makes the framebuffer-fetch variant (_bf), rewrote samples to fb0..fb7 in the whole source, main0_u included, but declared them only on main0, and renamed main0_out's members, which main0_u still reads: every variant of such a shader failed to compile (use of undeclared identifier 'fb0'), and the shader lost the tile read (its render passes are not merged). The host takes main0_u from the shader's own library (nvn_mtl.m entry_u), never from the variant, so the variant is now made from the source cut at struct main0_out_u, which dmsh_build.py always appends with main0_u at the end. A main0 with no parameters also got its fb parameters with a leading comma (main0(, float4 fb0 ...)); output-mapping-only variants only. Checked on Breath of the Wild's library MSL (chain 534b10d1d578, 10,560 pixel shaders), old and new variant() side by side: the 8,659 shaders without main0_u give byte-identical variants, with and without output-mapping-only; all 1,639 changed variants of shaders with main0_u failed to compile before and compile now (Mac), and 1,897 of 1,897 output-mapping-only ones when forced for every key (30 of them the leading comma; no profile's bf_want.tsv lists a key today); 40 of them compiled for iOS too. A full Mac dmsh_build.py run on the new chain 657c42552d82: 19,592 of 19,592 shaders and 6,254 variants with no compile failure (main's chain would lose the 1,639), stamped, both tables made; the Mac host booted Breath of the Wild with it past frame 5,100, its title presses into play, with no red line. dm/tools/tests pass. dmsh_fetch.py is part of the chain, so dm/gfx/nvn/dmsh_chain.h is written again and every game's shaders rebuild at their next build. Co-authored-by: v0lumehi <34629096+v0lumehi@users.noreply.github.com> --- dm/gfx/nvn/dmsh_chain.h | 2 +- dm/gfx/tools/dmsh_fetch.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/dm/gfx/nvn/dmsh_chain.h b/dm/gfx/nvn/dmsh_chain.h index ba7a955..448392c 100644 --- a/dm/gfx/nvn/dmsh_chain.h +++ b/dm/gfx/nvn/dmsh_chain.h @@ -1,3 +1,3 @@ // Generated file (dm/gfx/tools/dmsh_chain.py --header): the B shader library translation chain version this source tree expects (default options), one for every game; // the host loads only /b-/. Run dmsh_chain.py --header after changing the chain's tools and commit this file with the change -#define DMSH_CHAIN "3f4cd4aacd1d" +#define DMSH_CHAIN "657c42552d82" diff --git a/dm/gfx/tools/dmsh_fetch.py b/dm/gfx/tools/dmsh_fetch.py index 843f4d0..f366d7b 100644 --- a/dm/gfx/tools/dmsh_fetch.py +++ b/dm/gfx/tools/dmsh_fetch.py @@ -10,6 +10,11 @@ import re NFB = 8 def variant(msl, bindings, om_only_ok=False): + # main0_u (dmsh_build.py int_outputs: integer colour targets) is appended to the source after main0; the host takes it from the shader's own library, never from this variant. + # Its body reads main0's output struct, whose members the variant renames, and its samples were rewritten to fb0–fb7, which only main0 declares: 483 of Breath of the + # Wild's variants failed to compile once their cached copies were gone (use of undeclared identifier 'fb0'). The variant is built from the source without it + cut = msl.find('\nstruct main0_out_u\n') + if cut >= 0: msl = msl[:cut + 1] tex = {m.group(1) for m in re.finditer(r'texture2d (\w+) \[\[id\(\d+\)\]\];', msl)} locn = {m.group(1): int(m.group(2)) for m in re.finditer(r'float4 (m_\d+) \[\[user\(locn(\d+)\)\]\];', msl.split('struct main0_in', 1)[-1].split('};', 1)[0])} reg = {b['msl_name']: b['reg'] for b in bindings if b.get('kind') == 'tex' and b.get('msl_name') and b.get('reg') is not None} @@ -46,7 +51,9 @@ def variant(msl, bindings, om_only_ok=False): i0 = body.find('fragment main0_out main0(') i1 = body.find(')\n{', i0) if i0 >= 0 else -1 # end of signature: closing parenthesis immediately followed by a newline and an opening brace (parameter attributes contain brackets such as [[buffer(0)]]) if i0 < 0 or i1 < 0: return None, 'main function signature not recognized' - body = body[:i1] + ''.join(f', float4 fb{k} [[color({k}), function_constant(FB_{k})]]' for k in range(NFB)) + body[i1:] + fbs = ''.join(f', float4 fb{k} [[color({k}), function_constant(FB_{k})]]' for k in range(NFB)) + if body[i1 - 1] == '(': fbs = fbs[2:] # main0() with no parameters at all: no leading comma (30 of Breath of the Wild's output-mapping-only variants) + body = body[:i1] + fbs + body[i1:] decl = ''.join(f' {t} spv_o_{n} = {t}(0);\n' for t, n, c in outs) body = body.replace(' main0_out out = {};\n', ' main0_out out = {};\n' + decl, 1) for t, n, c in outs: body = body.replace(f'out.{n}', f'spv_o_{n}')