Skip to content

HWDER: 120 fps mode, controllers and local co-op, PC graphics options, setup GUI, stability and fixes

Choose a tag to compare

@blocker-3 blocker-3 released this 23 Oct 20:38
· 175 commits to main since this release

HWDER: 120 fps mode, controllers and local co-op, PC graphics options, setup GUI, stability and fixes

Draft for the push of local main (34873fc) to blocker-3/HWDER. Base: upstream sonsegajp/HWDER main 2a85d50 (v0.1.1). 220 commits, 161 files, +20,343 / -4,592. Every item below names the commit or merge it comes from.

Players: don't clone this repository. Download HWDER-setup-20261023.zip from the Releases page, extract it and open HWDER Setup.exe; the README's "Playing" section walks through Next, Install, Prepare and Play. The zip holds only the code-free setup exe and public source (no game code, keys or compilers).

Before: the recomp ran at 60 fps with a fixed window picture, keyboard plus basic pad input, no in-game menu, no startup checks, and needed a manual build with a hard-coded Visual Studio path.

After: an optional 120 fps mode, SDL3 controllers with remapping, rumble and two-player local co-op, an in-game menu with graphics options (FOV, FXAA, sharpening, contrast, bloom, ultrawide fill), a startup screen that checks the game files, a one-button Windows setup window, crash dumps and version stamps, and a number of rendering fixes.

Major items

  1. Guided Windows setup. HWDER Setup.exe walks a player through one button: Next (pick your own .nsp and prod.keys), Install (pinned build tools, SHA-256 checked, no admin rights), Prepare (builds the game from the player's own dump with a progress bar) and Play, with a desktop shortcut under Advanced. Ships as a setup zip with a player-first README; no Python, Visual Studio or repository clone needed. (14e957a, e0c7c0c, c76c34c, dbb20bb, 6b854a2)
  2. Local multiplayer (two-player co-op). The game's 2P mode works on PC: controllers take player slots in the order they connect, the keyboard joins the first free slot (or a fixed player, or off) in Settings > Input > Players, and each slot has its own remap, deadzones and rumble. The game's controller applet is answered with the connected player count, so its 2P mode starts, and the freeze on the warrior select controller screen is fixed. (a369d46, ce3aee5, 5860bb4, 1ae7e90, merged in 0adf36b)
  3. In-game UI. A tabbed HWDER menu (F1, Escape or hold View) that scales with the window and is fully usable with a controller: Display (120 fps, 60 Hz monitor switch, VSync, window size, borderless), Graphics (field of view, FXAA, sharpening, contrast, bloom, ultrawide fill, resolution, keep props), Input (remapping, deadzones, rumble, players), Audio and Gameplay (save folder), plus a startup screen that checks the game and keys. (1543470, c7fabb0, 4ef7f4c, 8c66fdb, 522b30a, e9668e0)
  4. 120 fps mode (Settings > Display > Frame rate > "120 fps (experimental)"). The game keeps its 60 logic steps per second; between two steps HWDER draws an extra frame with the camera, characters and objects blended halfway, presented at 120 Hz. Menus and loading screens stay at 60. Objects and bones are blended per whole block or not at all, which removed the jagged flashes. (03cc469, 875c35f, 1c88a71..5e677e4; research paths removed in eab5368)
  5. Controllers. SDL3 gamepads with remapping, deadzones, rumble and hot-plug; XInput fallback so a pad paired after launch works. A is the same button in HWDER's menus, the startup screen and gameplay, and new installs map face buttons by printed label. (63b2d43, 12c9f39, 66d46ed)
  6. Small stage props stay drawn in big battles (Graphics > "Keep props in big battles", on by default). Fences, rubble and other small scenery no longer vanish when a fight gets crowded. (4e96935)
  7. Stability, rendering fixes and faster starts. Present-race and index-range fixes, black-box fix, ultrawide fill and resize fixes, CPU texture write detection, Vulkan validation findings fixed, pipeline catalog and prewarm before the game starts, background cache persistence, crash minidumps, version stamp. (details below)
  8. Code structure. Named guest symbols and hook macros, the Vulkan renderer split into focused files, table-driven debug switches with a generated listing, and unit tests. (b92a493, 4dd73f2, 301f48e)
  9. Contrast Slider. Dramatic change to visuals by increasing the contrast higher than default on most monitors.

All details

120 fps / frame pacing

  • Game-drawn in-between frame per tick with a blended camera; 120 Hz release only while extra frames are drawn, skips the extra frame after a late tick; triple buffering under the mode (c909720, 4ece5df, a160707, merged in 03cc469).
  • Object and bone transforms blended in the in-between frame; ultrawide side bands no longer flash (852dbbb).
  • The 120 fps setting toggles only at frame boundaries (875c35f).
  • Whole-block blending: strict all-or-nothing per block (1c88a71), then relaxed pairing by block contents as the default (2578be0, 6be78bb, 78cd975, 1217aed, 5e677e4).
  • Rescaled render targets are recreated on next use, so queued 120 fps frames don't jump while resizing (9815003).
  • Widescreen: camera projections rebuild when the window aspect or FOV changes (d863fc8).
  • Removed the finished research paths: legacy Vulkan-level replay interpolation, camera-match mode, strict/legacy object modes, the frame probe and draw census, and their switches (d93c83d, merged in eab5368).
  • Settings honesty pass: the 60 Hz monitor switch is opt-in and restored on exit, resolution UI shows the real render size, hwder_settings.ini is versioned (v1 migrates to v2) (54fddad).

Input and co-op

  • SDL3 submodule (release-3.2.30, static, gamepads only); gamepads, rumble with preview, controller remapping in the Input tab, SDL-driven menu navigation; gyro camera mode dropped (1b2ba04, 1aea4e9, 4ef7f4c, 1ba51a9, 42bb63a).
  • XInput fallback while SDL has no controller open, so a pad paired after launch works without a restart (12c9f39).
  • Two-player local co-op (the game's 2P mode; the host side keeps four slots): controllers take player slots in connection order; keyboard slot in Settings > Input > Players (automatic / player N / off); per-slot remap, deadzones, View-hold and rumble (a369d46).
  • Input > Players offers the keyboard as Off, Automatic, Player 1 or Player 2 and lists two players; saved slots above 2 load as player 2 (b630068).
  • Controller applet is library applet 0x0C; it reads ControllerSupportArg, answers GetResult with the connected player count; the library-applet launchable event stays signaled, fixing the freeze on L3 at warrior select (ce3aee5, 5860bb4, 1ae7e90).
  • Menus confirm/back with the buttons bound to Switch A/B and the hints name them (cd38351); face buttons default to "by printed label" on first run only (3b1a9b7); the startup screen polls host input and accepts the same A/B (16b0b0f).

Graphics options and display

  • In-game menu: tabs, gamepad navigation, Escape / hold View to open, scales with the window, Segoe UI, gold theme (1543470, c7fabb0).
  • Graphics > Picture: field of view, FXAA, sharpening, contrast, bloom; unused depth of field / radial blur dropped (2edeee2, 1f1e387, f03dd17). Custom FOV is hidden while officer nameplates misalign (34c295f).
  • Fill ultrawide: widen the game camera to the window aspect, black side bands for movies and partial draws, preserved across queued presentations (3e304f2, 3769387, a453ab2).
  • Unlocked resolution fits 16:9 inside the window instead of stretching (08ab28a).
  • "Keep props in big battles": the game's docked crowd rule zeroes layer 1's draw distance above 60 counted characters; HWDER keeps it, with guards for the crowd-count pointer before a battle exists and for the distance scale (df6c056, fc650ac, ebc114f, 8d7d18d).
  • Configurable save folder on the startup screen and the menu's Gameplay tab (5e157ef); selected account save folders are recognized as save roots, fixing a duplicated leaf folder (8a21e70).

Rendering fixes and performance

  • Hold a presented buffer until its screen blit has read it; declare the present timeline before display init (ce6bf3f, 3ce919a).
  • Re-scan index ranges per submission; retry failed async pipelines once; reuse pending pipelines for synchronous draws (6cba553, 5cd1f83).
  • Render-target-sampling draws compile synchronously, fixing the black box (cbe8c0b).
  • CPU texture writes detected with write watch instead of timed re-hashing (acb38b2).
  • Vulkan validation: messages routed into hwder.log, start without the layer when it isn't installed, three findings fixed (present hazard, swapchain semaphore, sRGB view usage) (09c1059, 64721a5, 59713b8, 75659cd).
  • No double scaling of disabled-transform viewports; accurate nonconstant TEX offset counts (32122c3, 4b1117a).
  • Pipeline catalog: bounded warmup recipes with exact shader metadata, prewarm before guest startup, metadata-only catalogs with local game resolution, byte-unaligned NVN containers resolved, private history kept on partial imports (9013b31, 7ac4dbb, 95bafbc, cff149b, b20ef08, df950f1).
  • Cache persistence runs off the renderer thread with immutable snapshots (bc098c3, merged in 860684e).
  • The README and release notes tell players that a fresh install can hitch in the first minutes while pipelines compile, and that the saved pipeline_cache.bin / pipeline_warmup.bin are prebuilt on later launches until the HWDER version, GPU or driver changes (226a03e, 6b30a67).

Startup, setup and build

  • Clean clones build and boot: Visual Studio found with vswhere, entry seeds tracked, PLT0 discovered (805ca94).
  • Startup screen with game and key checks, file pickers and quick settings; unexpected title ID warns instead of blocking (39201b7, ab894db, 45c74c2).
  • First-run local build of the private game code, source setup kits, locked portable build modes, private generated-source paths, synthetic CI (7bdb2ea, ff941e1, f89b9bc, d6eb1f3, merged in e0c7c0c).
  • Windows setup window: one primary Next/Install/Prepare/Play button with an Advanced panel, pinned private tool installer with offline checks, LLVM-MinGW compatibility probe, step progress, packager requires the kit source to contain main's baseline (dac5e78, dac1404, f05f98e, ab61e05, 09f009d, merged in 14e957a).
  • README "Playing" now leads with the setup zip (unsigned-exe note, what players supply from their own Switch, each button); docs/windows-setup.md status, START HERE.txt and tools/kit_baseline.txt (main 97458d9) updated (c76c34c).
  • The setup kit drops top-level files the source no longer tracks (dbb20bb).
  • Setup's first Play: a new settings file carries face_labels=1, so new players get face buttons by printed label; Play no longer exports HWDER_NSP/HWDER_KEYS (their override warnings showed on the startup screen); kits carry version.txt so builds from a kit log a real version, and version stamps use v* tags only (6b854a2).
  • The Frame rate help no longer claims presentation is locked to 60 Hz (97458d9).
  • One machine-wide build lock in %LOCALAPPDATA%\hwder (45330df).
  • Hooks fall back to the original guest body when the runtime has no override, so older branches still link against newer generated code (351d0dc).

Diagnostics and tooling

  • Version stamp (git describe, commit, build date) in the log and the F1 menu (b96b506).
  • Crash minidump beside the log, 5 newest kept, HWDER_NO_DUMP=1 disables; nothing is uploaded (03025ee).
  • Bug report issue form and a README "Reporting a bug" section (b333a42).
  • Battle autoplay (cutscene skip, pre-battle menu) and windowed filmstrip capture for test runs (91b1f4e, 83a417d, merged in 2cf02d3).

Code structure

  • Game layer: named guest symbols, hook macros, checked guest structs (9730d33).
  • Renderer split into vk_sched, vk_resources, vk_pipelines, vk_interp_objects, vk_draw, vk_present, vk_debug; do_draw split into build_pipeline_key / read_indices / stage_vertex_input / stage_indices / record_draw; one gpu/diag.h (5a4e7bf..5d8d7e8, c7a3e4b..a621a39, 52da8b6).
  • Table-driven debug switches across all subsystems, with a generated README table and an F1 listing (d3ccb25..8b137f7, c8cd3e6).
  • Unit tests for renderer mappers, texture layout, IPC and handles (62347c8); IPC X-descriptor counter uses bits 9-11 (8316df8).

Testing

  • Clean worktree build (fresh checkout and submodules, empty build dir) of a2c3701 with tools/build.py: 622 steps, 5 min 13 s, no warnings.
  • tools/tests/run_synthetic.py --jobs 2: 50 Python tests OK; 5 native suites pass (pipeline warmup cache 443 checks, pipeline catalog 285, runtime unit 245, recompiler 8,896 comparisons).
  • Setup and managed-tool modules (test_setup_bridge, test_setup_dist, test_setup_play, test_setup_ux, test_managed_build, test_managed_progress, test_managed_tools): 59 tests OK.
  • Manual playtest by Max on the clean build: props at fence and rubble in a big battle, A on the startup screen / in-game menu / gameplay, 60 fps and 120 fps settings. All good.
  • Setup zip HWDER-setup-20261023.zip (14 MB, 2,250 entries, one exe; no game-derived, key or personal-path members) extracted to a new folder and run with a new Data folder through Next, Install, Prepare and Play.

Known issues

  • 120 fps: the Volga cutscene text box flashes; a slight ghost on Link at very high speed.
  • The video pillarbox shows purple garbage in both modes (low priority).
  • Keyboard as player 2 and ultrawide split-screen framing are untested.
  • Windows only; the native Linux runtime is not in this branch. HWDER Setup.exe is not code-signed (SmartScreen asks once), and setup has not yet been run on a second, clean machine. Without an AVX2 CPU or a Vulkan 1.2 driver the game closes at start with only a log line; a clear error is on branch startup-checks for the next release.
  • test_public_runtime_fixture (dev-only link probe for --managed-runtime-link, not on the player path) fails since the R7 merge: its guest-ID allowlist no longer matches the runtime's hook declarations and needs review.