chore: resolve merge conflicts, clean up workspace, update README

- Resolve merge conflicts in CMakeLists.txt, .gitignore, and all src/ files,
  keeping the HEAD (native renderer) versions throughout
- Remove stale SDK alternate-tree ref (rexglue-sdk~6e0ca2f) from index
- Drop scratch files: 'New Text Document.txt', *.bak
- Rewrite README with full setup instructions, prereqs table, build
  preset table, run command, project layout overview, and Linux section
This commit is contained in:
salh
2026-04-17 20:35:41 +03:00
11 changed files with 205 additions and 3683 deletions
+21 -1
View File
@@ -7,11 +7,18 @@ generated/
# Game assets (user-supplied)
assets/
*.xex
*.iso
*.god
*.zar
*.pkg
*.bin
# VS local settings
.vs/
.vscode/
# Copilot and debugging workflow
# Copilot and debugging workflow
.github/instructions/
# CMake user presets (machine-local)
@@ -19,3 +26,16 @@ CMakeUserPresets.json
# Local git worktrees
.worktrees/
# Local notes, backups, and scratch work
.claude/
backup/
OLD_XENON_BUILD/
rexglue_sdk_new/
autoresearch/
*.bak
*.tmp
tmp_*.py
build_*.txt
*.log
New Text Document.txt
+4
View File
@@ -0,0 +1,4 @@
[submodule "thirdparty/rexglue-sdk"]
path = thirdparty/rexglue-sdk
url = https://github.com/rapidsamphire/rexglue-sdk.git
branch = ac6recomp-fixes
+23
View File
@@ -0,0 +1,23 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "AC6Recomp (RelWithDebInfo)",
"type": "cppvsdbg",
"request": "launch",
"program": "C:\\Users\\Zalamander\\AC6Recomp\\out\\build\\win-amd64-relwithdebinfo\\ac6recomp.exe",
"args": [
"C:\\Users\\Zalamander\\AC6Recomp\\assets",
"--enable_console=false",
"--log_level=info",
"--d3d12_allow_variable_refresh_rate_and_tearing=true",
"--present_effect=fsr3",
"--present_fsr_quality_mode=nativeaa",
"--resolution=1440p",
"--fullscreen=true"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
}
]
}
-3
View File
@@ -26,8 +26,6 @@ set(AC6RECOMP_SOURCES
src/ac6_native_renderer/native_renderer.cpp
src/ac6_native_renderer/render_device.cpp
src/ac6_native_renderer/render_graph.cpp
src/render_hooks.cpp
src/d3d_hooks.cpp
)
if(WIN32)
@@ -37,4 +35,3 @@ else()
endif()
rexglue_setup_target(ac6recomp)
+152
View File
@@ -0,0 +1,152 @@
# AC6Recomp
> [!CAUTION]
> This project is still work in progress. It can boot and run in-game, but bugs, crashes, and missing functionality should be expected.
A native PC port of **Ace Combat 6: Fires of Liberation** (Xbox 360), built on top of the [ReXGlue SDK](https://github.com/rexglue/rexglue-sdk). The Xbox 360 PowerPC binary is statically recompiled to x86-64 so the original game logic runs natively on your host CPU, with a fully native D3D12/Vulkan renderer replacing the original Xenos GPU pipeline.
## Repository policy
This repository contains source code only.
Do **not** commit or redistribute:
- retail game data
- `default.xex`
- disc images, packages, title updates, or firmware files
- console keys or any other proprietary Microsoft / publisher material
Users must supply their own legally obtained game files locally.
---
## Prerequisites
| Tool | Version | Notes |
|---|---|---|
| [CMake](https://cmake.org/) | 3.25+ | |
| [Ninja](https://ninja-build.org/) | any recent | required generator |
| [Clang/LLVM](https://releases.llvm.org/) | any recent | `clang` / `clang++` must be on `PATH` |
| Windows SDK | 10.0.19041+ | D3D12 headers (Windows only) |
> [!NOTE]
> The Linux preset uses `clang-20` / `clang++-20` directly. Install the versioned binaries via your distro's package manager (`apt install clang-20`) or via the [LLVM APT repository](https://apt.llvm.org).
---
## Acquiring the game files
1. Obtain the original Xbox 360 disc image (ISO) by dumping your own disc.
Guides and tools: [consolemods.org ISO Extraction & Repacking](https://consolemods.org/wiki/Xbox:ISO_Extraction_%26_Repacking)
2. Extract the XEX and game data from the ISO.
3. Place the resulting files inside the `assets/` directory (created manually — it is git-ignored):
```text
assets/
default.xex ← required by the codegen step
media/ ← game data (audio, video, maps, …)
```
---
## Clone
```bash
git clone https://github.com/sal063/AC6_recomp.git
cd AC6_recomp
```
> [!NOTE]
> The ReXGlue SDK (`thirdparty/rexglue-sdk/`) is vendored directly in the repository. No submodule init is needed.
---
## Build
### 1 — Generate the recompiled code (first time, and after updating `default.xex`)
```bash
cmake --preset win-amd64-relwithdebinfo
cmake --build --preset win-amd64-relwithdebinfo --target ac6recomp_codegen
```
This step reads `assets/default.xex`, lifts all PowerPC instructions to C++, and writes the output to `generated/`. It can take a few minutes.
### 2 — Build the runtime
```bash
cmake --build --preset win-amd64-relwithdebinfo
```
The executable is placed at:
```
out/build/win-amd64-relwithdebinfo/ac6recomp.exe
```
> [!TIP]
> `RelWithDebInfo` is the recommended preset — it gives near-release performance with symbols intact for debugging. A full `Release` build disables assertions and can be used for distribution.
### Available presets
| Preset | Platform | Build type |
|---|---|---|
| `win-amd64-debug` | Windows | Debug |
| `win-amd64-release` | Windows | Release |
| `win-amd64-relwithdebinfo` | Windows | RelWithDebInfo ✅ recommended |
| `linux-amd64-debug` | Linux | Debug |
| `linux-amd64-release` | Linux | Release |
| `linux-amd64-relwithdebinfo` | Linux | RelWithDebInfo |
---
## Run
```bash
./out/build/win-amd64-relwithdebinfo/ac6recomp assets
```
The single argument is the path to the directory containing your game files (`assets/` by default). The runtime resolves all paths relative to it.
---
## Linux
Substitute `win-amd64-relwithdebinfo` with `linux-amd64-relwithdebinfo` in every command above.
```bash
cmake --preset linux-amd64-relwithdebinfo
cmake --build --preset linux-amd64-relwithdebinfo --target ac6recomp_codegen
cmake --build --preset linux-amd64-relwithdebinfo
./out/build/linux-amd64-relwithdebinfo/ac6recomp assets
```
---
## Project layout
```
AC6_recomp/
├── src/ Host-side runtime & renderer
│ ├── main.cpp
│ ├── ac6_native_graphics.* Xenon → native GPU command translation
│ ├── ac6_native_renderer/ Native rendering backend (D3D12 / Vulkan)
│ │ ├── backends/ Per-API backend implementations
│ │ ├── frame_plan.* Frame dependency graph construction
│ │ ├── frame_scheduler.* CPU/GPU timeline management
│ │ ├── native_renderer.* Top-level renderer orchestration
│ │ └── render_device.* Device abstraction layer
│ └── d3d_hooks.* Low-level D3D intercept layer
├── thirdparty/rexglue-sdk/ ReXGlue SDK (vendored)
├── assets/ ← NOT in repo; place your game files here
├── generated/ ← NOT in repo; output of codegen step
├── CMakeLists.txt
└── CMakePresets.json
```
---
## License
See [LICENSE](LICENSE).
Submodule autoresearch deleted from 228791fb49
File diff suppressed because it is too large Load Diff
+1 -180
View File
@@ -1,180 +1 @@
#pragma once
#include <cstdint>
#include <rex/cvar.h>
#include <rex/runtime.h>
#include "ac6_native_renderer/types.h"
#include "d3d_state.h"
REXCVAR_DECLARE(bool, ac6_allow_gpu_trace_stream);
REXCVAR_DECLARE(bool, ac6_native_present_enabled);
REXCVAR_DECLARE(bool, ac6_native_present_force_pm4_fallback);
REXCVAR_DECLARE(bool, ac6_native_present_enable_postfx);
REXCVAR_DECLARE(bool, ac6_native_present_enable_ui_compose);
REXCVAR_DECLARE(bool, ac6_native_present_allow_unstable);
namespace ac6::graphics {
enum class SwapRoutingMode : uint32_t {
kCompatPm4 = 0,
kNativeAuthoritative = 1,
kEmergencyHold = 2,
};
enum class SwapIngressPath : uint32_t {
kUnknown = 0,
kKernelDirectSwap = 1,
kPm4XeSwap = 2,
};
enum class SwapExecutionPath : uint32_t {
kUnknown = 0,
kDirectPresentSwap = 1,
kNativeRasterReplay = 2,
kNativeMinimalUav = 3,
kPm4Fallback = 4,
kEmergencyHoldNoPresent = 5,
};
enum class CadencePolicy : uint32_t {
kTimerVblank = 0,
kPresentCompletion = 1,
kHybridDebug = 2,
};
struct SelectedPassPreviewSummary {
bool valid{false};
uint32_t draw_count{0};
uint32_t clear_count{0};
uint32_t resolve_count{0};
uint32_t sampled_clear_color_count{0};
uint32_t sampled_clear_rect_count{0};
uint32_t first_clear_color{0};
uint32_t last_clear_color{0};
bool using_clear_fill{false};
bool using_raster_replay{false};
bool using_scene_stage_intermediate{false};
};
struct NativeReplayPlanSummary {
bool valid{false};
uint64_t frame_index{0};
uint32_t pass_count{0};
uint32_t swap_size_match_pass_count{0};
uint32_t draw_event_count{0};
uint32_t clear_event_count{0};
uint32_t resolve_event_count{0};
uint32_t largest_pass_draw_count{0};
uint32_t first_pass_draw_count{0};
uint32_t last_pass_draw_count{0};
uint32_t selected_pass_index{0};
uint32_t selected_pass_score{0};
uint32_t selected_pass_start_sequence{0};
uint32_t selected_pass_end_sequence{0};
uint32_t selected_pass_draw_count{0};
uint32_t selected_pass_clear_count{0};
uint32_t selected_pass_resolve_count{0};
uint32_t selected_pass_streak{0};
bool selected_pass_is_last{false};
bool selected_pass_has_resolve{false};
bool selected_pass_is_stable{false};
uint32_t present_candidate_rt0{0};
uint32_t present_candidate_depth_stencil{0};
uint32_t present_candidate_viewport_x{0};
uint32_t present_candidate_viewport_y{0};
uint32_t present_candidate_viewport_width{0};
uint32_t present_candidate_viewport_height{0};
bool present_candidate_matches_swap_size{false};
};
struct NativeGraphicsStatusSnapshot {
uint64_t decision_frame_index{0};
bool bootstrap_enabled{false};
bool parallel_renderer_enabled{false};
bool parallel_renderer_initialized{false};
bool backend_active{false};
bool placeholder_present_enabled{false};
bool native_present_enabled{false};
bool native_present_authoritative{false};
bool provider_ready{false};
bool presenter_ready{false};
bool placeholder_resources_initialized{false};
bool last_swap_intercepted{false};
bool last_swap_fell_back{false};
bool effects_capture_available{false};
bool effects_pass_classification_valid{false};
bool parity_confidence_good{false};
uint64_t total_swap_count{0};
uint64_t intercepted_swap_count{0};
uint64_t fallback_swap_count{0};
uint32_t last_frontbuffer_virtual_address{0};
uint32_t last_frontbuffer_physical_address{0};
uint32_t last_frontbuffer_width{0};
uint32_t last_frontbuffer_height{0};
uint32_t last_texture_format{0};
uint32_t last_color_space{0};
uint32_t last_present_mode{0};
uint32_t last_fallback_reason{0};
uint32_t configured_output_scale_percent{100};
uint32_t effective_output_width{0};
uint32_t effective_output_height{0};
uint32_t post_process_pass_count{0};
uint32_t ui_pass_count{0};
uint32_t scene_pass_count{0};
uint32_t missing_effects_counter{0};
uint32_t pixelation_counter{0};
uint32_t parity_confidence_score{0};
uint64_t parallel_renderer_frame_count{0};
uint64_t parallel_renderer_built_pass_count{0};
uint64_t parallel_renderer_transient_allocation_count{0};
uint32_t parallel_renderer_frame_slot{0};
uint32_t parallel_renderer_max_frames_in_flight{0};
uint32_t parallel_renderer_scene_pass_count{0};
uint32_t parallel_renderer_post_process_pass_count{0};
uint32_t parallel_renderer_ui_pass_count{0};
uint32_t parallel_renderer_selected_pass_index{0};
uint32_t parallel_renderer_total_draw_count{0};
uint32_t parallel_renderer_total_clear_count{0};
uint32_t parallel_renderer_total_resolve_count{0};
bool parallel_renderer_capture_valid{false};
bool parallel_frame_plan_valid{false};
bool parallel_frame_plan_has_scene_stage{false};
bool parallel_frame_plan_has_post_process_stage{false};
bool parallel_frame_plan_has_ui_stage{false};
bool parallel_frame_plan_present_from_selected_pass{false};
uint32_t parallel_frame_plan_scene_pass_index{0};
uint32_t parallel_frame_plan_post_process_pass_index{0};
uint32_t parallel_frame_plan_ui_pass_index{0};
uint32_t parallel_frame_plan_present_pass_index{0};
uint32_t parallel_frame_plan_scene_stage_score{0};
uint32_t parallel_frame_plan_post_process_stage_score{0};
uint32_t parallel_frame_plan_ui_stage_score{0};
uint32_t parallel_frame_plan_present_stage_score{0};
uint32_t parallel_frame_plan_scene_stage_draw_count{0};
uint32_t parallel_frame_plan_post_process_stage_draw_count{0};
uint32_t parallel_frame_plan_ui_stage_draw_count{0};
uint32_t parallel_frame_plan_scene_stage_texture_peak{0};
uint32_t parallel_frame_plan_post_process_stage_texture_peak{0};
uint32_t parallel_frame_plan_ui_stage_texture_peak{0};
uint32_t parallel_frame_plan_scene_stage_stream_peak{0};
uint32_t parallel_frame_plan_post_process_stage_stream_peak{0};
uint32_t parallel_frame_plan_ui_stage_stream_peak{0};
uint32_t parallel_frame_plan_output_width{0};
uint32_t parallel_frame_plan_output_height{0};
SwapRoutingMode routing_mode{SwapRoutingMode::kCompatPm4};
SwapIngressPath ingress_path{SwapIngressPath::kUnknown};
SwapExecutionPath execution_path{SwapExecutionPath::kUnknown};
CadencePolicy cadence_policy{CadencePolicy::kTimerVblank};
renderer::BackendType parallel_renderer_backend{renderer::BackendType::kUnknown};
renderer::FeatureLevel parallel_renderer_feature_level{renderer::FeatureLevel::kBootstrap};
d3d::FrameCaptureSummary capture_summary{};
NativeReplayPlanSummary replay_plan{};
SelectedPassPreviewSummary selected_pass_preview{};
};
void ConfigureGraphicsBackend(rex::RuntimeConfig& config);
NativeGraphicsStatusSnapshot GetNativeGraphicsStatus();
} // namespace ac6::graphics

+1 -378
View File
@@ -1,378 +1 @@
#include "ac6_native_graphics_overlay.h"
#include <rex/graphics/flags.h>
#include <rex/ui/keybinds.h>
#include <imgui.h>
#include "ac6_native_graphics.h"
#include "d3d_hooks.h"
#include "render_hooks.h"
namespace ac6::graphics {
namespace {
const char* DescribeNativeMode(const NativeGraphicsStatusSnapshot& status) {
if (!status.bootstrap_enabled) {
return "Disabled";
}
if (!status.backend_active) {
return "Configured, backend inactive";
}
if (status.routing_mode == SwapRoutingMode::kEmergencyHold) {
return "Emergency hold";
}
if (status.native_present_authoritative) {
return "Native present authoritative";
}
if (status.native_present_enabled) {
return "Native present gated (fallback active)";
}
if (status.placeholder_present_enabled) {
return "Diagnostic takeover";
}
return "Observe only";
}
const char* DescribeRoutingMode(SwapRoutingMode mode) {
switch (mode) {
case SwapRoutingMode::kNativeAuthoritative:
return "native";
case SwapRoutingMode::kEmergencyHold:
return "emergency";
case SwapRoutingMode::kCompatPm4:
default:
return "compat";
}
}
const char* DescribeExecutionPath(SwapExecutionPath path) {
switch (path) {
case SwapExecutionPath::kDirectPresentSwap:
return "direct-present-swap";
case SwapExecutionPath::kNativeRasterReplay:
return "native-raster-replay";
case SwapExecutionPath::kNativeMinimalUav:
return "native-minimal-uav";
case SwapExecutionPath::kPm4Fallback:
return "pm4-fallback";
case SwapExecutionPath::kEmergencyHoldNoPresent:
return "emergency-hold";
default:
return "unknown";
}
}
const char* DescribeCadencePolicy(CadencePolicy policy) {
switch (policy) {
case CadencePolicy::kPresentCompletion:
return "present";
case CadencePolicy::kHybridDebug:
return "hybrid";
case CadencePolicy::kTimerVblank:
default:
return "timer";
}
}
const char* DescribeParallelRendererBackend(ac6::renderer::BackendType backend) {
switch (backend) {
case ac6::renderer::BackendType::kD3D12:
return "d3d12";
case ac6::renderer::BackendType::kVulkan:
return "vulkan";
case ac6::renderer::BackendType::kMetal:
return "metal";
case ac6::renderer::BackendType::kUnknown:
default:
return "unknown";
}
}
const char* DescribeParallelRendererFeatureLevel(ac6::renderer::FeatureLevel level) {
switch (level) {
case ac6::renderer::FeatureLevel::kSceneSubmission:
return "scene_submission";
case ac6::renderer::FeatureLevel::kParityValidation:
return "parity_validation";
case ac6::renderer::FeatureLevel::kShipping:
return "shipping";
case ac6::renderer::FeatureLevel::kBootstrap:
default:
return "bootstrap";
}
}
const char* DescribeFallbackReason(uint32_t reason) {
switch (reason) {
case 1:
return "native present disabled";
case 2:
return "forced PM4 fallback";
case 3:
return "native resources unavailable";
case 4:
return "native composer failed";
case 5:
return "low parity confidence";
default:
return "none";
}
}
const char* DescribeLastSwapOutcome(const NativeGraphicsStatusSnapshot& status) {
if (!status.total_swap_count) {
return "No direct swaps yet";
}
if (status.last_swap_intercepted) {
return "Presented through native diagnostic path";
}
if (status.last_swap_fell_back) {
return "Handled by legacy PM4 presentation";
}
return "Observed";
}
} // namespace
NativeGraphicsStatusDialog::NativeGraphicsStatusDialog(rex::ui::ImGuiDrawer* imgui_drawer)
: ImGuiDialog(imgui_drawer) {
rex::ui::RegisterBind("bind_ac6_native_graphics_overlay", "F4",
"Toggle AC6 native graphics status overlay",
[this] { ToggleVisible(); });
}
NativeGraphicsStatusDialog::~NativeGraphicsStatusDialog() {
rex::ui::UnregisterBind("bind_ac6_native_graphics_overlay");
}
void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) {
if (!visible_) {
return;
}
NativeGraphicsStatusSnapshot native_status = GetNativeGraphicsStatus();
ac6::FrameStats frame_stats = ac6::GetFrameStats();
ac6::d3d::DrawStatsSnapshot draw_stats = ac6::d3d::GetDrawStats();
ImGui::SetNextWindowPos(ImVec2(10, 84), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(430, 486), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowBgAlpha(0.72f);
if (!ImGui::Begin("AC6 Native Graphics##status", &visible_, ImGuiWindowFlags_NoCollapse)) {
ImGui::End();
return;
}
ImGui::Text("Mode: %s", DescribeNativeMode(native_status));
ImGui::Text("Backend: %s | Provider: %s | Presenter: %s",
native_status.backend_active ? "active" : "inactive",
native_status.provider_ready ? "ready" : "missing",
native_status.presenter_ready ? "ready" : "missing");
ImGui::Text("Parallel renderer: %s | backend=%s | level=%s",
native_status.parallel_renderer_initialized ? "initialized" : "inactive",
DescribeParallelRendererBackend(native_status.parallel_renderer_backend),
DescribeParallelRendererFeatureLevel(
native_status.parallel_renderer_feature_level));
ImGui::Text("Parallel renderer frames: %llu | passes=%llu | slot=%u/%u",
static_cast<unsigned long long>(native_status.parallel_renderer_frame_count),
static_cast<unsigned long long>(native_status.parallel_renderer_built_pass_count),
native_status.parallel_renderer_frame_slot,
native_status.parallel_renderer_max_frames_in_flight);
ImGui::Text("Parallel capture: %s | selected pass=%u",
native_status.parallel_renderer_capture_valid ? "observed frame" : "bootstrap only",
native_status.parallel_renderer_selected_pass_index);
ImGui::Text("Parallel pass classes: scene=%u postfx=%u ui=%u",
native_status.parallel_renderer_scene_pass_count,
native_status.parallel_renderer_post_process_pass_count,
native_status.parallel_renderer_ui_pass_count);
ImGui::Text("Parallel workload: draws=%u clears=%u resolves=%u",
native_status.parallel_renderer_total_draw_count,
native_status.parallel_renderer_total_clear_count,
native_status.parallel_renderer_total_resolve_count);
ImGui::Text("Parallel frame plan: %s | output=%ux%u | selected-present=%s",
native_status.parallel_frame_plan_valid ? "valid" : "not ready",
native_status.parallel_frame_plan_output_width,
native_status.parallel_frame_plan_output_height,
native_status.parallel_frame_plan_present_from_selected_pass ? "yes" : "no");
ImGui::Text("Planned stages: scene=%s postfx=%s ui=%s present=%u",
native_status.parallel_frame_plan_has_scene_stage ? "yes" : "no",
native_status.parallel_frame_plan_has_post_process_stage ? "yes" : "no",
native_status.parallel_frame_plan_has_ui_stage ? "yes" : "no",
native_status.parallel_frame_plan_present_pass_index);
ImGui::Text("Planned pass indices: scene=%u postfx=%u ui=%u",
native_status.parallel_frame_plan_scene_pass_index,
native_status.parallel_frame_plan_post_process_pass_index,
native_status.parallel_frame_plan_ui_pass_index);
ImGui::Text("Planned stage scores: scene=%u postfx=%u ui=%u present=%u",
native_status.parallel_frame_plan_scene_stage_score,
native_status.parallel_frame_plan_post_process_stage_score,
native_status.parallel_frame_plan_ui_stage_score,
native_status.parallel_frame_plan_present_stage_score);
ImGui::Text("Stage draw counts: scene=%u postfx=%u ui=%u",
native_status.parallel_frame_plan_scene_stage_draw_count,
native_status.parallel_frame_plan_post_process_stage_draw_count,
native_status.parallel_frame_plan_ui_stage_draw_count);
ImGui::Text("Stage state peaks: tex %u/%u/%u | streams %u/%u/%u",
native_status.parallel_frame_plan_scene_stage_texture_peak,
native_status.parallel_frame_plan_post_process_stage_texture_peak,
native_status.parallel_frame_plan_ui_stage_texture_peak,
native_status.parallel_frame_plan_scene_stage_stream_peak,
native_status.parallel_frame_plan_post_process_stage_stream_peak,
native_status.parallel_frame_plan_ui_stage_stream_peak);
ImGui::Text("Native present: %s | authoritative=%s | PM4 force fallback=%s",
native_status.native_present_enabled ? "enabled" : "disabled",
native_status.native_present_authoritative ? "yes" : "no",
REXCVAR_GET(ac6_native_present_force_pm4_fallback) ? "yes" : "no");
ImGui::Text("Routing: %s | Exec: %s | Cadence: %s",
DescribeRoutingMode(native_status.routing_mode),
DescribeExecutionPath(native_status.execution_path),
DescribeCadencePolicy(native_status.cadence_policy));
ImGui::Text("Runtime controls: postfx=%s ui_compose=%s allow_unstable=%s",
REXCVAR_GET(ac6_native_present_enable_postfx) ? "on" : "off",
REXCVAR_GET(ac6_native_present_enable_ui_compose) ? "on" : "off",
REXCVAR_GET(ac6_native_present_allow_unstable) ? "on" : "off");
ImGui::Text("Placeholder resources: %s",
native_status.placeholder_resources_initialized ? "initialized" : "inactive");
ImGui::Text("GPU trace stream: %s | allow=%s", REXCVAR_GET(trace_gpu_stream) ? "on" : "off",
REXCVAR_GET(ac6_allow_gpu_trace_stream) ? "true" : "false");
ImGui::Separator();
ImGui::Text("Direct swaps: %llu total | %llu native | %llu legacy",
static_cast<unsigned long long>(native_status.total_swap_count),
static_cast<unsigned long long>(native_status.intercepted_swap_count),
static_cast<unsigned long long>(native_status.fallback_swap_count));
ImGui::Text("Last outcome: %s", DescribeLastSwapOutcome(native_status));
ImGui::Text("Fallback reason: %s", DescribeFallbackReason(native_status.last_fallback_reason));
ImGui::Text("Output scale: %u%% -> %ux%u", native_status.configured_output_scale_percent,
native_status.effective_output_width, native_status.effective_output_height);
ImGui::Text("Last frontbuffer: %ux%u fmt %08X cs %u", native_status.last_frontbuffer_width,
native_status.last_frontbuffer_height, native_status.last_texture_format,
native_status.last_color_space);
ImGui::Text("Last FB VA/PA: %08X / %08X", native_status.last_frontbuffer_virtual_address,
native_status.last_frontbuffer_physical_address);
ImGui::Separator();
ImGui::Text("Capture: %s frame=%llu draws=%u clears=%u resolves=%u",
native_status.capture_summary.capture_enabled ? "enabled" : "disabled",
static_cast<unsigned long long>(native_status.capture_summary.frame_index),
native_status.capture_summary.draw_count, native_status.capture_summary.clear_count,
native_status.capture_summary.resolve_count);
if (native_status.capture_summary.record_signature_valid) {
ImGui::Text("Record signature: %016llX",
static_cast<unsigned long long>(native_status.capture_summary.record_signature));
ImGui::Text("Recorded draw mix: indexed=%u shared=%u primitive=%u",
native_status.capture_summary.indexed_draw_count,
native_status.capture_summary.indexed_shared_draw_count,
native_status.capture_summary.primitive_draw_count);
ImGui::Text("Recorded RT0s: unique=%u switches=%u",
native_status.capture_summary.unique_rt0_count,
native_status.capture_summary.rt0_switch_count);
ImGui::Text("Recorded RT0 first/last: %08X / %08X",
native_status.capture_summary.first_draw_render_target_0,
native_status.capture_summary.last_draw_render_target_0);
} else {
ImGui::TextUnformatted("Record signature: unavailable (enable ac6_render_capture)");
}
ImGui::Text("Frame-end RT0/DS: %08X / %08X",
native_status.capture_summary.frame_end_render_target_0,
native_status.capture_summary.frame_end_depth_stencil);
ImGui::Text("Frame-end viewport: %ux%u | RTs=%u tex=%u fetch=%u",
native_status.capture_summary.frame_end_viewport_width,
native_status.capture_summary.frame_end_viewport_height,
native_status.capture_summary.frame_end_render_target_count,
native_status.capture_summary.frame_end_texture_count,
native_status.capture_summary.frame_end_texture_fetch_count);
ImGui::Text("Frame-end streams=%u samplers=%u | last draw prim=%u count=%u flags=%u",
native_status.capture_summary.frame_end_stream_count,
native_status.capture_summary.frame_end_sampler_count,
native_status.capture_summary.last_draw_primitive_type,
native_status.capture_summary.last_draw_count,
native_status.capture_summary.last_draw_flags);
ImGui::Separator();
if (native_status.replay_plan.valid) {
ImGui::Text("Replay plan: frame=%llu passes=%u draws=%u clears=%u resolves=%u",
static_cast<unsigned long long>(native_status.replay_plan.frame_index),
native_status.replay_plan.pass_count,
native_status.replay_plan.draw_event_count,
native_status.replay_plan.clear_event_count,
native_status.replay_plan.resolve_event_count);
ImGui::Text("Swap-size matching passes: %u | selected=%u score=%u",
native_status.replay_plan.swap_size_match_pass_count,
native_status.replay_plan.selected_pass_index,
native_status.replay_plan.selected_pass_score);
ImGui::Text("Pass draw counts: first=%u last=%u largest=%u",
native_status.replay_plan.first_pass_draw_count,
native_status.replay_plan.last_pass_draw_count,
native_status.replay_plan.largest_pass_draw_count);
ImGui::Text("Selected pass seq: %u..%u | draws=%u clears=%u resolves=%u",
native_status.replay_plan.selected_pass_start_sequence,
native_status.replay_plan.selected_pass_end_sequence,
native_status.replay_plan.selected_pass_draw_count,
native_status.replay_plan.selected_pass_clear_count,
native_status.replay_plan.selected_pass_resolve_count);
ImGui::Text("Selected pass flags: last=%s resolve=%s stable=%s streak=%u",
native_status.replay_plan.selected_pass_is_last ? "yes" : "no",
native_status.replay_plan.selected_pass_has_resolve ? "yes" : "no",
native_status.replay_plan.selected_pass_is_stable ? "yes" : "no",
native_status.replay_plan.selected_pass_streak);
ImGui::Text("Present candidate RT0/DS: %08X / %08X",
native_status.replay_plan.present_candidate_rt0,
native_status.replay_plan.present_candidate_depth_stencil);
ImGui::Text("Present candidate viewport: %u,%u %ux%u | swap match=%s",
native_status.replay_plan.present_candidate_viewport_x,
native_status.replay_plan.present_candidate_viewport_y,
native_status.replay_plan.present_candidate_viewport_width,
native_status.replay_plan.present_candidate_viewport_height,
native_status.replay_plan.present_candidate_matches_swap_size ? "yes" : "no");
if (native_status.selected_pass_preview.valid) {
ImGui::Text("Selected-pass preview: source=%s sampled clears=%u captured rects=%u",
native_status.selected_pass_preview.using_clear_fill
? "captured clear colors"
: "fallback candidate tint",
native_status.selected_pass_preview.sampled_clear_color_count,
native_status.selected_pass_preview.sampled_clear_rect_count);
ImGui::Text("Preview events: draws=%u clears=%u resolves=%u",
native_status.selected_pass_preview.draw_count,
native_status.selected_pass_preview.clear_count,
native_status.selected_pass_preview.resolve_count);
ImGui::Text("Preview renderer: %s",
native_status.selected_pass_preview.using_raster_replay
? "offscreen raster replay"
: "legacy diagnostic fill");
if (native_status.selected_pass_preview.clear_count) {
ImGui::Text("Preview clear colors: first=%08X last=%08X",
native_status.selected_pass_preview.first_clear_color,
native_status.selected_pass_preview.last_clear_color);
}
}
} else {
ImGui::TextUnformatted("Replay plan: unavailable (no captured frame events yet)");
}
ImGui::Separator();
ImGui::Text("Parity confidence: %u (%s)", native_status.parity_confidence_score,
native_status.parity_confidence_good ? "good" : "low");
ImGui::Text("Effects classification: %s | capture=%s",
native_status.effects_pass_classification_valid ? "valid" : "pending",
native_status.effects_capture_available ? "available" : "none");
ImGui::Text("Pass classes: scene=%u postfx=%u ui=%u", native_status.scene_pass_count,
native_status.post_process_pass_count, native_status.ui_pass_count);
ImGui::Text("Visual issue counters: missing_fx=%u pixelation=%u",
native_status.missing_effects_counter, native_status.pixelation_counter);
ImGui::Separator();
ImGui::Text("Guest frame: %.1f FPS (%.2f ms) frame=%llu", frame_stats.fps,
frame_stats.frame_time_ms, static_cast<unsigned long long>(frame_stats.frame_count));
ImGui::Text("D3D stats: draws=%u clears=%u resolves=%u", draw_stats.draw_calls,
draw_stats.clear_calls, draw_stats.resolve_calls);
ImGui::Text("Indexed=%u shared=%u primitive=%u", draw_stats.draw_calls_indexed,
draw_stats.draw_calls_indexed_shared, draw_stats.draw_calls_primitive);
ImGui::Separator();
ImGui::TextUnformatted("F4 toggles this panel. F3 toggles the base debug overlay.");
ImGui::End();
}
} // namespace ac6::graphics

+1 -88
View File
@@ -1,88 +1 @@
#pragma once
#include <string>
#include <rex/cvar.h>
#include <rex/graphics/flags.h>
#include <rex/logging.h>
#include <rex/rex_app.h>
#include <rex/ui/overlay/debug_overlay.h>
#include <rex/version.h>
#include "ac6_native_graphics.h"
#include "ac6_native_graphics_overlay.h"
#include "render_hooks.h"
REXCVAR_DECLARE(bool, vfetch_index_rounding_bias);
REXCVAR_DECLARE(bool, guest_vblank_sync_to_refresh);
class Ac6recompApp : public rex::ReXApp {
public:
using rex::ReXApp::ReXApp;
static std::unique_ptr<rex::ui::WindowedApp> Create(
rex::ui::WindowedAppContext& ctx) {
return std::unique_ptr<Ac6recompApp>(new Ac6recompApp(ctx, "ac6recomp",
PPCImageConfig));
}
protected:
void OnPreSetup(rex::RuntimeConfig& config) override {
REXCVAR_SET(vfetch_index_rounding_bias, true);
if (REXCVAR_GET(trace_gpu_stream) && !REXCVAR_GET(ac6_allow_gpu_trace_stream)) {
gpu_trace_stream_blocked_ = true;
REXLOG_WARN(
"AC6 safety guard disabled trace_gpu_stream to avoid unstable GPU "
"trace compression during native graphics experiments. Set "
"ac6_allow_gpu_trace_stream=true to re-enable it explicitly.");
REXCVAR_SET(trace_gpu_stream, false);
}
ac6::graphics::ConfigureGraphicsBackend(config);
// Keep timing/sync policy deterministic across launches.
// If unlock is disabled, force sync back on so stale config/runtime state
// cannot leave AC6 running uncapped.
if (REXCVAR_GET(ac6_unlock_fps)) {
REXCVAR_SET(vsync, false);
REXCVAR_SET(guest_vblank_sync_to_refresh, false);
} else {
REXCVAR_SET(vsync, true);
REXCVAR_SET(guest_vblank_sync_to_refresh, 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};
});
native_graphics_status_dialog_ =
std::make_unique<ac6::graphics::NativeGraphicsStatusDialog>(drawer);
if (window()) {
std::string title = std::string(GetName()) + " " + REXGLUE_BUILD_TITLE;
auto native_status = ac6::graphics::GetNativeGraphicsStatus();
if (native_status.bootstrap_enabled) {
if (native_status.routing_mode == ac6::graphics::SwapRoutingMode::kEmergencyHold) {
title += " | native emergency";
} else if (native_status.native_present_authoritative) {
title += " | native swap present";
} else if (native_status.native_present_enabled) {
title += " | native swap gated";
} else if (native_status.placeholder_present_enabled) {
title += " | native swap diagnostic";
} else {
title += " | native swap observe";
}
}
if (gpu_trace_stream_blocked_) {
title += " | trace stream blocked";
}
window()->SetTitle(title);
}
}
private:
bool gpu_trace_stream_blocked_ = false;
std::unique_ptr<ac6::graphics::NativeGraphicsStatusDialog> native_graphics_status_dialog_;
};

+1 -29
View File
@@ -1,29 +1 @@
/**
* @file d3d_hooks.h
* @brief Public interface for the D3D state shadowing layer.
*
* Provides per-frame draw statistics, a frame capture snapshot for renderer
* analysis, and a read-only view of the current D3D device shadow state.
* Call OnFrameBoundary() once per frame (typically from the present/swap hook)
* to snapshot stats and reset counters.
*/
#pragma once
#include <rex/cvar.h>
#include "d3d_state.h"
namespace ac6::d3d {
void OnFrameBoundary();
DrawStatsSnapshot GetDrawStats();
FrameCaptureSnapshot GetFrameCapture();
FrameCaptureSummary GetFrameCaptureSummary();
ShadowState GetShadowState();
} // namespace ac6::d3d
REXCVAR_DECLARE(bool, ac6_render_capture);