diff --git a/CMakeLists.txt b/CMakeLists.txt index ca45c57e..92b8a82c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set(AC6RECOMP_SOURCES src/ac6_native_graphics_overlay.cpp src/ac6_native_renderer/ac6_render_frontend.cpp src/ac6_native_renderer/frame_plan.cpp + src/ac6_native_renderer/replay_ir.cpp src/ac6_native_renderer/backends/backend_factory.cpp src/ac6_native_renderer/backends/d3d12_backend.cpp src/ac6_native_renderer/backends/metal_backend.cpp diff --git a/src/ac6_native_graphics.cpp b/src/ac6_native_graphics.cpp index c22faf4d..474d762a 100644 --- a/src/ac6_native_graphics.cpp +++ b/src/ac6_native_graphics.cpp @@ -90,6 +90,8 @@ bool EnsureInitialized() { void UpdateStatusFromRendererUnlocked() { g_runtime_status.renderer_stats = g_native_renderer.GetStats(); g_runtime_status.active_backend = g_runtime_status.renderer_stats.active_backend; + g_runtime_status.frontend_summary = g_native_renderer.frontend_summary(); + g_runtime_status.replay_summary = g_native_renderer.replay_summary(); g_runtime_status.frame_plan = g_native_renderer.frame_plan(); } diff --git a/src/ac6_native_graphics.h b/src/ac6_native_graphics.h index bc9c2c9b..2c447da7 100644 --- a/src/ac6_native_graphics.h +++ b/src/ac6_native_graphics.h @@ -2,7 +2,9 @@ #include +#include "ac6_native_renderer/ac6_render_frontend.h" #include "ac6_native_renderer/frame_plan.h" +#include "ac6_native_renderer/replay_ir.h" #include "ac6_native_renderer/types.h" #include "d3d_state.h" @@ -19,6 +21,8 @@ struct NativeGraphicsRuntimeStatus { 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::d3d::FrameCaptureSummary capture_summary{}; ac6::renderer::NativeFramePlan frame_plan{}; }; diff --git a/src/ac6_native_graphics_overlay.cpp b/src/ac6_native_graphics_overlay.cpp index fe44e755..5a394e7a 100644 --- a/src/ac6_native_graphics_overlay.cpp +++ b/src/ac6_native_graphics_overlay.cpp @@ -37,6 +37,10 @@ void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) { static_cast(status.renderer_stats.frame_count)); ImGui::Text("render passes built: %llu", static_cast(status.renderer_stats.built_pass_count)); + ImGui::Text("frontend passes/commands: %u / %u", status.frontend_summary.pass_count, + status.frontend_summary.total_command_count); + ImGui::Text("replay passes/commands: %u / %u", status.replay_summary.pass_count, + status.replay_summary.command_count); ImGui::Separator(); ImGui::Text("capture frame: %llu", static_cast(status.capture_summary.frame_index)); @@ -64,6 +68,9 @@ void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) { ImGui::Separator(); ImGui::Text("planned output: %ux%u", status.frame_plan.output_width, status.frame_plan.output_height); + ImGui::Text("replay output/present: %ux%u / %s", status.replay_summary.output_width, + status.replay_summary.output_height, + status.replay_summary.has_present_pass ? "yes" : "no"); ImGui::Text("stages scene/post/ui: %s / %s / %s", status.frame_plan.has_scene_stage ? "yes" : "no", status.frame_plan.has_post_process_stage ? "yes" : "no", diff --git a/src/ac6_native_renderer/ac6_render_frontend.cpp b/src/ac6_native_renderer/ac6_render_frontend.cpp index ae5abc0b..4cae3f16 100644 --- a/src/ac6_native_renderer/ac6_render_frontend.cpp +++ b/src/ac6_native_renderer/ac6_render_frontend.cpp @@ -48,9 +48,74 @@ enum class CapturedFrameEventType : uint8_t { 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, + }; +} + +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, + }; +} + +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, + }; +} + bool SamePassBinding(const ac6::d3d::ShadowState& left, const ac6::d3d::ShadowState& right) { return left.render_targets[0] == right.render_targets[0] && @@ -121,6 +186,19 @@ const char* ToString(ObservedPassKind kind) { } } +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(); @@ -143,18 +221,18 @@ FrontendFrameSummary Ac6RenderFrontend::BuildFromCapture( frame_capture.resolves.size()); for (const auto& draw : frame_capture.draws) { - events.push_back({draw.sequence, CapturedFrameEventType::kDraw, - &draw.shadow_state}); + 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, - &clear.shadow_state}); + 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, - &resolve.shadow_state}); + events.push_back({resolve.sequence, CapturedFrameEventType::kResolve, nullptr, nullptr, + &resolve, &resolve.shadow_state}); ++summary_.total_resolve_count; } @@ -198,6 +276,10 @@ FrontendFrameSummary Ac6RenderFrontend::BuildFromCapture( 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, @@ -215,9 +297,17 @@ FrontendFrameSummary Ac6RenderFrontend::BuildFromCapture( 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; } } diff --git a/src/ac6_native_renderer/ac6_render_frontend.h b/src/ac6_native_renderer/ac6_render_frontend.h index c02bb23f..afe9821a 100644 --- a/src/ac6_native_renderer/ac6_render_frontend.h +++ b/src/ac6_native_renderer/ac6_render_frontend.h @@ -14,6 +14,37 @@ enum class ObservedPassKind : uint8_t { 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; +}; + struct ObservedPassDesc { ObservedPassKind kind = ObservedPassKind::kUnknown; uint32_t start_sequence = 0; @@ -37,12 +68,14 @@ struct ObservedPassDesc { uint32_t viewport_height = 0; 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; @@ -66,5 +99,6 @@ class Ac6RenderFrontend { }; const char* ToString(ObservedPassKind kind); +const char* ToString(ObservedCommandType type); } // namespace ac6::renderer diff --git a/src/ac6_native_renderer/native_renderer.cpp b/src/ac6_native_renderer/native_renderer.cpp index 9c5ed345..2a8e18e1 100644 --- a/src/ac6_native_renderer/native_renderer.cpp +++ b/src/ac6_native_renderer/native_renderer.cpp @@ -21,10 +21,21 @@ RenderPassKind ToRenderPassKind(ObservedPassKind kind) { } } -std::string BuildObservedPassName(const ObservedPassDesc& pass, - uint32_t pass_index) { - return "ac6.observed." + std::string(ToString(pass.kind)) + "." + - std::to_string(pass_index); +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; + } } } // namespace @@ -58,6 +69,7 @@ void NativeRenderer::Shutdown() { graph_.Reset(); frontend_.Reset(); frame_plan_ = {}; + replay_frame_ = {}; stats_ = {}; } @@ -76,14 +88,25 @@ void NativeRenderer::BuildBootstrapFrame() { } frame_plan_ = {}; + replay_frame_ = replay_builder_.BuildBootstrapFrame(scheduler_.frame_index()); // Phase-1: do not present. Build a minimal graph to prove deterministic // ownership without touching Rexglue emulation paths. - graph_.AddPass(RenderPassDesc{ - .name = "ac6.native.bootstrap", - .kind = RenderPassKind::kBootstrap, - .async_compute = false, - }); + for (const ReplayPassDesc& pass : replay_frame_.passes) { + graph_.AddPass(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.viewport_width, + .viewport_height = pass.viewport_height, + .selected_for_present = pass.selected_for_present, + }); + } stats_.built_pass_count += graph_.pass_count(); REXLOG_TRACE("AC6 native renderer bootstrap frame built passes={}", @@ -103,12 +126,12 @@ void NativeRenderer::BuildCapturedFrame( } frame_plan_ = planner_.Build(summary, frontend_.passes()); + replay_frame_ = replay_builder_.Build(summary, frontend_.passes(), frame_plan_); - uint32_t pass_index = 0; - for (const ObservedPassDesc& pass : frontend_.passes()) { + for (const ReplayPassDesc& pass : replay_frame_.passes) { graph_.AddPass(RenderPassDesc{ - .name = BuildObservedPassName(pass, pass_index++), - .kind = ToRenderPassKind(pass.kind), + .name = pass.name, + .kind = ToRenderPassKind(pass.role), .async_compute = false, .draw_count = pass.draw_count, .clear_count = pass.clear_count, @@ -120,26 +143,12 @@ void NativeRenderer::BuildCapturedFrame( .selected_for_present = pass.selected_for_present, }); } - if (frame_plan_.valid && frame_plan_.requires_present_pass) { - graph_.AddPass(RenderPassDesc{ - .name = "ac6.plan.present", - .kind = RenderPassKind::kPostProcess, - .async_compute = false, - .draw_count = 0, - .clear_count = 0, - .resolve_count = 0, - .render_target_0 = frame_plan_.present_stage.render_target_0, - .depth_stencil = frame_plan_.present_stage.depth_stencil, - .viewport_width = frame_plan_.output_width, - .viewport_height = frame_plan_.output_height, - .selected_for_present = true, - }); - } stats_.built_pass_count += graph_.pass_count(); REXLOG_TRACE( - "AC6 native renderer observed frame={} passes={} selected={} draws={} clears={} resolves={} plan_valid={} out={}x{}", - summary.frame_index, summary.pass_count, summary.selected_pass_index, + "AC6 native renderer observed frame={} frontend_passes={} replay_passes={} replay_commands={} selected={} draws={} clears={} resolves={} plan_valid={} out={}x{}", + summary.frame_index, summary.pass_count, replay_frame_.summary.pass_count, + replay_frame_.summary.command_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); diff --git a/src/ac6_native_renderer/native_renderer.h b/src/ac6_native_renderer/native_renderer.h index 3f4bfcf7..2ad14705 100644 --- a/src/ac6_native_renderer/native_renderer.h +++ b/src/ac6_native_renderer/native_renderer.h @@ -5,6 +5,7 @@ #include "ac6_render_frontend.h" #include "frame_scheduler.h" #include "frame_plan.h" +#include "replay_ir.h" #include "render_device.h" #include "render_graph.h" #include "types.h" @@ -35,6 +36,8 @@ class NativeRenderer { 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_; } private: NativeRendererConfig config_{}; @@ -44,7 +47,9 @@ class NativeRenderer { FrameScheduler scheduler_{}; Ac6RenderFrontend frontend_{}; FramePlanner planner_{}; + ReplayIrBuilder replay_builder_{}; NativeFramePlan frame_plan_{}; + ReplayFrame replay_frame_{}; }; } // namespace ac6::renderer diff --git a/src/ac6_native_renderer/replay_ir.cpp b/src/ac6_native_renderer/replay_ir.cpp new file mode 100644 index 00000000..a1f724c5 --- /dev/null +++ b/src/ac6_native_renderer/replay_ir.cpp @@ -0,0 +1,164 @@ +#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, + }; +} + +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 new file mode 100644 index 00000000..a69fd218 --- /dev/null +++ b/src/ac6_native_renderer/replay_ir.h @@ -0,0 +1,90 @@ +#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; +}; + +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