From 9c1d548dc435bb6d9b577d8e72e15dd5a58db037 Mon Sep 17 00:00:00 2001 From: salh Date: Wed, 29 Apr 2026 09:05:48 +0300 Subject: [PATCH] Optimize active capture path and enable release LTO --- CMakeLists.txt | 16 +++++++++ .../win-amd64-relwithdebinfo/ac6recomp.toml | 14 ++++++-- src/ac6_backend_fixes/ac6_backend_hooks.cpp | 25 ++++++++----- src/ac6_backend_fixes/ac6_backend_hooks.h | 1 + src/ac6_native_graphics.cpp | 18 +++++++--- src/d3d_hooks.cpp | 26 +++++++++++--- src/render_hooks.cpp | 36 ++++++++----------- 7 files changed, 94 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a61bfc41..114b76ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,9 @@ project(ac6recomp LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) +include(CheckIPOSupported) +check_ipo_supported(RESULT AC6RECOMP_IPO_SUPPORTED OUTPUT AC6RECOMP_IPO_ERROR) + # Keep reconfigure passes pinned to the vendored SDK when the cache is empty. if(NOT DEFINED REXSDK_DIR OR REXSDK_DIR STREQUAL "") set(_ac6_vendored_rexsdk "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/rexglue-sdk") @@ -60,3 +63,16 @@ else() endif() rexglue_setup_target(ac6recomp) + +if(AC6RECOMP_IPO_SUPPORTED) + set_property(TARGET ac6recomp PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) + set_property(TARGET ac6recomp PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) +else() + message(STATUS "IPO/LTO disabled for ac6recomp: ${AC6RECOMP_IPO_ERROR}") +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(ac6recomp PRIVATE + $<$,$>:-fomit-frame-pointer> + ) +endif() diff --git a/out/build/win-amd64-relwithdebinfo/ac6recomp.toml b/out/build/win-amd64-relwithdebinfo/ac6recomp.toml index 54b636b5..9c1aab4b 100644 --- a/out/build/win-amd64-relwithdebinfo/ac6recomp.toml +++ b/out/build/win-amd64-relwithdebinfo/ac6recomp.toml @@ -1,14 +1,22 @@ # Auto-generated cvar configuration ac6_render_capture = false -ac6_timing_hooks_enabled = false +ac6_unlock_fps = false +ac6_native_graphics_require_capture = false +vsync = true +guest_vblank_sync_to_refresh = true +host_present_from_non_ui_thread = false +d3d12_allow_variable_refresh_rate_and_tearing = false log_level = "debug" log_file = "ac6recomp.log" video_mode_width = 1920 video_mode_height = 1080 resolution = "1080p" direct_host_resolve = false -guest_vblank_sync_to_refresh = true window_width = 1920 window_height = 1080 -ac6_effect_log_point_sampler_override = true vfetch_index_rounding_bias = true +d3d12_expand_point_sprites_in_vs = true +d3d12_debug = true +dump_shaders = "shaders" +d3d12_dxbc_disasm = true +d3d12_dxbc_disasm_dxilconv = true \ No newline at end of file diff --git a/src/ac6_backend_fixes/ac6_backend_hooks.cpp b/src/ac6_backend_fixes/ac6_backend_hooks.cpp index 3a278693..cd21ce28 100644 --- a/src/ac6_backend_fixes/ac6_backend_hooks.cpp +++ b/src/ac6_backend_fixes/ac6_backend_hooks.cpp @@ -12,6 +12,8 @@ REXCVAR_DEFINE_BOOL(ac6_backend_debug_swap, false, "AC6/Backend", "Log AC6 swap-path diagnostics from the authoritative backend"); REXCVAR_DEFINE_BOOL(ac6_backend_log_signatures, false, "AC6/Backend", "Log AC6 capture signatures used for backend-fix routing"); +REXCVAR_DEFINE_BOOL(ac6_backend_signature_diagnostics, false, "AC6/Backend", + "Track per-frame render signatures for diagnostics overlays and backend-fix research"); namespace ac6::backend { namespace { @@ -113,14 +115,21 @@ void AnalyzeFrameBoundary( g_snapshot.audio_callback_throttle_count = 0; } - g_snapshot.latest_signature = BuildRenderEventSignature( - frame_capture, capture_summary, shadow_state, swap_submission, - g_snapshot.active_vertex_shader_hash, g_snapshot.active_pixel_shader_hash); - g_snapshot.latest_signature.classification = - ClassifySignature(g_snapshot.latest_signature); - g_snapshot.latest_signature_tags = BuildSignatureTags(g_snapshot.latest_signature); - g_snapshot.repeated_signature_count = - ++g_signature_hits[g_snapshot.latest_signature.stable_id]; + if (REXCVAR_GET(ac6_backend_signature_diagnostics) || + REXCVAR_GET(ac6_backend_log_signatures)) { + g_snapshot.latest_signature = BuildRenderEventSignature( + frame_capture, capture_summary, shadow_state, swap_submission, + g_snapshot.active_vertex_shader_hash, g_snapshot.active_pixel_shader_hash); + g_snapshot.latest_signature.classification = + ClassifySignature(g_snapshot.latest_signature); + g_snapshot.latest_signature_tags = BuildSignatureTags(g_snapshot.latest_signature); + g_snapshot.repeated_signature_count = + ++g_signature_hits[g_snapshot.latest_signature.stable_id]; + } else { + g_snapshot.latest_signature = {}; + g_snapshot.latest_signature_tags.clear(); + g_snapshot.repeated_signature_count = 0; + } if (ShouldLogSignature(g_snapshot)) { REXLOG_INFO( diff --git a/src/ac6_backend_fixes/ac6_backend_hooks.h b/src/ac6_backend_fixes/ac6_backend_hooks.h index fa56a18f..921b3229 100644 --- a/src/ac6_backend_fixes/ac6_backend_hooks.h +++ b/src/ac6_backend_fixes/ac6_backend_hooks.h @@ -13,6 +13,7 @@ REXCVAR_DECLARE(bool, ac6_backend_debug_swap); REXCVAR_DECLARE(bool, ac6_backend_log_signatures); +REXCVAR_DECLARE(bool, ac6_backend_signature_diagnostics); namespace ac6 { struct FrameStats; diff --git a/src/ac6_native_graphics.cpp b/src/ac6_native_graphics.cpp index b012274e..80b8bfb6 100644 --- a/src/ac6_native_graphics.cpp +++ b/src/ac6_native_graphics.cpp @@ -21,7 +21,7 @@ REXCVAR_DEFINE_BOOL(ac6_native_graphics_enabled, true, "AC6/NativeGraphics", "Enable AC6 graphics capture analysis, overlay reporting, and backend fixes"); -REXCVAR_DEFINE_BOOL(ac6_native_graphics_require_capture, true, "AC6/NativeGraphics", +REXCVAR_DEFINE_BOOL(ac6_native_graphics_require_capture, false, "AC6/NativeGraphics", "Keep render capture enabled while AC6 graphics analysis is active"); REXCVAR_DEFINE_BOOL(ac6_force_safe_render_capture, true, "AC6/NativeGraphics", "Force AC6 hybrid backend fixes mode to keep per-draw render capture disabled until the capture path is stabilized"); @@ -423,10 +423,18 @@ void OnFrameBoundary(rex::memory::Memory* memory) { g_runtime_status.capture_summary = capture_summary; g_runtime_status.latest_capture_frame_index = capture_summary.frame_index; g_runtime_status.frontend_summary = g_capture_frontend.BuildFromCapture(frame_capture); - RefreshPassDiagnostics(g_capture_frontend.passes()); - RefreshResolveDiagnostics(frame_capture); - g_runtime_status.frame_plan = g_capture_frame_planner.Build( - g_runtime_status.frontend_summary, g_capture_frontend.passes()); + if (g_runtime_status.frontend_summary.capture_valid) { + RefreshPassDiagnostics(g_capture_frontend.passes()); + RefreshResolveDiagnostics(frame_capture); + g_runtime_status.frame_plan = g_capture_frame_planner.Build( + g_runtime_status.frontend_summary, g_capture_frontend.passes()); + } else { + g_runtime_status.pass_diagnostics_count = 0; + g_runtime_status.pass_diagnostics = {}; + g_runtime_status.resolve_diagnostics_count = 0; + g_runtime_status.resolve_diagnostics = {}; + g_runtime_status.frame_plan = {}; + } if (capture_summary.draw_count || capture_summary.clear_count || capture_summary.resolve_count) { g_runtime_status.last_meaningful_capture_frame_index = capture_summary.frame_index; diff --git a/src/d3d_hooks.cpp b/src/d3d_hooks.cpp index c0b6e060..3112e594 100644 --- a/src/d3d_hooks.cpp +++ b/src/d3d_hooks.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -194,7 +195,8 @@ ac6::d3d::FrameCaptureSummary MakeFrameCaptureSummary( if (summary.capture_enabled && !frame_capture.draws.empty()) { constexpr uint64_t kFnvOffsetBasis = 1469598103934665603ull; uint64_t signature = kFnvOffsetBasis; - std::vector unique_rt0s; + std::unordered_set unique_rt0s; + unique_rt0s.reserve(std::min(frame_capture.draws.size(), 128)); uint32_t previous_rt0 = frame_capture.draws.front().shadow_state.render_targets[0]; for (const auto& draw : frame_capture.draws) { switch (draw.kind) { @@ -210,9 +212,7 @@ ac6::d3d::FrameCaptureSummary MakeFrameCaptureSummary( } IncrementTopologyHistogram(summary, draw.primitive_type); uint32_t rt0 = draw.shadow_state.render_targets[0]; - if (std::find(unique_rt0s.begin(), unique_rt0s.end(), rt0) == unique_rt0s.end()) { - unique_rt0s.push_back(rt0); - } + unique_rt0s.insert(rt0); if (rt0 != previous_rt0) { ++summary.rt0_switch_count; previous_rt0 = rt0; @@ -787,13 +787,29 @@ PPC_FUNC_IMPL(rex_sub_821E10C8) { namespace ac6::d3d { void OnFrameBoundary() { - REXLOG_INFO("d3d::OnFrameBoundary: frame_index={}", g_capture_live_frame_index); ac6::d3d::DrawStatsSnapshot draw_stats = SnapshotDrawStats(); std::unique_lock lock(g_snapshot_mutex); g_snapshot = draw_stats; lock.unlock(); + if (!REXCVAR_GET(ac6_render_capture)) { + std::unique_lock capture_lock(g_capture_mutex); + g_capture_summary = {}; + g_capture_summary.capture_enabled = false; + g_capture_summary.frame_index = g_capture_live_frame_index++; + g_capture_summary.frame_stats = draw_stats; + g_capture_snapshot = {}; + g_capture_snapshot.frame_index = g_capture_summary.frame_index; + g_capture_snapshot.stats = draw_stats; + g_capture_live_sequence = 0; + g_live_draws.clear(); + g_live_clears.clear(); + g_live_resolves.clear(); + g_live_stats.Reset(); + return; + } + ac6::d3d::FrameCaptureSnapshot frame_capture; frame_capture.stats = draw_stats; frame_capture.frame_end_shadow = SnapshotShadowState(0); diff --git a/src/render_hooks.cpp b/src/render_hooks.cpp index 83405b4f..cf16cb5a 100644 --- a/src/render_hooks.cpp +++ b/src/render_hooks.cpp @@ -2,8 +2,8 @@ #include "d3d_hooks.h" #include "ac6_native_graphics.h" +#include #include -#include #include #include @@ -16,10 +16,9 @@ using Clock = std::chrono::steady_clock; namespace { -std::mutex g_frame_mutex; -double g_frame_time_ms{0.0}; -double g_fps{0.0}; -uint64_t g_frame_count{0}; +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{}; bool AreTimingHooksActive() { @@ -48,31 +47,26 @@ void ac6DeltaDivisorHook(PPCRegister& r29) { } void ac6PresentTimingHook(PPCRegister& /*r31*/) { - static uint64_t last_log = 0; - if (g_frame_count % 60 == 0 && g_frame_count != last_log) { - REXLOG_INFO("ac6PresentTimingHook firing: frame={}", g_frame_count); - last_log = g_frame_count; - } // ac6::d3d::OnFrameBoundary(); // MOVED TO GPU THREAD const auto now = Clock::now(); - { - std::lock_guard lock(g_frame_mutex); - if (g_frame_start.time_since_epoch().count() != 0) { - g_frame_time_ms = - std::chrono::duration(now - g_frame_start).count(); - g_fps = g_frame_time_ms > 0.0001 ? (1000.0 / g_frame_time_ms) : 0.0; - ++g_frame_count; - } - g_frame_start = now; + if (g_frame_start.time_since_epoch().count() != 0) { + const double frame_time_ms = + std::chrono::duration(now - g_frame_start).count(); + g_frame_time_ms.store(frame_time_ms, std::memory_order_relaxed); + g_fps.store(frame_time_ms > 0.0001 ? (1000.0 / frame_time_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() { - std::lock_guard lock(g_frame_mutex); - return FrameStats{g_frame_time_ms, g_fps, g_frame_count}; + 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