From f87cc7dde70e473af4d5e0f19011ac9ef939139d Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 9 Mar 2026 09:57:33 -0500 Subject: [PATCH] Add more rexcrt entries, add guest framerate counter --- CMakeLists.txt | 1 + ac6recomp_config.toml | 31 +++++++++++++++++++ src/ac6recomp_app.h | 11 ++++++- src/render_hooks.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++ src/render_hooks.h | 23 ++++++++++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/render_hooks.cpp create mode 100644 src/render_hooks.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 31ace1c2..57d537b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ include(generated/rexglue.cmake) # Sources set(AC6RECOMP_SOURCES src/main.cpp + src/render_hooks.cpp ) if(WIN32) diff --git a/ac6recomp_config.toml b/ac6recomp_config.toml index b6ca914d..11f09032 100644 --- a/ac6recomp_config.toml +++ b/ac6recomp_config.toml @@ -21,6 +21,14 @@ memcpy = 0x82382F18 memmove = 0x82383870 memset = 0x823830F0 XMemCpy = 0x82382A90 +strncmp = 0x82383930 +strncpy = 0x82382370 +strchr = 0x82382190 +strstr = 0x82382520 +strrchr = 0x8238E810 +strcpy_s = 0x823821D0 +memcpy_s = 0x82381DE0 +memmove_s = 0x823809F0 [functions] 0x82090000 = { name = "rex_sub_82090000" } @@ -10544,3 +10552,26 @@ XMemCpy = 0x82382A90 0x823E1250 = { name = "rex_sub_823E1250" } 0x823E1320 = { name = "rex_sub_823E1320" } 0x823E13F0 = { name = "rex_sub_823E13F0" } + +# --------------------------------------------------------------------------- +# Midasm hooks — 60fps unlock (Phase 1) +# --------------------------------------------------------------------------- + +# Skip `ori r3,r3,2` that sets vsync swap interval to 2 (30fps lock). +# When hook returns true, jumps past the ori to 0x821F075C. +[[midasm_hook]] +address = 0x821F0758 +name = "ac6VsyncIntervalHook" +jump_address_on_true = 0x821F075C + +# Override the FPS divisor in the delta time formula. +# r29 holds the divisor (default 60); hook sets it to 30 when unlocked. +[[midasm_hook]] +address = 0x821EFD38 +name = "ac6DeltaDivisorHook" +registers = ["r29"] + +# Record frame timestamps before VdSwap for timing overlay. +[[midasm_hook]] +address = 0x821F0664 +name = "ac6PresentTimingHook" diff --git a/src/ac6recomp_app.h b/src/ac6recomp_app.h index a63c07b9..3a15ab78 100644 --- a/src/ac6recomp_app.h +++ b/src/ac6recomp_app.h @@ -7,6 +7,9 @@ #include #include +#include + +#include "render_hooks.h" REXCVAR_DECLARE(bool, vfetch_index_rounding_bias); @@ -26,8 +29,14 @@ class Ac6recompApp : public rex::ReXApp { REXCVAR_SET(vfetch_index_rounding_bias, true); } + void OnCreateDialogs(rex::ui::ImGuiDrawer* drawer) override { + debug_overlay()->SetStatsProvider([] { + auto gs = ac6::GetFrameStats(); + return rex::ui::FrameStats{gs.frame_time_ms, gs.fps, gs.frame_count}; + }); + } + // void OnPostSetup() override {} - // void OnCreateDialogs(rex::ui::ImGuiDrawer* drawer) override {} // void OnShutdown() override {} // void OnConfigurePaths(rex::PathConfig& paths) override {} }; diff --git a/src/render_hooks.cpp b/src/render_hooks.cpp new file mode 100644 index 00000000..495ac9a6 --- /dev/null +++ b/src/render_hooks.cpp @@ -0,0 +1,71 @@ +/** + * @file render_hooks.cpp + * @brief Mid-asm hooks for vsync interval, frame timing, and 60fps unlock. + */ +#include "render_hooks.h" + +#include +#include + +#include +#include + +REXCVAR_DEFINE_BOOL(ac6_unlock_fps, false, "AC6", "Unlock frame rate to 60fps"); + +using Clock = std::chrono::steady_clock; + +namespace { + +// Frame timing state (written on render thread, read from UI thread). +std::atomic g_frame_time_ms{0.0}; +std::atomic g_fps{0.0}; +std::atomic g_frame_count{0}; +Clock::time_point g_frame_start{}; + +} // namespace + +// --------------------------------------------------------------------------- +// Hook: VdSetDisplayMode swap interval skip (0x821F0758) +// --------------------------------------------------------------------------- +bool ac6VsyncIntervalHook() { + return REXCVAR_GET(ac6_unlock_fps); +} + +// --------------------------------------------------------------------------- +// Hook: Delta time divisor override (0x821EFD38) +// --------------------------------------------------------------------------- +void ac6DeltaDivisorHook(PPCRegister& r29) { + if (REXCVAR_GET(ac6_unlock_fps)) { + r29.u64 = 30; + } +} + +// --------------------------------------------------------------------------- +// Hook: Frame timing stats (0x821F0664, before VdSwap call) +// +// Records timestamp at each present call to measure render-thread frame time. +// --------------------------------------------------------------------------- +void ac6PresentTimingHook() { + auto now = Clock::now(); + if (g_frame_start.time_since_epoch().count() != 0) { + double ms = + std::chrono::duration(now - g_frame_start) + .count(); + g_frame_time_ms.store(ms, std::memory_order_relaxed); + g_fps.store(ms > 0.0 ? 1000.0 / ms : 0.0, std::memory_order_relaxed); + g_frame_count.fetch_add(1, std::memory_order_relaxed); + } + g_frame_start = now; +} + +namespace ac6 { + +FrameStats GetFrameStats() { + return FrameStats{ + g_frame_time_ms.load(std::memory_order_relaxed), + g_fps.load(std::memory_order_relaxed), + g_frame_count.load(std::memory_order_relaxed), + }; +} + +} // namespace ac6 diff --git a/src/render_hooks.h b/src/render_hooks.h new file mode 100644 index 00000000..8949276f --- /dev/null +++ b/src/render_hooks.h @@ -0,0 +1,23 @@ +/** + * @file render_hooks.h + * @brief Mid-asm hooks for vsync interval, frame timing, and 60fps unlock. + */ +#pragma once + +#include + +#include + +REXCVAR_DECLARE(bool, ac6_unlock_fps); + +namespace ac6 { + +struct FrameStats { + double frame_time_ms; + double fps; + uint64_t frame_count; +}; + +FrameStats GetFrameStats(); + +} // namespace ac6