diff --git a/docs/dlss.md b/docs/dlss.md new file mode 100644 index 00000000..3059f1c3 --- /dev/null +++ b/docs/dlss.md @@ -0,0 +1,100 @@ +# DLSS Super Resolution + +Optional, and Windows-only. With DLSS off, or without the bridge DLL, or on a GPU that is not NVIDIA's, the Vulkan +renderer records the game's own TAAU network exactly as before. + +The game draws its 3D smaller than the output and resolves it with one temporal network per ratio of those sizes. +DLSS takes that dispatch (`vk_draw.cpp`, `taau_dlss`). The networks Hookshot already knows, at the 2048x1152 and +2560x1440 outputs, are: + +| Ratio of the output | Program key | +|---|---| +| 12/12 (DLAA, and the Resolution row's 100%) | `9dc98871eb7fc101` | +| 9/12 (the Resolution row's 75%) | `fd029a317a9a4b94` | +| 8/12 (DLSS Quality) | `6676619641320ba7` | +| 7/12 (Balanced) | `f10782f9e58b6a7c` | +| 6/12 (Performance, and the Resolution row's 50%) | `72ba090c70fe2614` | +| 4/12 (Ultra Performance) | `0775215f8da720e6` | + +Any other network of the same shape is taken too: cooperative-matrix code, one colour texture in, one B10G11R11 image +out, the input no larger than the output and the same aspect. That is how the 3840x2160 output is covered. The keys +are logged the first time each one is taken (`dlss: TAAU network ... taken`). + +The network has no motion vectors, depth or jitter of its own. It is given the scene colour only, stored as the square +root of the linear colour (the same thing `shaders/taau_upscale.comp` reads). `shaders/dlss_prep.comp` makes that +linear, builds motion by reprojecting the scene's depth with the camera found in the network's constants, and writes +depth. NGX evaluates DLSS into an RGBA16F image, and `shaders/dlss_out.comp` stores that into the network's B10G11R11 +output. The rest of the frame is unchanged. + +NGX's quality mode is the DLSS quality row's, not a guess from one ratio: + +| DLSS quality | 3D size | NGX mode | +|---|---|---| +| Quality | 8/12 | 2 (MaxQuality) | +| Balanced | 7/12 | 1 (Balanced) | +| Performance | 6/12 | 0 (MaxPerf) | +| Ultra Performance | 4/12 | 3 (UltraPerformance) | +| DLAA | 12/12 | 5 (DLAA) | +| Auto | the Resolution row | from the ratio of the sizes (0.99 and up is DLAA, then 0.62, 0.54, 0.42) | + +Ultra Performance at 2560x1440 draws 854x480, and mode 3 allows only 853x480. Create snaps into the mode's range and +evaluate reads that many pixels. It does not switch to another mode. A create that still fails is logged and, after +three failures, DLSS stays off until the next start. + +Motion is the camera's only. A character that moves on its own gets the motion of what is behind it, so a faint trail +is possible. The projection has no jitter, so the anti-aliasing is weaker than DLSS in a PC game that jitters. The +default motion row flips NDC Y, which is the one that is clean in this game. `RX_DLSS_DEPTH_INVERTED=1` treats the +depth as reversed-Z. + +The DLSS row's model is an NGX render-preset hint: CNN is preset E, Transformer is K, Transformer 2 is M, and NVIDIA +default is 0 except DLAA, which asks for K. If NGX refuses the preset, the bridge tries 0. Preset E is deprecated in +DLSS 310.x and may take that path. + +## What a player needs + +Both files sit beside `hookshot_vk.dll` (the game's `bin` folder): + +- `hookshot_ngx.dll`, built from `gpu/vk/ngx` as below +- `nvngx_dlss.dll`, from NVIDIA's DLSS SDK, `lib/Windows_x86_64/rel/nvngx_dlss.dll` + +NGX is told that folder. An NVIDIA driver new enough for the DLL is required. An RTX GPU is required (the bridge +checks vendor `0x10DE`). Linux builds the renderer without calling NGX. The menu row is there, and turning it on +leaves the game's upscaler in place. + +`RX_DLSS=0..4` and `RX_DLSS_MOTION=0..2` in `hookshot_env.txt` set the rows at start. `RX_DLSS_OFF=1` does not load +the bridge, and keeps DLSS off for that run even if the menu asks. + +On an 8 GB card the game is already close to the driver's video-memory allowance in a busy town. DLAA draws the 3D at +the full output and DLSS adds its own images, so the allowance can be passed and the frame rate falls. Quality or +Balanced is the better choice there. The same pressure is what +[#993](https://github.com/NotRexed/Hookshot/issues/993) and +[#995](https://github.com/NotRexed/Hookshot/issues/995) collect, and what +[#961](https://github.com/NotRexed/Hookshot/pull/961) and +[#963](https://github.com/NotRexed/Hookshot/pull/963) reduce on the renderer's side. DLSS does not raise those +budgets. While it is running, the log records a VRAM line every 300 presents, including `OVER` when the driver's +allowance is passed. + +## Building the bridge + +NVIDIA's [DLSS SDK](https://github.com/NVIDIA/DLSS) is under NVIDIA's own license, which forbids placing the SDK under +an open-source license. None of it is in this repository: not `nvngx_dlss.dll`, not the headers, not `nvsdk_ngx_s.lib`. +Do not add them. Check the SDK out somewhere else. + +The bridge is a small MSVC DLL because the SDK ships only as MSVC static libraries and the renderer is built with +llvm-mingw. On Linux, with [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) and an [xwin](https://github.com/Jake-Shadle/xwin) +splat of the Windows SDK and the UCRT: + +```sh +MINGW= XWIN= DLSS_SDK= VK_HEADERS= \ + gpu/vk/ngx/build_ngx.sh +``` + +The script writes `hookshot_ngx.dll` under `gpu/vk/ngx/out` (gitignored) and does not copy `nvngx_dlss.dll`. Copy both +into the game's `bin` folder yourself. The game build (`nx2/build.py`) does not run this script and does not need the +SDK. + +Diagnostic switches, off unless set: `RX_DLSS_NGX_LOG=1`, `RX_DLSS_LOG_CANDIDATES=1`, `RX_DLSS_LOG_CB=`, +`RX_DLSS_JITTER=,`, `RX_DLSS_NO_SQRT=1`, `RX_DLSS_MV=`, `RX_DLSS_COLOR=`, `RX_DLSS_DEPTH=`, +`RX_DLSS_KEYS=`. + +DLSS Frame Generation is not included. Hookshot's own frame generation still runs on top of the picture DLSS wrote. diff --git a/gpu/vk/VULKAN.md b/gpu/vk/VULKAN.md index a730c320..664628f1 100644 --- a/gpu/vk/VULKAN.md +++ b/gpu/vk/VULKAN.md @@ -138,6 +138,9 @@ Designed after totk-pc-recomp's runtime/vk_*.c (MIT) and Eden's renderer_vulkan, buffer are the task / mesh stages (marked in their headers as Eden's GraphicsEnvironment does). Unverified. - **Frame interpolation** (`vk_framegen.cpp`, `RX_VK_FRAMEGEN=1`): the WebGPU renderer's, ported to GLSL (`shaders/`, compiled with glslang): made frames between the game's, shown evenly by a display thread. +- **DLSS** (`vk_dlss.cpp`, off unless the Insert menu asks): on Windows, NVIDIA DLSS Super Resolution or DLAA in place + of the game's TAAU network, through `hookshot_ngx.dll` (`gpu/vk/ngx`, built against NVIDIA's SDK, which is not in + this tree). No DLL, or a GPU that is not NVIDIA's: the network runs as before. [docs/dlss.md](../../docs/dlss.md). - **Headless**: without a window presents render offscreen; `RX_VK_SHOTS=` writes every `RX_VK_SHOT_EVERY`-th as a PNG. `work/run-vk.ps1` runs it (refuses while Siege runs, stops past `-MaxGB` of working set); `work/vksum.py ` summarises the log. diff --git a/gpu/vk/ngx/build_ngx.sh b/gpu/vk/ngx/build_ngx.sh new file mode 100755 index 00000000..cd2441d7 --- /dev/null +++ b/gpu/vk/ngx/build_ngx.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# hookshot_ngx.dll: the NGX bridge (hookshot_ngx.cpp) for the MSVC target. The Vulkan renderer is built with +# llvm-mingw and does not link NVIDIA's SDK. This DLL does, and the game loads it only when it is beside hookshot_vk.dll. +# +# NVIDIA's DLSS SDK is not part of Hookshot. Its license does not allow putting the headers, the static library or +# nvngx_dlss.dll in this GPL repository. Check the SDK out somewhere else and point this script at it. Do not copy it +# into the tree. +# +# https://github.com/NVIDIA/DLSS +# +# On Linux, llvm-mingw's clang and lld-link with the MSVC CRT and Windows SDK that xwin splats +# (https://github.com/Jake-Shadle/xwin): +# MINGW= XWIN= DLSS_SDK= VK_HEADERS= \ +# OUT= ./build_ngx.sh +# OUT defaults to ./out next to this script (gitignored: *.dll). The game also needs nvngx_dlss.dll from the same SDK, +# copied by you into the game's bin folder. This script does not copy it. +set -e +HERE=$(cd "$(dirname "$0")" && pwd) +: "${OUT:=$HERE/out}" +missing= +for v in MINGW XWIN DLSS_SDK VK_HEADERS; do + eval "val=\${$v-}" + if [ -z "$val" ]; then + echo "build_ngx.sh: set $v (see the comment at the top)" >&2 + missing=1 + fi +done +[ -z "$missing" ] || exit 1 +if [ ! -f "$DLSS_SDK/include/nvsdk_ngx_vk.h" ] || [ ! -f "$DLSS_SDK/lib/Windows_x86_64/x64/nvsdk_ngx_s.lib" ]; then + echo "build_ngx.sh: $DLSS_SDK does not look like NVIDIA's DLSS SDK (include/nvsdk_ngx_vk.h and lib/Windows_x86_64/x64/nvsdk_ngx_s.lib)" >&2 + exit 1 +fi +case "$DLSS_SDK" in + "$HERE"/*|"$HERE") echo "build_ngx.sh: keep the SDK outside this repository" >&2; exit 1 ;; +esac +mkdir -p "$OUT" +"$MINGW/bin/clang" --target=x86_64-pc-windows-msvc -fms-compatibility -fms-extensions -std=c++17 -O2 -DNDEBUG -DHSNGX_BUILDING \ + -D_MT -Xclang --dependent-lib=libcmt -Xclang --dependent-lib=libcpmt -fno-exceptions -Wno-everything \ + -isystem "$XWIN/crt/include" -isystem "$XWIN/sdk/include/ucrt" -isystem "$XWIN/sdk/include/um" -isystem "$XWIN/sdk/include/shared" \ + -I "$VK_HEADERS/include" -I "$DLSS_SDK/include" -I "$HERE" -c "$HERE/hookshot_ngx.cpp" -o "$OUT/hookshot_ngx.obj" +"$MINGW/bin/lld-link" /dll /out:"$OUT/hookshot_ngx.dll" /machine:x64 /opt:ref /opt:icf \ + /libpath:"$XWIN/crt/lib/x86_64" /libpath:"$XWIN/sdk/lib/um/x86_64" /libpath:"$XWIN/sdk/lib/ucrt/x86_64" \ + "$OUT/hookshot_ngx.obj" "$DLSS_SDK/lib/Windows_x86_64/x64/nvsdk_ngx_s.lib" libcmt.lib libcpmt.lib libucrt.lib libvcruntime.lib \ + kernel32.lib user32.lib advapi32.lib version.lib shell32.lib ole32.lib uuid.lib setupapi.lib cfgmgr32.lib +echo "built $OUT/hookshot_ngx.dll" +echo "copy nvngx_dlss.dll from the SDK (lib/Windows_x86_64/) into the game's bin folder yourself; it is not built here" diff --git a/gpu/vk/ngx/hookshot_ngx.cpp b/gpu/vk/ngx/hookshot_ngx.cpp new file mode 100644 index 00000000..0a35527d --- /dev/null +++ b/gpu/vk/ngx/hookshot_ngx.cpp @@ -0,0 +1,244 @@ +// (hookshot DLSS) hookshot_ngx.dll: NVIDIA's NGX SDK for Vulkan behind a small C interface (ngx/hookshot_ngx.h). +// +// The NGX SDK ships only as Microsoft-ABI static libraries (nvsdk_ngx_s.lib, the static MSVC runtime), and the +// renderer is built with llvm-mingw, so the SDK lives in a DLL of its own, built for the MSVC target +// (gpu/vk/ngx/build_ngx.sh), which hookshot_vk.dll loads at run time (vk_dlss.cpp). With the DLL missing, an +// older driver or a GPU without DLSS, the renderer keeps the game's own upscaler. Nothing of the SDK crosses the +// interface but plain C types and Vulkan handles. +#define NOMINMAX +#include +#include +#include +#include +#include +#include +#include +#include "nvsdk_ngx_vk.h" +#include "nvsdk_ngx_helpers.h" +#include "nvsdk_ngx_helpers_vk.h" +#include "hookshot_ngx.h" + +namespace { +std::mutex mu; +NVSDK_NGX_Parameter* params = nullptr; +bool ready = false; +VkDevice ngx_device = VK_NULL_HANDLE; +HsNgxLog app_log = nullptr; +// (build 7) each feature's create size: Ultra Performance at 2560x1440 allows only 853x480, while the game +// draws 854x480 (4/12). Create snaps to the mode's allowed range; evaluate reads that many pixels as a subrect. +struct FeatureSize { uint32_t w, h; }; +std::unordered_map feature_sizes; + +void say(const char* fmt, ...) { + if (!app_log) return; + char b[512]; + va_list a; + va_start(a, fmt); + vsnprintf(b, sizeof b, fmt, a); + va_end(a); + app_log(b); +} +void NVSDK_CONV ngx_log(const char* message, NVSDK_NGX_Logging_Level, NVSDK_NGX_Feature) { + if (!app_log || !message) return; + char b[480]; + snprintf(b, sizeof b, "ngx: %s", message); + size_t n = strlen(b); + while (n && (b[n - 1] == '\n' || b[n - 1] == '\r')) b[--n] = 0; + app_log(b); +} +/// NGX's own text for a result ("NVSDK_NGX_Result_FAIL_UnsupportedParameter" and the like), ASCII +std::string result_text(NVSDK_NGX_Result r) { + std::string out; + if (const wchar_t* w = GetNGXResultAsString(r)) + for (; *w && out.size() < 200; w++) out += *w < 0x80 ? (char)*w : '?'; + return out.empty() ? "(no text)" : "(" + out + ")"; +} +NVSDK_NGX_Resource_VK resource(const HsNgxImage& im, bool rw) { + VkImageSubresourceRange r{(VkImageAspectFlags)im.aspect, 0, 1, 0, 1}; + return NVSDK_NGX_Create_ImageView_Resource_VK((VkImageView)im.view, (VkImage)im.image, r, (VkFormat)im.format, im.width, + im.height, rw); +} +} // namespace + +extern "C" { + +HSNGX_API int hsngx_interface_version(void) { return HSNGX_INTERFACE_VERSION; } + +HSNGX_API int hsngx_required_extensions(uint32_t* instance_count, const char*** instance_exts, uint32_t* device_count, + const char*** device_exts) { + unsigned int ni = 0, nd = 0; + const char** ie = nullptr; + const char** de = nullptr; + const NVSDK_NGX_Result r = NVSDK_NGX_VULKAN_RequiredExtensions(&ni, &ie, &nd, &de); + if (NVSDK_NGX_FAILED(r)) return (int)r; + *instance_count = ni, *instance_exts = ie, *device_count = nd, *device_exts = de; + return 0; +} + +HSNGX_API int hsngx_init(void* instance, void* physical, void* device, void* gipa, void* gdpa, const wchar_t* app_dir, + HsNgxLog log, int verbose) { + std::lock_guard lk(mu); + app_log = log; + if (ready) return 1; + NVSDK_NGX_FeatureCommonInfo info{}; + const wchar_t* paths[1] = {app_dir}; + info.PathListInfo.Path = paths; + info.PathListInfo.Length = app_dir ? 1 : 0; + info.LoggingInfo.LoggingCallback = ngx_log; + info.LoggingInfo.MinimumLoggingLevel = verbose ? NVSDK_NGX_LOGGING_LEVEL_VERBOSE : NVSDK_NGX_LOGGING_LEVEL_ON; + info.LoggingInfo.DisableOtherLoggingSinks = !verbose; + NVSDK_NGX_Result r = NVSDK_NGX_VULKAN_Init_with_ProjectID( + "5d1c2f0a-8e3b-4c6d-9a7f-0b0c5d070001", NVSDK_NGX_ENGINE_TYPE_CUSTOM, "2.0", app_dir, (VkInstance)instance, + (VkPhysicalDevice)physical, (VkDevice)device, (PFN_vkGetInstanceProcAddr)gipa, (PFN_vkGetDeviceProcAddr)gdpa, &info); + if (NVSDK_NGX_FAILED(r)) { + say("dlss: NGX init failed: %#x %s", (unsigned)r, result_text(r).c_str()); + return -1; + } + ngx_device = (VkDevice)device; + r = NVSDK_NGX_VULKAN_GetCapabilityParameters(¶ms); + if (NVSDK_NGX_FAILED(r) || !params) { + say("dlss: NGX capability parameters failed: %#x", (unsigned)r); + NVSDK_NGX_VULKAN_Shutdown1((VkDevice)device); + return -2; + } + int needs_driver = 0, available = 0; + unsigned int drv_major = 0, drv_minor = 0; + NVSDK_NGX_Parameter_GetI(params, NVSDK_NGX_Parameter_SuperSampling_NeedsUpdatedDriver, &needs_driver); + NVSDK_NGX_Parameter_GetUI(params, NVSDK_NGX_Parameter_SuperSampling_MinDriverVersionMajor, &drv_major); + NVSDK_NGX_Parameter_GetUI(params, NVSDK_NGX_Parameter_SuperSampling_MinDriverVersionMinor, &drv_minor); + r = NVSDK_NGX_Parameter_GetI(params, NVSDK_NGX_Parameter_SuperSampling_Available, &available); + if (needs_driver) say("dlss: the NVIDIA driver is too old for DLSS: %u.%u or newer is needed", drv_major, drv_minor); + if (NVSDK_NGX_FAILED(r) || !available) { + int init_result = 0; + NVSDK_NGX_Parameter_GetI(params, NVSDK_NGX_Parameter_SuperSampling_FeatureInitResult, &init_result); + say("dlss: DLSS Super Resolution is not available on this GPU/driver (feature init result %#x)", (unsigned)init_result); + NVSDK_NGX_VULKAN_DestroyParameters(params); + params = nullptr; + NVSDK_NGX_VULKAN_Shutdown1((VkDevice)device); + return -3; + } + say("dlss: NGX ready: DLSS Super Resolution available (driver needs update: %s)", needs_driver ? "yes" : "no"); + ready = true; + return 1; +} + +HSNGX_API void* hsngx_create(void* cmd, uint32_t render_w, uint32_t render_h, uint32_t out_w, uint32_t out_h, int quality, + int preset, int flags) { + std::lock_guard lk(mu); + if (!ready || !params) return nullptr; + // (build 7) Stick to the quality mode the caller asked for (DLAA 5, Quality 2, Balanced 1, Performance 0, + // Ultra Performance 3). Never fall back to another mode: an 854x480 Ultra Performance input is one pixel past + // mode 3's 853x480..853x480 range, and the old fallback picked Quality (mode 2), which then failed three times + // and turned DLSS off. Instead, snap the create size into that mode's allowed range and evaluate a subrect. + if (quality == 4) { + say("dlss: NGX mode 4 (UltraQuality) is not supported by DLSS SR"); + return nullptr; + } + unsigned int opt_w = 0, opt_h = 0, max_w = 0, max_h = 0, min_w = 0, min_h = 0; + float sharp = 0.0f; + const NVSDK_NGX_Result opt = NGX_DLSS_GET_OPTIMAL_SETTINGS(params, out_w, out_h, (NVSDK_NGX_PerfQuality_Value)quality, + &opt_w, &opt_h, &max_w, &max_h, &min_w, &min_h, &sharp); + if (NVSDK_NGX_FAILED(opt) || !opt_w || !max_w || !max_h) { + say("dlss: NGX mode %d for %ux%u output: %s (no usable range): not creating", quality, out_w, out_h, + NVSDK_NGX_SUCCEED(opt) ? "ok but empty" : result_text(opt).c_str()); + return nullptr; + } + if (!min_w) min_w = opt_w; + if (!min_h) min_h = opt_h; + say("dlss: NGX mode %d for %ux%u output: ok, render %ux%u optimal, %ux%u..%ux%u allowed", quality, out_w, out_h, opt_w, + opt_h, min_w, min_h, max_w, max_h); + uint32_t use_w = render_w, use_h = render_h; + if (use_w < min_w) use_w = min_w; + if (use_w > max_w) use_w = max_w; + if (use_h < min_h) use_h = min_h; + if (use_h > max_h) use_h = max_h; + if (use_w != render_w || use_h != render_h) + say("dlss: snapped create size %ux%u -> %ux%u so NGX mode %d's range holds it (evaluate uses a %ux%u subrect)", + render_w, render_h, use_w, use_h, quality, use_w, use_h); + // (preset asked first, then NVIDIA's default when NGX refuses it: preset E, the CNN, is deprecated in 310.x) + const int presets[2] = {preset, 0}; + for (int attempt = 0; attempt < (preset ? 2 : 1); attempt++) { + const int ps = presets[attempt]; + const char* keys[] = {NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_DLAA, NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_Quality, + NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_Balanced, NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_Performance, + NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_UltraPerformance, + NVSDK_NGX_Parameter_DLSS_Hint_Render_Preset_UltraQuality}; + for (const char* k : keys) NVSDK_NGX_Parameter_SetUI(params, k, (unsigned int)ps); + NVSDK_NGX_DLSS_Create_Params cp{}; + cp.Feature.InWidth = use_w, cp.Feature.InHeight = use_h; + cp.Feature.InTargetWidth = out_w, cp.Feature.InTargetHeight = out_h; + cp.Feature.InPerfQualityValue = (NVSDK_NGX_PerfQuality_Value)quality; + cp.InFeatureCreateFlags = flags; + cp.InEnableOutputSubrects = false; + NVSDK_NGX_Handle* h = nullptr; + const NVSDK_NGX_Result r = NGX_VULKAN_CREATE_DLSS_EXT1(ngx_device, (VkCommandBuffer)cmd, 1, 1, &h, params, &cp); + if (NVSDK_NGX_SUCCEED(r) && h) { + feature_sizes[h] = {use_w, use_h}; + say("dlss: DLSS made, %ux%u -> %ux%u, NGX mode %d, preset %d, flags %#x%s", use_w, use_h, out_w, out_h, quality, ps, + flags, (use_w != render_w || use_h != render_h) ? " (snapped)" : ""); + return h; + } + say("dlss: creating DLSS %ux%u -> %ux%u (NGX mode %d, preset %d, flags %#x) failed: %#x %s", use_w, use_h, out_w, out_h, + quality, ps, flags, (unsigned)r, result_text(r).c_str()); + } + return nullptr; +} + +HSNGX_API int hsngx_evaluate(void* cmd, void* handle, const HsNgxImage* color, const HsNgxImage* depth, const HsNgxImage* motion, + const HsNgxImage* output, float jitter_x, float jitter_y, float mv_scale_x, float mv_scale_y, + int reset, float frame_ms) { + std::lock_guard lk(mu); + if (!ready || !params || !handle || !color || !depth || !motion || !output) return -1; + NVSDK_NGX_Resource_VK c = resource(*color, false), d = resource(*depth, false), m = resource(*motion, false), + o = resource(*output, true); + NVSDK_NGX_VK_DLSS_Eval_Params ep{}; + ep.Feature.pInColor = &c; + ep.Feature.pInOutput = &o; + ep.pInDepth = &d; + ep.pInMotionVectors = &m; + ep.InJitterOffsetX = jitter_x, ep.InJitterOffsetY = jitter_y; + // (build 7) use the create size when the colour image is a pixel or two past the mode's range (854 vs 853) + uint32_t sub_w = color->width, sub_h = color->height; + if (auto it = feature_sizes.find(handle); it != feature_sizes.end()) { + if (sub_w > it->second.w) sub_w = it->second.w; + if (sub_h > it->second.h) sub_h = it->second.h; + } + ep.InRenderSubrectDimensions = {sub_w, sub_h}; + ep.InReset = reset; + ep.InMVScaleX = mv_scale_x, ep.InMVScaleY = mv_scale_y; + ep.InFrameTimeDeltaInMsec = frame_ms; + const NVSDK_NGX_Result r = NGX_VULKAN_EVALUATE_DLSS_EXT((VkCommandBuffer)cmd, (NVSDK_NGX_Handle*)handle, params, &ep); + if (NVSDK_NGX_FAILED(r)) { + static int told = 0; + if (told++ < 5) say("dlss: NGX evaluate failed: %#x %s", (unsigned)r, result_text(r).c_str()); + return (int)(r & 0x7fffffff) | 0x40000000; + } + return 0; +} + +HSNGX_API void hsngx_release(void* handle) { + std::lock_guard lk(mu); + if (handle && ready) { + feature_sizes.erase(handle); + NVSDK_NGX_VULKAN_ReleaseFeature((NVSDK_NGX_Handle*)handle); + } +} + +// (build 7, optional: an older renderer never asks) the VRAM NGX's DLSS features hold, in bytes; 0: not known +HSNGX_API unsigned long long hsngx_vram_bytes(void) { + std::lock_guard lk(mu); + if (!ready || !params) return 0; + unsigned long long bytes = 0; + return NVSDK_NGX_SUCCEED(NGX_DLSS_GET_STATS(params, &bytes)) ? bytes : 0; +} + +HSNGX_API void hsngx_shutdown(void) { + std::lock_guard lk(mu); + if (!ready) return; + if (params) NVSDK_NGX_VULKAN_DestroyParameters(params); + params = nullptr; + NVSDK_NGX_VULKAN_Shutdown1(ngx_device); + ready = false; +} + +} // extern "C" diff --git a/gpu/vk/ngx/hookshot_ngx.h b/gpu/vk/ngx/hookshot_ngx.h new file mode 100644 index 00000000..0f69233b --- /dev/null +++ b/gpu/vk/ngx/hookshot_ngx.h @@ -0,0 +1,44 @@ +// (hookshot DLSS) The interface of hookshot_ngx.dll (ngx/hookshot_ngx.cpp): NVIDIA's NGX SDK for Vulkan, loaded at run +// time by vk_dlss.cpp. Plain C: the DLL is built for the MSVC target and the renderer with llvm-mingw. +#pragma once +#include +#include + +#define HSNGX_INTERFACE_VERSION 1 +#if defined(HSNGX_BUILDING) +#define HSNGX_API __declspec(dllexport) +#else +#define HSNGX_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*HsNgxLog)(const char* line); + +/// A Vulkan image as NGX is given it: the handles as integers, the format, the size, and the view's aspect +typedef struct HsNgxImage { + uint64_t image, view; + uint32_t format, width, height, aspect; +} HsNgxImage; + +typedef int (*PFN_hsngx_interface_version)(void); +typedef int (*PFN_hsngx_required_extensions)(uint32_t* instance_count, const char*** instance_exts, uint32_t* device_count, + const char*** device_exts); +/// 1: DLSS Super Resolution is available; below 0: not (the reason logged through log) +typedef int (*PFN_hsngx_init)(void* instance, void* physical, void* device, void* gipa, void* gdpa, const wchar_t* app_dir, + HsNgxLog log, int verbose); +typedef void* (*PFN_hsngx_create)(void* cmd, uint32_t render_w, uint32_t render_h, uint32_t out_w, uint32_t out_h, int quality, + int preset, int flags); +/// 0: recorded; else NGX's result code +typedef int (*PFN_hsngx_evaluate)(void* cmd, void* handle, const HsNgxImage* color, const HsNgxImage* depth, + const HsNgxImage* motion, const HsNgxImage* output, float jitter_x, float jitter_y, + float mv_scale_x, float mv_scale_y, int reset, float frame_ms); +typedef void (*PFN_hsngx_release)(void* handle); +typedef void (*PFN_hsngx_shutdown)(void); +typedef unsigned long long (*PFN_hsngx_vram_bytes)(void); // (build 7, optional) + +#ifdef __cplusplus +} +#endif diff --git a/gpu/vk/shaders/dlss_out.comp b/gpu/vk/shaders/dlss_out.comp new file mode 100644 index 00000000..0f811d70 --- /dev/null +++ b/gpu/vk/shaders/dlss_out.comp @@ -0,0 +1,15 @@ +#version 450 +// (hookshot DLSS) DLSS's output (RGBA16F, vk_dlss.cpp) into the game's TAAU output image, B10G11R11, as the network +// would have written it: linear colour, no NaN, nothing below zero. +layout(local_size_x = 8, local_size_y = 8) in; +layout(binding = 0) uniform sampler2D src; +layout(binding = 1, r11f_g11f_b10f) uniform writeonly image2D dst; +layout(push_constant) uniform P { ivec2 size; } pc; +bool bad(float v) { return isnan(v) || isinf(v); } +void main() { + const ivec2 g = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(g, pc.size))) return; + vec3 c = textureLod(src, (vec2(g) + 0.5) / vec2(pc.size), 0.0).rgb; + c = mix(c, vec3(0.0), bvec3(bad(c.r), bad(c.g), bad(c.b))); + imageStore(dst, g, vec4(max(c, vec3(0.0)), 1.0)); +} diff --git a/gpu/vk/shaders/dlss_out_comp_spv.h b/gpu/vk/shaders/dlss_out_comp_spv.h new file mode 100644 index 00000000..fd6fbcaa --- /dev/null +++ b/gpu/vk/shaders/dlss_out_comp_spv.h @@ -0,0 +1,79 @@ + // 1116.6.0 + #pragma once +const uint32_t DLSS_OUT_COMP_SPV[] = { + 0x07230203,0x00010600,0x0008000b,0x00000069,0x00000000,0x00020011,0x00000001,0x00020011, + 0x00000031,0x0006000b,0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e, + 0x00000000,0x00000001,0x0009000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x0000001e, + 0x00000026,0x00000037,0x0000005d,0x00060010,0x00000004,0x00000011,0x00000008,0x00000008, + 0x00000001,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,0x00000000, + 0x00040005,0x0000000b,0x28646162,0x003b3166,0x00030005,0x0000000a,0x00000076,0x00030005, + 0x0000001a,0x00000067,0x00080005,0x0000001e,0x475f6c67,0x61626f6c,0x766e496c,0x7461636f, + 0x496e6f69,0x00000044,0x00030005,0x00000024,0x00000050,0x00050006,0x00000024,0x00000000, + 0x657a6973,0x00000000,0x00030005,0x00000026,0x00006370,0x00030005,0x00000033,0x00000063, + 0x00030005,0x00000037,0x00637273,0x00040005,0x00000049,0x61726170,0x0000006d,0x00040005, + 0x0000004e,0x61726170,0x0000006d,0x00040005,0x00000053,0x61726170,0x0000006d,0x00030005, + 0x0000005d,0x00747364,0x00040047,0x0000001e,0x0000000b,0x0000001c,0x00030047,0x00000024, + 0x00000002,0x00050048,0x00000024,0x00000000,0x00000023,0x00000000,0x00040047,0x00000037, + 0x00000021,0x00000000,0x00040047,0x00000037,0x00000022,0x00000000,0x00030047,0x0000005d, + 0x00000019,0x00040047,0x0000005d,0x00000021,0x00000001,0x00040047,0x0000005d,0x00000022, + 0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006, + 0x00000020,0x00040020,0x00000007,0x00000007,0x00000006,0x00020014,0x00000008,0x00040021, + 0x00000009,0x00000008,0x00000007,0x00040015,0x00000017,0x00000020,0x00000001,0x00040017, + 0x00000018,0x00000017,0x00000002,0x00040020,0x00000019,0x00000007,0x00000018,0x00040015, + 0x0000001b,0x00000020,0x00000000,0x00040017,0x0000001c,0x0000001b,0x00000003,0x00040020, + 0x0000001d,0x00000001,0x0000001c,0x0004003b,0x0000001d,0x0000001e,0x00000001,0x00040017, + 0x0000001f,0x0000001b,0x00000002,0x0003001e,0x00000024,0x00000018,0x00040020,0x00000025, + 0x00000009,0x00000024,0x0004003b,0x00000025,0x00000026,0x00000009,0x0004002b,0x00000017, + 0x00000027,0x00000000,0x00040020,0x00000028,0x00000009,0x00000018,0x00040017,0x0000002b, + 0x00000008,0x00000002,0x00040017,0x00000031,0x00000006,0x00000003,0x00040020,0x00000032, + 0x00000007,0x00000031,0x00090019,0x00000034,0x00000006,0x00000001,0x00000000,0x00000000, + 0x00000000,0x00000001,0x00000000,0x0003001b,0x00000035,0x00000034,0x00040020,0x00000036, + 0x00000000,0x00000035,0x0004003b,0x00000036,0x00000037,0x00000000,0x00040017,0x0000003a, + 0x00000006,0x00000002,0x0004002b,0x00000006,0x0000003c,0x3f000000,0x0004002b,0x00000006, + 0x00000043,0x00000000,0x00040017,0x00000044,0x00000006,0x00000004,0x0006002c,0x00000031, + 0x00000048,0x00000043,0x00000043,0x00000043,0x0004002b,0x0000001b,0x0000004a,0x00000000, + 0x0004002b,0x0000001b,0x0000004f,0x00000001,0x0004002b,0x0000001b,0x00000054,0x00000002, + 0x00040017,0x00000058,0x00000008,0x00000003,0x00090019,0x0000005b,0x00000006,0x00000001, + 0x00000000,0x00000000,0x00000000,0x00000002,0x00000008,0x00040020,0x0000005c,0x00000000, + 0x0000005b,0x0004003b,0x0000005c,0x0000005d,0x00000000,0x0004002b,0x00000006,0x00000062, + 0x3f800000,0x0004002b,0x0000001b,0x00000067,0x00000008,0x0006002c,0x0000001c,0x00000068, + 0x00000067,0x00000067,0x0000004f,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003, + 0x000200f8,0x00000005,0x0004003b,0x00000019,0x0000001a,0x00000007,0x0004003b,0x00000032, + 0x00000033,0x00000007,0x0004003b,0x00000007,0x00000049,0x00000007,0x0004003b,0x00000007, + 0x0000004e,0x00000007,0x0004003b,0x00000007,0x00000053,0x00000007,0x0004003d,0x0000001c, + 0x00000020,0x0000001e,0x0007004f,0x0000001f,0x00000021,0x00000020,0x00000020,0x00000000, + 0x00000001,0x0004007c,0x00000018,0x00000022,0x00000021,0x0003003e,0x0000001a,0x00000022, + 0x0004003d,0x00000018,0x00000023,0x0000001a,0x00050041,0x00000028,0x00000029,0x00000026, + 0x00000027,0x0004003d,0x00000018,0x0000002a,0x00000029,0x000500af,0x0000002b,0x0000002c, + 0x00000023,0x0000002a,0x0004009a,0x00000008,0x0000002d,0x0000002c,0x000300f7,0x0000002f, + 0x00000000,0x000400fa,0x0000002d,0x0000002e,0x0000002f,0x000200f8,0x0000002e,0x000100fd, + 0x000200f8,0x0000002f,0x0004003d,0x00000035,0x00000038,0x00000037,0x0004003d,0x00000018, + 0x00000039,0x0000001a,0x0004006f,0x0000003a,0x0000003b,0x00000039,0x00050050,0x0000003a, + 0x0000003d,0x0000003c,0x0000003c,0x00050081,0x0000003a,0x0000003e,0x0000003b,0x0000003d, + 0x00050041,0x00000028,0x0000003f,0x00000026,0x00000027,0x0004003d,0x00000018,0x00000040, + 0x0000003f,0x0004006f,0x0000003a,0x00000041,0x00000040,0x00050088,0x0000003a,0x00000042, + 0x0000003e,0x00000041,0x00070058,0x00000044,0x00000045,0x00000038,0x00000042,0x00000002, + 0x00000043,0x0008004f,0x00000031,0x00000046,0x00000045,0x00000045,0x00000000,0x00000001, + 0x00000002,0x0003003e,0x00000033,0x00000046,0x0004003d,0x00000031,0x00000047,0x00000033, + 0x00050041,0x00000007,0x0000004b,0x00000033,0x0000004a,0x0004003d,0x00000006,0x0000004c, + 0x0000004b,0x0003003e,0x00000049,0x0000004c,0x00050039,0x00000008,0x0000004d,0x0000000b, + 0x00000049,0x00050041,0x00000007,0x00000050,0x00000033,0x0000004f,0x0004003d,0x00000006, + 0x00000051,0x00000050,0x0003003e,0x0000004e,0x00000051,0x00050039,0x00000008,0x00000052, + 0x0000000b,0x0000004e,0x00050041,0x00000007,0x00000055,0x00000033,0x00000054,0x0004003d, + 0x00000006,0x00000056,0x00000055,0x0003003e,0x00000053,0x00000056,0x00050039,0x00000008, + 0x00000057,0x0000000b,0x00000053,0x00060050,0x00000058,0x00000059,0x0000004d,0x00000052, + 0x00000057,0x000600a9,0x00000031,0x0000005a,0x00000059,0x00000048,0x00000047,0x0003003e, + 0x00000033,0x0000005a,0x0004003d,0x0000005b,0x0000005e,0x0000005d,0x0004003d,0x00000018, + 0x0000005f,0x0000001a,0x0004003d,0x00000031,0x00000060,0x00000033,0x0007000c,0x00000031, + 0x00000061,0x00000001,0x00000028,0x00000060,0x00000048,0x00050051,0x00000006,0x00000063, + 0x00000061,0x00000000,0x00050051,0x00000006,0x00000064,0x00000061,0x00000001,0x00050051, + 0x00000006,0x00000065,0x00000061,0x00000002,0x00070050,0x00000044,0x00000066,0x00000063, + 0x00000064,0x00000065,0x00000062,0x00040063,0x0000005e,0x0000005f,0x00000066,0x000100fd, + 0x00010038,0x00050036,0x00000008,0x0000000b,0x00000000,0x00000009,0x00030037,0x00000007, + 0x0000000a,0x000200f8,0x0000000c,0x0004003d,0x00000006,0x0000000d,0x0000000a,0x0004009c, + 0x00000008,0x0000000e,0x0000000d,0x000400a8,0x00000008,0x0000000f,0x0000000e,0x000300f7, + 0x00000011,0x00000000,0x000400fa,0x0000000f,0x00000010,0x00000011,0x000200f8,0x00000010, + 0x0004003d,0x00000006,0x00000012,0x0000000a,0x0004009d,0x00000008,0x00000013,0x00000012, + 0x000200f9,0x00000011,0x000200f8,0x00000011,0x000700f5,0x00000008,0x00000014,0x0000000e, + 0x0000000c,0x00000013,0x00000010,0x000200fe,0x00000014,0x00010038 +}; diff --git a/gpu/vk/shaders/dlss_prep.comp b/gpu/vk/shaders/dlss_prep.comp new file mode 100644 index 00000000..ebceda29 --- /dev/null +++ b/gpu/vk/shaders/dlss_prep.comp @@ -0,0 +1,51 @@ +#version 450 +// (hookshot DLSS) The inputs DLSS Super Resolution is given (vk_dlss.cpp), from the game's TAAU network's own: its scene +// colour (the square root of the linear colour, as shaders/taau_upscale.comp reads it) made linear again, NaN and +// infinite pixels zeroed; its motion as render pixels in NVIDIA's convention (current to previous), the network's +// own scaled by pc.mv_scale; its depth. Into images of formats NGX takes everywhere: RGBA16F, RG16F and R32F. +// pc.flags: 1 colour is square-root encoded, 2 motion bound, 4 depth bound (else zero motion and far depth), 8 the +// motion is unsigned normalized (0.5: none), 16 camera motion: the depth reprojected by pc.reproj (current NDC, GL z +// -1..1, to the last frame's), 32 with NDC y = 1 - 2v (else 2v - 1), 64 the depth is reversed (1 near). +layout(local_size_x = 8, local_size_y = 8) in; +layout(binding = 0) uniform sampler2D src_color; +layout(binding = 1) uniform sampler2D src_mv; +layout(binding = 2) uniform sampler2D src_depth; +layout(binding = 3, rgba16f) uniform writeonly image2D dst_color; +layout(binding = 4, rg16f) uniform writeonly image2D dst_mv; +layout(binding = 5, r32f) uniform writeonly image2D dst_depth; +layout(push_constant) uniform P { ivec2 size; vec2 mv_scale; int flags; float far_depth; vec2 pad; mat4 reproj; } pc; +bool bad(float v) { return isnan(v) || isinf(v); } +void main() { + const ivec2 g = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(g, pc.size))) return; + const vec2 uv = (vec2(g) + 0.5) / vec2(pc.size); + vec3 c = textureLod(src_color, uv, 0.0).rgb; + if ((pc.flags & 1) != 0) c = c * c; + c = mix(c, vec3(0.0), bvec3(bad(c.r), bad(c.g), bad(c.b))); + imageStore(dst_color, g, vec4(max(c, vec3(0.0)), 1.0)); + vec2 mv = vec2(0.0); + if ((pc.flags & 2) != 0) { + mv = textureLod(src_mv, uv, 0.0).rg; + if ((pc.flags & 8) != 0) mv = mv * 2.0 - 1.0; + mv *= pc.mv_scale; + if (bad(mv.x) || bad(mv.y)) mv = vec2(0.0); + } + float d = pc.far_depth; + if ((pc.flags & 4) != 0) { + d = textureLod(src_depth, uv, 0.0).r; + if (bad(d)) d = pc.far_depth; + } + if ((pc.flags & 16) != 0) { + const bool flip = (pc.flags & 32) != 0; + const float z = (pc.flags & 64) != 0 ? 1.0 - d : d; + const vec4 p = pc.reproj * vec4(uv.x * 2.0 - 1.0, flip ? 1.0 - uv.y * 2.0 : uv.y * 2.0 - 1.0, z * 2.0 - 1.0, 1.0); + if (p.w > 1e-6) { + const vec2 n = p.xy / p.w; + const vec2 puv = vec2(n.x * 0.5 + 0.5, flip ? 0.5 - n.y * 0.5 : n.y * 0.5 + 0.5); + mv = (puv - uv) * vec2(pc.size); + if (bad(mv.x) || bad(mv.y)) mv = vec2(0.0); + } + } + imageStore(dst_mv, g, vec4(mv, 0.0, 0.0)); + imageStore(dst_depth, g, vec4(d, 0.0, 0.0, 0.0)); +} diff --git a/gpu/vk/shaders/dlss_prep_comp_spv.h b/gpu/vk/shaders/dlss_prep_comp_spv.h new file mode 100644 index 00000000..793666ba --- /dev/null +++ b/gpu/vk/shaders/dlss_prep_comp_spv.h @@ -0,0 +1,241 @@ + // glslang 15.1.0 + #pragma once +const uint32_t DLSS_PREP_COMP_SPV[] = { + 0x07230203,0x00010600,0x0008000b,0x0000013d,0x00000000,0x00020011,0x00000001,0x00020011, + 0x00000031,0x0006000b,0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e, + 0x00000000,0x00000001,0x000d000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x0000001e, + 0x00000029,0x00000045,0x0000006d,0x0000007f,0x000000af,0x0000012d,0x00000136,0x00060010, + 0x00000004,0x00000011,0x00000008,0x00000008,0x00000001,0x00030003,0x00000002,0x000001c2, + 0x00040005,0x00000004,0x6e69616d,0x00000000,0x00040005,0x0000000b,0x28646162,0x003b3166, + 0x00030005,0x0000000a,0x00000076,0x00030005,0x0000001a,0x00000067,0x00080005,0x0000001e, + 0x475f6c67,0x61626f6c,0x766e496c,0x7461636f,0x496e6f69,0x00000044,0x00030005,0x00000027, + 0x00000050,0x00050006,0x00000027,0x00000000,0x657a6973,0x00000000,0x00060006,0x00000027, + 0x00000001,0x735f766d,0x656c6163,0x00000000,0x00050006,0x00000027,0x00000002,0x67616c66, + 0x00000073,0x00060006,0x00000027,0x00000003,0x5f726166,0x74706564,0x00000068,0x00040006, + 0x00000027,0x00000004,0x00646170,0x00050006,0x00000027,0x00000005,0x72706572,0x00006a6f, + 0x00030005,0x00000029,0x00006370,0x00030005,0x00000035,0x00007675,0x00030005,0x00000041, + 0x00000063,0x00050005,0x00000045,0x5f637273,0x6f6c6f63,0x00000072,0x00040005,0x00000059, + 0x61726170,0x0000006d,0x00040005,0x0000005e,0x61726170,0x0000006d,0x00040005,0x00000063, + 0x61726170,0x0000006d,0x00050005,0x0000006d,0x5f747364,0x6f6c6f63,0x00000072,0x00030005, + 0x00000077,0x0000766d,0x00040005,0x0000007f,0x5f637273,0x0000766d,0x00040005,0x00000095, + 0x61726170,0x0000006d,0x00040005,0x0000009c,0x61726170,0x0000006d,0x00030005,0x000000a3, + 0x00000064,0x00050005,0x000000af,0x5f637273,0x74706564,0x00000068,0x00040005,0x000000b4, + 0x61726170,0x0000006d,0x00040005,0x000000c3,0x70696c66,0x00000000,0x00030005,0x000000c9, + 0x0000007a,0x00030005,0x000000d8,0x00000070,0x00030005,0x000000fb,0x0000006e,0x00030005, + 0x00000102,0x00767570,0x00040005,0x0000011d,0x61726170,0x0000006d,0x00040005,0x00000124, + 0x61726170,0x0000006d,0x00040005,0x0000012d,0x5f747364,0x0000766d,0x00050005,0x00000136, + 0x5f747364,0x74706564,0x00000068,0x00040047,0x0000001e,0x0000000b,0x0000001c,0x00030047, + 0x00000027,0x00000002,0x00050048,0x00000027,0x00000000,0x00000023,0x00000000,0x00050048, + 0x00000027,0x00000001,0x00000023,0x00000008,0x00050048,0x00000027,0x00000002,0x00000023, + 0x00000010,0x00050048,0x00000027,0x00000003,0x00000023,0x00000014,0x00050048,0x00000027, + 0x00000004,0x00000023,0x00000018,0x00040048,0x00000027,0x00000005,0x00000005,0x00050048, + 0x00000027,0x00000005,0x00000007,0x00000010,0x00050048,0x00000027,0x00000005,0x00000023, + 0x00000020,0x00040047,0x00000045,0x00000021,0x00000000,0x00040047,0x00000045,0x00000022, + 0x00000000,0x00030047,0x0000006d,0x00000019,0x00040047,0x0000006d,0x00000021,0x00000003, + 0x00040047,0x0000006d,0x00000022,0x00000000,0x00040047,0x0000007f,0x00000021,0x00000001, + 0x00040047,0x0000007f,0x00000022,0x00000000,0x00040047,0x000000af,0x00000021,0x00000002, + 0x00040047,0x000000af,0x00000022,0x00000000,0x00030047,0x0000012d,0x00000019,0x00040047, + 0x0000012d,0x00000021,0x00000004,0x00040047,0x0000012d,0x00000022,0x00000000,0x00030047, + 0x00000136,0x00000019,0x00040047,0x00000136,0x00000021,0x00000005,0x00040047,0x00000136, + 0x00000022,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016, + 0x00000006,0x00000020,0x00040020,0x00000007,0x00000007,0x00000006,0x00020014,0x00000008, + 0x00040021,0x00000009,0x00000008,0x00000007,0x00040015,0x00000017,0x00000020,0x00000001, + 0x00040017,0x00000018,0x00000017,0x00000002,0x00040020,0x00000019,0x00000007,0x00000018, + 0x00040015,0x0000001b,0x00000020,0x00000000,0x00040017,0x0000001c,0x0000001b,0x00000003, + 0x00040020,0x0000001d,0x00000001,0x0000001c,0x0004003b,0x0000001d,0x0000001e,0x00000001, + 0x00040017,0x0000001f,0x0000001b,0x00000002,0x00040017,0x00000024,0x00000006,0x00000002, + 0x00040017,0x00000025,0x00000006,0x00000004,0x00040018,0x00000026,0x00000025,0x00000004, + 0x0008001e,0x00000027,0x00000018,0x00000024,0x00000017,0x00000006,0x00000024,0x00000026, + 0x00040020,0x00000028,0x00000009,0x00000027,0x0004003b,0x00000028,0x00000029,0x00000009, + 0x0004002b,0x00000017,0x0000002a,0x00000000,0x00040020,0x0000002b,0x00000009,0x00000018, + 0x00040017,0x0000002e,0x00000008,0x00000002,0x00040020,0x00000034,0x00000007,0x00000024, + 0x0004002b,0x00000006,0x00000038,0x3f000000,0x00040017,0x0000003f,0x00000006,0x00000003, + 0x00040020,0x00000040,0x00000007,0x0000003f,0x00090019,0x00000042,0x00000006,0x00000001, + 0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b,0x00000043,0x00000042, + 0x00040020,0x00000044,0x00000000,0x00000043,0x0004003b,0x00000044,0x00000045,0x00000000, + 0x0004002b,0x00000006,0x00000048,0x00000000,0x0004002b,0x00000017,0x0000004b,0x00000002, + 0x00040020,0x0000004c,0x00000009,0x00000017,0x0004002b,0x00000017,0x0000004f,0x00000001, + 0x0006002c,0x0000003f,0x00000058,0x00000048,0x00000048,0x00000048,0x0004002b,0x0000001b, + 0x0000005a,0x00000000,0x0004002b,0x0000001b,0x0000005f,0x00000001,0x0004002b,0x0000001b, + 0x00000064,0x00000002,0x00040017,0x00000068,0x00000008,0x00000003,0x00090019,0x0000006b, + 0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000002,0x00000002,0x00040020, + 0x0000006c,0x00000000,0x0000006b,0x0004003b,0x0000006c,0x0000006d,0x00000000,0x0004002b, + 0x00000006,0x00000072,0x3f800000,0x0005002c,0x00000024,0x00000078,0x00000048,0x00000048, + 0x0004003b,0x00000044,0x0000007f,0x00000000,0x0004002b,0x00000017,0x00000086,0x00000008, + 0x0004002b,0x00000006,0x0000008c,0x40000000,0x00040020,0x00000090,0x00000009,0x00000024, + 0x0004002b,0x00000017,0x000000a4,0x00000003,0x00040020,0x000000a5,0x00000009,0x00000006, + 0x0004002b,0x00000017,0x000000aa,0x00000004,0x0004003b,0x00000044,0x000000af,0x00000000, + 0x0004002b,0x00000017,0x000000bd,0x00000010,0x00040020,0x000000c2,0x00000007,0x00000008, + 0x0004002b,0x00000017,0x000000c6,0x00000020,0x0004002b,0x00000017,0x000000cc,0x00000040, + 0x00040020,0x000000d7,0x00000007,0x00000025,0x0004002b,0x00000017,0x000000d9,0x00000005, + 0x00040020,0x000000da,0x00000009,0x00000026,0x0004002b,0x0000001b,0x000000f4,0x00000003, + 0x0004002b,0x00000006,0x000000f7,0x358637bd,0x00090019,0x0000012b,0x00000006,0x00000001, + 0x00000000,0x00000000,0x00000000,0x00000002,0x00000007,0x00040020,0x0000012c,0x00000000, + 0x0000012b,0x0004003b,0x0000012c,0x0000012d,0x00000000,0x00090019,0x00000134,0x00000006, + 0x00000001,0x00000000,0x00000000,0x00000000,0x00000002,0x00000003,0x00040020,0x00000135, + 0x00000000,0x00000134,0x0004003b,0x00000135,0x00000136,0x00000000,0x0004002b,0x0000001b, + 0x0000013b,0x00000008,0x0006002c,0x0000001c,0x0000013c,0x0000013b,0x0000013b,0x0000005f, + 0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003b, + 0x00000019,0x0000001a,0x00000007,0x0004003b,0x00000034,0x00000035,0x00000007,0x0004003b, + 0x00000040,0x00000041,0x00000007,0x0004003b,0x00000007,0x00000059,0x00000007,0x0004003b, + 0x00000007,0x0000005e,0x00000007,0x0004003b,0x00000007,0x00000063,0x00000007,0x0004003b, + 0x00000034,0x00000077,0x00000007,0x0004003b,0x00000007,0x00000095,0x00000007,0x0004003b, + 0x00000007,0x0000009c,0x00000007,0x0004003b,0x00000007,0x000000a3,0x00000007,0x0004003b, + 0x00000007,0x000000b4,0x00000007,0x0004003b,0x000000c2,0x000000c3,0x00000007,0x0004003b, + 0x00000007,0x000000c9,0x00000007,0x0004003b,0x00000007,0x000000cf,0x00000007,0x0004003b, + 0x000000d7,0x000000d8,0x00000007,0x0004003b,0x00000007,0x000000e2,0x00000007,0x0004003b, + 0x00000034,0x000000fb,0x00000007,0x0004003b,0x00000034,0x00000102,0x00000007,0x0004003b, + 0x00000007,0x00000108,0x00000007,0x0004003b,0x00000007,0x0000011d,0x00000007,0x0004003b, + 0x00000007,0x00000124,0x00000007,0x0004003d,0x0000001c,0x00000020,0x0000001e,0x0007004f, + 0x0000001f,0x00000021,0x00000020,0x00000020,0x00000000,0x00000001,0x0004007c,0x00000018, + 0x00000022,0x00000021,0x0003003e,0x0000001a,0x00000022,0x0004003d,0x00000018,0x00000023, + 0x0000001a,0x00050041,0x0000002b,0x0000002c,0x00000029,0x0000002a,0x0004003d,0x00000018, + 0x0000002d,0x0000002c,0x000500af,0x0000002e,0x0000002f,0x00000023,0x0000002d,0x0004009a, + 0x00000008,0x00000030,0x0000002f,0x000300f7,0x00000032,0x00000000,0x000400fa,0x00000030, + 0x00000031,0x00000032,0x000200f8,0x00000031,0x000100fd,0x000200f8,0x00000032,0x0004003d, + 0x00000018,0x00000036,0x0000001a,0x0004006f,0x00000024,0x00000037,0x00000036,0x00050050, + 0x00000024,0x00000039,0x00000038,0x00000038,0x00050081,0x00000024,0x0000003a,0x00000037, + 0x00000039,0x00050041,0x0000002b,0x0000003b,0x00000029,0x0000002a,0x0004003d,0x00000018, + 0x0000003c,0x0000003b,0x0004006f,0x00000024,0x0000003d,0x0000003c,0x00050088,0x00000024, + 0x0000003e,0x0000003a,0x0000003d,0x0003003e,0x00000035,0x0000003e,0x0004003d,0x00000043, + 0x00000046,0x00000045,0x0004003d,0x00000024,0x00000047,0x00000035,0x00070058,0x00000025, + 0x00000049,0x00000046,0x00000047,0x00000002,0x00000048,0x0008004f,0x0000003f,0x0000004a, + 0x00000049,0x00000049,0x00000000,0x00000001,0x00000002,0x0003003e,0x00000041,0x0000004a, + 0x00050041,0x0000004c,0x0000004d,0x00000029,0x0000004b,0x0004003d,0x00000017,0x0000004e, + 0x0000004d,0x000500c7,0x00000017,0x00000050,0x0000004e,0x0000004f,0x000500ab,0x00000008, + 0x00000051,0x00000050,0x0000002a,0x000300f7,0x00000053,0x00000000,0x000400fa,0x00000051, + 0x00000052,0x00000053,0x000200f8,0x00000052,0x0004003d,0x0000003f,0x00000054,0x00000041, + 0x0004003d,0x0000003f,0x00000055,0x00000041,0x00050085,0x0000003f,0x00000056,0x00000054, + 0x00000055,0x0003003e,0x00000041,0x00000056,0x000200f9,0x00000053,0x000200f8,0x00000053, + 0x0004003d,0x0000003f,0x00000057,0x00000041,0x00050041,0x00000007,0x0000005b,0x00000041, + 0x0000005a,0x0004003d,0x00000006,0x0000005c,0x0000005b,0x0003003e,0x00000059,0x0000005c, + 0x00050039,0x00000008,0x0000005d,0x0000000b,0x00000059,0x00050041,0x00000007,0x00000060, + 0x00000041,0x0000005f,0x0004003d,0x00000006,0x00000061,0x00000060,0x0003003e,0x0000005e, + 0x00000061,0x00050039,0x00000008,0x00000062,0x0000000b,0x0000005e,0x00050041,0x00000007, + 0x00000065,0x00000041,0x00000064,0x0004003d,0x00000006,0x00000066,0x00000065,0x0003003e, + 0x00000063,0x00000066,0x00050039,0x00000008,0x00000067,0x0000000b,0x00000063,0x00060050, + 0x00000068,0x00000069,0x0000005d,0x00000062,0x00000067,0x000600a9,0x0000003f,0x0000006a, + 0x00000069,0x00000058,0x00000057,0x0003003e,0x00000041,0x0000006a,0x0004003d,0x0000006b, + 0x0000006e,0x0000006d,0x0004003d,0x00000018,0x0000006f,0x0000001a,0x0004003d,0x0000003f, + 0x00000070,0x00000041,0x0007000c,0x0000003f,0x00000071,0x00000001,0x00000028,0x00000070, + 0x00000058,0x00050051,0x00000006,0x00000073,0x00000071,0x00000000,0x00050051,0x00000006, + 0x00000074,0x00000071,0x00000001,0x00050051,0x00000006,0x00000075,0x00000071,0x00000002, + 0x00070050,0x00000025,0x00000076,0x00000073,0x00000074,0x00000075,0x00000072,0x00040063, + 0x0000006e,0x0000006f,0x00000076,0x0003003e,0x00000077,0x00000078,0x00050041,0x0000004c, + 0x00000079,0x00000029,0x0000004b,0x0004003d,0x00000017,0x0000007a,0x00000079,0x000500c7, + 0x00000017,0x0000007b,0x0000007a,0x0000004b,0x000500ab,0x00000008,0x0000007c,0x0000007b, + 0x0000002a,0x000300f7,0x0000007e,0x00000000,0x000400fa,0x0000007c,0x0000007d,0x0000007e, + 0x000200f8,0x0000007d,0x0004003d,0x00000043,0x00000080,0x0000007f,0x0004003d,0x00000024, + 0x00000081,0x00000035,0x00070058,0x00000025,0x00000082,0x00000080,0x00000081,0x00000002, + 0x00000048,0x0007004f,0x00000024,0x00000083,0x00000082,0x00000082,0x00000000,0x00000001, + 0x0003003e,0x00000077,0x00000083,0x00050041,0x0000004c,0x00000084,0x00000029,0x0000004b, + 0x0004003d,0x00000017,0x00000085,0x00000084,0x000500c7,0x00000017,0x00000087,0x00000085, + 0x00000086,0x000500ab,0x00000008,0x00000088,0x00000087,0x0000002a,0x000300f7,0x0000008a, + 0x00000000,0x000400fa,0x00000088,0x00000089,0x0000008a,0x000200f8,0x00000089,0x0004003d, + 0x00000024,0x0000008b,0x00000077,0x0005008e,0x00000024,0x0000008d,0x0000008b,0x0000008c, + 0x00050050,0x00000024,0x0000008e,0x00000072,0x00000072,0x00050083,0x00000024,0x0000008f, + 0x0000008d,0x0000008e,0x0003003e,0x00000077,0x0000008f,0x000200f9,0x0000008a,0x000200f8, + 0x0000008a,0x00050041,0x00000090,0x00000091,0x00000029,0x0000004f,0x0004003d,0x00000024, + 0x00000092,0x00000091,0x0004003d,0x00000024,0x00000093,0x00000077,0x00050085,0x00000024, + 0x00000094,0x00000093,0x00000092,0x0003003e,0x00000077,0x00000094,0x00050041,0x00000007, + 0x00000096,0x00000077,0x0000005a,0x0004003d,0x00000006,0x00000097,0x00000096,0x0003003e, + 0x00000095,0x00000097,0x00050039,0x00000008,0x00000098,0x0000000b,0x00000095,0x000400a8, + 0x00000008,0x00000099,0x00000098,0x000300f7,0x0000009b,0x00000000,0x000400fa,0x00000099, + 0x0000009a,0x0000009b,0x000200f8,0x0000009a,0x00050041,0x00000007,0x0000009d,0x00000077, + 0x0000005f,0x0004003d,0x00000006,0x0000009e,0x0000009d,0x0003003e,0x0000009c,0x0000009e, + 0x00050039,0x00000008,0x0000009f,0x0000000b,0x0000009c,0x000200f9,0x0000009b,0x000200f8, + 0x0000009b,0x000700f5,0x00000008,0x000000a0,0x00000098,0x0000008a,0x0000009f,0x0000009a, + 0x000300f7,0x000000a2,0x00000000,0x000400fa,0x000000a0,0x000000a1,0x000000a2,0x000200f8, + 0x000000a1,0x0003003e,0x00000077,0x00000078,0x000200f9,0x000000a2,0x000200f8,0x000000a2, + 0x000200f9,0x0000007e,0x000200f8,0x0000007e,0x00050041,0x000000a5,0x000000a6,0x00000029, + 0x000000a4,0x0004003d,0x00000006,0x000000a7,0x000000a6,0x0003003e,0x000000a3,0x000000a7, + 0x00050041,0x0000004c,0x000000a8,0x00000029,0x0000004b,0x0004003d,0x00000017,0x000000a9, + 0x000000a8,0x000500c7,0x00000017,0x000000ab,0x000000a9,0x000000aa,0x000500ab,0x00000008, + 0x000000ac,0x000000ab,0x0000002a,0x000300f7,0x000000ae,0x00000000,0x000400fa,0x000000ac, + 0x000000ad,0x000000ae,0x000200f8,0x000000ad,0x0004003d,0x00000043,0x000000b0,0x000000af, + 0x0004003d,0x00000024,0x000000b1,0x00000035,0x00070058,0x00000025,0x000000b2,0x000000b0, + 0x000000b1,0x00000002,0x00000048,0x00050051,0x00000006,0x000000b3,0x000000b2,0x00000000, + 0x0003003e,0x000000a3,0x000000b3,0x0004003d,0x00000006,0x000000b5,0x000000a3,0x0003003e, + 0x000000b4,0x000000b5,0x00050039,0x00000008,0x000000b6,0x0000000b,0x000000b4,0x000300f7, + 0x000000b8,0x00000000,0x000400fa,0x000000b6,0x000000b7,0x000000b8,0x000200f8,0x000000b7, + 0x00050041,0x000000a5,0x000000b9,0x00000029,0x000000a4,0x0004003d,0x00000006,0x000000ba, + 0x000000b9,0x0003003e,0x000000a3,0x000000ba,0x000200f9,0x000000b8,0x000200f8,0x000000b8, + 0x000200f9,0x000000ae,0x000200f8,0x000000ae,0x00050041,0x0000004c,0x000000bb,0x00000029, + 0x0000004b,0x0004003d,0x00000017,0x000000bc,0x000000bb,0x000500c7,0x00000017,0x000000be, + 0x000000bc,0x000000bd,0x000500ab,0x00000008,0x000000bf,0x000000be,0x0000002a,0x000300f7, + 0x000000c1,0x00000000,0x000400fa,0x000000bf,0x000000c0,0x000000c1,0x000200f8,0x000000c0, + 0x00050041,0x0000004c,0x000000c4,0x00000029,0x0000004b,0x0004003d,0x00000017,0x000000c5, + 0x000000c4,0x000500c7,0x00000017,0x000000c7,0x000000c5,0x000000c6,0x000500ab,0x00000008, + 0x000000c8,0x000000c7,0x0000002a,0x0003003e,0x000000c3,0x000000c8,0x00050041,0x0000004c, + 0x000000ca,0x00000029,0x0000004b,0x0004003d,0x00000017,0x000000cb,0x000000ca,0x000500c7, + 0x00000017,0x000000cd,0x000000cb,0x000000cc,0x000500ab,0x00000008,0x000000ce,0x000000cd, + 0x0000002a,0x000300f7,0x000000d1,0x00000000,0x000400fa,0x000000ce,0x000000d0,0x000000d4, + 0x000200f8,0x000000d0,0x0004003d,0x00000006,0x000000d2,0x000000a3,0x00050083,0x00000006, + 0x000000d3,0x00000072,0x000000d2,0x0003003e,0x000000cf,0x000000d3,0x000200f9,0x000000d1, + 0x000200f8,0x000000d4,0x0004003d,0x00000006,0x000000d5,0x000000a3,0x0003003e,0x000000cf, + 0x000000d5,0x000200f9,0x000000d1,0x000200f8,0x000000d1,0x0004003d,0x00000006,0x000000d6, + 0x000000cf,0x0003003e,0x000000c9,0x000000d6,0x00050041,0x000000da,0x000000db,0x00000029, + 0x000000d9,0x0004003d,0x00000026,0x000000dc,0x000000db,0x00050041,0x00000007,0x000000dd, + 0x00000035,0x0000005a,0x0004003d,0x00000006,0x000000de,0x000000dd,0x00050085,0x00000006, + 0x000000df,0x000000de,0x0000008c,0x00050083,0x00000006,0x000000e0,0x000000df,0x00000072, + 0x0004003d,0x00000008,0x000000e1,0x000000c3,0x000300f7,0x000000e4,0x00000000,0x000400fa, + 0x000000e1,0x000000e3,0x000000e9,0x000200f8,0x000000e3,0x00050041,0x00000007,0x000000e5, + 0x00000035,0x0000005f,0x0004003d,0x00000006,0x000000e6,0x000000e5,0x00050085,0x00000006, + 0x000000e7,0x000000e6,0x0000008c,0x00050083,0x00000006,0x000000e8,0x00000072,0x000000e7, + 0x0003003e,0x000000e2,0x000000e8,0x000200f9,0x000000e4,0x000200f8,0x000000e9,0x00050041, + 0x00000007,0x000000ea,0x00000035,0x0000005f,0x0004003d,0x00000006,0x000000eb,0x000000ea, + 0x00050085,0x00000006,0x000000ec,0x000000eb,0x0000008c,0x00050083,0x00000006,0x000000ed, + 0x000000ec,0x00000072,0x0003003e,0x000000e2,0x000000ed,0x000200f9,0x000000e4,0x000200f8, + 0x000000e4,0x0004003d,0x00000006,0x000000ee,0x000000e2,0x0004003d,0x00000006,0x000000ef, + 0x000000c9,0x00050085,0x00000006,0x000000f0,0x000000ef,0x0000008c,0x00050083,0x00000006, + 0x000000f1,0x000000f0,0x00000072,0x00070050,0x00000025,0x000000f2,0x000000e0,0x000000ee, + 0x000000f1,0x00000072,0x00050091,0x00000025,0x000000f3,0x000000dc,0x000000f2,0x0003003e, + 0x000000d8,0x000000f3,0x00050041,0x00000007,0x000000f5,0x000000d8,0x000000f4,0x0004003d, + 0x00000006,0x000000f6,0x000000f5,0x000500ba,0x00000008,0x000000f8,0x000000f6,0x000000f7, + 0x000300f7,0x000000fa,0x00000000,0x000400fa,0x000000f8,0x000000f9,0x000000fa,0x000200f8, + 0x000000f9,0x0004003d,0x00000025,0x000000fc,0x000000d8,0x0007004f,0x00000024,0x000000fd, + 0x000000fc,0x000000fc,0x00000000,0x00000001,0x00050041,0x00000007,0x000000fe,0x000000d8, + 0x000000f4,0x0004003d,0x00000006,0x000000ff,0x000000fe,0x00050050,0x00000024,0x00000100, + 0x000000ff,0x000000ff,0x00050088,0x00000024,0x00000101,0x000000fd,0x00000100,0x0003003e, + 0x000000fb,0x00000101,0x00050041,0x00000007,0x00000103,0x000000fb,0x0000005a,0x0004003d, + 0x00000006,0x00000104,0x00000103,0x00050085,0x00000006,0x00000105,0x00000104,0x00000038, + 0x00050081,0x00000006,0x00000106,0x00000105,0x00000038,0x0004003d,0x00000008,0x00000107, + 0x000000c3,0x000300f7,0x0000010a,0x00000000,0x000400fa,0x00000107,0x00000109,0x0000010f, + 0x000200f8,0x00000109,0x00050041,0x00000007,0x0000010b,0x000000fb,0x0000005f,0x0004003d, + 0x00000006,0x0000010c,0x0000010b,0x00050085,0x00000006,0x0000010d,0x0000010c,0x00000038, + 0x00050083,0x00000006,0x0000010e,0x00000038,0x0000010d,0x0003003e,0x00000108,0x0000010e, + 0x000200f9,0x0000010a,0x000200f8,0x0000010f,0x00050041,0x00000007,0x00000110,0x000000fb, + 0x0000005f,0x0004003d,0x00000006,0x00000111,0x00000110,0x00050085,0x00000006,0x00000112, + 0x00000111,0x00000038,0x00050081,0x00000006,0x00000113,0x00000112,0x00000038,0x0003003e, + 0x00000108,0x00000113,0x000200f9,0x0000010a,0x000200f8,0x0000010a,0x0004003d,0x00000006, + 0x00000114,0x00000108,0x00050050,0x00000024,0x00000115,0x00000106,0x00000114,0x0003003e, + 0x00000102,0x00000115,0x0004003d,0x00000024,0x00000116,0x00000102,0x0004003d,0x00000024, + 0x00000117,0x00000035,0x00050083,0x00000024,0x00000118,0x00000116,0x00000117,0x00050041, + 0x0000002b,0x00000119,0x00000029,0x0000002a,0x0004003d,0x00000018,0x0000011a,0x00000119, + 0x0004006f,0x00000024,0x0000011b,0x0000011a,0x00050085,0x00000024,0x0000011c,0x00000118, + 0x0000011b,0x0003003e,0x00000077,0x0000011c,0x00050041,0x00000007,0x0000011e,0x00000077, + 0x0000005a,0x0004003d,0x00000006,0x0000011f,0x0000011e,0x0003003e,0x0000011d,0x0000011f, + 0x00050039,0x00000008,0x00000120,0x0000000b,0x0000011d,0x000400a8,0x00000008,0x00000121, + 0x00000120,0x000300f7,0x00000123,0x00000000,0x000400fa,0x00000121,0x00000122,0x00000123, + 0x000200f8,0x00000122,0x00050041,0x00000007,0x00000125,0x00000077,0x0000005f,0x0004003d, + 0x00000006,0x00000126,0x00000125,0x0003003e,0x00000124,0x00000126,0x00050039,0x00000008, + 0x00000127,0x0000000b,0x00000124,0x000200f9,0x00000123,0x000200f8,0x00000123,0x000700f5, + 0x00000008,0x00000128,0x00000120,0x0000010a,0x00000127,0x00000122,0x000300f7,0x0000012a, + 0x00000000,0x000400fa,0x00000128,0x00000129,0x0000012a,0x000200f8,0x00000129,0x0003003e, + 0x00000077,0x00000078,0x000200f9,0x0000012a,0x000200f8,0x0000012a,0x000200f9,0x000000fa, + 0x000200f8,0x000000fa,0x000200f9,0x000000c1,0x000200f8,0x000000c1,0x0004003d,0x0000012b, + 0x0000012e,0x0000012d,0x0004003d,0x00000018,0x0000012f,0x0000001a,0x0004003d,0x00000024, + 0x00000130,0x00000077,0x00050051,0x00000006,0x00000131,0x00000130,0x00000000,0x00050051, + 0x00000006,0x00000132,0x00000130,0x00000001,0x00070050,0x00000025,0x00000133,0x00000131, + 0x00000132,0x00000048,0x00000048,0x00040063,0x0000012e,0x0000012f,0x00000133,0x0004003d, + 0x00000134,0x00000137,0x00000136,0x0004003d,0x00000018,0x00000138,0x0000001a,0x0004003d, + 0x00000006,0x00000139,0x000000a3,0x00070050,0x00000025,0x0000013a,0x00000139,0x00000048, + 0x00000048,0x00000048,0x00040063,0x00000137,0x00000138,0x0000013a,0x000100fd,0x00010038, + 0x00050036,0x00000008,0x0000000b,0x00000000,0x00000009,0x00030037,0x00000007,0x0000000a, + 0x000200f8,0x0000000c,0x0004003d,0x00000006,0x0000000d,0x0000000a,0x0004009c,0x00000008, + 0x0000000e,0x0000000d,0x000400a8,0x00000008,0x0000000f,0x0000000e,0x000300f7,0x00000011, + 0x00000000,0x000400fa,0x0000000f,0x00000010,0x00000011,0x000200f8,0x00000010,0x0004003d, + 0x00000006,0x00000012,0x0000000a,0x0004009d,0x00000008,0x00000013,0x00000012,0x000200f9, + 0x00000011,0x000200f8,0x00000011,0x000700f5,0x00000008,0x00000014,0x0000000e,0x0000000c, + 0x00000013,0x00000010,0x000200fe,0x00000014,0x00010038, +}; diff --git a/gpu/vk/shaders/fg_scale.comp b/gpu/vk/shaders/fg_scale.comp index 1721e3c3..7ca1e87b 100644 --- a/gpu/vk/shaders/fg_scale.comp +++ b/gpu/vk/shaders/fg_scale.comp @@ -1,6 +1,6 @@ #version 450 // the frame to the window's size (the Insert menu's Upscaling, Sharpness): Catmull-Rom or Lanczos 2, or the linear filter, then -// a contrast-limited sharpen that cannot go past the light around the pixel, so it adds no halos and no flashes +// a contrast-adaptive sharpen (RCAS-style, build 6) that cannot go past the light around the pixel, so it adds no halos layout(local_size_x = 8, local_size_y = 8) in; layout(binding = 0) uniform sampler2D src; layout(binding = 1, rgba8) uniform writeonly image2D dst; @@ -43,11 +43,19 @@ void main() { const vec2 uv = (vec2(g) + 0.5) / vec2(d); vec3 c = filtered(uv); if (pc.sharp > 0.0) { - const vec2 e = 1.0 / vec2(d); + // (hookshot DLSS build 6) A contrast-adaptive sharpen in the manner of AMD FidelityFX RCAS: its four neighbours one + // source pixel away (the old sharpen took them one window pixel away, under a source pixel when the window is + // larger, and clamped to their range: next to no change at any setting). The lobe is limited per pixel so that + // nothing goes past the light around it (no halos, no clipping); pc.sharp 1 is RCAS's strongest. + const vec2 e = 1.0 / vec2(textureSize(src, 0)); const vec3 n = textureLod(src, uv + vec2(0.0, -e.y), 0.0).rgb, s = textureLod(src, uv + vec2(0.0, e.y), 0.0).rgb; const vec3 w = textureLod(src, uv + vec2(-e.x, 0.0), 0.0).rgb, r = textureLod(src, uv + vec2(e.x, 0.0), 0.0).rgb; - const vec3 lo = min(min(min(n, s), min(w, r)), c), hi = max(max(max(n, s), max(w, r)), c); - c = clamp(c + pc.sharp * (c - (n + s + w + r) * 0.25), lo, hi); + const vec3 mn4 = min(min(n, s), min(w, r)), mx4 = max(max(n, s), max(w, r)); + const vec3 hit_min = min(mn4, c) / max(4.0 * max(mx4, c), vec3(1e-5)); + const vec3 hit_max = (1.0 - max(mx4, c)) / min(4.0 * mn4 - 4.0, vec3(-1e-5)); + const vec3 lobe3 = max(-hit_min, hit_max); + const float lobe = max(-0.1875, min(max(max(lobe3.r, lobe3.g), lobe3.b), 0.0)) * clamp(pc.sharp, 0.0, 1.0); + c = clamp((lobe * (n + s + w + r) + c) / (4.0 * lobe + 1.0), 0.0, 1.0); } imageStore(dst, g, vec4(c, 1.0)); } diff --git a/gpu/vk/shaders/fg_scale_comp_spv.h b/gpu/vk/shaders/fg_scale_comp_spv.h index 747d403a..0a952faf 100644 --- a/gpu/vk/shaders/fg_scale_comp_spv.h +++ b/gpu/vk/shaders/fg_scale_comp_spv.h @@ -1,7 +1,7 @@ // 1116.6.0 #pragma once const uint32_t FG_SCALE_COMP_SPV[] = { - 0x07230203,0x00010500,0x0008000b,0x00000172,0x00000000,0x00020011,0x00000001,0x00020011, + 0x07230203,0x00010500,0x0008000b,0x000001a0,0x00000000,0x00020011,0x00000001,0x00020011, 0x00000032,0x0006000b,0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e, 0x00000000,0x00000001,0x0009000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x00000069, 0x00000074,0x000000f2,0x000000fa,0x00060010,0x00000004,0x00000011,0x00000008,0x00000008, @@ -21,14 +21,16 @@ const uint32_t FG_SCALE_COMP_SPV[] = { 0x00030005,0x000000ef,0x00000067,0x00080005,0x000000f2,0x475f6c67,0x61626f6c,0x766e496c, 0x7461636f,0x496e6f69,0x00000044,0x00030005,0x000000f7,0x00000064,0x00030005,0x000000fa, 0x00747364,0x00030005,0x00000105,0x00007675,0x00030005,0x0000010d,0x00000063,0x00040005, - 0x0000010e,0x61726170,0x0000006d,0x00030005,0x00000117,0x00000065,0x00030005,0x0000011c, - 0x0000006e,0x00030005,0x00000126,0x00000073,0x00030005,0x0000012f,0x00000077,0x00030005, - 0x00000139,0x00000072,0x00030005,0x00000142,0x00006f6c,0x00030005,0x0000014c,0x00006968, + 0x0000010e,0x61726170,0x0000006d,0x00030005,0x00000117,0x00000065,0x00030005,0x0000011e, + 0x0000006e,0x00030005,0x00000128,0x00000073,0x00030005,0x00000131,0x00000077,0x00030005, + 0x0000013b,0x00000072,0x00030005,0x00000144,0x00346e6d,0x00030005,0x0000014c,0x0034786d, + 0x00040005,0x00000154,0x5f746968,0x006e696d,0x00040005,0x00000160,0x5f746968,0x0078616d, + 0x00040005,0x0000016e,0x65626f6c,0x00000033,0x00040005,0x00000173,0x65626f6c,0x00000000, 0x00030047,0x00000067,0x00000002,0x00050048,0x00000067,0x00000000,0x00000023,0x00000000, 0x00050048,0x00000067,0x00000001,0x00000023,0x00000004,0x00040047,0x00000074,0x00000021, 0x00000000,0x00040047,0x00000074,0x00000022,0x00000000,0x00040047,0x000000f2,0x0000000b, 0x0000001c,0x00030047,0x000000fa,0x00000019,0x00040047,0x000000fa,0x00000021,0x00000001, - 0x00040047,0x000000fa,0x00000022,0x00000000,0x00040047,0x00000171,0x0000000b,0x00000019, + 0x00040047,0x000000fa,0x00000022,0x00000000,0x00040047,0x0000019f,0x0000000b,0x00000019, 0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020, 0x00040020,0x00000007,0x00000007,0x00000006,0x00040021,0x00000008,0x00000006,0x00000007, 0x00040017,0x0000000f,0x00000006,0x00000002,0x00040020,0x00000010,0x00000007,0x0000000f, @@ -56,15 +58,20 @@ const uint32_t FG_SCALE_COMP_SPV[] = { 0x00000001,0x00000000,0x00000000,0x00000000,0x00000002,0x00000004,0x00040020,0x000000f9, 0x00000000,0x000000f8,0x0004003b,0x000000f9,0x000000fa,0x00000000,0x00040017,0x000000ff, 0x0000001a,0x00000002,0x00040020,0x00000111,0x00000009,0x00000006,0x0004002b,0x00000006, - 0x00000161,0x3e800000,0x0004002b,0x000000b8,0x00000170,0x00000008,0x0006002c,0x000000f0, - 0x00000171,0x00000170,0x00000170,0x000000be,0x00050036,0x00000002,0x00000004,0x00000000, + 0x0000015c,0x3727c5ac,0x0006002c,0x00000011,0x0000015d,0x0000015c,0x0000015c,0x0000015c, + 0x0004002b,0x00000006,0x0000016a,0xb727c5ac,0x0006002c,0x00000011,0x0000016b,0x0000016a, + 0x0000016a,0x0000016a,0x0004002b,0x00000006,0x00000174,0xbe400000,0x0004002b,0x000000b8, + 0x0000017a,0x00000002,0x0004002b,0x000000b8,0x0000019e,0x00000008,0x0006002c,0x000000f0, + 0x0000019f,0x0000019e,0x0000019e,0x000000be,0x00050036,0x00000002,0x00000004,0x00000000, 0x00000003,0x000200f8,0x00000005,0x0004003b,0x00000087,0x000000ef,0x00000007,0x0004003b, 0x00000087,0x000000f7,0x00000007,0x0004003b,0x00000010,0x00000105,0x00000007,0x0004003b, 0x00000091,0x0000010d,0x00000007,0x0004003b,0x00000010,0x0000010e,0x00000007,0x0004003b, - 0x00000010,0x00000117,0x00000007,0x0004003b,0x00000091,0x0000011c,0x00000007,0x0004003b, - 0x00000091,0x00000126,0x00000007,0x0004003b,0x00000091,0x0000012f,0x00000007,0x0004003b, - 0x00000091,0x00000139,0x00000007,0x0004003b,0x00000091,0x00000142,0x00000007,0x0004003b, - 0x00000091,0x0000014c,0x00000007,0x0004003d,0x000000f0,0x000000f4,0x000000f2,0x0007004f, + 0x00000010,0x00000117,0x00000007,0x0004003b,0x00000091,0x0000011e,0x00000007,0x0004003b, + 0x00000091,0x00000128,0x00000007,0x0004003b,0x00000091,0x00000131,0x00000007,0x0004003b, + 0x00000091,0x0000013b,0x00000007,0x0004003b,0x00000091,0x00000144,0x00000007,0x0004003b, + 0x00000091,0x0000014c,0x00000007,0x0004003b,0x00000091,0x00000154,0x00000007,0x0004003b, + 0x00000091,0x00000160,0x00000007,0x0004003b,0x00000091,0x0000016e,0x00000007,0x0004003b, + 0x00000007,0x00000173,0x00000007,0x0004003d,0x000000f0,0x000000f4,0x000000f2,0x0007004f, 0x000000f3,0x000000f5,0x000000f4,0x000000f4,0x00000000,0x00000001,0x0004007c,0x0000007e, 0x000000f6,0x000000f5,0x0003003e,0x000000ef,0x000000f6,0x0004003d,0x000000f8,0x000000fb, 0x000000fa,0x00040068,0x0000007e,0x000000fc,0x000000fb,0x0003003e,0x000000f7,0x000000fc, @@ -80,176 +87,202 @@ const uint32_t FG_SCALE_COMP_SPV[] = { 0x00000110,0x00000014,0x0000010e,0x0003003e,0x0000010d,0x00000110,0x00050041,0x00000111, 0x00000112,0x00000069,0x000000d8,0x0004003d,0x00000006,0x00000113,0x00000112,0x000500ba, 0x0000001a,0x00000114,0x00000113,0x00000025,0x000300f7,0x00000116,0x00000000,0x000400fa, - 0x00000114,0x00000115,0x00000116,0x000200f8,0x00000115,0x0004003d,0x0000007e,0x00000118, - 0x000000f7,0x0004006f,0x0000000f,0x00000119,0x00000118,0x00050050,0x0000000f,0x0000011a, - 0x0000001e,0x0000001e,0x00050088,0x0000000f,0x0000011b,0x0000011a,0x00000119,0x0003003e, - 0x00000117,0x0000011b,0x0004003d,0x00000072,0x0000011d,0x00000074,0x0004003d,0x0000000f, - 0x0000011e,0x00000105,0x00050041,0x00000007,0x0000011f,0x00000117,0x000000be,0x0004003d, - 0x00000006,0x00000120,0x0000011f,0x0004007f,0x00000006,0x00000121,0x00000120,0x00050050, - 0x0000000f,0x00000122,0x00000025,0x00000121,0x00050081,0x0000000f,0x00000123,0x0000011e, - 0x00000122,0x00070058,0x00000077,0x00000124,0x0000011d,0x00000123,0x00000002,0x00000025, - 0x0008004f,0x00000011,0x00000125,0x00000124,0x00000124,0x00000000,0x00000001,0x00000002, - 0x0003003e,0x0000011c,0x00000125,0x0004003d,0x00000072,0x00000127,0x00000074,0x0004003d, - 0x0000000f,0x00000128,0x00000105,0x00050041,0x00000007,0x00000129,0x00000117,0x000000be, - 0x0004003d,0x00000006,0x0000012a,0x00000129,0x00050050,0x0000000f,0x0000012b,0x00000025, - 0x0000012a,0x00050081,0x0000000f,0x0000012c,0x00000128,0x0000012b,0x00070058,0x00000077, - 0x0000012d,0x00000127,0x0000012c,0x00000002,0x00000025,0x0008004f,0x00000011,0x0000012e, - 0x0000012d,0x0000012d,0x00000000,0x00000001,0x00000002,0x0003003e,0x00000126,0x0000012e, - 0x0004003d,0x00000072,0x00000130,0x00000074,0x0004003d,0x0000000f,0x00000131,0x00000105, - 0x00050041,0x00000007,0x00000132,0x00000117,0x000000b9,0x0004003d,0x00000006,0x00000133, - 0x00000132,0x0004007f,0x00000006,0x00000134,0x00000133,0x00050050,0x0000000f,0x00000135, - 0x00000134,0x00000025,0x00050081,0x0000000f,0x00000136,0x00000131,0x00000135,0x00070058, - 0x00000077,0x00000137,0x00000130,0x00000136,0x00000002,0x00000025,0x0008004f,0x00000011, - 0x00000138,0x00000137,0x00000137,0x00000000,0x00000001,0x00000002,0x0003003e,0x0000012f, - 0x00000138,0x0004003d,0x00000072,0x0000013a,0x00000074,0x0004003d,0x0000000f,0x0000013b, - 0x00000105,0x00050041,0x00000007,0x0000013c,0x00000117,0x000000b9,0x0004003d,0x00000006, - 0x0000013d,0x0000013c,0x00050050,0x0000000f,0x0000013e,0x0000013d,0x00000025,0x00050081, - 0x0000000f,0x0000013f,0x0000013b,0x0000013e,0x00070058,0x00000077,0x00000140,0x0000013a, - 0x0000013f,0x00000002,0x00000025,0x0008004f,0x00000011,0x00000141,0x00000140,0x00000140, - 0x00000000,0x00000001,0x00000002,0x0003003e,0x00000139,0x00000141,0x0004003d,0x00000011, - 0x00000143,0x0000011c,0x0004003d,0x00000011,0x00000144,0x00000126,0x0007000c,0x00000011, - 0x00000145,0x00000001,0x00000025,0x00000143,0x00000144,0x0004003d,0x00000011,0x00000146, - 0x0000012f,0x0004003d,0x00000011,0x00000147,0x00000139,0x0007000c,0x00000011,0x00000148, - 0x00000001,0x00000025,0x00000146,0x00000147,0x0007000c,0x00000011,0x00000149,0x00000001, - 0x00000025,0x00000145,0x00000148,0x0004003d,0x00000011,0x0000014a,0x0000010d,0x0007000c, - 0x00000011,0x0000014b,0x00000001,0x00000025,0x00000149,0x0000014a,0x0003003e,0x00000142, - 0x0000014b,0x0004003d,0x00000011,0x0000014d,0x0000011c,0x0004003d,0x00000011,0x0000014e, - 0x00000126,0x0007000c,0x00000011,0x0000014f,0x00000001,0x00000028,0x0000014d,0x0000014e, - 0x0004003d,0x00000011,0x00000150,0x0000012f,0x0004003d,0x00000011,0x00000151,0x00000139, - 0x0007000c,0x00000011,0x00000152,0x00000001,0x00000028,0x00000150,0x00000151,0x0007000c, - 0x00000011,0x00000153,0x00000001,0x00000028,0x0000014f,0x00000152,0x0004003d,0x00000011, - 0x00000154,0x0000010d,0x0007000c,0x00000011,0x00000155,0x00000001,0x00000028,0x00000153, - 0x00000154,0x0003003e,0x0000014c,0x00000155,0x0004003d,0x00000011,0x00000156,0x0000010d, - 0x00050041,0x00000111,0x00000157,0x00000069,0x000000d8,0x0004003d,0x00000006,0x00000158, - 0x00000157,0x0004003d,0x00000011,0x00000159,0x0000010d,0x0004003d,0x00000011,0x0000015a, - 0x0000011c,0x0004003d,0x00000011,0x0000015b,0x00000126,0x00050081,0x00000011,0x0000015c, - 0x0000015a,0x0000015b,0x0004003d,0x00000011,0x0000015d,0x0000012f,0x00050081,0x00000011, - 0x0000015e,0x0000015c,0x0000015d,0x0004003d,0x00000011,0x0000015f,0x00000139,0x00050081, - 0x00000011,0x00000160,0x0000015e,0x0000015f,0x0005008e,0x00000011,0x00000162,0x00000160, - 0x00000161,0x00050083,0x00000011,0x00000163,0x00000159,0x00000162,0x0005008e,0x00000011, - 0x00000164,0x00000163,0x00000158,0x00050081,0x00000011,0x00000165,0x00000156,0x00000164, - 0x0004003d,0x00000011,0x00000166,0x00000142,0x0004003d,0x00000011,0x00000167,0x0000014c, - 0x0008000c,0x00000011,0x00000168,0x00000001,0x0000002b,0x00000165,0x00000166,0x00000167, - 0x0003003e,0x0000010d,0x00000168,0x000200f9,0x00000116,0x000200f8,0x00000116,0x0004003d, - 0x000000f8,0x00000169,0x000000fa,0x0004003d,0x0000007e,0x0000016a,0x000000ef,0x0004003d, - 0x00000011,0x0000016b,0x0000010d,0x00050051,0x00000006,0x0000016c,0x0000016b,0x00000000, - 0x00050051,0x00000006,0x0000016d,0x0000016b,0x00000001,0x00050051,0x00000006,0x0000016e, - 0x0000016b,0x00000002,0x00070050,0x00000077,0x0000016f,0x0000016c,0x0000016d,0x0000016e, - 0x0000001e,0x00040063,0x00000169,0x0000016a,0x0000016f,0x000100fd,0x00010038,0x00050036, - 0x00000006,0x0000000a,0x00000000,0x00000008,0x00030037,0x00000007,0x00000009,0x000200f8, - 0x0000000b,0x0004003b,0x00000007,0x00000027,0x00000007,0x0004003d,0x00000006,0x00000016, - 0x00000009,0x0006000c,0x00000006,0x00000017,0x00000001,0x00000004,0x00000016,0x0003003e, - 0x00000009,0x00000017,0x0004003d,0x00000006,0x00000018,0x00000009,0x000500b8,0x0000001a, - 0x0000001b,0x00000018,0x00000019,0x000300f7,0x0000001d,0x00000000,0x000400fa,0x0000001b, - 0x0000001c,0x0000001d,0x000200f8,0x0000001c,0x000200fe,0x0000001e,0x000200f8,0x0000001d, - 0x0004003d,0x00000006,0x00000020,0x00000009,0x000500be,0x0000001a,0x00000022,0x00000020, - 0x00000021,0x000300f7,0x00000024,0x00000000,0x000400fa,0x00000022,0x00000023,0x00000024, - 0x000200f8,0x00000023,0x000200fe,0x00000025,0x000200f8,0x00000024,0x0004003d,0x00000006, - 0x00000029,0x00000009,0x00050085,0x00000006,0x0000002a,0x00000028,0x00000029,0x0003003e, - 0x00000027,0x0000002a,0x0004003d,0x00000006,0x0000002b,0x00000027,0x0006000c,0x00000006, - 0x0000002c,0x00000001,0x0000000d,0x0000002b,0x00050085,0x00000006,0x0000002d,0x00000021, - 0x0000002c,0x0004003d,0x00000006,0x0000002e,0x00000027,0x00050085,0x00000006,0x00000030, - 0x0000002e,0x0000002f,0x0006000c,0x00000006,0x00000031,0x00000001,0x0000000d,0x00000030, - 0x00050085,0x00000006,0x00000032,0x0000002d,0x00000031,0x0004003d,0x00000006,0x00000033, - 0x00000027,0x0004003d,0x00000006,0x00000034,0x00000027,0x00050085,0x00000006,0x00000035, - 0x00000033,0x00000034,0x00050088,0x00000006,0x00000036,0x00000032,0x00000035,0x000200fe, - 0x00000036,0x00010038,0x00050036,0x00000006,0x0000000d,0x00000000,0x00000008,0x00030037, - 0x00000007,0x0000000c,0x000200f8,0x0000000e,0x0004003d,0x00000006,0x00000039,0x0000000c, - 0x0006000c,0x00000006,0x0000003a,0x00000001,0x00000004,0x00000039,0x0003003e,0x0000000c, - 0x0000003a,0x0004003d,0x00000006,0x0000003b,0x0000000c,0x000500b8,0x0000001a,0x0000003c, - 0x0000003b,0x0000001e,0x000300f7,0x0000003e,0x00000000,0x000400fa,0x0000003c,0x0000003d, - 0x0000003e,0x000200f8,0x0000003d,0x0004003d,0x00000006,0x00000040,0x0000000c,0x00050085, - 0x00000006,0x00000041,0x0000003f,0x00000040,0x0004003d,0x00000006,0x00000042,0x0000000c, - 0x00050085,0x00000006,0x00000043,0x00000041,0x00000042,0x0004003d,0x00000006,0x00000044, - 0x0000000c,0x00050085,0x00000006,0x00000045,0x00000043,0x00000044,0x0004003d,0x00000006, - 0x00000047,0x0000000c,0x00050085,0x00000006,0x00000048,0x00000046,0x00000047,0x0004003d, - 0x00000006,0x00000049,0x0000000c,0x00050085,0x00000006,0x0000004a,0x00000048,0x00000049, - 0x00050083,0x00000006,0x0000004b,0x00000045,0x0000004a,0x00050081,0x00000006,0x0000004c, - 0x0000004b,0x0000001e,0x000200fe,0x0000004c,0x000200f8,0x0000003e,0x0004003d,0x00000006, - 0x0000004e,0x0000000c,0x000500b8,0x0000001a,0x0000004f,0x0000004e,0x00000021,0x000300f7, - 0x00000051,0x00000000,0x000400fa,0x0000004f,0x00000050,0x00000051,0x000200f8,0x00000050, - 0x0004003d,0x00000006,0x00000053,0x0000000c,0x00050085,0x00000006,0x00000054,0x00000052, - 0x00000053,0x0004003d,0x00000006,0x00000055,0x0000000c,0x00050085,0x00000006,0x00000056, - 0x00000054,0x00000055,0x0004003d,0x00000006,0x00000057,0x0000000c,0x00050085,0x00000006, - 0x00000058,0x00000056,0x00000057,0x0004003d,0x00000006,0x00000059,0x0000000c,0x00050085, - 0x00000006,0x0000005a,0x00000046,0x00000059,0x0004003d,0x00000006,0x0000005b,0x0000000c, - 0x00050085,0x00000006,0x0000005c,0x0000005a,0x0000005b,0x00050081,0x00000006,0x0000005d, - 0x00000058,0x0000005c,0x0004003d,0x00000006,0x0000005f,0x0000000c,0x00050085,0x00000006, - 0x00000060,0x0000005e,0x0000005f,0x00050083,0x00000006,0x00000061,0x0000005d,0x00000060, - 0x00050081,0x00000006,0x00000062,0x00000061,0x00000021,0x000200fe,0x00000062,0x000200f8, - 0x00000051,0x000200fe,0x00000025,0x00010038,0x00050036,0x00000011,0x00000014,0x00000000, - 0x00000012,0x00030037,0x00000010,0x00000013,0x000200f8,0x00000015,0x0004003b,0x00000010, - 0x0000007b,0x00000007,0x0004003b,0x00000010,0x00000081,0x00000007,0x0004003b,0x00000087, - 0x00000088,0x00000007,0x0004003b,0x00000010,0x0000008c,0x00000007,0x0004003b,0x00000091, - 0x00000092,0x00000007,0x0004003b,0x00000007,0x00000094,0x00000007,0x0004003b,0x00000095, - 0x00000096,0x00000007,0x0004003b,0x00000095,0x000000a0,0x00000007,0x0004003b,0x00000010, - 0x000000a8,0x00000007,0x0004003b,0x00000007,0x000000b0,0x00000007,0x0004003b,0x00000007, - 0x000000b4,0x00000007,0x0004003b,0x00000007,0x000000b7,0x00000007,0x0004003b,0x00000007, - 0x000000bd,0x00000007,0x0004003b,0x00000007,0x000000c4,0x00000007,0x0004003b,0x00000007, - 0x000000c8,0x00000007,0x00050041,0x0000006b,0x0000006c,0x00000069,0x0000006a,0x0004003d, - 0x00000066,0x0000006d,0x0000006c,0x000500aa,0x0000001a,0x0000006e,0x0000006d,0x0000006a, - 0x000300f7,0x00000070,0x00000000,0x000400fa,0x0000006e,0x0000006f,0x00000070,0x000200f8, - 0x0000006f,0x0004003d,0x00000072,0x00000075,0x00000074,0x0004003d,0x0000000f,0x00000076, - 0x00000013,0x00070058,0x00000077,0x00000078,0x00000075,0x00000076,0x00000002,0x00000025, - 0x0008004f,0x00000011,0x00000079,0x00000078,0x00000078,0x00000000,0x00000001,0x00000002, - 0x000200fe,0x00000079,0x000200f8,0x00000070,0x0004003d,0x00000072,0x0000007c,0x00000074, - 0x00040064,0x00000071,0x0000007d,0x0000007c,0x00050067,0x0000007e,0x0000007f,0x0000007d, - 0x0000006a,0x0004006f,0x0000000f,0x00000080,0x0000007f,0x0003003e,0x0000007b,0x00000080, - 0x0004003d,0x0000000f,0x00000082,0x00000013,0x0004003d,0x0000000f,0x00000083,0x0000007b, - 0x00050085,0x0000000f,0x00000084,0x00000082,0x00000083,0x00050050,0x0000000f,0x00000085, - 0x0000002f,0x0000002f,0x00050083,0x0000000f,0x00000086,0x00000084,0x00000085,0x0003003e, - 0x00000081,0x00000086,0x0004003d,0x0000000f,0x00000089,0x00000081,0x0006000c,0x0000000f, - 0x0000008a,0x00000001,0x00000008,0x00000089,0x0004006e,0x0000007e,0x0000008b,0x0000008a, - 0x0003003e,0x00000088,0x0000008b,0x0004003d,0x0000000f,0x0000008d,0x00000081,0x0004003d, - 0x0000007e,0x0000008e,0x00000088,0x0004006f,0x0000000f,0x0000008f,0x0000008e,0x00050083, - 0x0000000f,0x00000090,0x0000008d,0x0000008f,0x0003003e,0x0000008c,0x00000090,0x0003003e, - 0x00000092,0x00000093,0x0003003e,0x00000094,0x00000025,0x0003003e,0x00000096,0x00000097, - 0x000200f9,0x00000098,0x000200f8,0x00000098,0x000400f6,0x0000009a,0x0000009b,0x00000000, - 0x000200f9,0x0000009c,0x000200f8,0x0000009c,0x0004003d,0x00000066,0x0000009d,0x00000096, - 0x000500b3,0x0000001a,0x0000009f,0x0000009d,0x0000009e,0x000400fa,0x0000009f,0x00000099, - 0x0000009a,0x000200f8,0x00000099,0x0003003e,0x000000a0,0x00000097,0x000200f9,0x000000a1, - 0x000200f8,0x000000a1,0x000400f6,0x000000a3,0x000000a4,0x00000000,0x000200f9,0x000000a5, - 0x000200f8,0x000000a5,0x0004003d,0x00000066,0x000000a6,0x000000a0,0x000500b3,0x0000001a, - 0x000000a7,0x000000a6,0x0000009e,0x000400fa,0x000000a7,0x000000a2,0x000000a3,0x000200f8, - 0x000000a2,0x0004003d,0x00000066,0x000000a9,0x000000a0,0x0004006f,0x00000006,0x000000aa, - 0x000000a9,0x0004003d,0x00000066,0x000000ab,0x00000096,0x0004006f,0x00000006,0x000000ac, - 0x000000ab,0x00050050,0x0000000f,0x000000ad,0x000000aa,0x000000ac,0x0004003d,0x0000000f, - 0x000000ae,0x0000008c,0x00050083,0x0000000f,0x000000af,0x000000ad,0x000000ae,0x0003003e, - 0x000000a8,0x000000af,0x00050041,0x0000006b,0x000000b1,0x00000069,0x0000006a,0x0004003d, - 0x00000066,0x000000b2,0x000000b1,0x000500aa,0x0000001a,0x000000b3,0x000000b2,0x0000009e, - 0x000300f7,0x000000b6,0x00000000,0x000400fa,0x000000b3,0x000000b5,0x000000c3,0x000200f8, - 0x000000b5,0x00050041,0x00000007,0x000000ba,0x000000a8,0x000000b9,0x0004003d,0x00000006, - 0x000000bb,0x000000ba,0x0003003e,0x000000b7,0x000000bb,0x00050039,0x00000006,0x000000bc, - 0x0000000a,0x000000b7,0x00050041,0x00000007,0x000000bf,0x000000a8,0x000000be,0x0004003d, - 0x00000006,0x000000c0,0x000000bf,0x0003003e,0x000000bd,0x000000c0,0x00050039,0x00000006, - 0x000000c1,0x0000000a,0x000000bd,0x00050085,0x00000006,0x000000c2,0x000000bc,0x000000c1, - 0x0003003e,0x000000b4,0x000000c2,0x000200f9,0x000000b6,0x000200f8,0x000000c3,0x00050041, - 0x00000007,0x000000c5,0x000000a8,0x000000b9,0x0004003d,0x00000006,0x000000c6,0x000000c5, - 0x0003003e,0x000000c4,0x000000c6,0x00050039,0x00000006,0x000000c7,0x0000000d,0x000000c4, - 0x00050041,0x00000007,0x000000c9,0x000000a8,0x000000be,0x0004003d,0x00000006,0x000000ca, - 0x000000c9,0x0003003e,0x000000c8,0x000000ca,0x00050039,0x00000006,0x000000cb,0x0000000d, - 0x000000c8,0x00050085,0x00000006,0x000000cc,0x000000c7,0x000000cb,0x0003003e,0x000000b4, - 0x000000cc,0x000200f9,0x000000b6,0x000200f8,0x000000b6,0x0004003d,0x00000006,0x000000cd, - 0x000000b4,0x0003003e,0x000000b0,0x000000cd,0x0004003d,0x00000006,0x000000ce,0x000000b0, - 0x0004003d,0x00000072,0x000000cf,0x00000074,0x0004003d,0x0000007e,0x000000d0,0x00000088, - 0x0004003d,0x00000066,0x000000d1,0x000000a0,0x0004003d,0x00000066,0x000000d2,0x00000096, - 0x00050050,0x0000007e,0x000000d3,0x000000d1,0x000000d2,0x00050080,0x0000007e,0x000000d4, - 0x000000d0,0x000000d3,0x0004003d,0x0000000f,0x000000d6,0x0000007b,0x0004006e,0x0000007e, - 0x000000d7,0x000000d6,0x00050050,0x0000007e,0x000000d9,0x000000d8,0x000000d8,0x00050082, - 0x0000007e,0x000000da,0x000000d7,0x000000d9,0x0008000c,0x0000007e,0x000000db,0x00000001, - 0x0000002d,0x000000d4,0x000000d5,0x000000da,0x00040064,0x00000071,0x000000dc,0x000000cf, - 0x0007005f,0x00000077,0x000000dd,0x000000dc,0x000000db,0x00000002,0x0000006a,0x0008004f, - 0x00000011,0x000000de,0x000000dd,0x000000dd,0x00000000,0x00000001,0x00000002,0x0005008e, - 0x00000011,0x000000df,0x000000de,0x000000ce,0x0004003d,0x00000011,0x000000e0,0x00000092, - 0x00050081,0x00000011,0x000000e1,0x000000e0,0x000000df,0x0003003e,0x00000092,0x000000e1, - 0x0004003d,0x00000006,0x000000e2,0x000000b0,0x0004003d,0x00000006,0x000000e3,0x00000094, - 0x00050081,0x00000006,0x000000e4,0x000000e3,0x000000e2,0x0003003e,0x00000094,0x000000e4, - 0x000200f9,0x000000a4,0x000200f8,0x000000a4,0x0004003d,0x00000066,0x000000e5,0x000000a0, - 0x00050080,0x00000066,0x000000e6,0x000000e5,0x000000d8,0x0003003e,0x000000a0,0x000000e6, - 0x000200f9,0x000000a1,0x000200f8,0x000000a3,0x000200f9,0x0000009b,0x000200f8,0x0000009b, - 0x0004003d,0x00000066,0x000000e7,0x00000096,0x00050080,0x00000066,0x000000e8,0x000000e7, - 0x000000d8,0x0003003e,0x00000096,0x000000e8,0x000200f9,0x00000098,0x000200f8,0x0000009a, - 0x0004003d,0x00000011,0x000000e9,0x00000092,0x0004003d,0x00000006,0x000000ea,0x00000094, - 0x00060050,0x00000011,0x000000eb,0x000000ea,0x000000ea,0x000000ea,0x00050088,0x00000011, - 0x000000ec,0x000000e9,0x000000eb,0x000200fe,0x000000ec,0x00010038 + 0x00000114,0x00000115,0x00000116,0x000200f8,0x00000115,0x0004003d,0x00000072,0x00000118, + 0x00000074,0x00040064,0x00000071,0x00000119,0x00000118,0x00050067,0x0000007e,0x0000011a, + 0x00000119,0x0000006a,0x0004006f,0x0000000f,0x0000011b,0x0000011a,0x00050050,0x0000000f, + 0x0000011c,0x0000001e,0x0000001e,0x00050088,0x0000000f,0x0000011d,0x0000011c,0x0000011b, + 0x0003003e,0x00000117,0x0000011d,0x0004003d,0x00000072,0x0000011f,0x00000074,0x0004003d, + 0x0000000f,0x00000120,0x00000105,0x00050041,0x00000007,0x00000121,0x00000117,0x000000be, + 0x0004003d,0x00000006,0x00000122,0x00000121,0x0004007f,0x00000006,0x00000123,0x00000122, + 0x00050050,0x0000000f,0x00000124,0x00000025,0x00000123,0x00050081,0x0000000f,0x00000125, + 0x00000120,0x00000124,0x00070058,0x00000077,0x00000126,0x0000011f,0x00000125,0x00000002, + 0x00000025,0x0008004f,0x00000011,0x00000127,0x00000126,0x00000126,0x00000000,0x00000001, + 0x00000002,0x0003003e,0x0000011e,0x00000127,0x0004003d,0x00000072,0x00000129,0x00000074, + 0x0004003d,0x0000000f,0x0000012a,0x00000105,0x00050041,0x00000007,0x0000012b,0x00000117, + 0x000000be,0x0004003d,0x00000006,0x0000012c,0x0000012b,0x00050050,0x0000000f,0x0000012d, + 0x00000025,0x0000012c,0x00050081,0x0000000f,0x0000012e,0x0000012a,0x0000012d,0x00070058, + 0x00000077,0x0000012f,0x00000129,0x0000012e,0x00000002,0x00000025,0x0008004f,0x00000011, + 0x00000130,0x0000012f,0x0000012f,0x00000000,0x00000001,0x00000002,0x0003003e,0x00000128, + 0x00000130,0x0004003d,0x00000072,0x00000132,0x00000074,0x0004003d,0x0000000f,0x00000133, + 0x00000105,0x00050041,0x00000007,0x00000134,0x00000117,0x000000b9,0x0004003d,0x00000006, + 0x00000135,0x00000134,0x0004007f,0x00000006,0x00000136,0x00000135,0x00050050,0x0000000f, + 0x00000137,0x00000136,0x00000025,0x00050081,0x0000000f,0x00000138,0x00000133,0x00000137, + 0x00070058,0x00000077,0x00000139,0x00000132,0x00000138,0x00000002,0x00000025,0x0008004f, + 0x00000011,0x0000013a,0x00000139,0x00000139,0x00000000,0x00000001,0x00000002,0x0003003e, + 0x00000131,0x0000013a,0x0004003d,0x00000072,0x0000013c,0x00000074,0x0004003d,0x0000000f, + 0x0000013d,0x00000105,0x00050041,0x00000007,0x0000013e,0x00000117,0x000000b9,0x0004003d, + 0x00000006,0x0000013f,0x0000013e,0x00050050,0x0000000f,0x00000140,0x0000013f,0x00000025, + 0x00050081,0x0000000f,0x00000141,0x0000013d,0x00000140,0x00070058,0x00000077,0x00000142, + 0x0000013c,0x00000141,0x00000002,0x00000025,0x0008004f,0x00000011,0x00000143,0x00000142, + 0x00000142,0x00000000,0x00000001,0x00000002,0x0003003e,0x0000013b,0x00000143,0x0004003d, + 0x00000011,0x00000145,0x0000011e,0x0004003d,0x00000011,0x00000146,0x00000128,0x0007000c, + 0x00000011,0x00000147,0x00000001,0x00000025,0x00000145,0x00000146,0x0004003d,0x00000011, + 0x00000148,0x00000131,0x0004003d,0x00000011,0x00000149,0x0000013b,0x0007000c,0x00000011, + 0x0000014a,0x00000001,0x00000025,0x00000148,0x00000149,0x0007000c,0x00000011,0x0000014b, + 0x00000001,0x00000025,0x00000147,0x0000014a,0x0003003e,0x00000144,0x0000014b,0x0004003d, + 0x00000011,0x0000014d,0x0000011e,0x0004003d,0x00000011,0x0000014e,0x00000128,0x0007000c, + 0x00000011,0x0000014f,0x00000001,0x00000028,0x0000014d,0x0000014e,0x0004003d,0x00000011, + 0x00000150,0x00000131,0x0004003d,0x00000011,0x00000151,0x0000013b,0x0007000c,0x00000011, + 0x00000152,0x00000001,0x00000028,0x00000150,0x00000151,0x0007000c,0x00000011,0x00000153, + 0x00000001,0x00000028,0x0000014f,0x00000152,0x0003003e,0x0000014c,0x00000153,0x0004003d, + 0x00000011,0x00000155,0x00000144,0x0004003d,0x00000011,0x00000156,0x0000010d,0x0007000c, + 0x00000011,0x00000157,0x00000001,0x00000025,0x00000155,0x00000156,0x0004003d,0x00000011, + 0x00000158,0x0000014c,0x0004003d,0x00000011,0x00000159,0x0000010d,0x0007000c,0x00000011, + 0x0000015a,0x00000001,0x00000028,0x00000158,0x00000159,0x0005008e,0x00000011,0x0000015b, + 0x0000015a,0x0000005e,0x0007000c,0x00000011,0x0000015e,0x00000001,0x00000028,0x0000015b, + 0x0000015d,0x00050088,0x00000011,0x0000015f,0x00000157,0x0000015e,0x0003003e,0x00000154, + 0x0000015f,0x0004003d,0x00000011,0x00000161,0x0000014c,0x0004003d,0x00000011,0x00000162, + 0x0000010d,0x0007000c,0x00000011,0x00000163,0x00000001,0x00000028,0x00000161,0x00000162, + 0x00060050,0x00000011,0x00000164,0x0000001e,0x0000001e,0x0000001e,0x00050083,0x00000011, + 0x00000165,0x00000164,0x00000163,0x0004003d,0x00000011,0x00000166,0x00000144,0x0005008e, + 0x00000011,0x00000167,0x00000166,0x0000005e,0x00060050,0x00000011,0x00000168,0x0000005e, + 0x0000005e,0x0000005e,0x00050083,0x00000011,0x00000169,0x00000167,0x00000168,0x0007000c, + 0x00000011,0x0000016c,0x00000001,0x00000025,0x00000169,0x0000016b,0x00050088,0x00000011, + 0x0000016d,0x00000165,0x0000016c,0x0003003e,0x00000160,0x0000016d,0x0004003d,0x00000011, + 0x0000016f,0x00000154,0x0004007f,0x00000011,0x00000170,0x0000016f,0x0004003d,0x00000011, + 0x00000171,0x00000160,0x0007000c,0x00000011,0x00000172,0x00000001,0x00000028,0x00000170, + 0x00000171,0x0003003e,0x0000016e,0x00000172,0x00050041,0x00000007,0x00000175,0x0000016e, + 0x000000b9,0x0004003d,0x00000006,0x00000176,0x00000175,0x00050041,0x00000007,0x00000177, + 0x0000016e,0x000000be,0x0004003d,0x00000006,0x00000178,0x00000177,0x0007000c,0x00000006, + 0x00000179,0x00000001,0x00000028,0x00000176,0x00000178,0x00050041,0x00000007,0x0000017b, + 0x0000016e,0x0000017a,0x0004003d,0x00000006,0x0000017c,0x0000017b,0x0007000c,0x00000006, + 0x0000017d,0x00000001,0x00000028,0x00000179,0x0000017c,0x0007000c,0x00000006,0x0000017e, + 0x00000001,0x00000025,0x0000017d,0x00000025,0x0007000c,0x00000006,0x0000017f,0x00000001, + 0x00000028,0x00000174,0x0000017e,0x00050041,0x00000111,0x00000180,0x00000069,0x000000d8, + 0x0004003d,0x00000006,0x00000181,0x00000180,0x0008000c,0x00000006,0x00000182,0x00000001, + 0x0000002b,0x00000181,0x00000025,0x0000001e,0x00050085,0x00000006,0x00000183,0x0000017f, + 0x00000182,0x0003003e,0x00000173,0x00000183,0x0004003d,0x00000006,0x00000184,0x00000173, + 0x0004003d,0x00000011,0x00000185,0x0000011e,0x0004003d,0x00000011,0x00000186,0x00000128, + 0x00050081,0x00000011,0x00000187,0x00000185,0x00000186,0x0004003d,0x00000011,0x00000188, + 0x00000131,0x00050081,0x00000011,0x00000189,0x00000187,0x00000188,0x0004003d,0x00000011, + 0x0000018a,0x0000013b,0x00050081,0x00000011,0x0000018b,0x00000189,0x0000018a,0x0005008e, + 0x00000011,0x0000018c,0x0000018b,0x00000184,0x0004003d,0x00000011,0x0000018d,0x0000010d, + 0x00050081,0x00000011,0x0000018e,0x0000018c,0x0000018d,0x0004003d,0x00000006,0x0000018f, + 0x00000173,0x00050085,0x00000006,0x00000190,0x0000005e,0x0000018f,0x00050081,0x00000006, + 0x00000191,0x00000190,0x0000001e,0x00060050,0x00000011,0x00000192,0x00000191,0x00000191, + 0x00000191,0x00050088,0x00000011,0x00000193,0x0000018e,0x00000192,0x00060050,0x00000011, + 0x00000194,0x00000025,0x00000025,0x00000025,0x00060050,0x00000011,0x00000195,0x0000001e, + 0x0000001e,0x0000001e,0x0008000c,0x00000011,0x00000196,0x00000001,0x0000002b,0x00000193, + 0x00000194,0x00000195,0x0003003e,0x0000010d,0x00000196,0x000200f9,0x00000116,0x000200f8, + 0x00000116,0x0004003d,0x000000f8,0x00000197,0x000000fa,0x0004003d,0x0000007e,0x00000198, + 0x000000ef,0x0004003d,0x00000011,0x00000199,0x0000010d,0x00050051,0x00000006,0x0000019a, + 0x00000199,0x00000000,0x00050051,0x00000006,0x0000019b,0x00000199,0x00000001,0x00050051, + 0x00000006,0x0000019c,0x00000199,0x00000002,0x00070050,0x00000077,0x0000019d,0x0000019a, + 0x0000019b,0x0000019c,0x0000001e,0x00040063,0x00000197,0x00000198,0x0000019d,0x000100fd, + 0x00010038,0x00050036,0x00000006,0x0000000a,0x00000000,0x00000008,0x00030037,0x00000007, + 0x00000009,0x000200f8,0x0000000b,0x0004003b,0x00000007,0x00000027,0x00000007,0x0004003d, + 0x00000006,0x00000016,0x00000009,0x0006000c,0x00000006,0x00000017,0x00000001,0x00000004, + 0x00000016,0x0003003e,0x00000009,0x00000017,0x0004003d,0x00000006,0x00000018,0x00000009, + 0x000500b8,0x0000001a,0x0000001b,0x00000018,0x00000019,0x000300f7,0x0000001d,0x00000000, + 0x000400fa,0x0000001b,0x0000001c,0x0000001d,0x000200f8,0x0000001c,0x000200fe,0x0000001e, + 0x000200f8,0x0000001d,0x0004003d,0x00000006,0x00000020,0x00000009,0x000500be,0x0000001a, + 0x00000022,0x00000020,0x00000021,0x000300f7,0x00000024,0x00000000,0x000400fa,0x00000022, + 0x00000023,0x00000024,0x000200f8,0x00000023,0x000200fe,0x00000025,0x000200f8,0x00000024, + 0x0004003d,0x00000006,0x00000029,0x00000009,0x00050085,0x00000006,0x0000002a,0x00000028, + 0x00000029,0x0003003e,0x00000027,0x0000002a,0x0004003d,0x00000006,0x0000002b,0x00000027, + 0x0006000c,0x00000006,0x0000002c,0x00000001,0x0000000d,0x0000002b,0x00050085,0x00000006, + 0x0000002d,0x00000021,0x0000002c,0x0004003d,0x00000006,0x0000002e,0x00000027,0x00050085, + 0x00000006,0x00000030,0x0000002e,0x0000002f,0x0006000c,0x00000006,0x00000031,0x00000001, + 0x0000000d,0x00000030,0x00050085,0x00000006,0x00000032,0x0000002d,0x00000031,0x0004003d, + 0x00000006,0x00000033,0x00000027,0x0004003d,0x00000006,0x00000034,0x00000027,0x00050085, + 0x00000006,0x00000035,0x00000033,0x00000034,0x00050088,0x00000006,0x00000036,0x00000032, + 0x00000035,0x000200fe,0x00000036,0x00010038,0x00050036,0x00000006,0x0000000d,0x00000000, + 0x00000008,0x00030037,0x00000007,0x0000000c,0x000200f8,0x0000000e,0x0004003d,0x00000006, + 0x00000039,0x0000000c,0x0006000c,0x00000006,0x0000003a,0x00000001,0x00000004,0x00000039, + 0x0003003e,0x0000000c,0x0000003a,0x0004003d,0x00000006,0x0000003b,0x0000000c,0x000500b8, + 0x0000001a,0x0000003c,0x0000003b,0x0000001e,0x000300f7,0x0000003e,0x00000000,0x000400fa, + 0x0000003c,0x0000003d,0x0000003e,0x000200f8,0x0000003d,0x0004003d,0x00000006,0x00000040, + 0x0000000c,0x00050085,0x00000006,0x00000041,0x0000003f,0x00000040,0x0004003d,0x00000006, + 0x00000042,0x0000000c,0x00050085,0x00000006,0x00000043,0x00000041,0x00000042,0x0004003d, + 0x00000006,0x00000044,0x0000000c,0x00050085,0x00000006,0x00000045,0x00000043,0x00000044, + 0x0004003d,0x00000006,0x00000047,0x0000000c,0x00050085,0x00000006,0x00000048,0x00000046, + 0x00000047,0x0004003d,0x00000006,0x00000049,0x0000000c,0x00050085,0x00000006,0x0000004a, + 0x00000048,0x00000049,0x00050083,0x00000006,0x0000004b,0x00000045,0x0000004a,0x00050081, + 0x00000006,0x0000004c,0x0000004b,0x0000001e,0x000200fe,0x0000004c,0x000200f8,0x0000003e, + 0x0004003d,0x00000006,0x0000004e,0x0000000c,0x000500b8,0x0000001a,0x0000004f,0x0000004e, + 0x00000021,0x000300f7,0x00000051,0x00000000,0x000400fa,0x0000004f,0x00000050,0x00000051, + 0x000200f8,0x00000050,0x0004003d,0x00000006,0x00000053,0x0000000c,0x00050085,0x00000006, + 0x00000054,0x00000052,0x00000053,0x0004003d,0x00000006,0x00000055,0x0000000c,0x00050085, + 0x00000006,0x00000056,0x00000054,0x00000055,0x0004003d,0x00000006,0x00000057,0x0000000c, + 0x00050085,0x00000006,0x00000058,0x00000056,0x00000057,0x0004003d,0x00000006,0x00000059, + 0x0000000c,0x00050085,0x00000006,0x0000005a,0x00000046,0x00000059,0x0004003d,0x00000006, + 0x0000005b,0x0000000c,0x00050085,0x00000006,0x0000005c,0x0000005a,0x0000005b,0x00050081, + 0x00000006,0x0000005d,0x00000058,0x0000005c,0x0004003d,0x00000006,0x0000005f,0x0000000c, + 0x00050085,0x00000006,0x00000060,0x0000005e,0x0000005f,0x00050083,0x00000006,0x00000061, + 0x0000005d,0x00000060,0x00050081,0x00000006,0x00000062,0x00000061,0x00000021,0x000200fe, + 0x00000062,0x000200f8,0x00000051,0x000200fe,0x00000025,0x00010038,0x00050036,0x00000011, + 0x00000014,0x00000000,0x00000012,0x00030037,0x00000010,0x00000013,0x000200f8,0x00000015, + 0x0004003b,0x00000010,0x0000007b,0x00000007,0x0004003b,0x00000010,0x00000081,0x00000007, + 0x0004003b,0x00000087,0x00000088,0x00000007,0x0004003b,0x00000010,0x0000008c,0x00000007, + 0x0004003b,0x00000091,0x00000092,0x00000007,0x0004003b,0x00000007,0x00000094,0x00000007, + 0x0004003b,0x00000095,0x00000096,0x00000007,0x0004003b,0x00000095,0x000000a0,0x00000007, + 0x0004003b,0x00000010,0x000000a8,0x00000007,0x0004003b,0x00000007,0x000000b0,0x00000007, + 0x0004003b,0x00000007,0x000000b4,0x00000007,0x0004003b,0x00000007,0x000000b7,0x00000007, + 0x0004003b,0x00000007,0x000000bd,0x00000007,0x0004003b,0x00000007,0x000000c4,0x00000007, + 0x0004003b,0x00000007,0x000000c8,0x00000007,0x00050041,0x0000006b,0x0000006c,0x00000069, + 0x0000006a,0x0004003d,0x00000066,0x0000006d,0x0000006c,0x000500aa,0x0000001a,0x0000006e, + 0x0000006d,0x0000006a,0x000300f7,0x00000070,0x00000000,0x000400fa,0x0000006e,0x0000006f, + 0x00000070,0x000200f8,0x0000006f,0x0004003d,0x00000072,0x00000075,0x00000074,0x0004003d, + 0x0000000f,0x00000076,0x00000013,0x00070058,0x00000077,0x00000078,0x00000075,0x00000076, + 0x00000002,0x00000025,0x0008004f,0x00000011,0x00000079,0x00000078,0x00000078,0x00000000, + 0x00000001,0x00000002,0x000200fe,0x00000079,0x000200f8,0x00000070,0x0004003d,0x00000072, + 0x0000007c,0x00000074,0x00040064,0x00000071,0x0000007d,0x0000007c,0x00050067,0x0000007e, + 0x0000007f,0x0000007d,0x0000006a,0x0004006f,0x0000000f,0x00000080,0x0000007f,0x0003003e, + 0x0000007b,0x00000080,0x0004003d,0x0000000f,0x00000082,0x00000013,0x0004003d,0x0000000f, + 0x00000083,0x0000007b,0x00050085,0x0000000f,0x00000084,0x00000082,0x00000083,0x00050050, + 0x0000000f,0x00000085,0x0000002f,0x0000002f,0x00050083,0x0000000f,0x00000086,0x00000084, + 0x00000085,0x0003003e,0x00000081,0x00000086,0x0004003d,0x0000000f,0x00000089,0x00000081, + 0x0006000c,0x0000000f,0x0000008a,0x00000001,0x00000008,0x00000089,0x0004006e,0x0000007e, + 0x0000008b,0x0000008a,0x0003003e,0x00000088,0x0000008b,0x0004003d,0x0000000f,0x0000008d, + 0x00000081,0x0004003d,0x0000007e,0x0000008e,0x00000088,0x0004006f,0x0000000f,0x0000008f, + 0x0000008e,0x00050083,0x0000000f,0x00000090,0x0000008d,0x0000008f,0x0003003e,0x0000008c, + 0x00000090,0x0003003e,0x00000092,0x00000093,0x0003003e,0x00000094,0x00000025,0x0003003e, + 0x00000096,0x00000097,0x000200f9,0x00000098,0x000200f8,0x00000098,0x000400f6,0x0000009a, + 0x0000009b,0x00000000,0x000200f9,0x0000009c,0x000200f8,0x0000009c,0x0004003d,0x00000066, + 0x0000009d,0x00000096,0x000500b3,0x0000001a,0x0000009f,0x0000009d,0x0000009e,0x000400fa, + 0x0000009f,0x00000099,0x0000009a,0x000200f8,0x00000099,0x0003003e,0x000000a0,0x00000097, + 0x000200f9,0x000000a1,0x000200f8,0x000000a1,0x000400f6,0x000000a3,0x000000a4,0x00000000, + 0x000200f9,0x000000a5,0x000200f8,0x000000a5,0x0004003d,0x00000066,0x000000a6,0x000000a0, + 0x000500b3,0x0000001a,0x000000a7,0x000000a6,0x0000009e,0x000400fa,0x000000a7,0x000000a2, + 0x000000a3,0x000200f8,0x000000a2,0x0004003d,0x00000066,0x000000a9,0x000000a0,0x0004006f, + 0x00000006,0x000000aa,0x000000a9,0x0004003d,0x00000066,0x000000ab,0x00000096,0x0004006f, + 0x00000006,0x000000ac,0x000000ab,0x00050050,0x0000000f,0x000000ad,0x000000aa,0x000000ac, + 0x0004003d,0x0000000f,0x000000ae,0x0000008c,0x00050083,0x0000000f,0x000000af,0x000000ad, + 0x000000ae,0x0003003e,0x000000a8,0x000000af,0x00050041,0x0000006b,0x000000b1,0x00000069, + 0x0000006a,0x0004003d,0x00000066,0x000000b2,0x000000b1,0x000500aa,0x0000001a,0x000000b3, + 0x000000b2,0x0000009e,0x000300f7,0x000000b6,0x00000000,0x000400fa,0x000000b3,0x000000b5, + 0x000000c3,0x000200f8,0x000000b5,0x00050041,0x00000007,0x000000ba,0x000000a8,0x000000b9, + 0x0004003d,0x00000006,0x000000bb,0x000000ba,0x0003003e,0x000000b7,0x000000bb,0x00050039, + 0x00000006,0x000000bc,0x0000000a,0x000000b7,0x00050041,0x00000007,0x000000bf,0x000000a8, + 0x000000be,0x0004003d,0x00000006,0x000000c0,0x000000bf,0x0003003e,0x000000bd,0x000000c0, + 0x00050039,0x00000006,0x000000c1,0x0000000a,0x000000bd,0x00050085,0x00000006,0x000000c2, + 0x000000bc,0x000000c1,0x0003003e,0x000000b4,0x000000c2,0x000200f9,0x000000b6,0x000200f8, + 0x000000c3,0x00050041,0x00000007,0x000000c5,0x000000a8,0x000000b9,0x0004003d,0x00000006, + 0x000000c6,0x000000c5,0x0003003e,0x000000c4,0x000000c6,0x00050039,0x00000006,0x000000c7, + 0x0000000d,0x000000c4,0x00050041,0x00000007,0x000000c9,0x000000a8,0x000000be,0x0004003d, + 0x00000006,0x000000ca,0x000000c9,0x0003003e,0x000000c8,0x000000ca,0x00050039,0x00000006, + 0x000000cb,0x0000000d,0x000000c8,0x00050085,0x00000006,0x000000cc,0x000000c7,0x000000cb, + 0x0003003e,0x000000b4,0x000000cc,0x000200f9,0x000000b6,0x000200f8,0x000000b6,0x0004003d, + 0x00000006,0x000000cd,0x000000b4,0x0003003e,0x000000b0,0x000000cd,0x0004003d,0x00000006, + 0x000000ce,0x000000b0,0x0004003d,0x00000072,0x000000cf,0x00000074,0x0004003d,0x0000007e, + 0x000000d0,0x00000088,0x0004003d,0x00000066,0x000000d1,0x000000a0,0x0004003d,0x00000066, + 0x000000d2,0x00000096,0x00050050,0x0000007e,0x000000d3,0x000000d1,0x000000d2,0x00050080, + 0x0000007e,0x000000d4,0x000000d0,0x000000d3,0x0004003d,0x0000000f,0x000000d6,0x0000007b, + 0x0004006e,0x0000007e,0x000000d7,0x000000d6,0x00050050,0x0000007e,0x000000d9,0x000000d8, + 0x000000d8,0x00050082,0x0000007e,0x000000da,0x000000d7,0x000000d9,0x0008000c,0x0000007e, + 0x000000db,0x00000001,0x0000002d,0x000000d4,0x000000d5,0x000000da,0x00040064,0x00000071, + 0x000000dc,0x000000cf,0x0007005f,0x00000077,0x000000dd,0x000000dc,0x000000db,0x00000002, + 0x0000006a,0x0008004f,0x00000011,0x000000de,0x000000dd,0x000000dd,0x00000000,0x00000001, + 0x00000002,0x0005008e,0x00000011,0x000000df,0x000000de,0x000000ce,0x0004003d,0x00000011, + 0x000000e0,0x00000092,0x00050081,0x00000011,0x000000e1,0x000000e0,0x000000df,0x0003003e, + 0x00000092,0x000000e1,0x0004003d,0x00000006,0x000000e2,0x000000b0,0x0004003d,0x00000006, + 0x000000e3,0x00000094,0x00050081,0x00000006,0x000000e4,0x000000e3,0x000000e2,0x0003003e, + 0x00000094,0x000000e4,0x000200f9,0x000000a4,0x000200f8,0x000000a4,0x0004003d,0x00000066, + 0x000000e5,0x000000a0,0x00050080,0x00000066,0x000000e6,0x000000e5,0x000000d8,0x0003003e, + 0x000000a0,0x000000e6,0x000200f9,0x000000a1,0x000200f8,0x000000a3,0x000200f9,0x0000009b, + 0x000200f8,0x0000009b,0x0004003d,0x00000066,0x000000e7,0x00000096,0x00050080,0x00000066, + 0x000000e8,0x000000e7,0x000000d8,0x0003003e,0x00000096,0x000000e8,0x000200f9,0x00000098, + 0x000200f8,0x0000009a,0x0004003d,0x00000011,0x000000e9,0x00000092,0x0004003d,0x00000006, + 0x000000ea,0x00000094,0x00060050,0x00000011,0x000000eb,0x000000ea,0x000000ea,0x000000ea, + 0x00050088,0x00000011,0x000000ec,0x000000e9,0x000000eb,0x000200fe,0x000000ec,0x00010038 }; diff --git a/gpu/vk/vk_dlss.cpp b/gpu/vk/vk_dlss.cpp new file mode 100644 index 00000000..b747242e --- /dev/null +++ b/gpu/vk/vk_dlss.cpp @@ -0,0 +1,764 @@ +// (hookshot DLSS) NVIDIA DLSS Super Resolution in the place of OoT's gameplay TAAU networks (one per render scale). +// +// The game renders its 3D at a lower resolution and resolves it with its own temporal network; with DLSS chosen in the +// Insert menu, that dispatch is recorded as DLSS instead (vk_draw.cpp taau_dlss finds its inputs): +// 1. shaders/dlss_prep.comp: the network's scene colour made linear (RGBA16F), its motion as render pixels in NVIDIA's +// convention (RG16F, the menu's "DLSS motion" choosing the network's sign and units) and its depth (R32F); +// 2. NGX's DLSS evaluate, render size to the network's output size, into an RGBA16F image; +// 3. shaders/dlss_out.comp: that into the network's own output (B10G11R11), which the game then composes as before. +// NGX itself is in hookshot_ngx.dll (gpu/vk/ngx), loaded from beside this DLL. With it missing, a GPU or driver without +// DLSS, inputs it cannot use, or any NGX failure, the network runs as before and the reason is logged once. +// Motion: OoT's network takes the scene colour alone (no motion, depth or jitter), so DLSS's motion is made here: the +// scene's depth target of the render size (vk_draw.cpp end_pass notes every pass's) reprojected by the camera, the +// view-projection found in the network's constant buffers (vk_draw.cpp taau_dlss), this frame's to the last one's. +// That is the camera's motion only: what moves by itself (Link, enemies) gets the motion of what is behind it. +// Env: RX_DLSS=<0-4> the menu's DLSS row at start; RX_DLSS_MOTION=<0-2> its motion row (camera, camera Y flipped, none); +// RX_DLSS_OFF=1 does not load the bridge. RX_DLSS_DEPTH_INVERTED=1 (reversed Z); RX_DLSS_NO_SQRT=1 the colour taken as +// linear already. Diagnostic: RX_DLSS_NGX_LOG=1, RX_DLSS_LOG_CANDIDATES=1, RX_DLSS_LOG_CB=, RX_DLSS_JITTER=,, +// RX_DLSS_MV= (RX_DLSS_MV_SCALE=,), RX_DLSS_COLOR / RX_DLSS_DEPTH=, RX_DLSS_KEYS=. +// The bridge is a Windows DLL (gpu/vk/ngx). Without it, or on a GPU that is not NVIDIA's, nothing here runs. +#include "vk_dlss.h" + +#include +#include +#include +#include +#include +#include + +#include "vk_menu.h" +#include "vk_paths.h" +#include "vk_texture.h" +#include "ngx/hookshot_ngx.h" +#include "shaders/dlss_prep_comp_spv.h" +#include "shaders/dlss_out_comp_spv.h" + +// Linux's Win32 shim has LoadLibraryA. A full path loads that file; NGX then looks beside it for nvngx_dlss.dll. +#ifndef LOAD_WITH_ALTERED_SEARCH_PATH +#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 +static inline HMODULE LoadLibraryExA(const char* name, HANDLE, DWORD) { return LoadLibraryA(name); } +#endif + +namespace mkwvk { +namespace { + +struct Ngx { + HMODULE dll = nullptr; + PFN_hsngx_required_extensions required = nullptr; + PFN_hsngx_init init = nullptr; + PFN_hsngx_create create = nullptr; + PFN_hsngx_evaluate evaluate = nullptr; + PFN_hsngx_release release = nullptr; + PFN_hsngx_shutdown shutdown = nullptr; + PFN_hsngx_vram_bytes vram = nullptr; // (build 7, optional: an older hookshot_ngx.dll has none) + bool tried = false; +} ngx; + +enum class State { Untried, NoDll, NotNvidia, Unsupported, Running, Failed }; +std::atomic state{State::Untried}; +std::atomic no_motion_seen{false}; +std::mutex mu; + +void ngx_log(const char* line) { logf("%s", line); } + +/// The folder hookshot_vk.dll is in, with its separator ('\\' or '/'), or "" when not known +std::string own_dir() { + HMODULE self = nullptr; + char path[MAX_PATH]; + if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)(void*)&own_dir, &self)) + return ""; + const DWORD n = GetModuleFileNameA(self, path, MAX_PATH); + if (!n || n >= MAX_PATH) return ""; + std::string s(path, n); + const size_t at = s.find_last_of("\\/"); + return at == std::string::npos ? "" : s.substr(0, at + 1); +} + +/// hookshot_ngx.dll from beside this DLL, once. False: not there (the reason logged) +bool load_ngx() { + if (ngx.tried) return ngx.dll != nullptr; + ngx.tried = true; + const std::string path = own_dir() + "hookshot_ngx.dll"; + ngx.dll = LoadLibraryExA(path.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!ngx.dll) { + // (quiet: DLSS is off by default. The menu note and dlss_frame_status say why once it is turned on) + state = State::NoDll; + return false; + } + const auto version = (PFN_hsngx_interface_version)(void*)GetProcAddress(ngx.dll, "hsngx_interface_version"); + ngx.required = (PFN_hsngx_required_extensions)(void*)GetProcAddress(ngx.dll, "hsngx_required_extensions"); + ngx.init = (PFN_hsngx_init)(void*)GetProcAddress(ngx.dll, "hsngx_init"); + ngx.create = (PFN_hsngx_create)(void*)GetProcAddress(ngx.dll, "hsngx_create"); + ngx.evaluate = (PFN_hsngx_evaluate)(void*)GetProcAddress(ngx.dll, "hsngx_evaluate"); + ngx.release = (PFN_hsngx_release)(void*)GetProcAddress(ngx.dll, "hsngx_release"); + ngx.shutdown = (PFN_hsngx_shutdown)(void*)GetProcAddress(ngx.dll, "hsngx_shutdown"); + ngx.vram = (PFN_hsngx_vram_bytes)(void*)GetProcAddress(ngx.dll, "hsngx_vram_bytes"); + if (!version || version() != HSNGX_INTERFACE_VERSION || !ngx.required || !ngx.init || !ngx.create || !ngx.evaluate || + !ngx.release || !ngx.shutdown) { + logf("dlss: %s is not the version this renderer was built with: DLSS is off", path.c_str()); + FreeLibrary(ngx.dll); + ngx.dll = nullptr; + state = State::NoDll; + return false; + } + logf("dlss: %s loaded", path.c_str()); + return true; +} + +void add_extensions(uint32_t n, const char** names, const std::vector& have, + std::vector& want, const char* what) { + for (uint32_t i = 0; i < n; i++) { + if (!names || !names[i]) continue; + // (core in Vulkan 1.2, whose feature the renderer turns on: the EXT beside it is not allowed, the KHR is there already) + if (!strcmp(names[i], "VK_EXT_buffer_device_address") || !strcmp(names[i], "VK_KHR_buffer_device_address")) continue; + bool present = false, already = false; + for (const auto& e : have) present |= !strcmp(e.extensionName, names[i]); + for (const char* w : want) already |= !strcmp(w, names[i]); + if (present && !already) want.push_back(names[i]); + logf("dlss: NGX %s extension %s%s", what, names[i], !present ? ": not available" : already ? ": on already" : ": on"); + } +} + +// ---- the images and pipelines of the passes around NGX + +struct Image { + VkImage image = VK_NULL_HANDLE; + VmaAllocation mem = nullptr; + VkImageView view = VK_NULL_HANDLE; + VkFormat format = VK_FORMAT_UNDEFINED; + uint32_t w = 0, h = 0; +}; +struct Old { Image im; void* feature; uint64_t frame; }; +std::vector old; // (given back some presents later, when no work in flight can use them) + +struct Pass { + VkPipeline pipe = VK_NULL_HANDLE; + VkPipelineLayout layout = VK_NULL_HANDLE; +}; +Pass prep, out_pass; +VkSampler nearest = VK_NULL_HANDLE, linear = VK_NULL_HANDLE; +bool passes_tried = false, passes_ok = false; + +Pass make_pass(const uint32_t* code, size_t size, uint32_t samplers, uint32_t storages, uint32_t push) { + Pass p; + std::vector b; + for (uint32_t i = 0; i < samplers + storages; i++) + b.push_back({i, i < samplers ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, + VK_SHADER_STAGE_COMPUTE_BIT, nullptr}); + VkDescriptorSetLayoutCreateInfo li{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; + li.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR; + li.bindingCount = (uint32_t)b.size(), li.pBindings = b.data(); + VkDescriptorSetLayout set{}; + if (vkCreateDescriptorSetLayout(dev.device, &li, nullptr, &set) != VK_SUCCESS) return p; + const VkPushConstantRange pr{VK_SHADER_STAGE_COMPUTE_BIT, 0, push}; + VkPipelineLayoutCreateInfo pi{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; + pi.setLayoutCount = 1, pi.pSetLayouts = &set, pi.pushConstantRangeCount = 1, pi.pPushConstantRanges = ≺ + if (vkCreatePipelineLayout(dev.device, &pi, nullptr, &p.layout) != VK_SUCCESS) return p; + VkShaderModuleCreateInfo mi{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; + mi.codeSize = size, mi.pCode = code; + VkShaderModule m{}; + if (vkCreateShaderModule(dev.device, &mi, nullptr, &m) != VK_SUCCESS) return p; + VkComputePipelineCreateInfo ci{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; + ci.stage = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, VK_SHADER_STAGE_COMPUTE_BIT, m, "main", nullptr}; + ci.layout = p.layout; + if (vkCreateComputePipelines(dev.device, VK_NULL_HANDLE, 1, &ci, nullptr, &p.pipe) != VK_SUCCESS) p.pipe = VK_NULL_HANDLE; + vkDestroyShaderModule(dev.device, m, nullptr); + return p; +} + +bool make_passes() { + if (passes_tried) return passes_ok; + passes_tried = true; + prep = make_pass(DLSS_PREP_COMP_SPV, sizeof DLSS_PREP_COMP_SPV, 3, 3, 96); + out_pass = make_pass(DLSS_OUT_COMP_SPV, sizeof DLSS_OUT_COMP_SPV, 1, 1, 8); + VkSamplerCreateInfo si{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; + si.addressModeU = si.addressModeV = si.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + si.magFilter = si.minFilter = VK_FILTER_NEAREST; + vkCreateSampler(dev.device, &si, nullptr, &nearest); + si.magFilter = si.minFilter = VK_FILTER_LINEAR; + vkCreateSampler(dev.device, &si, nullptr, &linear); + passes_ok = prep.pipe && out_pass.pipe && nearest && linear; + if (!passes_ok) logf("dlss: the passes around DLSS were not made: DLSS is off"); + return passes_ok; +} + +void barrier(VkCommandBuffer cmd) { + VkMemoryBarrier2 mb{VK_STRUCTURE_TYPE_MEMORY_BARRIER_2}; + mb.srcStageMask = mb.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; + mb.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; + mb.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT; + VkDependencyInfo di{VK_STRUCTURE_TYPE_DEPENDENCY_INFO}; + di.memoryBarrierCount = 1, di.pMemoryBarriers = &mb; + vkCmdPipelineBarrier2(cmd, &di); +} + +void retire(Image& im, void* feature = nullptr) { + if (im.image || feature) old.push_back({im, feature, texture_frame_no()}); + im = Image{}; +} +void collect() { + for (size_t i = 0; i < old.size();) { + if (texture_frame_no() - old[i].frame < 8) { i++; continue; } + if (old[i].im.view) vkDestroyImageView(dev.device, old[i].im.view, nullptr); + if (old[i].im.image) destroy_image(old[i].im.image, old[i].im.mem); + if (old[i].feature) ngx.release(old[i].feature); + old.erase(old.begin() + (ptrdiff_t)i); + } +} + +/// im at w x h in format (made again, the old one retired, when either differs). False: not made +bool ensure(VkCommandBuffer cmd, Image& im, VkFormat format, uint32_t w, uint32_t h) { + if (im.image && im.w == w && im.h == h && im.format == format) return true; + retire(im); + VkImageCreateInfo ic{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + ic.imageType = VK_IMAGE_TYPE_2D, ic.format = format; + ic.extent = {w, h, 1}, ic.mipLevels = 1, ic.arrayLayers = 1, ic.samples = VK_SAMPLE_COUNT_1_BIT; + ic.tiling = VK_IMAGE_TILING_OPTIMAL, ic.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + if (!create_image(ic, im.image, im.mem)) { im = Image{}; return false; } + VkImageViewCreateInfo vc{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + vc.image = im.image, vc.viewType = VK_IMAGE_VIEW_TYPE_2D, vc.format = format; + vc.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + if (vkCreateImageView(dev.device, &vc, nullptr, &im.view) != VK_SUCCESS) { + destroy_image(im.image, im.mem); + im = Image{}; + return false; + } + im.format = format, im.w = w, im.h = h; + VkImageMemoryBarrier2 ib{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2}; // (UNDEFINED -> GENERAL, once) + ib.srcStageMask = VK_PIPELINE_STAGE_2_NONE, ib.srcAccessMask = VK_ACCESS_2_NONE; + ib.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; + ib.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT; + ib.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, ib.newLayout = VK_IMAGE_LAYOUT_GENERAL; + ib.srcQueueFamilyIndex = ib.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ib.image = im.image, ib.subresourceRange = vc.subresourceRange; + VkDependencyInfo di{VK_STRUCTURE_TYPE_DEPENDENCY_INFO}; + di.imageMemoryBarrierCount = 1, di.pImageMemoryBarriers = &ib; + vkCmdPipelineBarrier2(cmd, &di); + return true; +} + +HsNgxImage ngx_image(const Image& im) { + return HsNgxImage{(uint64_t)im.image, (uint64_t)im.view, (uint32_t)im.format, im.w, im.h, VK_IMAGE_ASPECT_COLOR_BIT}; +} + +// ---- the menu's choices + +int choice() { return dlss_choice(); } +int motion_choice() { + static const int env = getenv("RX_DLSS_MOTION") ? atoi(getenv("RX_DLSS_MOTION")) : -1; + const int m = env >= 0 ? env : opts.dlss_motion; + return m == 6 ? 2 : m < 0 || m > 2 ? 1 : m; // (0 camera, 1 camera with Y flipped (OoT's), 2 none; build 1's 0-6, 6 none) +} +/// NGX's render preset for the DLSS row: 1 CNN (E), 2 transformer (K), 3 second transformer (M), 4 NVIDIA's default +int preset_of(int c) { + switch (c) { + case 1: return 5; // NVSDK_NGX_DLSS_Hint_Render_Preset_E: the last CNN model + case 2: return 11; // _K: DLSS 4's transformer + case 3: return 13; // _M: DLSS 4.5's second-generation transformer + default: return opts.dlss_quality == 5 ? 11 : 0; // _Default (DLAA: _K, the transformer, as NVIDIA's own default for it) + } +} +/// The quality mode NGX is told, from the ratio of the sizes (the sizes themselves are given; the Resolution row sets them) +int quality_of(uint32_t rw, uint32_t ow) { + static const int asked[] = {-1, 2, 1, 0, 3, 5}; // (the DLSS quality row: Quality, Balanced, Performance, Ultra Performance, DLAA) + if (opts.dlss_quality > 0 && opts.dlss_quality <= 5) return asked[opts.dlss_quality]; + const double r = ow ? (double)rw / ow : 1.0; + if (r >= 0.99) return 5; // DLAA + if (r >= 0.62) return 2; // MaxQuality (UltraQuality, 4, is refused by DLSS SR: FAIL_UnsupportedParameter) + if (r >= 0.54) return 1; // Balanced + if (r >= 0.42) return 0; // MaxPerf + return 3; // UltraPerformance +} +/// (RX_DLSS_MV, a motion texture named by hand) its motion to render pixels, current to previous: RX_DLSS_MV_SCALE=, +/// multiplies UV (default -1,-1: a velocity buffer of current - previous in UV) +void mv_texture_scale(uint32_t rw, uint32_t rh, float& sx, float& sy) { + float x = -1.0f, y = -1.0f; + if (const char* e = getenv("RX_DLSS_MV_SCALE")) sscanf(e, "%f,%f", &x, &y); + sx = x * (float)rw, sy = y * (float)rh; +} + +// ---- the scene depth: the passes' depth targets (vk_draw.cpp end_pass), per present + +struct Cand { + VkImage image = VK_NULL_HANDLE; + VkFormat format = VK_FORMAT_UNDEFINED; + VkImageAspectFlags aspect = 0; + uint32_t id = 0, level = 0, layer = 0, w = 0, h = 0; + int draws = 0; +}; +std::mutex cand_mu; +uint64_t cand_frame = 0; +std::vector cands, cands_prev; // (this present's, and the one before's) +std::atomic log_cands_until{0}; +struct DepthView { VkImage image; uint32_t id, level, layer; VkFormat format; VkImageView view; uint64_t used; }; +std::vector depth_views; + +bool candidates_env() { + static const bool on = getenv("RX_DLSS_LOG_CANDIDATES") && atoi(getenv("RX_DLSS_LOG_CANDIDATES")); + return on; +} + +/// A depth-aspect 2D view of a candidate (kept per image; ones unused for a while are dropped) +VkImageView depth_view_of(const Cand& c) { + static std::mutex views_mu; + std::lock_guard lk(views_mu); + const uint64_t frame = texture_frame_no(); + for (auto& v : depth_views) + if (v.image == c.image && v.id == c.id && v.level == c.level && v.layer == c.layer && v.format == c.format) { + v.used = frame; + return v.view; + } + for (size_t i = 0; i < depth_views.size();) // (dropped long after their last use: no work in flight has them) + if (frame - depth_views[i].used > 120) { + vkDestroyImageView(dev.device, depth_views[i].view, nullptr); + depth_views.erase(depth_views.begin() + (ptrdiff_t)i); + } else i++; + VkImageViewCreateInfo vc{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + vc.image = c.image, vc.viewType = VK_IMAGE_VIEW_TYPE_2D, vc.format = c.format; + vc.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, c.level, 1, c.layer, 1}; + VkImageView view = VK_NULL_HANDLE; + if (vkCreateImageView(dev.device, &vc, nullptr, &view) != VK_SUCCESS) return VK_NULL_HANDLE; + depth_views.push_back({c.image, c.id, c.level, c.layer, c.format, view, frame}); + return view; +} + + +std::atomic input_w{0}, input_h{0}; + +// ---- the camera: this present's view-projection and the last one's, made one reprojection (current NDC to previous) + +bool invert4(const double m[16], double out[16]) { + double inv[16]; + inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; + inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; + inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; + inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; + inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; + inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; + inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; + inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; + inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; + inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; + inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; + inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; + inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; + inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; + inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; + inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; + const double det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; + if (!std::isfinite(det) || std::fabs(det) < 1e-12) return false; + for (int i = 0; i < 16; i++) out[i] = inv[i] / det; + return true; +} + +// ---- DLSS's own state +struct Run { + Image color, mv, depth, output; + void* feature = nullptr; + uint32_t rw = 0, rh = 0, ow = 0, oh = 0; + int preset = -1, flags = -1, motion = -1, quality = -1; + uint64_t last_frame = 0; + std::chrono::steady_clock::time_point last_time{}; + int failures = 0; + double prev_vp[16] = {}; // (the camera of the last frame DLSS ran in, and that frame) + uint64_t prev_vp_frame = 0; + std::string said; // (the last 'dlss: quality' line: logged again on every change) +} run; +std::atomic missing{0}; // (what the last frame lacked, for the menu: 1 depth, 2 the camera) + +} // namespace + +void dlss_instance_extensions(const std::vector& have, std::vector& want) { + if (getenv("RX_DLSS_OFF") || !load_ngx()) return; + uint32_t ni = 0, nd = 0; + const char** ie = nullptr; + const char** de = nullptr; + if (ngx.required(&ni, &ie, &nd, &de) == 0) add_extensions(ni, ie, have, want, "instance"); +} + +void dlss_device_extensions(const std::vector& have, std::vector& want) { + if (!ngx.dll) return; + if (dev.props.vendorID != 0x10DE) { + logf("dlss: not an NVIDIA GPU (vendor %#x): DLSS is off", dev.props.vendorID); + state = State::NotNvidia; + return; + } + uint32_t ni = 0, nd = 0; + const char** ie = nullptr; + const char** de = nullptr; + if (ngx.required(&ni, &ie, &nd, &de) == 0) add_extensions(nd, de, have, want, "device"); +} + +void dlss_device_extensions_refused() { state = State::Unsupported; } + +void dlss_shutdown() { + std::lock_guard lk(mu); + if (state != State::Running) return; + if (run.feature) ngx.release(run.feature); + run.feature = nullptr; + for (auto& o : old) + if (o.feature) ngx.release(o.feature), o.feature = nullptr; + for (auto& v : depth_views) if (v.view) vkDestroyImageView(dev.device, v.view, nullptr); + depth_views.clear(); + ngx.shutdown(); + state = State::Failed; +} + +bool dlss_wanted() { + if (choice() <= 0) return false; + const State s = state.load(); + return s == State::Untried || s == State::Running; +} + +const char* dlss_state() { + if (choice() <= 0) return ""; + switch (state.load()) { + case State::NoDll: return " (no hookshot_ngx.dll)"; + case State::NotNvidia: return " (needs an NVIDIA RTX GPU)"; + case State::Unsupported: return " (not supported here)"; + case State::Failed: return " (failed, see the log)"; + default: break; + } + const int m = missing.load(); + return m == 1 ? " (no depth found)" : m == 2 ? " (no camera found)" : m == 3 ? " (no depth or camera found)" : ""; +} + +// ---- (build 5) what the log says about the rows and the TAAU +std::atomic taau_frame{0}, taau_key{0}; +std::atomic taau_w{0}, taau_h{0}; +void dlss_taau_seen(uint64_t key, uint32_t w, uint32_t h) { + taau_frame = texture_frame_no(), taau_key = key, taau_w = w, taau_h = h; +} +const char* hookshot_build_version(); // (vk_menu.cpp) +void dlss_frame_status() { + static std::string said; + static uint64_t chosen_at = 0, told_missing = 0; + static const char* rows[] = {"Off", "CNN (DLSS 3)", "Transformer (DLSS 4)", "Transformer 2 (DLSS 4.5)", "NVIDIA default"}; + static const char* qs[] = {"Auto (from Resolution)", "Quality", "Balanced", "Performance", "Ultra Performance", "DLAA"}; + static const char* ms[] = {"Camera", "Camera, Y flipped", "None"}; + const uint64_t frame = texture_frame_no(); + const int c = choice(), q = opts.dlss_quality, m = motion_choice(); + const int k = dlss_quality_twelfths(); + char line[400]; + snprintf(line, sizeof line, "dlss: settings (%s): DLSS %s, DLSS quality %s, DLSS motion %s, Resolution row %d%s; the 3D %s; " + "Sharpness %d%%, Upscaling %d", hookshot_build_version(), c >= 0 && c <= 4 ? rows[c] : "?", q >= 0 && q <= 5 ? qs[q] : "?", m >= 0 && m <= 2 ? ms[m] : "?", + opts.resolution, k ? " (set by DLSS quality)" : "", + k ? (std::to_string(k) + "/12 of the output").c_str() : resolution_auto() ? "dynamic (Auto)" : "by the Resolution row", + opts.sharpness, opts.upscale); + if (said != line && (c > 0 || !said.empty())) { // (DLSS off, and it has not been on: the log stays as it was) + said = line; + chosen_at = frame, told_missing = 0; + logf("%s", line); + if (c > 0 && state.load() == State::NoDll) + logf("dlss: hookshot_ngx.dll is not beside the renderer: the game's own upscaler stays"); + } + const uint64_t seen = taau_frame.load(); + static uint64_t reported = ~0ull; + if (c > 0 && seen > chosen_at && reported != chosen_at) { // (the first TAAU network after a change of the rows) + reported = chosen_at; + logf("dlss: hook point after this setting: TAAU network %016llx, its input %ux%u", (unsigned long long)taau_key.load(), + taau_w.load(), taau_h.load()); + } + { // (build 7) every 300 presents while DLSS runs: what it holds in VRAM, and the driver's use and allowance + static uint64_t presents = 0; + std::unique_lock lk(mu, std::defer_lock); // (DLSS's state as the recording thread leaves it; busy: the next present) + if (state.load() == State::Running && ++presents >= 300 && lk.try_lock()) { + presents = 0; + const auto mb = [](const Image& im, uint32_t bpp) { return (double)im.w * im.h * bpp / (1024.0 * 1024.0); }; + const double ours = mb(run.color, 8) + mb(run.mv, 4) + mb(run.depth, 4) + mb(run.output, 8); + const unsigned long long nb = ngx.vram ? ngx.vram() : 0; + uint64_t used = 0, allowed = 0; + const bool known = vram_usage(&used, &allowed); + char ngx_mb[32]; + if (nb) snprintf(ngx_mb, sizeof ngx_mb, "%.0f MB", nb / (1024.0 * 1024.0)); + else snprintf(ngx_mb, sizeof ngx_mb, "not known"); + char drv[80]; + if (known) snprintf(drv, sizeof drv, "%llu MB used of %llu MB allowed", (unsigned long long)(used >> 20), (unsigned long long)(allowed >> 20)); + else snprintf(drv, sizeof drv, "not known"); + logf("dlss: VRAM: DLSS's own images %.0f MB (%ux%u in, %ux%u out), NGX %s, %zu depth views, %zu retired; the driver: %s%s", + ours, run.rw, run.rh, run.ow, run.oh, ngx_mb, depth_views.size(), old.size(), drv, + known && used > allowed ? " - OVER: the driver pages to system memory, the GPU slows down" : ""); + } + } + if (c <= 0) return; + const State st = state.load(); + // (chosen, and no TAAU network DLSS can take for 10 s of presents: why, once per change of the rows) + if (!told_missing && frame - chosen_at > 600 && (!seen || frame - seen > 600)) { + told_missing = frame; + logf("dlss: DLSS chosen but not running: %s", st == State::NoDll ? "hookshot_ngx.dll is missing" + : st == State::NotNvidia ? "not an NVIDIA GPU" + : st == State::Unsupported ? "NGX / the driver refused DLSS" + : st == State::Failed ? "it failed (see the lines above)" + : "no TAAU network ran for 10 s (a menu, a movie, or a network of a shape " + "it does not know: the 'vk: network' lines list them)"); + } else if (told_missing && seen && seen > told_missing) { + told_missing = 0; // (build 7: chosen_at kept, so the hook-point line is not said twice) + logf("dlss: TAAU network %016llx found again (%ux%u)", (unsigned long long)taau_key.load(), taau_w.load(), taau_h.load()); + } +} + +bool dlss_input_size(uint32_t& w, uint32_t& h) { + w = input_w.load(), h = input_h.load(); + return state == State::Running && choice() > 0 && w && texture_frame_no() - run.last_frame < 30; +} + +bool dlss_log_candidates() { + const uint64_t f = texture_frame_no(), until = log_cands_until.load(); + return candidates_env() && until && f + 3 > until && f <= until; +} +void dlss_log_candidates_from(uint64_t frame) { + if (!candidates_env()) return; + log_cands_until = frame + 3; // (the three whole frames after the first TAAU) + logf("dlss candidates: every pass of frames %llu..%llu follows (RX_DLSS_LOG_CANDIDATES)", (unsigned long long)frame + 1, + (unsigned long long)frame + 3); +} + +void dlss_note_pass(Texture* depth, uint32_t width, uint32_t height, int draws, int colours, VkFormat colour0, uint64_t prog) { + const bool want = choice() > 0 && (state == State::Untried || state == State::Running); + const bool log = dlss_log_candidates(); + if (!want && !log) return; + if (log) + logf("dlss candidates: frame %llu pass %ux%u, %d colour target(s) (first VkFormat %d), depth %s %ux%u VkFormat %d id %u, " + "%d draws, program %016llx", (unsigned long long)texture_frame_no(), width, height, colours, (int)colour0, + depth ? "yes" : "no", depth ? depth->width : 0, depth ? depth->height : 0, depth ? (int)depth->format : 0, + depth ? depth->id : 0, draws, (unsigned long long)prog); + if (!depth || draws <= 0 || !depth->image || !(depth->aspect & VK_IMAGE_ASPECT_DEPTH_BIT) || !(depth->usage & VK_IMAGE_USAGE_SAMPLED_BIT) || depth->samples != VK_SAMPLE_COUNT_1_BIT || + depth->type != VK_IMAGE_TYPE_2D) + return; + std::lock_guard lk(cand_mu); + const uint64_t frame = texture_frame_no(); + if (frame != cand_frame) { + cands_prev = frame == cand_frame + 1 ? std::move(cands) : std::vector{}; + cands.clear(); + cand_frame = frame; + } + for (auto& c : cands) // (one target drawn into by several passes: their draws together) + if (c.image == depth->image && c.id == depth->id) { c.draws += draws; return; } + Cand c; + c.image = depth->image, c.format = depth->format, c.aspect = depth->aspect, c.id = depth->id; + c.w = width, c.h = height, c.draws = draws; + if (cands.size() < 64) cands.push_back(c); +} + +VkImageView dlss_find_depth(uint32_t w, uint32_t h, uint32_t* found_w, uint32_t* found_h) { + Cand best; + { + std::lock_guard lk(cand_mu); + const uint64_t frame = texture_frame_no(); + const std::vector* lists[2] = {frame == cand_frame ? &cands : nullptr, + frame == cand_frame ? &cands_prev : frame == cand_frame + 1 ? &cands : nullptr}; + for (int pass = 0; pass < 2 && !best.image; pass++) // (this present's first, then the last one's) + for (int shape = 0; shape < 2 && !best.image; shape++) // (the render size, then its shape) + if (lists[pass]) + for (const Cand& c : *lists[pass]) { + const bool ok = shape == 0 ? c.w == w && c.h == h + : c.w >= w / 2 && std::fabs((double)c.w * h - (double)c.h * w) <= 0.01 * (double)c.h * w; + if (ok && c.draws > best.draws) best = c; + } + } + if (!best.image) return VK_NULL_HANDLE; + if (found_w) *found_w = best.w; + if (found_h) *found_h = best.h; + return depth_view_of(best); +} + +bool dlss_run(VkCommandBuffer cmd, const DlssInputs& in) { + std::lock_guard lk(mu); + if (!dlss_wanted() || !in.color_view || !in.out_view || in.render_w < 16 || in.render_h < 16 || in.out_w < 32 || + in.out_h < 32) + return false; + if (state == State::Untried) { + if (!ngx.dll || dev.props.vendorID != 0x10DE) { + state = ngx.dll ? State::NotNvidia : State::NoDll; + logf("dlss: %s: the game's own upscaler stays", + ngx.dll ? "not an NVIDIA GPU" : "hookshot_ngx.dll is not beside the renderer"); + return false; + } + const std::string dir = own_dir(); + std::wstring wdir(dir.begin(), dir.end()); // (the game folder's path; non-ASCII names: NGX gets none, its default) + bool ascii = true; + for (char ch : dir) ascii &= (unsigned char)ch < 0x80; + if (!wdir.empty() && (wdir.back() == L'\\' || wdir.back() == L'/')) wdir.pop_back(); + const int r = ngx.init((void*)dev.instance, (void*)dev.physical, (void*)dev.device, (void*)instance_proc(), + (void*)vkGetDeviceProcAddr, ascii && !wdir.empty() ? wdir.c_str() : nullptr, ngx_log, + getenv("RX_DLSS_NGX_LOG") ? 1 : 0); + if (r != 1) { + state = State::Unsupported; + logf("dlss: NGX did not start (%d): the game's own upscaler stays", r); + return false; + } + if (!make_passes()) { + state = State::Failed; + return false; + } + state = State::Running; + logf("dlss: running"); + } + collect(); + const int motion = motion_choice(); // 0 camera, 1 camera with Y flipped, 2 none + const bool have_mv = in.mv_view && motion != 2; // (a motion texture named by hand, RX_DLSS_MV) + const bool camera = !have_mv && motion != 2 && in.depth_view && in.camera; + missing = motion == 2 || have_mv ? 0 : (in.depth_view ? 0 : 1) | (in.camera ? 0 : 2); + if (!have_mv && !camera && motion != 2) { + // (without motion DLSS smears whatever moves: it runs only when the motion row says None) + if (!no_motion_seen.exchange(true)) + logf("dlss: %s: the game's own upscaler stays (DLSS motion: None runs DLSS without motion)", + !in.depth_view && !in.camera ? "no scene depth and no camera found" + : !in.depth_view ? "no scene depth found for the render size" + : "no camera (view-projection) found in the TAAU's constants"); + return false; + } + const uint32_t rw = in.render_w, rh = in.render_h, ow = in.out_w, oh = in.out_h; + static const bool inverted = getenv("RX_DLSS_DEPTH_INVERTED") && atoi(getenv("RX_DLSS_DEPTH_INVERTED")); + const int flags = (1 << 0) | (1 << 1) | (1 << 6) | (inverted ? 1 << 3 : 0); // IsHDR | MVLowRes | AutoExposure | DepthInverted + const int preset = preset_of(choice()); + if (!ensure(cmd, run.color, VK_FORMAT_R16G16B16A16_SFLOAT, rw, rh) || !ensure(cmd, run.mv, VK_FORMAT_R16G16_SFLOAT, rw, rh) || + !ensure(cmd, run.depth, VK_FORMAT_R32_SFLOAT, rw, rh) || !ensure(cmd, run.output, VK_FORMAT_R16G16B16A16_SFLOAT, ow, oh)) { + logf("dlss: its images (%ux%u, %ux%u) were not made", rw, rh, ow, oh); + return false; + } + bool reset = false; + if (!run.feature || run.rw != rw || run.rh != rh || run.ow != ow || run.oh != oh || run.preset != preset || run.flags != flags || + run.quality != quality_of(rw, ow)) { + if (run.feature) { + Image none; + retire(none, run.feature); + } + run.feature = ngx.create((void*)cmd, rw, rh, ow, oh, quality_of(rw, ow), preset, flags); + run.rw = rw, run.rh = rh, run.ow = ow, run.oh = oh, run.preset = preset, run.flags = flags, run.quality = quality_of(rw, ow); + if (!run.feature) { + if (++run.failures >= 3) { + state = State::Failed; + logf("dlss: DLSS could not be made three times: off until the next start"); + } + return false; + } + reset = true; + } + { // what the quality row asked and what this network's input actually is, on every change + static const char* names[] = {"Auto (from Resolution)", "Quality", "Balanced", "Performance", "Ultra Performance", "DLAA"}; + const int q = opts.dlss_quality >= 0 && opts.dlss_quality <= 5 ? opts.dlss_quality : 0; + const int k = dlss_quality_twelfths(); + int twelfths = k; + if (!twelfths && !resolution_auto()) { + const int s = resolution_scale(); + if (s > 0) twelfths = std::clamp((int)(s * 12.0 / 10000.0 + 0.5), 1, 12); + } + char asked[96], line[400]; + if (twelfths) { + const uint32_t ew = (ow * (uint32_t)twelfths + 11) / 12, eh = oh * (uint32_t)twelfths / 12; + snprintf(asked, sizeof asked, "%ux%u (%d/12 of the output)", ew, eh, twelfths); + } else if (resolution_auto()) snprintf(asked, sizeof asked, "the game's own size (Resolution Auto: dynamic)"); + else snprintf(asked, sizeof asked, "the game's own size (by the Resolution row)"); + snprintf(line, sizeof line, "dlss: quality %s%s: the 3D is drawn at %s; DLSS input %ux%u (the TAAU network's own input) -> " + "%ux%u, NGX mode %d, preset %d", names[q], k || !twelfths ? "" : " (by the Resolution row)", asked, rw, rh, ow, oh, + run.quality, preset); + if (run.said != line) { + run.said = line; + logf("%s", line); + } + input_w = rw, input_h = rh; + } + const uint64_t frame = texture_frame_no(); + if (!run.last_frame || frame - run.last_frame > 3 || run.motion != motion) reset = true; // (a pause, a menu, a new motion) + // the camera's reprojection: current NDC (GL, z -1..1) to the last frame's, prev_vp * inverse(vp) + float reproj[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; // (column-major, for the shader) + if (in.camera) { + double vp[16], inv[16]; + for (int i = 0; i < 16; i++) vp[i] = in.view_proj[i]; + const bool have_prev = run.prev_vp_frame && frame - run.prev_vp_frame <= 3 && !reset; + if (camera && have_prev && invert4(vp, inv)) { + double m[16]; // (row-major) + for (int r = 0; r < 4; r++) + for (int c = 0; c < 4; c++) { + double v = 0; + for (int k = 0; k < 4; k++) v += run.prev_vp[r * 4 + k] * inv[k * 4 + c]; + m[r * 4 + c] = v; + } + bool finite = true; + for (double v : m) finite &= std::isfinite(v); + if (finite) + for (int r = 0; r < 4; r++) + for (int c = 0; c < 4; c++) reproj[c * 4 + r] = (float)m[r * 4 + c]; + } else if (camera && !have_prev) reset = true; + memcpy(run.prev_vp, vp, sizeof vp); + run.prev_vp_frame = frame; + } + run.last_frame = frame, run.motion = motion; + const auto now = std::chrono::steady_clock::now(); + float frame_ms = run.last_time.time_since_epoch().count() + ? (float)std::chrono::duration(now - run.last_time).count() : 16.7f; + run.last_time = now; + if (frame_ms <= 0.0f || frame_ms > 250.0f) frame_ms = 16.7f; + + // 1. the inputs, made what NGX takes + struct { int32_t w, h; float sx, sy; int32_t flags; float far_depth, pad0, pad1; float reproj[16]; } pp{}; + pp.w = (int32_t)rw, pp.h = (int32_t)rh; + if (have_mv) mv_texture_scale(rw, rh, pp.sx, pp.sy); + if (in.mv_unorm) pp.flags |= 8; + if (camera) pp.flags |= 16 | (motion == 1 ? 32 : 0) | (inverted ? 64 : 0); + memcpy(pp.reproj, reproj, sizeof reproj); + static const bool no_sqrt = getenv("RX_DLSS_NO_SQRT") != nullptr; + if (!no_sqrt) pp.flags |= 1; // (the TAAU colour is the square root of the linear colour, as taau_upscale.comp reads it) + pp.flags |= (have_mv ? 2 : 0) | (in.depth_view ? 4 : 0); + pp.far_depth = inverted ? 0.0f : 1.0f; + const VkDescriptorImageInfo ii[6] = { + {linear, in.color_view, VK_IMAGE_LAYOUT_GENERAL}, + {nearest, have_mv ? in.mv_view : in.color_view, VK_IMAGE_LAYOUT_GENERAL}, + {nearest, in.depth_view ? in.depth_view : in.color_view, VK_IMAGE_LAYOUT_GENERAL}, + {VK_NULL_HANDLE, run.color.view, VK_IMAGE_LAYOUT_GENERAL}, + {VK_NULL_HANDLE, run.mv.view, VK_IMAGE_LAYOUT_GENERAL}, + {VK_NULL_HANDLE, run.depth.view, VK_IMAGE_LAYOUT_GENERAL}}; + VkWriteDescriptorSet w[6]; + for (uint32_t i = 0; i < 6; i++) { + w[i] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; + w[i].dstBinding = i, w[i].descriptorCount = 1; + w[i].descriptorType = i < 3 ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + w[i].pImageInfo = &ii[i]; + } + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, prep.pipe); + vkCmdPushDescriptorSetKHR(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, prep.layout, 0, 6, w); + vkCmdPushConstants(cmd, prep.layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof pp, &pp); + vkCmdDispatch(cmd, (rw + 7) / 8, (rh + 7) / 8, 1); + barrier(cmd); + + // 2. DLSS + float jx = 0.0f, jy = 0.0f; + if (const char* e = getenv("RX_DLSS_JITTER")) sscanf(e, "%f,%f", &jx, &jy); + const HsNgxImage c = ngx_image(run.color), d = ngx_image(run.depth), m = ngx_image(run.mv), o = ngx_image(run.output); + const int r = ngx.evaluate((void*)cmd, run.feature, &c, &d, &m, &o, jx, jy, 1.0f, 1.0f, reset ? 1 : 0, frame_ms); + if (r != 0) { + logf("dlss: evaluate failed (%#x)%s", (unsigned)r, run.failures + 1 >= 3 ? ": off until the next start" : ""); + if (++run.failures >= 3) state = State::Failed; + return false; // (what the prep pass wrote is DLSS's own: the network runs as before) + } + barrier(cmd); + + // 3. into the network's output + const VkDescriptorImageInfo oi[2] = {{linear, run.output.view, VK_IMAGE_LAYOUT_GENERAL}, + {VK_NULL_HANDLE, in.out_view, VK_IMAGE_LAYOUT_GENERAL}}; + VkWriteDescriptorSet ow2[2]; + for (uint32_t i = 0; i < 2; i++) { + ow2[i] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; + ow2[i].dstBinding = i, ow2[i].descriptorCount = 1; + ow2[i].descriptorType = i == 0 ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + ow2[i].pImageInfo = &oi[i]; + } + const int32_t os[2] = {(int32_t)ow, (int32_t)oh}; + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, out_pass.pipe); + vkCmdPushDescriptorSetKHR(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, out_pass.layout, 0, 2, ow2); + vkCmdPushConstants(cmd, out_pass.layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof os, os); + vkCmdDispatch(cmd, (ow + 7) / 8, (oh + 7) / 8, 1); + run.failures = 0; + static std::atomic told{0}; + if (told++ < 3 || reset) + if (told < 40) + logf("dlss: frame %llu: %ux%u -> %ux%u, preset %d, motion %s%s%s", + (unsigned long long)frame, rw, rh, ow, oh, preset, + have_mv ? "texture" : camera ? (motion == 1 ? "camera (Y flipped)" : "camera") : "none", + in.depth_view ? "" : ", no depth", reset ? ", reset" : ""); + return true; +} + +} // namespace mkwvk diff --git a/gpu/vk/vk_dlss.h b/gpu/vk/vk_dlss.h new file mode 100644 index 00000000..570e93f5 --- /dev/null +++ b/gpu/vk/vk_dlss.h @@ -0,0 +1,56 @@ +// (hookshot DLSS) NVIDIA DLSS Super Resolution in the place of OoT's gameplay TAAU network (vk_dlss.cpp, docs/dlss.md). +// Off unless the Insert menu asks for it and hookshot_ngx.dll loaded. With either missing, the network runs as before. +#pragma once + +#include "vk_internal.h" + +namespace mkwvk { + +struct Texture; + +/// (vk_main.cpp, before the instance and the device are made) what NGX needs enabled, where hookshot_ngx.dll is +/// beside the game: the extensions of `have` it asks for that `want` does not have yet. No DLL: nothing added +void dlss_instance_extensions(const std::vector& have, std::vector& want); +void dlss_device_extensions(const std::vector& have, std::vector& want); +void dlss_device_extensions_refused(); // (the device would not take them: DLSS off) +void dlss_shutdown(); // (before the device goes, when a shutdown path calls it) + +/// DLSS chosen in the Insert menu (or RX_DLSS), and nothing known yet that rules it out +bool dlss_wanted(); +/// A short note for the menu's DLSS row: why it is not running, or "" when it is (or has not been tried yet) +const char* dlss_state(); + +/// One TAAU dispatch's inputs, resolved from its descriptors (vk_draw.cpp taau_dlss) +struct DlssInputs { + VkImageView color_view = VK_NULL_HANDLE; // the network's scene colour (square-root encoded) + uint32_t render_w = 0, render_h = 0; + VkImageView mv_view = VK_NULL_HANDLE; // its motion (null: none found) + VkImageView depth_view = VK_NULL_HANDLE; // its depth (null: none found) + VkImageView out_view = VK_NULL_HANDLE; // its output, a B10G11R11 storage view of the level it writes + uint32_t out_w = 0, out_h = 0; + bool mv_found = false; // (what the binding search found, for the menu's note) + bool mv_unorm = false; // the motion is in an unsigned normalized format: 0.5 is no motion + bool depth_found = false; // (a scene depth target was found, vk_dlss.cpp dlss_find_depth) + bool camera = false; // view_proj holds this frame's camera (the game's constants) + float view_proj[16] = {}; // row-major, GL clip space (z -w..w), as the game keeps it +}; +/// The menu's DLSS quality row: the size DLSS takes now. False: DLSS is not running +bool dlss_input_size(uint32_t& w, uint32_t& h); +/// (build 5, vk_draw.cpp) a TAAU network DLSS can take ran this frame (its key and input size) +void dlss_taau_seen(uint64_t key, uint32_t w, uint32_t h); +/// (build 5, vk_replay.cpp present) once a present: the DLSS / quality / motion rows logged at the start and on every change, +/// and why DLSS is not running when it is chosen and has not run for a while +void dlss_frame_status(); +/// (vk_draw.cpp end_pass) a pass's depth target, with its size and draws, for the scene depth DLSS reprojects with. +/// RX_DLSS_LOG_CANDIDATES=1: every pass of a few frames is logged too +void dlss_note_pass(Texture* depth, uint32_t width, uint32_t height, int draws, int colours, VkFormat colour0, uint64_t prog); +/// The scene depth for a render size: the depth target of that size (or its shape) with the most draws this frame (or the +/// last one). Null: none +VkImageView dlss_find_depth(uint32_t w, uint32_t h, uint32_t* found_w = nullptr, uint32_t* found_h = nullptr); +bool dlss_log_candidates(); +void dlss_log_candidates_from(uint64_t frame); // (vk_draw.cpp, the first TAAU) +/// DLSS recorded into cmd in the network's place: true, its output written. False: the caller runs the network (or +/// the stand-in) as it would have, nothing of DLSS's left in the way +bool dlss_run(VkCommandBuffer cmd, const DlssInputs& in); + +} // namespace mkwvk diff --git a/gpu/vk/vk_draw.cpp b/gpu/vk/vk_draw.cpp index 57a65a9d..c9e3a3f5 100644 --- a/gpu/vk/vk_draw.cpp +++ b/gpu/vk/vk_draw.cpp @@ -7,6 +7,7 @@ // // Pipelines persist in a VkPipelineCache next to the executable (mkw_vk_pipelines.bin). #include +#include #include #include #include @@ -41,6 +42,7 @@ #include "shaders/clear_u_frag_spv.h" #include "shaders/clear_vert_spv.h" #include "shaders/taau_upscale_comp_spv.h" +#include "vk_dlss.h" // (hookshot DLSS: in the TAAU network's place) #include "shaders/movie_upscale_comp_spv.h" #include "shader_recompiler/backend/spirv/mma_layout.h" #include "vk_work.h" // (after the others: counts the commands that do GPU work, for full_barrier) @@ -3860,6 +3862,7 @@ void end_pass(VkCommandBuffer cmd) { if (!pass.open) return; vkCmdEndRendering(cmd); pass.open = false; + dlss_note_pass(pass.depth ? pass.depth_tex : nullptr, pass.width, pass.height, pass.draws, pass.n, pass.format0, pass.prog); if (dump_on() && !dump_mesh_indirect.empty()) { for (const auto& [key, addr] : dump_mesh_indirect) { const BufferRef r = gpu_buffer_bind(addr, 16, false); @@ -5868,6 +5871,214 @@ static bool typed_upscale(VkCommandBuffer cmd, VkImageView input_view, VkImageVi if (temporal) probe_image(cmd, out, level, 0, key, 't'); return true; } +/// (hookshot DLSS) What a sampled format can be to DLSS: 1 motion (two channels, float or normalized), 2 colour, 0 neither +static int dlss_kind(VkFormat f) { + switch (f) { + case VK_FORMAT_R16G16_SFLOAT: case VK_FORMAT_R16G16_SNORM: case VK_FORMAT_R16G16_UNORM: case VK_FORMAT_R32G32_SFLOAT: + case VK_FORMAT_R8G8_SNORM: case VK_FORMAT_R8G8_UNORM: + return 1; + default: + return 0; + } +} +/// (hookshot DLSS) OoT's gameplay TAAU network recorded as NVIDIA DLSS Super Resolution (vk_dlss.cpp), when DLSS is +/// chosen: its inputs read from its own descriptors, in descriptor order as build_descriptors resolved them (so slot i +/// is dw.imgs[i]): the colour the stand-in reads (slot 0), the first two-channel float/normalized texture as motion, +/// the first depth texture as depth, and its output as the stand-in finds it. Every binding is logged once, so a run +/// says which is which. RX_DLSS_COLOR / RX_DLSS_MV / RX_DLSS_DEPTH= choose a slot instead (-1: none); +/// RX_DLSS_KEYS= the networks it takes (default fd029a317a9a4b94). False: nothing recorded, the +/// network (or the stand-in) runs as before +// (build 5) The game has one TAAU network per ratio of the 3D to the output, and build 1-4 took only fd029a317a9a4b94, the +// 3/4 one: at any other Resolution / DLSS quality the game ran another network and DLSS never ran. 2560x1440 (and +// 2048x1152): 9dc98871eb7fc101 12/12 (DLAA), 6676619641320ba7 8/12, f10782f9e58b6a7c 7/12, 72ba090c70fe2614 6/12, +// 0775215f8da720e6 4/12. Any other network of the same shape (one colour texture in, one B10G11R11 image out at most its +// size, the same aspect) is taken too (dlss_like_taau): the 4K output's ones are not known by key. +static bool dlss_key(uint64_t key) { + static const char* keys = getenv("RX_DLSS_KEYS") ? getenv("RX_DLSS_KEYS") + : "fd029a317a9a4b94,9dc98871eb7fc101,6676619641320ba7,f10782f9e58b6a7c," + "72ba090c70fe2614,0775215f8da720e6"; + char hex[24]; + snprintf(hex, sizeof hex, "%016llx", (unsigned long long)key); + for (const char* q = keys; *q;) { + const char* e = q; + while (*e && *e != ',') e++; + if (e > q && !strncmp(hex, q, (size_t)(e - q))) return true; + q = *e ? e + 1 : e; + } + return false; +} +bool taau_dlss(VkCommandBuffer cmd, const NvnState& s, const Program& p) { + if (!dlss_wanted() || !dev.push_descriptor || !vkCmdPushDescriptorSetKHR || p.stages.empty()) return false; + const Stage& st = p.stages[0]; + const bool known = dlss_key(p.key); + static const bool by_shape = !getenv("RX_DLSS_KEYS"); + // (a network of a TAAU's shape: matrix code, one texture and one image binding; the sizes are checked below) + const bool shaped = by_shape && st.info.uses_cooperative_matrix && st.info.texture_descriptors.size() == 1 && + st.info.texture_descriptors[0].count == 1 && st.info.image_descriptors.size() == 1; + if (!known && !shaped) return false; + if (st.info.texture_descriptors.empty() || st.info.image_descriptors.empty()) return false; + struct Bound { Texture* tex; VkImageView view; VkImageViewType vt; }; + std::vector b; + for (const auto& d : st.info.texture_descriptors) { + const VkImageViewType vt = view_type(d.type); + for (uint32_t k = 0; k < d.count; k++) { + const uint32_t handle = handle_is_storage_pointer(st.info, d, k) ? 0u : handle_of(s, st, d, k); + Texture* tex = nullptr; + const VkImageView view = handle ? texture_view_of(handle, vt, ViewUse::Sample, VK_FORMAT_UNDEFINED, &tex) : VK_NULL_HANDLE; + b.push_back({view ? tex : nullptr, view, vt}); + } + } + if (dw.imgs.size() <= b.size()) return false; + const auto& od = st.info.image_descriptors[0]; + Texture* out = nullptr; + uint32_t level = 0; + const uint32_t handle = handle_of(s, st, od, 0); + if (!handle) return false; + texture_view_of(handle, VK_IMAGE_VIEW_TYPE_2D, ViewUse::Storage, image_format(od.format), &out, &level); + if (!out || out->format != VK_FORMAT_B10G11R11_UFLOAT_PACK32 || !dw.imgs[b.size()].imageView) return false; // (the last pass only) + const auto slot = [&](const char* env, int def) { return getenv(env) ? atoi(getenv(env)) : def; }; + const auto usable = [&](int i) { return i >= 0 && i < (int)b.size() && b[i].tex && b[i].view && b[i].vt == VK_IMAGE_VIEW_TYPE_2D; }; + const int color = slot("RX_DLSS_COLOR", 0); + // (a motion texture only when named by hand: OoT's TAAU network has none, its motion is made from the depth and the camera) + int mv = slot("RX_DLSS_MV", -1), depth = slot("RX_DLSS_DEPTH", -2); + if (!usable(color)) return false; + const Texture* ct = b[color].tex; + if (!known) { // (build 5: a network by its shape alone: the scene colour in, at most the output's size, the same aspect) + const uint32_t ow0 = std::max(1u, out->width >> level), oh0 = std::max(1u, out->height >> level); + if (ct->format != VK_FORMAT_B10G11R11_UFLOAT_PACK32 || ct->width > ow0 || ct->height > oh0 || ow0 < 1024 || + std::fabs((double)ct->width * oh0 - (double)ct->height * ow0) > 0.01 * (double)ct->height * ow0) + return false; + } + { // (build 5: every network DLSS takes, once each) + static std::mutex mu; + static std::unordered_set told; + std::lock_guard lk(mu); + if (told.insert(p.key).second) + logf("dlss: TAAU network %016llx taken (%s): %ux%u -> %ux%u", (unsigned long long)p.key, + known ? "known" : "by its shape", ct->width, ct->height, std::max(1u, out->width >> level), + std::max(1u, out->height >> level)); + } + dlss_taau_seen(p.key, ct->width, ct->height); + if (mv == -2) { // (the first two-channel texture, at the colour's size first, then any size) + for (int pass = 0; pass < 2 && mv == -2; pass++) + for (int i = 0; i < (int)b.size() && mv == -2; i++) + if (i != color && usable(i) && !(b[i].tex->aspect & VK_IMAGE_ASPECT_DEPTH_BIT) && dlss_kind(b[i].tex->format) == 1 && + (pass || (b[i].tex->width == ct->width && b[i].tex->height == ct->height))) + mv = i; + } + if (depth == -2) + for (int pass = 0; pass < 2 && depth == -2; pass++) + for (int i = 0; i < (int)b.size() && depth == -2; i++) + if (i != color && usable(i) && (b[i].tex->aspect & VK_IMAGE_ASPECT_DEPTH_BIT) && + (pass || (b[i].tex->width == ct->width && b[i].tex->height == ct->height))) + depth = i; + if (!usable(mv)) mv = -1; + if (!usable(depth)) depth = -1; + { // (once per change of what was found: every binding, and which DLSS takes for what) + static std::mutex mu; + static std::string said; + char line[1000]; + int n = snprintf(line, sizeof line, "dlss: TAAU %016llx inputs:", (unsigned long long)p.key); + for (int i = 0; i < (int)b.size() && n < 900; i++) { + const Texture* t = b[i].tex; + n += snprintf(line + n, sizeof line - n, " %d:%ux%u/%d%s%s", i, t ? t->width : 0, t ? t->height : 0, + t ? (int)t->format : -1, t && (t->aspect & VK_IMAGE_ASPECT_DEPTH_BIT) ? "d" : "", + i == color ? "=colour" : i == mv ? "=motion" : i == depth ? "=depth" : ""); + } + if (n < 960) snprintf(line + n, sizeof line - n, "; output %ux%u level %u", out->width, out->height, level); + std::lock_guard lk(mu); + if (said != line) said = line, logf("%s", line); + } + { // (the network's constant buffers, when asked: where its jitter and camera are. + // RX_DLSS_LOG_CB=: n frames, off when unset) + static std::atomic cb_frames{0}; + static const int cb_want = getenv("RX_DLSS_LOG_CB") ? atoi(getenv("RX_DLSS_LOG_CB")) : 0; + if (cb_frames.fetch_add(1) < cb_want) + for (int i = 0; i < 16; i++) { + const nvn_p_bind_buf& u = s.ubo[5][i]; + if (!u.addr || !u.size) continue; + float f[48]{}; + const uint64_t n = std::min(u.size, sizeof f) & ~3ull; + if (!n || !memory_read_as_submitted(u.addr, f, n)) continue; + char line[900]; + int k = snprintf(line, sizeof line, "dlss: TAAU frame %llu ub%d (%llu bytes):", (unsigned long long)texture_frame_no(), i, + (unsigned long long)u.size); + for (uint64_t j = 0; j < n / 4 && k < 860; j++) k += snprintf(line + k, sizeof line - k, " %.5g", f[j]); + logf("%s", line); + } + } + DlssInputs in; + in.color_view = b[color].view; + in.render_w = ct->width, in.render_h = ct->height; + in.out_w = std::max(1u, out->width >> level), in.out_h = std::max(1u, out->height >> level); + if (mv >= 0) { + in.mv_view = b[mv].view, in.mv_found = true; + const VkFormat f = b[mv].tex->format; + in.mv_unorm = f == VK_FORMAT_R16G16_UNORM || f == VK_FORMAT_R8G8_UNORM; + } + if (depth >= 0) in.depth_view = b[depth].view; + uint32_t dw_found = 0, dh_found = 0; + if (!in.depth_view) // (not among the network's inputs: the scene's depth target of the render size, vk_dlss.cpp) + in.depth_view = dlss_find_depth(in.render_w, in.render_h, &dw_found, &dh_found); + in.depth_found = in.depth_view != VK_NULL_HANDLE; + { // the camera: a view-projection followed by its projection (row 3 0 0 -1 0, GL) in the network's constant buffers + static std::atomic where{-1}; // (ubo * 4096 + float offset of the projection, once found) + static std::atomic told{false}; + const auto take = [&](int i, int k0, bool search) -> bool { + const nvn_p_bind_buf& u = s.ubo[5][i]; + if (!u.addr || u.size < 128) return false; + static thread_local float f[1024]; + const uint64_t n = std::min(u.size, sizeof f) & ~3ull; + if (!memory_read_as_submitted(u.addr, f, n)) return false; + const int nf = (int)(n / 4); + const auto z = [](float v) { return std::fabs(v) < 1e-4f; }; + for (int k = search ? 16 : k0; k + 16 <= nf && (search || k == k0); k += 4) { + const float* P = f + k; + if (!(P[0] > 0.05f && z(P[1]) && z(P[3]) && z(P[4]) && P[5] > 0.05f && z(P[7]) && z(P[8]) && z(P[9]) && + z(P[12]) && z(P[13]) && std::fabs(P[14] + 1.0f) < 1e-3f && z(P[15]))) + continue; + const float* VP = P - 16; + const float l = std::sqrt(VP[12] * VP[12] + VP[13] * VP[13] + VP[14] * VP[14]); + if (!(l > 0.98f && l < 1.02f)) continue; // (its w row: minus the view's z axis, unit length) + for (int j = 0; j < 16; j++) in.view_proj[j] = VP[j]; + in.camera = true; + if (!told.exchange(true)) + logf("dlss: camera found in the TAAU's constants: ub%d floats %d..%d (projection after it: x %.4g y %.4g, " + "jitter %.3g,%.3g)", i, k - 16, k - 1, P[0], P[5], P[2], P[6]); + where = i * 4096 + k; + return true; + } + return false; + }; + const int w0 = where.load(); + if (!(w0 >= 0 && take(w0 / 4096, w0 % 4096, false)) && !take(9, 28, false)) + for (int i = 0; i < 16 && !in.camera; i++) take(i, 0, true); + } + { // (what was found, once per change) + static std::mutex mu2; + static std::string said2; + char line[300]; + snprintf(line, sizeof line, "dlss: scene depth %s%s, camera %s", in.depth_found ? "found" : "not found", + dw_found ? (" (" + std::to_string(dw_found) + "x" + std::to_string(dh_found) + " depth target)").c_str() : "", + in.camera ? "found" : "not found"); + std::lock_guard lk(mu2); + if (said2 != line) said2 = line, logf("%s", line); + } + { + static std::atomic cand_frames{0}; // (RX_DLSS_LOG_CANDIDATES: the passes of the frames after the first TAAU, vk_dlss.cpp) + if (cand_frames.fetch_add(1) == 0) dlss_log_candidates_from(texture_frame_no()); + } + in.out_view = dw.imgs[b.size()].imageView; + in.out_w = std::max(1u, out->width >> level), in.out_h = std::max(1u, out->height >> level); + if (!dlss_run(cmd, in)) return false; + // (NGX binds its own pipelines, descriptors and push constants: everything the next draw or dispatch takes for granted + // is set again) + last_dyn_valid = false, last_desc_valid = false, last_vb_valid = false, last_pipeline = VK_NULL_HANDLE; + vertex_set.on = false, last_ib.on = false; + descriptors_disturbed(); + probe_image(cmd, out, level, 0, p.key, 't'); + return true; +} bool taau_stand_in(VkCommandBuffer cmd, const NvnState& s, const Program& p) { if (!taau_key(p.key) || !dev.push_descriptor || !vkCmdPushDescriptorSetKHR || p.stages.empty()) return false; const Stage& st = p.stages[0]; @@ -6464,6 +6675,7 @@ void dispatch(VkCommandBuffer cmd, NvnState& s, uint32_t x, uint32_t y, uint32_t logf("%s", line); } } + if (taau_dlss(cmd, s, p)) return void(st_dispatches++); // (hookshot DLSS: vk_dlss.cpp) if (taau_stand_in(cmd, s, p)) return void(st_dispatches++); if (!p.compute_pipeline) SKIP_HERE(); dump_ssbos(cmd, p, "before"); diff --git a/gpu/vk/vk_framegen.cpp b/gpu/vk/vk_framegen.cpp index bf2fd982..d335afd3 100644 --- a/gpu/vk/vk_framegen.cpp +++ b/gpu/vk/vk_framegen.cpp @@ -402,9 +402,9 @@ VkImage framegen_scale(VkCommandBuffer cmd, VkImageView src, VkImage image, uint static bool reported = false; if (report && !reported) { reported = true; - logf("vk: upscale dispatched: mode %d, sharpness %d, source %ux%u, crop request %d,%d %dx%d, destination %ux%u", - opts.upscale, opts.sharpness, sw, sh, crop ? crop[0] : 0, crop ? crop[1] : 0, - crop ? crop[2] : (int)sw, crop ? crop[3] : (int)sh, dw, dh); + logf("vk: upscale dispatched: mode %d, sharpness %d%s, source %ux%u, crop request %d,%d %dx%d, destination %ux%u", + opts.upscale, opts.sharpness, opts.sharpness ? " (contrast-adaptive)" : "", sw, sh, + crop ? crop[0] : 0, crop ? crop[1] : 0, crop ? crop[2] : (int)sw, crop ? crop[3] : (int)sh, dw, dh); } return scale.scaled.image; } diff --git a/gpu/vk/vk_main.cpp b/gpu/vk/vk_main.cpp index d3e5a09d..0eb6e6cf 100644 --- a/gpu/vk/vk_main.cpp +++ b/gpu/vk/vk_main.cpp @@ -17,6 +17,7 @@ #include "vk_internal.h" #include "gpu_choice.h" #include "vk_menu.h" +#include "vk_dlss.h" // (hookshot DLSS: the extensions NGX asks for) namespace vkx { #define VKX_DEFINE(f) PFN_##f f; @@ -87,8 +88,17 @@ bool create_instance() { return false; } #ifdef _WIN32 - const char* exts[] = {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME}; - const uint32_t n_exts = 2; + // (hookshot DLSS) the surface's, and what NGX asks for where hookshot_ngx.dll is beside the game (vk_dlss.cpp) + std::vector exts_v = {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME}; + { + uint32_t n = 0; + vkEnumerateInstanceExtensionProperties(nullptr, &n, nullptr); + std::vector all(n); + vkEnumerateInstanceExtensionProperties(nullptr, &n, all.data()); + dlss_instance_extensions(all, exts_v); + } + const char* const* exts = exts_v.data(); + const uint32_t n_exts = (uint32_t)exts_v.size(); #else // (linux: every window system's surface the loader has, for whichever one SDL's window is on: X11 or Wayland) const char* exts[4] = {VK_KHR_SURFACE_EXTENSION_NAME}; @@ -360,13 +370,25 @@ bool create_device() { dev.sparse_images = (families[dev.queue_family].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) && f.features.sparseBinding && f.features.sparseResidencyImage2D && !(getenv("RX_VK_SPARSE") && !atoi(getenv("RX_VK_SPARSE"))); dev.sparse_buffers = (families[dev.queue_family].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) && f.features.sparseBinding; + // (hookshot DLSS) NGX's extensions, after all of the renderer's own (none twice), where hookshot_ngx.dll is there and the + // GPU is NVIDIA's; a device that will not take them is made again without them, DLSS off + const size_t own_exts = want.size(); + dlss_device_extensions(exts, want); VkDeviceCreateInfo ci{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO}; ci.pNext = &f; ci.queueCreateInfoCount = 1; ci.pQueueCreateInfos = &q; ci.enabledExtensionCount = (uint32_t)want.size(); ci.ppEnabledExtensionNames = want.data(); - if (vkCreateDevice(dev.physical, &ci, nullptr, &dev.device) != VK_SUCCESS) { + VkResult made = vkCreateDevice(dev.physical, &ci, nullptr, &dev.device); + if (made != VK_SUCCESS && want.size() > own_exts) { + logf("vk: vkCreateDevice failed (%d) with NGX's extensions: made again without them, DLSS off", (int)made); + want.resize(own_exts); + dlss_device_extensions_refused(); + ci.enabledExtensionCount = (uint32_t)want.size(); + made = vkCreateDevice(dev.physical, &ci, nullptr, &dev.device); + } + if (made != VK_SUCCESS) { logf("vk: vkCreateDevice failed"); return false; } diff --git a/gpu/vk/vk_menu.cpp b/gpu/vk/vk_menu.cpp index 6d10a766..91f84354 100644 --- a/gpu/vk/vk_menu.cpp +++ b/gpu/vk/vk_menu.cpp @@ -21,11 +21,13 @@ #include "vk_menu.h" #include "vk_menu_hit.h" #include "oot_cheat_items.h" +#include "vk_dlss.h" // (hookshot DLSS: the DLSS row's note) #include "vk_work.h" // (after the others: counts the commands that do GPU work, for full_barrier) #ifndef HOOKSHOT_VERSION // (nx2/build.py gives it to this file: nx2/version.py) #define HOOKSHOT_VERSION "unknown" #endif +namespace mkwvk { const char* hookshot_build_version() { return HOOKSHOT_VERSION; } } namespace mkwvk { HWND app_window(); @@ -64,6 +66,18 @@ const char* tx_upscale(int v) { if ((unsigned)v >= sizeof t / sizeof t[0]) return t[0]; // (a setting past the row: the label, not the next bytes) return t[v]; } +const char* tx_dlss(int v) { // (hookshot DLSS) + static const char* t[] = {"Off", "CNN (DLSS 3)", "Transformer (DLSS 4)", "Transformer 2 (DLSS 4.5)", "NVIDIA default"}; + static char b[96]; + if ((unsigned)v >= sizeof t / sizeof t[0]) v = 0; + snprintf(b, sizeof b, "%s%s", t[v], v ? dlss_state() : ""); + return b; +} +const char* tx_dlss_motion(int v) { // (hookshot DLSS) + static const char* t[] = {"Camera, Y not flipped", "Camera, Y flipped (default)", "None (still image only)"}; + if ((unsigned)v >= sizeof t / sizeof t[0]) v = 0; + return t[v]; +} const char* tx_percent(int v) { static char b[8]; snprintf(b, sizeof b, "%d%%", v); @@ -90,12 +104,32 @@ void game_size(float scale, uint32_t& w, uint32_t& h) { } const char* tx_resolution(int v) { static char b[48]; + if (dlss_quality_twelfths()) return "Set by DLSS quality"; // (hookshot DLSS) if (v <= 0 || v > 4) return "Auto (dynamic)"; uint32_t w, h; game_size(kResPercent[v] / 100.0f, w, h); snprintf(b, sizeof b, "%d%% (%ux%u)", kResPercent[v], w, h); return b; } +const char* tx_dlss_quality(int v) { // (hookshot DLSS) + static const char* t[] = {"Auto (from Resolution)", "Quality", "Balanced", "Performance", "Ultra Performance", "DLAA (native)"}; + static char b[128]; + if ((unsigned)v >= sizeof t / sizeof t[0]) v = 0; + uint32_t iw = 0, ih = 0; // (what DLSS is taking now, when it is running) + const bool running = opts.dlss && dlss_input_size(iw, ih); + if (!v) { + if (running) snprintf(b, sizeof b, "%s - DLSS gets %ux%u", t[0], iw, ih); + return running ? b : t[0]; + } + uint32_t w, h; + static const int k[] = {0, 8, 7, 6, 4, 12}; + game_size(k[v] / 12.0f, w, h); + char note[48] = ""; + if (!opts.dlss) snprintf(note, sizeof note, " - DLSS is off"); + else if (running && (iw != w || ih != h)) snprintf(note, sizeof note, " - DLSS gets %ux%u", iw, ih); + snprintf(b, sizeof b, "%s (%ux%u)%s", t[v], w, h, note); + return b; +} const char* tx_output_res(int v) { static const char* t[] = {"2048x1152 (game, restart)", "2560x1440 (restart)", "3840x2160 (restart)"}; return t[v]; @@ -168,7 +202,7 @@ void apply_preset(int p) { } Row rows[] = { - // Graphics (11 rows) + // Graphics (15 rows) {TAB_GRAPHICS, "Quality preset", &opts.preset, 0, 5, 1, tx_preset, "Automatically configures resolution, upscaling, frame generation, and dynamic VRAM limits tailored to your GPU tier."}, {TAB_GRAPHICS, "Fullscreen", &opts.fullscreen, 0, 1, 1, tx_onoff, @@ -188,8 +222,19 @@ Row rows[] = { "Resolution percentage of it. Docked only. Applies after a restart."}, {TAB_GRAPHICS, "Upscaling", &opts.upscale, 0, 2, 1, tx_upscale, "The filter that scales the picture to your window. Off is the plain copy. Catmull-Rom is sharper, Lanczos sharper still."}, + {TAB_GRAPHICS, "DLSS", &opts.dlss, 0, 4, 1, tx_dlss, + "NVIDIA DLSS in place of the game's own upscaler (RTX cards, hookshot_ngx.dll and nvngx_dlss.dll beside the game). CNN is the " + "older model, Transformer DLSS 4's, Transformer 2 DLSS 4.5's. DLSS quality (or the Resolution row) sets how much it upscales."}, + {TAB_GRAPHICS, "DLSS motion", &opts.dlss_motion, 0, 2, 1, tx_dlss_motion, + "The motion DLSS is given, made from the scene's depth and the game's camera. If the world smears or doubles while you turn " + "the camera, try the other Camera setting. None runs DLSS without motion (sharp when still, smears when moving)."}, + {TAB_GRAPHICS, "DLSS quality", &opts.dlss_quality, 0, 5, 1, tx_dlss_quality, + "The size the 3D is drawn at for DLSS. Auto keeps the Resolution row. Quality 67%, Balanced 58%, Performance 50%, Ultra " + "Performance 33% of the output; DLAA draws it at the full output size and DLSS only anti-aliases (sharpest, slowest). " + "Changes at once, no restart: for a moment the game's own upscaler shows while the game remakes its 3D at the new size."}, {TAB_GRAPHICS, "Sharpness", &opts.sharpness, 0, 100, 10, tx_percent, - "Sharpens the picture as it goes to the window, with or without an upscaling filter. Too much shows halos around edges."}, + "Sharpens the picture as it goes to the window (a contrast-adaptive sharpen, with or without DLSS and an upscaling filter). " + "0 is off, 100 the strongest; it does not push edges past their neighbours, so no halos."}, {TAB_GRAPHICS, "Handheld profile", &opts.handheld, 0, 1, 1, tx_handheld, "Runs the game as if the console were undocked: lower resolutions, lighter on the GPU. Applies after a restart."}, {TAB_GRAPHICS, "Ultrawide", &opts.ultrawide, 0, 1, 1, tx_ultrawide, @@ -458,7 +503,8 @@ void set_row(Row& r, int v, int dir, bool keep = true) { *r.value = v > r.hi ? r.lo : v < r.lo ? r.hi : v; if (r.value != &opts.fullscreen && r.value != &opts.show_fps && r.value != &opts.gpu_device && r.value != &opts.invert_cam_x && r.value != &opts.invert_cam_y && r.value != &opts.mouse_native && - r.value != &opts.mouse_sens && r.value != &opts.mouse_invert) { // (the controls are not the preset's) + r.value != &opts.mouse_sens && r.value != &opts.mouse_invert && r.value != &opts.dlss && + r.value != &opts.dlss_motion) { // (not part of a quality preset: DLSS quality is, it changes the 3D's size) opts.preset = 0; // Custom sync_preset_budgets(0); } @@ -1102,6 +1148,11 @@ void menu_init() { opts.sharpness = 0; } if (opts.preset > 0) sync_preset_budgets(opts.preset); + if (const char* e = getenv("RX_DLSS")) opts.dlss = std::min(std::max(atoi(e), 0), 4); + if (const char* e = getenv("RX_DLSS_MOTION")) opts.dlss_motion = atoi(e); + if (const char* e = getenv("RX_DLSS_OFF")) if (atoi(e)) opts.dlss = 0; + if (opts.dlss_motion == 6) opts.dlss_motion = 2; // (an old options file: 6 was None) + else if (opts.dlss_motion < 0 || opts.dlss_motion > 2) opts.dlss_motion = 1; if (opts.framegen == 1) opts.framegen = 2; if (const char* e = getenv("RX_VK_FRAMEGEN_MULT")) opts.framegen = std::min(std::max(atoi(e), 2), 8); if (const char* e = getenv("RX_VK_PACE")) opts.pace = atoi(e) != 0; @@ -1113,8 +1164,18 @@ void menu_init() { if (const char* e = getenv("HOS_OUTPUT_RES")) opts.output_res = std::min(std::max(atoi(e), 0), 2); } -bool resolution_auto() { return opts.resolution == 0; } +int dlss_choice() { + static const int off = getenv("RX_DLSS_OFF") && atoi(getenv("RX_DLSS_OFF")) ? 1 : 0; + if (off) return 0; + return opts.dlss; +} +int dlss_quality_twelfths() { // (hookshot DLSS: Quality 8/12, Balanced 7/12, Performance 6/12, Ultra Performance 4/12, DLAA 12/12) + static const int k[] = {0, 8, 7, 6, 4, 12}; + return dlss_choice() > 0 && opts.dlss_quality > 0 && opts.dlss_quality <= 5 ? k[opts.dlss_quality] : 0; +} +bool resolution_auto() { return opts.resolution == 0 && !dlss_quality_twelfths(); } int resolution_scale() { + if (const int k = dlss_quality_twelfths()) return (int)(k * 10000.0f / 12.0f + 0.5f); // (hookshot DLSS: its quality row's) const float s = opts.resolution > 0 && opts.resolution <= 4 ? kResPercent[opts.resolution] / 100.0f : 0; return s > 0 ? (int)(twelfths(s) * 10000.0f / 12.0f + 0.5f) : 0; // (the twelfth game_size labels) } diff --git a/gpu/vk/vk_menu.h b/gpu/vk/vk_menu.h index 75aa3d38..237a1d35 100644 --- a/gpu/vk/vk_menu.h +++ b/gpu/vk/vk_menu.h @@ -31,6 +31,9 @@ struct Options { int preset = 0; // quality preset: 0 Custom, 1 Minimum (4GB), 2 Low (6GB), 3 Balanced (8GB), 4 High (12GB), 5 Ultra (16GB+) int vram_mb = 0; // dynamic texture cache budget limit in MB int mirror_mb = 0; // staging mirror limit in MB + int dlss = 0; // (hookshot DLSS) NVIDIA DLSS in the game's TAAU network's place: 0 off, 1 CNN, 2 transformer, 3 second transformer, 4 NVIDIA's default (vk_dlss.cpp) + int dlss_motion = 1; // (hookshot DLSS) DLSS's motion: 0 the camera's, from the scene depth; 1 the same with NDC Y flipped (OoT's); 2 none + int dlss_quality = 0; // (hookshot DLSS) 0 the Resolution row's; 1 Quality, 2 Balanced, 3 Performance, 4 Ultra Performance, 5 DLAA: the 3D scale set for DLSS }; extern Options opts; /// (vk_memory.cpp) true when the quality preset's memory budgets (vram_mb, mirror_mb) are to be used: the card has no more @@ -42,6 +45,10 @@ void menu_init(); // (the options from the file, once) void menu_set_gpu_count(int count); void menu_tick(); // (each frame shown: the keys, while the game's window has the focus) bool menu_is_open(); +/// (hookshot DLSS) the DLSS row in effect: RX_DLSS_OFF forces 0, else the menu (RX_DLSS sets it at start) +int dlss_choice(); +/// (hookshot DLSS) the 3D scale the DLSS quality row sets, in twelfths of the output (0: the Resolution row's) +int dlss_quality_twelfths(); bool resolution_auto(); // (the game's dynamic resolution: Resolution Auto) int resolution_scale(); // (the fixed path's scale in 1/10000: the Resolution row's, as a twelfth of the output) void menu_option_set(int id, int value); // (from the game's side: 7 the mouse's sensitivity, the - and = keys) diff --git a/gpu/vk/vk_replay.cpp b/gpu/vk/vk_replay.cpp index d4c0d8bc..b30e4368 100644 --- a/gpu/vk/vk_replay.cpp +++ b/gpu/vk/vk_replay.cpp @@ -38,6 +38,7 @@ #include "vk_present_rect.h" #include "vk_internal.h" +#include "vk_dlss.h" // (hookshot DLSS build 5: dlss_frame_status) #include "vk_work.h" // (after the others: counts the commands that do GPU work, for full_barrier) namespace mkwvk { @@ -3394,6 +3395,7 @@ void submit(const uint64_t* handles, int n) { submit_q(handles, n, 0); } void present(const nvn_tex* t, const int crop[4]) { + dlss_frame_status(); // (hookshot DLSS build 5: the rows in the log, and why DLSS is not running) if (!async_on()) { std::lock_guard order(sync_order_mu); if (sync_waits_on()) { diff --git a/readme.md b/readme.md index 636384c2..02c4c24a 100644 --- a/readme.md +++ b/readme.md @@ -196,6 +196,9 @@ and the right one closes the menu. Your choices are saved in `hookshot_options.i | | Resolution | Auto (the game's own dynamic resolution), 100%, 75%, 67%, 50% (each with its size) | Auto | | | Output resolution | 2048x1152, 2560x1440, 3840x2160 (after a restart) | 2048x1152 | | | Upscaling | Off, Catmull-Rom, Lanczos | Off | +| | DLSS | Off, CNN (DLSS 3), Transformer (DLSS 4), Transformer 2 (DLSS 4.5), NVIDIA default | Off | +| | DLSS motion | Camera, Y not flipped; Camera, Y flipped; None (still image only) | Camera, Y flipped | +| | DLSS quality | Auto (from Resolution), Quality (67%), Balanced (58%), Performance (50%), Ultra Performance (33%), DLAA (native, 100%) | Auto | | | Sharpness | 0 to 100%, in steps of 10 | 0% | | | Handheld profile | Off, On (after a restart) | Off | | | Ultrawide | 16:9, Monitor's shape (after a restart) | 16:9 | @@ -214,7 +217,7 @@ and the right one closes the menu. Your choices are saved in `hookshot_options.i - **Quality preset** sets Resolution, Upscaling, Sharpness, Frame generation, Frame pacing, Game speed, Handheld profile and the video memory limits for a card with that much memory. Changing most other rows yourself sets it back - to Custom. + to Custom. DLSS and DLSS motion do not: a preset does not set them. DLSS quality does, because it changes the 3D's size. - **GPU device** matters on a PC with more than one graphics card (a laptop's integrated and dedicated GPUs, or a second card for Lossless Scaling). Auto prefers a dedicated one, and only cards with Vulkan 1.3 are listed. `hookshot_log.txt` lists every card as `vk: device `; `RX_VK_DEVICE=` chooses one for a single run and wins @@ -224,7 +227,22 @@ and the right one closes the menu. Your choices are saved in `hookshot_options.i - **Output resolution** is the size of the game's whole frame, HUD included, before it is scaled to your window. Resolution is a share of it, and its row shows the size for the output you chose. Docked only. At 3840x2160 the game's render targets get a bigger GPU pool, so a full-size 3D fits. `HOS_OUTPUT_RES=0/1/2` sets it for one run. -- **Upscaling** and **Sharpness** also work when Frame generation is Off. +- **Upscaling** and **Sharpness** also work when Frame generation is Off. Sharpness is a contrast-adaptive sharpen + (in the manner of AMD FSR's RCAS) on the picture itself, one source pixel apart, so it still does something when the + window is larger than the picture. 0 is off, 100 is the strongest. It does not push a pixel past its neighbours. + `RX_VK_UPSCALE_LOG=1` in `hookshot_env.txt` logs that pass once, including the sharpness. +- **DLSS** (Windows, NVIDIA RTX) replaces the game's own temporal upscaler with NVIDIA DLSS Super Resolution or DLAA. + It is off unless you turn it on, and it stays off when `hookshot_ngx.dll` is not beside the renderer, when + `nvngx_dlss.dll` is not where NGX looks, or when the GPU is not NVIDIA's: the game's own upscaler runs, and the row + says why. You bring both DLLs yourself. Hookshot does not ship NVIDIA's runtime or SDK. + [docs/dlss.md](docs/dlss.md) says where the SDK is and how the bridge is built. + DLSS quality sets the 3D size as a share of the output (Quality 8/12, Balanced 7/12, Performance 6/12, Ultra + Performance 4/12, DLAA 12/12). Auto keeps the Resolution row. The row shows the size. On an 8 GB card, DLAA draws + the 3D at full size and can push video memory over what the driver allows in a busy town, which is when the frame + rate falls. Quality or Balanced is the better choice there. DLAA suits a card with more memory. + DLSS motion is made from the scene depth and the game's camera. What moves on its own (Link, an enemy) only gets + the camera's motion, so it can leave a faint trail. None runs DLSS with no motion: sharp while still, smearing + while the camera moves. `RX_DLSS`, `RX_DLSS_MOTION` and `RX_DLSS_OFF` in `hookshot_env.txt` set the rows for one run. - **Handheld profile** runs the game as if the console were undocked: lower resolution, lighter on the GPU. - **Robust buffers:** a read past a buffer's end is dropped instead of stopping the GPU. Auto is on, on every card; Off can be faster on some NVIDIA cards. diff --git a/tests/test_dlss_scale.py b/tests/test_dlss_scale.py new file mode 100644 index 00000000..da1d5b5e --- /dev/null +++ b/tests/test_dlss_scale.py @@ -0,0 +1,111 @@ +"""DLSS stays out of the resolution path until it is on, and the quality row's twelfths match the NGX modes. + +python -I -B tests/test_dlss_scale.py [--cxx g++] +""" +import argparse +import os +import subprocess +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def block(text, marker): + start = text.index(marker) + end = text.index("{", start) + 1 + depth = 1 + while depth: + depth += (text[end] == "{") - (text[end] == "}") + end += 1 + return text[start:end] + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--cxx", default="g++") + a = p.parse_args() + menu = (ROOT / "gpu" / "vk" / "vk_menu.cpp").read_text(encoding="utf-8") + dlss = (ROOT / "gpu" / "vk" / "vk_dlss.cpp").read_text(encoding="utf-8") + prep = (ROOT / "gpu" / "vk" / "shaders" / "dlss_prep.comp").read_text(encoding="utf-8") + for gone in ("RX_DLSS_TAP", "dlss_probe", "dlss_tap(", "dlss_scene("): + if gone in dlss or gone in menu: + raise SystemExit("the experimental scene tap is still in the renderer (%s)" % gone) + if "probe" in prep: + raise SystemExit("dlss_prep.comp still has the tap's probe") + ngx = ROOT / "gpu" / "vk" / "ngx" + for banned in ngx.rglob("*"): + if banned.suffix.lower() in {".dll", ".lib", ".h"} and banned.name.lower().startswith("nvsdk"): + raise SystemExit("NVIDIA SDK material is in the tree: %s" % banned) + if banned.name.lower() == "nvngx_dlss.dll": + raise SystemExit("nvngx_dlss.dll must not be committed") + choice = block(menu, "int dlss_choice()") + twelfths = block(menu, "int dlss_quality_twelfths()") + auto = block(menu, "bool resolution_auto()") + scale = block(menu, "int resolution_scale()") + quality = block(dlss, "int quality_of(uint32_t rw, uint32_t ow)") + if "dlss_choice() > 0" not in twelfths: + raise SystemExit("DLSS quality changes the 3D size even when DLSS is off") + fixture = r'''#include +#include +#include +#include +#define CHECK(x) do { if (!(x)) { std::fprintf(stderr, "FAIL:%d %s\n", __LINE__, #x); std::exit(1); } } while (0) +struct Options { int dlss = 0, dlss_quality = 0, resolution = 0; } opts; +constexpr int kResPercent[] = {0, 100, 75, 67, 50}; +int twelfths(float scale) { return std::min(std::max((int)(scale * 12.0f + 0.5f), 1), 12); } +''' + choice + twelfths + auto + scale + quality + r''' +int main() { + if (std::getenv("RX_DLSS_OFF") && std::atoi(std::getenv("RX_DLSS_OFF"))) { + opts.dlss = 2; opts.dlss_quality = 5; opts.resolution = 0; + CHECK(dlss_choice() == 0); + CHECK(dlss_quality_twelfths() == 0); + CHECK(resolution_auto()); + CHECK(resolution_scale() == 0); + std::printf("PASS: RX_DLSS_OFF leaves the resolution path alone\n"); + return 0; + } + CHECK(resolution_auto()); + CHECK(resolution_scale() == 0); + opts.resolution = 1; CHECK(!resolution_auto()); CHECK(resolution_scale() == 10000); + opts.resolution = 2; CHECK(resolution_scale() == 7500); + opts.resolution = 3; CHECK(resolution_scale() == 6667); + opts.resolution = 4; CHECK(resolution_scale() == 5000); + opts.resolution = 0; opts.dlss = 2; + CHECK(resolution_auto()); + CHECK(dlss_quality_twelfths() == 0); + static const int tw[] = {0, 8, 7, 6, 4, 12}; + static const int ngx[] = {-1, 2, 1, 0, 3, 5}; + for (int q = 1; q <= 5; q++) { + opts.dlss_quality = q; + CHECK(dlss_quality_twelfths() == tw[q]); + CHECK(!resolution_auto()); + CHECK(resolution_scale() == (int)(tw[q] * 10000.0f / 12.0f + 0.5f)); + CHECK(quality_of(1, 1) == ngx[q]); + } + opts.dlss = 0; opts.dlss_quality = 5; opts.resolution = 0; + CHECK(dlss_quality_twelfths() == 0); + CHECK(resolution_auto()); + CHECK(resolution_scale() == 0); + opts.dlss_quality = 0; opts.dlss = 2; + CHECK(quality_of(2560, 2560) == 5); + CHECK(quality_of(1707, 2560) == 2); + CHECK(quality_of(1494, 2560) == 1); + CHECK(quality_of(1280, 2560) == 0); + CHECK(quality_of(854, 2560) == 3); + std::printf("PASS: DLSS scale and NGX modes\n"); +} +''' + with tempfile.TemporaryDirectory(prefix="dlss-scale-") as tmp: + cpp = Path(tmp) / "fixture.cpp" + exe = Path(tmp) / "fixture" + cpp.write_text(fixture, encoding="utf-8") + subprocess.run([a.cxx, "-std=c++20", "-Wall", "-Wextra", "-Werror", str(cpp), "-o", str(exe)], check=True) + subprocess.run([str(exe)], check=True) + env = dict(os.environ) + env["RX_DLSS_OFF"] = "1" + subprocess.run([str(exe)], check=True, env=env) + + +if __name__ == "__main__": + main()