use super::pso_cache;
use super::types::{self, DxgiAdapterInfo, LogicalDevice};
use super::{utils, DeviceHandle, Dx12State};
use crate::backend::{AdapterInfo, BackendType};
use anyhow::{Context, Result};
use std::collections::HashMap;
use windows::core::Interface;
use windows::Win32::Graphics::{Direct3D::*, Direct3D12::*, Dxgi::*};
fn device_with_enhanced_barriers(device: ID3D12Device) -> Result<ID3D12Device10> {
let mut options12 = D3D12_FEATURE_DATA_D3D12_OPTIONS12::default();
unsafe {
device
.CheckFeatureSupport(
D3D12_FEATURE_D3D12_OPTIONS12,
&mut options12 as *mut _ as *mut _,
std::mem::size_of_val(&options12) as u32,
)
.context("CheckFeatureSupport(D3D12_OPTIONS12) failed")?;
}
if !options12.EnhancedBarriersSupported.as_bool() {
anyhow::bail!(
"Goldy DX12 requires D3D12 enhanced barriers (Windows 11 + WDDM 3.0+ driver, or WARP with GOLDY_DX12_ALLOW_WARP=1 on Win11)"
);
}
device
.cast::<ID3D12Device10>()
.context("ID3D12Device10 required for enhanced barriers")
}
pub(super) fn enumerate(adapters: &[DxgiAdapterInfo]) -> Vec<AdapterInfo> {
adapters
.iter()
.map(|adapter| {
let name = String::from_utf16_lossy(&adapter.desc.Description)
.trim_end_matches('\0')
.to_string();
let flags = DXGI_ADAPTER_FLAG(adapter.desc.Flags as i32);
let device_type = utils::device_type_from_flags(flags);
let vendor = utils::vendor_name(adapter.desc.VendorId);
AdapterInfo {
id: adapter.adapter_id,
name,
vendor: vendor.to_string(),
backend: BackendType::Dx12,
device_type,
}
})
.collect()
}
pub(super) fn query_supports_reserved_buffers(adapter: &IDXGIAdapter1) -> bool {
let mut device: Option<ID3D12Device> = None;
if unsafe { D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, &mut device) }.is_err() {
return false;
}
let Some(device) = device else {
return false;
};
let mut d3d12_options = D3D12_FEATURE_DATA_D3D12_OPTIONS::default();
unsafe {
if device
.CheckFeatureSupport(
D3D12_FEATURE_D3D12_OPTIONS,
&mut d3d12_options as *mut _ as *mut _,
std::mem::size_of_val(&d3d12_options) as u32,
)
.is_ok()
{
d3d12_options.TiledResourcesTier.0 >= 1
} else {
false
}
}
}
pub(super) fn adapter_capabilities(adapters: &[DxgiAdapterInfo], adapter_id: u32) -> crate::device::DeviceCapabilities {
let mut caps = crate::device::DeviceCapabilities {
has_zero_copy_storage_readback: false,
host_sidecar_on_submit_worker: true,
..Default::default()
};
if adapters
.iter()
.find(|a| a.adapter_id == adapter_id)
.is_some_and(|a| a.supports_reserved_buffers && !super::env_disable_reserved_buffers())
{
caps.buffer_resize_cost = crate::types::BufferResizeCost::PageBind;
caps.buffer_page_size = 64 * 1024;
caps.buffer_decommit_supported = true;
}
caps
}
#[allow(clippy::too_many_lines)]
pub(super) fn create(state: &mut Dx12State, adapter_id: u32) -> Result<DeviceHandle> {
let adapter = state
.adapters
.iter()
.find(|a| a.adapter_id == adapter_id)
.context("Invalid adapter ID")?;
let mut device: Option<ID3D12Device> = None;
unsafe { D3D12CreateDevice(&adapter.adapter, D3D_FEATURE_LEVEL_12_0, &mut device) }
.context("Failed to create D3D12 device")?;
let device = device.context("D3D12CreateDevice returned null")?;
let device = device_with_enhanced_barriers(device)?;
super::install_debug_layer_exception_handler();
let supports_reserved_buffers = adapter.supports_reserved_buffers;
debug_assert_eq!(
supports_reserved_buffers,
{
let mut d3d12_options = D3D12_FEATURE_DATA_D3D12_OPTIONS::default();
unsafe {
if device
.CheckFeatureSupport(
D3D12_FEATURE_D3D12_OPTIONS,
&mut d3d12_options as *mut _ as *mut _,
std::mem::size_of_val(&d3d12_options) as u32,
)
.is_ok()
{
d3d12_options.TiledResourcesTier.0 >= 1
} else {
false
}
}
},
"DxgiAdapterInfo reserved-buffer flag out of sync with live query"
);
let queue_desc = D3D12_COMMAND_QUEUE_DESC {
Type: D3D12_COMMAND_LIST_TYPE_DIRECT,
Priority: D3D12_COMMAND_QUEUE_PRIORITY_NORMAL.0,
Flags: D3D12_COMMAND_QUEUE_FLAG_NONE,
NodeMask: 0,
};
let command_queue: ID3D12CommandQueue =
unsafe { device.CreateCommandQueue(&queue_desc) }.context("Failed to create command queue")?;
let command_allocator: ID3D12CommandAllocator =
unsafe { device.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT) }
.context("Failed to create command allocator")?;
let rtv_heap_desc = D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
NumDescriptors: 256, Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
};
let rtv_heap: ID3D12DescriptorHeap =
unsafe { device.CreateDescriptorHeap(&rtv_heap_desc) }.context("Failed to create RTV heap")?;
let rtv_descriptor_size = unsafe { device.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV) };
let dsv_heap_desc = D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
NumDescriptors: 256,
Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
};
let dsv_heap: ID3D12DescriptorHeap =
unsafe { device.CreateDescriptorHeap(&dsv_heap_desc) }.context("Failed to create DSV heap")?;
let dsv_descriptor_size = unsafe { device.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV) };
let cbv_srv_uav_heap_desc = D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
NumDescriptors: 16384, Flags: D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
NodeMask: 0,
};
let cbv_srv_uav_heap: ID3D12DescriptorHeap =
unsafe { device.CreateDescriptorHeap(&cbv_srv_uav_heap_desc) }.context("Failed to create CBV/SRV/UAV heap")?;
let cbv_srv_uav_descriptor_size =
unsafe { device.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) };
let sampler_heap_desc = D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
NumDescriptors: 2048, Flags: D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
NodeMask: 0,
};
let sampler_heap: ID3D12DescriptorHeap =
unsafe { device.CreateDescriptorHeap(&sampler_heap_desc) }.context("Failed to create sampler heap")?;
let sampler_descriptor_size =
unsafe { device.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) };
tracing::info!("DX12 bindless (SM 6.6 via Slang DXIL)");
let bindless_root_signature = {
let root_constants = D3D12_ROOT_PARAMETER1 {
ParameterType: D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS,
Anonymous: D3D12_ROOT_PARAMETER1_0 {
Constants: D3D12_ROOT_CONSTANTS {
ShaderRegister: 0,
RegisterSpace: 0,
Num32BitValues: (types::TOTAL_PUSH_BYTES / 4) as u32,
},
},
ShaderVisibility: D3D12_SHADER_VISIBILITY_ALL,
};
let root_params = [root_constants];
let desc1 = D3D12_ROOT_SIGNATURE_DESC1 {
NumParameters: 1,
pParameters: root_params.as_ptr(),
NumStaticSamplers: 0,
pStaticSamplers: std::ptr::null(),
Flags: D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
| D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED
| D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED,
};
let versioned_desc = D3D12_VERSIONED_ROOT_SIGNATURE_DESC {
Version: D3D_ROOT_SIGNATURE_VERSION_1_1,
Anonymous: D3D12_VERSIONED_ROOT_SIGNATURE_DESC_0 { Desc_1_1: desc1 },
};
let mut signature_blob: Option<ID3DBlob> = None;
let mut error_blob: Option<ID3DBlob> = None;
unsafe { D3D12SerializeVersionedRootSignature(&versioned_desc, &mut signature_blob, Some(&mut error_blob)) }
.context("Failed to serialize shared bindless root signature")?;
let blob = signature_blob.context("Root signature serialization produced no output")?;
let root_sig: ID3D12RootSignature = unsafe {
device.CreateRootSignature(
0,
std::slice::from_raw_parts(blob.GetBufferPointer() as *const u8, blob.GetBufferSize()),
)
}
.context("Failed to create shared bindless root signature")?;
tracing::debug!("Created shared bindless root signature");
Some(root_sig)
};
let compute_dispatch_indirect_signature = {
let arg_desc = D3D12_INDIRECT_ARGUMENT_DESC {
Type: D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH,
Anonymous: unsafe { std::mem::zeroed() },
};
let arg_descs = [arg_desc];
let cmd_sig_desc = D3D12_COMMAND_SIGNATURE_DESC {
ByteStride: 12, NumArgumentDescs: 1,
pArgumentDescs: arg_descs.as_ptr(),
NodeMask: 0,
};
let mut sig: Option<ID3D12CommandSignature> = None;
unsafe {
device.CreateCommandSignature(
&cmd_sig_desc,
None, &mut sig,
)
}
.context("Failed to create compute indirect command signature")?;
tracing::debug!("Created compute indirect dispatch command signature");
sig
};
let compute_batch_dispatch_signature = if let Some(ref root_sig) = bindless_root_signature {
use crate::backend::shared::TOTAL_PUSH_BYTES;
let arg_descs = [
D3D12_INDIRECT_ARGUMENT_DESC {
Type: D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT,
Anonymous: D3D12_INDIRECT_ARGUMENT_DESC_0 {
Constant: D3D12_INDIRECT_ARGUMENT_DESC_0_1 {
RootParameterIndex: 0,
DestOffsetIn32BitValues: 0,
Num32BitValuesToSet: (TOTAL_PUSH_BYTES / 4) as u32,
},
},
},
D3D12_INDIRECT_ARGUMENT_DESC {
Type: D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH,
Anonymous: unsafe { std::mem::zeroed() },
},
];
let stride = (TOTAL_PUSH_BYTES + 3 * 4) as u32; let cmd_sig_desc = D3D12_COMMAND_SIGNATURE_DESC {
ByteStride: stride,
NumArgumentDescs: arg_descs.len() as u32,
pArgumentDescs: arg_descs.as_ptr(),
NodeMask: 0,
};
let mut sig: Option<ID3D12CommandSignature> = None;
let result = unsafe {
device.CreateCommandSignature(
&cmd_sig_desc,
root_sig, &mut sig,
)
};
if let Err(e) = result {
tracing::warn!(
"Failed to create batch dispatch command signature: {e}; DispatchBatch will use per-dispatch fallback"
);
None
} else {
tracing::debug!("Created batch dispatch command signature (stride={stride}B)");
sig
}
} else {
tracing::warn!("No bindless root signature; DispatchBatch batch path unavailable");
None
};
let zero_buffer_desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
Alignment: 0,
Width: super::buffer::ZERO_BUFFER_SIZE,
Height: 1,
DepthOrArraySize: 1,
MipLevels: 1,
Format: windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_UNKNOWN,
SampleDesc: windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC { Count: 1, Quality: 0 },
Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
Flags: D3D12_RESOURCE_FLAG_NONE,
};
let zero_heap_props = D3D12_HEAP_PROPERTIES {
Type: D3D12_HEAP_TYPE_UPLOAD,
..Default::default()
};
let mut zero_buffer_opt: Option<ID3D12Resource> = None;
unsafe {
device.CreateCommittedResource(
&zero_heap_props,
D3D12_HEAP_FLAG_NONE,
&zero_buffer_desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
None,
&mut zero_buffer_opt,
)
}
.context("Failed to create zero buffer")?;
let zero_buffer = zero_buffer_opt.context("CreateCommittedResource returned null for zero buffer")?;
let fence: ID3D12Fence =
unsafe { device.CreateFence(0, D3D12_FENCE_FLAG_NONE) }.context("Failed to create fence")?;
let handle = state.next_device_handle;
state.next_device_handle += 1;
let (graphics_pso_blobs, compute_pso_blobs) = dirs::cache_dir().map_or_else(
|| (HashMap::new(), HashMap::new()),
|cache_root| pso_cache::load_maps(&cache_root.join("goldy").join(format!("dx12_pso_{adapter_id}.bin"))),
);
let device_last_submitted_seq = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
state.devices.insert(
handle,
std::sync::Arc::new(LogicalDevice {
device,
adapter_id,
command_queue,
command_allocator,
rtv_heap,
rtv_descriptor_size,
dsv_heap,
dsv_descriptor_size,
cbv_srv_uav_heap,
cbv_srv_uav_descriptor_size,
sampler_heap,
sampler_descriptor_size,
fence,
timeline_next: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(1)),
retired_floor: std::sync::atomic::AtomicU64::new(0),
supports_reserved_buffers,
tile_heap_pool: std::sync::Mutex::new(if supports_reserved_buffers {
Some(super::tiles::TileHeapPool::new())
} else {
None
}),
bindless_root_signature,
compute_dispatch_indirect_signature,
compute_batch_dispatch_signature,
zero_buffer,
deletion_queue: std::sync::Mutex::new(super::types::DeviceDeletionQueue::new()),
pending_buffer_gpu_releases: std::sync::Mutex::new(Vec::new()),
device_removed: std::sync::Arc::clone(&state.device_removed),
descriptors: std::sync::Arc::new(std::sync::Mutex::new(super::types::DescriptorRegistry::new())),
pso_cache: std::sync::Arc::new(std::sync::RwLock::new(super::types::PsoCache::new(
graphics_pso_blobs,
compute_pso_blobs,
))),
queue_lock: std::sync::Arc::new(std::sync::Mutex::new(())),
device_last_submitted_seq: std::sync::Arc::clone(&device_last_submitted_seq),
device_direct_pool: std::sync::Mutex::new(Vec::new()),
legacy_frame_table: std::sync::Mutex::new(None),
submission_worker: std::sync::Arc::new(crate::backend::submission_worker::SubmissionWorker::new(
crate::backend::submission_worker::SUBMISSION_QUEUE_CAPACITY,
)),
}),
);
let owner_id = state.next_context_id;
state.next_context_id = state.next_context_id.saturating_add(1);
let ld = state.devices.get(&handle).unwrap();
state
.context_fences
.write()
.unwrap()
.insert(owner_id, (handle, ld.fence.clone(), device_last_submitted_seq));
state.device_owner_handles.insert(handle, owner_id);
{
let ld = state.devices.get(&handle).unwrap();
super::frame_table::reserve_device_bindless_slots(ld);
}
tracing::info!("Created DX12 device {} for adapter {}", handle, adapter_id);
if super::api_log::enabled() {
super::api_log::log_device_create(adapter_id, handle);
}
Ok(handle)
}