Files
AC6_recomp/src/ac6_native_graphics.cpp
T
2026-04-14 12:37:39 +03:00

1481 lines
66 KiB
C++

#include "ac6_native_graphics.h"
#include <algorithm>
#include <array>
#include <cstring>
#include <memory>
#include <mutex>
#include <utility>
#include <rex/cvar.h>
#include <rex/graphics/d3d12/graphics_system.h>
#include <rex/logging.h>
#include <rex/system/interfaces/graphics.h>
#include "d3d_hooks.h"
#if REX_HAS_D3D12
#include <native/ui/d3d12/d3d12_presenter.h>
#include <native/ui/d3d12/d3d12_provider.h>
#include <native/ui/d3d12/d3d12_submission_tracker.h>
#include <native/ui/d3d12/d3d12_util.h>
#include <native/ui/immediate_drawer.h>
#endif
REXCVAR_DEFINE_BOOL(ac6_native_graphics_bootstrap, true, "AC6/Render",
"Use the experimental native graphics bootstrap backend");
REXCVAR_DEFINE_BOOL(
ac6_native_graphics_placeholder_present, false, "AC6/Render",
"Allow the native graphics bootstrap backend to replace the legacy swap path with a "
"preview frame generated by the experimental native replay compositor");
REXCVAR_DEFINE_BOOL(
ac6_allow_gpu_trace_stream, false, "AC6/Render",
"Allow legacy GPU trace streaming during AC6 native graphics experiments");
namespace ac6::graphics {
namespace {
using rex::X_STATUS;
#if REX_HAS_D3D12
namespace shaders {
#include "thirdparty/rexglue-sdk/src/ui/shaders/bytecode/d3d12_5_1/immediate_ps.h"
#include "thirdparty/rexglue-sdk/src/ui/shaders/bytecode/d3d12_5_1/immediate_vs.h"
} // namespace shaders
#endif
std::mutex g_native_graphics_status_mutex;
NativeGraphicsStatusSnapshot g_native_graphics_status{};
struct CapturedFrameEvent {
enum class Type {
kDraw,
kClear,
kResolve,
};
uint32_t sequence = 0;
Type type = Type::kDraw;
const ac6::d3d::ShadowState* shadow_state = nullptr;
};
struct ReplayPassCandidate {
ac6::d3d::ShadowState binding{};
uint32_t start_sequence = 0;
uint32_t end_sequence = 0;
uint32_t draw_count = 0;
uint32_t clear_count = 0;
uint32_t resolve_count = 0;
};
struct ReplayCandidateKey {
uint32_t rt0 = 0;
uint32_t depth_stencil = 0;
uint32_t viewport_x = 0;
uint32_t viewport_y = 0;
uint32_t viewport_width = 0;
uint32_t viewport_height = 0;
};
constexpr uint32_t kSelectedPassPreviewColorSampleCount = 4;
constexpr uint32_t kSelectedPassPreviewStepCount = 4;
using FloatColor = std::array<float, 4>;
struct SelectedPassClearStep {
uint32_t color = 0;
uint32_t rect_count = 0;
std::array<ac6::d3d::ClearRect, ac6::d3d::kMaxClearRectsPerRecord> rects{};
};
struct SelectedPassPreviewData {
SelectedPassPreviewSummary summary{};
std::array<SelectedPassClearStep, kSelectedPassPreviewStepCount> clear_steps{};
uint32_t clear_step_count = 0;
};
bool SamePassBinding(const ac6::d3d::ShadowState& left,
const ac6::d3d::ShadowState& right) {
return left.render_targets[0] == right.render_targets[0] &&
left.depth_stencil == right.depth_stencil &&
left.viewport.width == right.viewport.width &&
left.viewport.height == right.viewport.height;
}
bool SameReplayCandidate(const ReplayCandidateKey& left, const ReplayCandidateKey& right) {
return left.rt0 == right.rt0 && left.depth_stencil == right.depth_stencil &&
left.viewport_x == right.viewport_x && left.viewport_y == right.viewport_y &&
left.viewport_width == right.viewport_width &&
left.viewport_height == right.viewport_height;
}
ReplayCandidateKey MakeReplayCandidateKey(const NativeReplayPlanSummary& replay_plan) {
return ReplayCandidateKey{replay_plan.present_candidate_rt0,
replay_plan.present_candidate_depth_stencil,
replay_plan.present_candidate_viewport_x,
replay_plan.present_candidate_viewport_y,
replay_plan.present_candidate_viewport_width,
replay_plan.present_candidate_viewport_height};
}
bool SequenceInSelectedPass(uint32_t sequence, const NativeReplayPlanSummary& replay_plan) {
return replay_plan.valid && sequence >= replay_plan.selected_pass_start_sequence &&
sequence <= replay_plan.selected_pass_end_sequence;
}
FloatColor DecodeArgbColor(uint32_t packed_color) {
return {float((packed_color >> 16) & 0xFFu) / 255.0f,
float((packed_color >> 8) & 0xFFu) / 255.0f,
float(packed_color & 0xFFu) / 255.0f, 1.0f};
}
FloatColor MixColors(const FloatColor& left, const FloatColor& right, float t) {
const float clamped_t = std::clamp(t, 0.0f, 1.0f);
const float inverse_t = 1.0f - clamped_t;
return {left[0] * inverse_t + right[0] * clamped_t,
left[1] * inverse_t + right[1] * clamped_t,
left[2] * inverse_t + right[2] * clamped_t, 1.0f};
}
uint32_t PackImmediateColor(const FloatColor& color) {
const auto pack_channel = [](float value) -> uint32_t {
return uint32_t(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f);
};
const uint32_t r = pack_channel(color[0]);
const uint32_t g = pack_channel(color[1]);
const uint32_t b = pack_channel(color[2]);
const uint32_t a = pack_channel(color[3]);
return r | (g << 8) | (b << 16) | (a << 24);
}
uint32_t ScoreReplayPass(const ReplayPassCandidate& pass,
const rex::system::GraphicsSwapSubmission& submission,
bool is_last_pass) {
uint32_t score = 0;
if (pass.binding.viewport.width == submission.frontbuffer_width &&
pass.binding.viewport.height == submission.frontbuffer_height) {
score += 100;
}
if (pass.draw_count) {
score += 40 + std::min<uint32_t>(pass.draw_count, 64);
}
if (pass.resolve_count) {
score += 20;
}
if (pass.binding.render_targets[0]) {
score += 10;
}
if (is_last_pass) {
score += 25;
}
return score;
}
NativeReplayPlanSummary AnalyzeReplayPlan(
const ac6::d3d::FrameCaptureSnapshot& frame_capture,
const rex::system::GraphicsSwapSubmission& submission) {
NativeReplayPlanSummary summary;
summary.frame_index = frame_capture.frame_index;
std::vector<CapturedFrameEvent> events;
events.reserve(frame_capture.draws.size() + frame_capture.clears.size() +
frame_capture.resolves.size());
for (const auto& draw : frame_capture.draws) {
events.push_back(
{draw.sequence, CapturedFrameEvent::Type::kDraw, &draw.shadow_state});
}
for (const auto& clear : frame_capture.clears) {
events.push_back(
{clear.sequence, CapturedFrameEvent::Type::kClear, &clear.shadow_state});
}
for (const auto& resolve : frame_capture.resolves) {
events.push_back(
{resolve.sequence, CapturedFrameEvent::Type::kResolve, &resolve.shadow_state});
}
if (events.empty()) {
return summary;
}
std::sort(events.begin(), events.end(),
[](const CapturedFrameEvent& left, const CapturedFrameEvent& right) {
return left.sequence < right.sequence;
});
std::vector<ReplayPassCandidate> passes;
ReplayPassCandidate current_pass;
bool current_pass_valid = false;
auto flush_pass = [&]() {
if (!current_pass_valid) {
return;
}
passes.push_back(current_pass);
current_pass = {};
current_pass_valid = false;
};
for (const CapturedFrameEvent& event : events) {
if (!event.shadow_state) {
continue;
}
if (!current_pass_valid || !SamePassBinding(current_pass.binding, *event.shadow_state)) {
flush_pass();
current_pass.binding = *event.shadow_state;
current_pass.start_sequence = event.sequence;
current_pass_valid = true;
}
current_pass.end_sequence = event.sequence;
switch (event.type) {
case CapturedFrameEvent::Type::kDraw:
++summary.draw_event_count;
++current_pass.draw_count;
break;
case CapturedFrameEvent::Type::kClear:
++summary.clear_event_count;
++current_pass.clear_count;
break;
case CapturedFrameEvent::Type::kResolve:
++summary.resolve_event_count;
++current_pass.resolve_count;
break;
}
}
flush_pass();
summary.pass_count = static_cast<uint32_t>(passes.size());
if (!passes.empty()) {
summary.first_pass_draw_count = passes.front().draw_count;
summary.last_pass_draw_count = passes.back().draw_count;
}
uint32_t best_score = 0;
uint32_t best_index = 0;
bool best_index_valid = false;
for (uint32_t i = 0; i < passes.size(); ++i) {
const ReplayPassCandidate& pass = passes[i];
summary.largest_pass_draw_count =
std::max(summary.largest_pass_draw_count, pass.draw_count);
bool matches_swap_size = pass.binding.viewport.width == submission.frontbuffer_width &&
pass.binding.viewport.height == submission.frontbuffer_height;
if (matches_swap_size) {
++summary.swap_size_match_pass_count;
}
bool is_last_pass = (i + 1) == passes.size();
uint32_t score = ScoreReplayPass(pass, submission, is_last_pass);
if (!best_index_valid || score > best_score ||
(score == best_score && i > best_index)) {
best_index_valid = true;
best_index = i;
best_score = score;
}
}
if (best_index_valid) {
const ReplayPassCandidate& selected_pass = passes[best_index];
summary.selected_pass_index = best_index;
summary.selected_pass_score = best_score;
summary.selected_pass_start_sequence = selected_pass.start_sequence;
summary.selected_pass_end_sequence = selected_pass.end_sequence;
summary.selected_pass_draw_count = selected_pass.draw_count;
summary.selected_pass_clear_count = selected_pass.clear_count;
summary.selected_pass_resolve_count = selected_pass.resolve_count;
summary.selected_pass_is_last = (best_index + 1) == passes.size();
summary.selected_pass_has_resolve = selected_pass.resolve_count != 0;
summary.present_candidate_rt0 = selected_pass.binding.render_targets[0];
summary.present_candidate_depth_stencil = selected_pass.binding.depth_stencil;
summary.present_candidate_viewport_x = selected_pass.binding.viewport.x;
summary.present_candidate_viewport_y = selected_pass.binding.viewport.y;
summary.present_candidate_viewport_width = selected_pass.binding.viewport.width;
summary.present_candidate_viewport_height = selected_pass.binding.viewport.height;
summary.present_candidate_matches_swap_size =
summary.present_candidate_viewport_width == submission.frontbuffer_width &&
summary.present_candidate_viewport_height == submission.frontbuffer_height;
summary.valid = true;
}
return summary;
}
SelectedPassPreviewData BuildSelectedPassPreview(
const ac6::d3d::FrameCaptureSnapshot& frame_capture,
const NativeReplayPlanSummary& replay_plan) {
SelectedPassPreviewData preview;
if (!replay_plan.valid) {
return preview;
}
preview.summary.valid = true;
preview.summary.draw_count = replay_plan.selected_pass_draw_count;
preview.summary.clear_count = replay_plan.selected_pass_clear_count;
preview.summary.resolve_count = replay_plan.selected_pass_resolve_count;
bool has_first_clear = false;
for (const auto& clear : frame_capture.clears) {
if (!SequenceInSelectedPass(clear.sequence, replay_plan)) {
continue;
}
if (!has_first_clear) {
preview.summary.first_clear_color = clear.color;
has_first_clear = true;
}
preview.summary.last_clear_color = clear.color;
preview.summary.using_clear_fill = true;
preview.summary.sampled_clear_rect_count += clear.captured_rect_count;
if (preview.clear_step_count < preview.clear_steps.size()) {
SelectedPassClearStep& step = preview.clear_steps[preview.clear_step_count++];
step.color = clear.color;
step.rect_count = clear.captured_rect_count;
step.rects = clear.rects;
} else {
SelectedPassClearStep& step = preview.clear_steps.back();
step.color = clear.color;
step.rect_count = clear.captured_rect_count;
step.rects = clear.rects;
}
}
preview.summary.sampled_clear_color_count = preview.clear_step_count;
return preview;
}
#if REX_HAS_D3D12
class Ac6NativeGraphicsSystem final : public rex::graphics::d3d12::D3D12GraphicsSystem {
public:
X_STATUS Setup(rex::runtime::FunctionDispatcher* function_dispatcher,
rex::system::KernelState* kernel_state,
rex::ui::WindowedAppContext* app_context,
bool with_presentation) override {
X_STATUS status = rex::graphics::d3d12::D3D12GraphicsSystem::Setup(
function_dispatcher, kernel_state, app_context, with_presentation);
if (XFAILED(status)) {
return status;
}
d3d12_provider_ = static_cast<rex::ui::d3d12::D3D12Provider*>(provider());
d3d12_presenter_ = with_presentation
? static_cast<rex::ui::d3d12::D3D12Presenter*>(presenter())
: nullptr;
if (!d3d12_provider_) {
REXLOG_ERROR("AC6 native graphics bootstrap failed to acquire the D3D12 provider");
rex::graphics::d3d12::D3D12GraphicsSystem::Shutdown();
return X_STATUS_UNSUCCESSFUL;
}
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
g_native_graphics_status.backend_active = true;
g_native_graphics_status.provider_ready = d3d12_provider_ != nullptr;
g_native_graphics_status.presenter_ready = d3d12_presenter_ != nullptr;
}
if (REXCVAR_GET(ac6_native_graphics_placeholder_present)) {
REXLOG_WARN(
"AC6 native graphics bootstrap backend is active. The legacy D3D12 "
"graphics core remains enabled for compatibility, while direct swap "
"presentation is intercepted by the native replay preview path.");
} else {
REXLOG_WARN(
"AC6 native graphics bootstrap backend is active. The legacy D3D12 "
"graphics core remains enabled for compatibility, and direct swap "
"submissions are observed natively while legacy PM4 presentation "
"remains authoritative.");
}
return X_STATUS_SUCCESS;
}
void Shutdown() override {
ShutdownPlaceholderRefreshResources();
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
g_native_graphics_status.backend_active = false;
g_native_graphics_status.provider_ready = false;
g_native_graphics_status.presenter_ready = false;
g_native_graphics_status.placeholder_resources_initialized = false;
g_native_graphics_status.last_swap_intercepted = false;
g_native_graphics_status.last_swap_fell_back = false;
}
d3d12_presenter_ = nullptr;
d3d12_provider_ = nullptr;
last_swap_submission_ = {};
swap_count_ = 0;
last_replay_plan_ = {};
last_selected_pass_preview_ = {};
last_selected_candidate_ = {};
last_selected_candidate_valid_ = false;
selected_candidate_streak_ = 0;
logged_first_swap_ = false;
logged_first_present_ = false;
logged_passthrough_swap_ = false;
logged_refresh_failure_ = false;
last_present_used_raster_replay_ = false;
rex::graphics::d3d12::D3D12GraphicsSystem::Shutdown();
}
bool HandleVideoSwap(const rex::system::GraphicsSwapSubmission& submission) override {
last_swap_submission_ = submission;
++swap_count_;
last_present_used_raster_replay_ = false;
ac6::d3d::FrameCaptureSnapshot frame_capture = ac6::d3d::GetFrameCapture();
NativeReplayPlanSummary replay_plan = AnalyzeReplayPlan(frame_capture, submission);
if (replay_plan.valid) {
ReplayCandidateKey candidate_key = MakeReplayCandidateKey(replay_plan);
if (last_selected_candidate_valid_ &&
SameReplayCandidate(last_selected_candidate_, candidate_key)) {
++selected_candidate_streak_;
} else {
last_selected_candidate_ = candidate_key;
last_selected_candidate_valid_ = true;
selected_candidate_streak_ = 1;
}
replay_plan.selected_pass_streak = selected_candidate_streak_;
replay_plan.selected_pass_is_stable = selected_candidate_streak_ >= 3;
} else {
last_selected_candidate_valid_ = false;
selected_candidate_streak_ = 0;
}
last_replay_plan_ = replay_plan;
last_selected_pass_preview_ = BuildSelectedPassPreview(frame_capture, replay_plan);
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
g_native_graphics_status.total_swap_count = swap_count_;
g_native_graphics_status.last_frontbuffer_virtual_address =
submission.frontbuffer_virtual_address;
g_native_graphics_status.last_frontbuffer_physical_address =
submission.frontbuffer_physical_address;
g_native_graphics_status.last_frontbuffer_width = submission.frontbuffer_width;
g_native_graphics_status.last_frontbuffer_height = submission.frontbuffer_height;
g_native_graphics_status.last_texture_format = submission.texture_format;
g_native_graphics_status.last_color_space = submission.color_space;
g_native_graphics_status.capture_summary = ac6::d3d::GetFrameCaptureSummary();
g_native_graphics_status.replay_plan = replay_plan;
g_native_graphics_status.selected_pass_preview = last_selected_pass_preview_.summary;
g_native_graphics_status.last_swap_intercepted = false;
g_native_graphics_status.last_swap_fell_back = false;
}
if (!logged_first_swap_) {
logged_first_swap_ = true;
REXLOG_WARN(
"AC6 native graphics bootstrap received the first direct swap "
"(fb_va={:08X}, fb_pa={:08X}, {}x{}, fmt={:08X})",
submission.frontbuffer_virtual_address, submission.frontbuffer_physical_address,
submission.frontbuffer_width, submission.frontbuffer_height, submission.texture_format);
}
if (!REXCVAR_GET(ac6_native_graphics_placeholder_present)) {
if (!logged_passthrough_swap_) {
logged_passthrough_swap_ = true;
REXLOG_WARN(
"AC6 native graphics bootstrap is leaving direct swap presentation "
"on the legacy PM4 path because ac6_native_graphics_placeholder_present=false");
}
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
++g_native_graphics_status.fallback_swap_count;
g_native_graphics_status.last_swap_fell_back = true;
}
return false;
}
if (!EnsurePlaceholderRefreshResources()) {
if (!logged_refresh_failure_) {
logged_refresh_failure_ = true;
REXLOG_WARN(
"AC6 native graphics bootstrap could not initialize diagnostic "
"direct-swap replay resources; falling back to the legacy PM4 swap path");
}
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
++g_native_graphics_status.fallback_swap_count;
g_native_graphics_status.last_swap_fell_back = true;
}
return false;
}
if (!RefreshPlaceholderFrame(submission)) {
if (!logged_refresh_failure_) {
logged_refresh_failure_ = true;
REXLOG_WARN(
"AC6 native graphics bootstrap could not refresh the diagnostic "
"direct-swap replay frame; falling back to the legacy PM4 swap path");
}
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
++g_native_graphics_status.fallback_swap_count;
g_native_graphics_status.last_swap_fell_back = true;
}
return false;
}
if (!logged_first_present_) {
logged_first_present_ = true;
REXLOG_WARN(
"AC6 native graphics bootstrap is now presenting a raster replay "
"preview frame through the direct swap path");
}
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
++g_native_graphics_status.intercepted_swap_count;
g_native_graphics_status.selected_pass_preview.using_raster_replay =
last_present_used_raster_replay_;
g_native_graphics_status.last_swap_intercepted = true;
}
DispatchInterruptCallback(0, 2);
return true;
}
private:
static constexpr uint32_t kPlaceholderRefreshSlotCount = 3;
static constexpr uint32_t kPlaceholderHeartbeatPeriod = 120;
static constexpr uint32_t kRasterPreviewMaxRectCount = 64;
static constexpr uint32_t kRasterPreviewMaxVertexCount = kRasterPreviewMaxRectCount * 6;
struct PlaceholderRefreshSlot {
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> command_allocator;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> shader_visible_uav_heap;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> cpu_uav_heap;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> rtv_heap;
Microsoft::WRL::ComPtr<ID3D12Resource> raster_render_target;
Microsoft::WRL::ComPtr<ID3D12Resource> vertex_upload_buffer;
uint8_t* vertex_upload_mapping = nullptr;
uint32_t raster_target_width = 0;
uint32_t raster_target_height = 0;
uint64_t last_submission = 0;
};
bool EnsurePlaceholderRefreshResources() {
if (placeholder_resources_initialized_) {
return true;
}
if (!InitializePlaceholderRefreshResources()) {
return false;
}
placeholder_resources_initialized_ = true;
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
g_native_graphics_status.placeholder_resources_initialized = true;
}
return true;
}
bool InitializeRasterPreviewResources(ID3D12Device* device) {
D3D12_ROOT_PARAMETER root_parameters[3] = {};
D3D12_DESCRIPTOR_RANGE descriptor_range_texture = {};
D3D12_DESCRIPTOR_RANGE descriptor_range_sampler = {};
root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
root_parameters[0].DescriptorTable.pDescriptorRanges = &descriptor_range_texture;
root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
descriptor_range_texture.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
descriptor_range_texture.NumDescriptors = 1;
descriptor_range_texture.BaseShaderRegister = 0;
descriptor_range_texture.RegisterSpace = 0;
descriptor_range_texture.OffsetInDescriptorsFromTableStart = 0;
root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_range_sampler;
root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
descriptor_range_sampler.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
descriptor_range_sampler.NumDescriptors = 1;
descriptor_range_sampler.BaseShaderRegister = 0;
descriptor_range_sampler.RegisterSpace = 0;
descriptor_range_sampler.OffsetInDescriptorsFromTableStart = 0;
root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
root_parameters[2].Constants.ShaderRegister = 0;
root_parameters[2].Constants.RegisterSpace = 0;
root_parameters[2].Constants.Num32BitValues = 2;
root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {};
root_signature_desc.NumParameters = uint32_t(std::size(root_parameters));
root_signature_desc.pParameters = root_parameters;
root_signature_desc.NumStaticSamplers = 0;
root_signature_desc.pStaticSamplers = nullptr;
root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
raster_preview_root_signature_.Attach(
rex::ui::d3d12::util::CreateRootSignature(*d3d12_provider_, root_signature_desc));
if (!raster_preview_root_signature_) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to create the raster replay "
"root signature");
return false;
}
D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_desc = {};
pipeline_desc.pRootSignature = raster_preview_root_signature_.Get();
pipeline_desc.VS.pShaderBytecode = shaders::immediate_vs;
pipeline_desc.VS.BytecodeLength = sizeof(shaders::immediate_vs);
pipeline_desc.PS.pShaderBytecode = shaders::immediate_ps;
pipeline_desc.PS.BytecodeLength = sizeof(shaders::immediate_ps);
D3D12_RENDER_TARGET_BLEND_DESC& blend_desc = pipeline_desc.BlendState.RenderTarget[0];
blend_desc.BlendEnable = TRUE;
blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
blend_desc.BlendOp = D3D12_BLEND_OP_ADD;
blend_desc.SrcBlendAlpha = D3D12_BLEND_ONE;
blend_desc.DestBlendAlpha = D3D12_BLEND_ONE;
blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
pipeline_desc.SampleMask = UINT_MAX;
pipeline_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
pipeline_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
pipeline_desc.RasterizerState.FrontCounterClockwise = FALSE;
pipeline_desc.RasterizerState.DepthClipEnable = TRUE;
D3D12_INPUT_ELEMENT_DESC input_elements[3] = {};
input_elements[0].SemanticName = "POSITION";
input_elements[0].Format = DXGI_FORMAT_R32G32_FLOAT;
input_elements[0].AlignedByteOffset = offsetof(rex::ui::ImmediateVertex, x);
input_elements[1].SemanticName = "TEXCOORD";
input_elements[1].Format = DXGI_FORMAT_R32G32_FLOAT;
input_elements[1].AlignedByteOffset = offsetof(rex::ui::ImmediateVertex, u);
input_elements[2].SemanticName = "COLOR";
input_elements[2].Format = DXGI_FORMAT_R8G8B8A8_UNORM;
input_elements[2].AlignedByteOffset = offsetof(rex::ui::ImmediateVertex, color);
pipeline_desc.InputLayout.pInputElementDescs = input_elements;
pipeline_desc.InputLayout.NumElements = uint32_t(std::size(input_elements));
pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pipeline_desc.NumRenderTargets = 1;
pipeline_desc.RTVFormats[0] = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat;
pipeline_desc.SampleDesc.Count = 1;
if (FAILED(device->CreateGraphicsPipelineState(&pipeline_desc,
IID_PPV_ARGS(&raster_preview_pipeline_)))) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to create the raster replay "
"pipeline");
return false;
}
D3D12_DESCRIPTOR_HEAP_DESC view_heap_desc = {};
view_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
view_heap_desc.NumDescriptors = 1;
view_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
if (FAILED(device->CreateDescriptorHeap(&view_heap_desc,
IID_PPV_ARGS(&raster_preview_view_heap_)))) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to create the raster replay "
"view heap");
return false;
}
D3D12_SHADER_RESOURCE_VIEW_DESC texture_view_desc = {};
texture_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texture_view_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
texture_view_desc.Shader4ComponentMapping =
D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1);
texture_view_desc.Texture2D.MostDetailedMip = 0;
texture_view_desc.Texture2D.MipLevels = 1;
texture_view_desc.Texture2D.PlaneSlice = 0;
texture_view_desc.Texture2D.ResourceMinLODClamp = 0.0f;
device->CreateShaderResourceView(
nullptr, &texture_view_desc,
raster_preview_view_heap_->GetCPUDescriptorHandleForHeapStart());
D3D12_DESCRIPTOR_HEAP_DESC sampler_heap_desc = {};
sampler_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
sampler_heap_desc.NumDescriptors = 1;
sampler_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
if (FAILED(device->CreateDescriptorHeap(&sampler_heap_desc,
IID_PPV_ARGS(&raster_preview_sampler_heap_)))) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to create the raster replay "
"sampler heap");
return false;
}
D3D12_SAMPLER_DESC sampler_desc = {};
sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sampler_desc.MaxAnisotropy = 1;
device->CreateSampler(&sampler_desc,
raster_preview_sampler_heap_->GetCPUDescriptorHandleForHeapStart());
D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc = {};
rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtv_heap_desc.NumDescriptors = 1;
rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtv_heap_desc.NodeMask = 0;
D3D12_RESOURCE_DESC upload_buffer_desc = {};
rex::ui::d3d12::util::FillBufferResourceDesc(
upload_buffer_desc, sizeof(rex::ui::ImmediateVertex) * kRasterPreviewMaxVertexCount,
D3D12_RESOURCE_FLAG_NONE);
for (PlaceholderRefreshSlot& slot : placeholder_refresh_slots_) {
if (FAILED(device->CreateDescriptorHeap(&rtv_heap_desc, IID_PPV_ARGS(&slot.rtv_heap)))) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to create a raster replay "
"RTV heap");
return false;
}
if (FAILED(device->CreateCommittedResource(
&rex::ui::d3d12::util::kHeapPropertiesUpload, D3D12_HEAP_FLAG_NONE,
&upload_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS(&slot.vertex_upload_buffer)))) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to create a raster replay "
"vertex upload buffer");
return false;
}
D3D12_RANGE read_range = {0, 0};
if (FAILED(slot.vertex_upload_buffer->Map(0, &read_range,
reinterpret_cast<void**>(&slot.vertex_upload_mapping)))) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to map a raster replay "
"vertex upload buffer");
return false;
}
}
return true;
}
bool EnsureRasterPreviewRenderTarget(PlaceholderRefreshSlot& slot, uint32_t width,
uint32_t height) {
if (slot.raster_render_target && slot.raster_target_width == width &&
slot.raster_target_height == height) {
return true;
}
slot.raster_render_target.Reset();
slot.raster_target_width = 0;
slot.raster_target_height = 0;
D3D12_CLEAR_VALUE clear_value = {};
clear_value.Format = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat;
clear_value.Color[0] = 0.0f;
clear_value.Color[1] = 0.0f;
clear_value.Color[2] = 0.0f;
clear_value.Color[3] = 1.0f;
D3D12_RESOURCE_DESC render_target_desc = {};
render_target_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
render_target_desc.Alignment = 0;
render_target_desc.Width = width;
render_target_desc.Height = height;
render_target_desc.DepthOrArraySize = 1;
render_target_desc.MipLevels = 1;
render_target_desc.Format = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat;
render_target_desc.SampleDesc.Count = 1;
render_target_desc.SampleDesc.Quality = 0;
render_target_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
render_target_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
if (FAILED(d3d12_provider_->GetDevice()->CreateCommittedResource(
&rex::ui::d3d12::util::kHeapPropertiesDefault,
d3d12_provider_->GetHeapFlagCreateNotZeroed(), &render_target_desc,
D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
IID_PPV_ARGS(&slot.raster_render_target)))) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to create a {}x{} raster replay "
"render target",
width, height);
return false;
}
d3d12_provider_->GetDevice()->CreateRenderTargetView(
slot.raster_render_target.Get(), nullptr, slot.rtv_heap->GetCPUDescriptorHandleForHeapStart());
slot.raster_target_width = width;
slot.raster_target_height = height;
return true;
}
bool InitializePlaceholderRefreshResources() {
auto fail = [this](const char* message) {
REXLOG_ERROR("{}", message);
ShutdownPlaceholderRefreshResources();
return false;
};
ID3D12Device* device = d3d12_provider_->GetDevice();
ID3D12CommandQueue* direct_queue = d3d12_provider_->GetDirectQueue();
if (!device || !direct_queue) {
return fail("AC6 native graphics bootstrap D3D12 provider is missing device or queue");
}
if (!placeholder_submission_tracker_.Initialize(device, direct_queue)) {
return fail("AC6 native graphics bootstrap failed to create the placeholder submission tracker");
}
D3D12_DESCRIPTOR_HEAP_DESC uav_heap_desc = {};
uav_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
uav_heap_desc.NumDescriptors = 1;
uav_heap_desc.NodeMask = 0;
for (uint32_t i = 0; i < kPlaceholderRefreshSlotCount; ++i) {
PlaceholderRefreshSlot& slot = placeholder_refresh_slots_[i];
if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&slot.command_allocator)))) {
return fail("AC6 native graphics bootstrap failed to create a placeholder command allocator");
}
uav_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
if (FAILED(device->CreateDescriptorHeap(&uav_heap_desc,
IID_PPV_ARGS(&slot.shader_visible_uav_heap)))) {
return fail(
"AC6 native graphics bootstrap failed to create a shader-visible UAV heap");
}
uav_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
if (FAILED(device->CreateDescriptorHeap(&uav_heap_desc, IID_PPV_ARGS(&slot.cpu_uav_heap)))) {
return fail("AC6 native graphics bootstrap failed to create a CPU UAV heap");
}
}
if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
placeholder_refresh_slots_[0].command_allocator.Get(),
nullptr, IID_PPV_ARGS(&placeholder_command_list_)))) {
return fail("AC6 native graphics bootstrap failed to create the placeholder command list");
}
if (FAILED(placeholder_command_list_->Close())) {
return fail("AC6 native graphics bootstrap failed to close the placeholder command list");
}
if (!InitializeRasterPreviewResources(device)) {
return fail("AC6 native graphics bootstrap failed to initialize raster replay resources");
}
placeholder_refresh_slot_index_ = 0;
return true;
}
void ShutdownPlaceholderRefreshResources() {
placeholder_submission_tracker_.Shutdown();
placeholder_command_list_.Reset();
raster_preview_pipeline_.Reset();
raster_preview_root_signature_.Reset();
raster_preview_view_heap_.Reset();
raster_preview_sampler_heap_.Reset();
placeholder_refresh_slot_index_ = 0;
placeholder_resources_initialized_ = false;
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
g_native_graphics_status.placeholder_resources_initialized = false;
}
for (PlaceholderRefreshSlot& slot : placeholder_refresh_slots_) {
slot.last_submission = 0;
if (slot.vertex_upload_buffer && slot.vertex_upload_mapping) {
slot.vertex_upload_buffer->Unmap(0, nullptr);
}
slot.vertex_upload_mapping = nullptr;
slot.vertex_upload_buffer.Reset();
slot.raster_render_target.Reset();
slot.rtv_heap.Reset();
slot.raster_target_width = 0;
slot.raster_target_height = 0;
slot.cpu_uav_heap.Reset();
slot.shader_visible_uav_heap.Reset();
slot.command_allocator.Reset();
}
}
bool RefreshPlaceholderFrame(const rex::system::GraphicsSwapSubmission& submission) {
if (!d3d12_presenter_ || !submission.frontbuffer_width || !submission.frontbuffer_height) {
return false;
}
return d3d12_presenter_->RefreshGuestOutput(
submission.frontbuffer_width, submission.frontbuffer_height,
submission.frontbuffer_width, submission.frontbuffer_height,
[this, submission](rex::ui::Presenter::GuestOutputRefreshContext& context) {
return RecordPlaceholderFrame(
static_cast<rex::ui::d3d12::D3D12Presenter::D3D12GuestOutputRefreshContext&>(
context),
submission);
});
}
bool SubmitPlaceholderCommandList(PlaceholderRefreshSlot& slot) {
if (FAILED(placeholder_command_list_->Close())) {
REXLOG_ERROR("AC6 native graphics bootstrap failed to close the placeholder command list");
return false;
}
ID3D12CommandList* execute_command_list = placeholder_command_list_.Get();
d3d12_provider_->GetDirectQueue()->ExecuteCommandLists(1, &execute_command_list);
slot.last_submission = placeholder_submission_tracker_.GetCurrentSubmission();
if (!placeholder_submission_tracker_.NextSubmission()) {
REXLOG_WARN(
"AC6 native graphics bootstrap could not signal the placeholder "
"refresh fence immediately");
}
placeholder_refresh_slot_index_ =
(placeholder_refresh_slot_index_ + 1) % kPlaceholderRefreshSlotCount;
return true;
}
bool TryRecordRasterPlaceholderFrame(
PlaceholderRefreshSlot& slot, ID3D12Resource* guest_output_resource,
const rex::system::GraphicsSwapSubmission& submission) {
if (!raster_preview_pipeline_ || !raster_preview_root_signature_ || !raster_preview_view_heap_ ||
!raster_preview_sampler_heap_ || !slot.rtv_heap || !slot.vertex_upload_mapping ||
!guest_output_resource) {
return false;
}
if (!EnsureRasterPreviewRenderTarget(slot, submission.frontbuffer_width,
submission.frontbuffer_height)) {
return false;
}
const UINT width = submission.frontbuffer_width;
const UINT height = submission.frontbuffer_height;
const UINT title_band_height = std::max<UINT>(1, height / 8);
const UINT status_band_height = std::max<UINT>(1, height / 20);
const UINT heartbeat_width =
std::max<UINT>(1, UINT((uint64_t(width) *
(((swap_count_ - 1) % kPlaceholderHeartbeatPeriod) + 1)) /
kPlaceholderHeartbeatPeriod));
const NativeReplayPlanSummary& replay_plan = last_replay_plan_;
const SelectedPassPreviewData& selected_pass_preview = last_selected_pass_preview_;
const uint32_t candidate_seed =
replay_plan.present_candidate_rt0 ^ (replay_plan.present_candidate_depth_stencil * 33u) ^
(replay_plan.selected_pass_score * 2654435761u);
auto channel = [candidate_seed](uint32_t shift, float low, float high) {
const float t = float((candidate_seed >> shift) & 0xFFu) / 255.0f;
return low + (high - low) * t;
};
const FloatColor background_color = {replay_plan.valid ? 0.03f : 0.08f,
replay_plan.valid ? 0.04f : 0.06f,
replay_plan.valid ? 0.07f : 0.10f, 1.0f};
const FloatColor title_band_color = {
replay_plan.selected_pass_is_stable ? 0.18f
: (replay_plan.present_candidate_matches_swap_size ? 0.18f : 0.55f),
replay_plan.selected_pass_is_stable ? 0.62f
: (replay_plan.present_candidate_matches_swap_size ? 0.56f : 0.22f),
replay_plan.selected_pass_is_stable ? 0.46f
: (replay_plan.present_candidate_matches_swap_size ? 0.24f : 0.18f), 1.0f};
const FloatColor fallback_candidate_color = {channel(0, 0.20f, 0.92f),
channel(8, 0.18f, 0.75f),
channel(16, 0.16f, 0.88f), 1.0f};
const FloatColor resolve_color = {0.98f, 0.95f, 0.28f, 0.92f};
const FloatColor heartbeat_color = {0.96f, 0.92f, 0.22f, 1.0f};
const FloatColor candidate_base_color =
selected_pass_preview.summary.using_clear_fill
? DecodeArgbColor(selected_pass_preview.summary.last_clear_color)
: fallback_candidate_color;
const FloatColor stripe_color = MixColors(candidate_base_color, title_band_color, 0.40f);
const FloatColor candidate_outline_color =
MixColors(candidate_base_color, heartbeat_color, 0.55f);
const FloatColor score_color = MixColors(candidate_outline_color, heartbeat_color, 0.35f);
UINT candidate_left = 0;
UINT candidate_top = title_band_height;
UINT candidate_width = width;
UINT candidate_height = std::max<UINT>(1, height - title_band_height - status_band_height);
if (replay_plan.valid && replay_plan.present_candidate_viewport_width &&
replay_plan.present_candidate_viewport_height) {
candidate_left = std::min<UINT>(width - 1, replay_plan.present_candidate_viewport_x);
candidate_top = std::min<UINT>(height - 1, replay_plan.present_candidate_viewport_y);
candidate_width =
std::max<UINT>(1, std::min<UINT>(replay_plan.present_candidate_viewport_width,
width - candidate_left));
candidate_height =
std::max<UINT>(1, std::min<UINT>(replay_plan.present_candidate_viewport_height,
height - candidate_top));
}
const UINT candidate_right = std::min(width, candidate_left + candidate_width);
const UINT candidate_bottom = std::min(height, candidate_top + candidate_height);
std::array<rex::ui::ImmediateVertex, kRasterPreviewMaxVertexCount> vertices = {};
uint32_t vertex_count = 0;
auto push_rect = [&](float left, float top, float right, float bottom, FloatColor color) {
left = std::clamp(left, 0.0f, float(width));
top = std::clamp(top, 0.0f, float(height));
right = std::clamp(right, 0.0f, float(width));
bottom = std::clamp(bottom, 0.0f, float(height));
if (left >= right || top >= bottom || vertex_count + 6 > vertices.size()) {
return;
}
const uint32_t packed_color = PackImmediateColor(color);
auto emit_vertex = [&](float x, float y) {
rex::ui::ImmediateVertex& vertex = vertices[vertex_count++];
vertex.x = x;
vertex.y = y;
vertex.u = 0.0f;
vertex.v = 0.0f;
vertex.color = packed_color;
};
emit_vertex(left, top);
emit_vertex(right, top);
emit_vertex(right, bottom);
emit_vertex(left, top);
emit_vertex(right, bottom);
emit_vertex(left, bottom);
};
push_rect(float(candidate_left), float(candidate_top), float(candidate_right),
float(candidate_bottom), candidate_base_color);
const FloatColor clear_overlay_tint = {1.0f, 1.0f, 1.0f, 0.92f};
for (uint32_t i = 0; i < selected_pass_preview.clear_step_count; ++i) {
const SelectedPassClearStep& step = selected_pass_preview.clear_steps[i];
FloatColor step_color = DecodeArgbColor(step.color);
step_color[3] = clear_overlay_tint[3];
if (step.rect_count) {
for (uint32_t rect_index = 0; rect_index < step.rect_count; ++rect_index) {
const ac6::d3d::ClearRect& rect = step.rects[rect_index];
push_rect(float(rect.left), float(rect.top), float(rect.right), float(rect.bottom),
step_color);
}
} else {
push_rect(float(candidate_left), float(candidate_top), float(candidate_right),
float(candidate_bottom), step_color);
}
}
UINT resolve_band_height = 0;
if (replay_plan.valid && replay_plan.selected_pass_has_resolve) {
resolve_band_height = std::max<UINT>(1, candidate_height / 10);
push_rect(float(candidate_left), float(candidate_bottom - resolve_band_height),
float(candidate_right), float(candidate_bottom), resolve_color);
}
if (selected_pass_preview.summary.draw_count && candidate_width > 8 && candidate_height > 8) {
const UINT stripe_count =
std::min<UINT>(12, std::max<UINT>(1, 1 + (selected_pass_preview.summary.draw_count / 6)));
const UINT stripe_width =
std::max<UINT>(1, candidate_width / std::max<UINT>(24, stripe_count * 5));
const UINT stripe_top = candidate_top;
const UINT stripe_bottom =
candidate_bottom > resolve_band_height ? (candidate_bottom - resolve_band_height)
: candidate_bottom;
const UINT stripe_area_height =
stripe_bottom > stripe_top ? (stripe_bottom - stripe_top) : 0;
for (UINT i = 0; i < stripe_count && stripe_area_height; ++i) {
const UINT stripe_center_x =
candidate_left + ((i + 1) * candidate_width) / (stripe_count + 1);
const UINT stripe_left = stripe_center_x > stripe_width / 2
? stripe_center_x - stripe_width / 2
: candidate_left;
const UINT stripe_right = std::min(candidate_right, stripe_left + stripe_width);
const UINT stripe_height =
std::max<UINT>(1, stripe_area_height * (45 + ((i * 13) % 40)) / 100);
const UINT stripe_start = stripe_bottom > stripe_height ? (stripe_bottom - stripe_height)
: stripe_top;
FloatColor lane_color =
(i & 1u) ? MixColors(stripe_color, heartbeat_color, 0.22f)
: MixColors(stripe_color, background_color, 0.10f);
lane_color[3] = 0.42f;
push_rect(float(stripe_left), float(stripe_start), float(stripe_right),
float(stripe_bottom), lane_color);
}
}
if (candidate_width > 2 && candidate_height > 2) {
const UINT outline_thickness =
std::max<UINT>(1, std::min<UINT>(4, std::min(candidate_width, candidate_height) / 96 + 1));
const UINT bottom_outline_top =
candidate_bottom > outline_thickness ? (candidate_bottom - outline_thickness)
: candidate_top;
push_rect(float(candidate_left), float(candidate_top), float(candidate_right),
float(std::min(candidate_bottom, candidate_top + outline_thickness)),
candidate_outline_color);
push_rect(float(candidate_left), float(bottom_outline_top), float(candidate_right),
float(candidate_bottom), candidate_outline_color);
push_rect(float(candidate_left), float(candidate_top),
float(std::min(candidate_right, candidate_left + outline_thickness)),
float(candidate_bottom), candidate_outline_color);
push_rect(float(candidate_right > outline_thickness ? (candidate_right - outline_thickness)
: candidate_left),
float(candidate_top), float(candidate_right), float(candidate_bottom),
candidate_outline_color);
}
if (replay_plan.valid && replay_plan.pass_count) {
const UINT score_width = std::max<UINT>(
1, UINT((uint64_t(width) * std::min<uint32_t>(replay_plan.selected_pass_score, 220u)) /
220u));
push_rect(0.0f, float(title_band_height), float(score_width),
float(std::min(height, title_band_height + status_band_height)), score_color);
}
push_rect(0.0f, 0.0f, float(width), float(title_band_height), title_band_color);
push_rect(0.0f, float(height - status_band_height), float(heartbeat_width), float(height),
heartbeat_color);
D3D12_VIEWPORT viewport = {};
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.Width = float(width);
viewport.Height = float(height);
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
D3D12_RECT scissor = {0, 0, LONG(width), LONG(height)};
placeholder_command_list_->RSSetViewports(1, &viewport);
placeholder_command_list_->RSSetScissorRects(1, &scissor);
const D3D12_CPU_DESCRIPTOR_HANDLE rtv =
slot.rtv_heap->GetCPUDescriptorHandleForHeapStart();
placeholder_command_list_->OMSetRenderTargets(1, &rtv, TRUE, nullptr);
placeholder_command_list_->ClearRenderTargetView(rtv, background_color.data(), 0, nullptr);
if (vertex_count) {
std::memcpy(slot.vertex_upload_mapping, vertices.data(),
sizeof(rex::ui::ImmediateVertex) * vertex_count);
D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view = {};
vertex_buffer_view.BufferLocation = slot.vertex_upload_buffer->GetGPUVirtualAddress();
vertex_buffer_view.SizeInBytes = UINT(sizeof(rex::ui::ImmediateVertex) * vertex_count);
vertex_buffer_view.StrideInBytes = UINT(sizeof(rex::ui::ImmediateVertex));
ID3D12DescriptorHeap* descriptor_heaps[] = {raster_preview_view_heap_.Get(),
raster_preview_sampler_heap_.Get()};
placeholder_command_list_->SetDescriptorHeaps(uint32_t(std::size(descriptor_heaps)),
descriptor_heaps);
placeholder_command_list_->SetGraphicsRootSignature(raster_preview_root_signature_.Get());
const float coordinate_space_size_inv[2] = {1.0f / float(width), 1.0f / float(height)};
placeholder_command_list_->SetGraphicsRoot32BitConstants(2, 2, coordinate_space_size_inv, 0);
placeholder_command_list_->SetGraphicsRootDescriptorTable(
0, raster_preview_view_heap_->GetGPUDescriptorHandleForHeapStart());
placeholder_command_list_->SetGraphicsRootDescriptorTable(
1, raster_preview_sampler_heap_->GetGPUDescriptorHandleForHeapStart());
placeholder_command_list_->SetPipelineState(raster_preview_pipeline_.Get());
placeholder_command_list_->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
placeholder_command_list_->IASetVertexBuffers(0, 1, &vertex_buffer_view);
placeholder_command_list_->DrawInstanced(vertex_count, 1, 0, 0);
}
D3D12_RESOURCE_BARRIER barriers[3] = {};
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Transition.pResource = slot.raster_render_target.Get();
barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[1].Transition.pResource = guest_output_resource;
barriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barriers[1].Transition.StateBefore = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
placeholder_command_list_->ResourceBarrier(2, barriers);
D3D12_TEXTURE_COPY_LOCATION copy_dest = {};
copy_dest.pResource = guest_output_resource;
copy_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
copy_dest.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION copy_source = {};
copy_source.pResource = slot.raster_render_target.Get();
copy_source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
copy_source.SubresourceIndex = 0;
placeholder_command_list_->CopyTextureRegion(&copy_dest, 0, 0, 0, &copy_source, nullptr);
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
barriers[0].Transition.StateAfter = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState;
barriers[0].Transition.pResource = guest_output_resource;
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[1].Transition.pResource = slot.raster_render_target.Get();
placeholder_command_list_->ResourceBarrier(2, barriers);
return true;
}
bool RecordPlaceholderFrame(
rex::ui::d3d12::D3D12Presenter::D3D12GuestOutputRefreshContext& context,
const rex::system::GraphicsSwapSubmission& submission) {
std::lock_guard<std::mutex> lock(placeholder_refresh_mutex_);
if (!d3d12_provider_ || !placeholder_command_list_) {
return false;
}
PlaceholderRefreshSlot& slot = placeholder_refresh_slots_[placeholder_refresh_slot_index_];
if (slot.last_submission &&
!placeholder_submission_tracker_.AwaitSubmissionCompletion(slot.last_submission)) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to await placeholder refresh "
"slot {}", placeholder_refresh_slot_index_);
return false;
}
ID3D12Resource* guest_output_resource = context.resource_uav_capable();
if (!guest_output_resource) {
return false;
}
if (FAILED(slot.command_allocator->Reset())) {
REXLOG_ERROR("AC6 native graphics bootstrap failed to reset a placeholder command allocator");
return false;
}
if (FAILED(placeholder_command_list_->Reset(slot.command_allocator.Get(), nullptr))) {
REXLOG_ERROR("AC6 native graphics bootstrap failed to reset the placeholder command list");
return false;
}
last_present_used_raster_replay_ = false;
if (TryRecordRasterPlaceholderFrame(slot, guest_output_resource, submission)) {
context.SetIs8bpc(true);
last_present_used_raster_replay_ = true;
return SubmitPlaceholderCommandList(slot);
}
if (FAILED(placeholder_command_list_->Reset(slot.command_allocator.Get(), nullptr))) {
REXLOG_ERROR(
"AC6 native graphics bootstrap failed to reset the placeholder command list "
"for the legacy fallback path");
return false;
}
ID3D12Device* device = d3d12_provider_->GetDevice();
D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = {};
uav_desc.Format = rex::ui::d3d12::D3D12Presenter::kGuestOutputFormat;
uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
uav_desc.Texture2D.MipSlice = 0;
uav_desc.Texture2D.PlaneSlice = 0;
D3D12_CPU_DESCRIPTOR_HANDLE uav_cpu_visible =
slot.shader_visible_uav_heap->GetCPUDescriptorHandleForHeapStart();
D3D12_GPU_DESCRIPTOR_HANDLE uav_gpu_visible =
slot.shader_visible_uav_heap->GetGPUDescriptorHandleForHeapStart();
D3D12_CPU_DESCRIPTOR_HANDLE uav_cpu =
slot.cpu_uav_heap->GetCPUDescriptorHandleForHeapStart();
device->CreateUnorderedAccessView(guest_output_resource, nullptr, &uav_desc, uav_cpu_visible);
device->CreateUnorderedAccessView(guest_output_resource, nullptr, &uav_desc, uav_cpu);
ID3D12DescriptorHeap* descriptor_heaps[] = {slot.shader_visible_uav_heap.Get()};
placeholder_command_list_->SetDescriptorHeaps(1, descriptor_heaps);
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = guest_output_resource;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
placeholder_command_list_->ResourceBarrier(1, &barrier);
context.SetIs8bpc(true);
const UINT width = submission.frontbuffer_width;
const UINT height = submission.frontbuffer_height;
const UINT title_band_height = std::max<UINT>(1, height / 8);
const UINT status_band_height = std::max<UINT>(1, height / 20);
const UINT heartbeat_width =
std::max<UINT>(1, UINT((uint64_t(width) *
(((swap_count_ - 1) % kPlaceholderHeartbeatPeriod) + 1)) /
kPlaceholderHeartbeatPeriod));
const NativeReplayPlanSummary& replay_plan = last_replay_plan_;
const SelectedPassPreviewData& selected_pass_preview = last_selected_pass_preview_;
const uint32_t candidate_seed =
replay_plan.present_candidate_rt0 ^ (replay_plan.present_candidate_depth_stencil * 33u) ^
(replay_plan.selected_pass_score * 2654435761u);
auto channel = [candidate_seed](uint32_t shift, float low, float high) {
float t = float((candidate_seed >> shift) & 0xFFu) / 255.0f;
return low + (high - low) * t;
};
const FloatColor background_color = {replay_plan.valid ? 0.04f : 0.08f,
replay_plan.valid ? 0.05f : 0.06f,
replay_plan.valid ? 0.08f : 0.10f, 1.0f};
const FloatColor title_band_color = {
replay_plan.selected_pass_is_stable ? 0.18f
: (replay_plan.present_candidate_matches_swap_size ? 0.18f : 0.55f),
replay_plan.selected_pass_is_stable ? 0.62f
: (replay_plan.present_candidate_matches_swap_size ? 0.56f : 0.22f),
replay_plan.selected_pass_is_stable ? 0.46f
: (replay_plan.present_candidate_matches_swap_size ? 0.24f : 0.18f), 1.0f};
const FloatColor fallback_candidate_color = {channel(0, 0.20f, 0.92f),
channel(8, 0.18f, 0.75f),
channel(16, 0.16f, 0.88f), 1.0f};
const FloatColor resolve_color = {0.96f, 0.94f, 0.30f, 1.0f};
const FloatColor heartbeat_color = {0.95f, 0.92f, 0.24f, 1.0f};
const FloatColor candidate_base_color =
selected_pass_preview.summary.using_clear_fill
? DecodeArgbColor(selected_pass_preview.summary.last_clear_color)
: fallback_candidate_color;
const FloatColor candidate_outline_color = MixColors(candidate_base_color, heartbeat_color, 0.5f);
const FloatColor stripe_color = MixColors(candidate_base_color, title_band_color, 0.4f);
auto clear_rect = [&](const FloatColor& color, UINT left, UINT top, UINT right, UINT bottom) {
if (left >= right || top >= bottom) {
return;
}
D3D12_RECT rect = {LONG(left), LONG(top), LONG(right), LONG(bottom)};
placeholder_command_list_->ClearUnorderedAccessViewFloat(
uav_gpu_visible, uav_cpu, guest_output_resource, color.data(), 1, &rect);
};
placeholder_command_list_->ClearUnorderedAccessViewFloat(
uav_gpu_visible, uav_cpu, guest_output_resource, background_color.data(), 0, nullptr);
UINT candidate_left = 0;
UINT candidate_top = title_band_height;
UINT candidate_width = width;
UINT candidate_height = std::max<UINT>(1, height - title_band_height - status_band_height);
if (replay_plan.valid && replay_plan.present_candidate_viewport_width &&
replay_plan.present_candidate_viewport_height) {
candidate_left = std::min<UINT>(width - 1, replay_plan.present_candidate_viewport_x);
candidate_top = std::min<UINT>(height - 1, replay_plan.present_candidate_viewport_y);
candidate_width =
std::max<UINT>(1, std::min<UINT>(replay_plan.present_candidate_viewport_width,
width - candidate_left));
candidate_height =
std::max<UINT>(1, std::min<UINT>(replay_plan.present_candidate_viewport_height,
height - candidate_top));
}
D3D12_RECT center_block_rect = {LONG(candidate_left), LONG(candidate_top),
LONG(std::min(width, candidate_left + candidate_width)),
LONG(std::min(height, candidate_top + candidate_height))};
placeholder_command_list_->ClearUnorderedAccessViewFloat(
uav_gpu_visible, uav_cpu, guest_output_resource, candidate_base_color.data(), 1,
&center_block_rect);
const UINT candidate_right = std::min(width, candidate_left + candidate_width);
const UINT candidate_bottom = std::min(height, candidate_top + candidate_height);
UINT sampled_clear_band_height = 0;
if (selected_pass_preview.clear_step_count && candidate_height > 2) {
sampled_clear_band_height =
std::max<UINT>(1, std::min<UINT>(candidate_height / 12, height / 32 + 1));
for (uint32_t i = 0; i < selected_pass_preview.clear_step_count; ++i) {
const UINT band_top = std::min(candidate_bottom, candidate_top + sampled_clear_band_height * i);
const UINT band_bottom =
std::min(candidate_bottom, band_top + sampled_clear_band_height);
clear_rect(DecodeArgbColor(selected_pass_preview.clear_steps[i].color), candidate_left,
band_top, candidate_right, band_bottom);
}
}
UINT resolve_band_height = 0;
if (replay_plan.valid && replay_plan.selected_pass_has_resolve) {
resolve_band_height = std::max<UINT>(1, candidate_height / 10);
clear_rect(resolve_color, candidate_left,
std::min(candidate_bottom, candidate_bottom - resolve_band_height),
candidate_right, candidate_bottom);
}
if (selected_pass_preview.summary.draw_count && candidate_width > 8 && candidate_height > 8) {
const UINT stripe_count =
std::min<UINT>(12, std::max<UINT>(1, 1 + (selected_pass_preview.summary.draw_count / 6)));
const UINT stripe_width =
std::max<UINT>(1, candidate_width / std::max<UINT>(24, stripe_count * 5));
const UINT stripe_top = std::min(candidate_bottom, candidate_top + sampled_clear_band_height);
const UINT stripe_bottom =
candidate_bottom > resolve_band_height ? (candidate_bottom - resolve_band_height)
: candidate_bottom;
const UINT stripe_area_height =
stripe_bottom > stripe_top ? (stripe_bottom - stripe_top) : 0;
for (UINT i = 0; i < stripe_count && stripe_area_height; ++i) {
const UINT stripe_center_x =
candidate_left + ((i + 1) * candidate_width) / (stripe_count + 1);
const UINT stripe_left = stripe_center_x > stripe_width / 2
? stripe_center_x - stripe_width / 2
: candidate_left;
const UINT stripe_right = std::min(candidate_right, stripe_left + stripe_width);
const UINT stripe_height =
std::max<UINT>(1, stripe_area_height * (45 + ((i * 13) % 40)) / 100);
const UINT stripe_start = stripe_bottom > stripe_height ? (stripe_bottom - stripe_height)
: stripe_top;
const FloatColor lane_color =
(i & 1u) ? MixColors(stripe_color, heartbeat_color, 0.22f)
: MixColors(stripe_color, background_color, 0.10f);
clear_rect(lane_color, stripe_left, stripe_start, stripe_right, stripe_bottom);
}
}
if (candidate_width > 2 && candidate_height > 2) {
const UINT outline_thickness =
std::max<UINT>(1, std::min<UINT>(4, std::min(candidate_width, candidate_height) / 96 + 1));
const UINT bottom_outline_top =
candidate_bottom > outline_thickness ? (candidate_bottom - outline_thickness)
: candidate_top;
clear_rect(candidate_outline_color, candidate_left, candidate_top, candidate_right,
std::min(candidate_bottom, candidate_top + outline_thickness));
clear_rect(candidate_outline_color, candidate_left, bottom_outline_top, candidate_right,
candidate_bottom);
clear_rect(candidate_outline_color, candidate_left, candidate_top,
std::min(candidate_right, candidate_left + outline_thickness), candidate_bottom);
clear_rect(candidate_outline_color,
candidate_right > outline_thickness ? (candidate_right - outline_thickness)
: candidate_left,
candidate_top, candidate_right, candidate_bottom);
}
if (replay_plan.valid && replay_plan.pass_count) {
const UINT score_width = std::max<UINT>(
1, UINT((uint64_t(width) * std::min<uint32_t>(replay_plan.selected_pass_score, 220u)) /
220u));
clear_rect(candidate_outline_color, 0, title_band_height, score_width,
std::min(height, title_band_height + status_band_height));
}
clear_rect(title_band_color, 0, 0, width, title_band_height);
clear_rect(heartbeat_color, 0, height - status_band_height, heartbeat_width, height);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
barrier.Transition.StateAfter = rex::ui::d3d12::D3D12Presenter::kGuestOutputInternalState;
placeholder_command_list_->ResourceBarrier(1, &barrier);
return SubmitPlaceholderCommandList(slot);
}
rex::ui::d3d12::D3D12Provider* d3d12_provider_ = nullptr;
rex::ui::d3d12::D3D12Presenter* d3d12_presenter_ = nullptr;
std::mutex placeholder_refresh_mutex_;
rex::ui::d3d12::D3D12SubmissionTracker placeholder_submission_tracker_;
std::array<PlaceholderRefreshSlot, kPlaceholderRefreshSlotCount> placeholder_refresh_slots_;
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> placeholder_command_list_;
Microsoft::WRL::ComPtr<ID3D12RootSignature> raster_preview_root_signature_;
Microsoft::WRL::ComPtr<ID3D12PipelineState> raster_preview_pipeline_;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> raster_preview_view_heap_;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> raster_preview_sampler_heap_;
uint32_t placeholder_refresh_slot_index_ = 0;
rex::system::GraphicsSwapSubmission last_swap_submission_;
NativeReplayPlanSummary last_replay_plan_{};
SelectedPassPreviewData last_selected_pass_preview_{};
ReplayCandidateKey last_selected_candidate_{};
bool last_selected_candidate_valid_ = false;
uint32_t selected_candidate_streak_ = 0;
uint64_t swap_count_ = 0;
bool logged_first_swap_ = false;
bool logged_first_present_ = false;
bool logged_passthrough_swap_ = false;
bool logged_refresh_failure_ = false;
bool last_present_used_raster_replay_ = false;
bool placeholder_resources_initialized_ = false;
};
#endif
} // namespace
void ConfigureGraphicsBackend(rex::RuntimeConfig& config) {
{
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
g_native_graphics_status = {};
g_native_graphics_status.bootstrap_enabled = REXCVAR_GET(ac6_native_graphics_bootstrap);
g_native_graphics_status.placeholder_present_enabled =
REXCVAR_GET(ac6_native_graphics_placeholder_present);
}
#if REX_HAS_D3D12
if (!REXCVAR_GET(ac6_native_graphics_bootstrap)) {
return;
}
config.graphics = std::make_unique<Ac6NativeGraphicsSystem>();
#else
(void)config;
#endif
}
NativeGraphicsStatusSnapshot GetNativeGraphicsStatus() {
std::lock_guard<std::mutex> lock(g_native_graphics_status_mutex);
NativeGraphicsStatusSnapshot snapshot = g_native_graphics_status;
snapshot.bootstrap_enabled = REXCVAR_GET(ac6_native_graphics_bootstrap);
snapshot.placeholder_present_enabled = REXCVAR_GET(ac6_native_graphics_placeholder_present);
return snapshot;
}
} // namespace ac6::graphics