mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Apply anisotropic filtering while the game runs, and add it to the Screen tab.
NX2NAVI_ANISO was read once, when the first sampler was made (vk_texture.c sampler_features), and make_sampler raised the level of the surface textures' samplers as it created them. Samplers are looked up by the game's parameters and live until shutdown, so a new level could only reach them after a restart, and the setting had no row in the menu. The decision which samplers get the level moves from make_sampler into aniso_raised, and sampler_for puts the raised level into the sampler key, as it does with the DLSS texture bias. vk_sampler_aniso_live reads the setting again after a settings change, at each present beside vk_dlaa_frame's bias; on a new level it clears the slots' samplers and bumps the sampler generation, as vk_sampler_dlss_bias does, so the slots and the descriptor caches take samplers of the new level from the next draw. The old level's samplers stay until shutdown like every sampler. The setting is marked live in cfg.c, and the Screen tab gets a Texture filtering row (Game, 2x, 4x, 8x, 16x) with the description the catalog already had. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
chrg6
parent
834d9e2d20
commit
03cb703e8f
@@ -108,7 +108,7 @@ beside the game.
|
||||
"Preparing shaders" once while the driver rebuilds them. With a driver that has Vulkan's graphics pipeline library
|
||||
it lasts at most about 20 seconds once the shaders are translated, and the remaining pipelines are built in the
|
||||
background while you play.
|
||||
- **Screen** in the menu has Upscaling (DLSS on NVIDIA RTX cards, FSR 3.1 on any card), Frame generation (NVIDIA DLSS
|
||||
- **Screen** in the menu has Texture filtering (anisotropic, up to 16x, at once), Upscaling (DLSS on NVIDIA RTX cards, FSR 3.1 on any card), Frame generation (NVIDIA DLSS
|
||||
on RTX 40 and 50 cards, with a DLSS multiplier of 2x, or 3x and 4x on RTX 50 cards; FSR 3.1 on any card; it needs
|
||||
DLSS or FSR 3.1 upscaling on, and the frame limit then counts the frames on screen; with no limit chosen it is the
|
||||
display's refresh rate), HDR (the game's picture tone mapped to your display, with its own grading rows, or Native, the game's own HDR; turn HDR on in Windows first) and the
|
||||
|
||||
@@ -281,7 +281,8 @@
|
||||
# aspect = fit
|
||||
# Anisotropic filtering of surface textures: game (the level the game asks for, as on the Switch) or 2, 4, 8, 16. Keeps
|
||||
# ground, roads and walls seen at a shallow angle sharp into the distance. Only filtered, mipmapped textures that tile
|
||||
# are raised; shadow maps, data maps and screen-sized buffers keep the game's setting. Read at start.
|
||||
# are raised; shadow maps, data maps and screen-sized buffers keep the game's setting. Changes while the game runs take
|
||||
# effect at once.
|
||||
# aniso = game
|
||||
# Sun shadow map size: game (the game's own, which changes with the view and drops to about 400 near the ground, so
|
||||
# leaf shadows look blocky and jump) or a fixed 256, 512, 768, 1024, 1536 or 2048. Above 960 the texture heap grows with
|
||||
|
||||
@@ -145,7 +145,7 @@ static const CfgMeta metas[] = {
|
||||
{"GAME_ASPECT", "16:9", "16:9|window|21:9|32:9", RESTART}, // widescreen.c
|
||||
{"GAME_RES", "1440", "1080|1440|1800|2160|2880|window", RESTART}, // widescreen.c: the picture's height
|
||||
{"ASPECT", "fit", "fit|stretch", LIVE}, {"FOV", "100", "70..140", LIVE},
|
||||
{"ANISO", "game", "game|2|4|8|16", RESTART}, // vk_texture.c sampler_features
|
||||
{"ANISO", "game", "game|2|4|8|16", LIVE}, // vk_texture.c sampler_features, vk_sampler_aniso_live
|
||||
// colour adjustment (vk_color.c); the preset is the menu's, the four values are what the present uses
|
||||
{"COLOR_PRESET", "default", "default|vivid|clarity|custom", LIVE}, {"COLOR_BRIGHTNESS", "0", "-30..30", LIVE},
|
||||
{"COLOR_CONTRAST", "100", "70..150", LIVE}, {"COLOR_SATURATION", "100", "50..200", LIVE},
|
||||
|
||||
@@ -988,6 +988,10 @@ static void tab_screen(float scale) {
|
||||
int dl = cfg_bool("NX2NAVI_DLAA"), fsr3 = dl && !strcmp(cfg_value("NX2NAVI_TAA"), "fsr3");
|
||||
if (!fsr3 && (strcmp(cfg_value("NX2NAVI_UPSCALER"), "fsr") || dl)) opt_disable_next(UI_MENU_SHARPNESS_NEEDS_FSR);
|
||||
opt_slider("NX2NAVI_SHARPNESS", UI_MENU_SHARPNESS, 0, 100, UI_MENU_SHARPNESS_DESC, 5, "%.0f %%");
|
||||
// Anisotropic filtering of the surface textures that tile (vk_texture.c aniso_raised), at once (vk_sampler_aniso_live)
|
||||
static const char *const aniso[] = {"game", "2", "4", "8", "16"};
|
||||
const char *aniso_names[] = {menu_text(UI_MENU_ANISO_GAME), "2x", "4x", "8x", "16x"};
|
||||
opt_choice_list("NX2NAVI_ANISO", UI_MENU_ANISO, aniso, aniso_names, 5, UI_MENU_ANISO_DESC);
|
||||
opt_end();
|
||||
}
|
||||
|
||||
|
||||
@@ -526,6 +526,7 @@ uint32_t vk_texture_entry_gen(void); // vk_texture.c: bumped by
|
||||
int vk_texture_dirty_slots_locked(uint64_t *pos, void (*fn)(int id, void *arg), void *arg);
|
||||
uint64_t vk_texture_dirty_pos_locked(void);
|
||||
void vk_sampler_dlss_bias(float bias); // texture LOD bias added for DLSS upscaling (material samplers)
|
||||
void vk_sampler_aniso_live(void); // each present: NX2NAVI_ANISO after a settings change (tiled samplers)
|
||||
uint32_t vk_sampler_gen(void); // changes whenever a sampler slot is registered
|
||||
void vk_texture_gpu_written(const tex_t *t); // rendered or copied into by the GPU: no re-upload from storage
|
||||
// The live image of the given format and img's size at img's storage address, NULL when there is none. Texture lock held.
|
||||
|
||||
@@ -105,6 +105,7 @@ static void present_image(const tex_t *t, const int crop[4], uint64_t at_ns, uin
|
||||
vk_exec_frame_log(t, frame);
|
||||
dynres_frame(); // before vk_dlaa_frame reads the DLSS mode it may override
|
||||
vk_dlaa_frame();
|
||||
vk_sampler_aniso_live(); // NX2NAVI_ANISO changed in the menu: the surface textures' samplers take the new level
|
||||
vk_mvsurvey_frame();
|
||||
vk_mvprev_frame();
|
||||
vk_mem_census(frame);
|
||||
|
||||
@@ -3128,6 +3128,17 @@ static VkCompareOp compare_op(uint32_t f) {
|
||||
|
||||
static struct { int init, mirror_clamp_edge, minmax, border_no_format; float aniso; } sf; // aniso: NX2NAVI_ANISO, 0 = the game's
|
||||
|
||||
static float aniso_setting(void) {
|
||||
const char *an = cfg_value("NX2NAVI_ANISO");
|
||||
return strcmp(an, "game") ? (float)atoi(an) : 0;
|
||||
}
|
||||
|
||||
static void aniso_log(void) {
|
||||
if (sf.aniso > 1) rt_log("vk: anisotropic filtering: %gx", sf.aniso);
|
||||
else rt_log("vk: anisotropic filtering: the game's");
|
||||
}
|
||||
|
||||
// Caller holds the lock.
|
||||
static void sampler_features(VkCtx *c) {
|
||||
if (sf.init) return;
|
||||
VkPhysicalDeviceCustomBorderColorFeaturesEXT cbf = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT};
|
||||
@@ -3138,12 +3149,25 @@ static void sampler_features(VkCtx *c) {
|
||||
sf.mirror_clamp_edge = f12.samplerMirrorClampToEdge;
|
||||
sf.minmax = f12.samplerFilterMinmax;
|
||||
sf.border_no_format = c->has_custom_border_color && cbf.customBorderColors && cbf.customBorderColorWithoutFormat;
|
||||
const char *an = cfg_value("NX2NAVI_ANISO");
|
||||
sf.aniso = strcmp(an, "game") ? (float)atoi(an) : 0;
|
||||
rt_log("vk: anisotropic filtering: %s", sf.aniso > 1 ? an : "the game's");
|
||||
sf.aniso = aniso_setting();
|
||||
aniso_log();
|
||||
sf.init = 1;
|
||||
}
|
||||
|
||||
// NX2NAVI_ANISO raises the level for filtered, mipmapped colour samplers that repeat on U and V: the surface textures,
|
||||
// which tile, so ground and walls seen at a shallow angle stay sharp into the distance. Depth-compare (shadow)
|
||||
// samplers, nearest or unmipmapped ones (data maps, LUTs) and clamped ones keep the game's level: clamped samplers
|
||||
// read screen-sized buffers, whose coordinates can stretch steeply at an object's edge, where anisotropic taps reach
|
||||
// across it and leave a speckled fringe (the inventory's character preview). The game's own wrap modes decide: a
|
||||
// mirror-clamp one maps to MIRRORED_REPEAT where the device lacks mirror-clamp-to-edge (make_sampler). Caller holds the
|
||||
// lock, after sampler_features.
|
||||
static int aniso_raised(const samp_par *d) {
|
||||
int tiled = (d->ws == NVN_WRAP_MODE_REPEAT || d->ws == NVN_WRAP_MODE_MIRRORED_REPEAT) &&
|
||||
(d->wt == NVN_WRAP_MODE_REPEAT || d->wt == NVN_WRAP_MODE_MIRRORED_REPEAT);
|
||||
return sf.aniso > d->aniso && tiled && !d->cmp_mode && d->magf == NVN_MAG_FILTER_LINEAR &&
|
||||
(d->minf == NVN_MIN_FILTER_LINEAR_MIPMAP_NEAREST || d->minf == NVN_MIN_FILTER_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
static VkSampler make_sampler(VkCtx *c, const samp_par *d) {
|
||||
sampler_features(c);
|
||||
VkSamplerCreateInfo si = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
|
||||
@@ -3175,21 +3199,10 @@ static VkSampler make_sampler(VkCtx *c, const samp_par *d) {
|
||||
if (si.maxLod < si.minLod) si.maxLod = si.minLod;
|
||||
float lim = c->props.limits.maxSamplerLodBias;
|
||||
si.mipLodBias = fmaxf(-lim, fminf(lim, d->lod_bias));
|
||||
// NX2NAVI_ANISO raises the level for filtered, mipmapped colour samplers that repeat on U and V: the surface textures,
|
||||
// which tile, so ground and walls seen at a shallow angle stay sharp into the distance. Depth-compare (shadow)
|
||||
// samplers, nearest or unmipmapped ones (data maps, LUTs) and clamped ones keep the game's level: clamped samplers
|
||||
// read screen-sized buffers, whose coordinates can stretch steeply at an object's edge, where anisotropic taps reach
|
||||
// across it and leave a speckled fringe (the inventory's character preview).
|
||||
float aniso = d->aniso;
|
||||
// The game's own wrap modes: a mirror-clamp one maps to MIRRORED_REPEAT where the device lacks mirror-clamp-to-edge.
|
||||
int tiled = (d->ws == NVN_WRAP_MODE_REPEAT || d->ws == NVN_WRAP_MODE_MIRRORED_REPEAT) &&
|
||||
(d->wt == NVN_WRAP_MODE_REPEAT || d->wt == NVN_WRAP_MODE_MIRRORED_REPEAT);
|
||||
if (sf.aniso > aniso && tiled && !d->cmp_mode && si.magFilter == VK_FILTER_LINEAR && si.minFilter == VK_FILTER_LINEAR &&
|
||||
(mf == NVN_MIN_FILTER_LINEAR_MIPMAP_NEAREST || mf == NVN_MIN_FILTER_LINEAR_MIPMAP_LINEAR))
|
||||
aniso = sf.aniso;
|
||||
if (aniso > 1) {
|
||||
// d->aniso holds NX2NAVI_ANISO's level where it applies (sampler_for, aniso_raised)
|
||||
if (d->aniso > 1) {
|
||||
si.anisotropyEnable = VK_TRUE;
|
||||
si.maxAnisotropy = fminf(aniso, c->props.limits.maxSamplerAnisotropy);
|
||||
si.maxAnisotropy = fminf(d->aniso, c->props.limits.maxSamplerAnisotropy);
|
||||
}
|
||||
if (d->cmp_mode) {
|
||||
si.compareEnable = VK_TRUE;
|
||||
@@ -3264,12 +3277,35 @@ void vk_sampler_dlss_bias(float b) {
|
||||
unlock();
|
||||
}
|
||||
|
||||
// NX2NAVI_ANISO, read again at each present after a settings change (vk_backend.c present_image, beside the DLSS bias of
|
||||
// vk_dlaa_frame): the raised level is part of the sampler key (sampler_for), so on a change the slots look their
|
||||
// samplers up again and the descriptor caches follow samp_gen, as for vk_sampler_dlss_bias. The old level's samplers
|
||||
// stay until shutdown like every sampler: one set per level at most. Before the first sampler there is nothing to do:
|
||||
// sampler_features reads the setting then.
|
||||
void vk_sampler_aniso_live(void) {
|
||||
static unsigned seen = ~0u;
|
||||
unsigned v = cfg_version();
|
||||
if (v == seen) return;
|
||||
seen = v;
|
||||
lock();
|
||||
float a = aniso_setting();
|
||||
if (sf.init && a != sf.aniso) {
|
||||
sf.aniso = a;
|
||||
aniso_log();
|
||||
for (int i = 0; i < nsslots; i++) sslots[i].s = sslots[i].sp = sslots[i].sc = VK_NULL_HANDLE; // looked up again (the key moves)
|
||||
samp_gen++;
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
// Caller holds the lock.
|
||||
static VkSampler sampler_for(VkCtx *c, const samp_par *s) {
|
||||
sampler_features(c);
|
||||
samp_par k;
|
||||
memset(&k, 0, sizeof k);
|
||||
k = *s;
|
||||
if (dlss_bias != 0 && dlss_biased(&k)) k.lod_bias += dlss_bias; // part of the key: samplers live until shutdown
|
||||
if (aniso_raised(&k)) k.aniso = sf.aniso; // likewise
|
||||
VkSampler v = (VkSampler)nvn_hm_get(&samplers, &k);
|
||||
if (!v) {
|
||||
v = make_sampler(c, &k);
|
||||
|
||||
Reference in New Issue
Block a user