#[allow(clippy::too_many_arguments)] pub(super) fn expose(
adapter: DxgiAdapter,
library: &Arc<D3D12Lib>,
device_factory: &Arc<DeviceFactory>,
dcomp_lib: &Arc<DCompLib>,
instance_flags: wgt::InstanceFlags,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
compiler_container: Arc<shader_compilation::CompilerContainer>,
backend_options: wgt::Dx12BackendOptions,
telemetry: Option<crate::Telemetry>,
) -> Option<crate::ExposedAdapter<super::Api>> { let desc = unsafe { adapter.GetDesc2() }.unwrap(); let driver_version = unsafe { adapter.CheckInterfaceSupport(&Dxgi::IDXGIDevice::IID) }; let driver_version = driver_version
.map(|driver_version| { let driver_version = driver_version as u64;
[
(driver_version >> 48) as u16,
(driver_version >> 32) as u16,
(driver_version >> 16) as u16,
driver_version as u16,
]
})
.map_err(|e| e.code());
// Create the device so that we can get the capabilities. let res = {
profiling::scope!("ID3D12Device::create_device");
device_factory.create_device(library, &adapter, Direct3D::D3D_FEATURE_LEVEL_11_0)
}; iflet Some(telemetry) = telemetry { iflet Err(err) = res {
(telemetry.d3d12_expose_adapter)(
&desc,
driver_version, crate::D3D12ExposeAdapterResult::CreateDeviceError(err),
);
}
} let device = res.ok()?;
let is_warp = device_name.contains("Microsoft Basic Render Driver");
// WARP uses two different versioning schemes. Versions that ship with windows // use a version that starts with 10.x.x.x. Versions that ship from Nuget use 1.0.x.x. // // As far as we know, this is only an issue on the Nuget versions. iflet Ok(driver_version) = driver_version { if is_warp && driver_version >= [1, 0, 13, 0] && driver_version[0] < 10 {
workarounds.avoid_shader_debug_info = true;
}
}
let driver_version_string = { let driver_version = driver_version.unwrap_or([0, 0, 0, 0]);
format!( "{}.{}.{}.{}",
driver_version[0], driver_version[1], driver_version[2], driver_version[3]
)
};
/// Resource Binding Tiers: https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-support#limits-dependant-on-hardware #[derive(PartialEq, Eq, PartialOrd, Ord)] enum ResourceBindingTier {
T1,
T2,
T3,
} let rbt = match options.ResourceBindingTier {
Direct3D12::D3D12_RESOURCE_BINDING_TIER_1 => ResourceBindingTier::T1,
Direct3D12::D3D12_RESOURCE_BINDING_TIER_2 => ResourceBindingTier::T2,
tier if tier.0 >= Direct3D12::D3D12_RESOURCE_BINDING_TIER_3.0 => {
ResourceBindingTier::T3
}
other => {
log::debug!("Got zero or negative value for resource binding tier {other:?}");
ResourceBindingTier::T1
}
};
if rbt == ResourceBindingTier::T1 { iflet Some(telemetry) = telemetry {
(telemetry.d3d12_expose_adapter)(
&desc,
driver_version, crate::D3D12ExposeAdapterResult::ResourceBindingTier2Requirement,
);
} // We require Tier 2 or higher for the ability to make samplers bindless in all cases. return None;
}
let heap_create_not_zeroed = { // For D3D12_HEAP_FLAG_CREATE_NOT_ZEROED we just need to // make sure that options7 can be queried. See also: // https://devblogs.microsoft.com/directx/coming-to-directx-12-more-control-over-memory-allocation/ letmut features7 = Direct3D12::D3D12_FEATURE_DATA_D3D12_OPTIONS7::default(); unsafe {
device.CheckFeatureSupport(
Direct3D12::D3D12_FEATURE_D3D12_OPTIONS7,
<*mut _>::cast(&mut features7),
size_of_val(&features7) as u32,
)
}
.is_ok()
};
letmut max_sampler_descriptor_heap_size =
Direct3D12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE;
{ letmut features19 = Direct3D12::D3D12_FEATURE_DATA_D3D12_OPTIONS19::default(); let res = unsafe {
device.CheckFeatureSupport(
Direct3D12::D3D12_FEATURE_D3D12_OPTIONS19,
<*mut _>::cast(&mut features19),
size_of_val(&features19) as u32,
)
};
// Sometimes on Windows 11 23H2, the function returns success, even though the runtime // does not know about `Options19`. This can cause this number to be 0 as the structure isn't written to. // This value is nonsense and creating zero-sized sampler heaps can cause drivers to explode. // As as we're guaranteed 2048 anyway, we make sure this value is not under 2048. // // https://github.com/gfx-rs/wgpu/issues/7053 let is_ok = res.is_ok(); let is_above_minimum = features19.MaxSamplerDescriptorHeapSize
> Direct3D12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE; if is_ok && is_above_minimum {
max_sampler_descriptor_heap_size = features19.MaxSamplerDescriptorHeapSize;
}
};
//TODO: in order to expose this, we need to run a compute shader // that extract the necessary statistics out of the D3D12 result. // Alternatively, we could allocate a buffer for the query set, // write the results there, and issue a bunch of copy commands. //| wgt::Features::PIPELINE_STATISTICS_QUERY
if max_feature_level >= FeatureLevel::V11_1 {
features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
}
// Once ray tracing pipelines are supported they also will go here let supports_ray_tracing = features5.RaytracingTier.0
>= Direct3D12::D3D12_RAYTRACING_TIER_1_1.0
&& shader_model >= naga::back::hlsl::ShaderModel::V6_5
&& has_features5;
// Binding arrays of TLAS are supported on D3D12 when ray tracing is supported. // // This flag is used for shader-side `binding_array<acceleration_structure>` as well as // allowing `BindGroupLayoutEntry::count = Some(...)` for `BindingType::AccelerationStructure`.
features.set(
wgt::Features::ACCELERATION_STRUCTURE_BINDING_ARRAY,
supports_ray_tracing,
);
// Check for Int64 atomic support on buffers. This is very convoluted, but is based on a conservative reading // of https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_Int64_and_Float_Atomics.html#integer-64-bit-capabilities. let atomic_int64_buffers; let atomic_int64_textures;
{ letmut features9 = Direct3D12::D3D12_FEATURE_DATA_D3D12_OPTIONS9::default(); let hr9 = unsafe {
device.CheckFeatureSupport(
Direct3D12::D3D12_FEATURE_D3D12_OPTIONS9,
<*mut _>::cast(&mut features9),
size_of_val(&features9) as u32,
)
}
.is_ok();
letmut features11 = Direct3D12::D3D12_FEATURE_DATA_D3D12_OPTIONS11::default(); let hr11 = unsafe {
device.CheckFeatureSupport(
Direct3D12::D3D12_FEATURE_D3D12_OPTIONS11,
<*mut _>::cast(&mut features11),
size_of_val(&features11) as u32,
)
}
.is_ok();
atomic_int64_buffers = hr9 && hr11 && hr.is_ok() // Int64 atomics show up in SM6.6.
&& shader_model >= naga::back::hlsl::ShaderModel::V6_6 // They require Int64 to be available in the shader at all.
&& features1.Int64ShaderOps.as_bool() // As our RWByteAddressBuffers can exist on both descriptor heaps and // as root descriptors, we need to ensure that both cases are supported. // base SM6.6 only guarantees Int64 atomics on resources in root descriptors.
&& features11.AtomicInt64OnDescriptorHeapResourceSupported.as_bool() // Our Int64 atomic caps currently require groupshared. This // prevents Intel or Qcomm from using Int64 currently. // https://github.com/gfx-rs/wgpu/issues/8666
&& features9.AtomicInt64OnGroupSharedSupported.as_bool();
atomic_int64_textures = hr9 && hr11 && hr.is_ok() // Int64 atomics show up in SM6.6.
&& shader_model >= naga::back::hlsl::ShaderModel::V6_6 // They require Int64 to be available in the shader at all.
&& features1.Int64ShaderOps.as_bool() // Textures are typed resources, so we need this flag.
&& features9.AtomicInt64OnTypedResourceSupported.as_bool() // As textures can only exist in descriptor heaps, we require this. // However, all architectures that support atomics on typed resources // support this as well, so this is somewhat redundant.
&& features11.AtomicInt64OnDescriptorHeapResourceSupported.as_bool();
};
features.set(
wgt::Features::SHADER_INT64_ATOMIC_ALL_OPS | wgt::Features::SHADER_INT64_ATOMIC_MIN_MAX,
atomic_int64_buffers,
);
features.set(wgt::Features::TEXTURE_INT64_ATOMIC, atomic_int64_textures); let mesh_shader_supported = { letmut features7 = Direct3D12::D3D12_FEATURE_DATA_D3D12_OPTIONS7::default(); unsafe {
device.CheckFeatureSupport(
Direct3D12::D3D12_FEATURE_D3D12_OPTIONS7,
<*mut _>::cast(&mut features7),
size_of_val(&features7) as u32,
)
}
.is_ok()
&& features7.MeshShaderTier != Direct3D12::D3D12_MESH_SHADER_TIER_NOT_SUPPORTED
&& shader_model >= naga::back::hlsl::ShaderModel::V6_5
};
features.set(
wgt::Features::EXPERIMENTAL_MESH_SHADER,
mesh_shader_supported,
); let shader_barycentrics_supported = { letmut features3 = Direct3D12::D3D12_FEATURE_DATA_D3D12_OPTIONS3::default(); unsafe {
device.CheckFeatureSupport(
Direct3D12::D3D12_FEATURE_D3D12_OPTIONS3,
<*mut _>::cast(&mut features3),
size_of_val(&features3) as u32,
)
}
.is_ok()
&& features3.BarycentricsSupported.as_bool()
&& shader_model >= naga::back::hlsl::ShaderModel::V6_1
};
features.set(
wgt::Features::SHADER_BARYCENTRICS | wgt::Features::SHADER_PER_VERTEX,
shader_barycentrics_supported,
);
// TODO: Determine if IPresentationManager is supported let presentation_timer = auxil::dxgi::time::PresentationTimer::new_dxgi();
let downlevel = wgt::DownlevelCapabilities::default();
// Limits that must share D3D12's root signature size of // D3D12_MAX_ROOT_COST 64 DWORDS (256 bytes). // // Root constants and root tables use 1 DWORD. // Root descriptors use 2 DWORDs. // Source: https://learn.microsoft.com/en-us/windows/win32/direct3d12/root-signature-limits#memory-limits-and-costs // // Per pipeline layout: // - RootElement::Immediates, 32 root constants // (bounded by maxImmediateSize) = 32 x 4 bytes = 128 bytes // - RootElement::SamplerHeapDescriptorTable, a descriptor table = 4 bytes // - RootElement::SpecialConstantBuffer, 3 root constants = 3 x 4 bytes = 12 bytes // - RootElement::DynamicStorageBufferOffsets, a root constant per dynamic storage buffer // (bounded by maxDynamicStorageBuffersPerPipelineLayout) = 4 x 4 bytes = 16 bytes // - RootElement::DynamicUniformBuffer, a root descriptor per dynamic uniform buffer // (bounded by maxDynamicUniformBuffersPerPipelineLayout) = 8 x 8 bytes = 64 bytes // Per bind group: // - RootElement::DescriptorTable, a descriptor table // (bounded by maxBindGroups) = 8 x 4 bytes = 32 bytes // // Source: logic in `create_pipeline_layout` // // Total: 128 + 4 + 12 + 16 + 64 + 32 = 256 bytes // let max_immediate_size = super::MAX_IMMEDIATE_SIZE; let max_bind_groups = 8; let max_dynamic_uniform_buffers_per_pipeline_layout = 8; let max_dynamic_storage_buffers_per_pipeline_layout = 4;
// "Maximum number of descriptors in a Constant Buffer View (CBV), Shader Resource View (SRV), or Unordered Access View(UAV) heap used for rendering" let full_heap_count = match rbt {
ResourceBindingTier::T1 | ResourceBindingTier::T2 => 1_000_000, // 1_000_000+
ResourceBindingTier::T3 => { // Theoretically vram limited, but in practice 2^20 is the limit 1 << 20
}
};
// "Maximum number of Constant Buffer Views in all descriptor tables per shader stage" let max_uniform_buffers_per_shader_stage = match rbt {
ResourceBindingTier::T1 | ResourceBindingTier::T2 => 14,
_ => full_heap_count,
};
// "Maximum number of Shader Resource Views in all descriptor tables per shader stage" letmut max_srv_per_shader_stage = match rbt {
ResourceBindingTier::T1 => 128,
_ => full_heap_count,
};
// We use an extra SRV for all samplers in a bind group. // See comment in `create_pipeline_layout`.
max_srv_per_shader_stage -= max_bind_groups;
// If we also support acceleration structures these are shared so we must halve it. // It's unlikely that this affects anything because most devices that support ray tracing // probably have a higher binding tier than one. letmut max_sampled_textures_per_shader_stage = if supports_ray_tracing {
max_srv_per_shader_stage / 2
} else {
max_srv_per_shader_stage
}; letmut max_acceleration_structures_per_shader_stage = if supports_ray_tracing {
max_srv_per_shader_stage / 2
} else { 0
};
// "Maximum number of Unordered Access Views in all descriptor tables across all stages" let max_uav_across_all_stages = match rbt {
ResourceBindingTier::T1 => match max_feature_level {
FeatureLevel::V11_0 => 8,
_ => 64,
},
ResourceBindingTier::T2 => 64,
ResourceBindingTier::T3 => full_heap_count,
}; const MAX_SHADER_STAGES_PER_PIPELINE: u32 = 2; // We must share the UAV limit across both storage resource limits. let max_uav_per_shader_stage = max_uav_across_all_stages / MAX_SHADER_STAGES_PER_PIPELINE; let max_storage_textures_per_shader_stage = max_uav_per_shader_stage / 2; letmut max_storage_buffers_per_shader_stage = max_uav_per_shader_stage / 2;
// WebGPU storage buffers count as 1 SRV if they are read-only // or as 1 UAV if they are read-write. See comment in // `create_pipeline_layout`. Make sure we don't exceed // the maximum number of SRVs for the relevant limits.
auxil::cap_limits_to_be_under_the_sum_limit(
[
&mut max_sampled_textures_per_shader_stage,
&mut max_acceleration_structures_per_shader_stage,
&mut max_storage_buffers_per_shader_stage,
],
max_srv_per_shader_stage,
);
// "Maximum number of Samplers in all descriptor tables per shader stage" let max_samplers_per_shader_stage = match rbt {
ResourceBindingTier::T1 => 16,
_ => 2048,
};
// Source: https://microsoft.github.io/DirectX-Specs/d3d/MeshShader.html#dispatchmesh-api let max_task_mesh_workgroup_total_count = if mesh_shader_supported { 2u32.pow(22)
} else { 0
}; // Technically it says "64k" but I highly doubt they want 65536 for compute and exactly 64,000 for task workgroups let max_task_mesh_workgroups_per_dimension = if mesh_shader_supported {
Direct3D12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION
} else { 0
};
Some(crate::ExposedAdapter {
adapter: super::Adapter {
raw: adapter,
device,
library: Arc::clone(library),
dcomp_lib: Arc::clone(dcomp_lib),
private_caps,
presentation_timer,
memory_budget_thresholds,
compiler_container,
options: backend_options,
},
info,
features,
capabilities: crate::Capabilities {
limits: auxil::adjust_raw_limits(wgt::Limits { // // WebGPU LIMITS: // Based on https://gpuweb.github.io/gpuweb/correspondence/#limits // // 16384
max_texture_dimension_1d: Direct3D12::D3D12_REQ_TEXTURE1D_U_DIMENSION, // 16384
max_texture_dimension_2d: Direct3D12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION
.min(Direct3D12::D3D12_REQ_TEXTURECUBE_DIMENSION), // 2048
max_texture_dimension_3d: Direct3D12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION, // 2048
max_texture_array_layers: Direct3D12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION, // No limit.
max_bind_groups_plus_vertex_buffers: u32::MAX, // No limit.
max_bindings_per_bind_group: u32::MAX,
max_sampled_textures_per_shader_stage,
max_samplers_per_shader_stage,
max_storage_textures_per_shader_stage,
max_storage_buffers_per_shader_stage,
max_uniform_buffers_per_shader_stage, // See `InputSlot` param docs: https://learn.microsoft.com/en-ca/windows/win32/api/d3d12/ns-d3d12-d3d12_input_element_desc
max_vertex_buffers: 16, // Dx12 does not expose a maximum buffer size in the API. // This limit is chosen to avoid potential issues with drivers should they internally // store buffer sizes using 32 bit ints (a situation we have already encountered with vulkan).
max_buffer_size: i32::MAX as u64,
max_storage_buffer_binding_size: auxil::MAX_I32_BINDING_SIZE as u64, // 65536
max_uniform_buffer_binding_size:
Direct3D12::D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT as u64 * 16, // 256
min_uniform_buffer_offset_alignment:
Direct3D12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, // 16
min_storage_buffer_offset_alignment:
Direct3D12::D3D12_RAW_UAV_SRV_BYTE_ALIGNMENT, // 30
max_vertex_attributes: Direct3D12::D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT
- 2, // -2 for `SV_VertexID` and `SV_InstanceID` // 2048
max_vertex_buffer_array_stride: Direct3D12::D3D12_SO_BUFFER_MAX_STRIDE_IN_BYTES, // 31
max_inter_stage_shader_variables: Direct3D12::D3D12_VS_OUTPUT_REGISTER_COUNT
.min(Direct3D12::D3D12_PS_INPUT_REGISTER_COUNT)
- 1, // - 1 for position
max_immediate_size,
max_bind_groups,
max_dynamic_uniform_buffers_per_pipeline_layout,
max_dynamic_storage_buffers_per_pipeline_layout, // 8
max_color_attachments: Direct3D12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT, // 128 (No documented limit)
max_color_attachment_bytes_per_sample:
Direct3D12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT
* wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST, // From: https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#18.6.6%20Inter-Thread%20Data%20Sharing
max_compute_workgroup_storage_size: 32768, // 1024
max_compute_invocations_per_workgroup:
Direct3D12::D3D12_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP, // 1024
max_compute_workgroup_size_x: Direct3D12::D3D12_CS_THREAD_GROUP_MAX_X, // 1024
max_compute_workgroup_size_y: Direct3D12::D3D12_CS_THREAD_GROUP_MAX_Y, // 64
max_compute_workgroup_size_z: Direct3D12::D3D12_CS_THREAD_GROUP_MAX_Z, // 65535
max_compute_workgroups_per_dimension:
Direct3D12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, // // NATIVE (Non-WebGPU) LIMITS: //
max_non_sampler_bindings: 1_000_000,
max_binding_array_elements_per_shader_stage: full_heap_count,
max_binding_array_sampler_elements_per_shader_stage:
Direct3D12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE,
let raw_format = match auxil::dxgi::conv::map_texture_format_failable(format) {
Some(f) => f,
None => return Tfc::empty(),
}; let srv_uav_format = if format.is_combined_depth_stencil_format() {
auxil::dxgi::conv::map_texture_format_for_srv_uav(
format, // use the depth aspect here as opposed to stencil since it has more capabilities crate::FormatAspects::DEPTH,
)
} else {
auxil::dxgi::conv::map_texture_format_for_srv_uav(
format, crate::FormatAspects::from(format),
)
}
.unwrap();
// Because we use a different format for SRV and UAV views of depth textures, we need to check // the features that use SRV/UAVs using the no-depth format. letmut data_srv_uav = Direct3D12::D3D12_FEATURE_DATA_FORMAT_SUPPORT {
Format: srv_uav_format,
Support1: Direct3D12::D3D12_FORMAT_SUPPORT1_NONE,
Support2: Direct3D12::D3D12_FORMAT_SUPPORT2_NONE,
}; if raw_format != srv_uav_format { // Only-recheck if we're using a different format unsafe { self.device.CheckFeatureSupport(
Direct3D12::D3D12_FEATURE_FORMAT_SUPPORT,
ptr::addr_of_mut!(data_srv_uav).cast(),
size_of::<Direct3D12::D3D12_FEATURE_DATA_FORMAT_SUPPORT>() as u32,
)
}
.unwrap();
} else { // Same format, just copy over.
data_srv_uav = data;
}
// We load via UAV/SRV so use srv_uav_format let no_msaa_load = caps.contains(Tfc::SAMPLED)
&& !data_srv_uav
.Support1
.contains(Direct3D12::D3D12_FORMAT_SUPPORT1_MULTISAMPLE_LOAD);
// Don't put barriers between inclusive uses // DX12 implicitly orders renderpasses on the same resources. fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
wgt::TextureUses::INCLUSIVE
| wgt::TextureUses::COLOR_TARGET
| wgt::TextureUses::DEPTH_STENCIL_WRITE
}
}
fn get_adapter_pci_info(vendor_id: u32, device_id: u32) -> String { // SAFETY: SetupDiGetClassDevsW is called with valid parameters let device_info_set = unsafe { match SetupDiGetClassDevsW(Some(&GUID_DEVCLASS_DISPLAY), None, None, DIGCF_PRESENT) {
Ok(set) => set,
Err(_) => return String::new(),
}
};
struct DeviceInfoSetGuard(HDEVINFO); impl Drop for DeviceInfoSetGuard { fn drop(&mutself) { // SAFETY: device_info_set is a valid HDEVINFO and is only dropped once via this guard unsafe { let _ = SetupDiDestroyDeviceInfoList(self.0);
}
}
} let _guard = DeviceInfoSetGuard(device_info_set);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.