mirror of
https://github.com/sal063/AC6_recomp
synced 2026-08-05 01:30:26 -04:00
renderer: add replay IR scaffolding
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#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{};
|
||||
};
|
||||
|
||||
@@ -37,6 +37,10 @@ void NativeGraphicsStatusDialog::OnDraw(ImGuiIO& io) {
|
||||
static_cast<unsigned long long>(status.renderer_stats.frame_count));
|
||||
ImGui::Text("render passes built: %llu",
|
||||
static_cast<unsigned long long>(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<unsigned long long>(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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ObservedCommandDesc> 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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#include "replay_ir.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
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<uint32_t>(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<ObservedPassDesc>& 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
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<ReplayCommandDesc> 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<ReplayPassDesc> passes;
|
||||
};
|
||||
|
||||
class ReplayIrBuilder {
|
||||
public:
|
||||
ReplayFrame BuildBootstrapFrame(uint64_t frame_index) const;
|
||||
ReplayFrame Build(const FrontendFrameSummary& summary,
|
||||
const std::vector<ObservedPassDesc>& passes,
|
||||
const NativeFramePlan& frame_plan) const;
|
||||
};
|
||||
|
||||
const char* ToString(ReplayPassRole role);
|
||||
|
||||
} // namespace ac6::renderer
|
||||
Reference in New Issue
Block a user