diff --git a/.gitignore b/.gitignore index 27f3739d..7d620b31 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.gitmodules b/.gitmodules index e69de29b..db1b5c3b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "thirdparty/rexglue-sdk"] + path = thirdparty/rexglue-sdk + url = https://github.com/rapidsamphire/rexglue-sdk.git + branch = ac6recomp-fixes diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..3e6fa827 --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt index d59f6ff9..4772bcb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) - diff --git a/README.md b/README.md new file mode 100644 index 00000000..7e7ec452 --- /dev/null +++ b/README.md @@ -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). diff --git a/autoresearch b/autoresearch deleted file mode 160000 index 228791fb..00000000 --- a/autoresearch +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 228791fb499afffb54b46200aca536f79142f117 diff --git a/src/ac6_native_graphics.cpp b/src/ac6_native_graphics.cpp index 818f1e26..e02abfc9 100644 --- a/src/ac6_native_graphics.cpp +++ b/src/ac6_native_graphics.cpp @@ -1,3003 +1 @@ -#include "ac6_native_graphics.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "ac6_native_renderer/native_renderer.h" -#include "d3d_hooks.h" - -#if REX_HAS_D3D12 -#include -#include -#include -#include -#include -#endif - -REXCVAR_DEFINE_BOOL(ac6_native_graphics_bootstrap, true, "AC6/Render", - "Use the experimental native graphics bootstrap backend"); -REXCVAR_DEFINE_BOOL( - ac6_native_graphics_auto_enable_capture, true, "AC6/Render", - "Automatically enable AC6 render capture when the parallel native renderer bootstrap is active"); -REXCVAR_DEFINE_BOOL(ac6_native_present_enabled, false, "AC6/Render", - "Enable authoritative native presentation path for direct swaps"); -REXCVAR_DEFINE_BOOL( - ac6_native_present_show_renderer_view, false, "AC6/Render", - "Force the native preview/compositor output to be shown on screen so you can see what the " - "experimental renderer path is currently seeing."); -REXCVAR_DEFINE_BOOL( - ac6_native_graphics_placeholder_present, false, "AC6/Render", - "Allow the native graphics bootstrap backend to replace the legacy swap path with a " - "preview frame generated by the experimental native replay compositor"); -REXCVAR_DEFINE_BOOL( - ac6_native_graphics_placeholder_present_allow_diagnostic, false, "AC6/Render", - "Second safety opt-in for the native diagnostic placeholder presenter. Keep false for " - "normal gameplay."); -REXCVAR_DEFINE_BOOL( - ac6_allow_gpu_trace_stream, false, "AC6/Render", - "Allow legacy GPU trace streaming during AC6 native graphics experiments"); -REXCVAR_DEFINE_BOOL(ac6_native_present_enable_postfx, true, "AC6/Render", - "Enable native post-process stage classification and heuristics"); -REXCVAR_DEFINE_BOOL(ac6_native_present_enable_ui_compose, true, "AC6/Render", - "Enable native UI/composite pass classification"); -REXCVAR_DEFINE_BOOL(ac6_native_present_force_pm4_fallback, false, "AC6/Render", - "Force legacy PM4 fallback even when native present is enabled"); -REXCVAR_DEFINE_BOOL(ac6_native_present_allow_unstable, false, "AC6/Render", - "Allow native present even when parity confidence is low"); -REXCVAR_DEFINE_BOOL( - ac6_native_present_strict_parity_gate, false, "AC6/Render", - "When enabled, low parity confidence blocks native present and falls back to PM4"); -REXCVAR_DEFINE_BOOL( - ac6_native_present_skip_submission_wait, true, "AC6/Render", - "Skip blocking submission-fence waits in native present to avoid swap stalls during bring-up"); -REXCVAR_DEFINE_BOOL( - ac6_native_present_force_minimal_uav, true, "AC6/Render", - "Use minimal native-present UAV clear path to avoid complex compositor stalls during bring-up"); -REXCVAR_DEFINE_BOOL( - ac6_native_present_fallback_to_minimal_uav, true, "AC6/Render", - "If the native compositor path fails, fallback to minimal UAV present before PM4 fallback"); -REXCVAR_DEFINE_BOOL( - ac6_native_present_use_real_swap_path, true, "AC6/Render", - "Use the real D3D12 swap path (command processor IssueSwap) for native present"); -REXCVAR_DEFINE_BOOL( - ac6_native_present_disable_pm4, true, "AC6/Render", - "Disable PM4 swap fallback completely and keep native path authoritative"); -REXCVAR_DEFINE_STRING( - ac6_native_present_routing_mode, "native", "AC6/Render", - "Swap routing mode: native, compat, emergency"); -REXCVAR_DEFINE_BOOL( - ac6_native_present_manual_interrupt_dispatch, false, "AC6/Render", - "Dispatch VBlank interrupt manually on native swap interception"); -REXCVAR_DEFINE_STRING( - ac6_native_present_cadence_policy, "timer", "AC6/Render", - "Cadence policy: timer, present, hybrid"); -REXCVAR_DEFINE_STRING( - ac6_native_present_output_scale, "100", "AC6/Render", - "Native present output scale percent (50-200). Example: 100 for native size"); -REXCVAR_DEFINE_STRING( - ac6_native_present_output_width, "0", "AC6/Render", - "Force native present output width. 0 keeps source width."); -REXCVAR_DEFINE_STRING( - ac6_native_present_output_height, "0", "AC6/Render", - "Force native present output height. 0 keeps source height."); - -namespace ac6::graphics { -namespace { -enum PresentMode : uint32_t { - kPresentModeObserve = 0, - kPresentModeDiagnostic = 1, - kPresentModeAuthoritative = 2, -}; - -enum FallbackReason : uint32_t { - kFallbackNone = 0, - kFallbackNativePresentDisabled = 1, - kFallbackForcedPm4 = 2, - kFallbackResourcesUnavailable = 3, - kFallbackComposerFailed = 4, - kFallbackLowParityConfidence = 5, -}; - - -using rex::X_STATUS; - -#if REX_HAS_D3D12 -namespace shaders { -#include "thirdparty/rexglue-sdk/src/ui/shaders/bytecode/d3d12_5_1/immediate_ps.h" -#include "thirdparty/rexglue-sdk/src/ui/shaders/bytecode/d3d12_5_1/immediate_vs.h" -} // namespace shaders -#endif - -std::mutex g_native_graphics_status_mutex; -NativeGraphicsStatusSnapshot g_native_graphics_status{}; -std::mutex g_parallel_renderer_mutex; -ac6::renderer::NativeRenderer g_parallel_renderer; - -struct CapturedFrameEvent { - enum class Type { - kDraw, - kClear, - kResolve, - }; - - uint32_t sequence = 0; - Type type = Type::kDraw; - const ac6::d3d::ShadowState* shadow_state = nullptr; -}; - -struct ReplayPassCandidate { - ac6::d3d::ShadowState binding{}; - uint32_t start_sequence = 0; - uint32_t end_sequence = 0; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; -}; - -struct ReplayCandidateKey { - uint32_t rt0 = 0; - uint32_t depth_stencil = 0; - uint32_t viewport_x = 0; - uint32_t viewport_y = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; -}; - -constexpr uint32_t kSelectedPassPreviewColorSampleCount = 4; -constexpr uint32_t kSelectedPassPreviewStepCount = 4; -constexpr uint32_t kPlannedStagePreviewStepCount = 4; -constexpr uint32_t kPlannedStagePreviewDrawSampleCount = 6; - -using FloatColor = std::array; - -template -uint32_t CountNonZeroEntries(const std::array& values) { - uint32_t count = 0; - for (const T& value : values) { - if (value) { - ++count; - } - } - return count; -} - -uint32_t CountBoundStreams( - const std::array& streams) { - uint32_t count = 0; - for (const auto& stream : streams) { - if (stream.buffer) { - ++count; - } - } - return count; -} - -uint32_t CountBoundSamplers( - const std::array& samplers) { - uint32_t count = 0; - for (const auto& sampler : samplers) { - if (sampler.mag_filter || sampler.min_filter || sampler.mip_filter || - sampler.mip_level || sampler.border_color) { - ++count; - } - } - return count; -} - -struct SelectedPassClearStep { - uint32_t color = 0; - uint32_t rect_count = 0; - std::array rects{}; -}; - -struct SelectedPassPreviewData { - SelectedPassPreviewSummary summary{}; - std::array clear_steps{}; - uint32_t clear_step_count = 0; -}; - -struct PlannedStageDrawSample { - uint32_t sequence = 0; - uint32_t primitive_type = 0; - uint32_t draw_count = 0; - uint32_t flags = 0; - uint32_t texture_count = 0; - uint32_t stream_count = 0; - uint32_t sampler_count = 0; - uint32_t fetch_constant_count = 0; - uint32_t viewport_x = 0; - uint32_t viewport_y = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; - bool indexed = false; -}; - -struct PlannedStagePreviewStage { - bool valid = false; - uint32_t pass_index = 0; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; - uint32_t viewport_x = 0; - uint32_t viewport_y = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; - uint32_t first_clear_color = 0; - uint32_t last_clear_color = 0; - bool using_clear_color = false; - uint32_t sampled_clear_rect_count = 0; - std::array clear_steps{}; - uint32_t clear_step_count = 0; - std::array draw_samples{}; - uint32_t draw_sample_count = 0; -}; - -struct PlannedStagePreviewData { - bool valid = false; - bool present_from_selected_pass = false; - PlannedStagePreviewStage scene{}; - PlannedStagePreviewStage postfx{}; - PlannedStagePreviewStage ui{}; -}; - -void UpdateParallelRendererStatusLocked( - const ac6::renderer::NativeRenderer& renderer_instance, - const ac6::renderer::NativeRendererStats& stats) { - const ac6::renderer::FrontendFrameSummary frontend_summary = - renderer_instance.frontend_summary(); - const ac6::renderer::NativeFramePlan frame_plan = renderer_instance.frame_plan(); - g_native_graphics_status.parallel_renderer_enabled = - REXCVAR_GET(ac6_native_graphics_bootstrap); - g_native_graphics_status.parallel_renderer_initialized = stats.initialized; - g_native_graphics_status.parallel_renderer_backend = stats.active_backend; - g_native_graphics_status.parallel_renderer_feature_level = - renderer_instance.feature_level(); - g_native_graphics_status.parallel_renderer_frame_count = stats.frame_count; - g_native_graphics_status.parallel_renderer_built_pass_count = - stats.built_pass_count; - g_native_graphics_status.parallel_renderer_transient_allocation_count = - stats.transient_allocation_count; - g_native_graphics_status.parallel_renderer_frame_slot = - renderer_instance.frame_slot(); - g_native_graphics_status.parallel_renderer_max_frames_in_flight = - renderer_instance.max_frames_in_flight(); - g_native_graphics_status.parallel_renderer_scene_pass_count = - frontend_summary.scene_pass_count; - g_native_graphics_status.parallel_renderer_post_process_pass_count = - frontend_summary.post_process_pass_count; - g_native_graphics_status.parallel_renderer_ui_pass_count = - frontend_summary.ui_pass_count; - g_native_graphics_status.parallel_renderer_selected_pass_index = - frontend_summary.selected_pass_index; - g_native_graphics_status.parallel_renderer_total_draw_count = - frontend_summary.total_draw_count; - g_native_graphics_status.parallel_renderer_total_clear_count = - frontend_summary.total_clear_count; - g_native_graphics_status.parallel_renderer_total_resolve_count = - frontend_summary.total_resolve_count; - g_native_graphics_status.parallel_renderer_capture_valid = - frontend_summary.capture_valid; - g_native_graphics_status.parallel_frame_plan_valid = frame_plan.valid; - g_native_graphics_status.parallel_frame_plan_has_scene_stage = - frame_plan.has_scene_stage; - g_native_graphics_status.parallel_frame_plan_has_post_process_stage = - frame_plan.has_post_process_stage; - g_native_graphics_status.parallel_frame_plan_has_ui_stage = - frame_plan.has_ui_stage; - g_native_graphics_status.parallel_frame_plan_present_from_selected_pass = - frame_plan.present_from_selected_pass; - g_native_graphics_status.parallel_frame_plan_scene_pass_index = - frame_plan.scene_stage.pass_index; - g_native_graphics_status.parallel_frame_plan_post_process_pass_index = - frame_plan.post_process_stage.pass_index; - g_native_graphics_status.parallel_frame_plan_ui_pass_index = - frame_plan.ui_stage.pass_index; - g_native_graphics_status.parallel_frame_plan_present_pass_index = - frame_plan.present_stage.pass_index; - g_native_graphics_status.parallel_frame_plan_scene_stage_score = - frame_plan.scene_stage_score; - g_native_graphics_status.parallel_frame_plan_post_process_stage_score = - frame_plan.post_process_stage_score; - g_native_graphics_status.parallel_frame_plan_ui_stage_score = - frame_plan.ui_stage_score; - g_native_graphics_status.parallel_frame_plan_present_stage_score = - frame_plan.present_stage_score; - g_native_graphics_status.parallel_frame_plan_scene_stage_draw_count = - frame_plan.scene_stage.draw_count; - g_native_graphics_status.parallel_frame_plan_post_process_stage_draw_count = - frame_plan.post_process_stage.draw_count; - g_native_graphics_status.parallel_frame_plan_ui_stage_draw_count = - frame_plan.ui_stage.draw_count; - g_native_graphics_status.parallel_frame_plan_scene_stage_texture_peak = - frame_plan.scene_stage.max_texture_count; - g_native_graphics_status.parallel_frame_plan_post_process_stage_texture_peak = - frame_plan.post_process_stage.max_texture_count; - g_native_graphics_status.parallel_frame_plan_ui_stage_texture_peak = - frame_plan.ui_stage.max_texture_count; - g_native_graphics_status.parallel_frame_plan_scene_stage_stream_peak = - frame_plan.scene_stage.max_stream_count; - g_native_graphics_status.parallel_frame_plan_post_process_stage_stream_peak = - frame_plan.post_process_stage.max_stream_count; - g_native_graphics_status.parallel_frame_plan_ui_stage_stream_peak = - frame_plan.ui_stage.max_stream_count; - g_native_graphics_status.parallel_frame_plan_output_width = - frame_plan.output_width; - g_native_graphics_status.parallel_frame_plan_output_height = - frame_plan.output_height; -} - -void SyncParallelRendererStatus() { - std::lock_guard renderer_lock(g_parallel_renderer_mutex); - const ac6::renderer::NativeRendererStats stats = g_parallel_renderer.GetStats(); - std::lock_guard status_lock(g_native_graphics_status_mutex); - UpdateParallelRendererStatusLocked(g_parallel_renderer, stats); -} - -void ShutdownParallelRenderer() { - std::lock_guard renderer_lock(g_parallel_renderer_mutex); - g_parallel_renderer.Shutdown(); - const ac6::renderer::NativeRendererStats stats = g_parallel_renderer.GetStats(); - std::lock_guard status_lock(g_native_graphics_status_mutex); - UpdateParallelRendererStatusLocked(g_parallel_renderer, stats); -} - -bool InitializeParallelRenderer() { - std::lock_guard renderer_lock(g_parallel_renderer_mutex); - - ac6::renderer::NativeRendererConfig config{}; - config.feature_level = ac6::renderer::FeatureLevel::kBootstrap; - config.max_frames_in_flight = 2; - - const bool initialized = g_parallel_renderer.Initialize(config); - const ac6::renderer::NativeRendererStats stats = g_parallel_renderer.GetStats(); - { - std::lock_guard status_lock(g_native_graphics_status_mutex); - UpdateParallelRendererStatusLocked(g_parallel_renderer, stats); - } - - if (!initialized) { - REXLOG_WARN("AC6 native renderer scaffold failed to initialize in parallel mode"); - } - return initialized; -} - -void TickParallelRendererFrame() { - std::lock_guard renderer_lock(g_parallel_renderer_mutex); - const ac6::renderer::NativeRendererStats initial_stats = g_parallel_renderer.GetStats(); - if (!initial_stats.initialized) { - return; - } - - const ac6::d3d::FrameCaptureSnapshot frame_capture = ac6::d3d::GetFrameCapture(); - g_parallel_renderer.BeginFrame(); - if (!frame_capture.draws.empty() || !frame_capture.clears.empty() || - !frame_capture.resolves.empty()) { - g_parallel_renderer.BuildCapturedFrame(frame_capture); - } else { - g_parallel_renderer.BuildBootstrapFrame(); - } - - const ac6::renderer::NativeRendererStats stats = g_parallel_renderer.GetStats(); - std::lock_guard status_lock(g_native_graphics_status_mutex); - UpdateParallelRendererStatusLocked(g_parallel_renderer, stats); -} - -bool SamePassBinding(const ac6::d3d::ShadowState& left, - const ac6::d3d::ShadowState& right) { - return left.render_targets[0] == right.render_targets[0] && - left.depth_stencil == right.depth_stencil && - left.viewport.width == right.viewport.width && - left.viewport.height == right.viewport.height; -} - -bool SameReplayCandidate(const ReplayCandidateKey& left, const ReplayCandidateKey& right) { - return left.rt0 == right.rt0 && left.depth_stencil == right.depth_stencil && - left.viewport_x == right.viewport_x && left.viewport_y == right.viewport_y && - left.viewport_width == right.viewport_width && - left.viewport_height == right.viewport_height; -} - -ReplayCandidateKey MakeReplayCandidateKey(const NativeReplayPlanSummary& replay_plan) { - return ReplayCandidateKey{replay_plan.present_candidate_rt0, - replay_plan.present_candidate_depth_stencil, - replay_plan.present_candidate_viewport_x, - replay_plan.present_candidate_viewport_y, - replay_plan.present_candidate_viewport_width, - replay_plan.present_candidate_viewport_height}; -} - -bool SequenceInSelectedPass(uint32_t sequence, const NativeReplayPlanSummary& replay_plan) { - return replay_plan.valid && sequence >= replay_plan.selected_pass_start_sequence && - sequence <= replay_plan.selected_pass_end_sequence; -} - -bool SequenceInObservedPass(uint32_t sequence, - const ac6::renderer::ObservedPassDesc& pass) { - return sequence >= pass.start_sequence && sequence <= pass.end_sequence; -} - -bool IsDiagnosticPlaceholderPresentEnabled() { - if (REXCVAR_GET(ac6_native_present_show_renderer_view)) { - return true; - } - return REXCVAR_GET(ac6_native_graphics_placeholder_present) && - REXCVAR_GET(ac6_native_graphics_placeholder_present_allow_diagnostic); -} - -uint32_t GetOutputScalePercent() { - const std::string value = REXCVAR_GET(ac6_native_present_output_scale); - if (value.empty()) { - return 100; - } - int parsed = std::atoi(value.c_str()); - parsed = std::clamp(parsed, 50, 200); - return static_cast(parsed); -} - -uint32_t ParseOptionalDimensionCvar(const std::string& value) { - if (value.empty()) { - return 0; - } - int parsed = std::atoi(value.c_str()); - if (parsed <= 0) { - return 0; - } - return static_cast(std::clamp(parsed, 1, 16384)); -} - -SwapRoutingMode GetSwapRoutingMode() { - const std::string value = REXCVAR_GET(ac6_native_present_routing_mode); - if (value == "compat") { - return SwapRoutingMode::kCompatPm4; - } - if (value == "emergency") { - return SwapRoutingMode::kEmergencyHold; - } - return SwapRoutingMode::kNativeAuthoritative; -} - -CadencePolicy GetCadencePolicy() { - const std::string value = REXCVAR_GET(ac6_native_present_cadence_policy); - if (value == "present") { - return CadencePolicy::kPresentCompletion; - } - if (value == "hybrid") { - return CadencePolicy::kHybridDebug; - } - return CadencePolicy::kTimerVblank; -} - -uint32_t GetForcedOutputWidth() { - return ParseOptionalDimensionCvar(REXCVAR_GET(ac6_native_present_output_width)); -} - -uint32_t GetForcedOutputHeight() { - return ParseOptionalDimensionCvar(REXCVAR_GET(ac6_native_present_output_height)); -} - -uint32_t ComputeParityConfidenceScore(const NativeReplayPlanSummary& replay_plan, - const SelectedPassPreviewData& selected_pass_preview, - uint32_t* scene_pass_count, uint32_t* postfx_pass_count, - uint32_t* ui_pass_count, uint32_t* missing_effects_counter, - uint32_t* pixelation_counter) { - const bool postfx_enabled = REXCVAR_GET(ac6_native_present_enable_postfx); - const bool ui_compose_enabled = REXCVAR_GET(ac6_native_present_enable_ui_compose); - uint32_t scene_count = replay_plan.pass_count; - uint32_t postfx_count = postfx_enabled ? replay_plan.resolve_event_count : 0; - uint32_t ui_count = (ui_compose_enabled && replay_plan.selected_pass_is_last) ? 1u : 0u; - uint32_t missing_effects = 0; - uint32_t pixelation = 0; - uint32_t score = 100; - if (!replay_plan.valid) { - score = 70; - } else { - if (!replay_plan.present_candidate_matches_swap_size) { - score = (score > 20) ? score - 20 : 0; - ++pixelation; - } - if (postfx_enabled && !replay_plan.selected_pass_has_resolve && replay_plan.resolve_event_count) { - score = (score > 18) ? score - 18 : 0; - ++missing_effects; - } - if (!replay_plan.selected_pass_is_stable) { - score = (score > 14) ? score - 14 : 0; - ++pixelation; - } - if (!selected_pass_preview.summary.using_clear_fill && - replay_plan.selected_pass_clear_count > 0) { - score = (score > 10) ? score - 10 : 0; - ++missing_effects; - } - } - if (scene_pass_count) { - *scene_pass_count = scene_count; - } - if (postfx_pass_count) { - *postfx_pass_count = postfx_count; - } - if (ui_pass_count) { - *ui_pass_count = ui_count; - } - if (missing_effects_counter) { - *missing_effects_counter = missing_effects; - } - if (pixelation_counter) { - *pixelation_counter = pixelation; - } - return std::min(score, 100u); -} - -FloatColor DecodeArgbColor(uint32_t packed_color) { - return {float((packed_color >> 16) & 0xFFu) / 255.0f, - float((packed_color >> 8) & 0xFFu) / 255.0f, - float(packed_color & 0xFFu) / 255.0f, 1.0f}; -} - -FloatColor MixColors(const FloatColor& left, const FloatColor& right, float t) { - const float clamped_t = std::clamp(t, 0.0f, 1.0f); - const float inverse_t = 1.0f - clamped_t; - return {left[0] * inverse_t + right[0] * clamped_t, - left[1] * inverse_t + right[1] * clamped_t, - left[2] * inverse_t + right[2] * clamped_t, 1.0f}; -} - -FloatColor EnsureVisibleColor(const FloatColor& color, float min_luma) { - FloatColor out = color; - const float luma = out[0] * 0.2126f + out[1] * 0.7152f + out[2] * 0.0722f; - if (luma < min_luma) { - const float lift = min_luma - luma; - out[0] = std::clamp(out[0] + lift, 0.0f, 1.0f); - out[1] = std::clamp(out[1] + lift, 0.0f, 1.0f); - out[2] = std::clamp(out[2] + lift, 0.0f, 1.0f); - } - out[3] = std::clamp(out[3], 0.0f, 1.0f); - return out; -} - -uint32_t PackImmediateColor(const FloatColor& color) { - const auto pack_channel = [](float value) -> uint32_t { - return uint32_t(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f); - }; - const uint32_t r = pack_channel(color[0]); - const uint32_t g = pack_channel(color[1]); - const uint32_t b = pack_channel(color[2]); - const uint32_t a = pack_channel(color[3]); - return r | (g << 8) | (b << 16) | (a << 24); -} - -uint32_t ScoreReplayPass(const ReplayPassCandidate& pass, - const rex::system::GraphicsSwapSubmission& submission, - bool is_last_pass) { - uint32_t score = 0; - if (pass.binding.viewport.width == submission.frontbuffer_width && - pass.binding.viewport.height == submission.frontbuffer_height) { - score += 100; - } - if (pass.draw_count) { - score += 40 + std::min(pass.draw_count, 64); - } - if (pass.resolve_count) { - score += 20; - } - if (pass.binding.render_targets[0]) { - score += 10; - } - if (is_last_pass) { - score += 25; - } - return score; -} - -uint32_t ScoreObservedPass(const ac6::renderer::ObservedPassDesc& pass, - const rex::system::GraphicsSwapSubmission& submission, - bool is_last_pass) { - uint32_t score = 0; - if (pass.viewport_width == submission.frontbuffer_width && - pass.viewport_height == submission.frontbuffer_height) { - score += 100; - } - if (pass.draw_count) { - score += 40 + std::min(pass.draw_count, 64); - } - if (pass.resolve_count) { - score += 20; - } - if (pass.render_target_0) { - score += 10; - } - if (is_last_pass) { - score += 25; - } - return score; -} - -void ApplyParallelFramePlanToReplayPlan( - const ac6::renderer::FrontendFrameSummary& frontend_summary, - const ac6::renderer::NativeFramePlan& frame_plan, - const std::vector& passes, - const rex::system::GraphicsSwapSubmission& submission, - NativeReplayPlanSummary* replay_plan) { - if (!replay_plan || !frontend_summary.capture_valid || passes.empty()) { - return; - } - - replay_plan->frame_index = frontend_summary.frame_index; - replay_plan->pass_count = frontend_summary.pass_count; - replay_plan->draw_event_count = frontend_summary.total_draw_count; - replay_plan->clear_event_count = frontend_summary.total_clear_count; - replay_plan->resolve_event_count = frontend_summary.total_resolve_count; - replay_plan->swap_size_match_pass_count = 0; - replay_plan->largest_pass_draw_count = 0; - replay_plan->first_pass_draw_count = passes.front().draw_count; - replay_plan->last_pass_draw_count = passes.back().draw_count; - - for (const ac6::renderer::ObservedPassDesc& pass : passes) { - replay_plan->largest_pass_draw_count = - std::max(replay_plan->largest_pass_draw_count, pass.draw_count); - if (pass.viewport_width == submission.frontbuffer_width && - pass.viewport_height == submission.frontbuffer_height) { - ++replay_plan->swap_size_match_pass_count; - } - } - - uint32_t selected_pass_index = 0; - bool selected_pass_valid = false; - if (frame_plan.valid && frame_plan.present_stage.valid && - frame_plan.present_stage.pass_index < passes.size()) { - selected_pass_index = frame_plan.present_stage.pass_index; - selected_pass_valid = true; - } else if (frontend_summary.selected_pass_index < passes.size()) { - selected_pass_index = frontend_summary.selected_pass_index; - selected_pass_valid = true; - } - if (!selected_pass_valid) { - return; - } - - const ac6::renderer::ObservedPassDesc& selected_pass = passes[selected_pass_index]; - replay_plan->selected_pass_index = selected_pass_index; - replay_plan->selected_pass_score = - ScoreObservedPass(selected_pass, submission, - (selected_pass_index + 1) == passes.size()); - replay_plan->selected_pass_start_sequence = selected_pass.start_sequence; - replay_plan->selected_pass_end_sequence = selected_pass.end_sequence; - replay_plan->selected_pass_draw_count = selected_pass.draw_count; - replay_plan->selected_pass_clear_count = selected_pass.clear_count; - replay_plan->selected_pass_resolve_count = selected_pass.resolve_count; - replay_plan->selected_pass_is_last = (selected_pass_index + 1) == passes.size(); - replay_plan->selected_pass_has_resolve = selected_pass.resolve_count != 0; - replay_plan->present_candidate_rt0 = selected_pass.render_target_0; - replay_plan->present_candidate_depth_stencil = selected_pass.depth_stencil; - replay_plan->present_candidate_viewport_x = selected_pass.viewport_x; - replay_plan->present_candidate_viewport_y = selected_pass.viewport_y; - replay_plan->present_candidate_viewport_width = selected_pass.viewport_width; - replay_plan->present_candidate_viewport_height = selected_pass.viewport_height; - replay_plan->present_candidate_matches_swap_size = - selected_pass.viewport_width == submission.frontbuffer_width && - selected_pass.viewport_height == submission.frontbuffer_height; - replay_plan->valid = true; -} - -PlannedStagePreviewStage BuildPlannedStagePreviewStage( - const ac6::d3d::FrameCaptureSnapshot& frame_capture, - const std::vector& passes, - const ac6::renderer::PlannedPassReference& stage_reference) { - PlannedStagePreviewStage stage; - if (!stage_reference.valid || stage_reference.pass_index >= passes.size()) { - return stage; - } - - const ac6::renderer::ObservedPassDesc& pass = passes[stage_reference.pass_index]; - stage.valid = true; - stage.pass_index = stage_reference.pass_index; - stage.draw_count = pass.draw_count; - stage.clear_count = pass.clear_count; - stage.resolve_count = pass.resolve_count; - stage.viewport_x = pass.viewport_x; - stage.viewport_y = pass.viewport_y; - stage.viewport_width = pass.viewport_width; - stage.viewport_height = pass.viewport_height; - - bool has_first_clear = false; - for (const ac6::d3d::ClearRecord& clear : frame_capture.clears) { - if (!SequenceInObservedPass(clear.sequence, pass)) { - continue; - } - if (!has_first_clear) { - stage.first_clear_color = clear.color; - has_first_clear = true; - } - stage.last_clear_color = clear.color; - stage.using_clear_color = true; - stage.sampled_clear_rect_count += clear.captured_rect_count; - if (stage.clear_step_count < stage.clear_steps.size()) { - SelectedPassClearStep& step = stage.clear_steps[stage.clear_step_count++]; - step.color = clear.color; - step.rect_count = clear.captured_rect_count; - step.rects = clear.rects; - } else if (!stage.clear_steps.empty()) { - SelectedPassClearStep& step = stage.clear_steps.back(); - step.color = clear.color; - step.rect_count = clear.captured_rect_count; - step.rects = clear.rects; - } - } - - for (const ac6::d3d::DrawCallRecord& draw : frame_capture.draws) { - if (!SequenceInObservedPass(draw.sequence, pass)) { - continue; - } - - PlannedStageDrawSample sample{}; - sample.sequence = draw.sequence; - sample.primitive_type = draw.primitive_type; - sample.draw_count = draw.count; - sample.flags = draw.flags; - sample.texture_count = CountNonZeroEntries(draw.shadow_state.textures); - sample.stream_count = CountBoundStreams(draw.shadow_state.streams); - sample.sampler_count = CountBoundSamplers(draw.shadow_state.samplers); - sample.fetch_constant_count = - CountNonZeroEntries(draw.shadow_state.texture_fetch_ptrs); - sample.viewport_x = draw.shadow_state.viewport.x; - sample.viewport_y = draw.shadow_state.viewport.y; - sample.viewport_width = draw.shadow_state.viewport.width; - sample.viewport_height = draw.shadow_state.viewport.height; - sample.indexed = draw.kind != ac6::d3d::DrawCallKind::kPrimitive; - - if (stage.draw_sample_count < stage.draw_samples.size()) { - stage.draw_samples[stage.draw_sample_count++] = sample; - } else if (!stage.draw_samples.empty()) { - stage.draw_samples.back() = sample; - } - } - - return stage; -} - -PlannedStagePreviewData BuildPlannedStagePreview( - const ac6::d3d::FrameCaptureSnapshot& frame_capture, - const ac6::renderer::NativeFramePlan& frame_plan, - const std::vector& passes) { - PlannedStagePreviewData preview; - if (!frame_plan.valid || passes.empty()) { - return preview; - } - - preview.present_from_selected_pass = frame_plan.present_from_selected_pass; - preview.scene = BuildPlannedStagePreviewStage(frame_capture, passes, frame_plan.scene_stage); - preview.postfx = - BuildPlannedStagePreviewStage(frame_capture, passes, frame_plan.post_process_stage); - preview.ui = BuildPlannedStagePreviewStage(frame_capture, passes, frame_plan.ui_stage); - preview.valid = preview.scene.valid || preview.postfx.valid || preview.ui.valid; - return preview; -} - -NativeReplayPlanSummary AnalyzeReplayPlan( - const ac6::d3d::FrameCaptureSnapshot& frame_capture, - const rex::system::GraphicsSwapSubmission& submission) { - NativeReplayPlanSummary summary; - summary.frame_index = frame_capture.frame_index; - - std::vector events; - events.reserve(frame_capture.draws.size() + frame_capture.clears.size() + - frame_capture.resolves.size()); - - for (const auto& draw : frame_capture.draws) { - events.push_back( - {draw.sequence, CapturedFrameEvent::Type::kDraw, &draw.shadow_state}); - } - for (const auto& clear : frame_capture.clears) { - events.push_back( - {clear.sequence, CapturedFrameEvent::Type::kClear, &clear.shadow_state}); - } - for (const auto& resolve : frame_capture.resolves) { - events.push_back( - {resolve.sequence, CapturedFrameEvent::Type::kResolve, &resolve.shadow_state}); - } - - if (events.empty()) { - return summary; - } - - std::sort(events.begin(), events.end(), - [](const CapturedFrameEvent& left, const CapturedFrameEvent& right) { - return left.sequence < right.sequence; - }); - - std::vector passes; - ReplayPassCandidate current_pass; - bool current_pass_valid = false; - auto flush_pass = [&]() { - if (!current_pass_valid) { - return; - } - passes.push_back(current_pass); - current_pass = {}; - current_pass_valid = false; - }; - - for (const CapturedFrameEvent& event : events) { - if (!event.shadow_state) { - continue; - } - if (!current_pass_valid || !SamePassBinding(current_pass.binding, *event.shadow_state)) { - flush_pass(); - current_pass.binding = *event.shadow_state; - current_pass.start_sequence = event.sequence; - current_pass_valid = true; - } - current_pass.end_sequence = event.sequence; - - switch (event.type) { - case CapturedFrameEvent::Type::kDraw: - ++summary.draw_event_count; - ++current_pass.draw_count; - break; - case CapturedFrameEvent::Type::kClear: - ++summary.clear_event_count; - ++current_pass.clear_count; - break; - case CapturedFrameEvent::Type::kResolve: - ++summary.resolve_event_count; - ++current_pass.resolve_count; - break; - } - } - - flush_pass(); - - summary.pass_count = static_cast(passes.size()); - if (!passes.empty()) { - summary.first_pass_draw_count = passes.front().draw_count; - summary.last_pass_draw_count = passes.back().draw_count; - } - - uint32_t best_score = 0; - uint32_t best_index = 0; - bool best_index_valid = false; - for (uint32_t i = 0; i < passes.size(); ++i) { - const ReplayPassCandidate& pass = passes[i]; - summary.largest_pass_draw_count = - std::max(summary.largest_pass_draw_count, pass.draw_count); - bool matches_swap_size = pass.binding.viewport.width == submission.frontbuffer_width && - pass.binding.viewport.height == submission.frontbuffer_height; - if (matches_swap_size) { - ++summary.swap_size_match_pass_count; - } - bool is_last_pass = (i + 1) == passes.size(); - uint32_t score = ScoreReplayPass(pass, submission, is_last_pass); - if (!best_index_valid || score > best_score || - (score == best_score && i > best_index)) { - best_index_valid = true; - best_index = i; - best_score = score; - } - } - - if (best_index_valid) { - const ReplayPassCandidate& selected_pass = passes[best_index]; - summary.selected_pass_index = best_index; - summary.selected_pass_score = best_score; - summary.selected_pass_start_sequence = selected_pass.start_sequence; - summary.selected_pass_end_sequence = selected_pass.end_sequence; - summary.selected_pass_draw_count = selected_pass.draw_count; - summary.selected_pass_clear_count = selected_pass.clear_count; - summary.selected_pass_resolve_count = selected_pass.resolve_count; - summary.selected_pass_is_last = (best_index + 1) == passes.size(); - summary.selected_pass_has_resolve = selected_pass.resolve_count != 0; - summary.present_candidate_rt0 = selected_pass.binding.render_targets[0]; - summary.present_candidate_depth_stencil = selected_pass.binding.depth_stencil; - summary.present_candidate_viewport_x = selected_pass.binding.viewport.x; - summary.present_candidate_viewport_y = selected_pass.binding.viewport.y; - summary.present_candidate_viewport_width = selected_pass.binding.viewport.width; - summary.present_candidate_viewport_height = selected_pass.binding.viewport.height; - summary.present_candidate_matches_swap_size = - summary.present_candidate_viewport_width == submission.frontbuffer_width && - summary.present_candidate_viewport_height == submission.frontbuffer_height; - summary.valid = true; - } - return summary; -} - -SelectedPassPreviewData BuildSelectedPassPreview( - const ac6::d3d::FrameCaptureSnapshot& frame_capture, - const NativeReplayPlanSummary& replay_plan) { - SelectedPassPreviewData preview; - if (!replay_plan.valid) { - return preview; - } - - preview.summary.valid = true; - preview.summary.draw_count = replay_plan.selected_pass_draw_count; - preview.summary.clear_count = replay_plan.selected_pass_clear_count; - preview.summary.resolve_count = replay_plan.selected_pass_resolve_count; - - bool has_first_clear = false; - for (const auto& clear : frame_capture.clears) { - if (!SequenceInSelectedPass(clear.sequence, replay_plan)) { - continue; - } - - if (!has_first_clear) { - preview.summary.first_clear_color = clear.color; - has_first_clear = true; - } - preview.summary.last_clear_color = clear.color; - preview.summary.using_clear_fill = true; - - preview.summary.sampled_clear_rect_count += clear.captured_rect_count; - if (preview.clear_step_count < preview.clear_steps.size()) { - SelectedPassClearStep& step = preview.clear_steps[preview.clear_step_count++]; - step.color = clear.color; - step.rect_count = clear.captured_rect_count; - step.rects = clear.rects; - } else { - SelectedPassClearStep& step = preview.clear_steps.back(); - step.color = clear.color; - step.rect_count = clear.captured_rect_count; - step.rects = clear.rects; - } - } - - preview.summary.sampled_clear_color_count = preview.clear_step_count; - return preview; -} - -#if REX_HAS_D3D12 -class Ac6NativeGraphicsSystem final : public rex::graphics::d3d12::D3D12GraphicsSystem { - public: - X_STATUS Setup(rex::runtime::FunctionDispatcher* function_dispatcher, - rex::system::KernelState* kernel_state, - rex::ui::WindowedAppContext* app_context, - bool with_presentation) override { - X_STATUS status = rex::graphics::d3d12::D3D12GraphicsSystem::Setup( - function_dispatcher, kernel_state, app_context, with_presentation); - if (XFAILED(status)) { - return status; - } - - d3d12_provider_ = static_cast(provider()); - d3d12_presenter_ = with_presentation - ? static_cast(presenter()) - : nullptr; - if (!d3d12_provider_) { - REXLOG_ERROR("AC6 native graphics bootstrap failed to acquire the D3D12 provider"); - rex::graphics::d3d12::D3D12GraphicsSystem::Shutdown(); - return X_STATUS_UNSUCCESSFUL; - } - - { - std::lock_guard lock(g_native_graphics_status_mutex); - g_native_graphics_status.backend_active = true; - g_native_graphics_status.provider_ready = d3d12_provider_ != nullptr; - g_native_graphics_status.presenter_ready = d3d12_presenter_ != nullptr; - g_native_graphics_status.native_present_enabled = REXCVAR_GET(ac6_native_present_enabled); - } - - if (IsDiagnosticPlaceholderPresentEnabled()) { - REXLOG_WARN( - "AC6 native graphics bootstrap backend is active. The legacy D3D12 " - "graphics core remains enabled for compatibility, while direct swap " - "presentation is intercepted by the native renderer-view preview path."); - } else if (REXCVAR_GET(ac6_native_present_enabled)) { - REXLOG_WARN( - "AC6 native graphics bootstrap backend is active. Authoritative " - "native present is enabled with PM4 fallback gates."); - } else { - REXLOG_WARN( - "AC6 native graphics bootstrap backend is active. The legacy D3D12 " - "graphics core remains enabled for compatibility, and direct swap " - "submissions are observed natively while legacy PM4 presentation " - "remains authoritative."); - } - return X_STATUS_SUCCESS; - } - - void Shutdown() override { - ShutdownPlaceholderRefreshResources(); - { - std::lock_guard lock(g_native_graphics_status_mutex); - g_native_graphics_status.backend_active = false; - g_native_graphics_status.provider_ready = false; - g_native_graphics_status.presenter_ready = false; - g_native_graphics_status.placeholder_resources_initialized = false; - g_native_graphics_status.last_swap_intercepted = false; - g_native_graphics_status.last_swap_fell_back = false; - } - d3d12_presenter_ = nullptr; - d3d12_provider_ = nullptr; - last_swap_submission_ = {}; - swap_count_ = 0; - last_replay_plan_ = {}; - last_selected_pass_preview_ = {}; - last_planned_stage_preview_ = {}; - last_selected_candidate_ = {}; - last_selected_candidate_valid_ = false; - selected_candidate_streak_ = 0; - logged_first_swap_ = false; - logged_first_present_ = false; - logged_passthrough_swap_ = false; - logged_refresh_failure_ = false; - last_present_used_raster_replay_ = false; - rex::graphics::d3d12::D3D12GraphicsSystem::Shutdown(); - } - - bool HandleVideoSwap(const rex::system::GraphicsSwapSubmission& submission) override { - last_swap_submission_ = submission; - ++swap_count_; - last_present_used_raster_replay_ = false; - TickParallelRendererFrame(); - ac6::renderer::FrontendFrameSummary parallel_frontend_summary{}; - ac6::renderer::NativeFramePlan parallel_frame_plan{}; - std::vector parallel_passes; - { - std::lock_guard renderer_lock(g_parallel_renderer_mutex); - parallel_frontend_summary = g_parallel_renderer.frontend_summary(); - parallel_frame_plan = g_parallel_renderer.frame_plan(); - parallel_passes = g_parallel_renderer.frontend_passes(); - } - ac6::d3d::FrameCaptureSnapshot frame_capture = ac6::d3d::GetFrameCapture(); - NativeReplayPlanSummary replay_plan = AnalyzeReplayPlan(frame_capture, submission); - ApplyParallelFramePlanToReplayPlan(parallel_frontend_summary, parallel_frame_plan, - parallel_passes, submission, &replay_plan); - if (replay_plan.valid) { - ReplayCandidateKey candidate_key = MakeReplayCandidateKey(replay_plan); - if (last_selected_candidate_valid_ && - SameReplayCandidate(last_selected_candidate_, candidate_key)) { - ++selected_candidate_streak_; - } else { - last_selected_candidate_ = candidate_key; - last_selected_candidate_valid_ = true; - selected_candidate_streak_ = 1; - } - replay_plan.selected_pass_streak = selected_candidate_streak_; - replay_plan.selected_pass_is_stable = selected_candidate_streak_ >= 3; - } else { - last_selected_candidate_valid_ = false; - selected_candidate_streak_ = 0; - } - last_replay_plan_ = replay_plan; - last_selected_pass_preview_ = BuildSelectedPassPreview(frame_capture, replay_plan); - last_planned_stage_preview_ = - BuildPlannedStagePreview(frame_capture, parallel_frame_plan, parallel_passes); - uint32_t scene_pass_count = 0; - uint32_t postfx_pass_count = 0; - uint32_t ui_pass_count = 0; - uint32_t missing_effects_counter = 0; - uint32_t pixelation_counter = 0; - const uint32_t parity_confidence_score = - ComputeParityConfidenceScore(replay_plan, last_selected_pass_preview_, &scene_pass_count, - &postfx_pass_count, &ui_pass_count, &missing_effects_counter, - &pixelation_counter); - if (parallel_frontend_summary.capture_valid) { - scene_pass_count = parallel_frontend_summary.scene_pass_count; - postfx_pass_count = REXCVAR_GET(ac6_native_present_enable_postfx) - ? parallel_frontend_summary.post_process_pass_count - : 0; - ui_pass_count = REXCVAR_GET(ac6_native_present_enable_ui_compose) - ? parallel_frontend_summary.ui_pass_count - : 0; - } - const bool parity_confidence_good = parity_confidence_score >= 65; - const bool native_present_enabled = REXCVAR_GET(ac6_native_present_enabled); - const SwapRoutingMode routing_mode = GetSwapRoutingMode(); - const bool disable_pm4 = REXCVAR_GET(ac6_native_present_disable_pm4); - const bool effective_disable_pm4 = - disable_pm4 || routing_mode == SwapRoutingMode::kNativeAuthoritative; - const bool effective_native_present_enabled = - native_present_enabled || effective_disable_pm4 || - routing_mode == SwapRoutingMode::kNativeAuthoritative; - const bool force_pm4_fallback = REXCVAR_GET(ac6_native_present_force_pm4_fallback); - const bool allow_unstable = REXCVAR_GET(ac6_native_present_allow_unstable); - const bool diagnostic_present_enabled = IsDiagnosticPlaceholderPresentEnabled(); - const bool swap_takeover_enabled = - effective_native_present_enabled || diagnostic_present_enabled; - const CadencePolicy cadence_policy = GetCadencePolicy(); - const uint32_t output_scale_percent = GetOutputScalePercent(); - const uint32_t scaled_output_width = - std::max(1, (submission.frontbuffer_width * output_scale_percent) / 100); - const uint32_t scaled_output_height = - std::max(1, (submission.frontbuffer_height * output_scale_percent) / 100); - const uint32_t forced_output_width = GetForcedOutputWidth(); - const uint32_t forced_output_height = GetForcedOutputHeight(); - const uint32_t effective_output_width = forced_output_width ? forced_output_width - : scaled_output_width; - const uint32_t effective_output_height = forced_output_height ? forced_output_height - : scaled_output_height; - - { - std::lock_guard lock(g_native_graphics_status_mutex); - g_native_graphics_status.total_swap_count = swap_count_; - g_native_graphics_status.decision_frame_index = swap_count_; - g_native_graphics_status.routing_mode = routing_mode; - g_native_graphics_status.ingress_path = SwapIngressPath::kKernelDirectSwap; - g_native_graphics_status.execution_path = SwapExecutionPath::kUnknown; - g_native_graphics_status.cadence_policy = cadence_policy; - g_native_graphics_status.last_frontbuffer_virtual_address = - submission.frontbuffer_virtual_address; - g_native_graphics_status.last_frontbuffer_physical_address = - submission.frontbuffer_physical_address; - g_native_graphics_status.last_frontbuffer_width = submission.frontbuffer_width; - g_native_graphics_status.last_frontbuffer_height = submission.frontbuffer_height; - g_native_graphics_status.last_texture_format = submission.texture_format; - g_native_graphics_status.last_color_space = submission.color_space; - g_native_graphics_status.capture_summary = ac6::d3d::GetFrameCaptureSummary(); - g_native_graphics_status.replay_plan = replay_plan; - g_native_graphics_status.selected_pass_preview = last_selected_pass_preview_.summary; - g_native_graphics_status.native_present_enabled = effective_native_present_enabled; - g_native_graphics_status.effects_capture_available = replay_plan.valid; - g_native_graphics_status.effects_pass_classification_valid = - replay_plan.valid && - (REXCVAR_GET(ac6_native_present_enable_postfx) || - REXCVAR_GET(ac6_native_present_enable_ui_compose)); - g_native_graphics_status.parity_confidence_good = parity_confidence_good; - g_native_graphics_status.configured_output_scale_percent = output_scale_percent; - g_native_graphics_status.effective_output_width = effective_output_width; - g_native_graphics_status.effective_output_height = effective_output_height; - // Keep last outcome latched until this frame chooses an explicit path. - // Resetting here causes transient "Observed" flicker in the overlay. - g_native_graphics_status.post_process_pass_count = postfx_pass_count; - g_native_graphics_status.ui_pass_count = ui_pass_count; - g_native_graphics_status.scene_pass_count = scene_pass_count; - g_native_graphics_status.missing_effects_counter = missing_effects_counter; - g_native_graphics_status.pixelation_counter = pixelation_counter; - g_native_graphics_status.parity_confidence_score = parity_confidence_score; - } - - if (!logged_first_swap_) { - logged_first_swap_ = true; - REXLOG_WARN( - "AC6 native graphics bootstrap received the first direct swap " - "(fb_va={:08X}, fb_pa={:08X}, {}x{}, fmt={:08X})", - submission.frontbuffer_virtual_address, submission.frontbuffer_physical_address, - submission.frontbuffer_width, submission.frontbuffer_height, submission.texture_format); - } - - if (routing_mode == SwapRoutingMode::kEmergencyHold) { - { - std::lock_guard lock(g_native_graphics_status_mutex); - g_native_graphics_status.last_swap_intercepted = true; - g_native_graphics_status.last_swap_fell_back = false; - g_native_graphics_status.native_present_authoritative = true; - g_native_graphics_status.last_present_mode = kPresentModeAuthoritative; - g_native_graphics_status.last_fallback_reason = kFallbackNone; - g_native_graphics_status.execution_path = SwapExecutionPath::kEmergencyHoldNoPresent; - } - return true; - } - - if (routing_mode == SwapRoutingMode::kCompatPm4 && !swap_takeover_enabled) { - if (!logged_passthrough_swap_) { - logged_passthrough_swap_ = true; - REXLOG_WARN( - "AC6 native graphics bootstrap is observing swaps (native present disabled)"); - } - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.fallback_swap_count; - g_native_graphics_status.last_swap_fell_back = true; - g_native_graphics_status.last_fallback_reason = kFallbackNativePresentDisabled; - g_native_graphics_status.execution_path = SwapExecutionPath::kPm4Fallback; - } - return false; - } - - if (force_pm4_fallback && !effective_disable_pm4) { - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.fallback_swap_count; - g_native_graphics_status.last_swap_fell_back = true; - g_native_graphics_status.last_fallback_reason = kFallbackForcedPm4; - g_native_graphics_status.execution_path = SwapExecutionPath::kPm4Fallback; - } - return false; - } - - if (!parity_confidence_good && !allow_unstable) { - std::lock_guard lock(g_native_graphics_status_mutex); - g_native_graphics_status.last_fallback_reason = kFallbackLowParityConfidence; - } - - if (!diagnostic_present_enabled && !logged_passthrough_swap_) { - logged_passthrough_swap_ = true; - REXLOG_WARN( - "AC6 native graphics bootstrap is presenting through authoritative native path"); - } - - auto maybe_dispatch_swap_interrupt = [this]() { - const CadencePolicy policy = GetCadencePolicy(); - const bool dispatch_for_policy = - policy == CadencePolicy::kPresentCompletion || policy == CadencePolicy::kHybridDebug; - if (dispatch_for_policy || REXCVAR_GET(ac6_native_present_manual_interrupt_dispatch)) { - DispatchInterruptCallback(0, 2); - } - }; - rex::system::GraphicsSwapSubmission present_submission = submission; - present_submission.frontbuffer_width = effective_output_width; - present_submission.frontbuffer_height = effective_output_height; - - if (!diagnostic_present_enabled && - REXCVAR_GET(ac6_native_present_use_real_swap_path)) { - auto* cp = command_processor(); - auto* d3d12_cp = cp ? dynamic_cast(cp) : nullptr; - if (d3d12_cp && d3d12_cp->PresentDirectSwap(present_submission)) { - if (!logged_first_present_) { - logged_first_present_ = true; - REXLOG_WARN("AC6 native graphics bootstrap is now presenting via real D3D12 swap path"); - } - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.intercepted_swap_count; - g_native_graphics_status.native_present_authoritative = true; - g_native_graphics_status.last_present_mode = kPresentModeAuthoritative; - g_native_graphics_status.last_fallback_reason = kFallbackNone; - g_native_graphics_status.last_swap_intercepted = true; - g_native_graphics_status.execution_path = SwapExecutionPath::kDirectPresentSwap; - } - maybe_dispatch_swap_interrupt(); - return true; - } - if (REXCVAR_GET(ac6_native_present_fallback_to_minimal_uav)) { - if (EnsurePlaceholderRefreshResources() && RefreshPlaceholderFrame(present_submission)) { - if (!logged_first_present_) { - logged_first_present_ = true; - REXLOG_WARN( - "AC6 native graphics bootstrap is using native minimal fallback after " - "real direct-swap present failure"); - } - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.intercepted_swap_count; - g_native_graphics_status.selected_pass_preview.using_raster_replay = - last_present_used_raster_replay_; - g_native_graphics_status.native_present_authoritative = true; - g_native_graphics_status.last_present_mode = kPresentModeAuthoritative; - g_native_graphics_status.last_fallback_reason = kFallbackNone; - g_native_graphics_status.last_swap_intercepted = true; - g_native_graphics_status.execution_path = last_present_used_raster_replay_ - ? SwapExecutionPath::kNativeRasterReplay - : SwapExecutionPath::kNativeMinimalUav; - } - maybe_dispatch_swap_interrupt(); - return true; - } - } - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.fallback_swap_count; - g_native_graphics_status.last_swap_fell_back = false; - g_native_graphics_status.last_swap_intercepted = true; - g_native_graphics_status.native_present_authoritative = true; - g_native_graphics_status.last_present_mode = kPresentModeAuthoritative; - g_native_graphics_status.last_fallback_reason = kFallbackComposerFailed; - g_native_graphics_status.execution_path = SwapExecutionPath::kEmergencyHoldNoPresent; - } - // Keep native present ownership to avoid per-frame PM4/native oscillation, - // which causes visible mode flapping and pacing instability. - maybe_dispatch_swap_interrupt(); - return true; - } - - if (!EnsurePlaceholderRefreshResources()) { - if (!logged_refresh_failure_) { - logged_refresh_failure_ = true; - REXLOG_WARN( - "AC6 native graphics bootstrap could not initialize diagnostic " - "direct-swap replay resources; falling back to the legacy PM4 swap path"); - } - if (effective_disable_pm4) { - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.fallback_swap_count; - g_native_graphics_status.last_swap_fell_back = false; - g_native_graphics_status.last_swap_intercepted = true; - g_native_graphics_status.native_present_authoritative = true; - g_native_graphics_status.last_present_mode = kPresentModeAuthoritative; - g_native_graphics_status.last_fallback_reason = kFallbackResourcesUnavailable; - g_native_graphics_status.execution_path = SwapExecutionPath::kEmergencyHoldNoPresent; - } - maybe_dispatch_swap_interrupt(); - return true; - } - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.fallback_swap_count; - g_native_graphics_status.last_swap_fell_back = true; - g_native_graphics_status.last_fallback_reason = kFallbackResourcesUnavailable; - g_native_graphics_status.execution_path = SwapExecutionPath::kPm4Fallback; - } - return false; - } - - if (!RefreshPlaceholderFrame(present_submission)) { - if (!logged_refresh_failure_) { - logged_refresh_failure_ = true; - REXLOG_WARN( - "AC6 native graphics bootstrap could not refresh the diagnostic " - "direct-swap replay frame; falling back to the legacy PM4 swap path"); - } - if (effective_disable_pm4) { - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.fallback_swap_count; - g_native_graphics_status.last_swap_fell_back = false; - g_native_graphics_status.last_swap_intercepted = true; - g_native_graphics_status.native_present_authoritative = true; - g_native_graphics_status.last_present_mode = kPresentModeAuthoritative; - g_native_graphics_status.last_fallback_reason = kFallbackComposerFailed; - g_native_graphics_status.execution_path = SwapExecutionPath::kEmergencyHoldNoPresent; - } - maybe_dispatch_swap_interrupt(); - return true; - } else { - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.fallback_swap_count; - g_native_graphics_status.last_swap_fell_back = true; - g_native_graphics_status.last_fallback_reason = kFallbackComposerFailed; - g_native_graphics_status.execution_path = SwapExecutionPath::kPm4Fallback; - } - return false; - } - } - - if (!logged_first_present_) { - logged_first_present_ = true; - if (diagnostic_present_enabled) { - REXLOG_WARN( - "AC6 native graphics bootstrap is now forcing the renderer-view preview " - "through the direct swap path"); - } else { - REXLOG_WARN( - "AC6 native graphics bootstrap is now presenting a raster replay " - "preview frame through the direct swap path"); - } - } - - { - std::lock_guard lock(g_native_graphics_status_mutex); - ++g_native_graphics_status.intercepted_swap_count; - g_native_graphics_status.selected_pass_preview.using_raster_replay = - last_present_used_raster_replay_; - g_native_graphics_status.native_present_authoritative = true; - g_native_graphics_status.last_present_mode = - diagnostic_present_enabled ? kPresentModeDiagnostic : kPresentModeAuthoritative; - g_native_graphics_status.last_fallback_reason = kFallbackNone; - g_native_graphics_status.last_swap_intercepted = true; - g_native_graphics_status.execution_path = last_present_used_raster_replay_ - ? SwapExecutionPath::kNativeRasterReplay - : SwapExecutionPath::kNativeMinimalUav; - } - maybe_dispatch_swap_interrupt(); - return true; - } - - private: - static constexpr uint32_t kPlaceholderRefreshSlotCount = 3; - static constexpr uint32_t kPlaceholderHeartbeatPeriod = 120; - static constexpr uint32_t kRasterPreviewMaxRectCount = 64; - static constexpr uint32_t kRasterPreviewMaxVertexCount = kRasterPreviewMaxRectCount * 6; - - struct PlaceholderRefreshSlot { - Microsoft::WRL::ComPtr command_allocator; - Microsoft::WRL::ComPtr shader_visible_uav_heap; - Microsoft::WRL::ComPtr cpu_uav_heap; - Microsoft::WRL::ComPtr rtv_heap; - Microsoft::WRL::ComPtr raster_render_target; - Microsoft::WRL::ComPtr vertex_upload_buffer; - uint8_t* vertex_upload_mapping = nullptr; - uint32_t raster_target_width = 0; - uint32_t raster_target_height = 0; - uint64_t last_submission = 0; - }; - - bool EnsurePlaceholderRefreshResources() { - if (placeholder_resources_initialized_) { - return true; - } - if (!InitializePlaceholderRefreshResources()) { - return false; - } - placeholder_resources_initialized_ = true; - { - std::lock_guard lock(g_native_graphics_status_mutex); - g_native_graphics_status.placeholder_resources_initialized = true; - } - return true; - } - - bool InitializeRasterPreviewResources(ID3D12Device* device) { - D3D12_ROOT_PARAMETER root_parameters[3] = {}; - D3D12_DESCRIPTOR_RANGE descriptor_range_texture = {}; - D3D12_DESCRIPTOR_RANGE descriptor_range_sampler = {}; - - root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - root_parameters[0].DescriptorTable.NumDescriptorRanges = 1; - root_parameters[0].DescriptorTable.pDescriptorRanges = &descriptor_range_texture; - root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - descriptor_range_texture.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - descriptor_range_texture.NumDescriptors = 1; - descriptor_range_texture.BaseShaderRegister = 0; - descriptor_range_texture.RegisterSpace = 0; - descriptor_range_texture.OffsetInDescriptorsFromTableStart = 0; - - root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - root_parameters[1].DescriptorTable.NumDescriptorRanges = 1; - root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_range_sampler; - root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - descriptor_range_sampler.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - descriptor_range_sampler.NumDescriptors = 1; - descriptor_range_sampler.BaseShaderRegister = 0; - descriptor_range_sampler.RegisterSpace = 0; - descriptor_range_sampler.OffsetInDescriptorsFromTableStart = 0; - - root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; - root_parameters[2].Constants.ShaderRegister = 0; - root_parameters[2].Constants.RegisterSpace = 0; - root_parameters[2].Constants.Num32BitValues = 2; - root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; - - D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {}; - root_signature_desc.NumParameters = uint32_t(std::size(root_parameters)); - root_signature_desc.pParameters = root_parameters; - root_signature_desc.NumStaticSamplers = 0; - root_signature_desc.pStaticSamplers = nullptr; - root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; - raster_preview_root_signature_.Attach( - rex::ui::d3d12::util::CreateRootSignature(*d3d12_provider_, root_signature_desc)); - if (!raster_preview_root_signature_) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to create the raster replay " - "root signature"); - return false; - } - - D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_desc = {}; - pipeline_desc.pRootSignature = raster_preview_root_signature_.Get(); - pipeline_desc.VS.pShaderBytecode = shaders::immediate_vs; - pipeline_desc.VS.BytecodeLength = sizeof(shaders::immediate_vs); - pipeline_desc.PS.pShaderBytecode = shaders::immediate_ps; - pipeline_desc.PS.BytecodeLength = sizeof(shaders::immediate_ps); - D3D12_RENDER_TARGET_BLEND_DESC& blend_desc = pipeline_desc.BlendState.RenderTarget[0]; - blend_desc.BlendEnable = TRUE; - blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA; - blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA; - blend_desc.BlendOp = D3D12_BLEND_OP_ADD; - blend_desc.SrcBlendAlpha = D3D12_BLEND_ONE; - blend_desc.DestBlendAlpha = D3D12_BLEND_ONE; - blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD; - blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; - pipeline_desc.SampleMask = UINT_MAX; - pipeline_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; - pipeline_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; - pipeline_desc.RasterizerState.FrontCounterClockwise = FALSE; - pipeline_desc.RasterizerState.DepthClipEnable = TRUE; - D3D12_INPUT_ELEMENT_DESC input_elements[3] = {}; - input_elements[0].SemanticName = "POSITION"; - input_elements[0].Format = DXGI_FORMAT_R32G32_FLOAT; - input_elements[0].AlignedByteOffset = offsetof(rex::ui::ImmediateVertex, x); - input_elements[1].SemanticName = "TEXCOORD"; - input_elements[1].Format = DXGI_FORMAT_R32G32_FLOAT; - input_elements[1].AlignedByteOffset = offsetof(rex::ui::ImmediateVertex, u); - input_elements[2].SemanticName = "COLOR"; - input_elements[2].Format = DXGI_FORMAT_R8G8B8A8_UNORM; - input_elements[2].AlignedByteOffset = offsetof(rex::ui::ImmediateVertex, color); - pipeline_desc.InputLayout.pInputElementDescs = input_elements; - pipeline_desc.InputLayout.NumElements = uint32_t(std::size(input_elements)); - pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - pipeline_desc.NumRenderTargets = 1; - pipeline_desc.RTVFormats[0] = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat; - pipeline_desc.SampleDesc.Count = 1; - if (FAILED(device->CreateGraphicsPipelineState(&pipeline_desc, - IID_PPV_ARGS(&raster_preview_pipeline_)))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to create the raster replay " - "pipeline"); - return false; - } - - D3D12_DESCRIPTOR_HEAP_DESC view_heap_desc = {}; - view_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - view_heap_desc.NumDescriptors = 1; - view_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - if (FAILED(device->CreateDescriptorHeap(&view_heap_desc, - IID_PPV_ARGS(&raster_preview_view_heap_)))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to create the raster replay " - "view heap"); - return false; - } - - D3D12_SHADER_RESOURCE_VIEW_DESC texture_view_desc = {}; - texture_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - texture_view_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - texture_view_desc.Shader4ComponentMapping = - D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING( - D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, - D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, - D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, - D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1); - texture_view_desc.Texture2D.MostDetailedMip = 0; - texture_view_desc.Texture2D.MipLevels = 1; - texture_view_desc.Texture2D.PlaneSlice = 0; - texture_view_desc.Texture2D.ResourceMinLODClamp = 0.0f; - device->CreateShaderResourceView( - nullptr, &texture_view_desc, - raster_preview_view_heap_->GetCPUDescriptorHandleForHeapStart()); - - D3D12_DESCRIPTOR_HEAP_DESC sampler_heap_desc = {}; - sampler_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; - sampler_heap_desc.NumDescriptors = 1; - sampler_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - if (FAILED(device->CreateDescriptorHeap(&sampler_heap_desc, - IID_PPV_ARGS(&raster_preview_sampler_heap_)))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to create the raster replay " - "sampler heap"); - return false; - } - - D3D12_SAMPLER_DESC sampler_desc = {}; - sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - sampler_desc.MaxAnisotropy = 1; - device->CreateSampler(&sampler_desc, - raster_preview_sampler_heap_->GetCPUDescriptorHandleForHeapStart()); - - D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc = {}; - rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - rtv_heap_desc.NumDescriptors = 1; - rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - rtv_heap_desc.NodeMask = 0; - - D3D12_RESOURCE_DESC upload_buffer_desc = {}; - rex::ui::d3d12::util::FillBufferResourceDesc( - upload_buffer_desc, sizeof(rex::ui::ImmediateVertex) * kRasterPreviewMaxVertexCount, - D3D12_RESOURCE_FLAG_NONE); - - for (PlaceholderRefreshSlot& slot : placeholder_refresh_slots_) { - if (FAILED(device->CreateDescriptorHeap(&rtv_heap_desc, IID_PPV_ARGS(&slot.rtv_heap)))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to create a raster replay " - "RTV heap"); - return false; - } - if (FAILED(device->CreateCommittedResource( - &rex::ui::d3d12::util::kHeapPropertiesUpload, D3D12_HEAP_FLAG_NONE, - &upload_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, - IID_PPV_ARGS(&slot.vertex_upload_buffer)))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to create a raster replay " - "vertex upload buffer"); - return false; - } - D3D12_RANGE read_range = {0, 0}; - if (FAILED(slot.vertex_upload_buffer->Map(0, &read_range, - reinterpret_cast(&slot.vertex_upload_mapping)))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to map a raster replay " - "vertex upload buffer"); - return false; - } - } - - return true; - } - - bool EnsureRasterPreviewRenderTarget(PlaceholderRefreshSlot& slot, uint32_t width, - uint32_t height) { - if (slot.raster_render_target && slot.raster_target_width == width && - slot.raster_target_height == height) { - return true; - } - - slot.raster_render_target.Reset(); - slot.raster_target_width = 0; - slot.raster_target_height = 0; - - D3D12_CLEAR_VALUE clear_value = {}; - clear_value.Format = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat; - clear_value.Color[0] = 0.0f; - clear_value.Color[1] = 0.0f; - clear_value.Color[2] = 0.0f; - clear_value.Color[3] = 1.0f; - - D3D12_RESOURCE_DESC render_target_desc = {}; - render_target_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - render_target_desc.Alignment = 0; - render_target_desc.Width = width; - render_target_desc.Height = height; - render_target_desc.DepthOrArraySize = 1; - render_target_desc.MipLevels = 1; - render_target_desc.Format = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat; - render_target_desc.SampleDesc.Count = 1; - render_target_desc.SampleDesc.Quality = 0; - render_target_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - render_target_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; - if (FAILED(d3d12_provider_->GetDevice()->CreateCommittedResource( - &rex::ui::d3d12::util::kHeapPropertiesDefault, - d3d12_provider_->GetHeapFlagCreateNotZeroed(), &render_target_desc, - D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value, - IID_PPV_ARGS(&slot.raster_render_target)))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to create a {}x{} raster replay " - "render target", - width, height); - return false; - } - - d3d12_provider_->GetDevice()->CreateRenderTargetView( - slot.raster_render_target.Get(), nullptr, slot.rtv_heap->GetCPUDescriptorHandleForHeapStart()); - slot.raster_target_width = width; - slot.raster_target_height = height; - return true; - } - - bool InitializePlaceholderRefreshResources() { - auto fail = [this](const char* message) { - REXLOG_ERROR("{}", message); - ShutdownPlaceholderRefreshResources(); - return false; - }; - - ID3D12Device* device = d3d12_provider_->GetDevice(); - ID3D12CommandQueue* direct_queue = d3d12_provider_->GetDirectQueue(); - if (!device || !direct_queue) { - return fail("AC6 native graphics bootstrap D3D12 provider is missing device or queue"); - } - - if (!placeholder_submission_tracker_.Initialize(device, direct_queue)) { - return fail("AC6 native graphics bootstrap failed to create the placeholder submission tracker"); - } - - D3D12_DESCRIPTOR_HEAP_DESC uav_heap_desc = {}; - uav_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - uav_heap_desc.NumDescriptors = 1; - uav_heap_desc.NodeMask = 0; - - for (uint32_t i = 0; i < kPlaceholderRefreshSlotCount; ++i) { - PlaceholderRefreshSlot& slot = placeholder_refresh_slots_[i]; - if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, - IID_PPV_ARGS(&slot.command_allocator)))) { - return fail("AC6 native graphics bootstrap failed to create a placeholder command allocator"); - } - - uav_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - if (FAILED(device->CreateDescriptorHeap(&uav_heap_desc, - IID_PPV_ARGS(&slot.shader_visible_uav_heap)))) { - return fail( - "AC6 native graphics bootstrap failed to create a shader-visible UAV heap"); - } - - uav_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - if (FAILED(device->CreateDescriptorHeap(&uav_heap_desc, IID_PPV_ARGS(&slot.cpu_uav_heap)))) { - return fail("AC6 native graphics bootstrap failed to create a CPU UAV heap"); - } - } - - if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, - placeholder_refresh_slots_[0].command_allocator.Get(), - nullptr, IID_PPV_ARGS(&placeholder_command_list_)))) { - return fail("AC6 native graphics bootstrap failed to create the placeholder command list"); - } - if (FAILED(placeholder_command_list_->Close())) { - return fail("AC6 native graphics bootstrap failed to close the placeholder command list"); - } - - if (!InitializeRasterPreviewResources(device)) { - return fail("AC6 native graphics bootstrap failed to initialize raster replay resources"); - } - - placeholder_refresh_slot_index_ = 0; - return true; - } - - void ShutdownPlaceholderRefreshResources() { - placeholder_submission_tracker_.Shutdown(); - placeholder_command_list_.Reset(); - raster_preview_pipeline_.Reset(); - raster_preview_root_signature_.Reset(); - raster_preview_view_heap_.Reset(); - raster_preview_sampler_heap_.Reset(); - placeholder_refresh_slot_index_ = 0; - placeholder_resources_initialized_ = false; - { - std::lock_guard lock(g_native_graphics_status_mutex); - g_native_graphics_status.placeholder_resources_initialized = false; - } - for (PlaceholderRefreshSlot& slot : placeholder_refresh_slots_) { - slot.last_submission = 0; - if (slot.vertex_upload_buffer && slot.vertex_upload_mapping) { - slot.vertex_upload_buffer->Unmap(0, nullptr); - } - slot.vertex_upload_mapping = nullptr; - slot.vertex_upload_buffer.Reset(); - slot.raster_render_target.Reset(); - slot.rtv_heap.Reset(); - slot.raster_target_width = 0; - slot.raster_target_height = 0; - slot.cpu_uav_heap.Reset(); - slot.shader_visible_uav_heap.Reset(); - slot.command_allocator.Reset(); - } - } - - bool RefreshPlaceholderFrame(const rex::system::GraphicsSwapSubmission& submission) { - if (!d3d12_presenter_ || !submission.frontbuffer_width || !submission.frontbuffer_height) { - return false; - } - - return d3d12_presenter_->RefreshGuestOutput( - submission.frontbuffer_width, submission.frontbuffer_height, - submission.frontbuffer_width, submission.frontbuffer_height, - [this, submission](rex::ui::Presenter::GuestOutputRefreshContext& context) { - return RecordPlaceholderFrame( - static_cast( - context), - submission); - }); - } - - bool SubmitPlaceholderCommandList(PlaceholderRefreshSlot& slot) { - if (FAILED(placeholder_command_list_->Close())) { - REXLOG_ERROR("AC6 native graphics bootstrap failed to close the placeholder command list"); - return false; - } - - ID3D12CommandList* execute_command_list = placeholder_command_list_.Get(); - d3d12_provider_->GetDirectQueue()->ExecuteCommandLists(1, &execute_command_list); - slot.last_submission = placeholder_submission_tracker_.GetCurrentSubmission(); - if (!placeholder_submission_tracker_.NextSubmission()) { - REXLOG_WARN( - "AC6 native graphics bootstrap could not signal the placeholder " - "refresh fence immediately"); - } - placeholder_refresh_slot_index_ = - (placeholder_refresh_slot_index_ + 1) % kPlaceholderRefreshSlotCount; - return true; - } - - bool TryRecordRasterPlaceholderFrame( - PlaceholderRefreshSlot& slot, ID3D12Resource* guest_output_resource, - const rex::system::GraphicsSwapSubmission& submission) { - if (!raster_preview_pipeline_ || !raster_preview_root_signature_ || !raster_preview_view_heap_ || - !raster_preview_sampler_heap_ || !slot.rtv_heap || !slot.vertex_upload_mapping || - !guest_output_resource) { - return false; - } - if (!EnsureRasterPreviewRenderTarget(slot, submission.frontbuffer_width, - submission.frontbuffer_height)) { - return false; - } - - const UINT width = submission.frontbuffer_width; - const UINT height = submission.frontbuffer_height; - const UINT title_band_height = std::max(1, height / 8); - const UINT status_band_height = std::max(1, height / 20); - const UINT heartbeat_width = - std::max(1, UINT((uint64_t(width) * - (((swap_count_ - 1) % kPlaceholderHeartbeatPeriod) + 1)) / - kPlaceholderHeartbeatPeriod)); - - const NativeReplayPlanSummary& replay_plan = last_replay_plan_; - const SelectedPassPreviewData& selected_pass_preview = last_selected_pass_preview_; - const PlannedStagePreviewData& planned_stage_preview = last_planned_stage_preview_; - const bool diagnostic_overlay = IsDiagnosticPlaceholderPresentEnabled(); - const uint32_t candidate_seed = - replay_plan.present_candidate_rt0 ^ (replay_plan.present_candidate_depth_stencil * 33u) ^ - (replay_plan.selected_pass_score * 2654435761u); - auto channel = [candidate_seed](uint32_t shift, float low, float high) { - const float t = float((candidate_seed >> shift) & 0xFFu) / 255.0f; - return low + (high - low) * t; - }; - - const FloatColor background_color = EnsureVisibleColor( - {replay_plan.valid ? 0.03f : 0.08f, replay_plan.valid ? 0.04f : 0.06f, - replay_plan.valid ? 0.07f : 0.10f, 1.0f}, - 0.16f); - const FloatColor title_band_color = { - replay_plan.selected_pass_is_stable ? 0.18f - : (replay_plan.present_candidate_matches_swap_size ? 0.18f : 0.55f), - replay_plan.selected_pass_is_stable ? 0.62f - : (replay_plan.present_candidate_matches_swap_size ? 0.56f : 0.22f), - replay_plan.selected_pass_is_stable ? 0.46f - : (replay_plan.present_candidate_matches_swap_size ? 0.24f : 0.18f), 1.0f}; - const FloatColor fallback_candidate_color = {channel(0, 0.20f, 0.92f), - channel(8, 0.18f, 0.75f), - channel(16, 0.16f, 0.88f), 1.0f}; - const FloatColor resolve_color = {0.98f, 0.95f, 0.28f, 0.92f}; - const FloatColor heartbeat_color = {0.96f, 0.92f, 0.22f, 1.0f}; - const FloatColor candidate_base_color = EnsureVisibleColor( - selected_pass_preview.summary.using_clear_fill - ? DecodeArgbColor(selected_pass_preview.summary.last_clear_color) - : fallback_candidate_color, - 0.20f); - const FloatColor scene_stage_color = EnsureVisibleColor( - planned_stage_preview.scene.using_clear_color - ? DecodeArgbColor(planned_stage_preview.scene.last_clear_color) - : MixColors(candidate_base_color, background_color, 0.18f), - 0.20f); - const FloatColor postfx_stage_color = EnsureVisibleColor( - planned_stage_preview.postfx.using_clear_color - ? DecodeArgbColor(planned_stage_preview.postfx.last_clear_color) - : resolve_color, - 0.24f); - const FloatColor ui_stage_color = EnsureVisibleColor( - planned_stage_preview.ui.using_clear_color - ? DecodeArgbColor(planned_stage_preview.ui.last_clear_color) - : MixColors(heartbeat_color, title_band_color, 0.35f), - 0.24f); - const FloatColor stripe_color = MixColors(candidate_base_color, title_band_color, 0.40f); - const FloatColor candidate_outline_color = - MixColors(candidate_base_color, heartbeat_color, 0.55f); - const FloatColor score_color = MixColors(candidate_outline_color, heartbeat_color, 0.35f); - - UINT candidate_left = 0; - UINT candidate_top = title_band_height; - UINT candidate_width = width; - UINT candidate_height = std::max(1, height - title_band_height - status_band_height); - if (replay_plan.valid && replay_plan.present_candidate_viewport_width && - replay_plan.present_candidate_viewport_height) { - candidate_left = std::min(width - 1, replay_plan.present_candidate_viewport_x); - candidate_top = std::min(height - 1, replay_plan.present_candidate_viewport_y); - candidate_width = - std::max(1, std::min(replay_plan.present_candidate_viewport_width, - width - candidate_left)); - candidate_height = - std::max(1, std::min(replay_plan.present_candidate_viewport_height, - height - candidate_top)); - } - const UINT candidate_right = std::min(width, candidate_left + candidate_width); - const UINT candidate_bottom = std::min(height, candidate_top + candidate_height); - - std::array vertices = {}; - uint32_t vertex_count = 0; - auto push_rect = [&](float left, float top, float right, float bottom, FloatColor color) { - left = std::clamp(left, 0.0f, float(width)); - top = std::clamp(top, 0.0f, float(height)); - right = std::clamp(right, 0.0f, float(width)); - bottom = std::clamp(bottom, 0.0f, float(height)); - if (left >= right || top >= bottom || vertex_count + 6 > vertices.size()) { - return; - } - const uint32_t packed_color = PackImmediateColor(color); - auto emit_vertex = [&](float x, float y) { - rex::ui::ImmediateVertex& vertex = vertices[vertex_count++]; - vertex.x = x; - vertex.y = y; - vertex.u = 0.0f; - vertex.v = 0.0f; - vertex.color = packed_color; - }; - emit_vertex(left, top); - emit_vertex(right, top); - emit_vertex(right, bottom); - emit_vertex(left, top); - emit_vertex(right, bottom); - emit_vertex(left, bottom); - }; - - auto push_stage_rect = [&](const PlannedStagePreviewStage& stage, FloatColor color) { - if (!stage.valid) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - push_rect(float(left), float(top), float(std::min(width, left + stage_width)), - float(std::min(height, top + stage_height)), color); - }; - auto push_stage_clear_steps = [&](const PlannedStagePreviewStage& stage, float alpha_scale) { - if (!stage.valid || !stage.clear_step_count) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - const UINT right = std::min(width, left + stage_width); - const UINT bottom = std::min(height, top + stage_height); - for (uint32_t i = 0; i < stage.clear_step_count; ++i) { - const SelectedPassClearStep& step = stage.clear_steps[i]; - FloatColor step_color = DecodeArgbColor(step.color); - step_color[3] = std::clamp(alpha_scale, 0.0f, 1.0f); - if (step.rect_count) { - for (uint32_t rect_index = 0; rect_index < step.rect_count; ++rect_index) { - const ac6::d3d::ClearRect& rect = step.rects[rect_index]; - push_rect(float(rect.left), float(rect.top), float(rect.right), float(rect.bottom), - step_color); - } - } else { - push_rect(float(left), float(top), float(right), float(bottom), step_color); - } - } - }; - auto push_stage_draw_samples = [&](const PlannedStagePreviewStage& stage, - const FloatColor& base_color) { - if (!stage.valid || !stage.draw_sample_count) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - const UINT right = std::min(width, left + stage_width); - const UINT bottom = std::min(height, top + stage_height); - const UINT columns = std::min(3, std::max(1, stage.draw_sample_count)); - const UINT rows = - std::max(1, (stage.draw_sample_count + columns - 1) / columns); - const UINT cell_width = std::max(1, stage_width / columns); - const UINT cell_height = std::max(1, stage_height / rows); - for (uint32_t i = 0; i < stage.draw_sample_count; ++i) { - const PlannedStageDrawSample& sample = stage.draw_samples[i]; - const UINT row = i / columns; - const UINT column = i % columns; - const UINT cell_left = std::min(right, left + column * cell_width); - const UINT cell_top = std::min(bottom, top + row * cell_height); - const UINT cell_right = (column + 1 == columns) - ? right - : std::min(right, cell_left + cell_width); - const UINT cell_bottom = (row + 1 == rows) - ? bottom - : std::min(bottom, cell_top + cell_height); - const UINT inset = std::max(1, std::min(6, cell_width / 12 + 1)); - const UINT content_left = std::min(cell_right, cell_left + inset); - const UINT content_top = std::min(cell_bottom, cell_top + inset); - const UINT content_right = - cell_right > inset ? cell_right - inset : cell_right; - const UINT content_bottom = - cell_bottom > inset ? cell_bottom - inset : cell_bottom; - if (content_left >= content_right || content_top >= content_bottom) { - continue; - } - const float state_mix = std::clamp( - 0.15f + float(sample.texture_count + sample.stream_count + - sample.fetch_constant_count) / - 24.0f, - 0.15f, 0.60f); - FloatColor sample_color = MixColors( - base_color, sample.indexed ? heartbeat_color : resolve_color, state_mix); - sample_color[3] = diagnostic_overlay ? 0.62f : 0.46f; - const UINT content_height = content_bottom - content_top; - const UINT panel_height = std::max( - 1, std::min(content_height, - std::max(1, (content_height * - std::min(sample.draw_count, 96u)) / - 96u))); - const UINT panel_top = content_bottom > panel_height - ? content_bottom - panel_height - : content_top; - push_rect(float(content_left), float(panel_top), float(content_right), - float(content_bottom), sample_color); - - const UINT stream_band_width = std::min( - content_right - content_left, - std::max(1, ((content_right - content_left) * - std::min(sample.stream_count, 8u)) / - 8u)); - FloatColor stream_band_color = MixColors(sample_color, background_color, 0.22f); - stream_band_color[3] = 0.85f; - push_rect(float(content_left), float(content_top), - float(std::min(content_right, content_left + stream_band_width)), - float(std::min(content_bottom, content_top + std::max(1, inset))), - stream_band_color); - - const UINT texture_band_height = std::min( - content_bottom - content_top, - std::max(1, ((content_bottom - content_top) * - std::min(sample.texture_count, 16u)) / - 16u)); - FloatColor texture_band_color = MixColors(sample_color, candidate_outline_color, 0.35f); - texture_band_color[3] = 0.88f; - push_rect(float(content_left), float(content_top), float(content_right), - float(std::min(content_bottom, content_top + texture_band_height)), - texture_band_color); - - if (sample.fetch_constant_count) { - FloatColor fetch_marker_color = MixColors(heartbeat_color, resolve_color, 0.4f); - fetch_marker_color[3] = 0.92f; - const UINT marker_width = - std::max(1, std::min(4, (content_right - content_left) / 8 + 1)); - push_rect(float(content_right > marker_width ? content_right - marker_width - : content_left), - float(content_top), float(content_right), float(content_bottom), - fetch_marker_color); - } - } - }; - - auto push_ui_hud = [&](const PlannedStagePreviewStage& stage, FloatColor color) { - if (!stage.valid) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - const UINT right = std::min(width, left + stage_width); - const UINT bottom = std::min(height, top + stage_height); - const UINT hud_band_height = std::max(1, stage_height / 12); - const UINT side_panel_width = std::max(1, stage_width / 7); - FloatColor strip_color = color; - strip_color[3] = diagnostic_overlay ? 0.78f : 0.58f; - push_rect(float(left), float(top), float(right), - float(std::min(bottom, top + hud_band_height)), strip_color); - push_rect(float(left), float(bottom > hud_band_height ? bottom - hud_band_height : top), - float(right), float(bottom), strip_color); - push_rect(float(left), float(top + hud_band_height), - float(std::min(right, left + side_panel_width)), float(bottom), strip_color); - push_rect(float(right > side_panel_width ? right - side_panel_width : left), - float(top + hud_band_height), float(right), float(bottom), strip_color); - }; - - push_rect(float(candidate_left), float(candidate_top), float(candidate_right), - float(candidate_bottom), candidate_base_color); - if (planned_stage_preview.scene.valid) { - FloatColor color = scene_stage_color; - color[3] = 1.0f; - push_stage_rect(planned_stage_preview.scene, color); - push_stage_clear_steps(planned_stage_preview.scene, 0.34f); - push_stage_draw_samples(planned_stage_preview.scene, color); - } - if (planned_stage_preview.postfx.valid) { - FloatColor color = postfx_stage_color; - color[3] = diagnostic_overlay ? 0.28f : 0.18f; - push_stage_rect(planned_stage_preview.postfx, color); - push_stage_clear_steps(planned_stage_preview.postfx, diagnostic_overlay ? 0.30f : 0.20f); - } - if (planned_stage_preview.ui.valid) { - push_stage_clear_steps(planned_stage_preview.ui, diagnostic_overlay ? 0.32f : 0.22f); - push_ui_hud(planned_stage_preview.ui, ui_stage_color); - } - - const FloatColor clear_overlay_tint = {1.0f, 1.0f, 1.0f, 0.92f}; - for (uint32_t i = 0; i < selected_pass_preview.clear_step_count; ++i) { - const SelectedPassClearStep& step = selected_pass_preview.clear_steps[i]; - FloatColor step_color = DecodeArgbColor(step.color); - step_color[3] = clear_overlay_tint[3]; - if (step.rect_count) { - for (uint32_t rect_index = 0; rect_index < step.rect_count; ++rect_index) { - const ac6::d3d::ClearRect& rect = step.rects[rect_index]; - push_rect(float(rect.left), float(rect.top), float(rect.right), float(rect.bottom), - step_color); - } - } else { - push_rect(float(candidate_left), float(candidate_top), float(candidate_right), - float(candidate_bottom), step_color); - } - } - - UINT resolve_band_height = 0; - if (replay_plan.valid && replay_plan.selected_pass_has_resolve) { - resolve_band_height = std::max(1, candidate_height / 10); - push_rect(float(candidate_left), float(candidate_bottom - resolve_band_height), - float(candidate_right), float(candidate_bottom), resolve_color); - } - - if (selected_pass_preview.summary.draw_count && candidate_width > 8 && candidate_height > 8) { - const UINT stripe_count = - std::min(12, std::max(1, 1 + (selected_pass_preview.summary.draw_count / 6))); - const UINT stripe_width = - std::max(1, candidate_width / std::max(24, stripe_count * 5)); - const UINT stripe_top = candidate_top; - const UINT stripe_bottom = - candidate_bottom > resolve_band_height ? (candidate_bottom - resolve_band_height) - : candidate_bottom; - const UINT stripe_area_height = - stripe_bottom > stripe_top ? (stripe_bottom - stripe_top) : 0; - for (UINT i = 0; i < stripe_count && stripe_area_height; ++i) { - const UINT stripe_center_x = - candidate_left + ((i + 1) * candidate_width) / (stripe_count + 1); - const UINT stripe_left = stripe_center_x > stripe_width / 2 - ? stripe_center_x - stripe_width / 2 - : candidate_left; - const UINT stripe_right = std::min(candidate_right, stripe_left + stripe_width); - const UINT stripe_height = - std::max(1, stripe_area_height * (45 + ((i * 13) % 40)) / 100); - const UINT stripe_start = stripe_bottom > stripe_height ? (stripe_bottom - stripe_height) - : stripe_top; - FloatColor lane_color = - (i & 1u) ? MixColors(stripe_color, heartbeat_color, 0.22f) - : MixColors(stripe_color, background_color, 0.10f); - lane_color[3] = 0.42f; - push_rect(float(stripe_left), float(stripe_start), float(stripe_right), - float(stripe_bottom), lane_color); - } - } - - if (candidate_width > 2 && candidate_height > 2) { - const UINT outline_thickness = - std::max(1, std::min(4, std::min(candidate_width, candidate_height) / 96 + 1)); - const UINT bottom_outline_top = - candidate_bottom > outline_thickness ? (candidate_bottom - outline_thickness) - : candidate_top; - push_rect(float(candidate_left), float(candidate_top), float(candidate_right), - float(std::min(candidate_bottom, candidate_top + outline_thickness)), - candidate_outline_color); - push_rect(float(candidate_left), float(bottom_outline_top), float(candidate_right), - float(candidate_bottom), candidate_outline_color); - push_rect(float(candidate_left), float(candidate_top), - float(std::min(candidate_right, candidate_left + outline_thickness)), - float(candidate_bottom), candidate_outline_color); - push_rect(float(candidate_right > outline_thickness ? (candidate_right - outline_thickness) - : candidate_left), - float(candidate_top), float(candidate_right), float(candidate_bottom), - candidate_outline_color); - } - - if (replay_plan.valid && replay_plan.pass_count) { - const UINT score_width = std::max( - 1, UINT((uint64_t(width) * std::min(replay_plan.selected_pass_score, 220u)) / - 220u)); - push_rect(0.0f, float(title_band_height), float(score_width), - float(std::min(height, title_band_height + status_band_height)), score_color); - } - push_rect(0.0f, 0.0f, float(width), float(title_band_height), title_band_color); - push_rect(0.0f, float(height - status_band_height), float(heartbeat_width), float(height), - heartbeat_color); - // Always paint a bright native-present marker to avoid a full-black frame. - push_rect(0.0f, 0.0f, float(std::max(1, width / 64)), - float(std::max(1, height / 18)), {0.95f, 0.30f, 0.12f, 1.0f}); - - D3D12_VIEWPORT viewport = {}; - viewport.TopLeftX = 0.0f; - viewport.TopLeftY = 0.0f; - viewport.Width = float(width); - viewport.Height = float(height); - viewport.MinDepth = 0.0f; - viewport.MaxDepth = 1.0f; - D3D12_RECT scissor = {0, 0, LONG(width), LONG(height)}; - placeholder_command_list_->RSSetViewports(1, &viewport); - placeholder_command_list_->RSSetScissorRects(1, &scissor); - - const D3D12_CPU_DESCRIPTOR_HANDLE rtv = - slot.rtv_heap->GetCPUDescriptorHandleForHeapStart(); - placeholder_command_list_->OMSetRenderTargets(1, &rtv, TRUE, nullptr); - placeholder_command_list_->ClearRenderTargetView(rtv, background_color.data(), 0, nullptr); - - if (vertex_count) { - std::memcpy(slot.vertex_upload_mapping, vertices.data(), - sizeof(rex::ui::ImmediateVertex) * vertex_count); - D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view = {}; - vertex_buffer_view.BufferLocation = slot.vertex_upload_buffer->GetGPUVirtualAddress(); - vertex_buffer_view.SizeInBytes = UINT(sizeof(rex::ui::ImmediateVertex) * vertex_count); - vertex_buffer_view.StrideInBytes = UINT(sizeof(rex::ui::ImmediateVertex)); - - ID3D12DescriptorHeap* descriptor_heaps[] = {raster_preview_view_heap_.Get(), - raster_preview_sampler_heap_.Get()}; - placeholder_command_list_->SetDescriptorHeaps(uint32_t(std::size(descriptor_heaps)), - descriptor_heaps); - placeholder_command_list_->SetGraphicsRootSignature(raster_preview_root_signature_.Get()); - const float coordinate_space_size_inv[2] = {1.0f / float(width), 1.0f / float(height)}; - placeholder_command_list_->SetGraphicsRoot32BitConstants(2, 2, coordinate_space_size_inv, 0); - placeholder_command_list_->SetGraphicsRootDescriptorTable( - 0, raster_preview_view_heap_->GetGPUDescriptorHandleForHeapStart()); - placeholder_command_list_->SetGraphicsRootDescriptorTable( - 1, raster_preview_sampler_heap_->GetGPUDescriptorHandleForHeapStart()); - placeholder_command_list_->SetPipelineState(raster_preview_pipeline_.Get()); - placeholder_command_list_->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - placeholder_command_list_->IASetVertexBuffers(0, 1, &vertex_buffer_view); - placeholder_command_list_->DrawInstanced(vertex_count, 1, 0, 0); - } - - D3D12_RESOURCE_BARRIER barriers[3] = {}; - barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barriers[0].Transition.pResource = slot.raster_render_target.Get(); - barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; - barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; - barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barriers[1].Transition.pResource = guest_output_resource; - barriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barriers[1].Transition.StateBefore = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState; - barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; - placeholder_command_list_->ResourceBarrier(2, barriers); - - D3D12_TEXTURE_COPY_LOCATION copy_dest = {}; - copy_dest.pResource = guest_output_resource; - copy_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - copy_dest.SubresourceIndex = 0; - D3D12_TEXTURE_COPY_LOCATION copy_source = {}; - copy_source.pResource = slot.raster_render_target.Get(); - copy_source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - copy_source.SubresourceIndex = 0; - placeholder_command_list_->CopyTextureRegion(©_dest, 0, 0, 0, ©_source, nullptr); - - barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; - barriers[0].Transition.StateAfter = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState; - barriers[0].Transition.pResource = guest_output_resource; - barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; - barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - barriers[1].Transition.pResource = slot.raster_render_target.Get(); - placeholder_command_list_->ResourceBarrier(2, barriers); - - return true; - } - - bool RecordPlaceholderFrame( - rex::ui::d3d12::D3D12Presenter::D3D12GuestOutputRefreshContext& context, - const rex::system::GraphicsSwapSubmission& submission) { - std::lock_guard lock(placeholder_refresh_mutex_); - - if (!d3d12_provider_ || !placeholder_command_list_) { - return false; - } - - PlaceholderRefreshSlot& slot = placeholder_refresh_slots_[placeholder_refresh_slot_index_]; - if (!REXCVAR_GET(ac6_native_present_skip_submission_wait)) { - if (slot.last_submission && - !placeholder_submission_tracker_.AwaitSubmissionCompletion(slot.last_submission)) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to await placeholder refresh " - "slot {}", placeholder_refresh_slot_index_); - return false; - } - } - - ID3D12Resource* guest_output_resource = context.resource_uav_capable(); - if (!guest_output_resource) { - return false; - } - - if (FAILED(slot.command_allocator->Reset())) { - REXLOG_ERROR("AC6 native graphics bootstrap failed to reset a placeholder command allocator"); - return false; - } - if (FAILED(placeholder_command_list_->Reset(slot.command_allocator.Get(), nullptr))) { - REXLOG_ERROR("AC6 native graphics bootstrap failed to reset the placeholder command list"); - return false; - } - - const UINT width = submission.frontbuffer_width; - const UINT height = submission.frontbuffer_height; - const UINT title_band_height = std::max(1, height / 8); - const UINT status_band_height = std::max(1, height / 20); - const UINT heartbeat_width = - std::max(1, UINT((uint64_t(width) * - (((swap_count_ - 1) % kPlaceholderHeartbeatPeriod) + 1)) / - kPlaceholderHeartbeatPeriod)); - - const NativeReplayPlanSummary& replay_plan = last_replay_plan_; - const SelectedPassPreviewData& selected_pass_preview = last_selected_pass_preview_; - const PlannedStagePreviewData& planned_stage_preview = last_planned_stage_preview_; - const bool diagnostic_overlay = IsDiagnosticPlaceholderPresentEnabled(); - const uint32_t candidate_seed = - replay_plan.present_candidate_rt0 ^ (replay_plan.present_candidate_depth_stencil * 33u) ^ - (replay_plan.selected_pass_score * 2654435761u); - auto channel = [candidate_seed](uint32_t shift, float low, float high) { - float t = float((candidate_seed >> shift) & 0xFFu) / 255.0f; - return low + (high - low) * t; - }; - - const FloatColor background_color = EnsureVisibleColor( - {replay_plan.valid ? 0.04f : 0.08f, replay_plan.valid ? 0.05f : 0.06f, - replay_plan.valid ? 0.08f : 0.10f, 1.0f}, - 0.16f); - const FloatColor title_band_color = { - replay_plan.selected_pass_is_stable ? 0.18f - : (replay_plan.present_candidate_matches_swap_size ? 0.18f : 0.55f), - replay_plan.selected_pass_is_stable ? 0.62f - : (replay_plan.present_candidate_matches_swap_size ? 0.56f : 0.22f), - replay_plan.selected_pass_is_stable ? 0.46f - : (replay_plan.present_candidate_matches_swap_size ? 0.24f : 0.18f), 1.0f}; - const FloatColor fallback_candidate_color = {channel(0, 0.20f, 0.92f), - channel(8, 0.18f, 0.75f), - channel(16, 0.16f, 0.88f), 1.0f}; - const FloatColor resolve_color = {0.96f, 0.94f, 0.30f, 1.0f}; - const FloatColor heartbeat_color = {0.95f, 0.92f, 0.24f, 1.0f}; - const FloatColor candidate_base_color = EnsureVisibleColor( - selected_pass_preview.summary.using_clear_fill - ? DecodeArgbColor(selected_pass_preview.summary.last_clear_color) - : fallback_candidate_color, - 0.20f); - const FloatColor scene_stage_color = EnsureVisibleColor( - planned_stage_preview.scene.using_clear_color - ? DecodeArgbColor(planned_stage_preview.scene.last_clear_color) - : MixColors(candidate_base_color, background_color, 0.18f), - 0.20f); - const FloatColor postfx_stage_color = EnsureVisibleColor( - planned_stage_preview.postfx.using_clear_color - ? DecodeArgbColor(planned_stage_preview.postfx.last_clear_color) - : resolve_color, - 0.24f); - const FloatColor ui_stage_color = EnsureVisibleColor( - planned_stage_preview.ui.using_clear_color - ? DecodeArgbColor(planned_stage_preview.ui.last_clear_color) - : MixColors(heartbeat_color, title_band_color, 0.35f), - 0.24f); - const FloatColor candidate_outline_color = - MixColors(candidate_base_color, heartbeat_color, 0.5f); - const FloatColor stripe_color = MixColors(candidate_base_color, title_band_color, 0.4f); - - UINT candidate_left = 0; - UINT candidate_top = title_band_height; - UINT candidate_width = width; - UINT candidate_height = std::max(1, height - title_band_height - status_band_height); - if (replay_plan.valid && replay_plan.present_candidate_viewport_width && - replay_plan.present_candidate_viewport_height) { - candidate_left = std::min(width - 1, replay_plan.present_candidate_viewport_x); - candidate_top = std::min(height - 1, replay_plan.present_candidate_viewport_y); - candidate_width = - std::max(1, std::min(replay_plan.present_candidate_viewport_width, - width - candidate_left)); - candidate_height = - std::max(1, std::min(replay_plan.present_candidate_viewport_height, - height - candidate_top)); - } - const UINT candidate_right = std::min(width, candidate_left + candidate_width); - const UINT candidate_bottom = std::min(height, candidate_top + candidate_height); - - auto record_minimal_uav = [&](ID3D12Resource* resource) -> bool { - if (!resource) { - return false; - } - ID3D12Device* device = d3d12_provider_->GetDevice(); - D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = {}; - uav_desc.Format = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat; - uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; - uav_desc.Texture2D.MipSlice = 0; - uav_desc.Texture2D.PlaneSlice = 0; - D3D12_CPU_DESCRIPTOR_HANDLE uav_cpu_visible = - slot.shader_visible_uav_heap->GetCPUDescriptorHandleForHeapStart(); - D3D12_GPU_DESCRIPTOR_HANDLE uav_gpu_visible = - slot.shader_visible_uav_heap->GetGPUDescriptorHandleForHeapStart(); - D3D12_CPU_DESCRIPTOR_HANDLE uav_cpu = slot.cpu_uav_heap->GetCPUDescriptorHandleForHeapStart(); - device->CreateUnorderedAccessView(guest_output_resource, nullptr, &uav_desc, uav_cpu_visible); - device->CreateUnorderedAccessView(guest_output_resource, nullptr, &uav_desc, uav_cpu); - ID3D12DescriptorHeap* descriptor_heaps[] = {slot.shader_visible_uav_heap.Get()}; - placeholder_command_list_->SetDescriptorHeaps(1, descriptor_heaps); - D3D12_RESOURCE_BARRIER barrier = {}; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = guest_output_resource; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - placeholder_command_list_->ResourceBarrier(1, &barrier); - auto clear_rect = [&](const FloatColor& color, UINT left, UINT top, UINT right, - UINT bottom) { - if (left >= right || top >= bottom) { - return; - } - D3D12_RECT rect = {LONG(left), LONG(top), LONG(right), LONG(bottom)}; - placeholder_command_list_->ClearUnorderedAccessViewFloat( - uav_gpu_visible, uav_cpu, resource, color.data(), 1, &rect); - }; - auto clear_stage_rect = [&](const PlannedStagePreviewStage& stage, FloatColor color) { - if (!stage.valid) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - clear_rect(color, left, top, std::min(width, left + stage_width), - std::min(height, top + stage_height)); - }; - auto clear_stage_clear_steps = [&](const PlannedStagePreviewStage& stage, float alpha_scale) { - if (!stage.valid || !stage.clear_step_count) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - const UINT right = std::min(width, left + stage_width); - const UINT bottom = std::min(height, top + stage_height); - for (uint32_t i = 0; i < stage.clear_step_count; ++i) { - const SelectedPassClearStep& step = stage.clear_steps[i]; - FloatColor step_color = DecodeArgbColor(step.color); - step_color[3] = std::clamp(alpha_scale, 0.0f, 1.0f); - if (step.rect_count) { - for (uint32_t rect_index = 0; rect_index < step.rect_count; ++rect_index) { - const ac6::d3d::ClearRect& rect = step.rects[rect_index]; - clear_rect(step_color, rect.left, rect.top, rect.right, rect.bottom); - } - } else { - clear_rect(step_color, left, top, right, bottom); - } - } - }; - auto clear_stage_draw_samples = [&](const PlannedStagePreviewStage& stage, - const FloatColor& base_color) { - if (!stage.valid || !stage.draw_sample_count) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - const UINT right = std::min(width, left + stage_width); - const UINT bottom = std::min(height, top + stage_height); - const UINT columns = std::min(3, std::max(1, stage.draw_sample_count)); - const UINT rows = - std::max(1, (stage.draw_sample_count + columns - 1) / columns); - const UINT cell_width = std::max(1, stage_width / columns); - const UINT cell_height = std::max(1, stage_height / rows); - for (uint32_t i = 0; i < stage.draw_sample_count; ++i) { - const PlannedStageDrawSample& sample = stage.draw_samples[i]; - const UINT row = i / columns; - const UINT column = i % columns; - const UINT cell_left = std::min(right, left + column * cell_width); - const UINT cell_top = std::min(bottom, top + row * cell_height); - const UINT cell_right = (column + 1 == columns) - ? right - : std::min(right, cell_left + cell_width); - const UINT cell_bottom = (row + 1 == rows) - ? bottom - : std::min(bottom, cell_top + cell_height); - const UINT inset = std::max(1, std::min(6, cell_width / 12 + 1)); - const UINT content_left = std::min(cell_right, cell_left + inset); - const UINT content_top = std::min(cell_bottom, cell_top + inset); - const UINT content_right = - cell_right > inset ? cell_right - inset : cell_right; - const UINT content_bottom = - cell_bottom > inset ? cell_bottom - inset : cell_bottom; - if (content_left >= content_right || content_top >= content_bottom) { - continue; - } - const float state_mix = std::clamp( - 0.15f + float(sample.texture_count + sample.stream_count + - sample.fetch_constant_count) / - 24.0f, - 0.15f, 0.60f); - FloatColor sample_color = MixColors( - base_color, sample.indexed ? heartbeat_color : resolve_color, state_mix); - sample_color[3] = diagnostic_overlay ? 0.62f : 0.46f; - const UINT content_height = content_bottom - content_top; - const UINT panel_height = std::max( - 1, std::min(content_height, - std::max(1, (content_height * - std::min(sample.draw_count, 96u)) / - 96u))); - const UINT panel_top = content_bottom > panel_height - ? content_bottom - panel_height - : content_top; - clear_rect(sample_color, content_left, panel_top, content_right, content_bottom); - - const UINT stream_band_width = std::min( - content_right - content_left, - std::max(1, ((content_right - content_left) * - std::min(sample.stream_count, 8u)) / - 8u)); - FloatColor stream_band_color = MixColors(sample_color, background_color, 0.22f); - stream_band_color[3] = 0.85f; - clear_rect(stream_band_color, content_left, content_top, - std::min(content_right, content_left + stream_band_width), - std::min(content_bottom, content_top + std::max(1, inset))); - - const UINT texture_band_height = std::min( - content_bottom - content_top, - std::max(1, ((content_bottom - content_top) * - std::min(sample.texture_count, 16u)) / - 16u)); - FloatColor texture_band_color = - MixColors(sample_color, candidate_outline_color, 0.35f); - texture_band_color[3] = 0.88f; - clear_rect(texture_band_color, content_left, content_top, content_right, - std::min(content_bottom, content_top + texture_band_height)); - - if (sample.fetch_constant_count) { - FloatColor fetch_marker_color = MixColors(heartbeat_color, resolve_color, 0.4f); - fetch_marker_color[3] = 0.92f; - const UINT marker_width = - std::max(1, std::min(4, (content_right - content_left) / 8 + 1)); - clear_rect(fetch_marker_color, - content_right > marker_width ? content_right - marker_width - : content_left, - content_top, content_right, content_bottom); - } - } - }; - auto clear_ui_hud = [&](const PlannedStagePreviewStage& stage, FloatColor color) { - if (!stage.valid) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - const UINT right = std::min(width, left + stage_width); - const UINT bottom = std::min(height, top + stage_height); - const UINT hud_band_height = std::max(1, stage_height / 12); - const UINT side_panel_width = std::max(1, stage_width / 7); - FloatColor strip_color = color; - strip_color[3] = diagnostic_overlay ? 0.78f : 0.58f; - clear_rect(strip_color, left, top, right, std::min(bottom, top + hud_band_height)); - clear_rect(strip_color, left, - bottom > hud_band_height ? bottom - hud_band_height : top, right, bottom); - clear_rect(strip_color, left, top + hud_band_height, - std::min(right, left + side_panel_width), bottom); - clear_rect(strip_color, right > side_panel_width ? right - side_panel_width : left, - top + hud_band_height, right, bottom); - }; - - placeholder_command_list_->ClearUnorderedAccessViewFloat( - uav_gpu_visible, uav_cpu, resource, background_color.data(), 0, nullptr); - clear_rect(candidate_base_color, candidate_left, candidate_top, candidate_right, - candidate_bottom); - if (planned_stage_preview.scene.valid) { - FloatColor color = scene_stage_color; - color[3] = 1.0f; - clear_stage_rect(planned_stage_preview.scene, color); - clear_stage_clear_steps(planned_stage_preview.scene, 0.34f); - clear_stage_draw_samples(planned_stage_preview.scene, color); - } - if (planned_stage_preview.postfx.valid) { - FloatColor color = postfx_stage_color; - color[3] = diagnostic_overlay ? 0.28f : 0.18f; - clear_stage_rect(planned_stage_preview.postfx, color); - clear_stage_clear_steps(planned_stage_preview.postfx, diagnostic_overlay ? 0.30f : 0.20f); - } - if (planned_stage_preview.ui.valid) { - clear_stage_clear_steps(planned_stage_preview.ui, diagnostic_overlay ? 0.32f : 0.22f); - clear_ui_hud(planned_stage_preview.ui, ui_stage_color); - } - clear_rect(title_band_color, 0, 0, width, title_band_height); - clear_rect(heartbeat_color, 0, height - status_band_height, heartbeat_width, height); - clear_rect({0.95f, 0.30f, 0.12f, 1.0f}, 0, 0, std::max(1, width / 64), - std::max(1, height / 18)); - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - barrier.Transition.StateAfter = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState; - placeholder_command_list_->ResourceBarrier(1, &barrier); - context.SetIs8bpc(true); - last_present_used_raster_replay_ = false; - return true; - }; - - if (REXCVAR_GET(ac6_native_present_force_minimal_uav)) { - if (!record_minimal_uav(guest_output_resource)) { - return false; - } - return SubmitPlaceholderCommandList(slot); - } - - last_present_used_raster_replay_ = false; - if (TryRecordRasterPlaceholderFrame(slot, guest_output_resource, submission)) { - context.SetIs8bpc(true); - last_present_used_raster_replay_ = true; - return SubmitPlaceholderCommandList(slot); - } - if (REXCVAR_GET(ac6_native_present_fallback_to_minimal_uav)) { - if (FAILED(placeholder_command_list_->Reset(slot.command_allocator.Get(), nullptr))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to reset command list for minimal fallback"); - return false; - } - if (!record_minimal_uav(guest_output_resource)) { - return false; - } - return SubmitPlaceholderCommandList(slot); - } - if (FAILED(placeholder_command_list_->Reset(slot.command_allocator.Get(), nullptr))) { - REXLOG_ERROR( - "AC6 native graphics bootstrap failed to reset the placeholder command list " - "for the legacy fallback path"); - return false; - } - - ID3D12Device* device = d3d12_provider_->GetDevice(); - D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = {}; - uav_desc.Format = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat; - uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; - uav_desc.Texture2D.MipSlice = 0; - uav_desc.Texture2D.PlaneSlice = 0; - - D3D12_CPU_DESCRIPTOR_HANDLE uav_cpu_visible = - slot.shader_visible_uav_heap->GetCPUDescriptorHandleForHeapStart(); - D3D12_GPU_DESCRIPTOR_HANDLE uav_gpu_visible = - slot.shader_visible_uav_heap->GetGPUDescriptorHandleForHeapStart(); - D3D12_CPU_DESCRIPTOR_HANDLE uav_cpu = - slot.cpu_uav_heap->GetCPUDescriptorHandleForHeapStart(); - device->CreateUnorderedAccessView(guest_output_resource, nullptr, &uav_desc, uav_cpu_visible); - device->CreateUnorderedAccessView(guest_output_resource, nullptr, &uav_desc, uav_cpu); - - ID3D12DescriptorHeap* descriptor_heaps[] = {slot.shader_visible_uav_heap.Get()}; - placeholder_command_list_->SetDescriptorHeaps(1, descriptor_heaps); - - D3D12_RESOURCE_BARRIER barrier = {}; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = guest_output_resource; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - placeholder_command_list_->ResourceBarrier(1, &barrier); - - context.SetIs8bpc(true); - - auto clear_rect = [&](const FloatColor& color, UINT left, UINT top, UINT right, UINT bottom) { - if (left >= right || top >= bottom) { - return; - } - D3D12_RECT rect = {LONG(left), LONG(top), LONG(right), LONG(bottom)}; - placeholder_command_list_->ClearUnorderedAccessViewFloat( - uav_gpu_visible, uav_cpu, guest_output_resource, color.data(), 1, &rect); - }; - - auto clear_stage_rect = [&](const PlannedStagePreviewStage& stage, FloatColor color) { - if (!stage.valid) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - clear_rect(color, left, top, std::min(width, left + stage_width), - std::min(height, top + stage_height)); - }; - auto clear_stage_clear_steps = [&](const PlannedStagePreviewStage& stage, float alpha_scale) { - if (!stage.valid || !stage.clear_step_count) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - const UINT right = std::min(width, left + stage_width); - const UINT bottom = std::min(height, top + stage_height); - for (uint32_t i = 0; i < stage.clear_step_count; ++i) { - const SelectedPassClearStep& step = stage.clear_steps[i]; - FloatColor step_color = DecodeArgbColor(step.color); - step_color[3] = std::clamp(alpha_scale, 0.0f, 1.0f); - if (step.rect_count) { - for (uint32_t rect_index = 0; rect_index < step.rect_count; ++rect_index) { - const ac6::d3d::ClearRect& rect = step.rects[rect_index]; - clear_rect(step_color, rect.left, rect.top, rect.right, rect.bottom); - } - } else { - clear_rect(step_color, left, top, right, bottom); - } - } - }; - - auto clear_ui_hud = [&](const PlannedStagePreviewStage& stage, FloatColor color) { - if (!stage.valid) { - return; - } - const UINT left = stage.viewport_width - ? std::min(width - 1, stage.viewport_x) - : candidate_left; - const UINT top = stage.viewport_height - ? std::min(height - 1, stage.viewport_y) - : candidate_top; - const UINT stage_width = stage.viewport_width - ? std::max( - 1, std::min(stage.viewport_width, width - left)) - : candidate_width; - const UINT stage_height = stage.viewport_height - ? std::max( - 1, std::min(stage.viewport_height, height - top)) - : candidate_height; - const UINT right = std::min(width, left + stage_width); - const UINT bottom = std::min(height, top + stage_height); - const UINT hud_band_height = std::max(1, stage_height / 12); - const UINT side_panel_width = std::max(1, stage_width / 7); - FloatColor strip_color = color; - strip_color[3] = diagnostic_overlay ? 0.78f : 0.58f; - clear_rect(strip_color, left, top, right, std::min(bottom, top + hud_band_height)); - clear_rect(strip_color, left, bottom > hud_band_height ? bottom - hud_band_height : top, right, - bottom); - clear_rect(strip_color, left, top + hud_band_height, - std::min(right, left + side_panel_width), bottom); - clear_rect(strip_color, right > side_panel_width ? right - side_panel_width : left, - top + hud_band_height, right, bottom); - }; - - placeholder_command_list_->ClearUnorderedAccessViewFloat( - uav_gpu_visible, uav_cpu, guest_output_resource, background_color.data(), 0, nullptr); - D3D12_RECT center_block_rect = {LONG(candidate_left), LONG(candidate_top), - LONG(std::min(width, candidate_left + candidate_width)), - LONG(std::min(height, candidate_top + candidate_height))}; - placeholder_command_list_->ClearUnorderedAccessViewFloat( - uav_gpu_visible, uav_cpu, guest_output_resource, candidate_base_color.data(), 1, - ¢er_block_rect); - if (planned_stage_preview.scene.valid) { - FloatColor color = scene_stage_color; - color[3] = 1.0f; - clear_stage_rect(planned_stage_preview.scene, color); - clear_stage_clear_steps(planned_stage_preview.scene, 0.34f); - } - if (planned_stage_preview.postfx.valid) { - FloatColor color = postfx_stage_color; - color[3] = diagnostic_overlay ? 0.28f : 0.18f; - clear_stage_rect(planned_stage_preview.postfx, color); - clear_stage_clear_steps(planned_stage_preview.postfx, diagnostic_overlay ? 0.30f : 0.20f); - } - if (planned_stage_preview.ui.valid) { - clear_stage_clear_steps(planned_stage_preview.ui, diagnostic_overlay ? 0.32f : 0.22f); - clear_ui_hud(planned_stage_preview.ui, ui_stage_color); - } - - UINT sampled_clear_band_height = 0; - if (selected_pass_preview.clear_step_count && candidate_height > 2) { - sampled_clear_band_height = - std::max(1, std::min(candidate_height / 12, height / 32 + 1)); - for (uint32_t i = 0; i < selected_pass_preview.clear_step_count; ++i) { - const UINT band_top = std::min(candidate_bottom, candidate_top + sampled_clear_band_height * i); - const UINT band_bottom = - std::min(candidate_bottom, band_top + sampled_clear_band_height); - clear_rect(DecodeArgbColor(selected_pass_preview.clear_steps[i].color), candidate_left, - band_top, candidate_right, band_bottom); - } - } - - UINT resolve_band_height = 0; - if (replay_plan.valid && replay_plan.selected_pass_has_resolve) { - resolve_band_height = std::max(1, candidate_height / 10); - clear_rect(resolve_color, candidate_left, - std::min(candidate_bottom, candidate_bottom - resolve_band_height), - candidate_right, candidate_bottom); - } - - if (selected_pass_preview.summary.draw_count && candidate_width > 8 && candidate_height > 8) { - const UINT stripe_count = - std::min(12, std::max(1, 1 + (selected_pass_preview.summary.draw_count / 6))); - const UINT stripe_width = - std::max(1, candidate_width / std::max(24, stripe_count * 5)); - const UINT stripe_top = std::min(candidate_bottom, candidate_top + sampled_clear_band_height); - const UINT stripe_bottom = - candidate_bottom > resolve_band_height ? (candidate_bottom - resolve_band_height) - : candidate_bottom; - const UINT stripe_area_height = - stripe_bottom > stripe_top ? (stripe_bottom - stripe_top) : 0; - for (UINT i = 0; i < stripe_count && stripe_area_height; ++i) { - const UINT stripe_center_x = - candidate_left + ((i + 1) * candidate_width) / (stripe_count + 1); - const UINT stripe_left = stripe_center_x > stripe_width / 2 - ? stripe_center_x - stripe_width / 2 - : candidate_left; - const UINT stripe_right = std::min(candidate_right, stripe_left + stripe_width); - const UINT stripe_height = - std::max(1, stripe_area_height * (45 + ((i * 13) % 40)) / 100); - const UINT stripe_start = stripe_bottom > stripe_height ? (stripe_bottom - stripe_height) - : stripe_top; - const FloatColor lane_color = - (i & 1u) ? MixColors(stripe_color, heartbeat_color, 0.22f) - : MixColors(stripe_color, background_color, 0.10f); - clear_rect(lane_color, stripe_left, stripe_start, stripe_right, stripe_bottom); - } - } - - if (candidate_width > 2 && candidate_height > 2) { - const UINT outline_thickness = - std::max(1, std::min(4, std::min(candidate_width, candidate_height) / 96 + 1)); - const UINT bottom_outline_top = - candidate_bottom > outline_thickness ? (candidate_bottom - outline_thickness) - : candidate_top; - clear_rect(candidate_outline_color, candidate_left, candidate_top, candidate_right, - std::min(candidate_bottom, candidate_top + outline_thickness)); - clear_rect(candidate_outline_color, candidate_left, bottom_outline_top, candidate_right, - candidate_bottom); - clear_rect(candidate_outline_color, candidate_left, candidate_top, - std::min(candidate_right, candidate_left + outline_thickness), candidate_bottom); - clear_rect(candidate_outline_color, - candidate_right > outline_thickness ? (candidate_right - outline_thickness) - : candidate_left, - candidate_top, candidate_right, candidate_bottom); - } - - if (replay_plan.valid && replay_plan.pass_count) { - const UINT score_width = std::max( - 1, UINT((uint64_t(width) * std::min(replay_plan.selected_pass_score, 220u)) / - 220u)); - clear_rect(candidate_outline_color, 0, title_band_height, score_width, - std::min(height, title_band_height + status_band_height)); - } - - clear_rect(title_band_color, 0, 0, width, title_band_height); - clear_rect(heartbeat_color, 0, height - status_band_height, heartbeat_width, height); - clear_rect({0.95f, 0.30f, 0.12f, 1.0f}, 0, 0, std::max(1, width / 64), - std::max(1, height / 18)); - - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - barrier.Transition.StateAfter = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState; - placeholder_command_list_->ResourceBarrier(1, &barrier); - - return SubmitPlaceholderCommandList(slot); - } - - rex::ui::d3d12::D3D12Provider* d3d12_provider_ = nullptr; - rex::ui::d3d12::D3D12Presenter* d3d12_presenter_ = nullptr; - std::mutex placeholder_refresh_mutex_; - rex::ui::d3d12::D3D12SubmissionTracker placeholder_submission_tracker_; - std::array placeholder_refresh_slots_; - Microsoft::WRL::ComPtr placeholder_command_list_; - Microsoft::WRL::ComPtr raster_preview_root_signature_; - Microsoft::WRL::ComPtr raster_preview_pipeline_; - Microsoft::WRL::ComPtr raster_preview_view_heap_; - Microsoft::WRL::ComPtr raster_preview_sampler_heap_; - uint32_t placeholder_refresh_slot_index_ = 0; - rex::system::GraphicsSwapSubmission last_swap_submission_; - NativeReplayPlanSummary last_replay_plan_{}; - SelectedPassPreviewData last_selected_pass_preview_{}; - PlannedStagePreviewData last_planned_stage_preview_{}; - ReplayCandidateKey last_selected_candidate_{}; - bool last_selected_candidate_valid_ = false; - uint32_t selected_candidate_streak_ = 0; - uint64_t swap_count_ = 0; - bool logged_first_swap_ = false; - bool logged_first_present_ = false; - bool logged_passthrough_swap_ = false; - bool logged_refresh_failure_ = false; - bool last_present_used_raster_replay_ = false; - bool placeholder_resources_initialized_ = false; -}; -#endif - -} // namespace - -void ConfigureGraphicsBackend(rex::RuntimeConfig& config) { - const bool bootstrap_enabled = REXCVAR_GET(ac6_native_graphics_bootstrap); - if (bootstrap_enabled && REXCVAR_GET(ac6_native_graphics_auto_enable_capture) && - !REXCVAR_GET(ac6_render_capture)) { - REXLOG_INFO( - "AC6 native renderer bootstrap enabled render capture automatically for parallel scene ingestion"); - REXCVAR_SET(ac6_render_capture, true); - } - { - std::lock_guard lock(g_native_graphics_status_mutex); - g_native_graphics_status = {}; - g_native_graphics_status.bootstrap_enabled = bootstrap_enabled; - g_native_graphics_status.parallel_renderer_enabled = bootstrap_enabled; - g_native_graphics_status.placeholder_present_enabled = - IsDiagnosticPlaceholderPresentEnabled(); - const SwapRoutingMode routing_mode = GetSwapRoutingMode(); - const bool effective_native = - REXCVAR_GET(ac6_native_present_enabled) || - REXCVAR_GET(ac6_native_present_disable_pm4) || - routing_mode == SwapRoutingMode::kNativeAuthoritative; - g_native_graphics_status.native_present_enabled = effective_native; - g_native_graphics_status.routing_mode = routing_mode; - g_native_graphics_status.cadence_policy = GetCadencePolicy(); - g_native_graphics_status.configured_output_scale_percent = GetOutputScalePercent(); - } - if (bootstrap_enabled) { - InitializeParallelRenderer(); - } else { - ShutdownParallelRenderer(); - } -#if REX_HAS_D3D12 - if (!bootstrap_enabled) { - return; - } - - config.graphics = std::make_unique(); -#else - (void)config; -#endif -} - -NativeGraphicsStatusSnapshot GetNativeGraphicsStatus() { - SyncParallelRendererStatus(); - std::lock_guard lock(g_native_graphics_status_mutex); - NativeGraphicsStatusSnapshot snapshot = g_native_graphics_status; - snapshot.bootstrap_enabled = REXCVAR_GET(ac6_native_graphics_bootstrap); - snapshot.parallel_renderer_enabled = snapshot.bootstrap_enabled; - snapshot.placeholder_present_enabled = IsDiagnosticPlaceholderPresentEnabled(); - const SwapRoutingMode routing_mode = GetSwapRoutingMode(); - snapshot.native_present_enabled = - REXCVAR_GET(ac6_native_present_enabled) || - REXCVAR_GET(ac6_native_present_disable_pm4) || - routing_mode == SwapRoutingMode::kNativeAuthoritative; - snapshot.routing_mode = routing_mode; - snapshot.cadence_policy = GetCadencePolicy(); - snapshot.configured_output_scale_percent = GetOutputScalePercent(); - return snapshot; -} - -} // namespace ac6::graphics + diff --git a/src/ac6_native_graphics.h b/src/ac6_native_graphics.h index 71ebf9c1..e02abfc9 100644 --- a/src/ac6_native_graphics.h +++ b/src/ac6_native_graphics.h @@ -1,180 +1 @@ -#pragma once - -#include - -#include -#include - -#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 + diff --git a/src/ac6_native_graphics_overlay.cpp b/src/ac6_native_graphics_overlay.cpp index 682e8c35..e02abfc9 100644 --- a/src/ac6_native_graphics_overlay.cpp +++ b/src/ac6_native_graphics_overlay.cpp @@ -1,378 +1 @@ -#include "ac6_native_graphics_overlay.h" - -#include -#include - -#include - -#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(native_status.parallel_renderer_frame_count), - static_cast(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(native_status.total_swap_count), - static_cast(native_status.intercepted_swap_count), - static_cast(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(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(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(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(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 + diff --git a/src/ac6recomp_app.h b/src/ac6recomp_app.h index 70a2f8b9..e02abfc9 100644 --- a/src/ac6recomp_app.h +++ b/src/ac6recomp_app.h @@ -1,88 +1 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#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 Create( - rex::ui::WindowedAppContext& ctx) { - return std::unique_ptr(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(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 native_graphics_status_dialog_; -}; + diff --git a/src/d3d_hooks.h b/src/d3d_hooks.h index df46e922..e02abfc9 100644 --- a/src/d3d_hooks.h +++ b/src/d3d_hooks.h @@ -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 - -#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); +