native guest output renderer hook (D3D12) + shared-memory accessor

This commit is contained in:
mchughalex 2026-07-01 21:57:17 +01:00
parent fac7ab2616
commit c4b0dfe2c9
8 changed files with 292 additions and 7 deletions

View file

@ -103,6 +103,8 @@ class D3D12CommandProcessor : public CommandProcessor {
ui::d3d12::D3D12UploadBufferPool& GetConstantBufferPool() const { return *constant_buffer_pool_; } ui::d3d12::D3D12UploadBufferPool& GetConstantBufferPool() const { return *constant_buffer_pool_; }
D3D12SharedMemory& GetSharedMemory() const { return *shared_memory_; }
D3D12_CPU_DESCRIPTOR_HANDLE GetViewBindlessHeapCPUStart() const { D3D12_CPU_DESCRIPTOR_HANDLE GetViewBindlessHeapCPUStart() const {
assert_true(bindless_resources_used_); assert_true(bindless_resources_used_);
return view_bindless_heap_cpu_start_; return view_bindless_heap_cpu_start_;

View file

@ -91,6 +91,23 @@ class DeferredCommandList {
} }
} }
void D3DClearUnorderedAccessViewFloat(
D3D12_GPU_DESCRIPTOR_HANDLE view_gpu_handle_in_current_heap,
D3D12_CPU_DESCRIPTOR_HANDLE view_cpu_handle, ID3D12Resource* resource,
const FLOAT values[4], UINT num_rects, const D3D12_RECT* rects) {
auto args = reinterpret_cast<ClearUnorderedAccessViewHeader*>(
WriteCommand(Command::kD3DClearUnorderedAccessViewFloat,
sizeof(ClearUnorderedAccessViewHeader) + num_rects * sizeof(D3D12_RECT)));
args->view_gpu_handle_in_current_heap = view_gpu_handle_in_current_heap;
args->view_cpu_handle = view_cpu_handle;
args->resource = resource;
std::memcpy(args->values_float, values, 4 * sizeof(FLOAT));
args->num_rects = num_rects;
if (num_rects != 0) {
std::memcpy(args + 1, rects, num_rects * sizeof(D3D12_RECT));
}
}
void D3DCopyBufferRegion(ID3D12Resource* dst_buffer, UINT64 dst_offset, void D3DCopyBufferRegion(ID3D12Resource* dst_buffer, UINT64 dst_offset,
ID3D12Resource* src_buffer, UINT64 src_offset, UINT64 num_bytes) { ID3D12Resource* src_buffer, UINT64 src_offset, UINT64 num_bytes) {
auto& args = *reinterpret_cast<D3DCopyBufferRegionArguments*>( auto& args = *reinterpret_cast<D3DCopyBufferRegionArguments*>(
@ -456,6 +473,7 @@ class DeferredCommandList {
kD3DClearDepthStencilView, kD3DClearDepthStencilView,
kD3DClearRenderTargetView, kD3DClearRenderTargetView,
kD3DClearUnorderedAccessViewUint, kD3DClearUnorderedAccessViewUint,
kD3DClearUnorderedAccessViewFloat,
kD3DCopyBufferRegion, kD3DCopyBufferRegion,
kD3DCopyResource, kD3DCopyResource,
kCopyTexture, kCopyTexture,

View file

@ -0,0 +1,89 @@
#pragma once
#include <cstdint>
#if REX_HAS_D3D12
#include <rex/ui/d3d12/d3d12_api.h>
#endif
namespace rex::graphics::d3d12 {
class D3D12CommandProcessor;
}
namespace rex::graphics {
enum class NativeGuestOutputBackend : uint32_t {
kUnknown = 0,
kD3D12,
kVulkan,
};
struct NativeGuestOutputRenderContext {
NativeGuestOutputBackend backend = NativeGuestOutputBackend::kUnknown;
uint32_t guest_output_width = 0;
uint32_t guest_output_height = 0;
uint32_t display_width = 0;
uint32_t display_height = 0;
#if REX_HAS_D3D12
struct D3D12Context {
d3d12::D3D12CommandProcessor* command_processor = nullptr;
void* command_processor_user_data = nullptr;
ID3D12Device* device = nullptr;
ID3D12Resource* guest_output_resource = nullptr;
DXGI_FORMAT guest_output_format = DXGI_FORMAT_UNKNOWN;
D3D12_RESOURCE_STATES guest_output_initial_state = D3D12_RESOURCE_STATE_COMMON;
bool (*request_one_use_view_descriptor)(void* user_data,
D3D12_CPU_DESCRIPTOR_HANDLE* cpu_out,
D3D12_GPU_DESCRIPTOR_HANDLE* gpu_out) = nullptr;
bool (*create_root_signature)(void* user_data, const D3D12_ROOT_SIGNATURE_DESC* desc,
ID3D12RootSignature** root_signature_out) = nullptr;
bool (*push_transition_barrier)(void* user_data, ID3D12Resource* resource,
D3D12_RESOURCE_STATES old_state,
D3D12_RESOURCE_STATES new_state) = nullptr;
void (*submit_barriers)(void* user_data) = nullptr;
void (*copy_texture_region)(void* user_data, const D3D12_TEXTURE_COPY_LOCATION* dst,
UINT dst_x, UINT dst_y, UINT dst_z,
const D3D12_TEXTURE_COPY_LOCATION* src,
const D3D12_BOX* src_box) = nullptr;
void (*clear_render_target_view)(void* user_data, D3D12_CPU_DESCRIPTOR_HANDLE rtv,
const FLOAT color[4]) = nullptr;
void (*clear_unordered_access_view_float)(void* user_data,
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle,
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle,
ID3D12Resource* resource,
const FLOAT values[4]) = nullptr;
void (*set_graphics_root_signature)(void* user_data,
ID3D12RootSignature* root_signature) = nullptr;
void (*set_graphics_root_32bit_constants)(void* user_data, UINT root_parameter_index,
UINT num_32bit_values_to_set,
const void* src_data,
UINT dest_offset_in_32bit_values) = nullptr;
void (*set_graphics_root_descriptor_table)(void* user_data, UINT root_parameter_index,
D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor) =
nullptr;
void (*set_pipeline_state)(void* user_data, ID3D12PipelineState* pipeline_state) = nullptr;
void (*ia_set_primitive_topology)(void* user_data,
D3D12_PRIMITIVE_TOPOLOGY primitive_topology) = nullptr;
void (*ia_set_vertex_buffers)(void* user_data, UINT start_slot, UINT num_views,
const D3D12_VERTEX_BUFFER_VIEW* views) = nullptr;
void (*om_set_render_targets)(void* user_data, UINT num_render_target_descriptors,
const D3D12_CPU_DESCRIPTOR_HANDLE* render_target_descriptors,
BOOL rts_single_handle_to_descriptor_range,
const D3D12_CPU_DESCRIPTOR_HANDLE* depth_stencil_descriptor) =
nullptr;
void (*rs_set_viewport)(void* user_data, const D3D12_VIEWPORT* viewport) = nullptr;
void (*rs_set_scissor_rect)(void* user_data, const D3D12_RECT* rect) = nullptr;
void (*draw_instanced)(void* user_data, UINT vertex_count_per_instance, UINT instance_count,
UINT start_vertex_location, UINT start_instance_location) = nullptr;
} d3d12;
#endif
};
using NativeGuestOutputRenderer = bool (*)(const NativeGuestOutputRenderContext& context,
void* user_data);
void SetNativeGuestOutputRenderer(NativeGuestOutputRenderer renderer, void* user_data);
bool TryRenderNativeGuestOutput(const NativeGuestOutputRenderContext& context);
} // namespace rex::graphics

View file

@ -24,6 +24,7 @@ set(REXGRAPHICS_SYSTEM_SOURCES
util/draw_extent_estimator.cpp util/draw_extent_estimator.cpp
util/draw.cpp util/draw.cpp
packet_disassembler.cpp packet_disassembler.cpp
native_guest_renderer.cpp
primitive_processor.cpp primitive_processor.cpp
pipeline/render_target/cache.cpp pipeline/render_target/cache.cpp
shared_memory.cpp shared_memory.cpp
@ -148,4 +149,3 @@ if(REXGLUE_USE_D3D12)
) )
target_compile_definitions(rexgraphics PUBLIC REX_HAS_D3D12=1) target_compile_definitions(rexgraphics PUBLIC REX_HAS_D3D12=1)
endif() endif()

View file

@ -21,6 +21,7 @@
#include <rex/dbg.h> #include <rex/dbg.h>
#include <rex/perf/counter.h> #include <rex/perf/counter.h>
#include <rex/graphics/d3d12/command_processor.h> #include <rex/graphics/d3d12/command_processor.h>
#include <rex/graphics/native_guest_renderer.h>
#include <rex/graphics/d3d12/graphics_system.h> #include <rex/graphics/d3d12/graphics_system.h>
#include <rex/graphics/d3d12/shader.h> #include <rex/graphics/d3d12/shader.h>
#include <rex/graphics/flags.h> #include <rex/graphics/flags.h>
@ -2320,7 +2321,8 @@ void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbu
presenter->RefreshGuestOutput( presenter->RefreshGuestOutput(
guest_output_width, guest_output_height, display_width, display_height, guest_output_width, guest_output_height, display_width, display_height,
[this, &swap_texture_srv_desc, frontbuffer_format, swap_texture_resource, guest_output_width, [this, &swap_texture_srv_desc, frontbuffer_format, swap_texture_resource, guest_output_width,
guest_output_height](ui::Presenter::GuestOutputRefreshContext& context) -> bool { guest_output_height, display_width,
display_height](ui::Presenter::GuestOutputRefreshContext& context) -> bool {
const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); const ui::d3d12::D3D12Provider& provider = GetD3D12Provider();
ID3D12Device* device = provider.GetDevice(); ID3D12Device* device = provider.GetDevice();
@ -2378,6 +2380,148 @@ void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbu
context.SetIs8bpc(!use_pwl_gamma_ramp && !use_fxaa); context.SetIs8bpc(!use_pwl_gamma_ramp && !use_fxaa);
ID3D12Resource* guest_output_resource =
static_cast<ui::d3d12::D3D12Presenter::D3D12GuestOutputRefreshContext&>(context)
.resource_uav_capable();
NativeGuestOutputRenderContext native_context;
native_context.backend = NativeGuestOutputBackend::kD3D12;
native_context.guest_output_width = guest_output_width;
native_context.guest_output_height = guest_output_height;
native_context.display_width = display_width;
native_context.display_height = display_height;
native_context.d3d12.command_processor = this;
native_context.d3d12.command_processor_user_data = this;
native_context.d3d12.device = device;
native_context.d3d12.guest_output_resource = guest_output_resource;
native_context.d3d12.guest_output_format = ui::d3d12::D3D12Presenter::kGuestOutputFormat;
native_context.d3d12.guest_output_initial_state =
ui::d3d12::D3D12Presenter::kGuestOutputInternalState;
native_context.d3d12.request_one_use_view_descriptor =
[](void* user_data, D3D12_CPU_DESCRIPTOR_HANDLE* cpu_out,
D3D12_GPU_DESCRIPTOR_HANDLE* gpu_out) -> bool {
auto* command_processor = static_cast<D3D12CommandProcessor*>(user_data);
ui::d3d12::util::DescriptorCpuGpuHandlePair descriptor;
if (!command_processor->RequestOneUseSingleViewDescriptors(1, &descriptor)) {
return false;
}
*cpu_out = descriptor.first;
*gpu_out = descriptor.second;
return true;
};
native_context.d3d12.create_root_signature =
[](void* user_data, const D3D12_ROOT_SIGNATURE_DESC* desc,
ID3D12RootSignature** root_signature_out) -> bool {
auto* command_processor = static_cast<D3D12CommandProcessor*>(user_data);
*root_signature_out =
ui::d3d12::util::CreateRootSignature(command_processor->GetD3D12Provider(), *desc);
return *root_signature_out != nullptr;
};
native_context.d3d12.push_transition_barrier =
[](void* user_data, ID3D12Resource* resource, D3D12_RESOURCE_STATES old_state,
D3D12_RESOURCE_STATES new_state) -> bool {
return static_cast<D3D12CommandProcessor*>(user_data)->PushTransitionBarrier(
resource, old_state, new_state);
};
native_context.d3d12.submit_barriers = [](void* user_data) {
static_cast<D3D12CommandProcessor*>(user_data)->SubmitBarriers();
};
native_context.d3d12.copy_texture_region =
[](void* user_data, const D3D12_TEXTURE_COPY_LOCATION* dst, UINT dst_x, UINT dst_y,
UINT dst_z, const D3D12_TEXTURE_COPY_LOCATION* src, const D3D12_BOX* src_box) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DCopyTextureRegion(dst, dst_x, dst_y, dst_z, src, src_box);
};
native_context.d3d12.clear_render_target_view =
[](void* user_data, D3D12_CPU_DESCRIPTOR_HANDLE rtv, const FLOAT color[4]) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DClearRenderTargetView(rtv, color, 0, nullptr);
};
native_context.d3d12.clear_unordered_access_view_float =
[](void* user_data, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle,
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, ID3D12Resource* resource,
const FLOAT values[4]) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DClearUnorderedAccessViewFloat(gpu_handle, cpu_handle, resource, values, 0,
nullptr);
};
native_context.d3d12.set_graphics_root_signature =
[](void* user_data, ID3D12RootSignature* root_signature) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DSetGraphicsRootSignature(root_signature);
};
native_context.d3d12.set_graphics_root_32bit_constants =
[](void* user_data, UINT root_parameter_index, UINT num_32bit_values_to_set,
const void* src_data, UINT dest_offset_in_32bit_values) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DSetGraphicsRoot32BitConstants(root_parameter_index, num_32bit_values_to_set,
src_data, dest_offset_in_32bit_values);
};
native_context.d3d12.set_graphics_root_descriptor_table =
[](void* user_data, UINT root_parameter_index,
D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DSetGraphicsRootDescriptorTable(root_parameter_index, base_descriptor);
};
native_context.d3d12.set_pipeline_state =
[](void* user_data, ID3D12PipelineState* pipeline_state) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DSetPipelineState(pipeline_state);
};
native_context.d3d12.ia_set_primitive_topology =
[](void* user_data, D3D12_PRIMITIVE_TOPOLOGY primitive_topology) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DIASetPrimitiveTopology(primitive_topology);
};
native_context.d3d12.ia_set_vertex_buffers =
[](void* user_data, UINT start_slot, UINT num_views,
const D3D12_VERTEX_BUFFER_VIEW* views) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DIASetVertexBuffers(start_slot, num_views, views);
};
native_context.d3d12.om_set_render_targets =
[](void* user_data, UINT num_render_target_descriptors,
const D3D12_CPU_DESCRIPTOR_HANDLE* render_target_descriptors,
BOOL rts_single_handle_to_descriptor_range,
const D3D12_CPU_DESCRIPTOR_HANDLE* depth_stencil_descriptor) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DOMSetRenderTargets(num_render_target_descriptors, render_target_descriptors,
rts_single_handle_to_descriptor_range,
depth_stencil_descriptor);
};
native_context.d3d12.rs_set_viewport = [](void* user_data,
const D3D12_VIEWPORT* viewport) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.RSSetViewport(*viewport);
};
native_context.d3d12.rs_set_scissor_rect = [](void* user_data, const D3D12_RECT* rect) {
static_cast<D3D12CommandProcessor*>(user_data)->GetDeferredCommandList().RSSetScissorRect(
*rect);
};
native_context.d3d12.draw_instanced =
[](void* user_data, UINT vertex_count_per_instance, UINT instance_count,
UINT start_vertex_location, UINT start_instance_location) {
static_cast<D3D12CommandProcessor*>(user_data)
->GetDeferredCommandList()
.D3DDrawInstanced(vertex_count_per_instance, instance_count, start_vertex_location,
start_instance_location);
};
if (TryRenderNativeGuestOutput(native_context)) {
EndSubmission(true);
return true;
}
// Upload the new gamma ramp, using the upload buffer for the current // Upload the new gamma ramp, using the upload buffer for the current
// frame (will close the frame after this anyway, so can't write // frame (will close the frame after this anyway, so can't write
// multiple times per frame). // multiple times per frame).
@ -2438,10 +2582,6 @@ void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbu
WriteGammaRampSRV(use_pwl_gamma_ramp, apply_gamma_descriptor_gamma_ramp.first); WriteGammaRampSRV(use_pwl_gamma_ramp, apply_gamma_descriptor_gamma_ramp.first);
} }
ID3D12Resource* guest_output_resource =
static_cast<ui::d3d12::D3D12Presenter::D3D12GuestOutputRefreshContext&>(context)
.resource_uav_capable();
if (use_fxaa) { if (use_fxaa) {
fxaa_source_texture_submission_ = submission_current_; fxaa_source_texture_submission_ = submission_current_;
} }

View file

@ -36,6 +36,7 @@ DeferredCommandPerfCategory GetPerfCategory(DeferredCommandList::Command command
case Command::kD3DClearDepthStencilView: case Command::kD3DClearDepthStencilView:
case Command::kD3DClearRenderTargetView: case Command::kD3DClearRenderTargetView:
case Command::kD3DClearUnorderedAccessViewUint: case Command::kD3DClearUnorderedAccessViewUint:
case Command::kD3DClearUnorderedAccessViewFloat:
return {CounterId::kDeferredClearCount, CounterId::kDeferredClearUs, return {CounterId::kDeferredClearCount, CounterId::kDeferredClearUs,
CounterId::kGpuClearUs}; CounterId::kGpuClearUs};
case Command::kD3DResourceBarrier: case Command::kD3DResourceBarrier:
@ -146,6 +147,13 @@ void DeferredCommandList::Execute(ID3D12GraphicsCommandList* command_list,
args.values_uint, args.num_rects, args.values_uint, args.num_rects,
args.num_rects ? reinterpret_cast<const D3D12_RECT*>(&args + 1) : nullptr); args.num_rects ? reinterpret_cast<const D3D12_RECT*>(&args + 1) : nullptr);
} break; } break;
case Command::kD3DClearUnorderedAccessViewFloat: {
auto& args = *reinterpret_cast<const ClearUnorderedAccessViewHeader*>(stream);
command_list->ClearUnorderedAccessViewFloat(
args.view_gpu_handle_in_current_heap, args.view_cpu_handle, args.resource,
args.values_float, args.num_rects,
args.num_rects ? reinterpret_cast<const D3D12_RECT*>(&args + 1) : nullptr);
} break;
case Command::kD3DCopyBufferRegion: { case Command::kD3DCopyBufferRegion: {
auto& args = *reinterpret_cast<const D3DCopyBufferRegionArguments*>(stream); auto& args = *reinterpret_cast<const D3DCopyBufferRegionArguments*>(stream);
PERF_counter_add(kDeferredCopyBufferBytes, static_cast<int64_t>(args.num_bytes)); PERF_counter_add(kDeferredCopyBufferBytes, static_cast<int64_t>(args.num_bytes));

View file

@ -0,0 +1,27 @@
#include <rex/graphics/native_guest_renderer.h>
#include <atomic>
namespace rex::graphics {
namespace {
std::atomic<NativeGuestOutputRenderer> g_renderer{nullptr};
std::atomic<void*> g_renderer_user_data{nullptr};
} // namespace
void SetNativeGuestOutputRenderer(NativeGuestOutputRenderer renderer, void* user_data) {
g_renderer_user_data.store(user_data, std::memory_order_release);
g_renderer.store(renderer, std::memory_order_release);
}
bool TryRenderNativeGuestOutput(const NativeGuestOutputRenderContext& context) {
NativeGuestOutputRenderer renderer = g_renderer.load(std::memory_order_acquire);
if (renderer == nullptr) {
return false;
}
void* user_data = g_renderer_user_data.load(std::memory_order_acquire);
return renderer(context, user_data);
}
} // namespace rex::graphics

View file

@ -626,7 +626,8 @@ bool D3D12Presenter::RefreshGuestOutputImpl(
guest_output_resource_new_desc.SampleDesc.Count = 1; guest_output_resource_new_desc.SampleDesc.Count = 1;
guest_output_resource_new_desc.SampleDesc.Quality = 0; guest_output_resource_new_desc.SampleDesc.Quality = 0;
guest_output_resource_new_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; guest_output_resource_new_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
guest_output_resource_new_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; guest_output_resource_new_desc.Flags =
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS | D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
if (FAILED(device->CreateCommittedResource( if (FAILED(device->CreateCommittedResource(
&util::kHeapPropertiesDefault, provider_.GetHeapFlagCreateNotZeroed(), &util::kHeapPropertiesDefault, provider_.GetHeapFlagCreateNotZeroed(),
&guest_output_resource_new_desc, kGuestOutputInternalState, nullptr, &guest_output_resource_new_desc, kGuestOutputInternalState, nullptr,