diff --git a/gpu/vk/vk_menu.cpp b/gpu/vk/vk_menu.cpp index 9cb3ead0f..c935e729f 100644 --- a/gpu/vk/vk_menu.cpp +++ b/gpu/vk/vk_menu.cpp @@ -506,8 +506,8 @@ const char* tx_robust(int v) { return (unsigned)v < 3 ? t[v] : t[0]; } const char* tx_preset(int v) { - static const char* t[] = {"Custom", "Minimum (4GB)", "Low (6GB)", "Balanced (8GB)", "High (12GB)", "Ultra (16GB+)"}; - return t[std::min(std::max(v, 0), 5)]; + static const char* t[] = {"Custom", "Minimum (4GB)", "Low (6GB)", "Balanced (8GB)", "High (12GB)", "Ultra (16GB+)", "PS3-style (720p)"}; + return t[std::min(std::max(v, 0), 6)]; } void sync_preset_budgets(int p) { switch (p) { @@ -544,6 +544,12 @@ void apply_preset(int p) { opts.resolution = 1; opts.upscale = 2; opts.sharpness = 50; opts.pace = 1; opts.realtime = 1; opts.handheld = 0; break; + case 6: // PS3-style: 67% of a 1920x1080 output is about 1280x720 + opts.output_res = 3; opts.native_scene = 1; opts.resolution = 3; + opts.upscale = 2; opts.sharpness = 30; opts.anisotropy = 0; + opts.pace = 1; opts.realtime = 1; opts.handheld = 0; + opts.sampler_cache = 1; + break; default: break; } @@ -558,7 +564,8 @@ MENU_HINTS(hn_preset, 0, "Each row as you set it. Changing most rows yourself co "Resolution 75% (with DLSS its nearest mode), Lanczos, Sharpness 30%, the Handheld Profile.", "Resolution 100% (with DLSS its nearest mode), Catmull-Rom, Sharpness 30%.", "Resolution 100% (with DLSS its nearest mode), Lanczos, Sharpness 40%.", - "Resolution 100% (with DLSS its nearest mode), Lanczos, Sharpness 50%.") + "Resolution 100% (with DLSS its nearest mode), Lanczos, Sharpness 50%.", + "A 1920x1080 output with the 3D at about 1280x720, Lanczos scaling, low anisotropic filtering and the texture-streaming cache. A PS3-era performance look, not GTA V assets.") MENU_HINTS(hn_gpu, 0, "The best dedicated GPU.") /// Frame generation: how many frames are made for each of the game's const char* hn_framegen(int v, int variant) { @@ -635,7 +642,7 @@ MENU_HINTS(hn_prompts, 0, Row rows[] = { // Graphics (Reflex, Anti-Lag 2, the Upscaler and the DLSS model only where they run here; HDR's paper white and peak // brightness only with HDR on: row_shown) - {TAB_GRAPHICS, "Quality preset", &opts.preset, 0, 5, 1, tx_preset, + {TAB_GRAPHICS, "Quality preset", &opts.preset, 0, 6, 1, tx_preset, "Sets the scaling rows and memory limits for a GPU with that much memory. The Upscaler and Frame generation stay yours.", hn_preset}, {TAB_GRAPHICS, "Fullscreen", &opts.fullscreen, 0, 1, 1, tx_onoff, @@ -673,6 +680,8 @@ Row rows[] = { "Which of NVIDIA's DLSS networks draws the picture.", hn_dlss_model, false, true}, {TAB_GRAPHICS, "Render resolution", &opts.resolution, 0, ur::kValues - 1, 1, tx_resolution, "The share of the output the 3D is drawn at; the Upscaler fills in the rest.", hn_resolution}, + {TAB_GRAPHICS, "Native 3D resolution", &opts.native_scene, 0, 1, 1, tx_onoff, + "Uses the full scene base scale instead of the game's 0.8 scale. Sharper 3D, with more GPU work. Applies after a restart." , nullptr, true}, {TAB_GRAPHICS, "Window scaling filter", &opts.upscale, 0, 2, 1, tx_filter, "How the finished picture is scaled to the window, when its size differs from the Output resolution.", hn_filter}, {TAB_GRAPHICS, "Sharpness", &opts.sharpness, 0, 100, 10, tx_percent, @@ -680,6 +689,8 @@ Row rows[] = { {TAB_GRAPHICS, "Anisotropic filtering", &opts.anisotropy, 0, 4, 1, tx_anisotropy, "Keeps textures sharp on surfaces seen at a slant, such as the ground ahead: higher is sharper in the distance, for a " "little GPU time. Game's is the console's own (1x or 2x).", nullptr}, + {TAB_GRAPHICS, "Texture streaming cache", &opts.sampler_cache, 0, 1, 1, tx_onoff, + "Keeps sampler settings on the replay thread, avoiding a registration lock while textures stream. Can reduce stalls when entering new areas. Applies after a restart.", nullptr, true}, {TAB_GRAPHICS, "Profile", &opts.handheld, 0, 1, 1, tx_profile, "Runs the game as the console docked or handheld. Applies after a restart.", hn_handheld, true}, {TAB_GRAPHICS, "Ultrawide", &opts.ultrawide, 0, 1, 1, tx_ultrawide, @@ -802,9 +813,6 @@ Row rows[] = { {TAB_DEBUG, "Descriptor reuse", &opts.descriptor_reuse, 0, 1, 1, tx_onoff, "A draw keeps its descriptors while the texture slots it samples are unchanged, so streaming elsewhere in the game no longer " "rebuilds every set each frame. Applies after restart.", nullptr, true}, - {TAB_DEBUG, "Sampler cache", &opts.sampler_cache, 0, 1, 1, tx_onoff, - "What a sampler holds is kept beside it on the replay thread, so resolving a sampler that was not registered anew takes no " - "lock. Helps while the game streams textures. Applies after restart.", nullptr, true}, // (Restart and Quit stay the table's last two: kRestartRow and kQuitRow count back from its end) {TAB_GAMEPLAY, "Restart", nullptr, 0, 0, 0, nullptr, "Starts the game again. Save your progress first. Press Enter or A twice to restart. Esc or B " @@ -1077,6 +1085,8 @@ const char* ini_key(const Row& r) { if (r.value == &opts.upscale) return "upscaling"; if (r.value == &opts.dlss) return "dlss"; // (as opts.dlss and RX_VK_DLSS) if (r.value == &opts.handheld) return "handheld_profile"; // (the Profile row; host/nn_applet.c reads it at the start) + if (r.value == &opts.sampler_cache) return "sampler_cache"; // (gpu/vk_draw.cpp reads it before the menu loads) + if (r.value == &opts.native_scene) return "native_3d_resolution"; // (host/hos_main.c reads it at the start) static char k[32]; size_t n = 0; for (const char* p = r.name; *p && n + 1 < sizeof k; p++) k[n++] = *p == ' ' ? '_' : (char)tolower(*p); diff --git a/gpu/vk/vk_menu.h b/gpu/vk/vk_menu.h index 64bcaa25e..a1ea2b4df 100644 --- a/gpu/vk/vk_menu.h +++ b/gpu/vk/vk_menu.h @@ -15,6 +15,7 @@ struct Options { int fps_cap = 0; // (hookshot) Frame rate cap: 0 60 (the console's), 90, 120, 144, 165, 240 (host/nn_vi.c reads frame_rate_cap= at the start) int resolution = 0; // the 3D's (Render resolution): 0 Auto (the game's dynamic resolution), else a share of the output, // 1..6 = 100%, 75%, 67%, 50%, 58%, 33% (upscaler_rows.h; nx2/oot_patches.py) + int native_scene = 1; // the game's 0.8 scene base scale changed to 1.0 (host/hos_main.c, nx2/oot_native_resolution.py) int output_res = 0; // the game's whole frame, docked: 0 its 2048x1152, 1 2560x1440, 2 3840x2160, 3 1920x1080, 4 5120x1440, 5 5120x2880, // 6 the player's own (vk_menu.cpp custom_output); saved as the size (output_res=3840x2160) and read once, at the start // (host/nn_applet.c) diff --git a/hookshot_env.txt b/hookshot_env.txt new file mode 100644 index 000000000..2405d6acb --- /dev/null +++ b/hookshot_env.txt @@ -0,0 +1,2 @@ +RX_VK_NOOPT=1 +RX_VK_CHECKPOINTS=1 diff --git a/host/hos_main.c b/host/hos_main.c index 197193c31..438b23217 100644 --- a/host/hos_main.c +++ b/host/hos_main.c @@ -1210,7 +1210,7 @@ volatile uint64_t hos_wide_out, hos_wide_scene; HOS_SCENE_NATIVE=0/1 over it */ volatile int hos_native_scene; volatile int hos_fix_res; /* (nx2/oot_native_resolution.py's cached-scale refresh: HOS_FIX_RES=1; #849's fixed-scale patch does it by default) */ -volatile float hos_wide_ratio, hos_wide_squeeze, hos_wide_margin; +volatile float hos_wide_ratio, hos_wide_squeeze, hos_wide_vertical_squeeze, hos_wide_margin; volatile uint32_t hos_wide_keep_mask = 0xf; /* (nx2/oot_wide.py KEEP_VTABLES: the screens that keep the whole picture, a bit each; MkwVkHost.wide_menus) */ /* (nx2/oot_occlusion.py) read by the game's code where it decides to make occlusion queries: 1, none (HOOKSHOT_OCCLUSION_OFF=1) */ volatile int hos_occlusion_off; @@ -1471,11 +1471,13 @@ static int game_main(int argc, char **argv) { const int sw = GetSystemMetrics(SM_CXSCREEN), sh = GetSystemMetrics(SM_CYSCREEN); if (on && sh > 0) want = (float)sw / (float)sh; } - if (getenv("HOS_SCENE_NATIVE")) hos_native_scene = atoi(getenv("HOS_SCENE_NATIVE")) != 0; + int hos_ini_option(const char *key, int otherwise); /* (host/nn_applet.c: the Graphics menu's saved choice) */ + hos_native_scene = getenv("HOS_SCENE_NATIVE") ? atoi(getenv("HOS_SCENE_NATIVE")) != 0 + : hos_ini_option("native_3d_resolution", 1) != 0; if (getenv("HOS_FIX_RES")) hos_fix_res = atoi(getenv("HOS_FIX_RES")) != 0; if (hos_native_scene) hos_log("boot: HOS_SCENE_NATIVE=1: the scene's base scale 1.0 (the game's 0.8 otherwise)"); extern volatile uint64_t hos_wide_out, hos_wide_scene; - extern volatile float hos_wide_ratio, hos_wide_squeeze, hos_wide_margin; + extern volatile float hos_wide_ratio, hos_wide_squeeze, hos_wide_vertical_squeeze, hos_wide_margin; const double game = 16.0 / 9.0; /* (a screen narrower than 16:9 too, 16:10 to 5:4: the picture keeps the game's height and is narrower, the cameras take its ratio, and the HUD fills its width instead of a middle 16:9 wider than the screen. A 4:3 CRT @@ -1496,7 +1498,9 @@ static int game_main(int argc, char **argv) { hos_wide_out = (uint64_t)oh << 32 | ow; hos_wide_scene = (uint64_t)sh << 32 | sw; hos_wide_ratio = (float)ow / (float)oh; - hos_wide_squeeze = (float)fmin(1.0, game / ((double)ow / oh)); /* (a narrower screen: the whole width) */ + const double ratio = (double)ow / oh; + hos_wide_squeeze = (float)fmin(1.0, game / ratio); /* horizontal scale for a wider-than-16:9 HUD */ + hos_wide_vertical_squeeze = (float)fmin(1.0, ratio / game); /* vertical scale for a narrower-than-16:9 HUD */ hos_wide_margin = (1.0f - hos_wide_squeeze) / 2.0f; hos_aspect_ratio = hos_wide_ratio; hos_log("boot: widescreen: picture %ux%u, scene %ux%u, camera ratio %.4f, HUD and movies in the middle %.1f%%", ow, oh, sw, sh, diff --git a/loader.bat b/loader.bat new file mode 100644 index 000000000..8ed93ae88 --- /dev/null +++ b/loader.bat @@ -0,0 +1,4 @@ +@echo off +set RX_VK_GPU_UNSWIZZLE=1 +set RX_VK_BATCH=1 +hookshot.exe diff --git a/nx2/oot_wide.py b/nx2/oot_wide.py index 8faad91e2..c3ffa9310 100644 --- a/nx2/oot_wide.py +++ b/nx2/oot_wide.py @@ -40,12 +40,18 @@ def _size(name): % (name, name, name)) -HUD = ("{ extern volatile float hos_wide_squeeze; const float _q = hos_wide_squeeze; if (_q > 0.0f) { float _m00, _m11; " +HUD = ("{ extern volatile float hos_wide_squeeze, hos_wide_vertical_squeeze; " + "const float _qx = hos_wide_squeeze, _qy = hos_wide_vertical_squeeze; " + "if (_qx > 0.0f || _qy > 0.0f) { float _m00, _m11; " "memcpy(&_m00, c->vreg[1], 4); memcpy(&_m11, (const uint8_t *)c->vreg[4] + 4, 4); " "if (_m00 == %s && _m11 == %s) { const uint64_t _vt = recomp_load64(c, c->x[0] - 0x1b8); " "extern volatile uint32_t hos_wide_keep_mask; const uint32_t _kb = %s; " - "if (!(_kb & hos_wide_keep_mask)) { float _row[4]; memcpy(_row, c->vreg[1], 16); for (int _k = 0; _k < 4; _k++) _row[_k] *= _q; " - "memcpy(c->vreg[1], _row, 16); } } } } /* (nx2/oot_wide.py: HUD in the middle 16:9) */" + "if (!(_kb & hos_wide_keep_mask)) { float _row[4]; " + "if (_qx > 0.0f && _qx < 1.0f) { memcpy(_row, c->vreg[1], 16); " + "for (int _k = 0; _k < 4; _k++) _row[_k] *= _qx; memcpy(c->vreg[1], _row, 16); } " + "else if (_qy > 0.0f && _qy < 1.0f) { memcpy(_row, (const uint8_t *)c->vreg[4] + 4, 16); " + "for (int _k = 0; _k < 4; _k++) _row[_k] *= _qy; memcpy((uint8_t *)c->vreg[4] + 4, _row, 16); } " + "} } } } /* (nx2/oot_wide.py: HUD keeps 16:9 on wide and narrow screens) */" % (UI_M00, UI_M11, " : ".join("_vt == 0x%xULL ? %du" % (v, 1 << k) for k, v in enumerate(KEEP_VTABLES)) + " : 0u")) MOVIE = ("{ extern volatile float hos_wide_margin; const float _m = hos_wide_margin; if (_m > 0.0f) { " diff --git a/tests/test_env_file.py b/tests/test_env_file.py index 9d6b45c57..e7adcb2b0 100644 --- a/tests/test_env_file.py +++ b/tests/test_env_file.py @@ -40,7 +40,7 @@ def main(): "HOS_T_BESIDE=1\nHOS_T_SHARED=beside\nVK_ADD_LAYER_PATH=/x\nLD_PRELOAD=/x.so\nPATH=C:\\x\nSDL_DYNAMIC_API=/x.so\n" "HOS_ENV_FILE=other.txt\nHOS_SAMPLE_FILE=/home/x/.bashrc\nRX_VK_DUMP_DIR=C:\\Users\\x\nRX_VK_SKIP_CODE=2c56,ab\n" "RX_VK_DLSS_DIR=/opt/ngx\nHOS_T_HOME=~\nSDL_VULKAN_LIBRARY=x.dll\nSDL_VIDEO_EGL_DRIVER=x.so\n" - "SDL_AUDIODRIVER=pulseaudio\nHOS_T_UP=..\nHOS_SAMPLE_FILE=hookshot_vk.dll\nRX_VK_FRAME_LOG=x.txt\n" + "SDL_AUDIO_DRIVER=pulseaudio\nSDL_AUDIODRIVER=pulseaudio\nHOS_T_UP=..\nHOS_SAMPLE_FILE=hookshot_vk.dll\nRX_VK_FRAME_LOG=x.txt\n" "RX_VK_DUMP_DIR=dumps\nHOS_PRESENT_TRACE=t.txt\nRX_VK_DLSS_DIR=\\\\server\\share\n" "HOS_AUDIO_DUMP=image.bin\nRX_VK_HANG_DUMP=hookshot_options.ini\nRX_VK_SHOTS=user\n" "HOS_INPUT_SCRIPT=1:A,2:A\nRX_VK_MENU_SCRIPT=1:INS\n", encoding="utf-8") @@ -83,7 +83,8 @@ def main(): CHECK(is("HOS_INPUT_SCRIPT", NULL) && is("RX_VK_MENU_SCRIPT", NULL)); CHECK(is("RX_VK_SKIP_CODE", "2c56,ab") && is("RX_VK_DLSS_DIR", "/opt/ngx")); CHECK(is("SDL_VULKAN_LIBRARY", NULL) && is("SDL_VIDEO_EGL_DRIVER", NULL) && is("HOS_T_UP", NULL)); - CHECK(is("SDL_AUDIODRIVER", "pulseaudio") && is("HOS_SAMPLE_FILE", NULL) && is("RX_VK_FRAME_LOG", NULL)); + CHECK(is("SDL_AUDIO_DRIVER", "pulseaudio") && is("SDL_AUDIODRIVER", "pulseaudio")); + CHECK(is("HOS_SAMPLE_FILE", NULL) && is("RX_VK_FRAME_LOG", NULL)); CHECK(is("RX_VK_DUMP_DIR", NULL) && is("HOS_PRESENT_TRACE", NULL)); CHECK(getenv("LD_PRELOAD") == NULL || strcmp(getenv("LD_PRELOAD"), "/x.so")); CHECK(getenv("PATH") == NULL || strcmp(getenv("PATH"), "C:\\x")); @@ -119,3 +120,4 @@ def main(): if __name__ == "__main__": main() +