From 58d7072c271f184a9343fb4ae6f3d2c4523fa334 Mon Sep 17 00:00:00 2001 From: seedds Date: Mon, 9 Nov 2026 19:47:39 -0800 Subject: [PATCH] Return stored NVN sampler builder query state --- dm/gfx/nvn/hle_nvn.c | 7 +++++++ dm/gfx/tests/t_nvn.c | 30 ++++++++++++++++++++++++++++++ docs/agents/symptoms.md | 1 + docs/development.md | 2 ++ docs/reference/nvn-objects.md | 7 +++++++ 5 files changed, 47 insertions(+) diff --git a/dm/gfx/nvn/hle_nvn.c b/dm/gfx/nvn/hle_nvn.c index c59e39ada8..0e8b0104ed 100644 --- a/dm/gfx/nvn/hle_nvn.c +++ b/dm/gfx/nvn/hle_nvn.c @@ -718,6 +718,13 @@ NVN(nvnSamplerBuilderGetWrapMode, void, const NVNsamplerBuilder *b, NVNwrapMode NVN(nvnSamplerBuilderGetLodClamp, void, const NVNsamplerBuilder *b, float *lo, float *hi) { // Bayonetta 3 reads it back while building its samplers const samp_par *o = &((const samp_builder_t *)b)->p; *lo = o->lod_min; *hi = o->lod_max; } +NVN(nvnSamplerBuilderGetLodBias, float, const NVNsamplerBuilder *b) { return ((const samp_builder_t *)b)->p.lod_bias; } +NVN(nvnSamplerBuilderGetCompare, void, const NVNsamplerBuilder *b, NVNcompareMode *mode, NVNcompareFunc *func) { + const samp_par *o = &((const samp_builder_t *)b)->p; *mode = (NVNcompareMode)o->cmp_mode; *func = (NVNcompareFunc)o->cmp_func; +} +NVN(nvnSamplerBuilderGetBorderColor, void, const NVNsamplerBuilder *b, float *c) { memcpy(c, ((const samp_builder_t *)b)->p.border, 16); } +NVN(nvnSamplerBuilderGetMaxAnisotropy, float, const NVNsamplerBuilder *b) { return ((const samp_builder_t *)b)->p.aniso; } +NVN(nvnSamplerBuilderGetReductionFilter, NVNsamplerReduction, const NVNsamplerBuilder *b) { return (NVNsamplerReduction)((const samp_builder_t *)b)->p.reduction; } NVN(nvnSamplerInitialize, NVNboolean, NVNsampler *s, const NVNsamplerBuilder *b) { samp_t *o = (samp_t *)s; memset(o, 0, sizeof *o); o->magic = MAGIC('S', 'M', 'P', 0); o->p = ((const samp_builder_t *)b)->p; return 1; // nvn_obj.h samp_t: games hash the bytes } diff --git a/dm/gfx/tests/t_nvn.c b/dm/gfx/tests/t_nvn.c index 3f300718eb..8cb6113df9 100644 --- a/dm/gfx/tests/t_nvn.c +++ b/dm/gfx/tests/t_nvn.c @@ -172,6 +172,36 @@ int main(void) { EXPECT("NVN2: the memory pool page size is read at run time", DEVINFO_RT, g_devinfo2[NVN_DEVICE_INFO_MEMORY_POOL_PAGE_SIZE]); EXPECT("NVN2: items up to 0xA9", 0xAA, (int)(sizeof g_devinfo2 / sizeof *g_devinfo2)); + { // #872: renderer setup reads its builder back through the exported NVN ABI. + NVNsamplerBuilder builders[2] = {0}; + for (int i = 0; i < 2; i++) P(nvnSamplerBuilderSetDefaults)(&builders[i]); + const float border[4] = {-0.25f, 0.5f, 0.75f, 1.f}; + P(nvnSamplerBuilderSetLodBias)(&builders[0], -2.25f); + P(nvnSamplerBuilderSetCompare)(&builders[0], NVN_COMPARE_MODE_COMPARE_R_TO_TEXTURE, NVN_COMPARE_FUNC_GEQUAL); + P(nvnSamplerBuilderSetBorderColor)(&builders[0], border); + P(nvnSamplerBuilderSetMaxAnisotropy)(&builders[0], 8.f); + P(nvnSamplerBuilderSetReductionFilter)(&builders[0], NVN_SAMPLER_REDUCTION_MAX); + // Query a changed builder, an independent default builder, then the + // changed one after SetDefaults. Expected values do not read host state. + for (int i = 0; i < 3; i++) { + if (i == 2) P(nvnSamplerBuilderSetDefaults)(&builders[0]); + const NVNsamplerBuilder *b = &builders[i == 1]; + NVNcompareMode mode = NVN_COMPARE_MODE_LARGE; + NVNcompareFunc func = NVN_COMPARE_FUNC_LARGE; + float color[6] = {13.f, 17.f, 19.f, 23.f, 29.f, 31.f}; + const float want[6] = {13.f, i ? 0.f : -0.25f, i ? 0.f : 0.5f, i ? 0.f : 0.75f, i ? 0.f : 1.f, 31.f}; + EXPECT("builder LOD bias returned in float register", 1, P(nvnSamplerBuilderGetLodBias)(b) == (i ? 0.f : -2.25f)); + P(nvnSamplerBuilderGetCompare)(b, &mode, &func); + EXPECT("builder compare mode written to caller", i ? NVN_COMPARE_MODE_NONE : NVN_COMPARE_MODE_COMPARE_R_TO_TEXTURE, mode); + EXPECT("builder compare function written to caller", i ? NVN_COMPARE_FUNC_LESS : NVN_COMPARE_FUNC_GEQUAL, func); + P(nvnSamplerBuilderGetBorderColor)(b, color + 1); + int equal = 1; for (int j = 0; j < 6; j++) if (color[j] != want[j]) equal = 0; + EXPECT("builder border writes exactly four floats", 1, equal); + EXPECT("builder anisotropy returned in float register", 1, P(nvnSamplerBuilderGetMaxAnisotropy)(b) == (i ? 1.f : 8.f)); + EXPECT("builder reduction returned as enum", i ? NVN_SAMPLER_REDUCTION_AVERAGE : NVN_SAMPLER_REDUCTION_MAX, P(nvnSamplerBuilderGetReductionFilter)(b)); + } + } + NVNsamplerBuilder sb; NVNsampler sampler; P(nvnSamplerBuilderSetDefaults)(&sb); const uint32_t opaque_black[4] = {0, 0, 0, 1}; diff --git a/docs/agents/symptoms.md b/docs/agents/symptoms.md index 5a8f523bce..4c6d91bfdd 100644 --- a/docs/agents/symptoms.md +++ b/docs/agents/symptoms.md @@ -605,6 +605,7 @@ One line per symptom in the [subsystem references](../reference/): what you see - about one run in four stalled early (around the opening movie or the title), with no log output, not even the time-limit report. → [Dirty-page tracker race and early stalls](../reference/nvn-objects.md#dirty-page-tracker-race-and-early-stalls) - a CPU-written texture stays stale after later writes; letters can be missing from a glyph cache. → [Dirty-page writes missed during upload](../reference/nvn-objects.md#dirty-page-writes-missed-during-upload) - a host system call writing into storage after its NVN pool was finalized fails with `EFAULT`; a direct write can fault after the tracker stopped owning the page. → [A retired pool remains read-only](../reference/nvn-objects.md#a-retired-pool-remains-read-only) +- `[RED] NVN unimplemented function called: nvnSamplerBuilderGetLodBias`, `nvnSamplerBuilderGetCompare`, `nvnSamplerBuilderGetBorderColor`, `nvnSamplerBuilderGetMaxAnisotropy` or `nvnSamplerBuilderGetReductionFilter` during renderer setup. → [Sampler builder queries leave renderer state unread](../reference/nvn-objects.md#sampler-builder-queries-leave-renderer-state-unread) - `[RED] NVN unimplemented function called: nvnSamplerBuilderSetBorderColorui` during renderer initialization. → [Integer sampler border colours](../reference/nvn-objects.md#integer-sampler-border-colours) - `[RED] sampler ID 4096 not registered` (or another ID past the sampler pool), once, from the first frames on. → [Bound handles are the driver's 32-bit words](../reference/nvn-objects.md#bound-handles-are-the-drivers-32-bit-words) - device / subsystem setup returns quietly and the next stage never starts. → [Version and capability checks that fail quietly](../reference/nvn-objects.md#version-and-capability-checks-that-fail-quietly) diff --git a/docs/development.md b/docs/development.md index 955a0260d6..55244ae135 100644 --- a/docs/development.md +++ b/docs/development.md @@ -14,6 +14,8 @@ The README covers packing and playing. This page is for working on nxnative itse | iOS app | One unsigned IPA for iPhone and iPad. The rewritten code is embedded in the executable; the shader libraries, the RomFS and the game icon are inside the app. A settings screen comes before the game (`Launcher.swift`, drawn from `dm/launcher.json`, which `dm/games/settings.py` writes from `dm/games/settings.tsv`); the host's loading starts at app launch meanwhile, and the frame rate and render scale go into the frame step state when the game starts (`loader.c` `dm_fps_state_apply`) | | Known gaps | Conditional render is always drawn; [indirect-count multi-draws](reference/rendering.md#indirect-count-multi-draws) can wait for a GPU-written count when the records need conversion, cannot be GPU-masked, or exceed the mask table capacity; geometry shaders do not translate. TBDR optimizations are off by default (they don't understand bindless textures yet). No amiibo. On the Mac, Mario's menu and dialogue text render blank (fine on the iPhone) | +NVN sampler builders expose their stored LOD bias, comparison settings, float border colour, maximum anisotropy and reduction filter through the corresponding getters, including after `SetDefaults` ([sampler builder queries](reference/nvn-objects.md#sampler-builder-queries-leave-renderer-state-unread)). + ## Cheats How a Switch with Atmosphère runs cheats, and where this host differs, is in [Cheats on a Switch](reference/cheats.md). diff --git a/docs/reference/nvn-objects.md b/docs/reference/nvn-objects.md index 217ff95635..060b5a7b6e 100644 --- a/docs/reference/nvn-objects.md +++ b/docs/reference/nvn-objects.md @@ -696,6 +696,13 @@ Games read this count as a position in their own command memory and size their h ## NVN entry points +### Sampler builder queries leave renderer state unread + +- **Symptom:** `[RED] NVN unimplemented function called: nvnSamplerBuilderGetLodBias`, `nvnSamplerBuilderGetCompare`, `nvnSamplerBuilderGetBorderColor`, `nvnSamplerBuilderGetMaxAnisotropy` or `nvnSamplerBuilderGetReductionFilter` during renderer setup. +- **Cause:** the setters stored these values, but the builder getters still reached the generic unimplemented handler. Output parameters were left untouched, and scalar results did not follow the getters' float or enum return signatures. +- **Fix:** the five getters read the builder's current sampler parameters through their typed NVN entry points, including the values restored by `SetDefaults`. +- **Found on:** [Digimon Story Time Stranger, issue #872](https://github.com/seedds/nxnative/issues/872), whose reporter identified these calls before an early crash. No repository profile exists yet. Archive extraction, rendering and the reported iPhone freeze are separate unresolved parts of that report; these getters alone do not establish that the game runs. + ### Integer sampler border colours - **Symptom:** `[RED] NVN unimplemented function called: nvnSamplerBuilderSetBorderColorui` during renderer initialization.