Decode the upscaler stand-in's picture with a square, emit SURED with its real opcode and keep the translator's warnings.

The post pass stores the square root of each channel, so the stand-in for Ocarina's untranslatable upscaler now
filters those values and squares them in its compute pass (shaders/fallback_tonemap.spvasm, vk_tonemap.c) instead of
decoding them with the sRGB curve, which darkened mid-tones and lifted the darkest values; the pass is compiled in again,
its round-24 device loss having been the push descriptor call outside the recorder. NX2NAVI_FALLBACK_EXPOSURE is gone.
AmpereLowering emitted SURED as 0xeb48, which matches no Maxwell instruction, so the translator skipped every image
reduction; with 0xeb58 the edge AA's blend-weight pass writes its image. The translator's own warnings go to res.json
notes, and shader dumps are named by program serial so they match the frame dumps' program numbers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-06 13:11:58 -05:00
parent 5dfa690d96
commit eed3e6b786
8 changed files with 92 additions and 123 deletions

View File

@@ -30,7 +30,7 @@ static int save_ok = 1; // the last cfg_save_to wrote (o
// Every name the runtime reads (and the patch sets in generated/), without the prefix. A file key outside this list is
// logged as unknown. A new variable goes here too, or its ini key logs a harmless "unknown key" line.
static const char *const known[] = {
"CORES", "FALLBACK_EXPOSURE", "TM_DISPATCH", "NVN2_CS_TABLE", "SKIP_SHADERS", "MOVIE_DEBAND", "MOVIE_SKIP", "MOVIE_OVERLAY", "MOVIES", "NPLN", "NVN2_PROGRAMS", "NVN_TRAP", "SHADER_DUMP", "SHOT_DIR", "SHOT_EVERY", // Switch 2 port: movie skip (romfs.c), NVN2 proc traps (nvn_procs.c)
"CORES", "TM_DISPATCH", "NVN2_CS_TABLE", "SKIP_SHADERS", "MOVIE_DEBAND", "MOVIE_SKIP", "MOVIE_OVERLAY", "MOVIES", "NPLN", "NVN2_PROGRAMS", "NVN_TRAP", "SHADER_DUMP", "SHOT_DIR", "SHOT_EVERY", // Switch 2 port: movie skip (romfs.c), NVN2 proc traps (nvn_procs.c)
"AB", "AMIIBO_DIR", "ANISO", "APP_POOL", "ASPECT", "ASPECT_PATCH", "ASSUME_FOCUS", "ASTC", "ASTC_BC", "ASTC_TIME", "ASYNC_PREEMPT", "AUDIO", "AUDIO_CHANNELS", "AUDIO_OFF", "AUDIO_PREFILL_MS",
"BACKGROUND_WINDOW", "BIND_A", "BIND_B", "BIND_DOWN", "BIND_L", "BIND_LCLICK", "BIND_LEFT", "BIND_LSTICK_DOWN", "BIND_LSTICK_LEFT",
"BIND_LSTICK_RIGHT", "BIND_LSTICK_UP", "BIND_MINUS",

View File

@@ -1,63 +1,47 @@
// Generated from runtime/shaders/fallback_tonemap.spvasm (spirv-as --target-env vulkan1.3); do not edit.
// source sha256 31e5bd112d259f6d65bd0d488b2bbe1dd0b2183ad1aca69f666c17e0589d447f
// source sha256 5785ee27470b43df9ce7dea3285bdcee404ba2ff220979b7d2c88280c6b1906b
#pragma once
#include <stdint.h>
static const uint32_t fallback_tonemap_spv[] = {
0x07230203, 0x00010600, 0x00070000, 0x00000052, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001,
0x0009000f, 0x00000005, 0x00000002, 0x6e69616d, 0x00000000, 0x00000003, 0x00000004, 0x00000005,
0x00000006, 0x00060010, 0x00000002, 0x00000011, 0x00000008, 0x00000008, 0x00000001, 0x00040047,
0x00000003, 0x0000000b, 0x0000001c, 0x00040047, 0x00000004, 0x00000022, 0x00000000, 0x00040047,
0x00000004, 0x00000021, 0x00000000, 0x00040047, 0x00000005, 0x00000022, 0x00000000, 0x00040047,
0x00000005, 0x00000021, 0x00000001, 0x00030047, 0x00000005, 0x00000019, 0x00030047, 0x00000007,
0x00000002, 0x00050048, 0x00000007, 0x00000000, 0x00000023, 0x00000000, 0x00050048, 0x00000007,
0x00000001, 0x00000023, 0x00000008, 0x00050048, 0x00000007, 0x00000002, 0x00000023, 0x0000000c,
0x00050048, 0x00000007, 0x00000003, 0x00000023, 0x00000010, 0x00020013, 0x00000008, 0x00030021,
0x00000009, 0x00000008, 0x00030016, 0x0000000a, 0x00000020, 0x00040015, 0x0000000b, 0x00000020,
0x00000001, 0x00040015, 0x0000000c, 0x00000020, 0x00000000, 0x00020014, 0x0000000d, 0x00040017,
0x0000000e, 0x0000000a, 0x00000002, 0x00040017, 0x0000000f, 0x0000000a, 0x00000003, 0x00040017,
0x00000010, 0x0000000a, 0x00000004, 0x00040017, 0x00000011, 0x0000000b, 0x00000002, 0x00040017,
0x00000012, 0x0000000c, 0x00000003, 0x00040017, 0x00000013, 0x0000000c, 0x00000002, 0x00040020,
0x00000014, 0x00000001, 0x00000012, 0x0004003b, 0x00000014, 0x00000003, 0x00000001, 0x00090019,
0x00000015, 0x0000000a, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
0x0003001b, 0x00000016, 0x00000015, 0x00040020, 0x00000017, 0x00000000, 0x00000016, 0x0004003b,
0x00000017, 0x00000004, 0x00000000, 0x00090019, 0x00000018, 0x0000000a, 0x00000001, 0x00000000,
0x00000000, 0x00000000, 0x00000002, 0x00000002, 0x00040020, 0x00000019, 0x00000000, 0x00000018,
0x0004003b, 0x00000019, 0x00000005, 0x00000000, 0x0006001e, 0x00000007, 0x0000000e, 0x0000000a,
0x0000000a, 0x00000011, 0x00040020, 0x0000001a, 0x00000009, 0x00000007, 0x0004003b, 0x0000001a,
0x00000006, 0x00000009, 0x00040020, 0x0000001b, 0x00000009, 0x0000000e, 0x00040020, 0x0000001c,
0x00000009, 0x0000000a, 0x00040020, 0x0000001d, 0x00000009, 0x00000011, 0x0004002b, 0x0000000b,
0x0000001e, 0x00000000, 0x0004002b, 0x0000000b, 0x0000001f, 0x00000001, 0x0004002b, 0x0000000b,
0x00000020, 0x00000003, 0x0004002b, 0x0000000a, 0x00000021, 0x00000000, 0x0004002b, 0x0000000a,
0x00000022, 0x3f800000, 0x0004002b, 0x0000000a, 0x00000023, 0x3f000000, 0x0004002b, 0x0000000a,
0x00000024, 0x4020a3d7, 0x0004002b, 0x0000000a, 0x00000025, 0x3cf5c28f, 0x0004002b, 0x0000000a,
0x00000026, 0x401b851f, 0x0004002b, 0x0000000a, 0x00000027, 0x3f170a3d, 0x0004002b, 0x0000000a,
0x00000028, 0x3e0f5c29, 0x0005002c, 0x0000000e, 0x00000029, 0x00000023, 0x00000023, 0x0006002c,
0x0000000f, 0x0000002a, 0x00000021, 0x00000021, 0x00000021, 0x0006002c, 0x0000000f, 0x0000002b,
0x00000022, 0x00000022, 0x00000022, 0x0006002c, 0x0000000f, 0x0000002c, 0x00000024, 0x00000024,
0x00000024, 0x0006002c, 0x0000000f, 0x0000002d, 0x00000025, 0x00000025, 0x00000025, 0x0006002c,
0x0000000f, 0x0000002e, 0x00000026, 0x00000026, 0x00000026, 0x0006002c, 0x0000000f, 0x0000002f,
0x00000027, 0x00000027, 0x00000027, 0x0006002c, 0x0000000f, 0x00000030, 0x00000028, 0x00000028,
0x00000028, 0x00040017, 0x00000031, 0x0000000d, 0x00000002, 0x00050036, 0x00000008, 0x00000002,
0x00000000, 0x00000009, 0x000200f8, 0x00000032, 0x0004003d, 0x00000012, 0x00000033, 0x00000003,
0x0007004f, 0x00000013, 0x00000034, 0x00000033, 0x00000033, 0x00000000, 0x00000001, 0x0004007c,
0x00000011, 0x00000035, 0x00000034, 0x00050041, 0x0000001d, 0x00000036, 0x00000006, 0x00000020,
0x0004003d, 0x00000011, 0x00000037, 0x00000036, 0x000500af, 0x00000031, 0x00000038, 0x00000035,
0x00000037, 0x0004009a, 0x0000000d, 0x00000039, 0x00000038, 0x000300f7, 0x0000003a, 0x00000000,
0x000400fa, 0x00000039, 0x0000003b, 0x0000003a, 0x000200f8, 0x0000003b, 0x000100fd, 0x000200f8,
0x0000003a, 0x0004006f, 0x0000000e, 0x0000003c, 0x00000035, 0x00050081, 0x0000000e, 0x0000003d,
0x0000003c, 0x00000029, 0x00050041, 0x0000001b, 0x0000003e, 0x00000006, 0x0000001e, 0x0004003d,
0x0000000e, 0x0000003f, 0x0000003e, 0x00050085, 0x0000000e, 0x00000040, 0x0000003d, 0x0000003f,
0x0004003d, 0x00000016, 0x00000041, 0x00000004, 0x00070058, 0x00000010, 0x00000042, 0x00000041,
0x00000040, 0x00000002, 0x00000021, 0x0008004f, 0x0000000f, 0x00000043, 0x00000042, 0x00000042,
0x00000000, 0x00000001, 0x00000002, 0x00050041, 0x0000001c, 0x00000044, 0x00000006, 0x0000001f,
0x0004003d, 0x0000000a, 0x00000045, 0x00000044, 0x0005008e, 0x0000000f, 0x00000046, 0x00000043,
0x00000045, 0x00050085, 0x0000000f, 0x00000047, 0x00000046, 0x0000002c, 0x00050081, 0x0000000f,
0x00000048, 0x00000047, 0x0000002d, 0x00050085, 0x0000000f, 0x00000049, 0x00000046, 0x00000048,
0x00050085, 0x0000000f, 0x0000004a, 0x00000046, 0x0000002e, 0x00050081, 0x0000000f, 0x0000004b,
0x0000004a, 0x0000002f, 0x00050085, 0x0000000f, 0x0000004c, 0x00000046, 0x0000004b, 0x00050081,
0x0000000f, 0x0000004d, 0x0000004c, 0x00000030, 0x00050088, 0x0000000f, 0x0000004e, 0x00000049,
0x0000004d, 0x0008000c, 0x0000000f, 0x0000004f, 0x00000001, 0x0000002b, 0x0000004e, 0x0000002a,
0x0000002b, 0x00050050, 0x00000010, 0x00000050, 0x0000004f, 0x00000022, 0x0004003d, 0x00000018,
0x00000051, 0x00000005, 0x00040063, 0x00000051, 0x00000035, 0x00000050, 0x000100fd, 0x00010038,
0x07230203, 0x00010600, 0x00070000, 0x00000038, 0x00000000, 0x00020011, 0x00000001, 0x0003000e,
0x00000000, 0x00000001, 0x0009000f, 0x00000005, 0x00000001, 0x6e69616d, 0x00000000, 0x00000002,
0x00000003, 0x00000004, 0x00000005, 0x00060010, 0x00000001, 0x00000011, 0x00000008, 0x00000008,
0x00000001, 0x00040047, 0x00000002, 0x0000000b, 0x0000001c, 0x00040047, 0x00000003, 0x00000022,
0x00000000, 0x00040047, 0x00000003, 0x00000021, 0x00000000, 0x00040047, 0x00000004, 0x00000022,
0x00000000, 0x00040047, 0x00000004, 0x00000021, 0x00000001, 0x00030047, 0x00000004, 0x00000019,
0x00030047, 0x00000006, 0x00000002, 0x00050048, 0x00000006, 0x00000000, 0x00000023, 0x00000000,
0x00050048, 0x00000006, 0x00000001, 0x00000023, 0x00000008, 0x00050048, 0x00000006, 0x00000002,
0x00000023, 0x0000000c, 0x00050048, 0x00000006, 0x00000003, 0x00000023, 0x00000010, 0x00020013,
0x00000007, 0x00030021, 0x00000008, 0x00000007, 0x00030016, 0x00000009, 0x00000020, 0x00040015,
0x0000000a, 0x00000020, 0x00000001, 0x00040015, 0x0000000b, 0x00000020, 0x00000000, 0x00020014,
0x0000000c, 0x00040017, 0x0000000d, 0x00000009, 0x00000002, 0x00040017, 0x0000000e, 0x00000009,
0x00000003, 0x00040017, 0x0000000f, 0x00000009, 0x00000004, 0x00040017, 0x00000010, 0x0000000a,
0x00000002, 0x00040017, 0x00000011, 0x0000000b, 0x00000003, 0x00040017, 0x00000012, 0x0000000b,
0x00000002, 0x00040020, 0x00000013, 0x00000001, 0x00000011, 0x0004003b, 0x00000013, 0x00000002,
0x00000001, 0x00090019, 0x00000014, 0x00000009, 0x00000001, 0x00000000, 0x00000000, 0x00000000,
0x00000001, 0x00000000, 0x0003001b, 0x00000015, 0x00000014, 0x00040020, 0x00000016, 0x00000000,
0x00000015, 0x0004003b, 0x00000016, 0x00000003, 0x00000000, 0x00090019, 0x00000017, 0x00000009,
0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000002, 0x00040020, 0x00000018,
0x00000000, 0x00000017, 0x0004003b, 0x00000018, 0x00000004, 0x00000000, 0x0006001e, 0x00000006,
0x0000000d, 0x00000009, 0x00000009, 0x00000010, 0x00040020, 0x00000019, 0x00000009, 0x00000006,
0x0004003b, 0x00000019, 0x00000005, 0x00000009, 0x00040020, 0x0000001a, 0x00000009, 0x0000000d,
0x00040020, 0x0000001b, 0x00000009, 0x00000010, 0x0004002b, 0x0000000a, 0x0000001c, 0x00000000,
0x0004002b, 0x0000000a, 0x0000001d, 0x00000003, 0x0004002b, 0x00000009, 0x0000001e, 0x00000000,
0x0004002b, 0x00000009, 0x0000001f, 0x3f800000, 0x0004002b, 0x00000009, 0x00000020, 0x3f000000,
0x0005002c, 0x0000000d, 0x00000021, 0x00000020, 0x00000020, 0x00040017, 0x00000022, 0x0000000c,
0x00000002, 0x00050036, 0x00000007, 0x00000001, 0x00000000, 0x00000008, 0x000200f8, 0x00000023,
0x0004003d, 0x00000011, 0x00000024, 0x00000002, 0x0007004f, 0x00000012, 0x00000025, 0x00000024,
0x00000024, 0x00000000, 0x00000001, 0x0004007c, 0x00000010, 0x00000026, 0x00000025, 0x00050041,
0x0000001b, 0x00000027, 0x00000005, 0x0000001d, 0x0004003d, 0x00000010, 0x00000028, 0x00000027,
0x000500af, 0x00000022, 0x00000029, 0x00000026, 0x00000028, 0x0004009a, 0x0000000c, 0x0000002a,
0x00000029, 0x000300f7, 0x0000002b, 0x00000000, 0x000400fa, 0x0000002a, 0x0000002c, 0x0000002b,
0x000200f8, 0x0000002c, 0x000100fd, 0x000200f8, 0x0000002b, 0x0004006f, 0x0000000d, 0x0000002d,
0x00000026, 0x00050081, 0x0000000d, 0x0000002e, 0x0000002d, 0x00000021, 0x00050041, 0x0000001a,
0x0000002f, 0x00000005, 0x0000001c, 0x0004003d, 0x0000000d, 0x00000030, 0x0000002f, 0x00050085,
0x0000000d, 0x00000031, 0x0000002e, 0x00000030, 0x0004003d, 0x00000015, 0x00000032, 0x00000003,
0x00070058, 0x0000000f, 0x00000033, 0x00000032, 0x00000031, 0x00000002, 0x0000001e, 0x0008004f,
0x0000000e, 0x00000034, 0x00000033, 0x00000033, 0x00000000, 0x00000001, 0x00000002, 0x00050085,
0x0000000e, 0x00000035, 0x00000034, 0x00000034, 0x00050050, 0x0000000f, 0x00000036, 0x00000035,
0x0000001f, 0x0004003d, 0x00000017, 0x00000037, 0x00000004, 0x00040063, 0x00000037, 0x00000026,
0x00000036, 0x000100fd, 0x00010038,
};

View File

@@ -447,8 +447,9 @@ static int prog_set_shaders(prog_t *o, int n, const NVNshaderData *sd) {
static void shader_dump(const prog_t *o, int n, const NVNshaderData *sd);
NVN_API(nvnProgramSetShaders) {
NVN_BLOCKING();
shader_dump(arg_p(c, 0), (int)arg_x(c, 1), arg_p(c, 2));
ret_x(c, prog_set_shaders(arg_p(c, 0), (int)arg_x(c, 1), arg_p(c, 2)));
int ok = prog_set_shaders(arg_p(c, 0), (int)arg_x(c, 1), arg_p(c, 2));
shader_dump(arg_p(c, 0), (int)arg_x(c, 1), arg_p(c, 2)); // after: the files carry the program's serial
ret_x(c, ok);
}
// NVNboolean nvnProgramSetShadersExt(NVNprogram *p, int n, const NVNshaderDataExt *sd): no prototype in nvn.h; the
// entries are {data, control, reserved} (0x18 bytes, reserved observed 0)
@@ -470,15 +471,17 @@ NVN_API(nvnProgramSetShadersExt) {
rt_fatal("nvn: ProgramSetShadersExt: unknown non-zero field %#llx in stage %d", (unsigned long long)sd[i].reserved, i);
plain[i] = (NVNshaderData){sd[i].data, sd[i].control};
}
int ok = prog_set_shaders(o, n, plain);
shader_dump(o, n, plain);
ret_x(c, prog_set_shaders(o, n, plain));
ret_x(c, ok);
}
// ---------- NVN2 program builder (Switch 2): SetDefaults / SetDevice / SetShaders on a builder, then
// nvnProgramInitializeFromBuilder(program, builder). No driver reference is available (the NVN2 driver is not in the
// title's modules), so the signatures are taken to mirror nvnProgramSetShaders: SetShaders(builder, n, shader data).
// The builder keeps the caller's array pointer, as NVN builders reference their inputs until the object is made.
// NX2NAVI_SHADER_DUMP=<dir> writes each stage's control section and code there for studying the NVN2 layout.
// NX2NAVI_SHADER_DUMP=<dir> writes each stage's control section and code there for studying the NVN2 layout, named by the
// program's serial (p<serial>_s<stage>), the number frame dumps print as "prog N" (vk_dump.c).
typedef struct { uint32_t magic; int32_t n; NVNdevice *dev; const NVNshaderData *sd; } prog_builder_t;
#define NVN_PROG_BUILDER_MAGIC 0x50524742u // "BGRP"
@@ -507,13 +510,13 @@ static void shader_dump(const prog_t *o, int n, const NVNshaderData *sd) {
}
for (int i = 0; i < n; i++) {
char path[1100];
snprintf(path, sizeof path, "%s/p%04d_s%d.ctl", dir, k, i);
snprintf(path, sizeof path, "%s/p%04llu_s%d.ctl", dir, (unsigned long long)o->serial, i);
if (sd[i].control) plat_file_write(path, sd[i].control, 0x880);
const pool_t *p = nvn_pool_find(sd[i].data, 1);
if (p && p->mem && !nvn_gpu_only(sd[i].data)) {
uint64_t end = (uint64_t)(uintptr_t)p->mem + p->size, len = end - sd[i].data;
if (len > 0x40000) len = 0x40000;
snprintf(path, sizeof path, "%s/p%04d_s%d.code", dir, k, i);
snprintf(path, sizeof path, "%s/p%04llu_s%d.code", dir, (unsigned long long)o->serial, i);
plat_file_write(path, (const void *)(uintptr_t)sd[i].data, (size_t)len);
}
}
@@ -548,8 +551,9 @@ NVN_API(nvnProgramInitializeFromBuilder) {
}
memset(o, 0, sizeof *o);
o->magic = NVN_PROG_MAGIC;
int ok = prog_set_shaders(o, b->n, b->sd);
shader_dump(o, b->n, b->sd);
ret_x(c, prog_set_shaders(o, b->n, b->sd));
ret_x(c, ok);
}
// ---------- State objects: packed in place (nvn_obj.h)

View File

@@ -1,18 +1,20 @@
; Tonemap and scale for the untranslatable upscaler (runtime/vk_exec_draw.c untranslated_dispatch_fallback).
; Scale and decode for the untranslatable scene upscaler (runtime/vk_exec_draw.c untranslated_dispatch_fallback).
; Its input is the post pass's picture with each channel stored as the square root of its linear value (prog 102 packs
; sqrt(composite) into RGB9E5), and its output is linear (the HUD is drawn over it, the frame's last pass encodes to
; sRGB). So: filter in the stored (sqrt) values, as the game's upscaler does, then square. The pass replaced a
; decode with the sRGB curve, which darkened mid-tones and lifted the darkest values.
; Written in SPIR-V assembly (spirv-as; no GLSL compiler in the build). As GLSL:
; layout(local_size_x = 8, local_size_y = 8) in;
; layout(binding = 0) uniform sampler2D src; // the HDR scene (linear filtering)
; layout(binding = 0) uniform sampler2D src; // the stored picture (linear filtering)
; layout(binding = 1, rgba16f) uniform writeonly image2D dst; // the pass output (vk_tonemap.c), blitted into the target
; layout(push_constant) uniform P { vec2 inv; float exposure; float pad; ivec2 size; } pc;
; layout(push_constant) uniform P { vec2 inv; float unused; float pad; ivec2 size; } pc;
; void main() {
; ivec2 p = ivec2(gl_GlobalInvocationID.xy);
; if (p.x >= pc.size.x || p.y >= pc.size.y) return;
; vec3 c = textureLod(src, (vec2(p) + 0.5) * pc.inv, 0.0).rgb * pc.exposure;
; c = clamp(c * (2.51 * c + 0.03) / (c * (2.43 * c + 0.59) + 0.14), 0.0, 1.0); // Narkowicz's ACES fit
; imageStore(dst, p, vec4(c, 1.0));
; vec3 s = textureLod(src, (vec2(p) + 0.5) * pc.inv, 0.0).rgb;
; imageStore(dst, p, vec4(s * s, 1.0));
; }
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %gid %src %dst %pc
OpExecutionMode %main LocalSize 8 8 1
@@ -52,27 +54,13 @@
%ptr_pc_P = OpTypePointer PushConstant %P
%pc = OpVariable %ptr_pc_P PushConstant
%ptr_pc_v2float = OpTypePointer PushConstant %v2float
%ptr_pc_float = OpTypePointer PushConstant %float
%ptr_pc_v2int = OpTypePointer PushConstant %v2int
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int_3 = OpConstant %int 3
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1
%float_half = OpConstant %float 0.5
%float_2_51 = OpConstant %float 2.51
%float_0_03 = OpConstant %float 0.03
%float_2_43 = OpConstant %float 2.43
%float_0_59 = OpConstant %float 0.59
%float_0_14 = OpConstant %float 0.14
%v2half = OpConstantComposite %v2float %float_half %float_half
%v3_0 = OpConstantComposite %v3float %float_0 %float_0 %float_0
%v3_1 = OpConstantComposite %v3float %float_1 %float_1 %float_1
%v3_2_51 = OpConstantComposite %v3float %float_2_51 %float_2_51 %float_2_51
%v3_0_03 = OpConstantComposite %v3float %float_0_03 %float_0_03 %float_0_03
%v3_2_43 = OpConstantComposite %v3float %float_2_43 %float_2_43 %float_2_43
%v3_0_59 = OpConstantComposite %v3float %float_0_59 %float_0_59 %float_0_59
%v3_0_14 = OpConstantComposite %v3float %float_0_14 %float_0_14 %float_0_14
%v2bool = OpTypeVector %bool 2
%main = OpFunction %void None %fnvoid
%entry = OpLabel
@@ -96,18 +84,7 @@
%si = OpLoad %simg2d %src
%s4 = OpImageSampleExplicitLod %v4float %si %uv Lod %float_0
%s3 = OpVectorShuffle %v3float %s4 %s4 0 1 2
%pexp = OpAccessChain %ptr_pc_float %pc %int_1
%exp = OpLoad %float %pexp
%c = OpVectorTimesScalar %v3float %s3 %exp
%a1 = OpFMul %v3float %c %v3_2_51
%a2 = OpFAdd %v3float %a1 %v3_0_03
%num = OpFMul %v3float %c %a2
%b1 = OpFMul %v3float %c %v3_2_43
%b2 = OpFAdd %v3float %b1 %v3_0_59
%b3 = OpFMul %v3float %c %b2
%den = OpFAdd %v3float %b3 %v3_0_14
%t = OpFDiv %v3float %num %den
%tc = OpExtInst %v3float %1 FClamp %t %v3_0 %v3_1
%tc = OpFMul %v3float %s3 %s3
%o4 = OpCompositeConstruct %v4float %tc %float_1
%di = OpLoad %stimg %dst
OpImageWrite %di %p %o4

View File

@@ -28,7 +28,7 @@
#include "vk.h"
#ifndef NX2NAVI_TONEMAP_STANDIN
#define NX2NAVI_TONEMAP_STANDIN 0 // vk_tonemap.c, see untranslated_dispatch_fallback
#define NX2NAVI_TONEMAP_STANDIN 1 // vk_tonemap.c, see untranslated_dispatch_fallback
#endif
#include "vk_exec.h"
#include "platform/platform.h"
@@ -4057,8 +4057,8 @@ static void untranslated_dispatch_fallback(VkExec *ex) {
si = e5;
}
#if NX2NAVI_TONEMAP_STANDIN
// Off: every build with this call lost the device right after the first gameplay load, even with the call never
// reached at run time (NX2NAVI_TM_DISPATCH=2/3), while the build without it ran clean; not understood yet (round 24).
// The square decode of the stored picture (vk_tonemap.c). Builds of round 24 lost the device with it: its descriptor
// push did not go through the recorder (vk_rec.h rec_push; PORT-HANDOFF 7.12 item 11), which it now does.
VkImage tm = e5 && dst_view ? vk_tonemap_record(ex->cb, e5, VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, dw, dh) : VK_NULL_HANDLE;
if (tm) { // the tonemapped picture, already at the target's size, then the same blit as below
B.pipe[1] = VK_NULL_HANDLE; // the replay must bind its compute pipeline again
@@ -4069,11 +4069,12 @@ static void untranslated_dispatch_fallback(VkExec *ex) {
VkImage tm = VK_NULL_HANDLE;
(void)dst_view;
#endif
// The scene upscaler's input is the post pass's picture already sRGB-encoded (prog 102 packs encode(composite)
// into RGB9E5), and its output is linear: the HUD is drawn over it and the frame's last pass encodes to sRGB. A
// plain blit carried the encoded values into the linear target, so the picture was encoded twice (washed out).
// The stand-in decodes: scale into an RGBA8 UNORM scratch (the encoded values), copy those bits into an RGBA8 sRGB
// scratch, and blit that into the target, which reads the sRGB image as linear.
// The scene upscaler's input is the post pass's picture stored as the square root of each linear value (prog 102
// packs sqrt(composite) into RGB9E5), and its output is linear: the HUD is drawn over it and the frame's last pass
// encodes to sRGB. A plain blit carried the stored values into the linear target, so the picture was washed out.
// The pass above squares them. Without it, this approximation decodes with the sRGB curve instead: scale into an
// RGBA8 UNORM scratch (the stored values), copy those bits into an RGBA8 sRGB scratch, and blit that into the
// target, which reads the sRGB image as linear (mid-tones a little dark, the darkest values a little lifted).
int filter_nearest = tm || (e5 && e5_blit == 1);
VkImage g8 = VK_NULL_HANDLE, g8s = VK_NULL_HANDLE;
if (e5 && !tm && !scratch && !fmt_rgba8(df)) {

View File

@@ -1,4 +1,4 @@
// Tonemap pass standing in for the scene upscaler (vk.h vk_tonemap_record; used by vk_exec_draw.c
// Decode-and-scale pass standing in for the scene upscaler (vk.h vk_tonemap_record; used by vk_exec_draw.c
// untranslated_dispatch_fallback).
#include "vk.h"
@@ -9,11 +9,13 @@
#include "fallback_tonemap_spv.h"
#include "host.h"
// The game's upscaler (tensor-core, untranslatable) also tonemaps: its input is the HDR scene in RGB9E5 (values up to a
// few hundred), its output the display picture the HUD is drawn over, and the present after it only converts to sRGB.
// A plain blit left the picture white. This pass (shaders/fallback_tonemap.spvasm) scales with linear filtering and
// maps the colour, times NX2NAVI_FALLBACK_EXPOSURE (default 0.04), through Narkowicz's ACES fit. Not the game's own curve
// or auto exposure: those live in the shader we cannot run.
// The game's upscaler (tensor-core, untranslatable) reads the post pass's picture, whose channels hold the square root of
// their linear value packed as RGB9E5 (prog 102), and writes the linear picture the HUD is drawn over; the frame's last
// pass encodes to sRGB. This pass (shaders/fallback_tonemap.spvasm) scales with linear filtering in the stored values
// and squares them into an RGBA16F image of the target's size, which the caller blits in 1:1. The game composites
// and tonemaps before the post pass, so nothing here changes exposure. (It was a tonemap once, when the input was
// taken for the raw HDR scene; the blit's sRGB decode that stood in since darkened mid-tones and lifted the darkest
// values.)
static struct {
int tried, ok;
PFN_vkCmdPushDescriptorSetKHR push;
@@ -95,7 +97,7 @@ static int tm_init(void) {
ok = vkCreateComputePipelines(c->device, VK_NULL_HANDLE, 1, &ci, NULL, &TM.pipe) == VK_SUCCESS;
}
if (m) vkDestroyShaderModule(c->device, m, NULL);
rt_log("vk_exec: upscaler stand-in: %s", ok ? "tonemap pass (NX2NAVI_FALLBACK_EXPOSURE)" : "blit (the tonemap pass could not be created)");
rt_log("vk_exec: upscaler stand-in: %s", ok ? "scale and square-decode pass" : "blit (its pass could not be created)");
return TM.ok = ok;
}
@@ -120,11 +122,6 @@ VkImage vk_tonemap_record(VkCommandBuffer cb, VkImage src, VkFormat sf, uint32_t
if (disp == 2 || !tm_init() || !tm_out(cb, dw, dh)) return VK_NULL_HANDLE;
VkImageView sv = tm_view(src, sf), dv = TM.out_view;
if (!sv) return VK_NULL_HANDLE;
static float exposure = -1;
if (exposure < 0) {
const char *e = cfg_get("NX2NAVI_FALLBACK_EXPOSURE");
exposure = e && *e ? (float)atof(e) : 0.04f;
}
VkMemoryBarrier2 mb = {.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT};
@@ -138,8 +135,8 @@ VkImage vk_tonemap_record(VkCommandBuffer cb, VkImage src, VkFormat sf, uint32_t
{.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstBinding = 1, .descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, .pImageInfo = &ii[1]}};
rec_push(TM.push, cb, VK_PIPELINE_BIND_POINT_COMPUTE, TM.layout, 0, 2, w); // through the recorder (vk_rec.h), in order
struct { float inv[2], exposure, pad; int32_t size[2]; } pc = {{1.0f / (float)dw, 1.0f / (float)dh}, exposure, 0,
{(int32_t)dw, (int32_t)dh}};
struct { float inv[2], unused, pad; int32_t size[2]; } pc = {{1.0f / (float)dw, 1.0f / (float)dh}, 0, 0,
{(int32_t)dw, (int32_t)dh}};
vkCmdPushConstants(cb, TM.layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof pc, &pc);
if (disp) vkCmdDispatch(cb, (dw + 7) / 8, (dh + 7) / 8, 1);
VkMemoryBarrier2 after = {.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,

View File

@@ -1307,13 +1307,16 @@ static class AmpereLowering
}
// SURED.D.op U32 (bound descriptor): data 32-39, coordinates 24-31, operation 87-89 (ADD MIN MAX INC DEC AND OR
// XOR, as Maxwell's), dimension 61-63 (1D buffer or 2D), descriptor offset 40-53 in bank 54-58.
// XOR, as Maxwell's), dimension 61-63 (1D buffer or 2D), descriptor offset 40-53 in bank 54-58. Maxwell's SURED
// is 1110101101011 in the top bits (0xeb58, Ryujinx InstTable Sured); 0xeb48 matched no instruction, so the
// translator skipped every SURED: Ocarina's edge AA (the blend-weight pass ORs its weights into an image) never
// wrote anything.
void Sured()
{
uint dim = _i.Bits(61, 3), handle = Handle(true), bank = _i.Bits(54, 5);
if ((_i.W2 & ~(7u << 23)) != 0x0010e000 || dim is not 1 and not 3 || _i.Bits(59, 2) != 0 || handle > 0x1fff || !TexBank(bank))
throw Fail("SURED mode");
EmitG(0xeb48000000000000UL | (uint)Gpr(_i.Bits(32, 8)) | ((ulong)Gpr(_i.Bits(24, 8)) << 8) | ((ulong)_i.Bits(87, 3) << 24)
EmitG(0xeb58000000000000UL | (uint)Gpr(_i.Bits(32, 8)) | ((ulong)Gpr(_i.Bits(24, 8)) << 8) | ((ulong)_i.Bits(87, 3) << 24)
| ((ulong)dim << 33) | ((ulong)handle << 36));
}

View File

@@ -37,6 +37,9 @@ static class SwshXlate
public Accessor(byte[] code, JsonObject job) { _code = code; _job = job; _c1 = job["c1"] is JsonNode h ? Convert.FromHexString((string)h) : Array.Empty<byte>(); }
// c1 常数段(着色器包控制段前 0x100 字节):间接跳转 BRX 的跳转表在这里(LDC c[1]);不提供时翻译器解不出跳转目标、控制流出错(粒子着色器死循环)
// c1 constant segment (the 0x100 bytes before the control segment in the shader package): the jump table of indirect jump BRX lives here (LDC c[1]); without it the translator cannot resolve jump targets and control flow goes wrong (particle shaders loop forever)
// The translator's own warnings (a global memory access it could not map to a storage buffer, an invalid
// sampler type, ...) go to res.json "notes" with ours; IGpuAccessor's default dropped them.
public void Log(string message) => Notes.Add(message);
public uint ConstantBuffer1Read(int offset) { if (offset >= 0 && offset + 4 <= _c1.Length) return BitConverter.ToUInt32(_c1, offset); Notes.Add($"c1 读越界 +{offset:X}(c1 长 {_c1.Length})"); return 0; }
// 全部资源放进第 0 组,各占不同绑定号(spirv-cross 出成一个参数缓冲);常量段号 index 的缓冲由翻译器给 binding = index+1 的惯例不依赖
// All resources go into set 0, each with a distinct binding number (spirv-cross emits them as one argument buffer); the translator's convention of giving the buffer of constant segment number index binding = index+1 is not relied on