Remove the low-end GPU mode from the settings.

The user asked for it: the mode no longer gains frame rate, and the mesh shader fallback covers the cards it was written for. Its switch in the settings menu's Game tab is gone with the rows under it, and so are their 57 text ids and strings.

low_end_gpu itself does nothing any more. It is still a known key, so an older nx2navi.ini gives no unknown-key warning, and while it is on the log says once that the mode was removed in 0.10.2. The preset that gave nine keys other defaults is gone, and so is the rule that read the mode's keys only while it was on: each of them (mesh_emulation, tex_clobber, foliage, vram_heap_limit, vram_reserve_mb, upload_batch_mb, mirror_stage, mirror_hot, submit_finish_gpu and the others) is an ordinary setting with the default of its row or of its reader, so a line a player wrote by hand keeps counting and a file with only the switch runs the game as it runs for everyone else. guest_opt in nx2navi.ini counts for the next rebuild without the switch too (tools/make_game.py).

The recorder thread read the switch itself: on an AMD card without mesh shaders the mode turned it on. It is off on every AMD card now unless vk_recorder = 1 is set.

nx2navi.ini.example lists the former mode's settings as single settings, the 0.10.2 notes have a Removed entry and name the foliage fix of the commit before this one, and the comments that said what the mode sets say so in the past tense. tests/cfg_parse_check has the new reading of the switch and of the keys without it; the test matrix is at 90 ok with the same two not ok as before (installer_test, worktree_test), and a clean build compiles 169 runtime files and links. Not run in the game.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-14 06:13:18 -05:00
parent 1610ab6e08
commit 7a8bbad47e
21 changed files with 138 additions and 364 deletions

View File

@@ -5,8 +5,8 @@
// full cfg_load on the file, then two cfg_set of keys the file does not have
// read cfg_load on the file and cfg_init's check of the environment; then what cfg_get, cfg_bool and cfg_int give
// for a list of keys
// lowend cfg_load on the file and cfg_init; then whether the low-end mode is on (cfg.c lowend_of, which reads its
// switch apart from cfg_get), what cfg_bool gives for its switch and what one of the mode's keys reads
// lowend cfg_load on the file and cfg_init; then what cfg_bool gives for the switch of the low-end GPU mode,
// which is gone (cfg.c lowend_set), what two of the mode's keys read and the default of one of them
// norow cfg_int and cfg_double of a key without a row whose value (the environment's) does not parse
#include <stdarg.h>
#include <stdint.h>
@@ -106,9 +106,9 @@ int main(int argc, char **argv) {
if (!strcmp(argv[1], "lowend")) {
if (!cfg_load(ini)) return printf("cfg_load failed\n"), 2;
cfg_init();
const char *mesh = cfg_get("NX2NAVI_MESH_EMULATION");
printf("got: low_end %d bool %d mesh_emulation '%s'\n", cfg_low_end_active(), cfg_bool("NX2NAVI_LOW_END_GPU"),
mesh ? mesh : "(unset)");
const char *mesh = cfg_get("NX2NAVI_MESH_EMULATION"), *res = cfg_get("NX2NAVI_VRAM_RESERVE_MB");
printf("got: bool %d mesh_emulation '%s' vram_reserve_mb '%s' default '%s'\n", cfg_bool("NX2NAVI_LOW_END_GPU"),
mesh ? mesh : "(unset)", res ? res : "(unset)", cfg_default("NX2NAVI_VRAM_RESERVE_MB"));
return 0;
}
if (!strcmp(argv[1], "norow")) {

View File

@@ -7,8 +7,8 @@ for what it links with. What the readers get and what the log says about the val
it is part of the value; at most 16 lines, then a count;
- switches written as words (cfg.c switch_word, cfg_bool): false, off and no in any case read as 0 and true, on and
yes as 1 through cfg_get, cfg_bool and cfg_int, from the file and from the environment, with no line about them;
the low-end mode's switch (cfg.c lowend_of, read apart from cfg_get) takes the same words, so "low_end_gpu = off"
leaves the mode and its keys off;
the switch of the low-end GPU mode, which is gone (cfg.c lowend_set), takes the same words for its one log line
and turns nothing on: the mode's keys keep their own defaults and are read by themselves;
- values outside their rows' sets (cfg.c check_value): a 0|1 switch with another value, a number outside its range, a
word that is none of the menu's choices, each named with its line; none for a file whose values fit, nor for a list
with numbers in it (the menu's presets); at most 16 lines, then a count;
@@ -187,17 +187,26 @@ def main():
'a file with 601 keys: one line at load from line 513 on', out)
check(rc == 0 and len(set_lines) == 1 and 'VK_PRESENT is not kept' in set_lines[0],
'two menu changes past a full table: one line', out)
# the low-end mode's switch, read apart from cfg_get (cfg.c lowend_of): the same words as cfg_bool, from the file
# and from the environment; the mode is decided once per process, so each value is its own run
for text, extra, want in (
('low_end_gpu = Off\n', None, "low_end 0 bool 0 mesh_emulation '(unset)'"),
('low_end_gpu = 0 ; note\n', None, "low_end 0 bool 0 mesh_emulation '(unset)'"),
('low_end_gpu = yes\n', None, "low_end 1 bool 1 mesh_emulation '1'"),
('; nothing\n', {'NX2NAVI_LOW_END_GPU': 'FALSE'}, "low_end 0 bool 0 mesh_emulation '(unset)'"),
('; nothing\n', {'NX2NAVI_LOW_END_GPU': '0 ; x'}, "low_end 1 bool 1 mesh_emulation '1'")):
# the switch of the low-end GPU mode, which is gone (cfg.c lowend_set): on, it turns none of the mode's keys on and
# gives none another default, and the log says once that the line does nothing; the same words as cfg_bool, from
# the file and from the environment. The mode's keys are read by themselves, with or without the switch.
gone = 'the low-end GPU mode was removed'
unset = "mesh_emulation '(unset)' vram_reserve_mb '(unset)' default '0'"
for text, extra, want, said in (
('low_end_gpu = Off\n', None, f'bool 0 {unset}', 0),
('low_end_gpu = 0 ; note\n', None, f'bool 0 {unset}', 0),
('low_end_gpu = yes\n', None, f'bool 1 {unset}', 1),
('low_end_gpu = 1\n', None, f'bool 1 {unset}', 1),
('; nothing\n', {'NX2NAVI_LOW_END_GPU': 'FALSE'}, f'bool 0 {unset}', 0),
('; nothing\n', {'NX2NAVI_LOW_END_GPU': '0 ; x'}, f'bool 1 {unset}', 1),
('mesh_emulation = 1\nvram_reserve_mb = 512\n', None,
"bool 0 mesh_emulation '1' vram_reserve_mb '512' default '0'", 0),
('low_end_gpu = 1\nmesh_emulation = 1\n', None,
"bool 1 mesh_emulation '1' vram_reserve_mb '(unset)' default '0'", 1)):
rc, out = run(exe, env, 'lowend', text, work, extra)
what = text.strip() if extra is None else f"the environment's {extra['NX2NAVI_LOW_END_GPU']!r}"
check(rc == 0 and f'got: {want}' in out, f'low end: {what}: {want}', out)
what = text.strip().replace('\n', ', ') if extra is None else f"the environment's {extra['NX2NAVI_LOW_END_GPU']!r}"
check(rc == 0 and f'got: {want}' in out and out.count(gone) == said,
f'low end: {what}: {want}, the removal said {said} time(s)', out)
rc, out = run(exe, env, 'norow', CLEAN, work, {'NX2NAVI_NO_ROW_CHECK': 'abc'})
check(rc == 0 and 'got: cfg_int 0 cfg_double 0' in out, 'cfg_int of a set, unparsable key without a row: 0', out)
print(f'cfg_parse_check: {"FAILED" if bad else "ok"}')

View File

@@ -2,7 +2,7 @@
content guard workflow runs it beside tools/check_content.py.
It fails on:
- a row or name after a // on a line of known[], metas[], sections[], DECK[] or LOWEND[]: it is not compiled, and a
- a row or name after a // on a line of known[], metas[], sections[] or DECK[]: it is not compiled, and a
hidden metas[] row ends every start with "FATAL: cfg: NX2NAVI_<X> has no row in the settings table" (this shipped
in two test builds); a /* inside those tables, which this check does not read;
- a name twice in known[], or two metas[] rows for one key in one #if branch (the first branch stands for the table,
@@ -32,7 +32,6 @@ TABLES = { # declaration, what a row looks like on a line
'metas[]': ('static const CfgMeta metas[]', r'\{\s*"(' + KEY + r')"'),
'sections[]': ('sections[] = {', r'\{\s*"(' + KEY + r')"'),
'DECK[]': ('DECK[] = {', r'\{\s*"(' + KEY + r')"'),
'LOWEND[]': ('LOWEND[] = {', r'\{\s*"(' + KEY + r')"'),
}
VALUE_FNS = ('cfg_value', 'cfg_int', 'cfg_bool', 'cfg_double')
GUARD_FNS = ('cfg_get', 'cfg_is_set', 'cfg_meta')

View File

@@ -2932,18 +2932,26 @@ class MakeGameTest(Tmp):
with mock.patch.dict(os.environ, {'NX2NAVI_SHADER_CARRY': 'Off'}):
self.assertFalse(self.mg.shader_carry(out))
def test_guest_opt_from_the_ini_only_with_the_low_end_mode_on(self):
"""guest_opt_ini takes guest_opt from nx2navi.ini only while low_end_gpu is on as runtime/cfg.c lowend_of
reads it: 0, off, false and no in any case are off."""
def test_guest_opt_from_the_ini_counts_without_the_low_end_mode(self):
"""guest_opt_ini takes guest_opt from nx2navi.ini by itself. Up to 0.10.1 it needed low_end_gpu on in the file;
that mode is gone, and a low_end_gpu line of any value changes nothing. The environment's value stays."""
out = self.tmp / 'game'
out.mkdir()
env = {k: v for k, v in os.environ.items() if k.upper() != 'NX2NAVI_GUEST_OPT'}
for low, taken in (('1', True), ('yes', True), ('On', True), ('0', False), ('off', False), ('FALSE', False),
('no', False), ('0 ; note', False)):
(out / 'nx2navi.ini').write_text(f'guest_opt = 2\nlow_end_gpu = {low}\n')
for low in ('', 'low_end_gpu = 1\n', 'low_end_gpu = yes\n', 'low_end_gpu = 0\n', 'low_end_gpu = off\n',
'low_end_gpu = 0 ; note\n'):
(out / 'nx2navi.ini').write_text('guest_opt = 2\n' + low)
with mock.patch.dict(os.environ, env, clear=True):
self.mg.guest_opt_ini(out)
self.assertEqual(os.environ.get('NX2NAVI_GUEST_OPT'), '2' if taken else None, low)
self.assertEqual(os.environ.get('NX2NAVI_GUEST_OPT'), '2', low)
(out / 'nx2navi.ini').write_text('low_end_gpu = 1\n')
with mock.patch.dict(os.environ, env, clear=True):
self.mg.guest_opt_ini(out)
self.assertIsNone(os.environ.get('NX2NAVI_GUEST_OPT')) # no guest_opt line: nothing is set
(out / 'nx2navi.ini').write_text('guest_opt = 2\n')
with mock.patch.dict(os.environ, dict(env, NX2NAVI_GUEST_OPT='1'), clear=True):
self.mg.guest_opt_ini(out)
self.assertEqual(os.environ.get('NX2NAVI_GUEST_OPT'), '1') # the environment wins over the file
def test_cache_from_before_carrying_names_its_translator(self):
"""Before a new translator goes beside the game, a shader cache with no identities.txt gets one naming the

View File

@@ -39,10 +39,11 @@ int main(void) {
// ---- the mesh emulation acts only without mesh shaders, and then before the fallback
expect(0, 0, "auto", "1", MESH_PATH_EMULATION, "no mesh shaders, mesh_emulation = 1");
expect(0, 1, "1", "1", MESH_PATH_EMULATION, "no mesh shaders, both set");
expect(1, 1, "auto", "1", MESH_PATH_FALLBACK, "an RX 6000 card with mesh_emulation = 1 (low_end_gpu)");
expect(1, 1, "auto", "1", MESH_PATH_FALLBACK, "an RX 6000 card with mesh_emulation = 1");
expect(1, 0, "auto", "1", MESH_PATH_NATIVE, "a card with mesh shaders and mesh_emulation = 1");
// ---- an NVIDIA card never takes the emulation, which ended the game inside its driver (GTX 10, low_end_gpu)
expect_nv(0, 0, 1, "auto", "1", MESH_PATH_FALLBACK, "a GTX 10 card with mesh_emulation = 1 (low_end_gpu)");
// ---- an NVIDIA card never takes the emulation, which ended the game inside its driver (GTX 10 cards in the
// low-end GPU mode of 0.10.1, which set mesh_emulation = 1)
expect_nv(0, 0, 1, "auto", "1", MESH_PATH_FALLBACK, "a GTX 10 card with mesh_emulation = 1");
expect_nv(0, 0, 1, "1", "1", MESH_PATH_FALLBACK, "a GTX 10 card with both set");
expect_nv(0, 0, 1, "0", "1", MESH_PATH_NONE, "a GTX 10 card with mesh_emulation = 1 and mesh_fallback = 0");
expect_nv(0, 0, 1, "auto", "0", MESH_PATH_FALLBACK, "a GTX 10 card by default");