From 96e10d2b6e2314868dfd812c0d440a5581fa450e Mon Sep 17 00:00:00 2001 From: salh Date: Thu, 30 Apr 2026 10:57:35 +0300 Subject: [PATCH] Removed a whole lot of Dead legacy code. Add a performence that is enabled by Default. --- CMakeLists.txt | 16 - .../win-amd64-relwithdebinfo/ac6recomp.toml | 7 +- src/ac6_native_graphics.cpp | 367 +----- src/ac6_native_graphics.h | 83 -- src/ac6_native_graphics_overlay.cpp | 491 +------- .../ac6_render_frontend.cpp | 450 -------- src/ac6_native_renderer/ac6_render_frontend.h | 124 -- .../backends/backend_factory.cpp | 24 - .../backends/d3d12_backend.cpp | 1006 ----------------- .../backends/d3d12_backend.h | 137 --- .../backends/d3d12_resource_manager.cpp | 258 ----- .../backends/d3d12_resource_manager.h | 84 -- .../backends/d3d12_resource_tracker.cpp | 52 - .../backends/d3d12_resource_tracker.h | 33 - .../backends/d3d12_shader_manager.cpp | 283 ----- .../backends/d3d12_shader_manager.h | 63 -- .../backends/metal_backend.cpp | 59 - .../backends/metal_backend.h | 23 - .../backends/vulkan_backend.cpp | 59 - .../backends/vulkan_backend.h | 23 - src/ac6_native_renderer/execution_plan.cpp | 193 ---- src/ac6_native_renderer/execution_plan.h | 111 -- src/ac6_native_renderer/frame_plan.cpp | 142 --- src/ac6_native_renderer/frame_plan.h | 59 - src/ac6_native_renderer/frame_scheduler.cpp | 15 - src/ac6_native_renderer/frame_scheduler.h | 22 - src/ac6_native_renderer/native_renderer.cpp | 189 ---- src/ac6_native_renderer/native_renderer.h | 85 -- src/ac6_native_renderer/render_device.cpp | 91 -- src/ac6_native_renderer/render_device.h | 63 -- src/ac6_native_renderer/render_graph.cpp | 16 - src/ac6_native_renderer/render_graph.h | 43 - src/ac6_native_renderer/replay_executor.cpp | 187 --- src/ac6_native_renderer/replay_executor.h | 94 -- src/ac6_native_renderer/replay_ir.cpp | 165 --- src/ac6_native_renderer/replay_ir.h | 91 -- src/ac6_native_renderer/types.h | 115 -- src/main.cpp | 84 +- .../src/graphics/d3d12/command_processor.cpp | 2 +- 39 files changed, 90 insertions(+), 5319 deletions(-) delete mode 100644 src/ac6_native_renderer/ac6_render_frontend.cpp delete mode 100644 src/ac6_native_renderer/ac6_render_frontend.h delete mode 100644 src/ac6_native_renderer/backends/backend_factory.cpp delete mode 100644 src/ac6_native_renderer/backends/d3d12_backend.cpp delete mode 100644 src/ac6_native_renderer/backends/d3d12_backend.h delete mode 100644 src/ac6_native_renderer/backends/d3d12_resource_manager.cpp delete mode 100644 src/ac6_native_renderer/backends/d3d12_resource_manager.h delete mode 100644 src/ac6_native_renderer/backends/d3d12_resource_tracker.cpp delete mode 100644 src/ac6_native_renderer/backends/d3d12_resource_tracker.h delete mode 100644 src/ac6_native_renderer/backends/d3d12_shader_manager.cpp delete mode 100644 src/ac6_native_renderer/backends/d3d12_shader_manager.h delete mode 100644 src/ac6_native_renderer/backends/metal_backend.cpp delete mode 100644 src/ac6_native_renderer/backends/metal_backend.h delete mode 100644 src/ac6_native_renderer/backends/vulkan_backend.cpp delete mode 100644 src/ac6_native_renderer/backends/vulkan_backend.h delete mode 100644 src/ac6_native_renderer/execution_plan.cpp delete mode 100644 src/ac6_native_renderer/execution_plan.h delete mode 100644 src/ac6_native_renderer/frame_plan.cpp delete mode 100644 src/ac6_native_renderer/frame_plan.h delete mode 100644 src/ac6_native_renderer/frame_scheduler.cpp delete mode 100644 src/ac6_native_renderer/frame_scheduler.h delete mode 100644 src/ac6_native_renderer/native_renderer.cpp delete mode 100644 src/ac6_native_renderer/native_renderer.h delete mode 100644 src/ac6_native_renderer/render_device.cpp delete mode 100644 src/ac6_native_renderer/render_device.h delete mode 100644 src/ac6_native_renderer/render_graph.cpp delete mode 100644 src/ac6_native_renderer/render_graph.h delete mode 100644 src/ac6_native_renderer/replay_executor.cpp delete mode 100644 src/ac6_native_renderer/replay_executor.h delete mode 100644 src/ac6_native_renderer/replay_ir.cpp delete mode 100644 src/ac6_native_renderer/replay_ir.h delete mode 100644 src/ac6_native_renderer/types.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 114b76ed..b402f94d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,22 +38,6 @@ set(AC6RECOMP_SOURCES src/ac6_backend_fixes/ac6_backend_capture_bridge.cpp src/ac6_backend_fixes/ac6_backend_hooks.cpp src/ac6_backend_fixes/ac6_backend_pass_classifier.cpp - src/ac6_native_renderer/ac6_render_frontend.cpp - src/ac6_native_renderer/execution_plan.cpp - src/ac6_native_renderer/frame_plan.cpp - src/ac6_native_renderer/replay_ir.cpp - src/ac6_native_renderer/replay_executor.cpp - src/ac6_native_renderer/backends/backend_factory.cpp - src/ac6_native_renderer/backends/d3d12_backend.cpp - src/ac6_native_renderer/backends/d3d12_resource_manager.cpp - src/ac6_native_renderer/backends/d3d12_resource_tracker.cpp - src/ac6_native_renderer/backends/d3d12_shader_manager.cpp - src/ac6_native_renderer/backends/metal_backend.cpp - src/ac6_native_renderer/backends/vulkan_backend.cpp - src/ac6_native_renderer/frame_scheduler.cpp - src/ac6_native_renderer/native_renderer.cpp - src/ac6_native_renderer/render_device.cpp - src/ac6_native_renderer/render_graph.cpp ) if(WIN32) diff --git a/out/build/win-amd64-relwithdebinfo/ac6recomp.toml b/out/build/win-amd64-relwithdebinfo/ac6recomp.toml index 9c1aab4b..6db32e01 100644 --- a/out/build/win-amd64-relwithdebinfo/ac6recomp.toml +++ b/out/build/win-amd64-relwithdebinfo/ac6recomp.toml @@ -14,9 +14,4 @@ resolution = "1080p" direct_host_resolve = false window_width = 1920 window_height = 1080 -vfetch_index_rounding_bias = true -d3d12_expand_point_sprites_in_vs = true -d3d12_debug = true -dump_shaders = "shaders" -d3d12_dxbc_disasm = true -d3d12_dxbc_disasm_dxilconv = true \ No newline at end of file +vfetch_index_rounding_bias = true \ No newline at end of file diff --git a/src/ac6_native_graphics.cpp b/src/ac6_native_graphics.cpp index 80b8bfb6..a995f7cb 100644 --- a/src/ac6_native_graphics.cpp +++ b/src/ac6_native_graphics.cpp @@ -12,45 +12,24 @@ #include #include #include -#include -#include "ac6_native_renderer/backends/d3d12_backend.h" -#include "ac6_native_renderer/native_renderer.h" #include "d3d_hooks.h" #include "render_hooks.h" REXCVAR_DEFINE_BOOL(ac6_native_graphics_enabled, true, "AC6/NativeGraphics", "Enable AC6 graphics capture analysis, overlay reporting, and backend fixes"); -REXCVAR_DEFINE_BOOL(ac6_native_graphics_require_capture, false, "AC6/NativeGraphics", - "Keep render capture enabled while AC6 graphics analysis is active"); -REXCVAR_DEFINE_BOOL(ac6_force_safe_render_capture, true, "AC6/NativeGraphics", - "Force AC6 hybrid backend fixes mode to keep per-draw render capture disabled until the capture path is stabilized"); REXCVAR_DEFINE_STRING(ac6_graphics_mode, "hybrid_backend_fixes", "AC6/NativeGraphics", - "AC6 graphics runtime mode: disabled, analysis_only, hybrid_backend_fixes, legacy_replay_experimental") - .allowed({"disabled", "analysis_only", "hybrid_backend_fixes", "legacy_replay_experimental"}); + "AC6 graphics runtime mode: disabled, analysis_only, hybrid_backend_fixes") + .allowed({"disabled", "analysis_only", "hybrid_backend_fixes"}); REXCVAR_DEFINE_BOOL(ac6_force_safe_draw_resolution_scale, true, "AC6/NativeGraphics", "Force AC6 hybrid backend fixes mode to use 1x draw resolution scaling until the scaled path is fixed"); REXCVAR_DEFINE_BOOL(ac6_force_safe_direct_host_resolve, true, "AC6/NativeGraphics", "Force AC6 hybrid backend fixes mode to keep direct_host_resolve disabled until the AC6 crash is fixed"); -REXCVAR_DEFINE_BOOL(ac6_experimental_replay_present, false, "AC6/NativeGraphics", - "Allow the legacy AC6 replay renderer to override the RexGlue swap source"); -REXCVAR_DEFINE_STRING(ac6_native_graphics_backend, "auto", "AC6/NativeGraphics", - "Legacy experimental replay backend preference"); -REXCVAR_DEFINE_STRING(ac6_native_graphics_feature_level, "scene_submission", "AC6/NativeGraphics", - "Legacy experimental replay feature level (shipping is a legacy scaffold label)") - .allowed({"bootstrap", "scene_submission", "parity_validation", "shipping"}); -REXCVAR_DEFINE_INT32(ac6_native_graphics_frames_in_flight, 2, "AC6/NativeGraphics", - "Legacy experimental replay max frames in flight") - .range(1, 4); namespace ac6::graphics { namespace { -ac6::renderer::NativeRenderer g_native_renderer; -ac6::renderer::Ac6RenderFrontend g_capture_frontend; -ac6::renderer::FramePlanner g_capture_frame_planner; NativeGraphicsRuntimeStatus g_runtime_status{}; -ac6::renderer::D3D12Backend* g_d3d12_backend = nullptr; std::atomic g_captured_memory{nullptr}; std::atomic_flag g_frame_boundary_active = ATOMIC_FLAG_INIT; std::atomic g_frame_boundary_reentry_count{0}; @@ -62,44 +41,9 @@ GraphicsRuntimeMode ParseGraphicsMode(std::string_view value) { if (value == "analysis_only") { return GraphicsRuntimeMode::kAnalysisOnly; } - if (value == "legacy_replay_experimental") { - return GraphicsRuntimeMode::kLegacyReplayExperimental; - } return GraphicsRuntimeMode::kHybridBackendFixes; } -ac6::renderer::FeatureLevel ParseFeatureLevel(std::string_view value) { - using ac6::renderer::FeatureLevel; - if (value == "scene_submission") { - return FeatureLevel::kSceneSubmission; - } - if (value == "parity_validation") { - return FeatureLevel::kParityValidation; - } - if (value == "shipping") { - return FeatureLevel::kShipping; - } - return FeatureLevel::kBootstrap; -} - -bool IsReplayMode(const GraphicsRuntimeMode mode) { - return mode == GraphicsRuntimeMode::kLegacyReplayExperimental; -} - -void ResetReplayStatus() { - g_runtime_status.initialized = false; - g_runtime_status.replay_frames_built = 0; - g_runtime_status.active_backend = ac6::renderer::BackendType::kUnknown; - g_runtime_status.renderer_stats = {}; - g_runtime_status.replay_summary = {}; - g_runtime_status.execution_summary = {}; - g_runtime_status.executor_summary = {}; - g_runtime_status.backend_executor_status = {}; - g_runtime_status.latest_renderer_frame_index = 0; - g_runtime_status.last_meaningful_renderer_frame_index = 0; - g_runtime_status.showing_latched_snapshot = false; -} - void SyncRuntimeFlags() { g_runtime_status.enabled = REXCVAR_GET(ac6_native_graphics_enabled); g_runtime_status.mode = ParseGraphicsMode(REXCVAR_GET(ac6_graphics_mode)); @@ -125,239 +69,6 @@ void SyncRuntimeFlags() { g_runtime_status.authoritative_renderer_active = g_runtime_status.enabled && g_runtime_status.mode != GraphicsRuntimeMode::kDisabled; - g_runtime_status.experimental_replay_present = - g_runtime_status.enabled && - IsReplayMode(g_runtime_status.mode) && - REXCVAR_GET(ac6_experimental_replay_present); -} - -void RefreshRuntimeStatusFromRenderer() { - g_runtime_status.active_backend = g_native_renderer.GetStats().active_backend; - g_runtime_status.feature_level = g_native_renderer.feature_level(); - g_runtime_status.renderer_stats = g_native_renderer.GetStats(); - g_runtime_status.frontend_summary = g_native_renderer.frontend_summary(); - g_runtime_status.replay_summary = g_native_renderer.replay_summary(); - g_runtime_status.execution_summary = g_native_renderer.execution_summary(); - g_runtime_status.executor_summary = g_native_renderer.executor_summary(); - g_runtime_status.backend_executor_status = g_native_renderer.backend_executor_status(); - g_runtime_status.frame_plan = g_native_renderer.frame_plan(); -} - -bool IsMeaningfulRendererSnapshot( - const ac6::d3d::FrameCaptureSummary& capture_summary, - const ac6::renderer::FrontendFrameSummary& frontend_summary, - const ac6::renderer::ReplayFrameSummary& replay_summary, - const ac6::renderer::ExecutionFrameSummary& execution_summary, - const ac6::renderer::ReplayExecutorFrameSummary& executor_summary, - const ac6::renderer::BackendExecutorStatus& backend_status) { - return capture_summary.draw_count != 0 || capture_summary.clear_count != 0 || - capture_summary.resolve_count != 0 || - frontend_summary.total_command_count != 0 || - replay_summary.command_count != 0 || - execution_summary.command_count != 0 || - executor_summary.command_count != 0 || - backend_status.draw_attempt_count != 0 || - backend_status.clear_command_count != 0 || - backend_status.resolve_command_count != 0; -} - -uint32_t ScoreObservedPassForDiagnostics(const ac6::renderer::ObservedPassDesc& pass) { - const uint64_t viewport_area = - uint64_t(pass.viewport_width) * uint64_t(pass.viewport_height); - uint32_t score = 0; - score += pass.selected_for_present ? 160u : 0u; - score += pass.matches_frame_end_viewport ? 120u : 0u; - score += pass.resolve_count * 80u; - score += pass.draw_count * 4u; - score += pass.clear_count * 6u; - score += pass.max_texture_count * 3u; - score += pass.max_stream_count * 2u; - score += pass.max_sampler_count * 2u; - score += static_cast(std::min(viewport_area / 32768u, 120u)); - switch (pass.kind) { - case ac6::renderer::ObservedPassKind::kScene: - score += 40u; - break; - case ac6::renderer::ObservedPassKind::kPostProcess: - score += 50u; - break; - case ac6::renderer::ObservedPassKind::kUiComposite: - score += 20u; - break; - case ac6::renderer::ObservedPassKind::kUnknown: - default: - break; - } - return score; -} - -void RefreshPassDiagnostics(const std::vector& passes) { - g_runtime_status.pass_diagnostics_count = 0; - g_runtime_status.pass_diagnostics = {}; - if (passes.empty()) { - return; - } - - struct RankedPass { - uint32_t pass_index = 0; - uint32_t score = 0; - }; - - std::vector ranked; - ranked.reserve(passes.size()); - for (uint32_t i = 0; i < passes.size(); ++i) { - const auto& pass = passes[i]; - ranked.push_back({i, ScoreObservedPassForDiagnostics(pass)}); - } - - std::sort(ranked.begin(), ranked.end(), [&](const RankedPass& left, const RankedPass& right) { - if (left.score != right.score) { - return left.score > right.score; - } - return left.pass_index < right.pass_index; - }); - - const uint32_t count = std::min(static_cast(ranked.size()), - static_cast(g_runtime_status.pass_diagnostics.size())); - g_runtime_status.pass_diagnostics_count = count; - for (uint32_t i = 0; i < count; ++i) { - const auto& ranked_pass = ranked[i]; - const auto& pass = passes[ranked_pass.pass_index]; - g_runtime_status.pass_diagnostics[i] = { - .valid = true, - .pass_index = ranked_pass.pass_index, - .kind = pass.kind, - .score = ranked_pass.score, - .render_target_0 = pass.render_target_0, - .depth_stencil = pass.depth_stencil, - .viewport_x = pass.viewport_x, - .viewport_y = pass.viewport_y, - .viewport_width = pass.viewport_width, - .viewport_height = pass.viewport_height, - .draw_count = pass.draw_count, - .clear_count = pass.clear_count, - .resolve_count = pass.resolve_count, - .max_texture_count = pass.max_texture_count, - .max_stream_count = pass.max_stream_count, - .max_sampler_count = pass.max_sampler_count, - .max_fetch_constant_count = pass.max_fetch_constant_count, - .max_shader_gpr_alloc = pass.max_shader_gpr_alloc, - .pass_signature = pass.pass_signature, - .first_texture_fetch_layout_signature = - pass.first_texture_fetch_layout_signature, - .last_texture_fetch_layout_signature = - pass.last_texture_fetch_layout_signature, - .first_resource_binding_signature = - pass.first_resource_binding_signature, - .last_resource_binding_signature = - pass.last_resource_binding_signature, - .first_textures = {.count = pass.first_textures.count, - .slots = pass.first_textures.slots, - .values = pass.first_textures.values}, - .last_textures = {.count = pass.last_textures.count, - .slots = pass.last_textures.slots, - .values = pass.last_textures.values}, - .first_fetch_constants = {.count = pass.first_fetch_constants.count, - .slots = pass.first_fetch_constants.slots, - .values = pass.first_fetch_constants.values}, - .last_fetch_constants = {.count = pass.last_fetch_constants.count, - .slots = pass.last_fetch_constants.slots, - .values = pass.last_fetch_constants.values}, - .selected_for_present = pass.selected_for_present, - .matches_frame_end_viewport = pass.matches_frame_end_viewport, - }; - } -} - -void RefreshResolveDiagnostics(const ac6::d3d::FrameCaptureSnapshot& frame_capture) { - g_runtime_status.resolve_diagnostics_count = 0; - g_runtime_status.resolve_diagnostics = {}; - if (frame_capture.resolves.empty()) { - return; - } - - const uint32_t total_resolves = static_cast(frame_capture.resolves.size()); - const uint32_t count = - std::min(total_resolves, - static_cast(g_runtime_status.resolve_diagnostics.size())); - const uint32_t start_index = total_resolves - count; - g_runtime_status.resolve_diagnostics_count = count; - for (uint32_t i = 0; i < count; ++i) { - const auto& resolve = frame_capture.resolves[start_index + i]; - g_runtime_status.resolve_diagnostics[i] = { - .valid = true, - .sequence = resolve.sequence, - .render_target_0 = resolve.shadow_state.render_targets[0], - .depth_stencil = resolve.shadow_state.depth_stencil, - .viewport_width = resolve.shadow_state.viewport.width, - .viewport_height = resolve.shadow_state.viewport.height, - .args = resolve.args, - .depth_or_scale = resolve.depth_or_scale, - }; - } -} - -void ShutdownReplayRenderer() { - g_d3d12_backend = nullptr; - if (!g_runtime_status.initialized) { - return; - } - g_native_renderer.Shutdown(); - ResetReplayStatus(); -} - -bool EnsureExperimentalReplayInitialized(rex::memory::Memory* memory) { - if (!g_runtime_status.enabled || !IsReplayMode(g_runtime_status.mode)) { - return false; - } - if (g_runtime_status.initialized) { - return true; - } - - ++g_runtime_status.init_attempts; - auto* ts = rex::runtime::ThreadState::Get(); - if (!ts || !ts->context() || !ts->context()->kernel_state) { - return false; - } - - auto* graphics_system = ts->context()->kernel_state->graphics_system(); - if (!graphics_system || !graphics_system->provider()) { - return false; - } - - auto* d3d_provider = - dynamic_cast(graphics_system->provider()); - if (!d3d_provider) { - g_runtime_status.had_init_failure = true; - return false; - } - - ID3D12Device* device = d3d_provider->GetDevice(); - ID3D12CommandQueue* queue = d3d_provider->GetDirectQueue(); - if (!device || !queue) { - return false; - } - - ac6::renderer::NativeRendererConfig config; - config.preferred_backend = ac6::renderer::BackendType::kD3D12; - config.feature_level = - ParseFeatureLevel(REXCVAR_GET(ac6_native_graphics_feature_level)); - config.max_frames_in_flight = static_cast( - std::clamp(REXCVAR_GET(ac6_native_graphics_frames_in_flight), 1, 4)); - config.enable_debug_markers = true; - config.enable_validation = false; - - if (!g_native_renderer.InitializeShared(config, memory, device, queue)) { - g_runtime_status.had_init_failure = true; - return false; - } - - g_d3d12_backend = g_native_renderer.GetD3D12Backend(); - ++g_runtime_status.init_successes; - g_runtime_status.initialized = true; - RefreshRuntimeStatusFromRenderer(); - REXLOG_INFO("AC6 graphics: legacy experimental replay renderer initialized"); - return true; } } // namespace @@ -370,8 +81,6 @@ std::string_view ToString(const GraphicsRuntimeMode mode) { return "analysis_only"; case GraphicsRuntimeMode::kHybridBackendFixes: return "hybrid_backend_fixes"; - case GraphicsRuntimeMode::kLegacyReplayExperimental: - return "legacy_replay_experimental"; default: return "unknown"; } @@ -398,20 +107,10 @@ void OnFrameBoundary(rex::memory::Memory* memory) { g_captured_memory.store(memory, std::memory_order_release); if (!g_runtime_status.enabled || g_runtime_status.mode == GraphicsRuntimeMode::kDisabled) { - ShutdownReplayRenderer(); ac6::backend::ShutdownDiagnostics(); return; } - if (REXCVAR_GET(ac6_native_graphics_require_capture)) { - REXCVAR_SET(ac6_render_capture, true); - g_runtime_status.capture_enabled = true; - } - - if (!IsReplayMode(g_runtime_status.mode)) { - ShutdownReplayRenderer(); - } - ac6::d3d::OnFrameBoundary(); ac6::d3d::FrameCaptureSummary capture_summary; @@ -422,19 +121,6 @@ void OnFrameBoundary(rex::memory::Memory* memory) { ++g_runtime_status.analysis_frames_observed; g_runtime_status.capture_summary = capture_summary; g_runtime_status.latest_capture_frame_index = capture_summary.frame_index; - g_runtime_status.frontend_summary = g_capture_frontend.BuildFromCapture(frame_capture); - if (g_runtime_status.frontend_summary.capture_valid) { - RefreshPassDiagnostics(g_capture_frontend.passes()); - RefreshResolveDiagnostics(frame_capture); - g_runtime_status.frame_plan = g_capture_frame_planner.Build( - g_runtime_status.frontend_summary, g_capture_frontend.passes()); - } else { - g_runtime_status.pass_diagnostics_count = 0; - g_runtime_status.pass_diagnostics = {}; - g_runtime_status.resolve_diagnostics_count = 0; - g_runtime_status.resolve_diagnostics = {}; - g_runtime_status.frame_plan = {}; - } if (capture_summary.draw_count || capture_summary.clear_count || capture_summary.resolve_count) { g_runtime_status.last_meaningful_capture_frame_index = capture_summary.frame_index; @@ -474,49 +160,10 @@ void OnFrameBoundary(rex::memory::Memory* memory) { guest_vblank_interval_ticks, last_guest_vblank_tick, ac6::GetFrameStats(), audio_telemetry_ptr, audio_timing_ptr); g_runtime_status.backend_diagnostics = ac6::backend::GetDiagnosticsSnapshot(); - - if (!IsReplayMode(g_runtime_status.mode)) { - return; - } - if (!EnsureExperimentalReplayInitialized(memory)) { - return; - } - - g_native_renderer.BeginFrame(); - g_native_renderer.BuildCapturedFrame(frame_capture); - g_runtime_status.replay_frames_built = g_native_renderer.GetStats().frame_count; - g_runtime_status.latest_renderer_frame_index = - g_native_renderer.GetStats().frame_count; - - const ac6::renderer::FrontendFrameSummary frontend_summary = - g_native_renderer.frontend_summary(); - const ac6::renderer::ReplayFrameSummary replay_summary = - g_native_renderer.replay_summary(); - const ac6::renderer::ExecutionFrameSummary execution_summary = - g_native_renderer.execution_summary(); - const ac6::renderer::ReplayExecutorFrameSummary executor_summary = - g_native_renderer.executor_summary(); - const ac6::renderer::BackendExecutorStatus backend_status = - g_native_renderer.backend_executor_status(); - - if (IsMeaningfulRendererSnapshot(capture_summary, frontend_summary, replay_summary, - execution_summary, executor_summary, backend_status) || - g_runtime_status.last_meaningful_renderer_frame_index == 0) { - RefreshRuntimeStatusFromRenderer(); - g_runtime_status.showing_latched_snapshot = false; - g_runtime_status.last_meaningful_renderer_frame_index = - g_runtime_status.latest_renderer_frame_index; - } else { - g_runtime_status.active_backend = g_native_renderer.GetStats().active_backend; - g_runtime_status.feature_level = g_native_renderer.feature_level(); - g_runtime_status.renderer_stats = g_native_renderer.GetStats(); - g_runtime_status.showing_latched_snapshot = true; - } } void Shutdown() { g_captured_memory.store(nullptr, std::memory_order_release); - ShutdownReplayRenderer(); } NativeGraphicsRuntimeStatus GetRuntimeStatus() { @@ -525,16 +172,6 @@ NativeGraphicsRuntimeStatus GetRuntimeStatus() { return g_runtime_status; } -ID3D12Resource* GetNativeOutputTexture() { - SyncRuntimeFlags(); - if (!g_runtime_status.enabled || !g_runtime_status.initialized || - !IsReplayMode(g_runtime_status.mode) || - !REXCVAR_GET(ac6_experimental_replay_present)) { - return nullptr; - } - return g_d3d12_backend ? g_d3d12_backend->GetOutputTexture() : nullptr; -} - rex::memory::Memory* GetCapturedMemory() { return g_captured_memory.load(std::memory_order_acquire); } diff --git a/src/ac6_native_graphics.h b/src/ac6_native_graphics.h index d673f1f9..51d717ae 100644 --- a/src/ac6_native_graphics.h +++ b/src/ac6_native_graphics.h @@ -1,126 +1,43 @@ #pragma once -#include #include #include #include #include "ac6_backend_fixes/ac6_backend_hooks.h" -#include "ac6_native_renderer/ac6_render_frontend.h" -#include "ac6_native_renderer/execution_plan.h" -#include "ac6_native_renderer/frame_plan.h" -#include "ac6_native_renderer/replay_executor.h" -#include "ac6_native_renderer/replay_ir.h" -#include "ac6_native_renderer/types.h" #include "d3d_state.h" -struct ID3D12Resource; - namespace ac6::graphics { enum class GraphicsRuntimeMode : uint8_t { kDisabled, kAnalysisOnly, kHybridBackendFixes, - kLegacyReplayExperimental, }; std::string_view ToString(GraphicsRuntimeMode mode); struct NativeGraphicsRuntimeStatus { - struct PassDiagnosticsEntry { - struct BoundResourceSample { - uint32_t count = 0; - std::array slots{}; - std::array values{}; - }; - - bool valid = false; - uint32_t pass_index = 0; - ac6::renderer::ObservedPassKind kind = ac6::renderer::ObservedPassKind::kUnknown; - uint32_t score = 0; - uint32_t render_target_0 = 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; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; - uint32_t max_texture_count = 0; - uint32_t max_stream_count = 0; - uint32_t max_sampler_count = 0; - uint32_t max_fetch_constant_count = 0; - uint32_t max_shader_gpr_alloc = 0; - uint64_t pass_signature = 0; - uint64_t first_texture_fetch_layout_signature = 0; - uint64_t last_texture_fetch_layout_signature = 0; - uint64_t first_resource_binding_signature = 0; - uint64_t last_resource_binding_signature = 0; - BoundResourceSample first_textures{}; - BoundResourceSample last_textures{}; - BoundResourceSample first_fetch_constants{}; - BoundResourceSample last_fetch_constants{}; - bool selected_for_present = false; - bool matches_frame_end_viewport = false; - }; - - struct ResolveDiagnosticsEntry { - bool valid = false; - uint32_t sequence = 0; - uint32_t render_target_0 = 0; - uint32_t depth_stencil = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; - std::array args{}; - float depth_or_scale = 0.0f; - }; - bool enabled = false; GraphicsRuntimeMode mode = GraphicsRuntimeMode::kHybridBackendFixes; bool capture_enabled = false; bool authoritative_renderer_active = false; - bool experimental_replay_present = false; uint32_t draw_resolution_scale_x = 1; uint32_t draw_resolution_scale_y = 1; bool direct_host_resolve = true; bool draw_resolution_scaled_texture_offsets = true; - bool initialized = false; - bool had_init_failure = false; - bool showing_latched_snapshot = false; - uint64_t init_attempts = 0; - uint64_t init_successes = 0; uint64_t analysis_frames_observed = 0; - uint64_t replay_frames_built = 0; uint64_t latest_capture_frame_index = 0; - uint64_t latest_renderer_frame_index = 0; uint64_t last_meaningful_capture_frame_index = 0; - uint64_t last_meaningful_renderer_frame_index = 0; - - ac6::renderer::BackendType active_backend = ac6::renderer::BackendType::kUnknown; - ac6::renderer::FeatureLevel feature_level = ac6::renderer::FeatureLevel::kBootstrap; - ac6::renderer::NativeRendererStats renderer_stats{}; - ac6::renderer::FrontendFrameSummary frontend_summary{}; - ac6::renderer::ReplayFrameSummary replay_summary{}; - ac6::renderer::ExecutionFrameSummary execution_summary{}; - ac6::renderer::ReplayExecutorFrameSummary executor_summary{}; - ac6::renderer::BackendExecutorStatus backend_executor_status{}; ac6::d3d::FrameCaptureSummary capture_summary{}; ac6::backend::BackendDiagnosticsSnapshot backend_diagnostics{}; - ac6::renderer::NativeFramePlan frame_plan{}; - uint32_t pass_diagnostics_count = 0; - std::array pass_diagnostics{}; - uint32_t resolve_diagnostics_count = 0; - std::array resolve_diagnostics{}; }; void OnFrameBoundary(rex::memory::Memory* memory); void Shutdown(); NativeGraphicsRuntimeStatus GetRuntimeStatus(); -ID3D12Resource* GetNativeOutputTexture(); rex::memory::Memory* GetCapturedMemory(); } // namespace ac6::graphics diff --git a/src/ac6_native_graphics_overlay.cpp b/src/ac6_native_graphics_overlay.cpp index e0c41abb..63660829 100644 --- a/src/ac6_native_graphics_overlay.cpp +++ b/src/ac6_native_graphics_overlay.cpp @@ -1,359 +1,15 @@ #include "ac6_native_graphics_overlay.h" -#include -#include -#include - #include -#include -#include -#include +#include #include "ac6_native_graphics.h" +REXCVAR_DECLARE(bool, ac6_performance_mode); + +extern void ApplyAc6PerformanceModeOverridesPublic(); + namespace ac6::graphics { -namespace { - -struct DecodedTextureFetch { - bool valid = false; - uint32_t header_offset = 0; - uint32_t width = 0; - uint32_t height = 0; - uint32_t depth = 0; - uint32_t base_address = 0; - const char* format_name = nullptr; - rex::graphics::xenos::DataDimension dimension = - rex::graphics::xenos::DataDimension::k2DOrStacked; - bool tiled = false; -}; - -template -struct DecodedSampleSet { - uint32_t count = 0; - std::array entries{}; -}; - -bool IsReadableGuestRange(rex::memory::Memory* memory, uint32_t guest_ptr, uint32_t size) { - if (memory == nullptr || guest_ptr == 0 || size == 0) { - return false; - } - const uint32_t guest_end = guest_ptr + size - 1; - if (guest_end < guest_ptr) { - return false; - } - auto* start_heap = memory->LookupHeap(guest_ptr); - if (start_heap == nullptr) { - return false; - } - auto* end_heap = memory->LookupHeap(guest_end); - if (end_heap != start_heap) { - return false; - } - const auto access = start_heap->QueryRangeAccess(guest_ptr, guest_end); - return access == rex::memory::PageAccess::kReadOnly || - access == rex::memory::PageAccess::kReadWrite || - access == rex::memory::PageAccess::kExecuteReadOnly || - access == rex::memory::PageAccess::kExecuteReadWrite; -} - -bool LoadTextureFetchAt(rex::memory::Memory* memory, uint32_t guest_ptr, - rex::graphics::xenos::xe_gpu_texture_fetch_t& fetch) { - if (!IsReadableGuestRange(memory, guest_ptr, sizeof(fetch))) { - return false; - } - const uint8_t* fetch_base = memory->TranslateVirtual(guest_ptr); - if (fetch_base == nullptr) { - return false; - } - fetch.dword_0 = rex::memory::load_and_swap(fetch_base + 0); - fetch.dword_1 = rex::memory::load_and_swap(fetch_base + 4); - fetch.dword_2 = rex::memory::load_and_swap(fetch_base + 8); - fetch.dword_3 = rex::memory::load_and_swap(fetch_base + 12); - fetch.dword_4 = rex::memory::load_and_swap(fetch_base + 16); - fetch.dword_5 = rex::memory::load_and_swap(fetch_base + 20); - return true; -} - -bool IsPlausibleTextureFetchHeader(const rex::graphics::xenos::xe_gpu_texture_fetch_t& fetch) { - using rex::graphics::xenos::DataDimension; - - if (static_cast(fetch.format) >= 64) { - return false; - } - - switch (fetch.dimension) { - case DataDimension::k1D: - case DataDimension::k3D: - return !fetch.stacked; - case DataDimension::k2DOrStacked: - return true; - case DataDimension::kCube: - return !fetch.stacked && fetch.size_2d.stack_depth == 5; - default: - return false; - } -} - -bool IsPlausibleTextureInfo(const rex::graphics::TextureInfo& info) { - const uint32_t width = info.width + 1; - const uint32_t height = info.height + 1; - const uint32_t depth = info.depth + 1; - return width >= 1 && width <= 8192 && height >= 1 && height <= 8192 && depth >= 1 && - depth <= 2048 && info.memory.base_address != 0 && info.format_info() != nullptr; -} - -bool DecodeTextureFetch(uint32_t guest_ptr, DecodedTextureFetch& out) { - rex::memory::Memory* memory = GetCapturedMemory(); - if (!IsReadableGuestRange(memory, guest_ptr, sizeof(rex::graphics::xenos::xe_gpu_texture_fetch_t))) { - return false; - } - - constexpr uint32_t kProbeLimit = 0x80; - for (uint32_t offset = 0; offset + sizeof(rex::graphics::xenos::xe_gpu_texture_fetch_t) <= - kProbeLimit; - offset += 4) { - if (!IsReadableGuestRange(memory, guest_ptr + offset, - sizeof(rex::graphics::xenos::xe_gpu_texture_fetch_t))) { - continue; - } - rex::graphics::xenos::xe_gpu_texture_fetch_t fetch{}; - if (!LoadTextureFetchAt(memory, guest_ptr + offset, fetch)) { - continue; - } - if (fetch.type != rex::graphics::xenos::FetchConstantType::kTexture) { - continue; - } - if (!IsPlausibleTextureFetchHeader(fetch)) { - continue; - } - - rex::graphics::TextureInfo texture_info; - if (!rex::graphics::TextureInfo::Prepare(fetch, &texture_info) || - !IsPlausibleTextureInfo(texture_info)) { - continue; - } - - out.valid = true; - out.header_offset = offset; - out.dimension = texture_info.dimension; - out.tiled = texture_info.is_tiled; - out.width = texture_info.width + 1; - out.height = texture_info.height + 1; - out.depth = texture_info.depth + 1; - out.base_address = texture_info.memory.base_address; - out.format_name = texture_info.format_info()->name; - return true; - } - return false; -} - -std::string FormatDecodedTextureObject(uint32_t guest_ptr) { - if (guest_ptr == 0) { - return "none"; - } - DecodedTextureFetch decoded; - if (!DecodeTextureFetch(guest_ptr, decoded) || !decoded.valid) { - char buffer[32]; - std::snprintf(buffer, sizeof(buffer), "%08X", guest_ptr); - return buffer; - } - - char buffer[128]; - const char* tiled_tag = decoded.tiled ? "t" : "l"; - if (decoded.dimension == rex::graphics::xenos::DataDimension::k3D || decoded.depth > 1) { - std::snprintf(buffer, sizeof(buffer), "%08X+%02X[%ux%ux%u %s @%08X %s]", guest_ptr, - decoded.header_offset, decoded.width, decoded.height, decoded.depth, tiled_tag, - decoded.base_address, decoded.format_name ? decoded.format_name : "?"); - } else { - std::snprintf(buffer, sizeof(buffer), "%08X+%02X[%ux%u %s @%08X %s]", guest_ptr, - decoded.header_offset, decoded.width, decoded.height, tiled_tag, - decoded.base_address, decoded.format_name ? decoded.format_name : "?"); - } - return buffer; -} - -template -DecodedSampleSet<4> DecodeSampleSet(const Sample& sample) { - DecodedSampleSet<4> decoded_set; - const uint32_t shown = - std::min(sample.count, static_cast(sample.values.size())); - for (uint32_t i = 0; i < shown; ++i) { - DecodedTextureFetch decoded; - if (!DecodeTextureFetch(sample.values[i], decoded) || !decoded.valid) { - continue; - } - decoded_set.entries[decoded_set.count++] = decoded; - } - return decoded_set; -} - -template -bool SampleUsesBaseAddress(const DecodedSampleSet& sample_set, uint32_t base_address) { - if (base_address == 0) { - return false; - } - for (uint32_t i = 0; i < sample_set.count; ++i) { - if (sample_set.entries[i].base_address == base_address) { - return true; - } - } - return false; -} - -std::string FormatResolveArgCandidates( - const NativeGraphicsRuntimeStatus::ResolveDiagnosticsEntry& resolve) { - std::string formatted; - for (uint32_t i = 0; i < resolve.args.size(); ++i) { - if (resolve.args[i] == 0) { - continue; - } - DecodedTextureFetch decoded; - if (!DecodeTextureFetch(resolve.args[i], decoded) || !decoded.valid) { - continue; - } - char buffer[96]; - std::snprintf(buffer, sizeof(buffer), "r%u=%08X+%02X[%ux%u @%08X %s]", 4u + i, - resolve.args[i], decoded.header_offset, decoded.width, decoded.height, - decoded.base_address, decoded.format_name ? decoded.format_name : "?"); - if (!formatted.empty()) { - formatted.append(", "); - } - formatted.append(buffer); - } - return formatted.empty() ? "none" : formatted; -} - -template -std::string FormatPassSources(const DecodedSampleSet& first_fetch, - const DecodedSampleSet& last_fetch, - const NativeGraphicsRuntimeStatus& status, uint32_t self_index) { - std::string formatted; - for (uint32_t other_index = 0; other_index < status.pass_diagnostics_count; ++other_index) { - if (other_index == self_index) { - continue; - } - const auto& other_pass = status.pass_diagnostics[other_index]; - if (!other_pass.valid || other_pass.render_target_0 == 0) { - continue; - } - DecodedTextureFetch decoded_rt0; - if (!DecodeTextureFetch(other_pass.render_target_0, decoded_rt0) || !decoded_rt0.valid || - decoded_rt0.base_address == 0) { - continue; - } - if (!SampleUsesBaseAddress(first_fetch, decoded_rt0.base_address) && - !SampleUsesBaseAddress(last_fetch, decoded_rt0.base_address)) { - continue; - } - char buffer[48]; - std::snprintf(buffer, sizeof(buffer), "#%u(rt0 #%u %ux%u @%08X)", other_index, - other_pass.pass_index, decoded_rt0.width, decoded_rt0.height, - decoded_rt0.base_address); - if (!formatted.empty()) { - formatted.append(", "); - } - formatted.append(buffer); - } - return formatted.empty() ? "none" : formatted; -} - -template -std::string FormatResolveSources(const DecodedSampleSet& first_fetch, - const DecodedSampleSet& last_fetch, - const NativeGraphicsRuntimeStatus& status) { - std::string formatted; - for (uint32_t resolve_index = 0; resolve_index < status.resolve_diagnostics_count; - ++resolve_index) { - const auto& resolve = status.resolve_diagnostics[resolve_index]; - if (!resolve.valid) { - continue; - } - for (uint32_t arg_index = 0; arg_index < resolve.args.size(); ++arg_index) { - if (resolve.args[arg_index] == 0) { - continue; - } - DecodedTextureFetch decoded; - if (!DecodeTextureFetch(resolve.args[arg_index], decoded) || !decoded.valid || - decoded.base_address == 0) { - continue; - } - if (!SampleUsesBaseAddress(first_fetch, decoded.base_address) && - !SampleUsesBaseAddress(last_fetch, decoded.base_address)) { - continue; - } - char buffer[72]; - std::snprintf(buffer, sizeof(buffer), "#%u:r%u[%ux%u @%08X]", resolve_index, 4u + arg_index, - decoded.width, decoded.height, decoded.base_address); - if (!formatted.empty()) { - formatted.append(", "); - } - formatted.append(buffer); - } - } - return formatted.empty() ? "none" : formatted; -} - -template -std::string FormatBoundResourceSample(const Sample& sample) { - if (sample.count == 0) { - return "none"; - } - std::string formatted; - const uint32_t shown = std::min(sample.count, static_cast(sample.values.size())); - for (uint32_t i = 0; i < shown; ++i) { - if (!formatted.empty()) { - formatted.append(", "); - } - char buffer[32]; - std::snprintf(buffer, sizeof(buffer), "%u:%08X", sample.slots[i], sample.values[i]); - formatted.append(buffer); - } - if (sample.count > shown) { - formatted.append(", ..."); - } - return formatted; -} - -template -std::string FormatFetchConstantSample(const Sample& sample) { - if (sample.count == 0) { - return "none"; - } - std::string formatted; - const uint32_t shown = std::min(sample.count, static_cast(sample.values.size())); - for (uint32_t i = 0; i < shown; ++i) { - if (!formatted.empty()) { - formatted.append(", "); - } - char buffer[96]; - DecodedTextureFetch decoded; - if (DecodeTextureFetch(sample.values[i], decoded) && decoded.valid) { - const char* tiled_tag = decoded.tiled ? "t" : "l"; - if (decoded.dimension == rex::graphics::xenos::DataDimension::k3D || - decoded.depth > 1) { - std::snprintf(buffer, sizeof(buffer), - "%u:%08X+%02X[%ux%ux%u %s @%08X %s]", sample.slots[i], sample.values[i], - decoded.header_offset, decoded.width, decoded.height, decoded.depth, - tiled_tag, decoded.base_address, - decoded.format_name ? decoded.format_name : "?"); - } else { - std::snprintf(buffer, sizeof(buffer), "%u:%08X+%02X[%ux%u %s @%08X %s]", - sample.slots[i], sample.values[i], decoded.header_offset, decoded.width, - decoded.height, tiled_tag, decoded.base_address, - decoded.format_name ? decoded.format_name : "?"); - } - } else { - std::snprintf(buffer, sizeof(buffer), "%u:%08X", sample.slots[i], sample.values[i]); - } - formatted.append(buffer); - } - if (sample.count > shown) { - formatted.append(", ..."); - } - return formatted; -} - -} // namespace NativeGraphicsStatusDialog::NativeGraphicsStatusDialog(rex::ui::ImGuiDrawer* imgui_drawer) : ImGuiDialog(imgui_drawer) {} @@ -362,7 +18,10 @@ NativeGraphicsStatusDialog::~NativeGraphicsStatusDialog() = default; void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) { (void)io; - if (!visible_) { + + ApplyAc6PerformanceModeOverridesPublic(); + + if (REXCVAR_GET(ac6_performance_mode) || !visible_) { return; } @@ -387,11 +46,8 @@ void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) { ImGui::Text("scaled tex offsets / direct host resolve: %s / %s", status.draw_resolution_scaled_texture_offsets ? "on" : "off", status.direct_host_resolve ? "on" : "off"); - ImGui::Text("experimental replay present override: %s", - status.experimental_replay_present ? "enabled" : "disabled"); - ImGui::Text("analysis frames / replay frames: %llu / %llu", - static_cast(status.analysis_frames_observed), - static_cast(status.replay_frames_built)); + ImGui::Text("analysis frames: %llu", + static_cast(status.analysis_frames_observed)); ImGui::Separator(); ImGui::Text("capture frame: %llu", @@ -409,109 +65,6 @@ void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) { ImGui::Text("frame-end viewport: %ux%u", status.capture_summary.frame_end_viewport_width, status.capture_summary.frame_end_viewport_height); - ImGui::Text("frontend passes scene/post/ui: %u / %u / %u / %u", - status.frontend_summary.pass_count, status.frontend_summary.scene_pass_count, - status.frontend_summary.post_process_pass_count, - status.frontend_summary.ui_pass_count); - if (status.frame_plan.present_stage.valid) { - ImGui::Text( - "present stage: #%u %s vp=%ux%u rt0=%08X depth=%08X draws/resolves=%u/%u", - status.frame_plan.present_stage.pass_index, - ac6::renderer::ToString(status.frame_plan.present_stage.kind), - status.frame_plan.present_stage.viewport_width, - status.frame_plan.present_stage.viewport_height, - status.frame_plan.present_stage.render_target_0, - status.frame_plan.present_stage.depth_stencil, - status.frame_plan.present_stage.draw_count, - status.frame_plan.present_stage.resolve_count); - } - if (status.frame_plan.scene_stage.valid) { - ImGui::Text( - "scene stage: #%u %s vp=%ux%u rt0=%08X depth=%08X draws/resolves=%u/%u", - status.frame_plan.scene_stage.pass_index, - ac6::renderer::ToString(status.frame_plan.scene_stage.kind), - status.frame_plan.scene_stage.viewport_width, - status.frame_plan.scene_stage.viewport_height, - status.frame_plan.scene_stage.render_target_0, - status.frame_plan.scene_stage.depth_stencil, - status.frame_plan.scene_stage.draw_count, - status.frame_plan.scene_stage.resolve_count); - } - if (status.frame_plan.post_process_stage.valid) { - ImGui::Text( - "post stage: #%u %s vp=%ux%u rt0=%08X depth=%08X draws/resolves=%u/%u", - status.frame_plan.post_process_stage.pass_index, - ac6::renderer::ToString(status.frame_plan.post_process_stage.kind), - status.frame_plan.post_process_stage.viewport_width, - status.frame_plan.post_process_stage.viewport_height, - status.frame_plan.post_process_stage.render_target_0, - status.frame_plan.post_process_stage.depth_stencil, - status.frame_plan.post_process_stage.draw_count, - status.frame_plan.post_process_stage.resolve_count); - } - if (status.frame_plan.ui_stage.valid) { - ImGui::Text( - "ui stage: #%u %s vp=%ux%u rt0=%08X depth=%08X draws/resolves=%u/%u", - status.frame_plan.ui_stage.pass_index, - ac6::renderer::ToString(status.frame_plan.ui_stage.kind), - status.frame_plan.ui_stage.viewport_width, - status.frame_plan.ui_stage.viewport_height, - status.frame_plan.ui_stage.render_target_0, - status.frame_plan.ui_stage.depth_stencil, - status.frame_plan.ui_stage.draw_count, - status.frame_plan.ui_stage.resolve_count); - } - for (uint32_t i = 0; i < status.pass_diagnostics_count; ++i) { - const auto& pass = status.pass_diagnostics[i]; - if (!pass.valid) { - continue; - } - ImGui::Text( - "pass[%u]: #%u %s score=%u vp=%ux%u+%u+%u rt0=%08X depth=%08X d/c/r=%u/%u/%u tex/str/samp/fetch=%u/%u/%u/%u gpr=%u %s%s", - i, pass.pass_index, ac6::renderer::ToString(pass.kind), pass.score, - pass.viewport_width, pass.viewport_height, pass.viewport_x, pass.viewport_y, - pass.render_target_0, pass.depth_stencil, pass.draw_count, pass.clear_count, - pass.resolve_count, pass.max_texture_count, pass.max_stream_count, - pass.max_sampler_count, pass.max_fetch_constant_count, pass.max_shader_gpr_alloc, - pass.selected_for_present ? "present " : "", - pass.matches_frame_end_viewport ? "frame_end" : ""); - ImGui::Text( - " sig=%016llX tfetch=%016llX->%016llX bind=%016llX->%016llX", - static_cast(pass.pass_signature), - static_cast(pass.first_texture_fetch_layout_signature), - static_cast(pass.last_texture_fetch_layout_signature), - static_cast(pass.first_resource_binding_signature), - static_cast(pass.last_resource_binding_signature)); - const std::string first_textures = FormatBoundResourceSample(pass.first_textures); - const std::string last_textures = FormatBoundResourceSample(pass.last_textures); - const std::string first_fetch = FormatFetchConstantSample(pass.first_fetch_constants); - const std::string last_fetch = FormatFetchConstantSample(pass.last_fetch_constants); - const std::string rt0_object = FormatDecodedTextureObject(pass.render_target_0); - const std::string depth_object = FormatDecodedTextureObject(pass.depth_stencil); - const DecodedSampleSet<4> decoded_first_fetch = DecodeSampleSet(pass.first_fetch_constants); - const DecodedSampleSet<4> decoded_last_fetch = DecodeSampleSet(pass.last_fetch_constants); - const std::string source_passes = - FormatPassSources(decoded_first_fetch, decoded_last_fetch, status, i); - const std::string resolve_sources = - FormatResolveSources(decoded_first_fetch, decoded_last_fetch, status); - ImGui::Text(" tex %s -> %s", first_textures.c_str(), last_textures.c_str()); - ImGui::Text(" fet %s -> %s", first_fetch.c_str(), last_fetch.c_str()); - ImGui::Text(" rt0 %s depth %s", rt0_object.c_str(), depth_object.c_str()); - ImGui::Text(" src %s", source_passes.c_str()); - ImGui::Text(" rslv %s", resolve_sources.c_str()); - } - for (uint32_t i = 0; i < status.resolve_diagnostics_count; ++i) { - const auto& resolve = status.resolve_diagnostics[i]; - if (!resolve.valid) { - continue; - } - ImGui::Text( - "resolve[%u]: seq=%u vp=%ux%u rt0=%08X depth=%08X f1=%.3f", i, resolve.sequence, - resolve.viewport_width, resolve.viewport_height, resolve.render_target_0, - resolve.depth_stencil, resolve.depth_or_scale); - const std::string candidates = FormatResolveArgCandidates(resolve); - ImGui::Text(" %s", candidates.c_str()); - } ImGui::Separator(); ImGui::Text("swap source: %s", ac6::backend::ToString(diagnostics.swap_source)); @@ -560,8 +113,6 @@ void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) { : diagnostics.latest_signature_tags.c_str()); const bool draw_scale_is_native = status.draw_resolution_scale_x <= 1 && status.draw_resolution_scale_y <= 1; - const bool draw_scale_is_scaled = - status.draw_resolution_scale_x > 1 || status.draw_resolution_scale_y > 1; const bool likely_point_filtered = diagnostics.latest_signature.point_min_sampler_count != 0 || diagnostics.latest_signature.point_mip_sampler_count != 0; @@ -613,26 +164,6 @@ void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) { diagnostics.audio_callback_dispatch_count, diagnostics.audio_callback_throttle_count); - if (status.mode == GraphicsRuntimeMode::kLegacyReplayExperimental) { - ImGui::Separator(); - ImGui::TextUnformatted("legacy replay diagnostics (experimental):"); - ImGui::Text("initialized: %s", status.initialized ? "true" : "false"); - ImGui::Text("init failures seen: %s", status.had_init_failure ? "true" : "false"); - ImGui::Text("replay backend: %s", - ac6::renderer::ToString(status.active_backend).data()); - ImGui::Text("replay feature level: %s", - ac6::renderer::ToString(status.feature_level).data()); - ImGui::Text("frontend / replay / execution commands: %u / %u / %u", - status.frontend_summary.total_command_count, - status.replay_summary.command_count, - status.execution_summary.command_count); - ImGui::Text("backend draw attempts / success: %u / %u", - status.backend_executor_status.draw_attempt_count, - status.backend_executor_status.draw_success_count); - ImGui::Text("planned output: %ux%u", status.frame_plan.output_width, - status.frame_plan.output_height); - } - ImGui::End(); } diff --git a/src/ac6_native_renderer/ac6_render_frontend.cpp b/src/ac6_native_renderer/ac6_render_frontend.cpp deleted file mode 100644 index 54ad2f74..00000000 --- a/src/ac6_native_renderer/ac6_render_frontend.cpp +++ /dev/null @@ -1,450 +0,0 @@ -#include "ac6_render_frontend.h" - -#include - -namespace ac6::renderer { -namespace { - -void HashU32(uint64_t& hash, uint32_t value) { - constexpr uint64_t kFnvPrime = 1099511628211ull; - hash ^= value; - hash *= kFnvPrime; -} - -void HashU64(uint64_t& hash, uint64_t value) { - HashU32(hash, uint32_t(value & 0xFFFFFFFFull)); - HashU32(hash, uint32_t(value >> 32)); -} - -template -uint32_t CountNonZeroEntries(const Container& values) { - uint32_t count = 0; - for (const auto& value : values) { - if (value) { - ++count; - } - } - return count; -} - -template -ObservedPassDesc::BoundResourceSample SampleBoundEntries( - const std::array& values) { - ObservedPassDesc::BoundResourceSample sample; - for (uint32_t slot = 0; slot < values.size(); ++slot) { - if (!values[slot]) { - continue; - } - if (sample.count < sample.values.size()) { - sample.slots[sample.count] = slot; - sample.values[sample.count] = values[slot]; - } - ++sample.count; - } - return sample; -} - -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; -} - -enum class CapturedFrameEventType : uint8_t { - kDraw = 0, - kClear = 1, - kResolve = 2, -}; - -struct CapturedFrameEvent { - uint32_t sequence = 0; - CapturedFrameEventType type = CapturedFrameEventType::kDraw; - const ac6::d3d::DrawCallRecord* draw = nullptr; - const ac6::d3d::ClearRecord* clear = nullptr; - const ac6::d3d::ResolveRecord* resolve = nullptr; - const ac6::d3d::ShadowState* shadow_state = nullptr; -}; - -ObservedCommandDesc MakeObservedCommand(const ac6::d3d::DrawCallRecord& draw) { - return ObservedCommandDesc{ - .type = ObservedCommandType::kDraw, - .sequence = draw.sequence, - .draw_kind = draw.kind, - .primitive_type = draw.primitive_type, - .start = draw.start, - .count = draw.count, - .flags = draw.flags, - .texture_count = CountNonZeroEntries(draw.shadow_state.textures), - .stream_count = CountBoundStreams(draw.shadow_state.streams), - .sampler_count = CountBoundSamplers(draw.shadow_state.samplers), - .fetch_constant_count = CountNonZeroEntries(draw.shadow_state.texture_fetch_ptrs), - .render_target_0 = draw.shadow_state.render_targets[0], - .depth_stencil = draw.shadow_state.depth_stencil, - .viewport_x = draw.shadow_state.viewport.x, - .viewport_y = draw.shadow_state.viewport.y, - .viewport_width = draw.shadow_state.viewport.width, - .viewport_height = draw.shadow_state.viewport.height, - .shadow_state = draw.shadow_state, - }; -} - -ObservedCommandDesc MakeObservedCommand(const ac6::d3d::ClearRecord& clear) { - return ObservedCommandDesc{ - .type = ObservedCommandType::kClear, - .sequence = clear.sequence, - .rect_count = clear.rect_count, - .captured_rect_count = clear.captured_rect_count, - .flags = clear.flags, - .color = clear.color, - .stencil = clear.stencil, - .depth = clear.depth, - .texture_count = CountNonZeroEntries(clear.shadow_state.textures), - .stream_count = CountBoundStreams(clear.shadow_state.streams), - .sampler_count = CountBoundSamplers(clear.shadow_state.samplers), - .fetch_constant_count = CountNonZeroEntries(clear.shadow_state.texture_fetch_ptrs), - .render_target_0 = clear.shadow_state.render_targets[0], - .depth_stencil = clear.shadow_state.depth_stencil, - .viewport_x = clear.shadow_state.viewport.x, - .viewport_y = clear.shadow_state.viewport.y, - .viewport_width = clear.shadow_state.viewport.width, - .viewport_height = clear.shadow_state.viewport.height, - .shadow_state = clear.shadow_state, - }; -} - -ObservedCommandDesc MakeObservedCommand(const ac6::d3d::ResolveRecord& resolve) { - return ObservedCommandDesc{ - .type = ObservedCommandType::kResolve, - .sequence = resolve.sequence, - .texture_count = CountNonZeroEntries(resolve.shadow_state.textures), - .stream_count = CountBoundStreams(resolve.shadow_state.streams), - .sampler_count = CountBoundSamplers(resolve.shadow_state.samplers), - .fetch_constant_count = CountNonZeroEntries(resolve.shadow_state.texture_fetch_ptrs), - .render_target_0 = resolve.shadow_state.render_targets[0], - .depth_stencil = resolve.shadow_state.depth_stencil, - .viewport_x = resolve.shadow_state.viewport.x, - .viewport_y = resolve.shadow_state.viewport.y, - .viewport_width = resolve.shadow_state.viewport.width, - .viewport_height = resolve.shadow_state.viewport.height, - .shadow_state = resolve.shadow_state, - }; -} - -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.x == right.viewport.x && - left.viewport.y == right.viewport.y && - left.viewport.width == right.viewport.width && - left.viewport.height == right.viewport.height; -} - -bool MatchesFrameEndViewport(const ObservedPassDesc& pass, - const ac6::d3d::ShadowState& frame_end_shadow) { - return pass.viewport_width != 0 && pass.viewport_height != 0 && - pass.viewport_width == frame_end_shadow.viewport.width && - pass.viewport_height == frame_end_shadow.viewport.height; -} - -uint32_t ScorePassCandidate(const ObservedPassDesc& pass, bool is_last_pass) { - uint32_t score = 0; - score += std::min(pass.draw_count * 3, 180u); - score += std::min(pass.clear_count * 8, 40u); - score += std::min(pass.resolve_count * 30, 90u); - score += std::min(pass.max_texture_count * 2, 24u); - score += std::min(pass.max_stream_count * 3, 24u); - if (pass.matches_frame_end_viewport) { - score += 80; - } - if (pass.render_target_0 != 0) { - score += 20; - } - if (is_last_pass) { - score += 25; - } - return score; -} - -ObservedPassKind ClassifyPass(const ObservedPassDesc& pass, bool is_last_pass) { - if (pass.resolve_count != 0) { - return ObservedPassKind::kPostProcess; - } - const bool likely_ui = - pass.draw_count != 0 && pass.clear_count == 0 && pass.max_texture_count >= 2 && - pass.max_stream_count <= 2 && pass.max_sampler_count <= 4 && - pass.viewport_width != 0 && pass.viewport_height != 0; - if ((is_last_pass && pass.draw_count <= 16 && pass.clear_count == 0) || - (likely_ui && pass.draw_count <= 24 && pass.max_draw_call_count <= 1024)) { - return ObservedPassKind::kUiComposite; - } - if (pass.draw_count != 0 || pass.clear_count != 0) { - return ObservedPassKind::kScene; - } - return ObservedPassKind::kUnknown; -} - -} // namespace - -const char* ToString(ObservedPassKind kind) { - switch (kind) { - case ObservedPassKind::kScene: - return "scene"; - case ObservedPassKind::kPostProcess: - return "post_process"; - case ObservedPassKind::kUiComposite: - return "ui_composite"; - case ObservedPassKind::kUnknown: - default: - return "unknown"; - } -} - -const char* ToString(ObservedCommandType type) { - switch (type) { - case ObservedCommandType::kDraw: - return "draw"; - case ObservedCommandType::kClear: - return "clear"; - case ObservedCommandType::kResolve: - return "resolve"; - default: - return "unknown"; - } -} - -void Ac6RenderFrontend::Reset() { - summary_ = {}; - passes_.clear(); -} - -FrontendFrameSummary Ac6RenderFrontend::BuildFromCapture( - const ac6::d3d::FrameCaptureSnapshot& frame_capture) { - Reset(); - - summary_.frame_index = frame_capture.frame_index; - summary_.capture_valid = - !frame_capture.draws.empty() || !frame_capture.clears.empty() || - !frame_capture.resolves.empty(); - if (!summary_.capture_valid) { - return summary_; - } - - 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, CapturedFrameEventType::kDraw, &draw, nullptr, - nullptr, &draw.shadow_state}); - ++summary_.total_draw_count; - } - for (const auto& clear : frame_capture.clears) { - events.push_back({clear.sequence, CapturedFrameEventType::kClear, nullptr, &clear, - nullptr, &clear.shadow_state}); - ++summary_.total_clear_count; - } - for (const auto& resolve : frame_capture.resolves) { - events.push_back({resolve.sequence, CapturedFrameEventType::kResolve, nullptr, nullptr, - &resolve, &resolve.shadow_state}); - ++summary_.total_resolve_count; - } - - std::sort(events.begin(), events.end(), - [](const CapturedFrameEvent& left, const CapturedFrameEvent& right) { - return left.sequence < right.sequence; - }); - - const ac6::d3d::ShadowState empty_shadow{}; - const ac6::d3d::ShadowState* current_binding = &empty_shadow; - bool current_pass_valid = false; - ObservedPassDesc current_pass{}; - - 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 == nullptr) { - continue; - } - if (!current_pass_valid || !SamePassBinding(*current_binding, *event.shadow_state)) { - flush_pass(); - current_binding = event.shadow_state; - current_pass_valid = true; - current_pass.start_sequence = event.sequence; - current_pass.render_target_0 = event.shadow_state->render_targets[0]; - current_pass.depth_stencil = event.shadow_state->depth_stencil; - current_pass.viewport_x = event.shadow_state->viewport.x; - current_pass.viewport_y = event.shadow_state->viewport.y; - current_pass.viewport_width = event.shadow_state->viewport.width; - current_pass.viewport_height = event.shadow_state->viewport.height; - current_pass.first_vertex_fetch_layout_signature = - event.shadow_state->vertex_fetch_layout_signature; - current_pass.last_vertex_fetch_layout_signature = - event.shadow_state->vertex_fetch_layout_signature; - current_pass.first_texture_fetch_layout_signature = - event.shadow_state->texture_fetch_layout_signature; - current_pass.last_texture_fetch_layout_signature = - event.shadow_state->texture_fetch_layout_signature; - current_pass.first_resource_binding_signature = - event.shadow_state->resource_binding_signature; - current_pass.last_resource_binding_signature = - event.shadow_state->resource_binding_signature; - current_pass.first_textures = SampleBoundEntries(event.shadow_state->textures); - current_pass.last_textures = current_pass.first_textures; - current_pass.first_fetch_constants = - SampleBoundEntries(event.shadow_state->texture_fetch_ptrs); - current_pass.last_fetch_constants = current_pass.first_fetch_constants; - constexpr uint64_t kFnvOffsetBasis = 1469598103934665603ull; - current_pass.pass_signature = kFnvOffsetBasis; - } - - current_pass.end_sequence = event.sequence; - HashU32(current_pass.pass_signature, static_cast(event.type)); - HashU32(current_pass.pass_signature, event.sequence); - HashU64(current_pass.pass_signature, event.shadow_state->vertex_fetch_layout_signature); - HashU64(current_pass.pass_signature, event.shadow_state->texture_fetch_layout_signature); - HashU64(current_pass.pass_signature, event.shadow_state->resource_binding_signature); - current_pass.last_vertex_fetch_layout_signature = - event.shadow_state->vertex_fetch_layout_signature; - current_pass.last_texture_fetch_layout_signature = - event.shadow_state->texture_fetch_layout_signature; - current_pass.last_resource_binding_signature = - event.shadow_state->resource_binding_signature; - current_pass.last_textures = SampleBoundEntries(event.shadow_state->textures); - current_pass.last_fetch_constants = - SampleBoundEntries(event.shadow_state->texture_fetch_ptrs); - current_pass.max_shader_gpr_alloc = - std::max(current_pass.max_shader_gpr_alloc, event.shadow_state->shader_gpr_alloc); - switch (event.type) { - case CapturedFrameEventType::kDraw: - ++current_pass.draw_count; - if (event.draw != nullptr) { - current_pass.commands.push_back(MakeObservedCommand(*event.draw)); - ++summary_.total_command_count; - } - if (event.shadow_state != nullptr) { - current_pass.max_texture_count = std::max( - current_pass.max_texture_count, - CountNonZeroEntries(event.shadow_state->textures)); - current_pass.max_stream_count = std::max( - current_pass.max_stream_count, - CountBoundStreams(event.shadow_state->streams)); - current_pass.max_sampler_count = std::max( - current_pass.max_sampler_count, - CountBoundSamplers(event.shadow_state->samplers)); - current_pass.max_fetch_constant_count = std::max( - current_pass.max_fetch_constant_count, - CountNonZeroEntries(event.shadow_state->texture_fetch_ptrs)); - } - break; - case CapturedFrameEventType::kClear: - ++current_pass.clear_count; - if (event.clear != nullptr) { - current_pass.commands.push_back(MakeObservedCommand(*event.clear)); - ++summary_.total_command_count; - } - break; - case CapturedFrameEventType::kResolve: - ++current_pass.resolve_count; - if (event.resolve != nullptr) { - current_pass.commands.push_back(MakeObservedCommand(*event.resolve)); - ++summary_.total_command_count; - } - break; - } - } - - flush_pass(); - - summary_.pass_count = static_cast(passes_.size()); - if (passes_.empty()) { - summary_.capture_valid = false; - return summary_; - } - - for (const auto& draw : frame_capture.draws) { - for (ObservedPassDesc& pass : passes_) { - if (draw.sequence < pass.start_sequence || draw.sequence > pass.end_sequence) { - continue; - } - switch (draw.kind) { - case ac6::d3d::DrawCallKind::kIndexed: - ++pass.indexed_draw_count; - break; - case ac6::d3d::DrawCallKind::kIndexedShared: - ++pass.indexed_shared_draw_count; - break; - case ac6::d3d::DrawCallKind::kPrimitive: - ++pass.primitive_draw_count; - break; - } - pass.max_draw_call_count = std::max(pass.max_draw_call_count, draw.count); - break; - } - } - - uint32_t selected_pass_index = 0; - uint32_t selected_pass_score = 0; - bool selected_pass_valid = false; - for (uint32_t i = 0; i < passes_.size(); ++i) { - ObservedPassDesc& pass = passes_[i]; - const bool is_last_pass = (i + 1) == passes_.size(); - pass.matches_frame_end_viewport = - MatchesFrameEndViewport(pass, frame_capture.frame_end_shadow); - pass.kind = ClassifyPass(pass, is_last_pass); - const uint32_t score = ScorePassCandidate(pass, is_last_pass); - if (!selected_pass_valid || score > selected_pass_score || - (score == selected_pass_score && is_last_pass)) { - selected_pass_valid = true; - selected_pass_index = i; - selected_pass_score = score; - } - - switch (pass.kind) { - case ObservedPassKind::kScene: - ++summary_.scene_pass_count; - break; - case ObservedPassKind::kPostProcess: - ++summary_.post_process_pass_count; - break; - case ObservedPassKind::kUiComposite: - ++summary_.ui_pass_count; - break; - case ObservedPassKind::kUnknown: - break; - } - } - - if (selected_pass_valid) { - passes_[selected_pass_index].selected_for_present = true; - summary_.selected_pass_index = selected_pass_index; - } - - return summary_; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/ac6_render_frontend.h b/src/ac6_native_renderer/ac6_render_frontend.h deleted file mode 100644 index e9e798c9..00000000 --- a/src/ac6_native_renderer/ac6_render_frontend.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "d3d_state.h" - -namespace ac6::renderer { - -enum class ObservedPassKind : uint8_t { - kUnknown = 0, - kScene = 1, - kPostProcess = 2, - kUiComposite = 3, -}; - -enum class ObservedCommandType : uint8_t { - kDraw = 0, - kClear = 1, - kResolve = 2, -}; - -struct ObservedCommandDesc { - ObservedCommandType type = ObservedCommandType::kDraw; - uint32_t sequence = 0; - ac6::d3d::DrawCallKind draw_kind = ac6::d3d::DrawCallKind::kIndexed; - uint32_t primitive_type = 0; - uint32_t start = 0; - uint32_t count = 0; - uint32_t flags = 0; - uint32_t rect_count = 0; - uint32_t captured_rect_count = 0; - uint32_t color = 0; - uint32_t stencil = 0; - float depth = 1.0f; - uint32_t texture_count = 0; - uint32_t stream_count = 0; - uint32_t sampler_count = 0; - uint32_t fetch_constant_count = 0; - uint32_t render_target_0 = 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; - ac6::d3d::ShadowState shadow_state{}; -}; - -struct ObservedPassDesc { - struct BoundResourceSample { - uint32_t count = 0; - std::array slots{}; - std::array values{}; - }; - - ObservedPassKind kind = ObservedPassKind::kUnknown; - uint64_t pass_signature = 0; - 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; - uint32_t indexed_draw_count = 0; - uint32_t indexed_shared_draw_count = 0; - uint32_t primitive_draw_count = 0; - uint32_t max_texture_count = 0; - uint32_t max_stream_count = 0; - uint32_t max_sampler_count = 0; - uint32_t max_fetch_constant_count = 0; - uint32_t max_draw_call_count = 0; - uint32_t max_shader_gpr_alloc = 0; - uint32_t render_target_0 = 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; - uint64_t first_vertex_fetch_layout_signature = 0; - uint64_t last_vertex_fetch_layout_signature = 0; - uint64_t first_texture_fetch_layout_signature = 0; - uint64_t last_texture_fetch_layout_signature = 0; - uint64_t first_resource_binding_signature = 0; - uint64_t last_resource_binding_signature = 0; - BoundResourceSample first_textures{}; - BoundResourceSample last_textures{}; - BoundResourceSample first_fetch_constants{}; - BoundResourceSample last_fetch_constants{}; - bool selected_for_present = false; - bool matches_frame_end_viewport = false; - std::vector commands; -}; - -struct FrontendFrameSummary { - bool capture_valid = false; - uint64_t frame_index = 0; - uint32_t pass_count = 0; - uint32_t total_command_count = 0; - uint32_t scene_pass_count = 0; - uint32_t post_process_pass_count = 0; - uint32_t ui_pass_count = 0; - uint32_t selected_pass_index = 0; - uint32_t total_draw_count = 0; - uint32_t total_clear_count = 0; - uint32_t total_resolve_count = 0; -}; - -class Ac6RenderFrontend { - public: - void Reset(); - FrontendFrameSummary BuildFromCapture(const ac6::d3d::FrameCaptureSnapshot& frame_capture); - - const FrontendFrameSummary& summary() const { return summary_; } - const std::vector& passes() const { return passes_; } - - private: - FrontendFrameSummary summary_{}; - std::vector passes_; -}; - -const char* ToString(ObservedPassKind kind); -const char* ToString(ObservedCommandType type); - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/backend_factory.cpp b/src/ac6_native_renderer/backends/backend_factory.cpp deleted file mode 100644 index f221be1d..00000000 --- a/src/ac6_native_renderer/backends/backend_factory.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "ac6_native_renderer/render_device.h" - -#include - -#include "ac6_native_renderer/backends/d3d12_backend.h" -#include "ac6_native_renderer/backends/metal_backend.h" -#include "ac6_native_renderer/backends/vulkan_backend.h" - -namespace ac6::renderer { - -std::unique_ptr CreateBackend(BackendType backend) { - switch (backend) { - case BackendType::kD3D12: - return std::make_unique(); - case BackendType::kVulkan: - return std::make_unique(); - case BackendType::kMetal: - return std::make_unique(); - default: - return nullptr; - } -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/d3d12_backend.cpp b/src/ac6_native_renderer/backends/d3d12_backend.cpp deleted file mode 100644 index 3806f5dd..00000000 --- a/src/ac6_native_renderer/backends/d3d12_backend.cpp +++ /dev/null @@ -1,1006 +0,0 @@ -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include "d3d12_backend.h" - -#include - -#include -#include - -#if defined(_WIN32) -#pragma comment(lib, "d3d12.lib") -#pragma comment(lib, "dxgi.lib") -#pragma comment(lib, "d3dcompiler.lib") -#endif - -namespace ac6::renderer { - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -#if defined(_WIN32) - -// Compute a safe index-buffer upload size from draw count + index type. -// Xenos always uses 16-bit indices unless count > 65535. -static uint32_t SafeIndexBufferSize(uint32_t index_count) { - // Xbox 360 indices are always big-endian uint16. Cap at 4 MB. - const uint64_t byte_size = uint64_t(index_count) * sizeof(uint16_t); - return byte_size <= 4u * 1024u * 1024u ? static_cast(byte_size) : 0u; -} - -// Compute a safe vertex-buffer upload size from vertex count + stride. -static uint32_t SafeVertexBufferSize(uint32_t vertex_count, uint32_t stride) { - if (stride == 0 || vertex_count == 0) return 0; - const uint64_t byte_size = uint64_t(vertex_count) * uint64_t(stride); - // Cap at 8 MB per stream. - return byte_size <= 8u * 1024u * 1024u ? static_cast(byte_size) : 0u; -} - -// Validate a guest address is non-zero before calling TranslateVirtual. -static bool ValidGuestAddress(uint32_t addr) { - return addr != 0 && addr < 0xFF000000u; -} - -// Hash a byte span with FNV-1a. -static uint64_t FnvHash64(const void* data, size_t size) { - constexpr uint64_t kBasis = 14695981039346656037ull; - constexpr uint64_t kPrime = 1099511628211ull; - uint64_t h = kBasis; - const auto* p = static_cast(data); - for (size_t i = 0; i < size; ++i) { h ^= p[i]; h *= kPrime; } - return h; -} - -static uint16_t ByteSwap16(uint16_t value) { - return static_cast((value << 8) | (value >> 8)); -} - -static D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTypeToTopologyType(uint32_t primitive_type) { - switch (primitive_type) { - case 1: - return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; - case 2: - case 3: - return D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; - case 4: - case 8: - case 13: - case 5: - case 6: - default: - return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - } -} - -static D3D12_PRIMITIVE_TOPOLOGY PrimitiveTypeToTopology(uint32_t primitive_type) { - switch (primitive_type) { - case 1: - return D3D_PRIMITIVE_TOPOLOGY_POINTLIST; - case 2: - return D3D_PRIMITIVE_TOPOLOGY_LINELIST; - case 3: - return D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; - case 4: - case 8: - case 13: - return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - case 5: - return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; - case 6: - // Triangle fans need index expansion. Use strip as the least-bad fallback - // until guest primitive conversion is implemented. - return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; - default: - return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - } -} - -static bool IsQuadListPrimitive(uint32_t primitive_type) { - // Xenos kQuadList. - return primitive_type == 13; -} - -static bool IsRectangleListPrimitive(uint32_t primitive_type) { - // Xenos kRectangleList. - return primitive_type == 8; -} - -static bool NeedsSyntheticIndices(uint32_t primitive_type) { - return IsQuadListPrimitive(primitive_type) || IsRectangleListPrimitive(primitive_type); -} - -static uint32_t GuessColorOffset(uint32_t vertex_stride) { - if (vertex_stride == 20 || vertex_stride == 24) { - return 12; - } - if (vertex_stride >= 28) { - return 16; - } - return 0xFFFFFFFFu; -} - -struct DrawRootConstants { - uint32_t vertex_base_offset = 0; - uint32_t vertex_stride = 0; - uint32_t vertex_buffer_size = 0; - uint32_t viewport_x = 0; - uint32_t viewport_y = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; - uint32_t color_offset = 0xFFFFFFFFu; - uint32_t flags = 0; -}; - -static void CreateRawBufferSRV(ID3D12Device* device, - ID3D12Resource* resource, - uint32_t size_bytes, - D3D12_CPU_DESCRIPTOR_HANDLE handle) { - D3D12_SHADER_RESOURCE_VIEW_DESC desc = {}; - desc.Format = DXGI_FORMAT_R32_TYPELESS; - desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - desc.Buffer.FirstElement = 0; - desc.Buffer.NumElements = std::max(1u, (size_bytes + 3u) / 4u); - desc.Buffer.StructureByteStride = 0; - desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; - device->CreateShaderResourceView(resource, &desc, handle); -} - -#endif // _WIN32 - -// --------------------------------------------------------------------------- -// D3D12Backend public interface -// --------------------------------------------------------------------------- - -bool D3D12Backend::IsSupported() const { -#if defined(_WIN32) - return true; -#else - return false; -#endif -} - -// --------------------------------------------------------------------------- -// InitializeShared — reuse the emulator's already-created device/queue -// --------------------------------------------------------------------------- -bool D3D12Backend::InitializeShared(const NativeRendererConfig& config, - rex::memory::Memory* memory, - ID3D12Device* device, - ID3D12CommandQueue* queue) { - if (initialized_) return true; - memory_ = memory; - device_ = device; - graphics_queue_ = queue; - - REXLOG_INFO("D3D12Backend::InitializeShared device=0x{:016X}", (uint64_t)device_.Get()); - - if (!CreateCommandObjects(config.max_frames_in_flight)) return false; - if (!resource_manager_.Initialize(device_.Get(), config.max_frames_in_flight)) return false; - if (!shader_manager_.Initialize(device_.Get())) return false; - if (!CreateRootSignature()) return false; - - frame_scheduler_.Configure(config.max_frames_in_flight); - initialized_ = true; - REXLOG_INFO("D3D12Backend::InitializeShared succeeded max_frames={}", config.max_frames_in_flight); - return true; -} - -// --------------------------------------------------------------------------- -// Initialize — create our own device (standalone mode) -// --------------------------------------------------------------------------- -bool D3D12Backend::Initialize(const NativeRendererConfig& config, - rex::memory::Memory* memory) { - if (initialized_) return true; - memory_ = memory; - REXLOG_INFO("D3D12Backend::Initialize starting"); - -#if defined(_WIN32) - if (!CreateDevice()) return false; - if (!CreateCommandObjects(config.max_frames_in_flight)) return false; - if (!resource_manager_.Initialize(device_.Get(), config.max_frames_in_flight)) return false; - if (!shader_manager_.Initialize(device_.Get())) return false; - if (!CreateRootSignature()) return false; - frame_scheduler_.Configure(config.max_frames_in_flight); -#endif - - initialized_ = true; - REXLOG_INFO("D3D12Backend::Initialize succeeded max_frames={}", config.max_frames_in_flight); - return true; -} - -// --------------------------------------------------------------------------- -// Phase 4 helper — ensure output texture exists at correct size -// --------------------------------------------------------------------------- -#if defined(_WIN32) -bool D3D12Backend::EnsureOutputTexture(uint32_t width, uint32_t height) { - if (!device_) return false; - // Clamp / default - if (width == 0) width = 1280; - if (height == 0) height = 720; - - // Texture exists and is the correct size — reuse it. - if (output_texture_ && output_width_ == width && output_height_ == height) - return true; - - // If we already have a texture at a different size: keep the old one rather - // than destroying it mid-flight. Resize only when safe (i.e., on first creation). - if (output_texture_) { - // Already created at a different size — just return success and keep - // rendering into the old size to avoid a WaitForGpu on the shared queue. - return true; - } - - D3D12_RESOURCE_DESC desc = {}; - desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - desc.Width = width; - desc.Height = height; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = kOutputFormat; - desc.SampleDesc.Count = 1; - desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; - - D3D12_CLEAR_VALUE clear_val = {}; - clear_val.Format = kOutputFormat; - clear_val.Color[3] = 1.0f; - - D3D12_HEAP_PROPERTIES heap = {}; - heap.Type = D3D12_HEAP_TYPE_DEFAULT; - - HRESULT hr = device_->CreateCommittedResource( - &heap, D3D12_HEAP_FLAG_NONE, &desc, - D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_val, - IID_PPV_ARGS(&output_texture_)); - if (FAILED(hr)) { - REXLOG_ERROR("D3D12Backend: CreateCommittedResource output texture failed 0x{:08X}", (uint32_t)hr); - return false; - } - output_texture_->SetName(L"ac6.native.output"); - - // Create/recreate the RTV heap + descriptor - if (!output_rtv_heap_) { - D3D12_DESCRIPTOR_HEAP_DESC rtv_desc = {}; - rtv_desc.NumDescriptors = 1; - rtv_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - hr = device_->CreateDescriptorHeap(&rtv_desc, IID_PPV_ARGS(&output_rtv_heap_)); - if (FAILED(hr)) return false; - } - output_rtv_ = output_rtv_heap_->GetCPUDescriptorHandleForHeapStart(); - device_->CreateRenderTargetView(output_texture_.Get(), nullptr, output_rtv_); - - // Track the new resource - resource_tracker_.TrackResource(output_texture_.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET); - - output_width_ = width; - output_height_ = height; - REXLOG_INFO("D3D12Backend: output texture {}x{} created", width, height); - return true; -} -#endif - -// --------------------------------------------------------------------------- -// SubmitExecutorFrame — Phases 1, 3, 4 -// --------------------------------------------------------------------------- -bool D3D12Backend::SubmitExecutorFrame(const ReplayExecutorFrame& frame) { - if (!initialized_ || !device_) return false; - - // Guard: device removed? - if (FAILED(device_->GetDeviceRemovedReason())) { - REXLOG_ERROR("D3D12Backend: device removed, disabling backend"); - initialized_ = false; - return false; - } - -#if defined(_WIN32) - submission_debug_stats_ = {}; - - // Determine output dimensions from the frame summary - uint32_t out_w = frame.summary.output_width; - uint32_t out_h = frame.summary.output_height; - if (!EnsureOutputTexture(out_w, out_h)) return false; - - // Pick the frame slot - uint32_t slot = static_cast(frame.summary.frame_index % frame_contexts_.size()); - FrameContext& ctx = frame_contexts_[slot]; - - // Wait for this slot to finish on GPU - if (fence_->GetCompletedValue() < ctx.fence_value) { - fence_->SetEventOnCompletion(ctx.fence_value, (HANDLE)fence_event_); - WaitForSingleObject((HANDLE)fence_event_, INFINITE); - } - - // Reset for this frame - HRESULT hr = ctx.command_allocator->Reset(); - if (FAILED(hr)) { REXLOG_ERROR("D3D12Backend: allocator Reset failed"); return false; } - - hr = ctx.command_list->Reset(ctx.command_allocator.Get(), nullptr); - if (FAILED(hr)) { REXLOG_ERROR("D3D12Backend: cmdlist Reset failed"); return false; } - - resource_manager_.BeginFrame(slot); - - // ------------------------------------------------------------------- - // Phase 4: Transition output texture → RTV, clear it - // ------------------------------------------------------------------- - resource_tracker_.TransitionBarrier(ctx.command_list.Get(), - output_texture_.Get(), - D3D12_RESOURCE_STATE_RENDER_TARGET); - float clear_color[4] = {0.05f, 0.05f, 0.08f, 1.0f}; - ctx.command_list->ClearRenderTargetView(output_rtv_, clear_color, 0, nullptr); - ctx.command_list->OMSetRenderTargets(1, &output_rtv_, FALSE, nullptr); - - D3D12_VIEWPORT vp = {0.f, 0.f, (float)output_width_, (float)output_height_, 0.f, 1.f}; - D3D12_RECT scissor = {0, 0, (LONG)output_width_, (LONG)output_height_}; - ctx.command_list->RSSetViewports(1, &vp); - ctx.command_list->RSSetScissorRects(1, &scissor); - ctx.command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - - // ------------------------------------------------------------------- - // Phase 3: Dispatch per-pass commands - // ------------------------------------------------------------------- - for (const ReplayExecutorPassPacket& pass : frame.passes) { - DispatchPassCommands(ctx.command_list.Get(), pass, slot); - } - - // ------------------------------------------------------------------- - // Phase 4: Transition output → PIXEL_SHADER_RESOURCE for blit consumer - // ------------------------------------------------------------------- - resource_tracker_.TransitionBarrier(ctx.command_list.Get(), - output_texture_.Get(), - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - - hr = ctx.command_list->Close(); - if (FAILED(hr)) { REXLOG_ERROR("D3D12Backend: cmdlist Close failed"); return false; } - - ID3D12CommandList* lists[] = { ctx.command_list.Get() }; - graphics_queue_->ExecuteCommandLists(1, lists); - - ++current_fence_value_; - hr = graphics_queue_->Signal(fence_.Get(), current_fence_value_); - if (FAILED(hr)) { REXLOG_ERROR("D3D12Backend: Signal failed"); return false; } - ctx.fence_value = current_fence_value_; -#endif - - executor_status_ = { - .initialized = true, - .frame_valid = frame.summary.valid, - .frame_index = frame.summary.frame_index, - .submitted_pass_count = frame.summary.pass_count, - .submitted_command_count = frame.summary.command_count, - .graphics_pass_count = frame.summary.graphics_pass_count, - .async_compute_pass_count = frame.summary.async_compute_pass_count, - .copy_pass_count = frame.summary.copy_pass_count, - .present_pass_count = frame.summary.present_pass_count, - .resource_translation_pass_count = frame.summary.resource_translation_pass_count, - .pipeline_state_pass_count = frame.summary.pipeline_state_pass_count, - .descriptor_setup_pass_count = frame.summary.descriptor_setup_pass_count, - .draw_attempt_count = submission_debug_stats_.draw_attempt_count, - .draw_success_count = submission_debug_stats_.draw_success_count, - .draw_prepare_failure_count = submission_debug_stats_.draw_prepare_failure_count, - .draw_pso_failure_count = submission_debug_stats_.draw_pso_failure_count, - .indexed_draw_count = submission_debug_stats_.indexed_draw_count, - .non_indexed_draw_count = submission_debug_stats_.non_indexed_draw_count, - .clear_command_count = submission_debug_stats_.clear_command_count, - .resolve_command_count = submission_debug_stats_.resolve_command_count, - .invalid_stream_binding_count = submission_debug_stats_.invalid_stream_binding_count, - .invalid_index_buffer_count = submission_debug_stats_.invalid_index_buffer_count, - .index_count_overflow_count = submission_debug_stats_.index_count_overflow_count, - .index_data_unavailable_count = submission_debug_stats_.index_data_unavailable_count, - .index_buffer_create_failure_count = - submission_debug_stats_.index_buffer_create_failure_count, - .index_upload_failure_count = submission_debug_stats_.index_upload_failure_count, - .zero_vertex_count = submission_debug_stats_.zero_vertex_count, - .invalid_vertex_range_count = submission_debug_stats_.invalid_vertex_range_count, - .vertex_buffer_size_invalid_count = - submission_debug_stats_.vertex_buffer_size_invalid_count, - .vertex_buffer_create_failure_count = - submission_debug_stats_.vertex_buffer_create_failure_count, - .vertex_data_unavailable_count = - submission_debug_stats_.vertex_data_unavailable_count, - .vertex_upload_failure_count = submission_debug_stats_.vertex_upload_failure_count, - }; - return true; -} - -// --------------------------------------------------------------------------- -// Phase 3: per-pass command dispatch -// --------------------------------------------------------------------------- -#if defined(_WIN32) -void D3D12Backend::DispatchPassCommands(ID3D12GraphicsCommandList* cmd, - const ReplayExecutorPassPacket& pass, - uint32_t slot) { - ID3D12DescriptorHeap* descriptor_heaps[] = {resource_manager_.GetSrvHeap()}; - const DXGI_FORMAT rt_fmt = kOutputFormat; - const DXGI_FORMAT ds_fmt = DXGI_FORMAT_UNKNOWN; - const bool soft_part = (pass.role == ReplayPassRole::kPostProcess || - pass.role == ReplayPassRole::kPresent); - - for (const ReplayExecutorCommandPacket& command : pass.commands) { - switch (command.category) { - case ExecutionCommandCategory::kClear: { - ++submission_debug_stats_.clear_command_count; - // Use the captured clear color rather than deriving nonsense from the - // render-target handle stored in shadow state. - float cc[4] = { - static_cast((command.color >> 16) & 0xFF) / 255.f, - static_cast((command.color >> 8) & 0xFF) / 255.f, - static_cast((command.color >> 0) & 0xFF) / 255.f, - static_cast((command.color >> 24) & 0xFF) / 255.f, - }; - cmd->ClearRenderTargetView(output_rtv_, cc, 0, nullptr); - break; - } - - case ExecutionCommandCategory::kDraw: { - ++submission_debug_stats_.draw_attempt_count; - // Draw a full-screen triangle via SV_VertexID — the passthrough VS - // does not need any vertex buffer. Real geometry binding (Phase 1.4) - // requires a CPU-safe readback copy, not a live TranslateVirtual read. - DrawResources draw_resources; - if (!PrepareDrawResources(cmd, command, slot, draw_resources)) { - ++submission_debug_stats_.draw_prepare_failure_count; - break; - } - - const uint64_t pso_hash = MakePSOHash(rt_fmt, ds_fmt, - draw_resources.topology_type, - soft_part); - ID3D12PipelineState* pso = shader_manager_.GetOrCreatePSO( - pso_hash, root_signature_.Get(), rt_fmt, ds_fmt, - draw_resources.topology_type, soft_part); - if (!pso) { - ++submission_debug_stats_.draw_pso_failure_count; - break; - } - - DrawRootConstants constants; - constants.vertex_base_offset = draw_resources.vertex_base_offset; - constants.vertex_stride = draw_resources.vertex_stride; - constants.vertex_buffer_size = draw_resources.vertex_buffer_size; - constants.viewport_x = command.shadow_state.viewport.x; - constants.viewport_y = command.shadow_state.viewport.y; - constants.viewport_width = - command.shadow_state.viewport.width ? command.shadow_state.viewport.width : pass.output_width; - constants.viewport_height = - command.shadow_state.viewport.height ? command.shadow_state.viewport.height : pass.output_height; - constants.color_offset = draw_resources.color_offset; - - D3D12_VIEWPORT vp = { - static_cast(constants.viewport_x), - static_cast(constants.viewport_y), - static_cast(std::max(constants.viewport_width, 1u)), - static_cast(std::max(constants.viewport_height, 1u)), - 0.0f, - 1.0f, - }; - D3D12_RECT scissor = { - static_cast(constants.viewport_x), - static_cast(constants.viewport_y), - static_cast(constants.viewport_x + std::max(constants.viewport_width, 1u)), - static_cast(constants.viewport_y + std::max(constants.viewport_height, 1u)), - }; - - cmd->SetPipelineState(pso); - cmd->SetGraphicsRootSignature(root_signature_.Get()); - if (descriptor_heaps[0]) { - cmd->SetDescriptorHeaps(1, descriptor_heaps); - cmd->SetGraphicsRootDescriptorTable(1, draw_resources.vertex_buffer_gpu); - } - cmd->SetGraphicsRoot32BitConstants( - 0, sizeof(constants) / sizeof(uint32_t), &constants, 0); - cmd->RSSetViewports(1, &vp); - cmd->RSSetScissorRects(1, &scissor); - cmd->IASetPrimitiveTopology(draw_resources.topology); - - if (draw_resources.indexed) { - ++submission_debug_stats_.indexed_draw_count; - cmd->DrawIndexedInstanced(draw_resources.draw_count, 1, - draw_resources.draw_start, 0, 0); - } else { - ++submission_debug_stats_.non_indexed_draw_count; - cmd->DrawInstanced(draw_resources.draw_count, 1, - draw_resources.draw_start, 0); - } - ++submission_debug_stats_.draw_success_count; - break; - } - - case ExecutionCommandCategory::kResolve: - ++submission_debug_stats_.resolve_command_count; - // Resolve is a no-op at this stage — handled by the output RT clear - break; - - default: - break; - } - } -} - -// --------------------------------------------------------------------------- -// Phase 1: Safe buffer upload -// --------------------------------------------------------------------------- -bool D3D12Backend::PrepareDrawResources(ID3D12GraphicsCommandList* cmd, - const ReplayExecutorCommandPacket& command, - uint32_t slot, - DrawResources& out_resources) { - (void)slot; - if (!memory_ || !cmd || command.count == 0) return false; - out_resources.draw_count = command.count; - out_resources.draw_start = 0; - const auto& ss = command.shadow_state; - const auto& stream = ss.streams[0]; - if (!ValidGuestAddress(stream.buffer) || stream.stride == 0) { - ++submission_debug_stats_.invalid_stream_binding_count; - return false; - } - - uint32_t vertex_count = 0; - uint32_t vertex_first = command.start; - if (command.draw_kind != d3d::DrawCallKind::kPrimitive) { - if (!ValidGuestAddress(ss.index_buffer)) { - if (!NeedsSyntheticIndices(command.primitive_type)) { - ++submission_debug_stats_.invalid_index_buffer_count; - return false; - } - - if (IsRectangleListPrimitive(command.primitive_type)) { - // D3D9 resolve-style rectangle lists are frequently emitted as 3-vertex - // draws. Our replay path doesn't implement rectangle expansion yet, so - // prefer a non-indexed triangle fallback over dropping the draw. - out_resources.indexed = false; - out_resources.draw_count = command.count; - out_resources.draw_start = 0; - vertex_count = command.count; - vertex_first = command.start; - } else { - const uint32_t quad_count = command.count / 4; - if (quad_count == 0) { - ++submission_debug_stats_.invalid_index_buffer_count; - return false; - } - const uint32_t expanded_index_count = quad_count * 6; - const uint32_t ib_size = SafeIndexBufferSize(expanded_index_count); - if (ib_size == 0) { - ++submission_debug_stats_.index_count_overflow_count; - return false; - } - - std::vector host_indices; - host_indices.reserve(expanded_index_count); - for (uint32_t quad = 0; quad < quad_count; ++quad) { - const uint32_t base_vertex = quad * 4; - if (base_vertex + 3 > UINT16_MAX) { - ++submission_debug_stats_.index_count_overflow_count; - return false; - } - const uint16_t i0 = static_cast(base_vertex + 0); - const uint16_t i1 = static_cast(base_vertex + 1); - const uint16_t i2 = static_cast(base_vertex + 2); - const uint16_t i3 = static_cast(base_vertex + 3); - host_indices.push_back(i0); - host_indices.push_back(i1); - host_indices.push_back(i2); - host_indices.push_back(i0); - host_indices.push_back(i2); - host_indices.push_back(i3); - } - - ID3D12Resource* host_ib = resource_manager_.GetOrCreateBuffer( - stream.buffer ^ 0x80000000u ^ command.start, ib_size); - if (!host_ib) { - ++submission_debug_stats_.index_buffer_create_failure_count; - return false; - } - - resource_tracker_.TransitionBarrier(cmd, host_ib, D3D12_RESOURCE_STATE_COPY_DEST); - if (!resource_manager_.UploadData(cmd, host_ib, host_indices.data(), ib_size)) { - ++submission_debug_stats_.index_upload_failure_count; - return false; - } - resource_tracker_.TransitionBarrier(cmd, host_ib, D3D12_RESOURCE_STATE_INDEX_BUFFER); - - D3D12_INDEX_BUFFER_VIEW ibv = {}; - ibv.BufferLocation = host_ib->GetGPUVirtualAddress(); - ibv.SizeInBytes = ib_size; - ibv.Format = DXGI_FORMAT_R16_UINT; - cmd->IASetIndexBuffer(&ibv); - - out_resources.indexed = true; - out_resources.draw_count = expanded_index_count; - out_resources.draw_start = 0; - vertex_count = command.count; - vertex_first = command.start; - } - } else { - const uint64_t index_end_u64 = - uint64_t(command.start) + uint64_t(command.count); - if (index_end_u64 > UINT32_MAX) { - ++submission_debug_stats_.index_count_overflow_count; - return false; - } - const uint32_t upload_index_count = command.count; - const uint32_t ib_size = SafeIndexBufferSize(upload_index_count); - const auto* guest_indices = memory_->TranslateVirtual(ss.index_buffer); - if (!guest_indices || ib_size == 0) { - ++submission_debug_stats_.index_data_unavailable_count; - return false; - } - - std::vector host_indices(upload_index_count); - uint32_t min_index = UINT32_MAX; - uint32_t max_index = 0; - for (uint32_t i = 0; i < upload_index_count; ++i) { - const uint16_t value = ByteSwap16(guest_indices[command.start + i]); - host_indices[i] = value; - min_index = std::min(min_index, value); - max_index = std::max(max_index, value); - } - vertex_first = min_index == UINT32_MAX ? 0u : min_index; - vertex_count = max_index >= vertex_first ? (max_index - vertex_first + 1u) : 0u; - - if (IsQuadListPrimitive(command.primitive_type)) { - const uint32_t quad_count = command.count / 4; - if (quad_count == 0) { - ++submission_debug_stats_.invalid_index_buffer_count; - return false; - } - - std::vector quad_indices; - quad_indices.reserve(static_cast(quad_count) * 6); - for (uint32_t quad = 0; quad < quad_count; ++quad) { - const uint32_t base = command.start + quad * 4; - if (base + 3 >= host_indices.size()) { - break; - } - const uint16_t i0 = host_indices[base + 0]; - const uint16_t i1 = host_indices[base + 1]; - const uint16_t i2 = host_indices[base + 2]; - const uint16_t i3 = host_indices[base + 3]; - quad_indices.push_back(i0); - quad_indices.push_back(i1); - quad_indices.push_back(i2); - quad_indices.push_back(i0); - quad_indices.push_back(i2); - quad_indices.push_back(i3); - } - - if (quad_indices.empty()) { - ++submission_debug_stats_.invalid_index_buffer_count; - return false; - } - - host_indices = std::move(quad_indices); - out_resources.draw_count = static_cast(host_indices.size()); - out_resources.draw_start = 0; - - min_index = UINT32_MAX; - max_index = 0; - for (const uint16_t value : host_indices) { - min_index = std::min(min_index, value); - max_index = std::max(max_index, value); - } - vertex_first = min_index == UINT32_MAX ? 0u : min_index; - vertex_count = max_index >= vertex_first ? (max_index - vertex_first + 1u) : 0u; - } - - if (vertex_count == 0) { - ++submission_debug_stats_.zero_vertex_count; - return false; - } - - if (vertex_first != 0) { - for (uint16_t& value : host_indices) { - value = static_cast(value - vertex_first); - } - } - - const uint32_t upload_ib_size = - static_cast(host_indices.size() * sizeof(uint16_t)); - ID3D12Resource* host_ib = - resource_manager_.GetOrCreateBuffer(ss.index_buffer, upload_ib_size); - if (!host_ib) { - ++submission_debug_stats_.index_buffer_create_failure_count; - return false; - } - - resource_tracker_.TransitionBarrier(cmd, host_ib, D3D12_RESOURCE_STATE_COPY_DEST); - if (!resource_manager_.UploadData(cmd, host_ib, host_indices.data(), upload_ib_size)) { - ++submission_debug_stats_.index_upload_failure_count; - return false; - } - resource_tracker_.TransitionBarrier(cmd, host_ib, D3D12_RESOURCE_STATE_INDEX_BUFFER); - - D3D12_INDEX_BUFFER_VIEW ibv = {}; - ibv.BufferLocation = host_ib->GetGPUVirtualAddress(); - ibv.SizeInBytes = upload_ib_size; - ibv.Format = DXGI_FORMAT_R16_UINT; - cmd->IASetIndexBuffer(&ibv); - out_resources.indexed = true; - } - } else { - if (IsQuadListPrimitive(command.primitive_type)) { - const uint32_t quad_count = command.count / 4; - if (quad_count == 0) { - ++submission_debug_stats_.invalid_vertex_range_count; - return false; - } - const uint32_t expanded_index_count = quad_count * 6; - const uint32_t ib_size = SafeIndexBufferSize(expanded_index_count); - if (ib_size == 0) { - ++submission_debug_stats_.index_count_overflow_count; - return false; - } - - std::vector host_indices; - host_indices.reserve(expanded_index_count); - for (uint32_t quad = 0; quad < quad_count; ++quad) { - const uint32_t base_vertex = command.start + quad * 4; - if (base_vertex + 3 > UINT16_MAX) { - ++submission_debug_stats_.index_count_overflow_count; - return false; - } - const uint16_t i0 = static_cast(base_vertex + 0); - const uint16_t i1 = static_cast(base_vertex + 1); - const uint16_t i2 = static_cast(base_vertex + 2); - const uint16_t i3 = static_cast(base_vertex + 3); - host_indices.push_back(i0); - host_indices.push_back(i1); - host_indices.push_back(i2); - host_indices.push_back(i0); - host_indices.push_back(i2); - host_indices.push_back(i3); - } - - ID3D12Resource* host_ib = resource_manager_.GetOrCreateBuffer( - stream.buffer ^ 0x80000000u ^ command.start, ib_size); - if (!host_ib) { - ++submission_debug_stats_.index_buffer_create_failure_count; - return false; - } - - resource_tracker_.TransitionBarrier(cmd, host_ib, D3D12_RESOURCE_STATE_COPY_DEST); - if (!resource_manager_.UploadData(cmd, host_ib, host_indices.data(), ib_size)) { - ++submission_debug_stats_.index_upload_failure_count; - return false; - } - resource_tracker_.TransitionBarrier(cmd, host_ib, D3D12_RESOURCE_STATE_INDEX_BUFFER); - - D3D12_INDEX_BUFFER_VIEW ibv = {}; - ibv.BufferLocation = host_ib->GetGPUVirtualAddress(); - ibv.SizeInBytes = ib_size; - ibv.Format = DXGI_FORMAT_R16_UINT; - cmd->IASetIndexBuffer(&ibv); - out_resources.indexed = true; - out_resources.draw_count = expanded_index_count; - out_resources.draw_start = 0; - } - - const uint64_t vertex_count_u64 = uint64_t(command.count); - if (vertex_count_u64 > UINT32_MAX) { - return false; - } - vertex_count = static_cast(vertex_count_u64); - vertex_first = command.start; - } - - // --- Vertex streams --- - // We only bind stream 0 (the primary stream) for now — a complete implementation - // would iterate all kMaxStreams, but that is expensive and risks upload overflow. - if (vertex_count == 0) { - ++submission_debug_stats_.zero_vertex_count; - return false; - } - - const uint64_t guest_start_u64 = - uint64_t(stream.buffer) + uint64_t(stream.offset) + - uint64_t(vertex_first) * uint64_t(stream.stride); - if (guest_start_u64 > UINT32_MAX) { - ++submission_debug_stats_.invalid_vertex_range_count; - return false; - } - const uint32_t guest_start = static_cast(guest_start_u64); - if (!ValidGuestAddress(guest_start)) { - ++submission_debug_stats_.invalid_vertex_range_count; - return false; - } - - const uint32_t vb_size = SafeVertexBufferSize(vertex_count, stream.stride); - if (vb_size == 0) { - ++submission_debug_stats_.vertex_buffer_size_invalid_count; - return false; - } - - ID3D12Resource* host_vb = resource_manager_.GetOrCreateBuffer(guest_start, vb_size); - if (!host_vb) { - ++submission_debug_stats_.vertex_buffer_create_failure_count; - return false; - } - - const void* guest_data = memory_->TranslateVirtual(guest_start); - if (!guest_data) { - ++submission_debug_stats_.vertex_data_unavailable_count; - return false; - } - - resource_tracker_.TransitionBarrier(cmd, host_vb, D3D12_RESOURCE_STATE_COPY_DEST); - if (!resource_manager_.UploadData(cmd, host_vb, guest_data, vb_size)) { - ++submission_debug_stats_.vertex_upload_failure_count; - return false; - } - resource_tracker_.TransitionBarrier(cmd, host_vb, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); - - const auto vb_srv = resource_manager_.AllocateSRV(); - CreateRawBufferSRV(device_.Get(), host_vb, vb_size, vb_srv.cpu_handle); - - out_resources.valid = true; - out_resources.vertex_stride = stream.stride; - out_resources.vertex_buffer_size = vb_size; - out_resources.color_offset = GuessColorOffset(stream.stride); - out_resources.vertex_buffer_gpu = vb_srv.gpu_handle; - out_resources.topology = PrimitiveTypeToTopology(command.primitive_type); - out_resources.topology_type = PrimitiveTypeToTopologyType(command.primitive_type); - return true; -} - -// --------------------------------------------------------------------------- -// PSO hash -// --------------------------------------------------------------------------- -uint64_t D3D12Backend::MakePSOHash(DXGI_FORMAT rt_fmt, DXGI_FORMAT ds_fmt, - D3D12_PRIMITIVE_TOPOLOGY_TYPE topo, - bool soft_particle) const { - struct Key { uint32_t rt, ds, topo; uint8_t sp; }; - Key k = { (uint32_t)rt_fmt, (uint32_t)ds_fmt, (uint32_t)topo, static_cast(soft_particle ? 1u : 0u) }; - return FnvHash64(&k, sizeof(k)); -} - -#endif // _WIN32 - -// --------------------------------------------------------------------------- -// Shutdown -// --------------------------------------------------------------------------- -void D3D12Backend::Shutdown() { - if (!initialized_) return; - -#if defined(_WIN32) - WaitForGpu(); - - resource_tracker_.Reset(); - resource_manager_.Shutdown(); - shader_manager_.Shutdown(); - - output_texture_.Reset(); - output_rtv_heap_.Reset(); - - if (fence_event_) { - CloseHandle((HANDLE)fence_event_); - fence_event_ = nullptr; - } - for (auto& c : frame_contexts_) { - c.command_list.Reset(); - c.command_allocator.Reset(); - } - frame_contexts_.clear(); - graphics_queue_.Reset(); - fence_.Reset(); - root_signature_.Reset(); - device_.Reset(); - dxgi_factory_.Reset(); -#endif - - initialized_ = false; - executor_status_ = {}; -} - -// --------------------------------------------------------------------------- -// Device / command object creation -// --------------------------------------------------------------------------- -#if defined(_WIN32) -bool D3D12Backend::CreateDevice() { - if (device_) return true; - - UINT flags = 0; -#if defined(_DEBUG) - Microsoft::WRL::ComPtr dbg; - if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&dbg)))) { - dbg->EnableDebugLayer(); - flags |= DXGI_CREATE_FACTORY_DEBUG; - } -#endif - HRESULT hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(&dxgi_factory_)); - if (FAILED(hr)) return false; - - hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device_)); - if (FAILED(hr)) { - Microsoft::WRL::ComPtr warp; - hr = dxgi_factory_->EnumWarpAdapter(IID_PPV_ARGS(&warp)); - if (FAILED(hr)) return false; - hr = D3D12CreateDevice(warp.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device_)); - if (FAILED(hr)) return false; - } - REXLOG_INFO("D3D12Backend: device created 0x{:016X}", (uint64_t)device_.Get()); - return true; -} - -bool D3D12Backend::CreateCommandObjects(uint32_t num_frames) { - HRESULT hr; - if (!graphics_queue_) { - D3D12_COMMAND_QUEUE_DESC qd = {}; - qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - qd.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - hr = device_->CreateCommandQueue(&qd, IID_PPV_ARGS(&graphics_queue_)); - if (FAILED(hr)) return false; - } - - frame_contexts_.resize(num_frames); - for (uint32_t i = 0; i < num_frames; ++i) { - hr = device_->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, - IID_PPV_ARGS(&frame_contexts_[i].command_allocator)); - if (FAILED(hr)) return false; - hr = device_->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, - frame_contexts_[i].command_allocator.Get(), nullptr, - IID_PPV_ARGS(&frame_contexts_[i].command_list)); - if (FAILED(hr)) return false; - frame_contexts_[i].command_list->Close(); - } - - hr = device_->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence_)); - if (FAILED(hr)) return false; - current_fence_value_ = 0; - - fence_event_ = CreateEventA(nullptr, FALSE, FALSE, nullptr); - return fence_event_ != nullptr; -} - -bool D3D12Backend::CreateRootSignature() { - // Slot 0: 16 root constants (b0) - // Slot 1: SRV descriptor table (t0..t15) — visible to PS - // Slot 2: Sampler descriptor table (s0..s15) — visible to PS - D3D12_ROOT_PARAMETER params[2] = {}; - - params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; - params[0].Constants.ShaderRegister = 0; - params[0].Constants.RegisterSpace = 0; - params[0].Constants.Num32BitValues = sizeof(DrawRootConstants) / sizeof(uint32_t); - params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; - - D3D12_DESCRIPTOR_RANGE srv_range = {}; - srv_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - srv_range.NumDescriptors = 1; - srv_range.BaseShaderRegister = 0; - srv_range.RegisterSpace = 0; - srv_range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - params[1].DescriptorTable.NumDescriptorRanges = 1; - params[1].DescriptorTable.pDescriptorRanges = &srv_range; - params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; - - D3D12_ROOT_SIGNATURE_DESC desc = {}; - desc.NumParameters = 2; - desc.pParameters = params; - desc.NumStaticSamplers = 0; - desc.pStaticSamplers = nullptr; - desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; - - Microsoft::WRL::ComPtr blob, err; - if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &err))) - return false; - return SUCCEEDED(device_->CreateRootSignature( - 0, blob->GetBufferPointer(), blob->GetBufferSize(), - IID_PPV_ARGS(&root_signature_))); -} - -void D3D12Backend::WaitForGpu() { - if (!graphics_queue_ || !fence_ || !fence_event_) return; - ++current_fence_value_; - if (SUCCEEDED(graphics_queue_->Signal(fence_.Get(), current_fence_value_))) { - if (fence_->GetCompletedValue() < current_fence_value_) { - fence_->SetEventOnCompletion(current_fence_value_, (HANDLE)fence_event_); - WaitForSingleObject((HANDLE)fence_event_, INFINITE); - } - } -} -#endif // _WIN32 - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/d3d12_backend.h b/src/ac6_native_renderer/backends/d3d12_backend.h deleted file mode 100644 index 09670526..00000000 --- a/src/ac6_native_renderer/backends/d3d12_backend.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include "../render_device.h" -#include "../frame_scheduler.h" - -#include "d3d12_resource_manager.h" -#include "d3d12_resource_tracker.h" -#include "d3d12_shader_manager.h" - -#include -#include - -#if defined(_WIN32) -#include -#include -#include -#endif - -namespace ac6::renderer { - -// Experimental replay backend retained for research and targeted override work. -// The authoritative default presentation path remains the RexGlue backend. -class D3D12Backend final : public RenderDeviceBackend { - public: - BackendType GetType() const override { return BackendType::kD3D12; } - std::string_view GetName() const override { return "d3d12"; } - bool IsSupported() const override; - bool Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory) override; - bool InitializeShared(const NativeRendererConfig& config, rex::memory::Memory* memory, - ID3D12Device* device, ID3D12CommandQueue* queue); - bool SubmitExecutorFrame(const ReplayExecutorFrame& frame) override; - BackendExecutorStatus GetExecutorStatus() const override { return executor_status_; } - void Shutdown() override; - - // Phase 4: Returns the native output texture for swapchain blit. - // nullptr until a frame has been rendered. - ID3D12Resource* GetOutputTexture() const { return output_texture_.Get(); } - - private: - BackendExecutorStatus executor_status_{}; - bool initialized_ = false; - rex::memory::Memory* memory_ = nullptr; - -#if defined(_WIN32) - struct FrameContext { - Microsoft::WRL::ComPtr command_allocator; - Microsoft::WRL::ComPtr command_list; - uint64_t fence_value = 0; - }; - - struct DrawResources { - bool valid = false; - bool indexed = false; - uint32_t draw_count = 0; - uint32_t draw_start = 0; - uint32_t vertex_base_offset = 0; - uint32_t vertex_stride = 0; - uint32_t vertex_buffer_size = 0; - uint32_t color_offset = 0xFFFFFFFFu; - D3D12_GPU_DESCRIPTOR_HANDLE vertex_buffer_gpu{}; - D3D12_PRIMITIVE_TOPOLOGY topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - D3D12_PRIMITIVE_TOPOLOGY_TYPE topology_type = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - }; - - struct SubmissionDebugStats { - uint32_t draw_attempt_count = 0; - uint32_t draw_success_count = 0; - uint32_t draw_prepare_failure_count = 0; - uint32_t draw_pso_failure_count = 0; - uint32_t indexed_draw_count = 0; - uint32_t non_indexed_draw_count = 0; - uint32_t clear_command_count = 0; - uint32_t resolve_command_count = 0; - uint32_t invalid_stream_binding_count = 0; - uint32_t invalid_index_buffer_count = 0; - uint32_t index_count_overflow_count = 0; - uint32_t index_data_unavailable_count = 0; - uint32_t index_buffer_create_failure_count = 0; - uint32_t index_upload_failure_count = 0; - uint32_t zero_vertex_count = 0; - uint32_t invalid_vertex_range_count = 0; - uint32_t vertex_buffer_size_invalid_count = 0; - uint32_t vertex_buffer_create_failure_count = 0; - uint32_t vertex_data_unavailable_count = 0; - uint32_t vertex_upload_failure_count = 0; - }; - - Microsoft::WRL::ComPtr dxgi_factory_; - Microsoft::WRL::ComPtr device_; - Microsoft::WRL::ComPtr graphics_queue_; - - Microsoft::WRL::ComPtr fence_; - void* fence_event_ = nullptr; - uint64_t current_fence_value_ = 0; - - Microsoft::WRL::ComPtr root_signature_; - - // Phase 4: output render target (native renderer draws into this) - Microsoft::WRL::ComPtr output_texture_; - Microsoft::WRL::ComPtr output_rtv_heap_; - D3D12_CPU_DESCRIPTOR_HANDLE output_rtv_{}; - uint32_t output_width_ = 0; - uint32_t output_height_ = 0; - static constexpr DXGI_FORMAT kOutputFormat = DXGI_FORMAT_R8G8B8A8_UNORM; - - FrameScheduler frame_scheduler_; - std::vector frame_contexts_; - SubmissionDebugStats submission_debug_stats_{}; - - D3D12ResourceManager resource_manager_; - D3D12ResourceTracker resource_tracker_; - D3D12ShaderManager shader_manager_; - - // PSO state hash helper - uint64_t MakePSOHash(DXGI_FORMAT rt_fmt, DXGI_FORMAT ds_fmt, - D3D12_PRIMITIVE_TOPOLOGY_TYPE topo, bool soft_particle) const; - - // Ensure output texture is created at the right size - bool EnsureOutputTexture(uint32_t width, uint32_t height); - - bool CreateDevice(); - bool CreateCommandObjects(uint32_t num_frames); - bool CreateRootSignature(); - void WaitForGpu(); - - // Phase 3 helpers - void DispatchPassCommands(ID3D12GraphicsCommandList* cmd, - const ReplayExecutorPassPacket& pass, - uint32_t slot); - bool PrepareDrawResources(ID3D12GraphicsCommandList* cmd, - const ReplayExecutorCommandPacket& command, - uint32_t slot, - DrawResources& out_resources); -#endif -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/d3d12_resource_manager.cpp b/src/ac6_native_renderer/backends/d3d12_resource_manager.cpp deleted file mode 100644 index 5aed2a31..00000000 --- a/src/ac6_native_renderer/backends/d3d12_resource_manager.cpp +++ /dev/null @@ -1,258 +0,0 @@ -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include "d3d12_resource_manager.h" - -#include -#include - -namespace ac6::renderer { - -bool D3D12ResourceManager::Initialize(ID3D12Device* device, uint32_t max_frames) { - device_ = device; - max_frames_ = max_frames; - - D3D12_DESCRIPTOR_HEAP_DESC rtv_desc = {}; - rtv_desc.NumDescriptors = kMaxRtvDescriptors; - rtv_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - if (FAILED(device_->CreateDescriptorHeap(&rtv_desc, IID_PPV_ARGS(&rtv_heap_)))) return false; - rtv_size_ = device_->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); - - D3D12_DESCRIPTOR_HEAP_DESC dsv_desc = {}; - dsv_desc.NumDescriptors = kMaxDsvDescriptors; - dsv_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; - if (FAILED(device_->CreateDescriptorHeap(&dsv_desc, IID_PPV_ARGS(&dsv_heap_)))) return false; - dsv_size_ = device_->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV); - - D3D12_DESCRIPTOR_HEAP_DESC srv_desc = {}; - srv_desc.NumDescriptors = kMaxSrvDescriptors; - srv_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - srv_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - if (FAILED(device_->CreateDescriptorHeap(&srv_desc, IID_PPV_ARGS(&srv_heap_)))) return false; - srv_size_ = device_->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - - frame_contexts_.resize(max_frames); - for (uint32_t i = 0; i < max_frames; ++i) { - D3D12_HEAP_PROPERTIES upload_props = {}; - upload_props.Type = D3D12_HEAP_TYPE_UPLOAD; - - D3D12_RESOURCE_DESC buffer_desc = {}; - buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - buffer_desc.Width = kUploadBufferSize; - buffer_desc.Height = 1; - buffer_desc.DepthOrArraySize = 1; - buffer_desc.MipLevels = 1; - buffer_desc.Format = DXGI_FORMAT_UNKNOWN; - buffer_desc.SampleDesc.Count = 1; - buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - - if (FAILED(device_->CreateCommittedResource(&upload_props, D3D12_HEAP_FLAG_NONE, &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&frame_contexts_[i].upload_buffer)))) { - return false; - } - - D3D12_RANGE read_range = {0, 0}; - if (FAILED(frame_contexts_[i].upload_buffer->Map(0, &read_range, reinterpret_cast(&frame_contexts_[i].upload_ptr)))) { - return false; - } - } - - return true; -} - -void D3D12ResourceManager::Shutdown() { - for (auto& ctx : frame_contexts_) { - if (ctx.upload_buffer) { - ctx.upload_buffer->Unmap(0, nullptr); - } - } - frame_contexts_.clear(); - resource_cache_.clear(); - rtv_heap_.Reset(); - dsv_heap_.Reset(); - srv_heap_.Reset(); -} - -void D3D12ResourceManager::BeginFrame(uint32_t frame_index) { - current_frame_index_ = frame_index; - // Reset transient descriptors - rtv_ptr_ = 0; - dsv_ptr_ = 0; - srv_ptr_ = 0; - - FrameContext& ctx = frame_contexts_[current_frame_index_ % max_frames_]; - ctx.upload_offset = 0; - - // Simple LRU cleanup could go here -} - -ID3D12Resource* D3D12ResourceManager::GetOrCreateBuffer(uint32_t guest_address, uint32_t size, D3D12_RESOURCE_FLAGS flags) { - if (guest_address == 0) return nullptr; - - auto it = resource_cache_.find(guest_address); - if (it != resource_cache_.end()) { - if (it->second.size_bytes >= size) { - it->second.last_used_frame = current_frame_index_; - return it->second.resource.Get(); - } - REXLOG_INFO("Growing cached D3D12 buffer for guest address 0x{:08X} from {} to {} bytes", - guest_address, it->second.size_bytes, size); - resource_cache_.erase(it); - } - - if (size == 0) { - return nullptr; - } - - D3D12_HEAP_PROPERTIES heap_props = {}; - heap_props.Type = D3D12_HEAP_TYPE_DEFAULT; - - D3D12_RESOURCE_DESC desc = {}; - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Width = size; - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = flags; - - Microsoft::WRL::ComPtr resource; - if (FAILED(device_->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&resource)))) { - REXLOG_ERROR("Failed to create D3D12 buffer for guest address 0x{:08X}", guest_address); - return nullptr; - } - - resource_cache_[guest_address] = {resource, size, current_frame_index_}; - return resource.Get(); -} - -ID3D12Resource* D3D12ResourceManager::GetOrCreateTexture(uint32_t guest_address, const d3d::ShadowState& state) { - // In a real implementation, we would extract width, height, format from the fetch constant at guest_address - // For this scaffold realization, we use guest_address as the key. - (void)state; - - if (guest_address == 0) return nullptr; - - auto it = resource_cache_.find(guest_address); - if (it != resource_cache_.end()) { - it->second.last_used_frame = current_frame_index_; - return it->second.resource.Get(); - } - - // Placeholder texture creation - D3D12_RESOURCE_DESC desc = {}; - desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - desc.Width = 1024; // Mock - desc.Height = 1024; // Mock - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.SampleDesc.Count = 1; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; - - D3D12_HEAP_PROPERTIES heap_props = {}; - heap_props.Type = D3D12_HEAP_TYPE_DEFAULT; - - Microsoft::WRL::ComPtr resource; - if (FAILED(device_->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&resource)))) { - return nullptr; - } - - resource_cache_[guest_address] = {resource, 0, current_frame_index_}; - return resource.Get(); -} - -D3D12ResourceManager::ResourceView D3D12ResourceManager::AllocateRTV() { - uint32_t index = rtv_ptr_++; - D3D12_CPU_DESCRIPTOR_HANDLE cpu = rtv_heap_->GetCPUDescriptorHandleForHeapStart(); - cpu.ptr += index * rtv_size_; - return {cpu, {0}, index}; -} - -D3D12ResourceManager::ResourceView D3D12ResourceManager::AllocateDSV() { - uint32_t index = dsv_ptr_++; - D3D12_CPU_DESCRIPTOR_HANDLE cpu = dsv_heap_->GetCPUDescriptorHandleForHeapStart(); - cpu.ptr += index * dsv_size_; - return {cpu, {0}, index}; -} - -D3D12ResourceManager::ResourceView D3D12ResourceManager::AllocateSRV() { - uint32_t index = srv_ptr_++; - D3D12_CPU_DESCRIPTOR_HANDLE cpu = srv_heap_->GetCPUDescriptorHandleForHeapStart(); - D3D12_GPU_DESCRIPTOR_HANDLE gpu = srv_heap_->GetGPUDescriptorHandleForHeapStart(); - cpu.ptr += index * srv_size_; - gpu.ptr += index * srv_size_; - return {cpu, gpu, index}; -} - -bool D3D12ResourceManager::UploadData(ID3D12GraphicsCommandList* command_list, ID3D12Resource* destination, const void* data, uint64_t size, uint64_t destination_offset) { - if (!destination || !data || size == 0) return false; - - FrameContext& ctx = frame_contexts_[current_frame_index_ % max_frames_]; - - // Align offset to 256 bytes for good practice, though not strictly required for all buffer copies - uint32_t aligned_offset = (ctx.upload_offset + 255) & ~255; - if (aligned_offset + size > kUploadBufferSize) { - REXLOG_ERROR("Upload buffer overflow in frame {}", current_frame_index_); - return false; - } - - memcpy(ctx.upload_ptr + aligned_offset, data, size); - ctx.upload_offset = aligned_offset + static_cast(size); - - command_list->CopyBufferRegion(destination, destination_offset, ctx.upload_buffer.Get(), aligned_offset, size); - return true; -} - -DXGI_FORMAT D3D12ResourceManager::TranslateColorFormat(uint32_t guest_format) { - using namespace rex::graphics::xenos; - switch (static_cast(guest_format)) { - case ColorRenderTargetFormat::k_8_8_8_8: return DXGI_FORMAT_R8G8B8A8_UNORM; - case ColorRenderTargetFormat::k_8_8_8_8_GAMMA: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - case ColorRenderTargetFormat::k_2_10_10_10: return DXGI_FORMAT_R10G10B10A2_UNORM; - case ColorRenderTargetFormat::k_16_16_FLOAT: return DXGI_FORMAT_R16G16_FLOAT; - case ColorRenderTargetFormat::k_16_16_16_16_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; - case ColorRenderTargetFormat::k_32_FLOAT: return DXGI_FORMAT_R32_FLOAT; - case ColorRenderTargetFormat::k_32_32_FLOAT: return DXGI_FORMAT_R32G32_FLOAT; - default: return DXGI_FORMAT_R8G8B8A8_UNORM; - } -} - -DXGI_FORMAT D3D12ResourceManager::TranslateDepthFormat(uint32_t guest_format) { - using namespace rex::graphics::xenos; - switch (static_cast(guest_format)) { - case DepthRenderTargetFormat::kD24S8: return DXGI_FORMAT_D24_UNORM_S8_UINT; - case DepthRenderTargetFormat::kD24FS8: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; // Nearest - default: return DXGI_FORMAT_D24_UNORM_S8_UINT; - } -} - -DXGI_FORMAT D3D12ResourceManager::TranslateTextureFormat(uint32_t guest_format) { - using namespace rex::graphics::xenos; - switch (static_cast(guest_format)) { - case TextureFormat::k_8_8_8_8: return DXGI_FORMAT_R8G8B8A8_UNORM; - case TextureFormat::k_DXT1: return DXGI_FORMAT_BC1_UNORM; - case TextureFormat::k_DXT2_3: return DXGI_FORMAT_BC2_UNORM; - case TextureFormat::k_DXT4_5: return DXGI_FORMAT_BC3_UNORM; - case TextureFormat::k_16_16_16_16_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; - case TextureFormat::k_32_FLOAT: return DXGI_FORMAT_R32_FLOAT; - default: return DXGI_FORMAT_R8G8B8A8_UNORM; - } -} - -DXGI_FORMAT D3D12ResourceManager::TranslateVertexFormat(uint32_t guest_format) { - using namespace rex::graphics::xenos; - switch (static_cast(guest_format)) { - case VertexFormat::k_32_32_32_32_FLOAT: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case VertexFormat::k_32_32_32_FLOAT: return DXGI_FORMAT_R32G32B32_FLOAT; - case VertexFormat::k_32_32_FLOAT: return DXGI_FORMAT_R32G32_FLOAT; - case VertexFormat::k_32_FLOAT: return DXGI_FORMAT_R32_FLOAT; - case VertexFormat::k_16_16_16_16_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; - case VertexFormat::k_16_16_FLOAT: return DXGI_FORMAT_R16G16_FLOAT; - case VertexFormat::k_8_8_8_8: return DXGI_FORMAT_R8G8B8A8_UNORM; - default: return DXGI_FORMAT_R32G32B32A32_FLOAT; - } -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/d3d12_resource_manager.h b/src/ac6_native_renderer/backends/d3d12_resource_manager.h deleted file mode 100644 index 96915dfc..00000000 --- a/src/ac6_native_renderer/backends/d3d12_resource_manager.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#include "../types.h" -#include "../../d3d_state.h" - -namespace ac6::renderer { - -class D3D12ResourceManager { - public: - struct ResourceView { - D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle; - D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle; - uint32_t heap_index; - }; - - bool Initialize(ID3D12Device* device, uint32_t max_frames); - void Shutdown(); - - void BeginFrame(uint32_t frame_index); - - // Translation - ID3D12Resource* GetOrCreateBuffer(uint32_t guest_address, uint32_t size, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE); - ID3D12Resource* GetOrCreateTexture(uint32_t guest_address, const d3d::ShadowState& state); - - // Descriptor management - ResourceView AllocateRTV(); - ResourceView AllocateDSV(); - ResourceView AllocateSRV(); - ID3D12DescriptorHeap* GetSrvHeap() const { return srv_heap_.Get(); } - - // Data sync - bool UploadData(ID3D12GraphicsCommandList* command_list, ID3D12Resource* destination, const void* data, uint64_t size, uint64_t destination_offset = 0); - - // Format translation - DXGI_FORMAT TranslateColorFormat(uint32_t guest_format); - DXGI_FORMAT TranslateDepthFormat(uint32_t guest_format); - DXGI_FORMAT TranslateTextureFormat(uint32_t guest_format); - DXGI_FORMAT TranslateVertexFormat(uint32_t guest_format); - - private: - ID3D12Device* device_ = nullptr; - uint32_t max_frames_ = 0; - uint32_t current_frame_index_ = 0; - - struct CachedResource { - Microsoft::WRL::ComPtr resource; - uint64_t size_bytes = 0; - uint64_t last_used_frame = 0; - }; - - std::unordered_map resource_cache_; - - Microsoft::WRL::ComPtr rtv_heap_; - Microsoft::WRL::ComPtr dsv_heap_; - Microsoft::WRL::ComPtr srv_heap_; - - uint32_t rtv_ptr_ = 0; - uint32_t dsv_ptr_ = 0; - uint32_t srv_ptr_ = 0; - - uint32_t rtv_size_ = 0; - uint32_t dsv_size_ = 0; - uint32_t srv_size_ = 0; - - struct FrameContext { - Microsoft::WRL::ComPtr upload_buffer; - uint8_t* upload_ptr = nullptr; - uint32_t upload_offset = 0; - }; - std::vector frame_contexts_; - - static constexpr uint32_t kMaxRtvDescriptors = 1024; - static constexpr uint32_t kMaxDsvDescriptors = 256; - static constexpr uint32_t kMaxSrvDescriptors = 4096; - static constexpr uint32_t kUploadBufferSize = 16 * 1024 * 1024; // 16 MB per frame -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/d3d12_resource_tracker.cpp b/src/ac6_native_renderer/backends/d3d12_resource_tracker.cpp deleted file mode 100644 index 96946db2..00000000 --- a/src/ac6_native_renderer/backends/d3d12_resource_tracker.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "d3d12_resource_tracker.h" - -namespace ac6::renderer { - -void D3D12ResourceTracker::TrackResource(ID3D12Resource* resource, - D3D12_RESOURCE_STATES initial_state) { - if (!resource) return; - tracked_[resource] = {initial_state}; -} - -bool D3D12ResourceTracker::TransitionBarrier(ID3D12GraphicsCommandList* cmd_list, - ID3D12Resource* resource, - D3D12_RESOURCE_STATES target_state) { - if (!resource || !cmd_list) return false; - - auto it = tracked_.find(resource); - if (it == tracked_.end()) { - // First time seeing this resource — assume COMMON - tracked_[resource] = {D3D12_RESOURCE_STATE_COMMON}; - it = tracked_.find(resource); - } - - if (it->second.current_state == target_state) { - return false; // No transition needed - } - - D3D12_RESOURCE_BARRIER barrier = {}; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = resource; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = it->second.current_state; - barrier.Transition.StateAfter = target_state; - - cmd_list->ResourceBarrier(1, &barrier); - it->second.current_state = target_state; - return true; -} - -void D3D12ResourceTracker::FlushBarriers(ID3D12GraphicsCommandList* cmd_list) { - if (pending_barriers_.empty() || !cmd_list) return; - cmd_list->ResourceBarrier(static_cast(pending_barriers_.size()), - pending_barriers_.data()); - pending_barriers_.clear(); -} - -void D3D12ResourceTracker::Reset() { - tracked_.clear(); - pending_barriers_.clear(); -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/d3d12_resource_tracker.h b/src/ac6_native_renderer/backends/d3d12_resource_tracker.h deleted file mode 100644 index 907e4118..00000000 --- a/src/ac6_native_renderer/backends/d3d12_resource_tracker.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace ac6::renderer { - -// Tracks D3D12 resource states for automatic barrier generation. -class D3D12ResourceTracker { - public: - void TrackResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initial_state); - - // Returns true if a barrier was needed and appended. - bool TransitionBarrier(ID3D12GraphicsCommandList* cmd_list, - ID3D12Resource* resource, - D3D12_RESOURCE_STATES target_state); - - // Flush all pending barriers at once (batch mode). - void FlushBarriers(ID3D12GraphicsCommandList* cmd_list); - - void Reset(); - - private: - struct TrackedResource { - D3D12_RESOURCE_STATES current_state = D3D12_RESOURCE_STATE_COMMON; - }; - - std::unordered_map tracked_; - std::vector pending_barriers_; -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/d3d12_shader_manager.cpp b/src/ac6_native_renderer/backends/d3d12_shader_manager.cpp deleted file mode 100644 index c2bdd26d..00000000 --- a/src/ac6_native_renderer/backends/d3d12_shader_manager.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include "d3d12_shader_manager.h" - -#include - -#include - -#pragma comment(lib, "d3dcompiler.lib") - -namespace ac6::renderer { - -// --------------------------------------------------------------------------- -// Embedded HLSL shaders (compiled inline at runtime, no external files) -// --------------------------------------------------------------------------- - -// Generic vertex-pulling shader. It reads stream 0 directly as a raw byte -// buffer, byte-swaps guest big-endian dwords, and interprets the first float3 -// plus an optional fourth component as position data. Pre-transformed menu / UI vertices are projected from the -// captured viewport into clip space; already-transformed clip-space vertices -// are passed through. -static constexpr const char kPassthroughVS[] = R"HLSL( -cbuffer DrawConstants : register(b0) { - uint vertex_base_offset; - uint vertex_stride; - uint vertex_buffer_size; - uint viewport_x; - uint viewport_y; - uint viewport_width; - uint viewport_height; - uint color_offset; - uint flags; -}; - -ByteAddressBuffer g_vertex_buffer : register(t0); - -struct VSOut { - float4 pos : SV_Position; - float4 color : COLOR0; - float2 uv : TEXCOORD0; -}; - -uint ByteSwap32(uint v) { - return (v << 24) | ((v << 8) & 0x00FF0000u) | ((v >> 8) & 0x0000FF00u) | (v >> 24); -} - -bool CanLoadDword(uint byte_offset) { - return (byte_offset & 3u) == 0u && (byte_offset + 4u) <= vertex_buffer_size; -} - -float LoadGuestFloat(uint byte_offset, float fallback_value) { - if (!CanLoadDword(byte_offset)) { - return fallback_value; - } - return asfloat(ByteSwap32(g_vertex_buffer.Load(byte_offset))); -} - -float4 LoadGuestColor(uint byte_offset, float4 fallback_value) { - if (!CanLoadDword(byte_offset)) { - return fallback_value; - } - uint packed = ByteSwap32(g_vertex_buffer.Load(byte_offset)); - return float4( - float((packed >> 16) & 0xFFu) / 255.0f, - float((packed >> 8) & 0xFFu) / 255.0f, - float((packed >> 0) & 0xFFu) / 255.0f, - max(float((packed >> 24) & 0xFFu) / 255.0f, 1.0f / 255.0f)); -} - -VSOut main(uint vid : SV_VertexID) { - uint stride = vertex_stride; - uint byte_offset = vertex_base_offset + vid * stride; - float4 default_color = float4(1.0f, 1.0f, 1.0f, 1.0f); - - // Unsupported stream layouts should draw nothing rather than issue invalid - // raw-buffer reads that can remove the device. - if (stride == 0u || byte_offset >= vertex_buffer_size) { - VSOut empty; - empty.pos = float4(0.0f, 0.0f, 0.0f, 0.0f); - empty.color = default_color; - empty.uv = float2(0.0f, 0.0f); - return empty; - } - - float4 raw_pos = float4( - LoadGuestFloat(byte_offset + 0, 0.0f), - LoadGuestFloat(byte_offset + 4, 0.0f), - LoadGuestFloat(byte_offset + 8, 0.0f), - 1.0f); - if (stride >= 16u && CanLoadDword(byte_offset + 12u)) { - float candidate_w = LoadGuestFloat(byte_offset + 12, 1.0f); - if (candidate_w == candidate_w && abs(candidate_w) < 10000.0f) { - raw_pos.w = candidate_w; - } - } - bool has_viewport = viewport_width != 0 && viewport_height != 0; - bool looks_screen_space = - has_viewport && - (abs(raw_pos.x) > 2.5f || abs(raw_pos.y) > 2.5f || raw_pos.w > 2.0f || raw_pos.w < 0.0f); - - VSOut o; - if (looks_screen_space) { - float2 viewport_size = float2(max(viewport_width, 1u), max(viewport_height, 1u)); - float2 viewport_origin = float2(viewport_x, viewport_y); - float2 pixel = raw_pos.xy - viewport_origin; - float2 ndc = float2( - pixel.x / viewport_size.x * 2.0f - 1.0f, - 1.0f - pixel.y / viewport_size.y * 2.0f); - o.pos = float4(ndc, saturate(raw_pos.z), 1.0f); - } else { - float w = abs(raw_pos.w) > 1.0e-6f ? raw_pos.w : 1.0f; - o.pos = float4(raw_pos.xyz, w); - } - - if (color_offset != 0xFFFFFFFFu && (color_offset + 4u) <= stride) { - o.color = LoadGuestColor(byte_offset + color_offset, default_color); - } else { - o.color = default_color; - } - o.uv = raw_pos.xy; - return o; -} -)HLSL"; - -// Simple pixel shader: output the pulled vertex color. -static constexpr const char kPassthroughPS[] = R"HLSL( -struct PSIn { - float4 pos : SV_Position; - float4 color : COLOR0; - float2 uv : TEXCOORD0; -}; - -float4 main(PSIn i) : SV_Target { - return i.color; -} -)HLSL"; - -// Diagnostic variant for post-process / present-tagged passes. This makes -// successful fullscreen-style replay obvious even before real texture sampling -// is implemented. -static constexpr const char kSoftParticlePS[] = R"HLSL( -struct PSIn { - float4 pos : SV_Position; - float4 color : COLOR0; - float2 uv : TEXCOORD0; -}; - -float4 main(PSIn i) : SV_Target { - float2 pos_band = frac(abs(i.pos.xy) * 0.015625f); - float2 uv_band = frac(abs(i.uv.xy) * 0.001953125f); - float3 debug_color = float3( - max(pos_band.x, 0.2f), - max(pos_band.y, 0.2f), - max(frac(uv_band.x + uv_band.y), 0.35f)); - return float4(debug_color, 1.0f); -} -)HLSL"; - -// --------------------------------------------------------------------------- - -/*static*/ Microsoft::WRL::ComPtr D3D12ShaderManager::CompileHLSL( - const char* source, const char* entry_point, const char* target) { - UINT flags = 0; -#if defined(_DEBUG) - flags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; -#else - flags = D3DCOMPILE_OPTIMIZATION_LEVEL3; -#endif - - Microsoft::WRL::ComPtr blob; - Microsoft::WRL::ComPtr error; - HRESULT hr = D3DCompile(source, strlen(source), nullptr, nullptr, nullptr, - entry_point, target, flags, 0, &blob, &error); - if (FAILED(hr)) { - if (error) { - REXLOG_ERROR("Shader compile error [{}]: {}", entry_point, - static_cast(error->GetBufferPointer())); - } else { - REXLOG_ERROR("Shader compile error [{}]: hr=0x{:08X}", entry_point, - static_cast(hr)); - } - return nullptr; - } - return blob; -} - -bool D3D12ShaderManager::Initialize(ID3D12Device* device) { - device_ = device; - - REXLOG_INFO("D3D12ShaderManager: Compiling passthrough VS..."); - passthrough_vs_ = CompileHLSL(kPassthroughVS, "main", "vs_5_0"); - if (!passthrough_vs_) { - REXLOG_ERROR("D3D12ShaderManager: Failed to compile passthrough VS"); - return false; - } - - REXLOG_INFO("D3D12ShaderManager: Compiling passthrough PS..."); - passthrough_ps_ = CompileHLSL(kPassthroughPS, "main", "ps_5_0"); - if (!passthrough_ps_) { - REXLOG_ERROR("D3D12ShaderManager: Failed to compile passthrough PS"); - return false; - } - - REXLOG_INFO("D3D12ShaderManager: Compiling soft-particle PS..."); - soft_particle_ps_ = CompileHLSL(kSoftParticlePS, "main", "ps_5_0"); - if (!soft_particle_ps_) { - REXLOG_WARN("D3D12ShaderManager: Soft-particle PS compile failed, using passthrough"); - soft_particle_ps_ = passthrough_ps_; - } - - REXLOG_INFO("D3D12ShaderManager: All shaders compiled successfully"); - return true; -} - -void D3D12ShaderManager::Shutdown() { - pso_cache_.clear(); - passthrough_vs_.Reset(); - passthrough_ps_.Reset(); - soft_particle_ps_.Reset(); - device_ = nullptr; -} - -ID3D12PipelineState* D3D12ShaderManager::GetOrCreatePSO( - uint64_t state_hash, - ID3D12RootSignature* root_sig, - DXGI_FORMAT rt_format, - DXGI_FORMAT ds_format, - D3D12_PRIMITIVE_TOPOLOGY_TYPE topology_type, - bool use_soft_particle_ps) { - auto it = pso_cache_.find(state_hash); - if (it != pso_cache_.end()) { - return it->second.pso.Get(); - } - - if (!device_ || !passthrough_vs_ || !passthrough_ps_) { - return nullptr; - } - - ID3DBlob* ps = use_soft_particle_ps ? soft_particle_ps_.Get() : passthrough_ps_.Get(); - - D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = {}; - desc.pRootSignature = root_sig; - desc.VS = {passthrough_vs_->GetBufferPointer(), passthrough_vs_->GetBufferSize()}; - desc.PS = {ps->GetBufferPointer(), ps->GetBufferSize()}; - desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; - desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; - desc.RasterizerState.DepthClipEnable = TRUE; - desc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; - desc.BlendState.RenderTarget[0].BlendEnable = use_soft_particle_ps ? TRUE : FALSE; - desc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; - desc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; - desc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; - desc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE; - desc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO; - desc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; - desc.DepthStencilState.DepthEnable = (ds_format != DXGI_FORMAT_UNKNOWN) ? TRUE : FALSE; - desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; - desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; - desc.SampleMask = UINT_MAX; - desc.PrimitiveTopologyType = topology_type; - desc.NumRenderTargets = (rt_format != DXGI_FORMAT_UNKNOWN) ? 1 : 0; - if (rt_format != DXGI_FORMAT_UNKNOWN) { - desc.RTVFormats[0] = rt_format; - } - desc.DSVFormat = ds_format; - desc.SampleDesc.Count = 1; - - Microsoft::WRL::ComPtr pso; - HRESULT hr = device_->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(&pso)); - if (FAILED(hr)) { - REXLOG_ERROR( - "D3D12ShaderManager: CreateGraphicsPipelineState failed 0x{:08X} hash=0x{:016X}", - static_cast(hr), state_hash); - return nullptr; - } - - auto& entry = pso_cache_[state_hash]; - entry.pso = pso; - return entry.pso.Get(); -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/d3d12_shader_manager.h b/src/ac6_native_renderer/backends/d3d12_shader_manager.h deleted file mode 100644 index bd545dba..00000000 --- a/src/ac6_native_renderer/backends/d3d12_shader_manager.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include - -namespace ac6::renderer { - -// Manages native passthrough shaders and a PSO cache for the native renderer. -// Uses D3DCompile to compile embedded HLSL source inline at startup — no -// external shader files required. -class D3D12ShaderManager { - public: - bool Initialize(ID3D12Device* device); - void Shutdown(); - - // Returns the shared passthrough vertex shader blob (compiled once). - ID3DBlob* GetPassthroughVS() const { return passthrough_vs_.Get(); } - - // Returns the shared solid-color pixel shader blob (compiled once). - ID3DBlob* GetPassthroughPS() const { return passthrough_ps_.Get(); } - - // Returns the soft-particle / high-precision effect pixel shader blob. - ID3DBlob* GetSoftParticlePS() const { return soft_particle_ps_.Get(); } - - // Get or create a PSO for the given state hash. - // root_sig must already be created on the device. - // Returns nullptr on failure. - ID3D12PipelineState* GetOrCreatePSO( - uint64_t state_hash, - ID3D12RootSignature* root_sig, - DXGI_FORMAT rt_format, - DXGI_FORMAT ds_format, - D3D12_PRIMITIVE_TOPOLOGY_TYPE topology_type, - bool use_soft_particle_ps = false); - - // Backwards compat stubs (unused but keep old callers linking) - ID3DBlob* GetVertexShader(uint32_t /*guest_hash*/) { return passthrough_vs_.Get(); } - ID3DBlob* GetPixelShader(uint32_t /*guest_hash*/) { return passthrough_ps_.Get(); } - ID3DBlob* GetGenericSoftParticleShader() { return soft_particle_ps_.Get(); } - - private: - ID3D12Device* device_ = nullptr; - - Microsoft::WRL::ComPtr passthrough_vs_; - Microsoft::WRL::ComPtr passthrough_ps_; - Microsoft::WRL::ComPtr soft_particle_ps_; - - struct CachedPSO { - Microsoft::WRL::ComPtr pso; - }; - std::unordered_map pso_cache_; - - static Microsoft::WRL::ComPtr CompileHLSL( - const char* source, const char* entry_point, const char* target); -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/metal_backend.cpp b/src/ac6_native_renderer/backends/metal_backend.cpp deleted file mode 100644 index a1f5ebe6..00000000 --- a/src/ac6_native_renderer/backends/metal_backend.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "ac6_native_renderer/backends/metal_backend.h" - -#include - -namespace ac6::renderer { - -bool MetalBackend::IsSupported() const { -#if defined(__APPLE__) - return true; -#else - return false; -#endif -} - -bool MetalBackend::Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory) { - (void)config; - (void)memory; - if (initialized_) { - return true; - } - executor_status_ = {}; - executor_status_.initialized = true; - initialized_ = true; - REXLOG_INFO("AC6 native renderer Metal backend initialized (scaffold)"); - return true; -} - -bool MetalBackend::SubmitExecutorFrame(const ReplayExecutorFrame& frame) { - if (!initialized_) { - return false; - } - executor_status_.initialized = true; - executor_status_.frame_valid = frame.summary.valid; - executor_status_.frame_index = frame.summary.frame_index; - executor_status_.submitted_pass_count = frame.summary.pass_count; - executor_status_.submitted_command_count = frame.summary.command_count; - executor_status_.graphics_pass_count = frame.summary.graphics_pass_count; - executor_status_.async_compute_pass_count = - frame.summary.async_compute_pass_count; - executor_status_.copy_pass_count = frame.summary.copy_pass_count; - executor_status_.present_pass_count = frame.summary.present_pass_count; - executor_status_.resource_translation_pass_count = - frame.summary.resource_translation_pass_count; - executor_status_.pipeline_state_pass_count = - frame.summary.pipeline_state_pass_count; - executor_status_.descriptor_setup_pass_count = - frame.summary.descriptor_setup_pass_count; - return true; -} - -void MetalBackend::Shutdown() { - if (!initialized_) { - return; - } - executor_status_ = {}; - initialized_ = false; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/metal_backend.h b/src/ac6_native_renderer/backends/metal_backend.h deleted file mode 100644 index 323544ae..00000000 --- a/src/ac6_native_renderer/backends/metal_backend.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "ac6_native_renderer/render_device.h" - -namespace ac6::renderer { - -class MetalBackend final : public RenderDeviceBackend { - public: - BackendType GetType() const override { return BackendType::kMetal; } - std::string_view GetName() const override { return "metal"; } - bool IsSupported() const override; - bool Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory) override; - bool InitializeShared(const NativeRendererConfig& config, rex::memory::Memory* memory, ID3D12Device* device, ID3D12CommandQueue* queue) override { return false; } - bool SubmitExecutorFrame(const ReplayExecutorFrame& frame) override; - BackendExecutorStatus GetExecutorStatus() const override { return executor_status_; } - void Shutdown() override; - - private: - BackendExecutorStatus executor_status_{}; - bool initialized_ = false; -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/vulkan_backend.cpp b/src/ac6_native_renderer/backends/vulkan_backend.cpp deleted file mode 100644 index b96fa09d..00000000 --- a/src/ac6_native_renderer/backends/vulkan_backend.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "ac6_native_renderer/backends/vulkan_backend.h" - -#include - -namespace ac6::renderer { - -bool VulkanBackend::IsSupported() const { -#if defined(__linux__) - return true; -#else - return false; -#endif -} - -bool VulkanBackend::Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory) { - (void)config; - (void)memory; - if (initialized_) { - return true; - } - executor_status_ = {}; - executor_status_.initialized = true; - initialized_ = true; - REXLOG_INFO("AC6 native renderer Vulkan backend initialized (scaffold)"); - return true; -} - -bool VulkanBackend::SubmitExecutorFrame(const ReplayExecutorFrame& frame) { - if (!initialized_) { - return false; - } - executor_status_.initialized = true; - executor_status_.frame_valid = frame.summary.valid; - executor_status_.frame_index = frame.summary.frame_index; - executor_status_.submitted_pass_count = frame.summary.pass_count; - executor_status_.submitted_command_count = frame.summary.command_count; - executor_status_.graphics_pass_count = frame.summary.graphics_pass_count; - executor_status_.async_compute_pass_count = - frame.summary.async_compute_pass_count; - executor_status_.copy_pass_count = frame.summary.copy_pass_count; - executor_status_.present_pass_count = frame.summary.present_pass_count; - executor_status_.resource_translation_pass_count = - frame.summary.resource_translation_pass_count; - executor_status_.pipeline_state_pass_count = - frame.summary.pipeline_state_pass_count; - executor_status_.descriptor_setup_pass_count = - frame.summary.descriptor_setup_pass_count; - return true; -} - -void VulkanBackend::Shutdown() { - if (!initialized_) { - return; - } - executor_status_ = {}; - initialized_ = false; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/backends/vulkan_backend.h b/src/ac6_native_renderer/backends/vulkan_backend.h deleted file mode 100644 index f0863fc8..00000000 --- a/src/ac6_native_renderer/backends/vulkan_backend.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../render_device.h" - -namespace ac6::renderer { - -class VulkanBackend final : public RenderDeviceBackend { - public: - BackendType GetType() const override { return BackendType::kVulkan; } - std::string_view GetName() const override { return "vulkan"; } - bool IsSupported() const override; - bool Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory) override; - bool InitializeShared(const NativeRendererConfig& config, rex::memory::Memory* memory, ID3D12Device* device, ID3D12CommandQueue* queue) override { return false; } - bool SubmitExecutorFrame(const ReplayExecutorFrame& frame) override; - BackendExecutorStatus GetExecutorStatus() const override { return executor_status_; } - void Shutdown() override; - - private: - BackendExecutorStatus executor_status_{}; - bool initialized_ = false; -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/execution_plan.cpp b/src/ac6_native_renderer/execution_plan.cpp deleted file mode 100644 index a9e32641..00000000 --- a/src/ac6_native_renderer/execution_plan.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "execution_plan.h" - -#include -#include - -namespace ac6::renderer { -namespace { - -ExecutionCommandCategory ToExecutionCommandCategory(ObservedCommandType type) { - switch (type) { - case ObservedCommandType::kDraw: - return ExecutionCommandCategory::kDraw; - case ObservedCommandType::kClear: - return ExecutionCommandCategory::kClear; - case ObservedCommandType::kResolve: - return ExecutionCommandCategory::kResolve; - default: - return ExecutionCommandCategory::kNone; - } -} - -ExecutionCommandPacket BuildExecutionCommandPacket(const ReplayCommandDesc& command, - uint32_t replay_pass_index, - uint32_t replay_command_index) { - return ExecutionCommandPacket{ - .category = ToExecutionCommandCategory(command.type), - .source_type = command.type, - .replay_pass_index = replay_pass_index, - .replay_command_index = replay_command_index, - .sequence = command.sequence, - .draw_kind = command.draw_kind, - .primitive_type = command.primitive_type, - .start = command.start, - .count = command.count, - .flags = command.flags, - .rect_count = command.rect_count, - .captured_rect_count = command.captured_rect_count, - .color = command.color, - .stencil = command.stencil, - .depth = command.depth, - .texture_count = command.texture_count, - .stream_count = command.stream_count, - .sampler_count = command.sampler_count, - .fetch_constant_count = command.fetch_constant_count, - .render_target_0 = command.render_target_0, - .depth_stencil = command.depth_stencil, - .viewport_x = command.viewport_x, - .viewport_y = command.viewport_y, - .viewport_width = command.viewport_width, - .viewport_height = command.viewport_height, - .shadow_state = command.shadow_state, - }; -} - -void AccumulateResourceRequirements(ExecutionResourceRequirements& resources, - const ExecutionCommandPacket& command) { - resources.needs_render_target |= command.render_target_0 != 0; - resources.needs_depth_stencil |= command.depth_stencil != 0; - resources.max_texture_count = - std::max(resources.max_texture_count, command.texture_count); - resources.max_stream_count = - std::max(resources.max_stream_count, command.stream_count); - resources.max_sampler_count = - std::max(resources.max_sampler_count, command.sampler_count); - resources.max_fetch_constant_count = - std::max(resources.max_fetch_constant_count, command.fetch_constant_count); - resources.max_viewport_width = - std::max(resources.max_viewport_width, command.viewport_width); - resources.max_viewport_height = - std::max(resources.max_viewport_height, command.viewport_height); - - if (command.category != ExecutionCommandCategory::kDraw) { - return; - } - - resources.needs_vertex_streams |= command.stream_count != 0; - resources.needs_index_buffer |= command.draw_kind != ac6::d3d::DrawCallKind::kPrimitive; - resources.needs_textures |= command.texture_count != 0; - resources.needs_samplers |= command.sampler_count != 0; - resources.needs_fetch_constants |= command.fetch_constant_count != 0; -} - -ExecutionPassPacket BuildExecutionPassPacket(const ReplayPassDesc& replay_pass, - uint32_t replay_pass_index, - const NativeFramePlan& frame_plan) { - ExecutionPassPacket pass_packet; - pass_packet.name = replay_pass.name; - pass_packet.role = replay_pass.role; - pass_packet.replay_pass_valid = true; - pass_packet.replay_pass_index = replay_pass_index; - pass_packet.source_pass_valid = replay_pass.source_pass_valid; - pass_packet.source_pass_index = replay_pass.source_pass_index; - pass_packet.draw_count = replay_pass.draw_count; - pass_packet.clear_count = replay_pass.clear_count; - pass_packet.resolve_count = replay_pass.resolve_count; - pass_packet.render_target_0 = replay_pass.render_target_0; - pass_packet.depth_stencil = replay_pass.depth_stencil; - pass_packet.output_width = replay_pass.viewport_width; - pass_packet.output_height = replay_pass.viewport_height; - pass_packet.selected_for_present = replay_pass.selected_for_present; - pass_packet.commands.reserve(replay_pass.commands.size()); - - if (pass_packet.role == ReplayPassRole::kPresent) { - pass_packet.output_width = frame_plan.output_width; - pass_packet.output_height = frame_plan.output_height; - } - - for (uint32_t i = 0; i < replay_pass.commands.size(); ++i) { - ExecutionCommandPacket command_packet = - BuildExecutionCommandPacket(replay_pass.commands[i], replay_pass_index, i); - AccumulateResourceRequirements(pass_packet.resources, command_packet); - pass_packet.commands.push_back(std::move(command_packet)); - } - - pass_packet.resources.needs_render_target |= pass_packet.render_target_0 != 0; - pass_packet.resources.needs_depth_stencil |= pass_packet.depth_stencil != 0; - pass_packet.resources.max_viewport_width = - std::max(pass_packet.resources.max_viewport_width, pass_packet.output_width); - pass_packet.resources.max_viewport_height = - std::max(pass_packet.resources.max_viewport_height, pass_packet.output_height); - return pass_packet; -} - -void AccumulateSummary(ExecutionFrameSummary& summary, - const ExecutionPassPacket& pass_packet) { - ++summary.pass_count; - summary.command_count += static_cast(pass_packet.commands.size()); - summary.draw_packet_count += pass_packet.draw_count; - summary.clear_packet_count += pass_packet.clear_count; - summary.resolve_packet_count += pass_packet.resolve_count; - if (pass_packet.role == ReplayPassRole::kPresent) { - ++summary.present_pass_count; - summary.has_present_pass = true; - } - summary.valid = summary.pass_count != 0; -} - -} // namespace - -const char* ToString(ExecutionCommandCategory category) { - switch (category) { - case ExecutionCommandCategory::kDraw: - return "draw"; - case ExecutionCommandCategory::kClear: - return "clear"; - case ExecutionCommandCategory::kResolve: - return "resolve"; - case ExecutionCommandCategory::kNone: - default: - return "none"; - } -} - -ExecutionFramePlan ExecutionPlanBuilder::BuildBootstrapPlan(uint64_t frame_index) const { - ExecutionFramePlan plan; - plan.summary.frame_index = frame_index; - - ExecutionPassPacket bootstrap_pass; - bootstrap_pass.name = "ac6.execution.bootstrap"; - bootstrap_pass.role = ReplayPassRole::kBootstrap; - - AccumulateSummary(plan.summary, bootstrap_pass); - plan.passes.push_back(std::move(bootstrap_pass)); - return plan; -} - -ExecutionFramePlan ExecutionPlanBuilder::Build( - const ReplayFrame& replay_frame, const NativeFramePlan& frame_plan) const { - ExecutionFramePlan plan; - plan.summary.frame_index = replay_frame.summary.frame_index; - plan.summary.output_width = replay_frame.summary.output_width; - plan.summary.output_height = replay_frame.summary.output_height; - - if (!replay_frame.summary.valid || replay_frame.passes.empty()) { - return plan; - } - - plan.passes.reserve(replay_frame.passes.size()); - for (uint32_t i = 0; i < replay_frame.passes.size(); ++i) { - ExecutionPassPacket pass_packet = - BuildExecutionPassPacket(replay_frame.passes[i], i, frame_plan); - AccumulateSummary(plan.summary, pass_packet); - plan.passes.push_back(std::move(pass_packet)); - } - - plan.summary.valid = - plan.summary.pass_count != 0 && - (!frame_plan.valid || - (plan.summary.output_width != 0 && plan.summary.output_height != 0)); - return plan; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/execution_plan.h b/src/ac6_native_renderer/execution_plan.h deleted file mode 100644 index fcc5de8d..00000000 --- a/src/ac6_native_renderer/execution_plan.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "frame_plan.h" -#include "replay_ir.h" - -namespace ac6::renderer { - -enum class ExecutionCommandCategory : uint8_t { - kNone = 0, - kDraw = 1, - kClear = 2, - kResolve = 3, -}; - -struct ExecutionCommandPacket { - ExecutionCommandCategory category = ExecutionCommandCategory::kNone; - ObservedCommandType source_type = ObservedCommandType::kDraw; - uint32_t replay_pass_index = 0; - uint32_t replay_command_index = 0; - uint32_t sequence = 0; - ac6::d3d::DrawCallKind draw_kind = ac6::d3d::DrawCallKind::kIndexed; - uint32_t primitive_type = 0; - uint32_t start = 0; - uint32_t count = 0; - uint32_t flags = 0; - uint32_t rect_count = 0; - uint32_t captured_rect_count = 0; - uint32_t color = 0; - uint32_t stencil = 0; - float depth = 1.0f; - uint32_t texture_count = 0; - uint32_t stream_count = 0; - uint32_t sampler_count = 0; - uint32_t fetch_constant_count = 0; - uint32_t render_target_0 = 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; - ac6::d3d::ShadowState shadow_state{}; -}; - -struct ExecutionResourceRequirements { - bool needs_render_target = false; - bool needs_depth_stencil = false; - bool needs_vertex_streams = false; - bool needs_index_buffer = false; - bool needs_textures = false; - bool needs_samplers = false; - bool needs_fetch_constants = false; - uint32_t max_texture_count = 0; - uint32_t max_stream_count = 0; - uint32_t max_sampler_count = 0; - uint32_t max_fetch_constant_count = 0; - uint32_t max_viewport_width = 0; - uint32_t max_viewport_height = 0; -}; - -struct ExecutionPassPacket { - std::string name; - ReplayPassRole role = ReplayPassRole::kUnknown; - bool replay_pass_valid = false; - uint32_t replay_pass_index = 0; - bool source_pass_valid = false; - uint32_t source_pass_index = 0; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; - uint32_t render_target_0 = 0; - uint32_t depth_stencil = 0; - uint32_t output_width = 0; - uint32_t output_height = 0; - bool selected_for_present = false; - ExecutionResourceRequirements resources{}; - std::vector commands; -}; - -struct ExecutionFrameSummary { - bool valid = false; - uint64_t frame_index = 0; - uint32_t pass_count = 0; - uint32_t command_count = 0; - uint32_t draw_packet_count = 0; - uint32_t clear_packet_count = 0; - uint32_t resolve_packet_count = 0; - uint32_t present_pass_count = 0; - uint32_t output_width = 0; - uint32_t output_height = 0; - bool has_present_pass = false; -}; - -struct ExecutionFramePlan { - ExecutionFrameSummary summary{}; - std::vector passes; -}; - -class ExecutionPlanBuilder { - public: - ExecutionFramePlan BuildBootstrapPlan(uint64_t frame_index) const; - ExecutionFramePlan Build(const ReplayFrame& replay_frame, - const NativeFramePlan& frame_plan) const; -}; - -const char* ToString(ExecutionCommandCategory category); - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/frame_plan.cpp b/src/ac6_native_renderer/frame_plan.cpp deleted file mode 100644 index ac2d5e9f..00000000 --- a/src/ac6_native_renderer/frame_plan.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "frame_plan.h" - -namespace ac6::renderer { -namespace { - -PlannedPassReference MakeReference(const ObservedPassDesc& pass, uint32_t pass_index) { - return PlannedPassReference{ - .valid = true, - .pass_index = pass_index, - .kind = pass.kind, - .render_target_0 = pass.render_target_0, - .depth_stencil = pass.depth_stencil, - .viewport_x = pass.viewport_x, - .viewport_y = pass.viewport_y, - .viewport_width = pass.viewport_width, - .viewport_height = pass.viewport_height, - .draw_count = pass.draw_count, - .clear_count = pass.clear_count, - .resolve_count = pass.resolve_count, - .indexed_draw_count = pass.indexed_draw_count, - .indexed_shared_draw_count = pass.indexed_shared_draw_count, - .primitive_draw_count = pass.primitive_draw_count, - .max_texture_count = pass.max_texture_count, - .max_stream_count = pass.max_stream_count, - .max_sampler_count = pass.max_sampler_count, - .max_fetch_constant_count = pass.max_fetch_constant_count, - }; -} - -uint32_t ScoreScenePass(const ObservedPassDesc& pass) { - uint32_t score = 0; - score += pass.draw_count * 8; - score += pass.indexed_draw_count * 6; - score += pass.indexed_shared_draw_count * 7; - score += pass.primitive_draw_count * 2; - score += pass.clear_count * 3; - score += pass.max_texture_count * 6; - score += pass.max_stream_count * 8; - score += pass.max_sampler_count * 4; - score += pass.max_fetch_constant_count * 4; - score += pass.matches_frame_end_viewport ? 12u : 0u; - if (pass.resolve_count == 0) { - score += 16; - } - return score; -} - -uint32_t ScorePostProcessPass(const ObservedPassDesc& pass, uint32_t pass_index, - uint32_t selected_index) { - uint32_t score = 0; - score += pass.resolve_count * 48; - score += pass.clear_count * 4; - score += std::min(pass.draw_count, 24u) * 2; - score += pass.max_texture_count * 10; - score += pass.max_sampler_count * 5; - score += pass.matches_frame_end_viewport ? 18u : 0u; - score += pass.max_stream_count <= 2 ? 10u : 0u; - score += pass_index <= selected_index ? 6u : 0u; - return score; -} - -uint32_t ScoreUiPass(const ObservedPassDesc& pass, uint32_t pass_index, uint32_t selected_index) { - uint32_t score = 10; - if (pass_index >= selected_index) { - score += 15; - } - score += (pass.draw_count <= 16 ? 10u : 0u); - score += (pass.matches_frame_end_viewport ? 5u : 0u); - score += (pass.clear_count == 0 ? 8u : 0u); - score += (pass.max_stream_count <= 2 ? 12u : 0u); - score += (pass.max_texture_count <= 8 ? 8u : 0u); - score += (pass.primitive_draw_count == 0 ? 6u : 0u); - return score; -} - -} // namespace - -NativeFramePlan FramePlanner::Build(const FrontendFrameSummary& summary, - const std::vector& passes) { - NativeFramePlan plan; - plan.frame_index = summary.frame_index; - plan.observed_pass_count = summary.pass_count; - if (!summary.capture_valid || passes.empty()) { - return plan; - } - - const uint32_t selected_index = - summary.selected_pass_index < passes.size() ? summary.selected_pass_index - : uint32_t(passes.size() - 1); - - uint32_t best_scene_score = 0; - bool have_scene = false; - uint32_t best_ui_score = 0; - bool have_ui = false; - - for (uint32_t i = 0; i < passes.size(); ++i) { - const ObservedPassDesc& pass = passes[i]; - if (pass.kind == ObservedPassKind::kScene) { - const uint32_t score = ScoreScenePass(pass); - if (!have_scene || score > best_scene_score) { - plan.scene_stage = MakeReference(pass, i); - plan.scene_stage_score = score; - best_scene_score = score; - have_scene = true; - } - } - if (pass.kind == ObservedPassKind::kPostProcess) { - const uint32_t score = ScorePostProcessPass(pass, i, selected_index); - if (!plan.post_process_stage.valid || score >= plan.post_process_stage_score) { - plan.post_process_stage = MakeReference(pass, i); - plan.post_process_stage_score = score; - } - } - if (pass.kind == ObservedPassKind::kUiComposite) { - const uint32_t score = ScoreUiPass(pass, i, selected_index); - if (!have_ui || score >= best_ui_score) { - plan.ui_stage = MakeReference(pass, i); - plan.ui_stage_score = score; - best_ui_score = score; - have_ui = true; - } - } - } - - plan.present_stage = MakeReference(passes[selected_index], selected_index); - plan.present_stage_score = - ScorePostProcessPass(passes[selected_index], selected_index, selected_index) + - (passes[selected_index].selected_for_present ? 12u : 0u); - plan.present_from_selected_pass = true; - plan.has_scene_stage = plan.scene_stage.valid; - plan.has_post_process_stage = plan.post_process_stage.valid; - plan.has_ui_stage = plan.ui_stage.valid; - plan.output_width = plan.present_stage.viewport_width; - plan.output_height = plan.present_stage.viewport_height; - plan.requires_present_pass = plan.present_stage.valid && - (plan.has_post_process_stage || plan.has_ui_stage || - plan.has_scene_stage); - plan.valid = plan.present_stage.valid && plan.output_width != 0 && plan.output_height != 0; - return plan; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/frame_plan.h b/src/ac6_native_renderer/frame_plan.h deleted file mode 100644 index d3d634b0..00000000 --- a/src/ac6_native_renderer/frame_plan.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include - -#include "ac6_render_frontend.h" - -namespace ac6::renderer { - -struct PlannedPassReference { - bool valid = false; - uint32_t pass_index = 0; - ObservedPassKind kind = ObservedPassKind::kUnknown; - uint32_t render_target_0 = 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; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; - uint32_t indexed_draw_count = 0; - uint32_t indexed_shared_draw_count = 0; - uint32_t primitive_draw_count = 0; - uint32_t max_texture_count = 0; - uint32_t max_stream_count = 0; - uint32_t max_sampler_count = 0; - uint32_t max_fetch_constant_count = 0; -}; - -struct NativeFramePlan { - bool valid = false; - uint64_t frame_index = 0; - uint32_t observed_pass_count = 0; - uint32_t output_width = 0; - uint32_t output_height = 0; - bool has_scene_stage = false; - bool has_post_process_stage = false; - bool has_ui_stage = false; - bool requires_present_pass = false; - bool present_from_selected_pass = false; - uint32_t scene_stage_score = 0; - uint32_t post_process_stage_score = 0; - uint32_t ui_stage_score = 0; - uint32_t present_stage_score = 0; - PlannedPassReference scene_stage{}; - PlannedPassReference post_process_stage{}; - PlannedPassReference ui_stage{}; - PlannedPassReference present_stage{}; -}; - -class FramePlanner { - public: - NativeFramePlan Build(const FrontendFrameSummary& summary, - const std::vector& passes); -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/frame_scheduler.cpp b/src/ac6_native_renderer/frame_scheduler.cpp deleted file mode 100644 index d57d16b7..00000000 --- a/src/ac6_native_renderer/frame_scheduler.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "frame_scheduler.h" - -namespace ac6::renderer { - -void FrameScheduler::Configure(uint32_t max_frames_in_flight) { - max_frames_in_flight_ = max_frames_in_flight == 0 ? 1 : max_frames_in_flight; - frame_slot_ = 0; -} - -void FrameScheduler::BeginFrame() { - ++frame_index_; - frame_slot_ = static_cast(frame_index_ % max_frames_in_flight_); -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/frame_scheduler.h b/src/ac6_native_renderer/frame_scheduler.h deleted file mode 100644 index 439bd3d6..00000000 --- a/src/ac6_native_renderer/frame_scheduler.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -namespace ac6::renderer { - -class FrameScheduler { - public: - void Configure(uint32_t max_frames_in_flight); - void BeginFrame(); - - uint64_t frame_index() const { return frame_index_; } - uint32_t frame_slot() const { return frame_slot_; } - uint32_t max_frames_in_flight() const { return max_frames_in_flight_; } - - private: - uint64_t frame_index_ = 0; - uint32_t frame_slot_ = 0; - uint32_t max_frames_in_flight_ = 2; -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/native_renderer.cpp b/src/ac6_native_renderer/native_renderer.cpp deleted file mode 100644 index 8b294905..00000000 --- a/src/ac6_native_renderer/native_renderer.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include "native_renderer.h" - -#include "backends/d3d12_backend.h" -#include - -#include - -namespace ac6::renderer { -namespace { - -RenderPassKind ToRenderPassKind(ReplayPassRole role) { - switch (role) { - case ReplayPassRole::kScene: - return RenderPassKind::kScene; - case ReplayPassRole::kPostProcess: - case ReplayPassRole::kPresent: - return RenderPassKind::kPostProcess; - case ReplayPassRole::kUiComposite: - return RenderPassKind::kUiComposite; - case ReplayPassRole::kBootstrap: - return RenderPassKind::kBootstrap; - case ReplayPassRole::kUnknown: - default: - return RenderPassKind::kUnknown; - } -} - -RenderPassDesc BuildRenderPassDesc(const ReplayExecutorPassPacket& pass) { - return RenderPassDesc{ - .name = pass.name, - .kind = ToRenderPassKind(pass.role), - .async_compute = false, - .draw_count = pass.draw_count, - .clear_count = pass.clear_count, - .resolve_count = pass.resolve_count, - .render_target_0 = pass.render_target_0, - .depth_stencil = pass.depth_stencil, - .viewport_width = pass.output_width, - .viewport_height = pass.output_height, - .selected_for_present = pass.selected_for_present, - }; -} - -} // namespace - -NativeRenderer::NativeRenderer() = default; - -NativeRenderer::~NativeRenderer() { - Shutdown(); -} - -bool NativeRenderer::InitializeShared(const NativeRendererConfig& config, rex::memory::Memory* memory, ID3D12Device* device, ID3D12CommandQueue* queue) { - Shutdown(); - config_ = config; - scheduler_.Configure(config_.max_frames_in_flight); - - if (!device_.InitializeShared(config, memory, device, queue)) { - return false; - } - - stats_.initialized = true; - stats_.active_backend = BackendType::kD3D12; - return true; -} - -bool NativeRenderer::Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory) { - REXLOG_INFO("NativeRenderer::Initialize starting"); - Shutdown(); - - config_ = config; - REXLOG_INFO("NativeRenderer: Configuring scheduler (max_frames={})", config_.max_frames_in_flight); - scheduler_.Configure(config_.max_frames_in_flight); - - REXLOG_INFO("NativeRenderer: Initializing render device..."); - if (!device_.Initialize(config_, memory)) { - REXLOG_ERROR("NativeRenderer: device_.Initialize failed"); - return false; - } - - stats_.initialized = true; - stats_.active_backend = device_.active_backend(); - stats_.frame_count = 0; - stats_.built_pass_count = 0; - stats_.backend_submit_count = 0; - stats_.transient_allocation_count = 0; - return true; -} - -void NativeRenderer::Shutdown() { - device_.Shutdown(); - graph_.Reset(); - frontend_.Reset(); - frame_plan_ = {}; - replay_frame_ = {}; - execution_plan_ = {}; - executor_frame_ = {}; - stats_ = {}; -} - -void NativeRenderer::BeginFrame() { - if (!stats_.initialized) { - return; - } - scheduler_.BeginFrame(); - ++stats_.frame_count; - graph_.Reset(); -} - -void NativeRenderer::BuildBootstrapFrame() { - if (!stats_.initialized) { - return; - } - - frame_plan_ = {}; - replay_frame_ = replay_builder_.BuildBootstrapFrame(scheduler_.frame_index()); - execution_plan_ = execution_builder_.BuildBootstrapPlan(scheduler_.frame_index()); - executor_frame_ = executor_builder_.BuildBootstrapFrame(scheduler_.frame_index()); - if (device_.SubmitExecutorFrame(executor_frame_)) { - ++stats_.backend_submit_count; - } - - // Phase-1: do not present. Build a minimal graph to prove deterministic - // ownership without touching Rexglue emulation paths. - for (const ReplayExecutorPassPacket& pass : executor_frame_.passes) { - graph_.AddPass(BuildRenderPassDesc(pass)); - } - stats_.built_pass_count += graph_.pass_count(); - - REXLOG_TRACE("AC6 native renderer bootstrap frame built passes={}", - graph_.pass_count()); -} - -void NativeRenderer::BuildCapturedFrame( - const ac6::d3d::FrameCaptureSnapshot& frame_capture) { - if (!stats_.initialized) { - return; - } - - const FrontendFrameSummary summary = frontend_.BuildFromCapture(frame_capture); - if (!summary.capture_valid) { - BuildBootstrapFrame(); - return; - } - - frame_plan_ = planner_.Build(summary, frontend_.passes()); - replay_frame_ = replay_builder_.Build(summary, frontend_.passes(), frame_plan_); - execution_plan_ = execution_builder_.Build(replay_frame_, frame_plan_); - executor_frame_ = executor_builder_.Build(execution_plan_); - if (device_.SubmitExecutorFrame(executor_frame_)) { - ++stats_.backend_submit_count; - } - - for (const ReplayExecutorPassPacket& pass : executor_frame_.passes) { - graph_.AddPass(BuildRenderPassDesc(pass)); - } - - stats_.built_pass_count += graph_.pass_count(); - REXLOG_INFO( - "AC6 native renderer observed frame={} frontend_passes={} replay_passes={} replay_commands={} execution_passes={} execution_commands={} executor_passes={} executor_commands={} backend_submits={} selected={} draws={} clears={} resolves={} plan_valid={} out={}x{}", - summary.frame_index, summary.pass_count, replay_frame_.summary.pass_count, - replay_frame_.summary.command_count, execution_plan_.summary.pass_count, - execution_plan_.summary.command_count, executor_frame_.summary.pass_count, - executor_frame_.summary.command_count, stats_.backend_submit_count, - summary.selected_pass_index, - summary.total_draw_count, summary.total_clear_count, - summary.total_resolve_count, frame_plan_.valid, frame_plan_.output_width, - frame_plan_.output_height); -} - -} // namespace ac6::renderer - -// --------------------------------------------------------------------------- -// Phase 4: backend accessors -// --------------------------------------------------------------------------- -namespace ac6::renderer { - -D3D12Backend* NativeRenderer::GetD3D12Backend() const { - if (!device_.backend() || device_.active_backend() != BackendType::kD3D12) { - return nullptr; - } - return static_cast(device_.backend()); -} - -ID3D12Resource* NativeRenderer::GetOutputTexture() const { - D3D12Backend* b = GetD3D12Backend(); - return b ? b->GetOutputTexture() : nullptr; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/native_renderer.h b/src/ac6_native_renderer/native_renderer.h deleted file mode 100644 index 819d3f6b..00000000 --- a/src/ac6_native_renderer/native_renderer.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include - -#include - -#include "ac6_render_frontend.h" -#include "execution_plan.h" -#include "frame_scheduler.h" -#include "frame_plan.h" -#include "replay_ir.h" -#include "replay_executor.h" -#include "render_device.h" -#include "render_graph.h" -#include "types.h" - -// Forward declare so callers can access the output texture without pulling in -// all of d3d12_backend.h transitively. -struct ID3D12Resource; - -namespace ac6::renderer { - -// Experimental capture-replay renderer retained for diagnostics and future -// targeted overrides. It is not the default presentation path. -class NativeRenderer { - public: - NativeRenderer(); - ~NativeRenderer(); - - NativeRenderer(const NativeRenderer&) = delete; - NativeRenderer& operator=(const NativeRenderer&) = delete; - - bool Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory); - bool InitializeShared(const NativeRendererConfig& config, rex::memory::Memory* memory, ID3D12Device* device, ID3D12CommandQueue* queue); - void Shutdown(); - - void BeginFrame(); - void BuildBootstrapFrame(); - void BuildCapturedFrame(const ac6::d3d::FrameCaptureSnapshot& frame_capture); - - NativeRendererStats GetStats() const { return stats_; } - FeatureLevel feature_level() const { return config_.feature_level; } - uint32_t frame_slot() const { return scheduler_.frame_slot(); } - uint32_t max_frames_in_flight() const { return scheduler_.max_frames_in_flight(); } - FrontendFrameSummary frontend_summary() const { return frontend_.summary(); } - const std::vector& frontend_passes() const { - return frontend_.passes(); - } - NativeFramePlan frame_plan() const { return frame_plan_; } - ReplayFrameSummary replay_summary() const { return replay_frame_.summary; } - const ReplayFrame& replay_frame() const { return replay_frame_; } - ExecutionFrameSummary execution_summary() const { return execution_plan_.summary; } - const ExecutionFramePlan& execution_plan() const { return execution_plan_; } - ReplayExecutorFrameSummary executor_summary() const { return executor_frame_.summary; } - const ReplayExecutorFrame& executor_frame() const { return executor_frame_; } - BackendExecutorStatus backend_executor_status() const { - return device_.executor_status(); - } - - // Phase 4: returns the native output texture produced by the D3D12 backend, - // or nullptr if not yet available. The texture is in - // D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE after SubmitExecutorFrame. - ID3D12Resource* GetOutputTexture() const; - - // Phase 4: returns the raw D3D12Backend* (nullptr for non-D3D12 backends). - class D3D12Backend* GetD3D12Backend() const; - - private: - NativeRendererConfig config_{}; - NativeRendererStats stats_{}; - RenderDevice device_{}; - RenderGraph graph_{}; - FrameScheduler scheduler_{}; - Ac6RenderFrontend frontend_{}; - FramePlanner planner_{}; - ReplayIrBuilder replay_builder_{}; - ExecutionPlanBuilder execution_builder_{}; - ReplayExecutorPlanBuilder executor_builder_{}; - NativeFramePlan frame_plan_{}; - ReplayFrame replay_frame_{}; - ExecutionFramePlan execution_plan_{}; - ReplayExecutorFrame executor_frame_{}; -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/render_device.cpp b/src/ac6_native_renderer/render_device.cpp deleted file mode 100644 index c4b365e2..00000000 --- a/src/ac6_native_renderer/render_device.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "render_device.h" - -#include - -#include - -#include "backends/d3d12_backend.h" - -namespace ac6::renderer { - -RenderDevice::RenderDevice() = default; - -RenderDevice::~RenderDevice() { - Shutdown(); -} - -bool RenderDevice::InitializeShared(const NativeRendererConfig& config, rex::memory::Memory* memory, ID3D12Device* device, ID3D12CommandQueue* queue) { - Shutdown(); - - active_backend_ = BackendType::kD3D12; - backend_ = CreateBackend(active_backend_); - if (!backend_) return false; - - auto* d3d_backend = static_cast(backend_.get()); - if (!d3d_backend->InitializeShared(config, memory, device, queue)) { - backend_.reset(); - return false; - } - - initialized_ = true; - return true; -} - -bool RenderDevice::Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory) { - Shutdown(); - - active_backend_ = ResolveBackend(config.preferred_backend); - backend_ = CreateBackend(active_backend_); - if (!backend_) { - REXLOG_ERROR("AC6 native renderer has no backend factory for {}", - ToString(active_backend_)); - active_backend_ = BackendType::kUnknown; - return false; - } - if (!backend_->IsSupported()) { - REXLOG_WARN("AC6 native renderer backend {} is not supported on this platform", - backend_->GetName()); - backend_.reset(); - active_backend_ = BackendType::kUnknown; - return false; - } - if (!backend_->Initialize(config, memory)) { - REXLOG_ERROR("AC6 native renderer backend {} failed initialization", - backend_->GetName()); - backend_.reset(); - active_backend_ = BackendType::kUnknown; - return false; - } - - initialized_ = true; - REXLOG_INFO("AC6 native renderer initialized backend={} feature_level={} frames_in_flight={}", - backend_->GetName(), ToString(config.feature_level), - config.max_frames_in_flight); - return true; -} - -void RenderDevice::Shutdown() { - if (backend_) { - backend_->Shutdown(); - backend_.reset(); - } - initialized_ = false; - active_backend_ = BackendType::kUnknown; -} - -bool RenderDevice::SubmitExecutorFrame(const ReplayExecutorFrame& frame) { - if (!backend_ || !initialized_) { - return false; - } - return backend_->SubmitExecutorFrame(frame); -} - -std::string_view RenderDevice::backend_name() const { - return backend_ ? backend_->GetName() : ToString(BackendType::kUnknown); -} - -BackendExecutorStatus RenderDevice::executor_status() const { - return backend_ ? backend_->GetExecutorStatus() : BackendExecutorStatus{}; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/render_device.h b/src/ac6_native_renderer/render_device.h deleted file mode 100644 index fa90b90c..00000000 --- a/src/ac6_native_renderer/render_device.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include - -#if defined(_WIN32) -#include -#else -struct ID3D12Device; -struct ID3D12CommandQueue; -#endif - -#include - -#include "replay_executor.h" -#include "types.h" - -namespace ac6::renderer { - -class RenderDeviceBackend { - public: - virtual ~RenderDeviceBackend() = default; - - virtual BackendType GetType() const = 0; - virtual std::string_view GetName() const = 0; - virtual bool IsSupported() const = 0; - virtual bool Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory) = 0; - virtual bool InitializeShared(const NativeRendererConfig& config, rex::memory::Memory* memory, ID3D12Device* device, ID3D12CommandQueue* queue) = 0; - virtual bool SubmitExecutorFrame(const ReplayExecutorFrame& frame) = 0; - virtual BackendExecutorStatus GetExecutorStatus() const = 0; - virtual void Shutdown() = 0; -}; - -class RenderDevice { - public: - RenderDevice(); - ~RenderDevice(); - - RenderDevice(const RenderDevice&) = delete; - RenderDevice& operator=(const RenderDevice&) = delete; - - bool Initialize(const NativeRendererConfig& config, rex::memory::Memory* memory); - bool InitializeShared(const NativeRendererConfig& config, rex::memory::Memory* memory, ID3D12Device* device, ID3D12CommandQueue* queue); - void Shutdown(); - bool SubmitExecutorFrame(const ReplayExecutorFrame& frame); - - bool initialized() const { return initialized_; } - BackendType active_backend() const { return active_backend_; } - std::string_view backend_name() const; - BackendExecutorStatus executor_status() const; - - // Raw backend pointer — used by NativeRenderer to downcast to D3D12Backend*. - RenderDeviceBackend* backend() const { return backend_.get(); } - - private: - std::unique_ptr backend_; - BackendType active_backend_ = BackendType::kUnknown; - bool initialized_ = false; -}; - -std::unique_ptr CreateBackend(BackendType backend); - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/render_graph.cpp b/src/ac6_native_renderer/render_graph.cpp deleted file mode 100644 index 9766e668..00000000 --- a/src/ac6_native_renderer/render_graph.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "render_graph.h" - -#include - -namespace ac6::renderer { - -void RenderGraph::Reset() { - passes_.clear(); -} - -uint32_t RenderGraph::AddPass(RenderPassDesc pass) { - passes_.push_back(std::move(pass)); - return static_cast(passes_.size() - 1); -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/render_graph.h b/src/ac6_native_renderer/render_graph.h deleted file mode 100644 index f060643b..00000000 --- a/src/ac6_native_renderer/render_graph.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace ac6::renderer { - -enum class RenderPassKind : uint8_t { - kUnknown = 0, - kScene = 1, - kPostProcess = 2, - kUiComposite = 3, - kBootstrap = 4, -}; - -struct RenderPassDesc { - std::string name; - RenderPassKind kind = RenderPassKind::kUnknown; - bool async_compute = false; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; - uint32_t render_target_0 = 0; - uint32_t depth_stencil = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; - bool selected_for_present = false; -}; - -class RenderGraph { - public: - void Reset(); - uint32_t AddPass(RenderPassDesc pass); - - uint32_t pass_count() const { return static_cast(passes_.size()); } - const std::vector& passes() const { return passes_; } - - private: - std::vector passes_; -}; - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/replay_executor.cpp b/src/ac6_native_renderer/replay_executor.cpp deleted file mode 100644 index 2848936d..00000000 --- a/src/ac6_native_renderer/replay_executor.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include "replay_executor.h" - -#include - -namespace ac6::renderer { -namespace { - -SubmissionQueueType SelectQueue(const ExecutionPassPacket& pass) { - (void)pass; - // Current scaffold keeps all work on the graphics queue until backend - // implementations can prove safe async-compute or copy splits. - return SubmissionQueueType::kGraphics; -} - -ReplayExecutorCommandPacket BuildExecutorCommandPacket( - const ExecutionCommandPacket& command, uint32_t execution_pass_index, - uint32_t execution_command_index) { - const bool is_draw = command.category == ExecutionCommandCategory::kDraw; - return ReplayExecutorCommandPacket{ - .category = command.category, - .execution_pass_index = execution_pass_index, - .execution_command_index = execution_command_index, - .sequence = command.sequence, - .requires_resource_translation = - command.render_target_0 != 0 || command.depth_stencil != 0 || - command.texture_count != 0 || command.stream_count != 0 || - command.fetch_constant_count != 0, - .requires_pipeline_state = is_draw, - .requires_descriptor_setup = - is_draw && - (command.texture_count != 0 || command.sampler_count != 0 || - command.fetch_constant_count != 0), - .touches_render_target = command.render_target_0 != 0, - .touches_depth_stencil = command.depth_stencil != 0, - // Forwarded dispatch fields - .draw_kind = command.draw_kind, - .primitive_type = command.primitive_type, - .start = command.start, - .count = command.count, - .flags = command.flags, - .rect_count = command.rect_count, - .captured_rect_count = command.captured_rect_count, - .color = command.color, - .stencil = command.stencil, - .depth = command.depth, - .shadow_state = command.shadow_state, - }; -} - -ReplayExecutorPassPacket BuildExecutorPassPacket(const ExecutionPassPacket& pass, - uint32_t execution_pass_index) { - ReplayExecutorPassPacket executor_pass; - executor_pass.name = pass.name; - executor_pass.role = pass.role; - executor_pass.queue = SelectQueue(pass); - executor_pass.execution_pass_valid = true; - executor_pass.execution_pass_index = execution_pass_index; - executor_pass.draw_count = pass.draw_count; - executor_pass.clear_count = pass.clear_count; - executor_pass.resolve_count = pass.resolve_count; - executor_pass.render_target_0 = pass.render_target_0; - executor_pass.depth_stencil = pass.depth_stencil; - executor_pass.output_width = pass.output_width; - executor_pass.output_height = pass.output_height; - executor_pass.selected_for_present = pass.selected_for_present; - executor_pass.requires_present = pass.selected_for_present; - executor_pass.resources = pass.resources; - executor_pass.commands.reserve(pass.commands.size()); - - for (uint32_t i = 0; i < pass.commands.size(); ++i) { - ReplayExecutorCommandPacket command_packet = - BuildExecutorCommandPacket(pass.commands[i], execution_pass_index, i); - executor_pass.requires_resource_translation |= - command_packet.requires_resource_translation; - executor_pass.requires_pipeline_state |= command_packet.requires_pipeline_state; - executor_pass.requires_descriptor_setup |= - command_packet.requires_descriptor_setup; - executor_pass.commands.push_back(std::move(command_packet)); - } - - executor_pass.requires_resource_translation |= - pass.resources.needs_render_target || pass.resources.needs_depth_stencil || - pass.resources.needs_vertex_streams || pass.resources.needs_index_buffer || - pass.resources.needs_textures || pass.resources.needs_fetch_constants; - executor_pass.requires_pipeline_state |= pass.draw_count != 0; - executor_pass.requires_descriptor_setup |= - pass.resources.needs_textures || pass.resources.needs_samplers || - pass.resources.needs_fetch_constants; - executor_pass.requires_present |= pass.role == ReplayPassRole::kPresent; - return executor_pass; -} - -void AccumulateSummary(ReplayExecutorFrameSummary& summary, - const ReplayExecutorPassPacket& pass) { - ++summary.pass_count; - summary.command_count += static_cast(pass.commands.size()); - - switch (pass.queue) { - case SubmissionQueueType::kGraphics: - ++summary.graphics_pass_count; - break; - case SubmissionQueueType::kAsyncCompute: - ++summary.async_compute_pass_count; - break; - case SubmissionQueueType::kCopy: - ++summary.copy_pass_count; - break; - case SubmissionQueueType::kUnknown: - default: - break; - } - - if (pass.requires_present) { - ++summary.present_pass_count; - summary.has_present_pass = true; - } - if (pass.requires_resource_translation) { - ++summary.resource_translation_pass_count; - } - if (pass.requires_pipeline_state) { - ++summary.pipeline_state_pass_count; - } - if (pass.requires_descriptor_setup) { - ++summary.descriptor_setup_pass_count; - } - summary.valid = summary.pass_count != 0; -} - -} // namespace - -const char* ToString(SubmissionQueueType queue) { - switch (queue) { - case SubmissionQueueType::kGraphics: - return "graphics"; - case SubmissionQueueType::kAsyncCompute: - return "async_compute"; - case SubmissionQueueType::kCopy: - return "copy"; - case SubmissionQueueType::kUnknown: - default: - return "unknown"; - } -} - -ReplayExecutorFrame ReplayExecutorPlanBuilder::BuildBootstrapFrame( - uint64_t frame_index) const { - ReplayExecutorFrame frame; - frame.summary.frame_index = frame_index; - - ReplayExecutorPassPacket bootstrap_pass; - bootstrap_pass.name = "ac6.executor.bootstrap"; - bootstrap_pass.role = ReplayPassRole::kBootstrap; - bootstrap_pass.queue = SubmissionQueueType::kGraphics; - - AccumulateSummary(frame.summary, bootstrap_pass); - frame.passes.push_back(std::move(bootstrap_pass)); - return frame; -} - -ReplayExecutorFrame ReplayExecutorPlanBuilder::Build( - const ExecutionFramePlan& execution_plan) const { - ReplayExecutorFrame frame; - frame.summary.frame_index = execution_plan.summary.frame_index; - frame.summary.output_width = execution_plan.summary.output_width; - frame.summary.output_height = execution_plan.summary.output_height; - - if (!execution_plan.summary.valid || execution_plan.passes.empty()) { - return frame; - } - - frame.passes.reserve(execution_plan.passes.size()); - for (uint32_t i = 0; i < execution_plan.passes.size(); ++i) { - ReplayExecutorPassPacket pass = - BuildExecutorPassPacket(execution_plan.passes[i], i); - AccumulateSummary(frame.summary, pass); - frame.passes.push_back(std::move(pass)); - } - - frame.summary.valid = - frame.summary.pass_count != 0 && - (frame.summary.output_width != 0 || frame.summary.output_height != 0 || - execution_plan.summary.frame_index != 0 || - !frame.passes.empty()); - return frame; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/replay_executor.h b/src/ac6_native_renderer/replay_executor.h deleted file mode 100644 index 65abc55d..00000000 --- a/src/ac6_native_renderer/replay_executor.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "execution_plan.h" - -namespace ac6::renderer { - -enum class SubmissionQueueType : uint8_t { - kUnknown = 0, - kGraphics = 1, - kAsyncCompute = 2, - kCopy = 3, -}; - -struct ReplayExecutorCommandPacket { - ExecutionCommandCategory category = ExecutionCommandCategory::kNone; - uint32_t execution_pass_index = 0; - uint32_t execution_command_index = 0; - uint32_t sequence = 0; - bool requires_resource_translation = false; - bool requires_pipeline_state = false; - bool requires_descriptor_setup = false; - bool touches_render_target = false; - bool touches_depth_stencil = false; - // Draw call dispatch fields (forwarded from ExecutionCommandPacket) - ac6::d3d::DrawCallKind draw_kind = ac6::d3d::DrawCallKind::kIndexed; - uint32_t primitive_type = 0; - uint32_t start = 0; - uint32_t count = 0; - uint32_t flags = 0; - uint32_t rect_count = 0; - uint32_t captured_rect_count = 0; - uint32_t color = 0; - uint32_t stencil = 0; - float depth = 1.0f; - ac6::d3d::ShadowState shadow_state{}; -}; - -struct ReplayExecutorPassPacket { - std::string name; - ReplayPassRole role = ReplayPassRole::kUnknown; - SubmissionQueueType queue = SubmissionQueueType::kUnknown; - bool execution_pass_valid = false; - uint32_t execution_pass_index = 0; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; - uint32_t render_target_0 = 0; - uint32_t depth_stencil = 0; - uint32_t output_width = 0; - uint32_t output_height = 0; - bool selected_for_present = false; - bool requires_present = false; - bool requires_resource_translation = false; - bool requires_pipeline_state = false; - bool requires_descriptor_setup = false; - ExecutionResourceRequirements resources{}; - std::vector commands; -}; - -struct ReplayExecutorFrameSummary { - bool valid = false; - uint64_t frame_index = 0; - uint32_t pass_count = 0; - uint32_t command_count = 0; - uint32_t graphics_pass_count = 0; - uint32_t async_compute_pass_count = 0; - uint32_t copy_pass_count = 0; - uint32_t present_pass_count = 0; - uint32_t resource_translation_pass_count = 0; - uint32_t pipeline_state_pass_count = 0; - uint32_t descriptor_setup_pass_count = 0; - uint32_t output_width = 0; - uint32_t output_height = 0; - bool has_present_pass = false; -}; - -struct ReplayExecutorFrame { - ReplayExecutorFrameSummary summary{}; - std::vector passes; -}; - -class ReplayExecutorPlanBuilder { - public: - ReplayExecutorFrame BuildBootstrapFrame(uint64_t frame_index) const; - ReplayExecutorFrame Build(const ExecutionFramePlan& execution_plan) const; -}; - -const char* ToString(SubmissionQueueType queue); - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/replay_ir.cpp b/src/ac6_native_renderer/replay_ir.cpp deleted file mode 100644 index 3f1f0516..00000000 --- a/src/ac6_native_renderer/replay_ir.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "replay_ir.h" - -#include - -namespace ac6::renderer { -namespace { - -ReplayPassRole ToReplayPassRole(ObservedPassKind kind) { - switch (kind) { - case ObservedPassKind::kScene: - return ReplayPassRole::kScene; - case ObservedPassKind::kPostProcess: - return ReplayPassRole::kPostProcess; - case ObservedPassKind::kUiComposite: - return ReplayPassRole::kUiComposite; - case ObservedPassKind::kUnknown: - default: - return ReplayPassRole::kUnknown; - } -} - -std::string BuildObservedPassName(const ObservedPassDesc& pass, - uint32_t pass_index) { - return "ac6.replay." + std::string(ToString(pass.kind)) + "." + - std::to_string(pass_index); -} - -ReplayCommandDesc BuildReplayCommand(const ObservedCommandDesc& command) { - return ReplayCommandDesc{ - .type = command.type, - .sequence = command.sequence, - .draw_kind = command.draw_kind, - .primitive_type = command.primitive_type, - .start = command.start, - .count = command.count, - .flags = command.flags, - .rect_count = command.rect_count, - .captured_rect_count = command.captured_rect_count, - .color = command.color, - .stencil = command.stencil, - .depth = command.depth, - .texture_count = command.texture_count, - .stream_count = command.stream_count, - .sampler_count = command.sampler_count, - .fetch_constant_count = command.fetch_constant_count, - .render_target_0 = command.render_target_0, - .depth_stencil = command.depth_stencil, - .viewport_x = command.viewport_x, - .viewport_y = command.viewport_y, - .viewport_width = command.viewport_width, - .viewport_height = command.viewport_height, - .shadow_state = command.shadow_state, - }; -} - -ReplayPassDesc BuildReplayPass(const ObservedPassDesc& pass, uint32_t pass_index) { - ReplayPassDesc replay_pass; - replay_pass.name = BuildObservedPassName(pass, pass_index); - replay_pass.role = ToReplayPassRole(pass.kind); - replay_pass.source_pass_valid = true; - replay_pass.source_pass_index = pass_index; - replay_pass.draw_count = pass.draw_count; - replay_pass.clear_count = pass.clear_count; - replay_pass.resolve_count = pass.resolve_count; - replay_pass.render_target_0 = pass.render_target_0; - replay_pass.depth_stencil = pass.depth_stencil; - replay_pass.viewport_width = pass.viewport_width; - replay_pass.viewport_height = pass.viewport_height; - replay_pass.selected_for_present = pass.selected_for_present; - replay_pass.commands.reserve(pass.commands.size()); - for (const ObservedCommandDesc& command : pass.commands) { - replay_pass.commands.push_back(BuildReplayCommand(command)); - } - return replay_pass; -} - -void AccumulateSummary(ReplayFrameSummary& summary, const ReplayPassDesc& pass) { - ++summary.pass_count; - summary.command_count += static_cast(pass.commands.size()); - summary.draw_count += pass.draw_count; - summary.clear_count += pass.clear_count; - summary.resolve_count += pass.resolve_count; - summary.valid = summary.pass_count != 0; -} - -} // namespace - -const char* ToString(ReplayPassRole role) { - switch (role) { - case ReplayPassRole::kBootstrap: - return "bootstrap"; - case ReplayPassRole::kScene: - return "scene"; - case ReplayPassRole::kPostProcess: - return "post_process"; - case ReplayPassRole::kUiComposite: - return "ui_composite"; - case ReplayPassRole::kPresent: - return "present"; - case ReplayPassRole::kUnknown: - default: - return "unknown"; - } -} - -ReplayFrame ReplayIrBuilder::BuildBootstrapFrame(uint64_t frame_index) const { - ReplayFrame replay_frame; - replay_frame.summary.frame_index = frame_index; - - ReplayPassDesc bootstrap_pass; - bootstrap_pass.name = "ac6.replay.bootstrap"; - bootstrap_pass.role = ReplayPassRole::kBootstrap; - bootstrap_pass.viewport_width = 0; - bootstrap_pass.viewport_height = 0; - - AccumulateSummary(replay_frame.summary, bootstrap_pass); - replay_frame.passes.push_back(std::move(bootstrap_pass)); - return replay_frame; -} - -ReplayFrame ReplayIrBuilder::Build( - const FrontendFrameSummary& summary, - const std::vector& passes, - const NativeFramePlan& frame_plan) const { - ReplayFrame replay_frame; - replay_frame.summary.frame_index = summary.frame_index; - replay_frame.summary.output_width = frame_plan.output_width; - replay_frame.summary.output_height = frame_plan.output_height; - - if (!summary.capture_valid || passes.empty()) { - return replay_frame; - } - - replay_frame.passes.reserve(passes.size() + (frame_plan.requires_present_pass ? 1u : 0u)); - for (uint32_t i = 0; i < passes.size(); ++i) { - ReplayPassDesc replay_pass = BuildReplayPass(passes[i], i); - AccumulateSummary(replay_frame.summary, replay_pass); - replay_frame.passes.push_back(std::move(replay_pass)); - } - - if (frame_plan.valid && frame_plan.requires_present_pass) { - ReplayPassDesc present_pass; - present_pass.name = "ac6.replay.present"; - present_pass.role = ReplayPassRole::kPresent; - present_pass.source_pass_valid = frame_plan.present_stage.valid; - present_pass.source_pass_index = frame_plan.present_stage.pass_index; - present_pass.render_target_0 = frame_plan.present_stage.render_target_0; - present_pass.depth_stencil = frame_plan.present_stage.depth_stencil; - present_pass.viewport_width = frame_plan.output_width; - present_pass.viewport_height = frame_plan.output_height; - present_pass.selected_for_present = true; - replay_frame.summary.has_present_pass = true; - AccumulateSummary(replay_frame.summary, present_pass); - replay_frame.passes.push_back(std::move(present_pass)); - } - - replay_frame.summary.valid = - replay_frame.summary.pass_count != 0 && - (!frame_plan.valid || - (replay_frame.summary.output_width != 0 && - replay_frame.summary.output_height != 0)); - return replay_frame; -} - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/replay_ir.h b/src/ac6_native_renderer/replay_ir.h deleted file mode 100644 index 1936113a..00000000 --- a/src/ac6_native_renderer/replay_ir.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "ac6_render_frontend.h" -#include "frame_plan.h" - -namespace ac6::renderer { - -enum class ReplayPassRole : uint8_t { - kUnknown = 0, - kBootstrap = 1, - kScene = 2, - kPostProcess = 3, - kUiComposite = 4, - kPresent = 5, -}; - -struct ReplayCommandDesc { - ObservedCommandType type = ObservedCommandType::kDraw; - uint32_t sequence = 0; - ac6::d3d::DrawCallKind draw_kind = ac6::d3d::DrawCallKind::kIndexed; - uint32_t primitive_type = 0; - uint32_t start = 0; - uint32_t count = 0; - uint32_t flags = 0; - uint32_t rect_count = 0; - uint32_t captured_rect_count = 0; - uint32_t color = 0; - uint32_t stencil = 0; - float depth = 1.0f; - uint32_t texture_count = 0; - uint32_t stream_count = 0; - uint32_t sampler_count = 0; - uint32_t fetch_constant_count = 0; - uint32_t render_target_0 = 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; - ac6::d3d::ShadowState shadow_state{}; -}; - -struct ReplayPassDesc { - std::string name; - ReplayPassRole role = ReplayPassRole::kUnknown; - bool source_pass_valid = false; - uint32_t source_pass_index = 0; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; - uint32_t render_target_0 = 0; - uint32_t depth_stencil = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; - bool selected_for_present = false; - std::vector commands; -}; - -struct ReplayFrameSummary { - bool valid = false; - uint64_t frame_index = 0; - uint32_t pass_count = 0; - uint32_t command_count = 0; - uint32_t draw_count = 0; - uint32_t clear_count = 0; - uint32_t resolve_count = 0; - uint32_t output_width = 0; - uint32_t output_height = 0; - bool has_present_pass = false; -}; - -struct ReplayFrame { - ReplayFrameSummary summary{}; - std::vector passes; -}; - -class ReplayIrBuilder { - public: - ReplayFrame BuildBootstrapFrame(uint64_t frame_index) const; - ReplayFrame Build(const FrontendFrameSummary& summary, - const std::vector& passes, - const NativeFramePlan& frame_plan) const; -}; - -const char* ToString(ReplayPassRole role); - -} // namespace ac6::renderer diff --git a/src/ac6_native_renderer/types.h b/src/ac6_native_renderer/types.h deleted file mode 100644 index 58215888..00000000 --- a/src/ac6_native_renderer/types.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - -#include -#include - -namespace ac6::renderer { - -enum class BackendType : uint8_t { - kUnknown = 0, - kD3D12 = 1, - kVulkan = 2, - kMetal = 3, -}; - -enum class FeatureLevel : uint8_t { - kBootstrap = 0, - kSceneSubmission = 1, - kParityValidation = 2, - kShipping = 3, -}; - -struct NativeRendererConfig { - BackendType preferred_backend = BackendType::kUnknown; - FeatureLevel feature_level = FeatureLevel::kBootstrap; - uint32_t max_frames_in_flight = 2; - bool enable_debug_markers = true; - bool enable_validation = true; -}; - -struct NativeRendererStats { - bool initialized = false; - BackendType active_backend = BackendType::kUnknown; - uint64_t frame_count = 0; - uint64_t built_pass_count = 0; - uint64_t backend_submit_count = 0; - uint64_t transient_allocation_count = 0; -}; - -struct BackendExecutorStatus { - bool initialized = false; - bool frame_valid = false; - uint64_t frame_index = 0; - uint32_t submitted_pass_count = 0; - uint32_t submitted_command_count = 0; - uint32_t graphics_pass_count = 0; - uint32_t async_compute_pass_count = 0; - uint32_t copy_pass_count = 0; - uint32_t present_pass_count = 0; - uint32_t resource_translation_pass_count = 0; - uint32_t pipeline_state_pass_count = 0; - uint32_t descriptor_setup_pass_count = 0; - uint32_t draw_attempt_count = 0; - uint32_t draw_success_count = 0; - uint32_t draw_prepare_failure_count = 0; - uint32_t draw_pso_failure_count = 0; - uint32_t indexed_draw_count = 0; - uint32_t non_indexed_draw_count = 0; - uint32_t clear_command_count = 0; - uint32_t resolve_command_count = 0; - uint32_t invalid_stream_binding_count = 0; - uint32_t invalid_index_buffer_count = 0; - uint32_t index_count_overflow_count = 0; - uint32_t index_data_unavailable_count = 0; - uint32_t index_buffer_create_failure_count = 0; - uint32_t index_upload_failure_count = 0; - uint32_t zero_vertex_count = 0; - uint32_t invalid_vertex_range_count = 0; - uint32_t vertex_buffer_size_invalid_count = 0; - uint32_t vertex_buffer_create_failure_count = 0; - uint32_t vertex_data_unavailable_count = 0; - uint32_t vertex_upload_failure_count = 0; -}; - -constexpr std::string_view ToString(BackendType backend) { - switch (backend) { - case BackendType::kD3D12: - return "d3d12"; - case BackendType::kVulkan: - return "vulkan"; - case BackendType::kMetal: - return "metal"; - default: - return "unknown"; - } -} - -constexpr std::string_view ToString(FeatureLevel level) { - switch (level) { - case FeatureLevel::kBootstrap: - return "bootstrap"; - case FeatureLevel::kSceneSubmission: - return "scene_submission"; - case FeatureLevel::kParityValidation: - return "parity_validation"; - case FeatureLevel::kShipping: - return "shipping"; - default: - return "unknown"; - } -} - -#if defined(_WIN32) -constexpr BackendType kPlatformDefaultBackend = BackendType::kD3D12; -#elif defined(__APPLE__) -constexpr BackendType kPlatformDefaultBackend = BackendType::kMetal; -#else -constexpr BackendType kPlatformDefaultBackend = BackendType::kVulkan; -#endif - -inline BackendType ResolveBackend(BackendType preferred_backend) { - return preferred_backend == BackendType::kUnknown ? kPlatformDefaultBackend - : preferred_backend; -} - -} // namespace ac6::renderer diff --git a/src/main.cpp b/src/main.cpp index 54d71c61..3b8b35d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,9 +10,6 @@ REXCVAR_DECLARE(bool, ac6_render_capture); REXCVAR_DECLARE(bool, ac6_timing_hooks_enabled); REXCVAR_DECLARE(bool, ac6_unlock_fps); REXCVAR_DECLARE(bool, ac6_native_graphics_enabled); -REXCVAR_DECLARE(bool, ac6_native_graphics_require_capture); -REXCVAR_DECLARE(bool, ac6_experimental_replay_present); -REXCVAR_DECLARE(bool, ac6_force_safe_render_capture); REXCVAR_DECLARE(bool, ac6_force_safe_draw_resolution_scale); REXCVAR_DECLARE(bool, ac6_force_safe_direct_host_resolve); REXCVAR_DECLARE(std::string, ac6_graphics_mode); @@ -22,6 +19,24 @@ REXCVAR_DECLARE(int32_t, draw_resolution_scale_x); REXCVAR_DECLARE(int32_t, draw_resolution_scale_y); REXCVAR_DECLARE(std::string, log_file); REXCVAR_DECLARE(std::string, log_level); +REXCVAR_DECLARE(bool, ac6_d3d_trace); +REXCVAR_DECLARE(bool, ac6_backend_debug_swap); +REXCVAR_DECLARE(bool, ac6_backend_log_signatures); +REXCVAR_DECLARE(bool, ac6_backend_signature_diagnostics); +REXCVAR_DECLARE(bool, ac6_texture_swaps_dump_enabled); +REXCVAR_DECLARE(bool, vsync); +REXCVAR_DECLARE(bool, guest_vblank_sync_to_refresh); +REXCVAR_DECLARE(bool, host_present_from_non_ui_thread); +REXCVAR_DECLARE(bool, d3d12_allow_variable_refresh_rate_and_tearing); +REXCVAR_DECLARE(bool, vfetch_index_rounding_bias); +REXCVAR_DECLARE(int32_t, video_mode_width); +REXCVAR_DECLARE(int32_t, video_mode_height); +REXCVAR_DECLARE(std::string, resolution); +REXCVAR_DECLARE(int32_t, window_width); +REXCVAR_DECLARE(int32_t, window_height); + +REXCVAR_DEFINE_BOOL(ac6_performance_mode, true, "AC6/Performance", + "Disable all diagnostics, logging, and development overlays for maximum runtime performance"); #include "generated/ac6recomp_config.h" #include "generated/ac6recomp_init.h" @@ -55,15 +70,63 @@ void ApplyAc6HybridStartupSafetyOverrides() { if (REXCVAR_GET(ac6_force_safe_direct_host_resolve)) { REXCVAR_SET(direct_host_resolve, false); } +} - if (REXCVAR_GET(ac6_force_safe_render_capture)) { - REXCVAR_SET(ac6_native_graphics_require_capture, false); - REXCVAR_SET(ac6_render_capture, false); +void ApplyAc6DefaultSettings() { + if (!rex::cvar::HasNonDefaultValue("vsync")) { + REXCVAR_SET(vsync, true); + } + if (!rex::cvar::HasNonDefaultValue("guest_vblank_sync_to_refresh")) { + REXCVAR_SET(guest_vblank_sync_to_refresh, true); + } + if (!rex::cvar::HasNonDefaultValue("host_present_from_non_ui_thread")) { + REXCVAR_SET(host_present_from_non_ui_thread, false); + } + if (!rex::cvar::HasNonDefaultValue("d3d12_allow_variable_refresh_rate_and_tearing")) { + REXCVAR_SET(d3d12_allow_variable_refresh_rate_and_tearing, false); + } + if (!rex::cvar::HasNonDefaultValue("vfetch_index_rounding_bias")) { + REXCVAR_SET(vfetch_index_rounding_bias, true); + } + if (!rex::cvar::HasNonDefaultValue("direct_host_resolve")) { + REXCVAR_SET(direct_host_resolve, false); + } + if (!rex::cvar::HasNonDefaultValue("video_mode_width")) { + REXCVAR_SET(video_mode_width, 1920); + } + if (!rex::cvar::HasNonDefaultValue("video_mode_height")) { + REXCVAR_SET(video_mode_height, 1080); + } + if (!rex::cvar::HasNonDefaultValue("resolution")) { + REXCVAR_SET(resolution, "1080p"); + } + if (!rex::cvar::HasNonDefaultValue("window_width")) { + REXCVAR_SET(window_width, 1920); + } + if (!rex::cvar::HasNonDefaultValue("window_height")) { + REXCVAR_SET(window_height, 1080); } } +void ApplyAc6PerformanceModeOverrides() { + if (!REXCVAR_GET(ac6_performance_mode)) { + return; + } + REXCVAR_SET(log_level, "error"); + REXCVAR_SET(ac6_d3d_trace, false); + REXCVAR_SET(ac6_render_capture, false); + REXCVAR_SET(ac6_backend_debug_swap, false); + REXCVAR_SET(ac6_backend_log_signatures, false); + REXCVAR_SET(ac6_backend_signature_diagnostics, false); + REXCVAR_SET(ac6_texture_swaps_dump_enabled, false); +} + } // namespace +void ApplyAc6PerformanceModeOverridesPublic() { + ApplyAc6PerformanceModeOverrides(); +} + void InitEarlyLog() { g_boot_log.open("boot.log", std::ios::out | std::ios::trunc); if (g_boot_log.is_open()) { @@ -82,13 +145,16 @@ std::unique_ptr Ac6recompAppCreate(rex::ui::WindowedAppCon // Force SDK logging to a file as well REXCVAR_SET(log_file, "ac6recomp.log"); - REXCVAR_SET(log_level, "info"); + if (!rex::cvar::HasNonDefaultValue("log_level")) { + REXCVAR_SET(log_level, "debug"); + } REXCVAR_SET(ac6_unlock_fps, false); + ApplyAc6DefaultSettings(); ApplyAc6HybridStartupSafetyOverrides(); + ApplyAc6PerformanceModeOverrides(); - REXLOG_INFO("Ac6recompAppCreate: graphics mode={} replay_present={} capture={}", + REXLOG_INFO("Ac6recompAppCreate: graphics mode={} capture={}", REXCVAR_GET(ac6_graphics_mode), - REXCVAR_GET(ac6_experimental_replay_present) ? "true" : "false", REXCVAR_GET(ac6_render_capture) ? "true" : "false"); return Ac6recompApp::Create(ctx); diff --git a/thirdparty/rexglue-sdk/src/graphics/d3d12/command_processor.cpp b/thirdparty/rexglue-sdk/src/graphics/d3d12/command_processor.cpp index 9695fd77..2e44d371 100644 --- a/thirdparty/rexglue-sdk/src/graphics/d3d12/command_processor.cpp +++ b/thirdparty/rexglue-sdk/src/graphics/d3d12/command_processor.cpp @@ -2105,7 +2105,7 @@ bool D3D12CommandProcessor::IssueSwapInternal(uint32_t frontbuffer_ptr, bool using_native_swap_texture = false; bool used_direct_display_fallback = false; - ID3D12Resource* swap_texture_resource = ac6::graphics::GetNativeOutputTexture(); + ID3D12Resource* swap_texture_resource = nullptr; if (swap_texture_resource) { D3D12_RESOURCE_DESC native_desc = swap_texture_resource->GetDesc(); swap_texture_srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;