mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
Strip milestone and task labels from code comments and drop the dead gfx_stubs.c
Comments named the agent task (T1-T7, M2-M5, Task 2.x) that once owned each file or block, and pointed at local notes the repository does not carry (backlog items, milestone plans, research notes). They now say what the code does. gfx_stubs.c held weak stand-ins for functions that every build has defined for a long time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fb51973fa3
commit
97d3762ba1
35 files changed
+103
-128
No files matched your search
@@ -1,6 +1,6 @@
|
||||
// Adapted from MacNativeGames games/totk/runtime/audio_mixer.c @ b75be8c (MIT, Copyright (c) 2026 MacNativeGames
|
||||
// contributors): the mixing stage (voice decode and resampling, splitters, mixes, effects, sinks) and the IAudioDevice
|
||||
// service. The CoreAudio output is replaced by SDL3 audio (docs/architecture.md M5). Effect algorithms follow
|
||||
// service. The CoreAudio output is replaced by SDL3 audio. Effect algorithms follow
|
||||
// Ryujinx.Audio (MIT, Copyright (c) Ryujinx Team and Contributors).
|
||||
//
|
||||
// Per 5 ms frame the render thread (audren.c, renderer lock held) decodes every started voice (PCM8/16/24/32, float,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// contributors): runtime_frame_step_update (2338bb0: no 1/120 s floor) and the speed survey's player chain
|
||||
// (world_survey.c player_actor).
|
||||
// Frame rate: the frame limit and the real-time frame step, the host half of the patch sets fps60, realtime and cloth
|
||||
// (build tables patches-v121.txt, patches-v143.txt; docs/research/macnativegames.md section 6.2).
|
||||
// (build tables patches-v121.txt, patches-v143.txt).
|
||||
//
|
||||
// TotK's adaptive present-interval controller (main+0x8f26c8) turns the main thread's measured work into a number of
|
||||
// 60 Hz ticks clamped to [minimum, maximum]; shipping data sets the minimum to 2 (30 fps), the set fps60 lowers it to
|
||||
|
||||
+7
-8
@@ -1,5 +1,4 @@
|
||||
// Shared interface of the M4 graphics layer. Owned by T1; frozen after T1, changes by
|
||||
// agreement. Every function here has a weak named-fatal stand-in in gfx_stubs.c until its task lands.
|
||||
// Interface of the graphics layer: window, shader service, back ends and the NVN front end.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include "host.h"
|
||||
@@ -17,7 +16,7 @@ const OvNvState *ov_nv_state(void); // ov_gfx.c
|
||||
#define RT_GPU_VA_BASE 0x8000000000ull // 2^39, never mapped on the host
|
||||
#define RT_GPU_VA_SIZE (64ull << 30)
|
||||
|
||||
// ---- window, vsync, focus (gfx_window.c, vk_device.c: T2). Host threads only; never run guest code.
|
||||
// ---- window, vsync, focus (gfx_window.c, vk_device.c). Host threads only; never run guest code.
|
||||
void gfx_window_start(void); // window thread, Vulkan device, swapchain; idempotent
|
||||
void gfx_vsync_bind(uint64_t system_event); // nn::vi vsync event; signalled at 60 Hz
|
||||
uint64_t gfx_vsync_ticks(void); // ticks since start
|
||||
@@ -25,7 +24,7 @@ uint64_t gfx_vsync_time_ns(uint64_t tick); // kern_now_ns() time
|
||||
double gfx_window_refresh_hz(void); // refresh rate of the window's monitor; 0 unknown
|
||||
int gfx_window_focused(void);
|
||||
|
||||
// ---- shader service client (gfx_shader.c: T4)
|
||||
// ---- shader service client (gfx_shader.c)
|
||||
enum { GFX_STAGE_VS, GFX_STAGE_TCS, GFX_STAGE_TES, GFX_STAGE_GS, GFX_STAGE_FS, GFX_STAGE_CS };
|
||||
enum { GFX_TEX_1D = 1, GFX_TEX_1D_ARRAY, GFX_TEX_BUFFER, GFX_TEX_2D, GFX_TEX_2D_ARRAY, GFX_TEX_2D_MS,
|
||||
GFX_TEX_2D_MS_ARRAY, GFX_TEX_3D, GFX_TEX_CUBE, GFX_TEX_CUBE_ARRAY }; // gfx_shader.c tex_names order
|
||||
@@ -59,7 +58,7 @@ const GfxShader *gfx_shader_load_input(const uint8_t iid[32], int xlate);
|
||||
// Boot prewarm: out[i] = gfx_shader_load_input(iids[i], 1) on `threads` threads with up to `procs` translators, then
|
||||
// every other stored input not cached under this translator is translated. Logs progress and the total.
|
||||
void gfx_shader_prewarm(const uint8_t (*iids)[32], size_t n, const GfxShader **out, int threads, int procs);
|
||||
// Answers met in play (Task 2.3): from this call on, every (program, answers) a gfx_shader_get* call resolves is appended
|
||||
// Answers met in play: from this call on, every (program, answers) a gfx_shader_get* call resolves is appended
|
||||
// to shader-answers.bin, and its program to shader-programs.bin, in the directory of `keys_path` (pipeline-keys.bin).
|
||||
void gfx_shader_record_at(const char *keys_path);
|
||||
void gfx_shader_record_flush(int force); // appends pending records; without force at most every 2 s
|
||||
@@ -75,7 +74,7 @@ const GfxShader *gfx_shader_load_private(const uint8_t iid[32], int *owned);
|
||||
void gfx_shader_release_private(const GfxShader *s);
|
||||
const uint32_t *gfx_shader_spirv(const GfxShader *s, uint64_t *words);
|
||||
uint64_t gfx_shader_failures(void); // failed translations; feeds GfxStats.shader_failures
|
||||
// Reflection (T6b): parsed once per shader from the translator's .res.json and the SPIR-V before it enters the cache.
|
||||
// Reflection: parsed once per shader from the translator's .res.json and the SPIR-V before it enters the cache.
|
||||
enum { GFX_RES_UBO, GFX_RES_SSBO, GFX_RES_TEX, GFX_RES_IMG, GFX_RES_KINDS };
|
||||
typedef struct {
|
||||
uint16_t set, binding; // as the SPIR-V decorates them: set 0, bindings from 1 per shader (0: support buffer)
|
||||
@@ -98,7 +97,7 @@ uint32_t gfx_shader_inputs(const GfxShader *s, GfxShaderIo io[32]); // Input
|
||||
uint32_t gfx_shader_outputs(const GfxShader *s, GfxShaderIo io[32]); // Output Locations (VS varyings, FS colour targets)
|
||||
void gfx_shader_local_size(const GfxShader *s, uint32_t local[3]); // CS: the LocalSize execution mode; 1, 1, 1 otherwise
|
||||
|
||||
// ---- back end (gfx_null.c: T3; vk_*.c: T5, T6). Called by nvn_api.c on guest threads.
|
||||
// ---- back end (gfx_null.c, vk_*.c). Called by nvn_api.c on guest threads.
|
||||
typedef struct {
|
||||
const char *name;
|
||||
int (*pool_init)(pool_t *p); // 0 fails MemoryPoolInitialize
|
||||
@@ -126,7 +125,7 @@ typedef struct {
|
||||
extern const GfxBackend gfx_null_backend, gfx_vulkan_backend;
|
||||
const GfxBackend *gfx_backend(void); // TOTK_GPU=null|vulkan (default vulkan)
|
||||
|
||||
// ---- NVN front end (nvn_procs.c: T3a)
|
||||
// ---- NVN front end (nvn_procs.c)
|
||||
void nvn_procs_register(void); // hostfn_register every proc; before hostfn_seal (ov_gfx.c)
|
||||
uint64_t nvn_proc_address(const char *name); // synthetic host address of a proc, 0 for an unknown name
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
// input id, can be translated again under a new translator without guest memory (gfx_shader_load_input,
|
||||
// gfx_shader_prewarm).
|
||||
//
|
||||
// Reflection (T6b): every shader that enters the cache has its .res.json parsed into GfxShaderRes arrays and its SPIR-V
|
||||
// Reflection: every shader that enters the cache has its .res.json parsed into GfxShaderRes arrays and its SPIR-V
|
||||
// scanned for interface Locations and the workgroup size (gfx.h accessors at the end of the file).
|
||||
//
|
||||
// Provenance: the c2 handle ranges of handle_slot are those of MacNativeGames' totk_xlate Service.cs (MIT).
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Weak stand-ins for everything gfx.h declares, so the runtime links while M4 tasks T2-T6 land one by one. Each
|
||||
// function stops with its name; each back-end table is zero-filled with the name "unimplemented". A task's strong
|
||||
// definition replaces the stand-in at link time. Deleted by M4 T7.
|
||||
#include <stdio.h>
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
#define WEAK __attribute__((weak))
|
||||
|
||||
WEAK void oe_push_message(uint32_t message) { (void)message; rt_fatal("gfx: oe_push_message is not implemented"); }
|
||||
WEAK void oe_set_focus(int in_focus) { (void)in_focus; rt_fatal("gfx: oe_set_focus is not implemented"); }
|
||||
WEAK const OvNvState *ov_nv_state(void) { rt_fatal("gfx: ov_nv_state is not implemented"); }
|
||||
|
||||
|
||||
WEAK const GfxBackend gfx_null_backend = {.name = "unimplemented"};
|
||||
|
||||
#define STUB_PROBE(fn, name) WEAK int fn(void) { printf(name ": not built yet\n"); return 2; }
|
||||
STUB_PROBE(probe_nvn_tex, "probe-nvn-tex")
|
||||
STUB_PROBE(probe_vkmem, "probe-vkmem")
|
||||
STUB_PROBE(probe_astc, "probe-astc")
|
||||
+16
-17
@@ -1,4 +1,4 @@
|
||||
// Shared interface of the M3 host runtime. Owned by T1; changes by agreement.
|
||||
// Interface of the host runtime: guest memory, modules, kernel, IPC, file system and boot.
|
||||
//
|
||||
// Guest addresses are host addresses. Every guest-visible region lies below 2^39 (IPC buffer descriptors carry 39
|
||||
// address bits). Fixed parts of the guest address map:
|
||||
@@ -78,7 +78,7 @@ static inline void ret_x(Cpu *c, uint64_t v) { c->x[0] = v; }
|
||||
static inline void ret_s(Cpu *c, float f) { uint32_t b; memcpy(&b, &f, 4); c->v[0] = b; }
|
||||
static inline void ret_d(Cpu *c, double d) { uint64_t b; memcpy(&b, &d, 8); c->v[0] = b; }
|
||||
|
||||
// ---- host functions (hostfn.c, T1)
|
||||
// ---- host functions (hostfn.c)
|
||||
// Every host function is a GuestFn at a synthetic address RT_HOSTFN_BASE + 16*i. It runs on the calling guest thread,
|
||||
// may clobber x0-x18, v0-v7, v16-v31, must preserve x19-x29, sp, v8-v15, tpidr, and returns to the guest BL site with
|
||||
// a C return. On entry c->pc holds the function's own synthetic address.
|
||||
@@ -113,7 +113,7 @@ uint64_t rt_call(Cpu *c, uint64_t fn, int n, ...); // host -> guest, sec
|
||||
#define RT_RET_HOST 0x70200ff000ull // x30 sentinel inside the host-function window; a jump to
|
||||
// it is a named fatal
|
||||
|
||||
// ---- guest memory (memory.c, T1)
|
||||
// ---- guest memory (memory.c)
|
||||
typedef struct { uint64_t base, size; } RtRange;
|
||||
typedef struct { RtRange aslr, stack, heap, alias, kernel, hostfn; uint64_t pool; } RtGuestLayout;
|
||||
typedef struct { uint64_t base, size; uint32_t state, attr, perm, ipc, device, pad; } RtMemoryInfo; // Horizon layout
|
||||
@@ -151,7 +151,7 @@ void mem_freeze_stats(uint64_t *frozen, uint64_t *freezes, uint64_t *fault_thaws
|
||||
// regions frozen now; freezes and thaws since the last call
|
||||
void mem_kernel_free(void *p, uint64_t size); // TLR/TLS reuse after thread exit
|
||||
|
||||
// ---- modules (loader.c, T1)
|
||||
// ---- modules (loader.c)
|
||||
typedef struct {
|
||||
const char *name; uint64_t base, size, text_end, data_off; // text_end, data_off: offsets from base
|
||||
uint64_t init, init_array, init_array_size, ctor_flag; // absolute guest addresses; init_array_size in bytes
|
||||
@@ -173,7 +173,7 @@ extern const char rt_default_exefs[], rt_default_romfs[], rt_default_profile[];
|
||||
extern const int rt_game_version; // the game build: 121 (1.2.1) or 143 (1.4.3)
|
||||
#define RT_V143 (rt_game_version >= 143)
|
||||
|
||||
// ---- kernel (kernel.c, T2)
|
||||
// ---- kernel (kernel.c)
|
||||
typedef struct RtThread RtThread;
|
||||
Cpu *kern_cpu(void); // current guest thread's Cpu, NULL on host threads
|
||||
uint32_t kern_main_thread(Cpu **cpu); // main thread object and Cpu for the calling host thread
|
||||
@@ -211,14 +211,14 @@ uint64_t kern_now_ns(void);
|
||||
typedef struct { uint64_t svcs, threads, host_calls; } RtStats;
|
||||
RtStats rt_stats(void); // host_calls: hostfn_calls()
|
||||
|
||||
// ---- IPC (ipc.c, T3)
|
||||
// ---- IPC (ipc.c)
|
||||
int ipc_svc(Cpu *c, uint32_t imm); // 0x15, 0x1F, 0x21, 0x22; 1 if handled
|
||||
void ipc_handle_closed(int kind, void *obj); // CloseHandle on a KERN_SESSION/KERN_TMEM handle
|
||||
|
||||
// ---- file system (romfs.c / fs_host.c, T5)
|
||||
// ---- file system (romfs.c / fs_host.c)
|
||||
void fs_init(void); // idempotent; paths from rt_config()
|
||||
|
||||
// ---- account (ov_system.c, T4)
|
||||
// ---- account (ov_system.c)
|
||||
void account_uid(uint64_t out[2]); // the local user's nn::account::Uid {lo, hi}, persistent in
|
||||
// <profile>/account.uid; fs_host.c names save data after it
|
||||
|
||||
@@ -241,26 +241,25 @@ int64_t frame_step_world_us(void); // game time since th
|
||||
// (TOTK_FPS_LIMIT, live)
|
||||
void frame_step_present(uint64_t display_ns, int interval); // nvnQueuePresentTexture: the frame's display time and
|
||||
// present interval (60 Hz ticks)
|
||||
// ---- boot (boot.c T6, main.c T1)
|
||||
// ---- boot (boot.c, main.c)
|
||||
typedef struct { const char *exefs, *romfs, *profile; int boot; int frames; const char *shot, *gpu; } RtConfig;
|
||||
// romfs: directory with romfs.bin and index.json;
|
||||
// frames: --frames N (0 = no limit); shot: --shot
|
||||
// PATH; gpu: TOTK_GPU
|
||||
const RtConfig *rt_config(void);
|
||||
void rt_boot_event(const char *what, Cpu *c, const char *detail); // checkpoint log; "nvn" ends --boot
|
||||
void rt_boot(void); // T6: steps 9-14 of section 1
|
||||
void rt_boot(void); // boot.c
|
||||
// Modes of totk.exe. main.c has run mem_init and parsed the paths; each mode calls ldr_load itself if it needs the
|
||||
// modules, registers its host functions, then seals (hostfn_seal, or implicitly at the first rt_call). Return the
|
||||
// process exit code.
|
||||
int probe_cfg(void); // cfg_probe.c
|
||||
int probe_loader(void), probe_kernel(void), probe_ipc(void), probe_services(void), probe_fs(void);
|
||||
int probe_gfx_abi(void); // M4 T1: gfx_abi_probe.c
|
||||
int probe_vkdevice(void); // M4 T2: vk_device.c
|
||||
int probe_nvn(void), probe_nvn_tex(void); // M4 T3: nvn_probe.c
|
||||
int probe_window(void); // M4 T2
|
||||
int probe_shader(void); // M4 T4
|
||||
int probe_vkmem(void), probe_vkexec(void), probe_astc(void); // M4 T5, T6
|
||||
int probe_gfx_abi(void); // gfx_abi_probe.c
|
||||
int probe_vkdevice(void); // vk_device.c
|
||||
int probe_nvn(void), probe_nvn_tex(void); // nvn_probe.c, nvn_tex_probe.c
|
||||
int probe_window(void); // gfx_window.c
|
||||
int probe_shader(void); // gfx_shader.c
|
||||
int probe_vkmem(void), probe_vkexec(void), probe_astc(void); // tex_probe.c, vk_exec_probe.c, astc_probe.c
|
||||
int probe_movie(void); // movie_probe.c: host VP9 movie::Decoder on one RomFS movie
|
||||
int vk_pipeline_prepare_shaders(void); // vk_pipeline.c: --prepare-shaders (tools/prepare_shaders.py)
|
||||
int vk_pipeline_warm_shaders(void); // vk_pipeline.c: --warm-shaders (tools/build_game.py)
|
||||
// gfx_stubs.c defines each of these weakly: prints "probe-x: not built yet", returns 2.
|
||||
+1
-1
@@ -714,7 +714,7 @@ static uint64_t stub(const char *sym) {
|
||||
// ThreadType+0x1B0 (sdk+0x13186c), which nnosInitialize sets to the thread's handle; a zero there makes every free
|
||||
// mutex look owned by the caller (__cxa_guard_acquire in nn::sm::Initialize aborts);
|
||||
// - sdk's full DT_INIT needs nn::os initialised (its TLS slot constructor reaches ThreadManager), which is the boot
|
||||
// sequence's job (T6). The audio client only needs its two sf heap constructors: 16 KiB ExpHeaps on static buffers,
|
||||
// sequence's job. The audio client only needs its two sf heap constructors: 16 KiB ExpHeaps on static buffers,
|
||||
// stored at sdk+0xd37e90 and sdk+0xd3bec0 (CreateAudioRendererManagerByHipc reads the second). The 1.4.3 SDK creates
|
||||
// that heap lazily; its OpenAudioRenderer takes nn::audio::detail::g_RendererResourceSemaphore first, which the
|
||||
// constructor at sdk+0x221fb0 initializes (two renderers).
|
||||
|
||||
+1
-1
@@ -1334,7 +1334,7 @@ void kern_run_on_guest_main(void (*fn)(Cpu *c)) {
|
||||
// The high-resolution waitable timer ends a wait 0.3-0.5 ms late at best: the SDK's 20 us polling sleeps (main thread
|
||||
// and job workers, about 1.5 per frame each) took 460-515 us. Sleeps up to SLEEP_SPIN_NS spin on the performance
|
||||
// counter instead; the virtual core is already given up. Linux: clock_nanosleep wakes within about 50 us (timer slack),
|
||||
// so a Linux host spins only below that, or not at all (docs/backlog.md item 35).
|
||||
// so a Linux host spins only below that, or not at all.
|
||||
#define SLEEP_SPIN_NS 1000000
|
||||
static void sleep_ns(RtThread *t, int64_t ns) {
|
||||
if (ns <= SLEEP_SPIN_NS) {
|
||||
|
||||
+5
-6
@@ -3,13 +3,12 @@
|
||||
// totk.exe [--exefs DIR] [--romfs DIR] [--profile DIR] <mode>
|
||||
// --selftest layout, dispatch table and host-function registry; needs no game files
|
||||
// --probe-loader load and relocate the modules, check them against the measured 1.2.1 values
|
||||
// --probe-kernel, --probe-ipc, --probe-services, --probe-fs task probes (T2-T5)
|
||||
// --probe-gfx-abi NVN object layouts against nvn.h's reserved sizes (M4 T1); needs no game files
|
||||
// --probe-nvn, --probe-nvn-tex, --probe-window, --probe-shader, --probe-vkmem, --probe-vkexec M4 probes
|
||||
// --probe-kernel, --probe-ipc, --probe-services, --probe-fs host probes
|
||||
// --probe-gfx-abi NVN object layouts against nvn.h's reserved sizes; needs no game files
|
||||
// --probe-nvn, --probe-nvn-tex, --probe-window, --probe-shader, --probe-vkmem, --probe-vkexec graphics probes
|
||||
// --probe-astc the compute ASTC decoder against astc_decode_ldr; needs no game files
|
||||
// "not built yet", exit 2, until their task lands
|
||||
// --probe-movie host VP9 movie::Decoder on one RomFS movie; skips without game data
|
||||
// --boot run the game to the first NVN call (T6)
|
||||
// --boot run the game to the first NVN call
|
||||
// --frames N run the game (implies --boot's start-up) until the Nth present, then exit 0
|
||||
// --shot PNG with --frames: save the Nth frame; env TOTK_GPU=null|vulkan selects the back end
|
||||
// (no mode) run the game
|
||||
@@ -202,7 +201,7 @@ static LONG CALLBACK crash_handler(EXCEPTION_POINTERS *ep) {
|
||||
ExitProcess(71);
|
||||
}
|
||||
|
||||
// ---- self-test (milestone M2): layout, dispatch table, host-function registry, without game files
|
||||
// ---- self-test: layout, dispatch table, host-function registry, without game files
|
||||
static int failures;
|
||||
#define CHECK(cond, ...) do { if (!(cond)) { failures++; printf(" FAIL: " __VA_ARGS__); putchar('\n'); } } while (0)
|
||||
|
||||
|
||||
+4
-5
@@ -1,6 +1,5 @@
|
||||
// Internal header of the NVN front end, shared by nvn_api_core.c (T3c) and the
|
||||
// later slices (textures, samplers, programs, windows, state objects, command buffers). Frozen after M4 T3c: later tasks
|
||||
// only append. Adapted from MacNativeGames games/totk/third_party/d2r_gfx/gfx/nvn/hle_nvn.c and nvn_impl.h @ b75be8cf
|
||||
// Internal header of the NVN front end, shared by nvn_api_core.c and the other
|
||||
// slices (textures, samplers, programs, windows, state objects, command buffers). Adapted from MacNativeGames games/totk/third_party/d2r_gfx/gfx/nvn/hle_nvn.c and nvn_impl.h @ b75be8cf
|
||||
// (MIT, Copyright (c) 2026 jhaizhou; origin d2r-mobile).
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
@@ -61,7 +60,7 @@ uint64_t gfx_replayed_submits(void); // newest submit numb
|
||||
extern GfxStats gfx_counters; // behind gfx_stats(); add with __atomic_fetch_add. frames:
|
||||
// the back end's present; nvn_calls/nvn_distinct: NVN_API
|
||||
|
||||
// ---- T3d (nvn_api_obj.c): textures, samplers, programs, windows
|
||||
// ---- nvn_api_obj.c: textures, samplers, programs, windows
|
||||
#define NVN_TEX_MAGIC MAGIC('T', 'E', 'X', 0) // tex_t.magic of an initialized texture
|
||||
#define NVN_SAMP_MAGIC MAGIC('S', 'M', 'P', 0) // samp_t.magic
|
||||
#define NVN_PROG_MAGIC MAGIC('P', 'R', 'O', 'G') // prog_t.magic
|
||||
@@ -72,7 +71,7 @@ int nvn_tex_live_at(const void *mem, tex_t *out);
|
||||
// Byte offset of a view's first level and layer inside the texture storage (nvnTextureGetViewOffset); v may be NULL.
|
||||
uint64_t nvn_tex_view_offset(const tex_t *t, const view_t *v);
|
||||
|
||||
// ---- T3e (nvn_api_cmd.c, nvn_ext.c): command buffers and their replay contract
|
||||
// ---- nvn_api_cmd.c, nvn_ext.c: command buffers and their replay contract
|
||||
// A command handle (what QueueSubmitCommands receives) points at a host copy of one recording: a flat sequence of
|
||||
// 8-byte-aligned records {op_hdr, payload}, OP_BEGIN first and OP_END last. Called recordings are inlined, so a copy
|
||||
// holds no OP_CALL or OP_JUMP. Texture pointers in payloads (p_set_rt, p_copy_b2t, p_copy_t2t, p_clear_tex) are
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// NVN proc table. Every name the game's proc loader asks for gets a synthetic host
|
||||
// address through hostfn_register before hostfn_seal; nvnBootstrapLoader and nvnDeviceGetProcAddress hand them out.
|
||||
// nvnp_<name> is the implementation of one proc: the weak definitions here stop with the proc's name, nvn_api.c and
|
||||
// nvn_ext.c (T3b onward) define the strong ones. The name list is runtime/nvn_procs.inc (tools/gen_nvn_procs.py).
|
||||
// nvn_ext.c define the strong ones. The name list is runtime/nvn_procs.inc (tools/gen_nvn_procs.py).
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Ported from MacNativeGames games/totk/tests/texture_auto_layout/probe.c @ b75be8cf (MIT, Copyright (c) 2026 jhaizhou).
|
||||
// The 40 size and block-height goldens are original-SDK CPU queries for uncompressed and BC3-sRGB boundary cases; no game bytes.
|
||||
// The old rounded-up heuristic mismatches 16 of them, including the 160x160 shrine pad. MNG's nvn_bl_offset address checks
|
||||
// belong to the deswizzler (M4 T5) and are not part of this probe.
|
||||
// belong to the deswizzler and are not part of this probe.
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "host.h"
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
// Shared by the system overrides of M3 task T4: runtime/ov_system.c, ov_input.c,
|
||||
// ov_media.c and ov_gfx.c, and their probe. oe_push_message, oe_set_focus and ov_nv_state moved to gfx.h in M4 (the
|
||||
// window posts focus messages, the NVN layer allocates through the recorded callbacks).
|
||||
// Shared by the system overrides: runtime/ov_system.c, ov_input.c, ov_media.c and ov_gfx.c, and their probe.
|
||||
// oe_push_message, oe_set_focus and ov_nv_state are in gfx.h (the window posts focus messages, the NVN layer
|
||||
// allocates through the recorded callbacks).
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
// Adapted from MacNativeGames games/totk/runtime/frontend.m (nn::vi, lines 345-359) and gfx_bridge.c (nv, lines
|
||||
// 74-86) @ b75be8cf (MIT, Copyright (c) 2026 MacNativeGames contributors). The nvnBootstrapLoader stop takes the place
|
||||
// of MacNativeGames' NVN entry (third_party/d2r_gfx/gfx/nvn/hle_nvn.c:115-118); no code of it is copied.
|
||||
// Graphics bring-up without a GPU:
|
||||
// nv::* record the game's allocator callbacks and graphics work memory (guest addresses) for M4
|
||||
// nn::vi::* display, layer and native window identities; the vsync event is bound, nothing signals it in M3
|
||||
// Graphics bring-up:
|
||||
// nv::* record the game's allocator callbacks and graphics work memory (guest addresses) for the NVN layer
|
||||
// nn::vi::* display, layer and native window identities; the vsync event is bound here
|
||||
// nvnBootstrapLoader the end of --boot (rt_boot_event "nvn"); otherwise the synthetic address of an NVN proc (nvn_procs.c)
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
@@ -40,7 +40,7 @@ static void ov_initialize_graphics(Cpu *c) {
|
||||
nv.memory_size = size;
|
||||
nv.initialized = 1;
|
||||
ReleaseSRWLockExclusive(&lock);
|
||||
rt_log("nv: graphics work memory %#llx+%#llx recorded (no GPU in M3)", (unsigned long long)mem,
|
||||
rt_log("nv: graphics work memory %#llx+%#llx recorded", (unsigned long long)mem,
|
||||
(unsigned long long)size);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ static void ov_create_layer(Cpu *c) {
|
||||
ret_x(c, 0);
|
||||
}
|
||||
|
||||
// Result nn::vi::GetNativeWindow(void **out, Layer *layer): a placeholder the NVN layer of M4 recognises
|
||||
// Result nn::vi::GetNativeWindow(void **out, Layer *layer): a placeholder the NVN layer recognises
|
||||
static void ov_native_window(Cpu *c) {
|
||||
void **out = arg_p(c, 0);
|
||||
if (!out || !vi_up() || arg_p(c, 1) != layer) { ret_x(c, VI_R_INVALID); return; }
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Adapted from MacNativeGames games/totk/runtime/opus_codec.c @ b75be8cf (MIT, Copyright (c) 2026 MacNativeGames
|
||||
// contributors).
|
||||
// The hardware Opus decoder (nn::codec), decoded with libopus on the host (M5). The software keyboard (nn::swkbd) is
|
||||
// The hardware Opus decoder (nn::codec), decoded with libopus on the host. The software keyboard (nn::swkbd) is
|
||||
// in swkbd_win.c.
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
+5
-5
@@ -958,7 +958,7 @@ int probe_services(void) {
|
||||
if (child && *child) return run_as_child(child);
|
||||
ldr_load(rt_config()->exefs);
|
||||
|
||||
// Names bound by the loader: each override and observer of T4 must match a relocation.
|
||||
// Names bound by the loader: each override and observer probed here must match a relocation.
|
||||
int bound = 0, obs_bound = 0;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
const char *n = hostfn_name(RT_HOSTFN_BASE + 16ull * (uint64_t)i);
|
||||
@@ -974,7 +974,7 @@ int probe_services(void) {
|
||||
for (int k = 0; k < NOBS; k++) CHECK(hostfn_observer(observers[k]), "observer %s is not linked", observers[k]);
|
||||
CHECK(obs_bound == NOBS, "%d of %d observers match a JUMP_SLOT", obs_bound, NOBS);
|
||||
|
||||
// Every linked override is either probed here or belongs to another probe or owner (nn::fs: T5, fibers: T2,
|
||||
// Every linked override is either probed here or belongs to another probe or owner (nn::fs: fs probe, fibers: kernel probe,
|
||||
// movie::Decoder: --probe-movie, memcpy: the graphics write-back in nvn_api_core.c).
|
||||
int linked = hostfn_table_size(0), others = 0;
|
||||
for (int i = 0; i < linked; i++) {
|
||||
@@ -988,7 +988,7 @@ int probe_services(void) {
|
||||
others += theirs;
|
||||
CHECK(mine || theirs, "override %s is linked but not probed", n);
|
||||
}
|
||||
printf("bound: %d of %d T4 overrides and %d of %d observers match relocations of the loaded modules\n", bound, NOV,
|
||||
printf("bound: %d of %d overrides and %d of %d observers match relocations of the loaded modules\n", bound, NOV,
|
||||
obs_bound, NOBS);
|
||||
|
||||
bind_addresses();
|
||||
@@ -1001,8 +1001,8 @@ int probe_services(void) {
|
||||
called += ovs[k].calls > 0;
|
||||
CHECK(ovs[k].calls > 0, "override %s was not called", ovs[k].key);
|
||||
}
|
||||
printf("called: %d of %d T4 overrides through rt_dispatch (%d named fatal stubs in child processes); %d other "
|
||||
"overrides are T2/T5's\n", called, NOV, NFATAL, others);
|
||||
printf("called: %d of %d overrides through rt_dispatch (%d named fatal stubs in child processes); %d other "
|
||||
"overrides belong to the kernel and fs probes\n", called, NOV, NFATAL, others);
|
||||
printf("overrides %d, observers %d\n", linked, hostfn_table_size(1));
|
||||
if (failures) {
|
||||
printf("probe-services: FAILED (%d of %d checks)\n", failures, checks);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Platform layer: threads, time, locks. New runtime code calls this instead of the OS;
|
||||
// one implementation per OS (win32.c in M5). The FP control register lives in the per-architecture fp_*.h headers,
|
||||
// one implementation per OS (win32.c). The FP control register lives in the per-architecture fp_*.h headers,
|
||||
// inline, because lifted FP code touches it on every operation.
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// CPU-side texture code of the M4 graphics layer: block-linear <-> linear
|
||||
// copies, ASTC decode, NVN format -> VkFormat. Vulkan memory and the texture objects are T5b.
|
||||
// CPU-side texture code: block-linear <-> linear copies, ASTC decode, NVN format -> VkFormat. Vulkan memory and
|
||||
// the texture objects are in vk_memory.c and vk_texture.c.
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
// totk.exe --probe-vkmem: texture memory checks of M4 T5. The CPU part (task T5a): block-linear copies against an
|
||||
// independent reference, ASTC void-extent blocks, and the NVN -> VkFormat table against GPU0. The GPU part (task T5b):
|
||||
// totk.exe --probe-vkmem: texture memory checks. The CPU part: block-linear copies against an
|
||||
// independent reference, ASTC void-extent blocks, and the NVN -> VkFormat table against GPU0. The GPU part:
|
||||
// guest heap imported as a VkBuffer, write watch against CPU, GPU and ReadFile writes, the virtual-pool page table,
|
||||
// and textures uploaded from block-linear storage and read back.
|
||||
// The reference swizzler walks the GOBs in storage order and places each byte by bit operations, so it shares no
|
||||
@@ -290,7 +290,7 @@ static int test_formats(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---- GPU part (T5b) ----
|
||||
// ---- GPU part ----
|
||||
#define GCHECK(cond, ...) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
|
||||
+14
-15
@@ -1,5 +1,4 @@
|
||||
// Vulkan-internal header of the M4 graphics layer. Owned by T2; shared with T5 and
|
||||
// T6 by agreement. Host code only; the guest never sees it.
|
||||
// Vulkan-internal header of the graphics layer. Host code only; the guest never sees it.
|
||||
#pragma once
|
||||
#ifndef VK_USE_PLATFORM_WIN32_KHR
|
||||
#define VK_USE_PLATFORM_WIN32_KHR 1
|
||||
@@ -11,8 +10,8 @@
|
||||
#pragma clang diagnostic ignored "-Wnullability-completeness"
|
||||
#include "vk_mem_alloc.h"
|
||||
#pragma clang diagnostic pop
|
||||
#include "nvn_obj.h" // pool_t, tex_t, view_t, samp_par, NVNcopyRegion (memory and textures block, T5)
|
||||
#include "gfx.h" // GfxShader, GfxShaderRes, GfxShaderAnswers (pipelines block, T6b)
|
||||
#include "nvn_obj.h" // pool_t, tex_t, view_t, samp_par, NVNcopyRegion (memory and textures block)
|
||||
#include "gfx.h" // GfxShader, GfxShaderRes, GfxShaderAnswers (pipelines block)
|
||||
|
||||
// Required (device choice fails without them): Vulkan 1.3; VK_KHR_swapchain, VK_KHR_push_descriptor,
|
||||
// VK_EXT_depth_clip_control, VK_EXT_external_memory_host; features timelineSemaphore, dynamicRendering,
|
||||
@@ -31,7 +30,7 @@ typedef struct VkCtx {
|
||||
VkQueue xfer_queue; // equals gfx_queue when xfer_shared
|
||||
int xfer_shared; // 1: no separate queue; serialise with gfx_queue
|
||||
VmaAllocator vma;
|
||||
VkSemaphore timeline; // one timeline semaphore; values are issued by T6
|
||||
VkSemaphore timeline; // one timeline semaphore; values are issued by vk_sync.c
|
||||
VkDebugUtilsMessengerEXT messenger;
|
||||
volatile long validation_errors; // errors seen by the messenger (TOTK_VK_VALIDATION=1)
|
||||
int validation; // layer active
|
||||
@@ -71,7 +70,7 @@ int vk_init(void); // idempotent; 1 on success, 0 on fa
|
||||
void vk_shutdown(void);
|
||||
int vk_selftest(void); // 0 on success
|
||||
|
||||
// ---- window and present (gfx_window.c, T2). Host threads only.
|
||||
// ---- window and present (gfx_window.c). Host threads only.
|
||||
// vk_present_image: blits the crop rectangle {x, y, w, h} of src (NULL, or w/h <= 0: the whole w x h image), scaled
|
||||
// to the swapchain extent, into the acquired swapchain image and presents it. src is in layout, which is
|
||||
// VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL (its writes made visible to the transfer stage by the caller's barrier) or
|
||||
@@ -99,8 +98,8 @@ unsigned gfx_window_focus_messages(void); // OE_MSG_FOCUS_STATE_CHANGED
|
||||
void gfx_window_release_vk(void); // vk_shutdown calls it first: destroys swapchain, surface, present objects
|
||||
long vk_validation_errors_total(void); // validation errors since the first vk_init, across vk_shutdown
|
||||
|
||||
// ---- memory and textures (vk_memory.c, vk_texture.c: T5)
|
||||
// Guest or host threads; every call needs vk_init(). The first nine have the GfxBackend signatures of gfx.h; T6 builds
|
||||
// ---- memory and textures (vk_memory.c, vk_texture.c)
|
||||
// Guest or host threads; every call needs vk_init(). The first nine have the GfxBackend signatures of gfx.h; vk_backend.c builds
|
||||
// gfx_vulkan_backend from them.
|
||||
int vk_pool_init(pool_t *p); // CPU-visible: guest storage imported (VK_EXT_external_memory_host) as one
|
||||
// VkBuffer; physical: device-local memory; virtual: a 64 KiB page table
|
||||
@@ -189,7 +188,7 @@ void vk_mem_snap_stats(uint64_t *checks, uint64_t *calls, uint64_t *untracked);
|
||||
// vk_upload_flush(0) before each submission that may read uploaded images; queue order and the batch's closing
|
||||
// barrier make the uploads visible to it. Returns the batch sequence number; wait=1 blocks until it completed.
|
||||
// TOTK_FIX_ZERO (default 1): physical pools are zeroed once at creation and GPU-only images with memory of their own
|
||||
// are cleared when created; 0 leaves both as the driver hands them out (M4).
|
||||
// are cleared when created; 0 leaves both as the driver hands them out.
|
||||
int vk_fix_zero(void);
|
||||
VkCommandBuffer vk_upload_begin(void);
|
||||
void vk_upload_end(void);
|
||||
@@ -221,11 +220,11 @@ void vk_astc_time_enable(int on);
|
||||
int vk_astc_time_get(uint32_t bw, uint32_t bh, uint64_t *texels, uint64_t *calls, double *us); // totals so far
|
||||
void vk_astc_time_summary(void); // logs the totals
|
||||
void vk_astc_release(void); // vk_device.c, before the device goes: frees the decoder's objects
|
||||
void vk_mem_note_submit(uint64_t timeline_value); // T6: after each submit that signals ctx->timeline with this value
|
||||
void vk_mem_note_submit(uint64_t timeline_value); // vk_sync.c: after each submit that signals ctx->timeline with this value
|
||||
void vk_mem_collect(void); // frees what has completed
|
||||
typedef struct { uint64_t imported_pools, physical_pools, virtual_pools, import_failures, upload_batches; } VkMemStats;
|
||||
void vk_mem_stats(VkMemStats *s);
|
||||
void vk_mem_shutdown(void); // before vk_shutdown: waits idle, frees every T5 object
|
||||
void vk_mem_shutdown(void); // before vk_shutdown: waits idle, frees every memory and texture object
|
||||
|
||||
// Textures. Images are keyed by storage GPU address plus descriptor and stay in VK_IMAGE_LAYOUT_GENERAL. A mirrored
|
||||
// texture (CPU-written pool storage, MNG tex_mirrored) is uploaded on first use and again when its storage pages were
|
||||
@@ -298,7 +297,7 @@ int vk_sampler_params(int id, samp_par *out); // parameters of a sampler s
|
||||
// 8-bit RGBA PNG with stored deflate blocks (boot.c); stops with rt_fatal when the file cannot be written.
|
||||
void rt_write_png(const char *path, const uint8_t *rgba, uint32_t w, uint32_t h);
|
||||
|
||||
// ---- submission, synchronisation, completion (vk_sync.c, vk_backend.c: T6a)
|
||||
// ---- submission, synchronisation, completion (vk_sync.c, vk_backend.c)
|
||||
// Fence values are ctx->timeline values: each submission signals the next one, in submit order. A value counts as done
|
||||
// once the GPU reached it and the completion thread ran its side effects. Guest or host threads.
|
||||
void vk_sync_start(void); // vk_init (fatal without a device) and the completion thread; idempotent
|
||||
@@ -309,7 +308,7 @@ uint64_t vk_sync_fence_done(void); // highest done value
|
||||
uint64_t vk_sync_issued(void); // newest submitted value (the one being recorded is this + 1)
|
||||
void vk_sync_finish(void); // every submitted value done
|
||||
void vk_sync_shutdown(void); // finish, stop the completion thread, free the command buffers
|
||||
// The op-stream replay (vk_exec.c: T6c): records the command handles of one submission into cb, which the caller has
|
||||
// The op-stream replay (vk_exec.c): records the command handles of one submission into cb, which the caller has
|
||||
// begun and ends. fence is the value this submission will signal. Returns the number of commands recorded into the
|
||||
// command buffer it holds at the end (0: that one stays empty and is not submitted). A weak vk_sync.c version accepts
|
||||
// empty streams only.
|
||||
@@ -327,10 +326,10 @@ int rdoc_active(void);
|
||||
void rdoc_after_present(uint64_t frame);
|
||||
void vk_present_drain(void); // returns once every queued present was submitted to gfx_queue
|
||||
int vk_replay_async(void); // TOTK_REPLAY_ASYNC (default 1): submissions replay on the replay thread
|
||||
void vk_program_release(const prog_t *p); // GfxBackend.program_release: pipelines (T6b); weak no-op in vk_backend.c
|
||||
void vk_program_release(const prog_t *p); // GfxBackend.program_release: vk_pipeline.c; weak no-op in vk_backend.c
|
||||
void vk_backend_shutdown(void); // presents, vk_sync_shutdown, vk_mem_shutdown, vk_shutdown in that order
|
||||
|
||||
// ---- pipelines (vk_pipeline.c: T6b)
|
||||
// ---- pipelines (vk_pipeline.c)
|
||||
// Binding model (comment block at the top of vk_pipeline.c): one descriptor set, set 0, per pipeline. Binding 0 is
|
||||
// Ryujinx's support buffer (a uniform buffer every stage shares); the vertex stage keeps its own bindings, the fragment
|
||||
// stage's follow them (its SPIR-V is renumbered). Set 0 is a push-descriptor set unless the pipeline needs more than
|
||||
|
||||
@@ -352,7 +352,7 @@ static void vk_be_present(const tex_t *t, const int crop[4], uint64_t at_ns) {
|
||||
replay_enqueue(&it);
|
||||
}
|
||||
|
||||
// Pipelines (T6b) drop a released program's pipelines here.
|
||||
// vk_pipeline.c drops a released program's pipelines here.
|
||||
WEAK void vk_program_release(const prog_t *p) { (void)p; }
|
||||
|
||||
static int vk_be_pool_init(pool_t *p) { vk_sync_start(); return vk_pool_init(p); }
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
// storage offsets from the guest address rounded down to that alignment (Ryujinx's unaligned storage path), and
|
||||
// vk_exec_draw.c ssbo_info binds the rounded-down address, so shader writes reach guest memory instead of a copy.
|
||||
// - no graphics pipeline library with fast linking (TOTK_GPL=0 forces it): vk_caps.gpl = 0, the extension stays
|
||||
// disabled, and pipelines are built whole as in M4.
|
||||
// disabled, and pipelines are built whole.
|
||||
// - no 32-lane pin (no subgroupSizeControl, 32 outside min..max, or TOTK_VK_SUBGROUP_PIN=0): stages run at the
|
||||
// device's subgroupSize (SPIR-V 1.3 without a required size) and the translator is told that size.
|
||||
// TOTK_VK_SUBGROUP=<n> pins n instead of 32 where the driver allows it.
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ enum {
|
||||
VK_EXEC_DIRTY_ALL = (1 << 5) - 1,
|
||||
};
|
||||
|
||||
// One render target of OP_SET_RT, resolved to its T5b image for the current command buffer.
|
||||
// One render target of OP_SET_RT, resolved to its vk_texture.c image for the current command buffer.
|
||||
typedef struct {
|
||||
const tex_t *tex; // payload copy (lives for the run); NULL: no target in this slot
|
||||
VkImage image; // VK_NULL_HANDLE: no target or not renderable (logged once)
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
//
|
||||
// Per command buffer the replay keeps transient memory (support buffers, copied uniform ranges, generated indices),
|
||||
// descriptor pools, image views and the storage-buffer ranges the GPU may write; all of it is freed, and the ranges are
|
||||
// reported with vk_guest_written, from one completion callback once the submission's fence is done (T5b: the write
|
||||
// watch does not see GPU writes). Nothing here locks: it runs on the submitting thread under vk_sync_submit's lock.
|
||||
// reported with vk_guest_written, from one completion callback once the submission's fence is done (the write watch
|
||||
// does not see GPU writes). Nothing here locks: it runs on the submitting thread under vk_sync_submit's lock.
|
||||
//
|
||||
// Provenance: the op semantics (bind ops, program and slot conventions, draw and dispatch flow, vertex streams with a
|
||||
// zeroed buffer for unbound ones) follow MacNativeGames games/totk/third_party/d2r_gfx/gfx/nvn/nvn_mtl.m:7409-7460,
|
||||
@@ -122,7 +122,7 @@ static struct {
|
||||
} B;
|
||||
|
||||
// Contents of B.ds_last: the set's packed descriptors (VkDescSlot), which with its layout determine every write. A draw
|
||||
// whose allocated set would hold the same reuses the set instead of allocating and writing one (Task 2.2's per-draw
|
||||
// whose allocated set would hold the same reuses the set instead of allocating and writing one (a per-draw
|
||||
// cost). psig: the same for the last push per set number; a push equal to it under the same layout is not recorded
|
||||
// again (the descriptors stay bound across pipeline binds of that layout).
|
||||
#define DS_SIG_BYTES (MAX_DESC * sizeof(VkDescSlot))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// totk.exe --probe-vkexec: the Vulkan replay of M4 T6, through gfx_vulkan_backend. Sync part (task T6a): empty
|
||||
// totk.exe --probe-vkexec: the Vulkan replay, through gfx_vulkan_backend. Sync part: empty
|
||||
// submissions get consecutive fence values; fence_wait and fence_done agree; completion callbacks run in submit order on
|
||||
// the completion thread; a guest-memory word a callback writes is visible once fence_wait returns; finish drains every
|
||||
// submission; a present of a cleared vk_texture.c image (layout GENERAL) reads back exactly through the swapchain
|
||||
@@ -56,7 +56,7 @@ static int test_sync(const GfxBackend *b, uint8_t *guest, uint64_t *first, uint6
|
||||
uint64_t prev = b->fence_done();
|
||||
*first = prev + 1;
|
||||
for (int i = 0; i < SUBMITS; i++) {
|
||||
vk_completion_add(prev + 1, note, (void *)(uintptr_t)(prev + 1)); // queued before the value exists, as T6c does
|
||||
vk_completion_add(prev + 1, note, (void *)(uintptr_t)(prev + 1)); // queued before the value exists, as vk_exec.c does
|
||||
uint64_t f = b->submit(i % 3 ? handles : NULL, i % 3);
|
||||
CHECK(f == prev + 1, "submission %d got fence %llu after %llu", i, (unsigned long long)f, (unsigned long long)prev);
|
||||
CHECK(b->fence_done() <= f, "fence_done %llu ahead of the newest submission %llu",
|
||||
@@ -460,7 +460,7 @@ static int test_draw(const GfxBackend *b, uint8_t *guest, int *n_drawn, int *n_t
|
||||
int diff = 0;
|
||||
for (int k = 0; k < TEX_W * TEX_H; k++) diff += px[k] != clear_v;
|
||||
if (diff) drawn = diff, hit = prog;
|
||||
if (diff && vk_caps.gpl) { // the same draw through the shared set 0 layout: texel-equal (Task 2.2)
|
||||
if (diff && vk_caps.gpl) { // the same draw through the shared set 0 layout: texel-equal
|
||||
static uint32_t px2[TEX_W * TEX_H];
|
||||
int was = vk_pipeline_set_indep(0);
|
||||
f = b->submit(&handle, 1);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Transfers of the Vulkan op-stream replay: buffer and texture copies, texture
|
||||
// clears, event signals, counter reports and conditional rendering. vk_exec.c (T6c1) calls vk_exec_op_xfer for these ops.
|
||||
//
|
||||
// Buffers are guest memory: vk_mem_resolve maps an address to the VkBuffer behind its pool, textures are the T5b images
|
||||
// Buffers are guest memory: vk_mem_resolve maps an address to the VkBuffer behind its pool, textures are the vk_texture.c images
|
||||
// (linear texel layout, GENERAL layout; block-linear storage is only converted when a mirrored texture is uploaded). So a
|
||||
// copy is one Vulkan transfer command and the pitch of a buffer side comes from the payload (OP_COPY_STRIDE or the
|
||||
// tight row). Every copy ends the open pass and starts with a full barrier. Destinations: an image is marked
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// NVN format -> VkFormat for the textures of the M4 graphics layer.
|
||||
// NVN format -> VkFormat for the textures of the graphics layer.
|
||||
// Ported from the format table of LAS Recomp runtime/nvn/vulkan/textures.c @ da35b83a (MIT, Copyright (c) 2026 Peiikai).
|
||||
// Differences from LAS: the table is indexed by the NVNformat values of nvn.h and covers all of them, ASTC decodes to
|
||||
// RGBA8 unless the device samples it (vk_astc.c, TOTK_ASTC), D24S8 is always D32S8 (no probing: the RX 9070 lacks
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
// Vulkan memory of the M4 graphics layer: NVN memory pools as Vulkan
|
||||
// Vulkan memory of the graphics layer: NVN memory pools as Vulkan
|
||||
// buffers, the virtual-pool page table, GPU address resolution, page write generations for re-uploads, and the
|
||||
// upload batch (command buffer, staging buffers, deferred destruction) that vk_texture.c records into.
|
||||
//
|
||||
@@ -1581,7 +1581,7 @@ static int nspare, cap_spare;
|
||||
static uint64_t next_seq, done_seq; // batches handed out / completed (all batches up to done_seq)
|
||||
static Dead *dead;
|
||||
static int ndead, cap_dead;
|
||||
static uint64_t tl_noted; // newest timeline value T6 reported submitted
|
||||
static uint64_t tl_noted; // newest timeline value vk_sync.c reported submitted
|
||||
|
||||
static BOOL CALLBACK cmd_init(PINIT_ONCE o, PVOID p, PVOID *ctx) {
|
||||
(void)o, (void)p, (void)ctx;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
//
|
||||
// Fragment inputs the vertex stage does not write (stages are translated separately; MNG nvn_mtl.m msl_varyings) get
|
||||
// outputs added to the vertex module, as Vulkan rejects such pairs, initialised to (0, 0, 0, 1) as Maxwell reads an
|
||||
// unwritten output (docs/research/maxwell.md 1.2). The unwritten components of an output the vertex stage does write
|
||||
// unwritten output (docs/maxwell.md 1.2). The unwritten components of an output the vertex stage does write
|
||||
// start at (0, 0, 0, 1) in the translator (tools/xlate/patches/0006).
|
||||
//
|
||||
// Pipeline key. Everything Vulkan 1.3 can take as dynamic state is dynamic: viewports and scissors with count, depth
|
||||
@@ -649,7 +649,7 @@ static uint32_t *spirv_set(const uint32_t *w, uint64_t n, uint32_t set) {
|
||||
return o;
|
||||
}
|
||||
|
||||
// Dual-source blending (docs/research/maxwell.md 5.6): SRC1 blend factors read the fragment shader's colour output 1,
|
||||
// Dual-source blending (docs/maxwell.md 5.6): SRC1 blend factors read the fragment shader's colour output 1,
|
||||
// which Vulkan takes from Location 0, Index 1. The Output variable at Location 1 moves there, as Ryujinx declares it
|
||||
// with DualSourceBlendEnable (CodeGen/Spirv/Declarations.cs); the pipeline is keyed by GKey.dsb, so only pipelines
|
||||
// that blend with SRC1 get this module. NULL when the module has no Output at Location 1.
|
||||
@@ -1446,7 +1446,7 @@ out:
|
||||
return pipe;
|
||||
}
|
||||
|
||||
// ---- graphics pipeline library (vk_caps.gpl, Task 2.4)
|
||||
// ---- graphics pipeline library (vk_caps.gpl)
|
||||
// A pipeline is linked from four libraries, each cached by the part of the pipeline key it depends on: vertex input
|
||||
// (topology class, vertex attributes without dynamic vertex input), pre-rasterization (vertex shader, the outputs its
|
||||
// union adds, static rasterization state), fragment shader (fragment shader, dual-source rewrite, samples) and fragment
|
||||
@@ -1954,7 +1954,7 @@ static void keys_open(void) {
|
||||
char *slash = strrchr(K.path, '\\'), *fslash = strrchr(K.path, '/');
|
||||
if (fslash > slash) slash = fslash;
|
||||
snprintf(slash ? slash + 1 : K.path, sizeof K.path - (size_t)(slash ? slash + 1 - K.path : 0), "pipeline-keys.bin");
|
||||
gfx_shader_record_at(K.path); // shader-answers.bin next to it (gfx_shader.c, Task 2.3)
|
||||
gfx_shader_record_at(K.path); // shader-answers.bin next to it (gfx_shader.c)
|
||||
FILE *f = fopen(K.path, "rb");
|
||||
if (!f) return;
|
||||
KeysHeader h;
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
// Submission, fences and the completion thread of the Vulkan back end.
|
||||
// One timeline semaphore (VkCtx.timeline) orders everything: each submission records its command handles into a
|
||||
// primary command buffer (vk_exec_record, task T6c) and signals the next timeline value, which is the NVN fence value
|
||||
// primary command buffer (vk_exec_record) and signals the next timeline value, which is the NVN fence value
|
||||
// the front end hands to syncs. The completion thread waits on the timeline and, in submit order, runs the CPU side
|
||||
// effects queued for each value (event writes, counter reports, sync state), then frees the T5 objects whose users
|
||||
// effects queued for each value (event writes, counter reports, sync state), then frees the memory and texture objects whose users
|
||||
// completed. A value counts as done only after its side effects ran, so a guest woken by fence_wait sees them.
|
||||
//
|
||||
// The submission shape (one timeline value per submission, signalled at ALL_COMMANDS, vkWaitSemaphores for waits, a
|
||||
@@ -161,13 +161,13 @@ static void gpu_stats(PerfGpu *g) {
|
||||
g->have_vram = vk_mem_vram(&g->vram_used, &g->vram_budget, &g->vram_exact);
|
||||
}
|
||||
|
||||
// Without the replay (T6c) only empty recordings ({OP_BEGIN, OP_END}) can be submitted.
|
||||
// Weak stand-in for vk_exec.c's replay: only empty recordings ({OP_BEGIN, OP_END}) can be submitted.
|
||||
WEAK int vk_exec_record(VkCommandBuffer cb, const uint64_t *handles, int n, uint64_t fence) {
|
||||
(void)cb, (void)fence;
|
||||
for (int i = 0; i < n; i++) {
|
||||
const op_hdr *h = handles[i] ? nvn_op_first(handles[i]) : NULL;
|
||||
if (!h || h->op != OP_BEGIN || h->size < sizeof *h || nvn_op_next(h)->op != OP_END)
|
||||
rt_fatal("vk: op replay is not implemented (T6c)");
|
||||
rt_fatal("vk: op replay is not linked");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Vulkan textures of the M4 graphics layer: a VkImage cache keyed by storage
|
||||
// Vulkan textures of the graphics layer: a VkImage cache keyed by storage
|
||||
// GPU address plus descriptor, uploads through staging buffers with block-linear deswizzle and ASTC decode (vk_astc.c),
|
||||
// re-uploads when page write generations (vk_memory.c) show the storage written, ReadTexels / WriteTexels, the texture
|
||||
// and sampler descriptor slots, and VkSamplers.
|
||||
|
||||
+4
-4
@@ -78,7 +78,7 @@ GEN_FLAGS = ['-O1', '-fno-strict-aliasing', '-Wno-return-type', '-Wno-unused-lab
|
||||
'-march=x86-64-v3']
|
||||
RT_FLAGS = ['-O2', '-g', '-Wall', '-fno-strict-aliasing', '-march=x86-64-v3']
|
||||
LINK_FLAGS = ['-fuse-ld=lld', '-Wl,--stack,16777216', '"-Wl,--Map=$builddir/totk.map"'] # the map feeds tools/prof_report.py
|
||||
LINK_LIBS = ['-lpsapi', '-lbcrypt', '-lvulkan-1', '-luser32', '-lgdi32', '-ldwmapi', '-lwinmm', '-lSDL3', '-lopus', '-l:libvpx.a', '-l:libstdc++.a', '-l:libwinpthread.a'] # Vulkan back end of M4; libvpx static (movies)
|
||||
LINK_LIBS = ['-lpsapi', '-lbcrypt', '-lvulkan-1', '-luser32', '-lgdi32', '-ldwmapi', '-lwinmm', '-lSDL3', '-lopus', '-l:libvpx.a', '-l:libstdc++.a', '-l:libwinpthread.a'] # Vulkan back end; libvpx static (movies)
|
||||
VMA_INCLUDE = UCRT.parent / 'include' / 'vma' # vk_mem_alloc.h; MSYS2 package vulkan-memory-allocator
|
||||
# --toolchain: an llvm-mingw tree. The libraries, headers and DLLs MSYS2 supplies come from the deps directory instead.
|
||||
LLVM_LINK_LIBS = ['-lpsapi', '-lbcrypt', '-luser32', '-lgdi32', '-ldwmapi', '-lwinmm', '-lvulkan-1', '-lSDL3', '-lopus',
|
||||
@@ -300,8 +300,8 @@ def ninja_file(gen, out, mods, pool_depth, skip=(), prebuilt=False, runtime_lib=
|
||||
return '\n'.join(lines), objs, exe
|
||||
rule = 'link'
|
||||
if runtime_lib:
|
||||
# Whole archive: the weak probe stubs in gfx_stubs.c would otherwise satisfy references to the probes before the
|
||||
# linker looks into the library, and the real probes would stay out of totk.exe.
|
||||
# Whole archive: the runtime's weak defaults (nvn_procs.c, nvn_api_core.c, vk_sync.c) would otherwise satisfy
|
||||
# references before the linker pulls the members with the strong definitions, which would stay out of totk.exe.
|
||||
rule = 'link_lib'
|
||||
lines += ['', 'rule link_lib',
|
||||
f' command = $cc $ldflags @$out.rsp -Wl,--whole-archive {cmd_path(runtime_lib)} -Wl,--no-whole-archive $libs -o $out',
|
||||
@@ -632,7 +632,7 @@ def main():
|
||||
dlls = sorted((DEPS / 'bin').glob('*.dll'))
|
||||
for tool in (CLANG, NINJA):
|
||||
if not tool.is_file():
|
||||
sys.exit(f'{tool} not found: install MSYS2 UCRT64 clang, lld and ninja (docs/toolchain-msys2-python.md)')
|
||||
sys.exit(f'{tool} not found: install MSYS2 UCRT64 clang, lld and ninja (docs/building.md)')
|
||||
if not (gen / 'layout.json').is_file():
|
||||
sys.exit(f'{gen / "layout.json"} not found: run the generator first')
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Copies tools/vendor/ryujinx to build/xlate/ryujinx, applies tools/xlate/patches/*.patch there (tools/vendor stays
|
||||
byte-identical; 0001-0004 are MNG's, 0005-0009 are ours), then runs `dotnet publish`. Needs the .NET 10 SDK, the MSVC build
|
||||
tools and vswhere.exe (docs/toolchain-windows.md). Run with .venv.
|
||||
tools and vswhere.exe (docs/building.md). Run with .venv.
|
||||
|
||||
Next to the exe it writes totk_xlate.id: the translator identity, a sha256 over everything the build reads (this script,
|
||||
Program.cs, the csproj, the patches, the vendor tree). runtime/gfx_shader.c puts it into every shader cache key, so a
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
# Clone the upstream projects studied in docs/research/ into refs/, at the
|
||||
# commits the notes cite. refs/ is read-only reference material and ignored by git.
|
||||
# Clone the upstream projects this port builds on into refs/, at pinned commits. refs/ is read-only reference
|
||||
# material and ignored by git.
|
||||
set -eu
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Score a game log and its TOTK_SHOTS frames for tools/regress.py.
|
||||
|
||||
Log metrics come from the TOTK_TRACE=perf lines (runtime/prof_win.c `perf_present`); frame metrics look for the two
|
||||
glitches the M5 targets name: near-black blocks inside a lit picture and a far field that lost its detail.
|
||||
glitches it targets: near-black blocks inside a lit picture and a far field that lost its detail.
|
||||
"""
|
||||
import re
|
||||
import statistics
|
||||
|
||||
Reference in new issue
Block a user