summaryrefslogtreecommitdiffstats
path: root/third_party/rust/gfx-backend-dx12/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/gfx-backend-dx12/src/lib.rs')
-rw-r--r--third_party/rust/gfx-backend-dx12/src/lib.rs1495
1 files changed, 1495 insertions, 0 deletions
diff --git a/third_party/rust/gfx-backend-dx12/src/lib.rs b/third_party/rust/gfx-backend-dx12/src/lib.rs
new file mode 100644
index 0000000000..907977263f
--- /dev/null
+++ b/third_party/rust/gfx-backend-dx12/src/lib.rs
@@ -0,0 +1,1495 @@
+/*!
+# D3D12 backend internals.
+
+## Resource transitions
+
+Vulkan semantics for resource states doesn't exactly match D3D12.
+
+For regular images, whenever there is a specific layout used,
+we map it to a corresponding D3D12 resource state.
+
+For the swapchain images, we consider them to be in COMMON state
+everywhere except for render passes, where it's forcefully
+transitioned into the render state. When transfers to/from are
+requested, we transition them into and from the COPY_ states.
+
+For buffers and images in General layout, we the best effort of guessing
+the single mutable state based on the access flags. We can't reliably
+handle a case where multiple mutable access flags are used.
+*/
+
+#[macro_use]
+extern crate bitflags;
+#[macro_use]
+extern crate log;
+
+mod command;
+mod conv;
+mod descriptors_cpu;
+mod device;
+mod internal;
+mod pool;
+mod resource;
+mod root_constants;
+mod window;
+
+use auxil::FastHashMap;
+use hal::{
+ adapter, format as f, image, memory, pso::PipelineStage, queue as q, Features, Hints, Limits,
+};
+use range_alloc::RangeAllocator;
+
+use parking_lot::{Mutex, RwLock};
+use smallvec::SmallVec;
+use winapi::{
+ shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, minwindef::TRUE, winerror},
+ um::{d3d12, d3d12sdklayers, handleapi, synchapi, winbase},
+ Interface,
+};
+
+use std::{
+ borrow::Borrow,
+ ffi::OsString,
+ fmt,
+ mem,
+ os::windows::ffi::OsStringExt,
+ //TODO: use parking_lot
+ sync::Arc,
+};
+
+use self::descriptors_cpu::DescriptorCpuPool;
+
+#[derive(Debug)]
+pub(crate) struct HeapProperties {
+ pub page_property: d3d12::D3D12_CPU_PAGE_PROPERTY,
+ pub memory_pool: d3d12::D3D12_MEMORY_POOL,
+}
+
+// https://msdn.microsoft.com/de-de/library/windows/desktop/dn770377(v=vs.85).aspx
+// Only 16 input slots allowed.
+const MAX_VERTEX_BUFFERS: usize = 16;
+const MAX_DESCRIPTOR_SETS: usize = 8;
+
+const NUM_HEAP_PROPERTIES: usize = 3;
+
+pub type DescriptorIndex = u64;
+
+// Memory types are grouped according to the supported resources.
+// Grouping is done to circumvent the limitations of heap tier 1 devices.
+// Devices with Tier 1 will expose `BuffersOnly`, `ImageOnly` and `TargetOnly`.
+// Devices with Tier 2 or higher will only expose `Universal`.
+enum MemoryGroup {
+ Universal = 0,
+ BufferOnly,
+ ImageOnly,
+ TargetOnly,
+
+ NumGroups,
+}
+
+// https://msdn.microsoft.com/de-de/library/windows/desktop/dn788678(v=vs.85).aspx
+static HEAPS_NUMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [
+ // DEFAULT
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L1,
+ },
+ // UPLOAD
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
+ },
+ // READBACK
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
+ },
+];
+
+static HEAPS_UMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [
+ // DEFAULT
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
+ },
+ // UPLOAD
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
+ },
+ // READBACK
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
+ },
+];
+
+static HEAPS_CCUMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [
+ // DEFAULT
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
+ },
+ // UPLOAD
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
+ },
+ //READBACK
+ HeapProperties {
+ page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
+ memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
+ },
+];
+
+#[derive(Debug, Copy, Clone)]
+pub enum QueueFamily {
+ // Specially marked present queue.
+ // It's basically a normal 3D queue but D3D12 swapchain creation requires an
+ // associated queue, which we don't know on `create_swapchain`.
+ Present,
+ Normal(q::QueueType),
+}
+
+const MAX_QUEUES: usize = 16; // infinite, to be fair
+
+impl q::QueueFamily for QueueFamily {
+ fn queue_type(&self) -> q::QueueType {
+ match *self {
+ QueueFamily::Present => q::QueueType::General,
+ QueueFamily::Normal(ty) => ty,
+ }
+ }
+ fn max_queues(&self) -> usize {
+ match *self {
+ QueueFamily::Present => 1,
+ QueueFamily::Normal(_) => MAX_QUEUES,
+ }
+ }
+ fn id(&self) -> q::QueueFamilyId {
+ // This must match the order exposed by `QUEUE_FAMILIES`
+ q::QueueFamilyId(match *self {
+ QueueFamily::Present => 0,
+ QueueFamily::Normal(q::QueueType::General) => 1,
+ QueueFamily::Normal(q::QueueType::Compute) => 2,
+ QueueFamily::Normal(q::QueueType::Transfer) => 3,
+ _ => unreachable!(),
+ })
+ }
+}
+
+impl QueueFamily {
+ fn native_type(&self) -> native::CmdListType {
+ use hal::queue::QueueFamily as _;
+ use native::CmdListType as Clt;
+
+ let queue_type = self.queue_type();
+ match queue_type {
+ q::QueueType::General | q::QueueType::Graphics => Clt::Direct,
+ q::QueueType::Compute => Clt::Compute,
+ q::QueueType::Transfer => Clt::Copy,
+ }
+ }
+}
+
+static QUEUE_FAMILIES: [QueueFamily; 4] = [
+ QueueFamily::Present,
+ QueueFamily::Normal(q::QueueType::General),
+ QueueFamily::Normal(q::QueueType::Compute),
+ QueueFamily::Normal(q::QueueType::Transfer),
+];
+
+#[derive(Default)]
+struct Workarounds {
+ // On WARP, temporary CPU descriptors are still used by the runtime
+ // after we call `CopyDescriptors`.
+ avoid_cpu_descriptor_overwrites: bool,
+}
+
+//Note: fields are dropped in the order of declaration, so we put the
+// most owning fields last.
+pub struct PhysicalDevice {
+ features: Features,
+ hints: Hints,
+ limits: Limits,
+ format_properties: Arc<FormatProperties>,
+ private_caps: Capabilities,
+ workarounds: Workarounds,
+ heap_properties: &'static [HeapProperties; NUM_HEAP_PROPERTIES],
+ memory_properties: adapter::MemoryProperties,
+ // Indicates that there is currently an active logical device.
+ // Opening the same adapter multiple times will return the same D3D12Device again.
+ is_open: Arc<Mutex<bool>>,
+ adapter: native::WeakPtr<dxgi1_2::IDXGIAdapter2>,
+ library: Arc<native::D3D12Lib>,
+}
+
+impl fmt::Debug for PhysicalDevice {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.write_str("PhysicalDevice")
+ }
+}
+
+unsafe impl Send for PhysicalDevice {}
+unsafe impl Sync for PhysicalDevice {}
+
+impl adapter::PhysicalDevice<Backend> for PhysicalDevice {
+ unsafe fn open(
+ &self,
+ families: &[(&QueueFamily, &[q::QueuePriority])],
+ requested_features: Features,
+ ) -> Result<adapter::Gpu<Backend>, hal::device::CreationError> {
+ let mut open_guard = match self.is_open.try_lock() {
+ Some(inner) => inner,
+ None => return Err(hal::device::CreationError::TooManyObjects),
+ };
+
+ if !self.features().contains(requested_features) {
+ return Err(hal::device::CreationError::MissingFeature);
+ }
+
+ let device_raw = match self
+ .library
+ .create_device(self.adapter, native::FeatureLevel::L11_0)
+ {
+ Ok((device, hr)) if winerror::SUCCEEDED(hr) => device,
+ Ok((_, hr)) => {
+ error!("error on device creation: {:x}", hr);
+ return Err(hal::device::CreationError::InitializationFailed);
+ }
+ Err(e) => panic!("device creation failed with {:?}", e),
+ };
+
+ // Always create the presentation queue in case we want to build a swapchain.
+ let (present_queue, hr_queue) = device_raw.create_command_queue(
+ QueueFamily::Present.native_type(),
+ native::Priority::Normal,
+ native::CommandQueueFlags::empty(),
+ 0,
+ );
+ if !winerror::SUCCEEDED(hr_queue) {
+ error!("error on queue creation: {:x}", hr_queue);
+ }
+
+ let mut device = Device::new(device_raw, &self, present_queue);
+ device.features = requested_features;
+
+ let queue_groups = families
+ .into_iter()
+ .map(|&(&family, priorities)| {
+ use hal::queue::QueueFamily as _;
+ let mut group = q::QueueGroup::new(family.id());
+
+ let create_idle_event = || native::Event::create(true, false);
+
+ match family {
+ QueueFamily::Present => {
+ // Exactly **one** present queue!
+ // Number of queues need to be larger than 0 else it
+ // violates the specification.
+ let queue = CommandQueue {
+ raw: device.present_queue.clone(),
+ idle_fence: device.create_raw_fence(false),
+ idle_event: create_idle_event(),
+ };
+ device.append_queue(queue.clone());
+ group.add_queue(queue);
+ }
+ QueueFamily::Normal(_) => {
+ let list_type = family.native_type();
+ for _ in 0..priorities.len() {
+ let (queue, hr_queue) = device_raw.create_command_queue(
+ list_type,
+ native::Priority::Normal,
+ native::CommandQueueFlags::empty(),
+ 0,
+ );
+
+ if winerror::SUCCEEDED(hr_queue) {
+ let queue = CommandQueue {
+ raw: queue,
+ idle_fence: device.create_raw_fence(false),
+ idle_event: create_idle_event(),
+ };
+ device.append_queue(queue.clone());
+ group.add_queue(queue);
+ } else {
+ error!("error on queue creation: {:x}", hr_queue);
+ }
+ }
+ }
+ }
+
+ group
+ })
+ .collect();
+
+ *open_guard = true;
+
+ Ok(adapter::Gpu {
+ device,
+ queue_groups,
+ })
+ }
+
+ fn format_properties(&self, fmt: Option<f::Format>) -> f::Properties {
+ let idx = fmt.map(|fmt| fmt as usize).unwrap_or(0);
+ self.format_properties.resolve(idx).properties
+ }
+
+ fn image_format_properties(
+ &self,
+ format: f::Format,
+ dimensions: u8,
+ tiling: image::Tiling,
+ usage: image::Usage,
+ view_caps: image::ViewCapabilities,
+ ) -> Option<image::FormatProperties> {
+ conv::map_format(format)?; //filter out unknown formats
+ let format_info = self.format_properties.resolve(format as usize);
+
+ let supported_usage = {
+ use hal::image::Usage as U;
+ let props = match tiling {
+ image::Tiling::Optimal => format_info.properties.optimal_tiling,
+ image::Tiling::Linear => format_info.properties.linear_tiling,
+ };
+ let mut flags = U::empty();
+ // Note: these checks would have been nicer if we had explicit BLIT usage
+ if props.contains(f::ImageFeature::BLIT_SRC) {
+ flags |= U::TRANSFER_SRC;
+ }
+ if props.contains(f::ImageFeature::BLIT_DST) {
+ flags |= U::TRANSFER_DST;
+ }
+ if props.contains(f::ImageFeature::SAMPLED) {
+ flags |= U::SAMPLED;
+ }
+ if props.contains(f::ImageFeature::STORAGE) {
+ flags |= U::STORAGE;
+ }
+ if props.contains(f::ImageFeature::COLOR_ATTACHMENT) {
+ flags |= U::COLOR_ATTACHMENT;
+ }
+ if props.contains(f::ImageFeature::DEPTH_STENCIL_ATTACHMENT) {
+ flags |= U::DEPTH_STENCIL_ATTACHMENT;
+ }
+ flags
+ };
+ if !supported_usage.contains(usage) {
+ return None;
+ }
+
+ let max_resource_size =
+ (d3d12::D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM as usize) << 20;
+ Some(match tiling {
+ image::Tiling::Optimal => image::FormatProperties {
+ max_extent: match dimensions {
+ 1 => image::Extent {
+ width: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION,
+ height: 1,
+ depth: 1,
+ },
+ 2 => image::Extent {
+ width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION,
+ height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION,
+ depth: 1,
+ },
+ 3 => image::Extent {
+ width: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION,
+ height: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION,
+ depth: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION,
+ },
+ _ => return None,
+ },
+ max_levels: d3d12::D3D12_REQ_MIP_LEVELS as _,
+ max_layers: match dimensions {
+ 1 => d3d12::D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION as _,
+ 2 => d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _,
+ _ => return None,
+ },
+ sample_count_mask: if dimensions == 2
+ && !view_caps.contains(image::ViewCapabilities::KIND_CUBE)
+ && !usage.contains(image::Usage::STORAGE)
+ {
+ format_info.sample_count_mask
+ } else {
+ 0x1
+ },
+ max_resource_size,
+ },
+ image::Tiling::Linear => image::FormatProperties {
+ max_extent: match dimensions {
+ 2 => image::Extent {
+ width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION,
+ height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION,
+ depth: 1,
+ },
+ _ => return None,
+ },
+ max_levels: 1,
+ max_layers: 1,
+ sample_count_mask: 0x1,
+ max_resource_size,
+ },
+ })
+ }
+
+ fn memory_properties(&self) -> adapter::MemoryProperties {
+ self.memory_properties.clone()
+ }
+
+ fn features(&self) -> Features {
+ self.features
+ }
+
+ fn hints(&self) -> Hints {
+ self.hints
+ }
+
+ fn limits(&self) -> Limits {
+ self.limits
+ }
+}
+
+#[derive(Clone)]
+pub struct CommandQueue {
+ pub(crate) raw: native::CommandQueue,
+ idle_fence: native::Fence,
+ idle_event: native::Event,
+}
+
+impl fmt::Debug for CommandQueue {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.write_str("CommandQueue")
+ }
+}
+
+impl CommandQueue {
+ unsafe fn destroy(&self) {
+ handleapi::CloseHandle(self.idle_event.0);
+ self.idle_fence.destroy();
+ self.raw.destroy();
+ }
+}
+
+unsafe impl Send for CommandQueue {}
+unsafe impl Sync for CommandQueue {}
+
+impl q::CommandQueue<Backend> for CommandQueue {
+ unsafe fn submit<'a, T, Ic, S, Iw, Is>(
+ &mut self,
+ submission: q::Submission<Ic, Iw, Is>,
+ fence: Option<&resource::Fence>,
+ ) where
+ T: 'a + Borrow<command::CommandBuffer>,
+ Ic: IntoIterator<Item = &'a T>,
+ S: 'a + Borrow<resource::Semaphore>,
+ Iw: IntoIterator<Item = (&'a S, PipelineStage)>,
+ Is: IntoIterator<Item = &'a S>,
+ {
+ // Reset idle fence and event
+ // That's safe here due to exclusive access to the queue
+ self.idle_fence.signal(0);
+ synchapi::ResetEvent(self.idle_event.0);
+
+ // TODO: semaphores
+ let lists = submission
+ .command_buffers
+ .into_iter()
+ .map(|cmd_buf| cmd_buf.borrow().as_raw_list())
+ .collect::<SmallVec<[_; 4]>>();
+ self.raw
+ .ExecuteCommandLists(lists.len() as _, lists.as_ptr());
+
+ if let Some(fence) = fence {
+ assert_eq!(winerror::S_OK, self.raw.Signal(fence.raw.as_mut_ptr(), 1));
+ }
+ }
+
+ unsafe fn present(
+ &mut self,
+ surface: &mut window::Surface,
+ image: window::SwapchainImage,
+ _wait_semaphore: Option<&resource::Semaphore>,
+ ) -> Result<Option<hal::window::Suboptimal>, hal::window::PresentError> {
+ surface.present(image).map(|()| None)
+ }
+
+ fn wait_idle(&self) -> Result<(), hal::device::OutOfMemory> {
+ self.raw.signal(self.idle_fence, 1);
+ assert_eq!(
+ winerror::S_OK,
+ self.idle_fence.set_event_on_completion(self.idle_event, 1)
+ );
+
+ unsafe {
+ synchapi::WaitForSingleObject(self.idle_event.0, winbase::INFINITE);
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum MemoryArchitecture {
+ NUMA,
+ UMA,
+ CacheCoherentUMA,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Capabilities {
+ heterogeneous_resource_heaps: bool,
+ memory_architecture: MemoryArchitecture,
+}
+
+#[derive(Clone, Debug)]
+struct CmdSignatures {
+ draw: native::CommandSignature,
+ draw_indexed: native::CommandSignature,
+ dispatch: native::CommandSignature,
+}
+
+impl CmdSignatures {
+ unsafe fn destroy(&self) {
+ self.draw.destroy();
+ self.draw_indexed.destroy();
+ self.dispatch.destroy();
+ }
+}
+
+// Shared objects between command buffers, owned by the device.
+#[derive(Debug)]
+struct Shared {
+ pub signatures: CmdSignatures,
+ pub service_pipes: internal::ServicePipes,
+}
+
+impl Shared {
+ unsafe fn destroy(&self) {
+ self.signatures.destroy();
+ self.service_pipes.destroy();
+ }
+}
+
+pub struct SamplerStorage {
+ map: Mutex<FastHashMap<image::SamplerDesc, descriptors_cpu::Handle>>,
+ pool: Mutex<DescriptorCpuPool>,
+ heap: resource::DescriptorHeap,
+ origins: RwLock<resource::DescriptorOrigins>,
+}
+
+impl SamplerStorage {
+ unsafe fn destroy(&mut self) {
+ self.pool.lock().destroy();
+ self.heap.destroy();
+ }
+}
+
+pub struct Device {
+ raw: native::Device,
+ private_caps: Capabilities,
+ features: Features,
+ format_properties: Arc<FormatProperties>,
+ heap_properties: &'static [HeapProperties],
+ // CPU only pools
+ rtv_pool: Mutex<DescriptorCpuPool>,
+ dsv_pool: Mutex<DescriptorCpuPool>,
+ srv_uav_pool: Mutex<DescriptorCpuPool>,
+ descriptor_updater: Mutex<descriptors_cpu::DescriptorUpdater>,
+ // CPU/GPU descriptor heaps
+ heap_srv_cbv_uav: (
+ resource::DescriptorHeap,
+ Mutex<RangeAllocator<DescriptorIndex>>,
+ ),
+ samplers: SamplerStorage,
+ events: Mutex<Vec<native::Event>>,
+ shared: Arc<Shared>,
+ // Present queue exposed by the `Present` queue family.
+ // Required for swapchain creation. Only a single queue supports presentation.
+ present_queue: native::CommandQueue,
+ // List of all queues created from this device, including present queue.
+ // Needed for `wait_idle`.
+ queues: Vec<CommandQueue>,
+ // Indicates that there is currently an active device.
+ open: Arc<Mutex<bool>>,
+ library: Arc<native::D3D12Lib>,
+}
+
+impl fmt::Debug for Device {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.write_str("Device")
+ }
+}
+
+unsafe impl Send for Device {} //blocked by ComPtr
+unsafe impl Sync for Device {} //blocked by ComPtr
+
+impl Device {
+ fn new(
+ device: native::Device,
+ physical_device: &PhysicalDevice,
+ present_queue: native::CommandQueue,
+ ) -> Self {
+ // Allocate descriptor heaps
+ let rtv_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Rtv);
+ let dsv_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Dsv);
+ let srv_uav_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::CbvSrvUav);
+
+ // maximum number of CBV/SRV/UAV descriptors in heap for Tier 1
+ let view_capacity = 1_000_000;
+ let heap_srv_cbv_uav = Self::create_descriptor_heap_impl(
+ device,
+ native::DescriptorHeapType::CbvSrvUav,
+ true,
+ view_capacity,
+ );
+ let view_range_allocator = RangeAllocator::new(0..(view_capacity as u64));
+
+ let sampler_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Sampler);
+ let heap_sampler = Self::create_descriptor_heap_impl(
+ device,
+ native::DescriptorHeapType::Sampler,
+ true,
+ 2_048,
+ );
+
+ let descriptor_updater = descriptors_cpu::DescriptorUpdater::new(
+ device,
+ physical_device.workarounds.avoid_cpu_descriptor_overwrites,
+ );
+
+ let draw_signature = Self::create_command_signature(device, device::CommandSignature::Draw);
+ let draw_indexed_signature =
+ Self::create_command_signature(device, device::CommandSignature::DrawIndexed);
+ let dispatch_signature =
+ Self::create_command_signature(device, device::CommandSignature::Dispatch);
+
+ let signatures = CmdSignatures {
+ draw: draw_signature,
+ draw_indexed: draw_indexed_signature,
+ dispatch: dispatch_signature,
+ };
+ let service_pipes =
+ internal::ServicePipes::new(device, Arc::clone(&physical_device.library));
+ let shared = Shared {
+ signatures,
+ service_pipes,
+ };
+
+ Device {
+ raw: device,
+ library: Arc::clone(&physical_device.library),
+ private_caps: physical_device.private_caps,
+ features: Features::empty(),
+ format_properties: physical_device.format_properties.clone(),
+ heap_properties: physical_device.heap_properties,
+ rtv_pool: Mutex::new(rtv_pool),
+ dsv_pool: Mutex::new(dsv_pool),
+ srv_uav_pool: Mutex::new(srv_uav_pool),
+ descriptor_updater: Mutex::new(descriptor_updater),
+ heap_srv_cbv_uav: (heap_srv_cbv_uav, Mutex::new(view_range_allocator)),
+ samplers: SamplerStorage {
+ map: Mutex::default(),
+ pool: Mutex::new(sampler_pool),
+ heap: heap_sampler,
+ origins: RwLock::default(),
+ },
+ events: Mutex::new(Vec::new()),
+ shared: Arc::new(shared),
+ present_queue,
+ queues: Vec::new(),
+ open: Arc::clone(&physical_device.is_open),
+ }
+ }
+
+ fn append_queue(&mut self, queue: CommandQueue) {
+ self.queues.push(queue);
+ }
+
+ /// Get the native d3d12 device.
+ ///
+ /// Required for FFI with libraries like RenderDoc.
+ pub unsafe fn as_raw(&self) -> *mut d3d12::ID3D12Device {
+ self.raw.as_mut_ptr()
+ }
+}
+
+impl Drop for Device {
+ fn drop(&mut self) {
+ *self.open.lock() = false;
+
+ unsafe {
+ for queue in &mut self.queues {
+ let _ = q::CommandQueue::wait_idle(queue);
+ queue.destroy();
+ }
+
+ self.shared.destroy();
+ self.heap_srv_cbv_uav.0.destroy();
+ self.samplers.destroy();
+ self.rtv_pool.lock().destroy();
+ self.dsv_pool.lock().destroy();
+ self.srv_uav_pool.lock().destroy();
+
+ self.descriptor_updater.lock().destroy();
+
+ // Debug tracking alive objects
+ let (debug_device, hr_debug) = self.raw.cast::<d3d12sdklayers::ID3D12DebugDevice>();
+ if winerror::SUCCEEDED(hr_debug) {
+ debug_device.ReportLiveDeviceObjects(d3d12sdklayers::D3D12_RLDO_DETAIL);
+ debug_device.destroy();
+ }
+
+ self.raw.destroy();
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct Instance {
+ pub(crate) factory: native::Factory4,
+ library: Arc<native::D3D12Lib>,
+ lib_dxgi: native::DxgiLib,
+}
+
+impl Drop for Instance {
+ fn drop(&mut self) {
+ unsafe {
+ self.factory.destroy();
+ }
+ }
+}
+
+unsafe impl Send for Instance {}
+unsafe impl Sync for Instance {}
+
+impl hal::Instance<Backend> for Instance {
+ fn create(_: &str, _: u32) -> Result<Self, hal::UnsupportedBackend> {
+ let lib_main = match native::D3D12Lib::new() {
+ Ok(lib) => lib,
+ Err(_) => return Err(hal::UnsupportedBackend),
+ };
+
+ #[cfg(debug_assertions)]
+ {
+ // Enable debug layer
+ match lib_main.get_debug_interface() {
+ Ok((debug_controller, hr)) if winerror::SUCCEEDED(hr) => {
+ debug_controller.enable_layer();
+ unsafe {
+ debug_controller.Release();
+ }
+ }
+ _ => {
+ warn!("Unable to get D3D12 debug interface");
+ }
+ }
+ }
+
+ let lib_dxgi = native::DxgiLib::new().unwrap();
+
+ // The `DXGI_CREATE_FACTORY_DEBUG` flag is only allowed to be passed to
+ // `CreateDXGIFactory2` if the debug interface is actually available. So
+ // we check for whether it exists first.
+ let factory_flags = match lib_dxgi.get_debug_interface1() {
+ Ok((queue, hr)) if winerror::SUCCEEDED(hr) => {
+ unsafe { queue.destroy() };
+ native::FactoryCreationFlags::DEBUG
+ }
+ _ => native::FactoryCreationFlags::empty(),
+ };
+
+ // Create DXGI factory
+ let factory = match lib_dxgi.create_factory2(factory_flags) {
+ Ok((factory, hr)) if winerror::SUCCEEDED(hr) => factory,
+ Ok((_, hr)) => {
+ info!("Failed on dxgi factory creation: {:?}", hr);
+ return Err(hal::UnsupportedBackend);
+ }
+ Err(_) => return Err(hal::UnsupportedBackend),
+ };
+
+ Ok(Instance {
+ factory,
+ library: Arc::new(lib_main),
+ lib_dxgi,
+ })
+ }
+
+ fn enumerate_adapters(&self) -> Vec<adapter::Adapter<Backend>> {
+ use self::memory::Properties;
+
+ // Try to use high performance order by default (returns None on Windows < 1803)
+ let (use_f6, factory6) = unsafe {
+ let (f6, hr) = self.factory.cast::<dxgi1_6::IDXGIFactory6>();
+ if winerror::SUCCEEDED(hr) {
+ // It's okay to decrement the refcount here because we
+ // have another reference to the factory already owned by `self`.
+ f6.destroy();
+ (true, f6)
+ } else {
+ (false, native::WeakPtr::null())
+ }
+ };
+
+ // Enumerate adapters
+ let mut cur_index = 0;
+ let mut adapters = Vec::new();
+ loop {
+ let adapter = if use_f6 {
+ let mut adapter2 = native::WeakPtr::<dxgi1_2::IDXGIAdapter2>::null();
+ let hr = unsafe {
+ factory6.EnumAdapterByGpuPreference(
+ cur_index,
+ 2, // HIGH_PERFORMANCE
+ &dxgi1_2::IDXGIAdapter2::uuidof(),
+ adapter2.mut_void() as *mut *mut _,
+ )
+ };
+
+ if hr == winerror::DXGI_ERROR_NOT_FOUND {
+ break;
+ }
+ if !winerror::SUCCEEDED(hr) {
+ error!("Failed enumerating adapters: 0x{:x}", hr);
+ break;
+ }
+
+ adapter2
+ } else {
+ let mut adapter1 = native::WeakPtr::<dxgi::IDXGIAdapter1>::null();
+ let hr1 = unsafe {
+ self.factory
+ .EnumAdapters1(cur_index, adapter1.mut_void() as *mut *mut _)
+ };
+
+ if hr1 == winerror::DXGI_ERROR_NOT_FOUND {
+ break;
+ }
+
+ let (adapter2, hr2) = unsafe { adapter1.cast::<dxgi1_2::IDXGIAdapter2>() };
+ if !winerror::SUCCEEDED(hr2) {
+ error!("Failed casting to Adapter2: 0x{:x}", hr2);
+ break;
+ }
+
+ unsafe {
+ adapter1.destroy();
+ }
+ adapter2
+ };
+
+ cur_index += 1;
+
+ // Check for D3D12 support
+ // Create temporary device to get physical device information
+ let device = match self
+ .library
+ .create_device(adapter, native::FeatureLevel::L11_0)
+ {
+ Ok((device, hr)) if winerror::SUCCEEDED(hr) => device,
+ _ => continue,
+ };
+
+ // We have found a possible adapter
+ // acquire the device information
+ let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() };
+ unsafe {
+ adapter.GetDesc2(&mut desc);
+ }
+
+ let device_name = {
+ let len = desc.Description.iter().take_while(|&&c| c != 0).count();
+ let name = <OsString as OsStringExt>::from_wide(&desc.Description[..len]);
+ name.to_string_lossy().into_owned()
+ };
+
+ let mut features_architecture: d3d12::D3D12_FEATURE_DATA_ARCHITECTURE =
+ unsafe { mem::zeroed() };
+ assert_eq!(winerror::S_OK, unsafe {
+ device.CheckFeatureSupport(
+ d3d12::D3D12_FEATURE_ARCHITECTURE,
+ &mut features_architecture as *mut _ as *mut _,
+ mem::size_of::<d3d12::D3D12_FEATURE_DATA_ARCHITECTURE>() as _,
+ )
+ });
+
+ let mut workarounds = Workarounds::default();
+
+ let info = adapter::AdapterInfo {
+ name: device_name,
+ vendor: desc.VendorId as usize,
+ device: desc.DeviceId as usize,
+ device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 {
+ workarounds.avoid_cpu_descriptor_overwrites = true;
+ adapter::DeviceType::VirtualGpu
+ } else if features_architecture.CacheCoherentUMA == TRUE {
+ adapter::DeviceType::IntegratedGpu
+ } else {
+ adapter::DeviceType::DiscreteGpu
+ },
+ };
+
+ let mut features: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS = unsafe { mem::zeroed() };
+ assert_eq!(winerror::S_OK, unsafe {
+ device.CheckFeatureSupport(
+ d3d12::D3D12_FEATURE_D3D12_OPTIONS,
+ &mut features as *mut _ as *mut _,
+ mem::size_of::<d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS>() as _,
+ )
+ });
+
+ let depth_bounds_test_supported = {
+ let mut features2: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS2 =
+ unsafe { mem::zeroed() };
+ let hr = unsafe {
+ device.CheckFeatureSupport(
+ d3d12::D3D12_FEATURE_D3D12_OPTIONS2,
+ &mut features2 as *mut _ as *mut _,
+ mem::size_of::<d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS2>() as _,
+ )
+ };
+ if hr == winerror::S_OK {
+ features2.DepthBoundsTestSupported != 0
+ } else {
+ false
+ }
+ };
+
+ let heterogeneous_resource_heaps =
+ features.ResourceHeapTier != d3d12::D3D12_RESOURCE_HEAP_TIER_1;
+
+ let uma = features_architecture.UMA == TRUE;
+ let cc_uma = features_architecture.CacheCoherentUMA == TRUE;
+
+ let (memory_architecture, heap_properties) = match (uma, cc_uma) {
+ (true, true) => (MemoryArchitecture::CacheCoherentUMA, &HEAPS_CCUMA),
+ (true, false) => (MemoryArchitecture::UMA, &HEAPS_UMA),
+ (false, _) => (MemoryArchitecture::NUMA, &HEAPS_NUMA),
+ };
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/dn788678(v=vs.85).aspx
+ let base_memory_types: [adapter::MemoryType; NUM_HEAP_PROPERTIES] =
+ match memory_architecture {
+ MemoryArchitecture::NUMA => [
+ // DEFAULT
+ adapter::MemoryType {
+ properties: Properties::DEVICE_LOCAL,
+ heap_index: 0,
+ },
+ // UPLOAD
+ adapter::MemoryType {
+ properties: Properties::CPU_VISIBLE | Properties::COHERENT,
+ heap_index: 1,
+ },
+ // READBACK
+ adapter::MemoryType {
+ properties: Properties::CPU_VISIBLE
+ | Properties::COHERENT
+ | Properties::CPU_CACHED,
+ heap_index: 1,
+ },
+ ],
+ MemoryArchitecture::UMA => [
+ // DEFAULT
+ adapter::MemoryType {
+ properties: Properties::DEVICE_LOCAL,
+ heap_index: 0,
+ },
+ // UPLOAD
+ adapter::MemoryType {
+ properties: Properties::DEVICE_LOCAL
+ | Properties::CPU_VISIBLE
+ | Properties::COHERENT,
+ heap_index: 0,
+ },
+ // READBACK
+ adapter::MemoryType {
+ properties: Properties::DEVICE_LOCAL
+ | Properties::CPU_VISIBLE
+ | Properties::COHERENT
+ | Properties::CPU_CACHED,
+ heap_index: 0,
+ },
+ ],
+ MemoryArchitecture::CacheCoherentUMA => [
+ // DEFAULT
+ adapter::MemoryType {
+ properties: Properties::DEVICE_LOCAL,
+ heap_index: 0,
+ },
+ // UPLOAD
+ adapter::MemoryType {
+ properties: Properties::DEVICE_LOCAL
+ | Properties::CPU_VISIBLE
+ | Properties::COHERENT
+ | Properties::CPU_CACHED,
+ heap_index: 0,
+ },
+ // READBACK
+ adapter::MemoryType {
+ properties: Properties::DEVICE_LOCAL
+ | Properties::CPU_VISIBLE
+ | Properties::COHERENT
+ | Properties::CPU_CACHED,
+ heap_index: 0,
+ },
+ ],
+ };
+
+ let memory_types = if heterogeneous_resource_heaps {
+ base_memory_types.to_vec()
+ } else {
+ // We multiplicate the base memory types depending on the resource usage:
+ // 0.. 3: Reserved for futures use
+ // 4.. 6: Buffers
+ // 7.. 9: Images
+ // 10..12: Targets
+ //
+ // The supported memory types for a resource can be requested by asking for
+ // the memory requirements. Memory type indices are encoded as bitflags.
+ // `device::MEM_TYPE_MASK` (0b111) defines the bitmask for one base memory type group.
+ // The corresponding shift masks (`device::MEM_TYPE_BUFFER_SHIFT`,
+ // `device::MEM_TYPE_IMAGE_SHIFT`, `device::MEM_TYPE_TARGET_SHIFT`)
+ // denote the usage group.
+ let mut types = Vec::new();
+ for i in 0..MemoryGroup::NumGroups as _ {
+ types.extend(base_memory_types.iter().map(|mem_type| {
+ let mut ty = mem_type.clone();
+
+ // Images and Targets are not host visible as we can't create
+ // a corresponding buffer for mapping.
+ if i == MemoryGroup::ImageOnly as _ || i == MemoryGroup::TargetOnly as _ {
+ ty.properties.remove(Properties::CPU_VISIBLE);
+ // Coherent and cached can only be on memory types that are cpu visible
+ ty.properties.remove(Properties::COHERENT);
+ ty.properties.remove(Properties::CPU_CACHED);
+ }
+ ty
+ }));
+ }
+ types
+ };
+
+ let memory_heaps = {
+ // Get the IDXGIAdapter3 from the created device to query video memory information.
+ let adapter_id = unsafe { device.GetAdapterLuid() };
+ let adapter = {
+ let mut adapter = native::WeakPtr::<dxgi1_4::IDXGIAdapter3>::null();
+ unsafe {
+ assert_eq!(
+ winerror::S_OK,
+ self.factory.EnumAdapterByLuid(
+ adapter_id,
+ &dxgi1_4::IDXGIAdapter3::uuidof(),
+ adapter.mut_void(),
+ )
+ );
+ }
+ adapter
+ };
+
+ let query_memory = |segment: dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP| unsafe {
+ let mut mem_info: dxgi1_4::DXGI_QUERY_VIDEO_MEMORY_INFO = mem::zeroed();
+ assert_eq!(
+ winerror::S_OK,
+ adapter.QueryVideoMemoryInfo(0, segment, &mut mem_info)
+ );
+ mem_info.Budget
+ };
+
+ let mut heaps = vec![adapter::MemoryHeap {
+ size: query_memory(dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP_LOCAL),
+ flags: memory::HeapFlags::DEVICE_LOCAL,
+ }];
+ if let MemoryArchitecture::NUMA = memory_architecture {
+ heaps.push(adapter::MemoryHeap {
+ size: query_memory(dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL),
+ flags: memory::HeapFlags::empty(),
+ });
+ }
+ heaps
+ };
+ //TODO: find a way to get a tighter bound?
+ let sample_count_mask = 0x3F;
+
+ // Theoretically vram limited, but in practice 2^20 is the limit
+ let tier3_practical_descriptor_limit = 1 << 20;
+
+ let full_heap_count = match features.ResourceBindingTier {
+ d3d12::D3D12_RESOURCE_BINDING_TIER_1 => {
+ d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1
+ }
+ d3d12::D3D12_RESOURCE_BINDING_TIER_2 => {
+ d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_2
+ }
+ d3d12::D3D12_RESOURCE_BINDING_TIER_3 => tier3_practical_descriptor_limit,
+ _ => unreachable!(),
+ } as _;
+
+ let uav_limit = match features.ResourceBindingTier {
+ d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 8, // conservative, is 64 on feature level 11.1
+ d3d12::D3D12_RESOURCE_BINDING_TIER_2 => 64,
+ d3d12::D3D12_RESOURCE_BINDING_TIER_3 => tier3_practical_descriptor_limit,
+ _ => unreachable!(),
+ } as _;
+
+ let physical_device = PhysicalDevice {
+ library: Arc::clone(&self.library),
+ adapter,
+ features:
+ // TODO: add more features, based on
+ // https://msdn.microsoft.com/de-de/library/windows/desktop/mt186615(v=vs.85).aspx
+ Features::ROBUST_BUFFER_ACCESS |
+ Features::IMAGE_CUBE_ARRAY |
+ Features::GEOMETRY_SHADER |
+ Features::TESSELLATION_SHADER |
+ Features::NON_FILL_POLYGON_MODE |
+ if depth_bounds_test_supported { Features::DEPTH_BOUNDS } else { Features::empty() } |
+ //logic_op: false, // Optional on feature level 11_0
+ Features::MULTI_DRAW_INDIRECT |
+ Features::FORMAT_BC |
+ Features::INSTANCE_RATE |
+ Features::DEPTH_CLAMP |
+ Features::SAMPLER_MIP_LOD_BIAS |
+ Features::SAMPLER_BORDER_COLOR |
+ Features::MUTABLE_COMPARISON_SAMPLER |
+ Features::SAMPLER_ANISOTROPY |
+ Features::TEXTURE_DESCRIPTOR_ARRAY |
+ Features::SAMPLER_MIRROR_CLAMP_EDGE |
+ Features::NDC_Y_UP |
+ Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING |
+ Features::SHADER_STORAGE_IMAGE_ARRAY_DYNAMIC_INDEXING |
+ Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING |
+ Features::STORAGE_TEXTURE_DESCRIPTOR_INDEXING |
+ Features::UNSIZED_DESCRIPTOR_ARRAY |
+ Features::DRAW_INDIRECT_COUNT,
+ hints:
+ Hints::BASE_VERTEX_INSTANCE_DRAWING,
+ limits: Limits {
+ //TODO: verify all of these not linked to constants
+ max_bound_descriptor_sets: MAX_DESCRIPTOR_SETS as u16,
+ max_descriptor_set_uniform_buffers_dynamic: 8,
+ max_descriptor_set_storage_buffers_dynamic: 4,
+ max_descriptor_set_sampled_images: full_heap_count,
+ max_descriptor_set_storage_buffers: uav_limit,
+ max_descriptor_set_storage_images: uav_limit,
+ max_descriptor_set_uniform_buffers: full_heap_count,
+ max_descriptor_set_samplers: d3d12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE as _,
+ max_per_stage_descriptor_sampled_images: match features.ResourceBindingTier {
+ d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 128,
+ d3d12::D3D12_RESOURCE_BINDING_TIER_2
+ | d3d12::D3D12_RESOURCE_BINDING_TIER_3
+ | _ => full_heap_count,
+ } as _,
+ max_per_stage_descriptor_samplers: match features.ResourceBindingTier {
+ d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 16,
+ d3d12::D3D12_RESOURCE_BINDING_TIER_2
+ | d3d12::D3D12_RESOURCE_BINDING_TIER_3
+ | _ => d3d12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE
+ } as _,
+ max_per_stage_descriptor_storage_buffers: uav_limit,
+ max_per_stage_descriptor_storage_images: uav_limit,
+ max_per_stage_descriptor_uniform_buffers: match features.ResourceBindingTier {
+ d3d12::D3D12_RESOURCE_BINDING_TIER_1
+ | d3d12::D3D12_RESOURCE_BINDING_TIER_2 => d3d12::D3D12_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT,
+ d3d12::D3D12_RESOURCE_BINDING_TIER_3
+ | _ => full_heap_count as _,
+ } as _,
+ max_uniform_buffer_range: (d3d12::D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16) as _,
+ max_storage_buffer_range: !0,
+ // Is actually 256, but need space for the descriptors in there, so leave at 128 to discourage explosions
+ max_push_constants_size: 128,
+ max_image_1d_size: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION as _,
+ max_image_2d_size: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION as _,
+ max_image_3d_size: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION as _,
+ max_image_cube_size: d3d12::D3D12_REQ_TEXTURECUBE_DIMENSION as _,
+ max_image_array_layers: d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _,
+ max_texel_elements: 0,
+ max_patch_size: 0,
+ max_viewports: d3d12::D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as _,
+ max_viewport_dimensions: [d3d12::D3D12_VIEWPORT_BOUNDS_MAX as _; 2],
+ max_framebuffer_extent: hal::image::Extent { //TODO
+ width: 4096,
+ height: 4096,
+ depth: 1,
+ },
+ max_compute_work_group_count: [
+ d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION,
+ d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION,
+ d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION,
+ ],
+ max_compute_work_group_invocations: d3d12::D3D12_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP as _,
+ max_compute_work_group_size: [
+ d3d12::D3D12_CS_THREAD_GROUP_MAX_X,
+ d3d12::D3D12_CS_THREAD_GROUP_MAX_Y,
+ d3d12::D3D12_CS_THREAD_GROUP_MAX_Z,
+ ],
+ max_vertex_input_attributes: d3d12::D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT as _,
+ max_vertex_input_bindings: 31, //TODO
+ max_vertex_input_attribute_offset: 255, // TODO
+ max_vertex_input_binding_stride: d3d12::D3D12_REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES as _,
+ max_vertex_output_components: 16, // TODO
+ min_texel_buffer_offset_alignment: 1, // TODO
+ min_uniform_buffer_offset_alignment: d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT as _,
+ min_storage_buffer_offset_alignment: 4, // TODO
+ framebuffer_color_sample_counts: sample_count_mask,
+ framebuffer_depth_sample_counts: sample_count_mask,
+ framebuffer_stencil_sample_counts: sample_count_mask,
+ max_color_attachments: d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as _,
+ buffer_image_granularity: 1,
+ non_coherent_atom_size: 1, //TODO: confirm
+ max_sampler_anisotropy: 16.,
+ optimal_buffer_copy_offset_alignment: d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as _,
+ optimal_buffer_copy_pitch_alignment: d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT as _,
+ min_vertex_input_binding_stride_alignment: 1,
+ .. Limits::default() //TODO
+ },
+ format_properties: Arc::new(FormatProperties::new(device)),
+ private_caps: Capabilities {
+ heterogeneous_resource_heaps,
+ memory_architecture,
+ },
+ workarounds,
+ heap_properties,
+ memory_properties: adapter::MemoryProperties {
+ memory_types,
+ memory_heaps,
+ },
+ is_open: Arc::new(Mutex::new(false)),
+ };
+
+ let queue_families = QUEUE_FAMILIES.to_vec();
+
+ adapters.push(adapter::Adapter {
+ info,
+ physical_device,
+ queue_families,
+ });
+ }
+ adapters
+ }
+
+ unsafe fn create_surface(
+ &self,
+ has_handle: &impl raw_window_handle::HasRawWindowHandle,
+ ) -> Result<window::Surface, hal::window::InitError> {
+ match has_handle.raw_window_handle() {
+ raw_window_handle::RawWindowHandle::Windows(handle) => {
+ Ok(self.create_surface_from_hwnd(handle.hwnd))
+ }
+ _ => Err(hal::window::InitError::UnsupportedWindowHandle),
+ }
+ }
+
+ unsafe fn destroy_surface(&self, _surface: window::Surface) {
+ // TODO: Implement Surface cleanup
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+pub enum Backend {}
+impl hal::Backend for Backend {
+ type Instance = Instance;
+ type PhysicalDevice = PhysicalDevice;
+ type Device = Device;
+ type Surface = window::Surface;
+
+ type QueueFamily = QueueFamily;
+ type CommandQueue = CommandQueue;
+ type CommandBuffer = command::CommandBuffer;
+
+ type Memory = resource::Memory;
+ type CommandPool = pool::CommandPool;
+
+ type ShaderModule = resource::ShaderModule;
+ type RenderPass = resource::RenderPass;
+ type Framebuffer = resource::Framebuffer;
+
+ type Buffer = resource::Buffer;
+ type BufferView = resource::BufferView;
+ type Image = resource::Image;
+ type ImageView = resource::ImageView;
+ type Sampler = resource::Sampler;
+
+ type ComputePipeline = resource::ComputePipeline;
+ type GraphicsPipeline = resource::GraphicsPipeline;
+ type PipelineLayout = resource::PipelineLayout;
+ type PipelineCache = ();
+ type DescriptorSetLayout = resource::DescriptorSetLayout;
+ type DescriptorPool = resource::DescriptorPool;
+ type DescriptorSet = resource::DescriptorSet;
+
+ type Fence = resource::Fence;
+ type Semaphore = resource::Semaphore;
+ type Event = ();
+ type QueryPool = resource::QueryPool;
+}
+
+fn validate_line_width(width: f32) {
+ // Note from the Vulkan spec:
+ // > If the wide lines feature is not enabled, lineWidth must be 1.0
+ // Simply assert and no-op because DX12 never exposes `Features::LINE_WIDTH`
+ assert_eq!(width, 1.0);
+}
+
+#[derive(Clone, Copy, Debug, Default)]
+struct FormatInfo {
+ properties: f::Properties,
+ sample_count_mask: u8,
+}
+
+#[derive(Debug)]
+pub struct FormatProperties {
+ info: Box<[Mutex<Option<FormatInfo>>]>,
+ device: native::Device,
+}
+
+impl Drop for FormatProperties {
+ fn drop(&mut self) {
+ unsafe {
+ self.device.destroy();
+ }
+ }
+}
+
+impl FormatProperties {
+ fn new(device: native::Device) -> Self {
+ let mut buf = Vec::with_capacity(f::NUM_FORMATS);
+ buf.push(Mutex::new(Some(FormatInfo::default())));
+ for _ in 1..f::NUM_FORMATS {
+ buf.push(Mutex::new(None))
+ }
+ FormatProperties {
+ info: buf.into_boxed_slice(),
+ device,
+ }
+ }
+
+ fn resolve(&self, idx: usize) -> FormatInfo {
+ let mut guard = self.info[idx].lock();
+ if let Some(info) = *guard {
+ return info;
+ }
+ let format: f::Format = unsafe { mem::transmute(idx as u32) };
+ let is_compressed = format.surface_desc().is_compressed();
+ let dxgi_format = match conv::map_format(format) {
+ Some(format) => format,
+ None => {
+ let info = FormatInfo::default();
+ *guard = Some(info);
+ return info;
+ }
+ };
+
+ let properties = {
+ let mut props = f::Properties::default();
+ let mut data = d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT {
+ Format: dxgi_format,
+ Support1: unsafe { mem::zeroed() },
+ Support2: unsafe { mem::zeroed() },
+ };
+ assert_eq!(winerror::S_OK, unsafe {
+ self.device.CheckFeatureSupport(
+ d3d12::D3D12_FEATURE_FORMAT_SUPPORT,
+ &mut data as *mut _ as *mut _,
+ mem::size_of::<d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT>() as _,
+ )
+ });
+ let can_buffer = 0 != data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BUFFER;
+ let can_image = 0
+ != data.Support1
+ & (d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE1D
+ | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE2D
+ | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE3D
+ | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURECUBE);
+ let can_linear = can_image && !is_compressed;
+ if can_image {
+ props.optimal_tiling |= f::ImageFeature::SAMPLED | f::ImageFeature::BLIT_SRC;
+ }
+ if can_linear {
+ props.linear_tiling |= f::ImageFeature::SAMPLED | f::ImageFeature::BLIT_SRC;
+ }
+ if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER != 0 {
+ props.buffer_features |= f::BufferFeature::VERTEX;
+ }
+ if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE != 0 {
+ props.optimal_tiling |= f::ImageFeature::SAMPLED_LINEAR;
+ }
+ if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_RENDER_TARGET != 0 {
+ props.optimal_tiling |=
+ f::ImageFeature::COLOR_ATTACHMENT | f::ImageFeature::BLIT_DST;
+ if can_linear {
+ props.linear_tiling |=
+ f::ImageFeature::COLOR_ATTACHMENT | f::ImageFeature::BLIT_DST;
+ }
+ }
+ if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BLENDABLE != 0 {
+ props.optimal_tiling |= f::ImageFeature::COLOR_ATTACHMENT_BLEND;
+ }
+ if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL != 0 {
+ props.optimal_tiling |= f::ImageFeature::DEPTH_STENCIL_ATTACHMENT;
+ }
+ if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_LOAD != 0 {
+ //TODO: check d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD ?
+ if can_buffer {
+ props.buffer_features |= f::BufferFeature::UNIFORM_TEXEL;
+ }
+ }
+ if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_ADD != 0 {
+ //TODO: other atomic flags?
+ if can_buffer {
+ props.buffer_features |= f::BufferFeature::STORAGE_TEXEL_ATOMIC;
+ }
+ if can_image {
+ props.optimal_tiling |= f::ImageFeature::STORAGE_ATOMIC;
+ }
+ }
+ if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE != 0 {
+ if can_buffer {
+ props.buffer_features |= f::BufferFeature::STORAGE_TEXEL;
+ }
+ if can_image {
+ props.optimal_tiling |= f::ImageFeature::STORAGE;
+ }
+ }
+ //TODO: blits, linear tiling
+ props
+ };
+
+ let sample_count_mask = if is_compressed {
+ // just an optimization to avoid the queries
+ 1
+ } else {
+ let mut mask = 0;
+ for i in 0..6 {
+ let mut data = d3d12::D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS {
+ Format: dxgi_format,
+ SampleCount: 1 << i,
+ Flags: 0,
+ NumQualityLevels: 0,
+ };
+ assert_eq!(winerror::S_OK, unsafe {
+ self.device.CheckFeatureSupport(
+ d3d12::D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
+ &mut data as *mut _ as *mut _,
+ mem::size_of::<d3d12::D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS>() as _,
+ )
+ });
+ if data.NumQualityLevels != 0 {
+ mask |= 1 << i;
+ }
+ }
+ mask
+ };
+
+ let info = FormatInfo {
+ properties,
+ sample_count_mask,
+ };
+ *guard = Some(info);
+ info
+ }
+}