mirror of
https://github.com/sal063/AC6_recomp
synced 2026-05-23 06:54:32 -04:00
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:
+21
-1
@@ -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
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[submodule "thirdparty/rexglue-sdk"]
|
||||
path = thirdparty/rexglue-sdk
|
||||
url = https://github.com/rapidsamphire/rexglue-sdk.git
|
||||
branch = ac6recomp-fixes
|
||||
|
||||
Vendored
+23
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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).
|
||||
-1
Submodule autoresearch deleted from 228791fb49
+1
-3003
File diff suppressed because it is too large
Load Diff
+1
-180
@@ -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 +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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user