mirror of
https://github.com/sal063/AC6_recomp
synced 2026-06-06 19:52:05 -04:00
Optimize active capture path and enable release LTO
This commit is contained in:
@@ -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
|
||||
$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:-fomit-frame-pointer>
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -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
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
+21
-5
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <rex/cvar.h>
|
||||
#include <rex/logging.h>
|
||||
@@ -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<uint32_t> unique_rt0s;
|
||||
std::unordered_set<uint32_t> unique_rt0s;
|
||||
unique_rt0s.reserve(std::min<size_t>(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<std::shared_mutex> lock(g_snapshot_mutex);
|
||||
g_snapshot = draw_stats;
|
||||
lock.unlock();
|
||||
|
||||
if (!REXCVAR_GET(ac6_render_capture)) {
|
||||
std::unique_lock<std::shared_mutex> 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);
|
||||
|
||||
+15
-21
@@ -2,8 +2,8 @@
|
||||
#include "d3d_hooks.h"
|
||||
#include "ac6_native_graphics.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
|
||||
#include <rex/cvar.h>
|
||||
#include <rex/system/kernel_state.h>
|
||||
@@ -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<double> g_frame_time_ms{0.0};
|
||||
std::atomic<double> g_fps{0.0};
|
||||
std::atomic<uint64_t> 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<std::mutex> lock(g_frame_mutex);
|
||||
if (g_frame_start.time_since_epoch().count() != 0) {
|
||||
g_frame_time_ms =
|
||||
std::chrono::duration<double, std::milli>(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<double, std::milli>(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<std::mutex> 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
|
||||
|
||||
Reference in New Issue
Block a user