1 extern crate gfx_hal as hal;
2 extern crate auxil;
3 extern crate range_alloc;
4 #[macro_use]
5 extern crate bitflags;
6 extern crate d3d12 as native;
7 #[macro_use]
8 extern crate log;
9 extern crate smallvec;
10 extern crate spirv_cross;
11 extern crate winapi;
12
13 mod command;
14 mod conv;
15 mod descriptors_cpu;
16 mod device;
17 mod internal;
18 mod pool;
19 mod resource;
20 mod root_constants;
21 mod window;
22
23 use hal::pso::PipelineStage;
24 use hal::{adapter, format as f, image, memory, queue as q, Features, Limits};
25
26 use winapi::shared::minwindef::TRUE;
27 use winapi::shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, winerror};
28 use winapi::um::{d3d12, d3d12sdklayers, handleapi, synchapi, winbase};
29 use winapi::Interface;
30
31 use std::borrow::Borrow;
32 use std::ffi::OsString;
33 use std::os::windows::ffi::OsStringExt;
34 use std::sync::{Arc, Mutex};
35 use std::{fmt, mem};
36
37 use descriptors_cpu::DescriptorCpuPool;
38
39 #[derive(Debug)]
40 pub(crate) struct HeapProperties {
41 pub page_property: d3d12::D3D12_CPU_PAGE_PROPERTY,
42 pub memory_pool: d3d12::D3D12_MEMORY_POOL,
43 }
44
45 // https://msdn.microsoft.com/de-de/library/windows/desktop/dn770377(v=vs.85).aspx
46 // Only 16 input slots allowed.
47 const MAX_VERTEX_BUFFERS: usize = 16;
48
49 const NUM_HEAP_PROPERTIES: usize = 3;
50
51 // Memory types are grouped according to the supported resources.
52 // Grouping is done to circumvent the limitations of heap tier 1 devices.
53 // Devices with Tier 1 will expose `BuffersOnl`, `ImageOnly` and `TargetOnly`.
54 // Devices with Tier 2 or higher will only expose `Universal`.
55 enum MemoryGroup {
56 Universal = 0,
57 BufferOnly,
58 ImageOnly,
59 TargetOnly,
60
61 NumGroups,
62 }
63
64 // https://msdn.microsoft.com/de-de/library/windows/desktop/dn788678(v=vs.85).aspx
65 static HEAPS_NUMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [
66 // DEFAULT
67 HeapProperties {
68 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
69 memory_pool: d3d12::D3D12_MEMORY_POOL_L1,
70 },
71 // UPLOAD
72 HeapProperties {
73 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE,
74 memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
75 },
76 // READBACK
77 HeapProperties {
78 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
79 memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
80 },
81 ];
82
83 static HEAPS_UMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [
84 // DEFAULT
85 HeapProperties {
86 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
87 memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
88 },
89 // UPLOAD
90 HeapProperties {
91 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE,
92 memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
93 },
94 // READBACK
95 HeapProperties {
96 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
97 memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
98 },
99 ];
100
101 static HEAPS_CCUMA: [HeapProperties; NUM_HEAP_PROPERTIES] = [
102 // DEFAULT
103 HeapProperties {
104 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
105 memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
106 },
107 // UPLOAD
108 HeapProperties {
109 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
110 memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
111 },
112 //READBACK
113 HeapProperties {
114 page_property: d3d12::D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
115 memory_pool: d3d12::D3D12_MEMORY_POOL_L0,
116 },
117 ];
118
119 #[derive(Debug, Copy, Clone)]
120 pub enum QueueFamily {
121 // Specially marked present queue.
122 // It's basically a normal 3D queue but D3D12 swapchain creation requires an
123 // associated queue, which we don't know on `create_swapchain`.
124 Present,
125 Normal(q::QueueType),
126 }
127
128 const MAX_QUEUES: usize = 16; // infinite, to be fair
129
130 impl q::QueueFamily for QueueFamily {
queue_type(&self) -> q::QueueType131 fn queue_type(&self) -> q::QueueType {
132 match *self {
133 QueueFamily::Present => q::QueueType::General,
134 QueueFamily::Normal(ty) => ty,
135 }
136 }
max_queues(&self) -> usize137 fn max_queues(&self) -> usize {
138 match *self {
139 QueueFamily::Present => 1,
140 QueueFamily::Normal(_) => MAX_QUEUES,
141 }
142 }
id(&self) -> q::QueueFamilyId143 fn id(&self) -> q::QueueFamilyId {
144 // This must match the order exposed by `QUEUE_FAMILIES`
145 q::QueueFamilyId(match *self {
146 QueueFamily::Present => 0,
147 QueueFamily::Normal(q::QueueType::General) => 1,
148 QueueFamily::Normal(q::QueueType::Compute) => 2,
149 QueueFamily::Normal(q::QueueType::Transfer) => 3,
150 _ => unreachable!(),
151 })
152 }
153 }
154
155 impl QueueFamily {
native_type(&self) -> native::CmdListType156 fn native_type(&self) -> native::CmdListType {
157 use hal::queue::QueueFamily as _;
158 use native::CmdListType as Clt;
159
160 let queue_type = self.queue_type();
161 match queue_type {
162 q::QueueType::General | q::QueueType::Graphics => Clt::Direct,
163 q::QueueType::Compute => Clt::Compute,
164 q::QueueType::Transfer => Clt::Copy,
165 }
166 }
167 }
168
169 static QUEUE_FAMILIES: [QueueFamily; 4] = [
170 QueueFamily::Present,
171 QueueFamily::Normal(q::QueueType::General),
172 QueueFamily::Normal(q::QueueType::Compute),
173 QueueFamily::Normal(q::QueueType::Transfer),
174 ];
175
176 pub struct PhysicalDevice {
177 library: Arc<native::D3D12Lib>,
178 adapter: native::WeakPtr<dxgi1_2::IDXGIAdapter2>,
179 features: Features,
180 limits: Limits,
181 format_properties: Arc<FormatProperties>,
182 private_caps: Capabilities,
183 heap_properties: &'static [HeapProperties; NUM_HEAP_PROPERTIES],
184 memory_properties: adapter::MemoryProperties,
185 // Indicates that there is currently an active logical device.
186 // Opening the same adapter multiple times will return the same D3D12Device again.
187 is_open: Arc<Mutex<bool>>,
188 }
189
190 impl fmt::Debug for PhysicalDevice {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result191 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
192 fmt.write_str("PhysicalDevice")
193 }
194 }
195
196 unsafe impl Send for PhysicalDevice {}
197 unsafe impl Sync for PhysicalDevice {}
198
199 impl adapter::PhysicalDevice<Backend> for PhysicalDevice {
open( &self, families: &[(&QueueFamily, &[q::QueuePriority])], requested_features: Features, ) -> Result<adapter::Gpu<Backend>, hal::device::CreationError>200 unsafe fn open(
201 &self,
202 families: &[(&QueueFamily, &[q::QueuePriority])],
203 requested_features: Features,
204 ) -> Result<adapter::Gpu<Backend>, hal::device::CreationError> {
205 let lock = self.is_open.try_lock();
206 let mut open_guard = match lock {
207 Ok(inner) => inner,
208 Err(_) => return Err(hal::device::CreationError::TooManyObjects),
209 };
210
211 if !self.features().contains(requested_features) {
212 return Err(hal::device::CreationError::MissingFeature);
213 }
214
215 let device_raw = match self.library.create_device(
216 self.adapter,
217 native::FeatureLevel::L11_0,
218 ) {
219 Ok((device, hr)) if winerror::SUCCEEDED(hr) => device,
220 Ok((_, hr)) => {
221 error!("error on device creation: {:x}", hr);
222 return Err(hal::device::CreationError::InitializationFailed);
223 }
224 Err(e) => panic!("device creation failed with {:?}", e),
225 };
226
227 // Always create the presentation queue in case we want to build a swapchain.
228 let (present_queue, hr_queue) = device_raw.create_command_queue(
229 QueueFamily::Present.native_type(),
230 native::Priority::Normal,
231 native::CommandQueueFlags::empty(),
232 0,
233 );
234 if !winerror::SUCCEEDED(hr_queue) {
235 error!("error on queue creation: {:x}", hr_queue);
236 }
237
238 let mut device = Device::new(device_raw, &self, present_queue);
239
240 let queue_groups = families
241 .into_iter()
242 .map(|&(&family, priorities)| {
243 use hal::queue::QueueFamily as _;
244 let mut group = q::QueueGroup::new(family.id());
245
246 let create_idle_event = || native::Event::create(true, false);
247
248 match family {
249 QueueFamily::Present => {
250 // Exactly **one** present queue!
251 // Number of queues need to be larger than 0 else it
252 // violates the specification.
253 let queue = CommandQueue {
254 raw: device.present_queue.clone(),
255 idle_fence: device.create_raw_fence(false),
256 idle_event: create_idle_event(),
257 };
258 device.append_queue(queue.clone());
259 group.add_queue(queue);
260 }
261 QueueFamily::Normal(_) => {
262 let list_type = family.native_type();
263 for _ in 0 .. priorities.len() {
264 let (queue, hr_queue) = device_raw.create_command_queue(
265 list_type,
266 native::Priority::Normal,
267 native::CommandQueueFlags::empty(),
268 0,
269 );
270
271 if winerror::SUCCEEDED(hr_queue) {
272 let queue = CommandQueue {
273 raw: queue,
274 idle_fence: device.create_raw_fence(false),
275 idle_event: create_idle_event(),
276 };
277 device.append_queue(queue.clone());
278 group.add_queue(queue);
279 } else {
280 error!("error on queue creation: {:x}", hr_queue);
281 }
282 }
283 }
284 }
285
286 group
287 })
288 .collect();
289
290 *open_guard = true;
291
292 Ok(adapter::Gpu {
293 device,
294 queue_groups,
295 })
296 }
297
format_properties(&self, fmt: Option<f::Format>) -> f::Properties298 fn format_properties(&self, fmt: Option<f::Format>) -> f::Properties {
299 let idx = fmt.map(|fmt| fmt as usize).unwrap_or(0);
300 self.format_properties.get(idx).properties
301 }
302
image_format_properties( &self, format: f::Format, dimensions: u8, tiling: image::Tiling, usage: image::Usage, view_caps: image::ViewCapabilities, ) -> Option<image::FormatProperties>303 fn image_format_properties(
304 &self,
305 format: f::Format,
306 dimensions: u8,
307 tiling: image::Tiling,
308 usage: image::Usage,
309 view_caps: image::ViewCapabilities,
310 ) -> Option<image::FormatProperties> {
311 conv::map_format(format)?; //filter out unknown formats
312 let format_info = self.format_properties.get(format as usize);
313
314 let supported_usage = {
315 use hal::image::Usage as U;
316 let props = match tiling {
317 image::Tiling::Optimal => format_info.properties.optimal_tiling,
318 image::Tiling::Linear => format_info.properties.linear_tiling,
319 };
320 let mut flags = U::empty();
321 // Note: these checks would have been nicer if we had explicit BLIT usage
322 if props.contains(f::ImageFeature::BLIT_SRC) {
323 flags |= U::TRANSFER_SRC;
324 }
325 if props.contains(f::ImageFeature::BLIT_DST) {
326 flags |= U::TRANSFER_DST;
327 }
328 if props.contains(f::ImageFeature::SAMPLED) {
329 flags |= U::SAMPLED;
330 }
331 if props.contains(f::ImageFeature::STORAGE) {
332 flags |= U::STORAGE;
333 }
334 if props.contains(f::ImageFeature::COLOR_ATTACHMENT) {
335 flags |= U::COLOR_ATTACHMENT;
336 }
337 if props.contains(f::ImageFeature::DEPTH_STENCIL_ATTACHMENT) {
338 flags |= U::DEPTH_STENCIL_ATTACHMENT;
339 }
340 flags
341 };
342 if !supported_usage.contains(usage) {
343 return None;
344 }
345
346 let max_resource_size =
347 (d3d12::D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM as usize) << 20;
348 Some(match tiling {
349 image::Tiling::Optimal => image::FormatProperties {
350 max_extent: match dimensions {
351 1 => image::Extent {
352 width: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION,
353 height: 1,
354 depth: 1,
355 },
356 2 => image::Extent {
357 width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION,
358 height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION,
359 depth: 1,
360 },
361 3 => image::Extent {
362 width: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION,
363 height: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION,
364 depth: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION,
365 },
366 _ => return None,
367 },
368 max_levels: d3d12::D3D12_REQ_MIP_LEVELS as _,
369 max_layers: match dimensions {
370 1 => d3d12::D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION as _,
371 2 => d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _,
372 _ => return None,
373 },
374 sample_count_mask: if dimensions == 2
375 && !view_caps.contains(image::ViewCapabilities::KIND_CUBE)
376 && !usage.contains(image::Usage::STORAGE)
377 {
378 format_info.sample_count_mask
379 } else {
380 0x1
381 },
382 max_resource_size,
383 },
384 image::Tiling::Linear => image::FormatProperties {
385 max_extent: match dimensions {
386 2 => image::Extent {
387 width: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION,
388 height: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION,
389 depth: 1,
390 },
391 _ => return None,
392 },
393 max_levels: 1,
394 max_layers: 1,
395 sample_count_mask: 0x1,
396 max_resource_size,
397 },
398 })
399 }
400
memory_properties(&self) -> adapter::MemoryProperties401 fn memory_properties(&self) -> adapter::MemoryProperties {
402 self.memory_properties.clone()
403 }
404
features(&self) -> Features405 fn features(&self) -> Features {
406 self.features
407 }
limits(&self) -> Limits408 fn limits(&self) -> Limits {
409 self.limits
410 }
411 }
412
413 #[derive(Clone)]
414 pub struct CommandQueue {
415 pub(crate) raw: native::CommandQueue,
416 idle_fence: native::Fence,
417 idle_event: native::Event,
418 }
419
420 impl fmt::Debug for CommandQueue {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result421 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
422 fmt.write_str("CommandQueue")
423 }
424 }
425
426 impl CommandQueue {
destroy(&self)427 unsafe fn destroy(&self) {
428 handleapi::CloseHandle(self.idle_event.0);
429 self.idle_fence.destroy();
430 self.raw.destroy();
431 }
432 }
433
434 unsafe impl Send for CommandQueue {}
435 unsafe impl Sync for CommandQueue {}
436
437 impl q::CommandQueue<Backend> for CommandQueue {
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>,438 unsafe fn submit<'a, T, Ic, S, Iw, Is>(
439 &mut self,
440 submission: q::Submission<Ic, Iw, Is>,
441 fence: Option<&resource::Fence>,
442 ) where
443 T: 'a + Borrow<command::CommandBuffer>,
444 Ic: IntoIterator<Item = &'a T>,
445 S: 'a + Borrow<resource::Semaphore>,
446 Iw: IntoIterator<Item = (&'a S, PipelineStage)>,
447 Is: IntoIterator<Item = &'a S>,
448 {
449 // Reset idle fence and event
450 // That's safe here due to exclusive access to the queue
451 self.idle_fence.signal(0);
452 synchapi::ResetEvent(self.idle_event.0);
453
454 // TODO: semaphores
455 let mut lists = submission
456 .command_buffers
457 .into_iter()
458 .map(|buf| buf.borrow().as_raw_list())
459 .collect::<Vec<_>>();
460 self.raw
461 .ExecuteCommandLists(lists.len() as _, lists.as_mut_ptr());
462
463 if let Some(fence) = fence {
464 assert_eq!(winerror::S_OK, self.raw.Signal(fence.raw.as_mut_ptr(), 1));
465 }
466 }
467
present<'a, W, Is, S, Iw>( &mut self, swapchains: Is, _wait_semaphores: Iw, ) -> Result<Option<hal::window::Suboptimal>, hal::window::PresentError> where W: 'a + Borrow<window::Swapchain>, Is: IntoIterator<Item = (&'a W, hal::window::SwapImageIndex)>, S: 'a + Borrow<resource::Semaphore>, Iw: IntoIterator<Item = &'a S>,468 unsafe fn present<'a, W, Is, S, Iw>(
469 &mut self,
470 swapchains: Is,
471 _wait_semaphores: Iw,
472 ) -> Result<Option<hal::window::Suboptimal>, hal::window::PresentError>
473 where
474 W: 'a + Borrow<window::Swapchain>,
475 Is: IntoIterator<Item = (&'a W, hal::window::SwapImageIndex)>,
476 S: 'a + Borrow<resource::Semaphore>,
477 Iw: IntoIterator<Item = &'a S>,
478 {
479 // TODO: semaphores
480 for (swapchain, _) in swapchains {
481 swapchain.borrow().inner.Present(1, 0);
482 }
483
484 Ok(None)
485 }
486
present_surface( &mut self, surface: &mut window::Surface, _image: resource::ImageView, _wait_semaphore: Option<&resource::Semaphore>, ) -> Result<Option<hal::window::Suboptimal>, hal::window::PresentError>487 unsafe fn present_surface(
488 &mut self,
489 surface: &mut window::Surface,
490 _image: resource::ImageView,
491 _wait_semaphore: Option<&resource::Semaphore>,
492 ) -> Result<Option<hal::window::Suboptimal>, hal::window::PresentError> {
493 surface.present();
494 Ok(None)
495 }
496
wait_idle(&self) -> Result<(), hal::device::OutOfMemory>497 fn wait_idle(&self) -> Result<(), hal::device::OutOfMemory> {
498 self.raw.signal(self.idle_fence, 1);
499 assert_eq!(
500 winerror::S_OK,
501 self.idle_fence.set_event_on_completion(self.idle_event, 1)
502 );
503
504 unsafe {
505 synchapi::WaitForSingleObject(self.idle_event.0, winbase::INFINITE);
506 }
507
508 Ok(())
509 }
510 }
511
512 #[derive(Debug, Clone, Copy)]
513 enum MemoryArchitecture {
514 NUMA,
515 UMA,
516 CacheCoherentUMA,
517 }
518
519 #[derive(Debug, Clone, Copy)]
520 pub struct Capabilities {
521 heterogeneous_resource_heaps: bool,
522 memory_architecture: MemoryArchitecture,
523 }
524
525 #[derive(Clone, Debug)]
526 struct CmdSignatures {
527 draw: native::CommandSignature,
528 draw_indexed: native::CommandSignature,
529 dispatch: native::CommandSignature,
530 }
531
532 impl CmdSignatures {
destroy(&self)533 unsafe fn destroy(&self) {
534 self.draw.destroy();
535 self.draw_indexed.destroy();
536 self.dispatch.destroy();
537 }
538 }
539
540 // Shared objects between command buffers, owned by the device.
541 #[derive(Debug)]
542 struct Shared {
543 pub signatures: CmdSignatures,
544 pub service_pipes: internal::ServicePipes,
545 }
546
547 impl Shared {
destroy(&self)548 unsafe fn destroy(&self) {
549 self.signatures.destroy();
550 self.service_pipes.destroy();
551 }
552 }
553
554 pub struct Device {
555 raw: native::Device,
556 library: Arc<native::D3D12Lib>,
557 private_caps: Capabilities,
558 format_properties: Arc<FormatProperties>,
559 heap_properties: &'static [HeapProperties],
560 // CPU only pools
561 rtv_pool: Mutex<DescriptorCpuPool>,
562 dsv_pool: Mutex<DescriptorCpuPool>,
563 srv_uav_pool: Mutex<DescriptorCpuPool>,
564 sampler_pool: Mutex<DescriptorCpuPool>,
565 descriptor_update_pools: Mutex<Vec<descriptors_cpu::HeapLinear>>,
566 // CPU/GPU descriptor heaps
567 heap_srv_cbv_uav: Mutex<resource::DescriptorHeap>,
568 heap_sampler: Mutex<resource::DescriptorHeap>,
569 events: Mutex<Vec<native::Event>>,
570 shared: Arc<Shared>,
571 // Present queue exposed by the `Present` queue family.
572 // Required for swapchain creation. Only a single queue supports presentation.
573 present_queue: native::CommandQueue,
574 // List of all queues created from this device, including present queue.
575 // Needed for `wait_idle`.
576 queues: Vec<CommandQueue>,
577 // Indicates that there is currently an active device.
578 open: Arc<Mutex<bool>>,
579 }
580
581 impl fmt::Debug for Device {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result582 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
583 fmt.write_str("Device")
584 }
585 }
586
587 unsafe impl Send for Device {} //blocked by ComPtr
588 unsafe impl Sync for Device {} //blocked by ComPtr
589
590 impl Device {
new( device: native::Device, physical_device: &PhysicalDevice, present_queue: native::CommandQueue, ) -> Self591 fn new(
592 device: native::Device,
593 physical_device: &PhysicalDevice,
594 present_queue: native::CommandQueue,
595 ) -> Self {
596 // Allocate descriptor heaps
597 let rtv_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Rtv);
598 let dsv_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Dsv);
599 let srv_uav_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::CbvSrvUav);
600 let sampler_pool = DescriptorCpuPool::new(device, native::DescriptorHeapType::Sampler);
601
602 let heap_srv_cbv_uav = Self::create_descriptor_heap_impl(
603 device,
604 native::DescriptorHeapType::CbvSrvUav,
605 true,
606 1_000_000, // maximum number of CBV/SRV/UAV descriptors in heap for Tier 1
607 );
608
609 let heap_sampler =
610 Self::create_descriptor_heap_impl(device, native::DescriptorHeapType::Sampler, true, 2_048);
611
612 let draw_signature = Self::create_command_signature(device, device::CommandSignature::Draw);
613 let draw_indexed_signature =
614 Self::create_command_signature(device, device::CommandSignature::DrawIndexed);
615 let dispatch_signature =
616 Self::create_command_signature(device, device::CommandSignature::Dispatch);
617
618 let signatures = CmdSignatures {
619 draw: draw_signature,
620 draw_indexed: draw_indexed_signature,
621 dispatch: dispatch_signature,
622 };
623 let service_pipes = internal::ServicePipes::new(
624 device,
625 Arc::clone(&physical_device.library),
626 );
627 let shared = Shared {
628 signatures,
629 service_pipes,
630 };
631
632 Device {
633 raw: device,
634 library: Arc::clone(&physical_device.library),
635 private_caps: physical_device.private_caps,
636 format_properties: physical_device.format_properties.clone(),
637 heap_properties: physical_device.heap_properties,
638 rtv_pool: Mutex::new(rtv_pool),
639 dsv_pool: Mutex::new(dsv_pool),
640 srv_uav_pool: Mutex::new(srv_uav_pool),
641 sampler_pool: Mutex::new(sampler_pool),
642 descriptor_update_pools: Mutex::new(Vec::new()),
643 heap_srv_cbv_uav: Mutex::new(heap_srv_cbv_uav),
644 heap_sampler: Mutex::new(heap_sampler),
645 events: Mutex::new(Vec::new()),
646 shared: Arc::new(shared),
647 present_queue,
648 queues: Vec::new(),
649 open: physical_device.is_open.clone(),
650 }
651 }
652
append_queue(&mut self, queue: CommandQueue)653 fn append_queue(&mut self, queue: CommandQueue) {
654 self.queues.push(queue);
655 }
656
657 /// Get the native d3d12 device.
658 ///
659 /// Required for FFI with libraries like RenderDoc.
as_raw(&self) -> *mut d3d12::ID3D12Device660 pub unsafe fn as_raw(&self) -> *mut d3d12::ID3D12Device {
661 self.raw.as_mut_ptr()
662 }
663 }
664
665 impl Drop for Device {
drop(&mut self)666 fn drop(&mut self) {
667 *self.open.lock().unwrap() = false;
668
669 unsafe {
670 for queue in &mut self.queues {
671 queue.destroy();
672 }
673
674 self.shared.destroy();
675 self.heap_srv_cbv_uav.lock().unwrap().destroy();
676 self.heap_sampler.lock().unwrap().destroy();
677 self.rtv_pool.lock().unwrap().destroy();
678 self.dsv_pool.lock().unwrap().destroy();
679 self.srv_uav_pool.lock().unwrap().destroy();
680 self.sampler_pool.lock().unwrap().destroy();
681
682 for pool in &*self.descriptor_update_pools.lock().unwrap() {
683 pool.destroy();
684 }
685
686 // Debug tracking alive objects
687 let (debug_device, hr_debug) = self.raw.cast::<d3d12sdklayers::ID3D12DebugDevice>();
688 if winerror::SUCCEEDED(hr_debug) {
689 debug_device.ReportLiveDeviceObjects(d3d12sdklayers::D3D12_RLDO_DETAIL);
690 debug_device.destroy();
691 }
692
693 self.raw.destroy();
694 }
695 }
696 }
697
698 #[derive(Debug)]
699 pub struct Instance {
700 pub(crate) factory: native::Factory4,
701 library: Arc<native::D3D12Lib>,
702 }
703
704 impl Drop for Instance {
drop(&mut self)705 fn drop(&mut self) {
706 unsafe {
707 self.factory.destroy();
708 }
709 }
710 }
711
712 unsafe impl Send for Instance {}
713 unsafe impl Sync for Instance {}
714
715 impl hal::Instance<Backend> for Instance {
create(_: &str, _: u32) -> Result<Self, hal::UnsupportedBackend>716 fn create(_: &str, _: u32) -> Result<Self, hal::UnsupportedBackend> {
717 let lib_main = match native::D3D12Lib::new() {
718 Ok(lib) => lib,
719 Err(_) => return Err(hal::UnsupportedBackend),
720 };
721
722 #[cfg(debug_assertions)]
723 {
724 // Enable debug layer
725 match lib_main.get_debug_interface() {
726 Ok((debug_controller, hr)) if winerror::SUCCEEDED(hr) => {
727 debug_controller.enable_layer();
728 unsafe { debug_controller.Release() };
729 }
730 _ => {
731 warn!("Unable to get D3D12 debug interface");
732 }
733 }
734 }
735
736 let lib_dxgi = native::DxgiLib::new().unwrap();
737
738 // The `DXGI_CREATE_FACTORY_DEBUG` flag is only allowed to be passed to
739 // `CreateDXGIFactory2` if the debug interface is actually available. So
740 // we check for whether it exists first.
741 let factory_flags = match lib_dxgi.get_debug_interface1() {
742 Ok((queue, hr)) if winerror::SUCCEEDED(hr) => {
743 unsafe { queue.destroy() };
744 native::FactoryCreationFlags::DEBUG
745 }
746 _ => native::FactoryCreationFlags::empty(),
747 };
748
749 // Create DXGI factory
750 let factory = match lib_dxgi.create_factory2(factory_flags) {
751 Ok((factory, hr)) if winerror::SUCCEEDED(hr) => factory,
752 Ok((_, hr)) => {
753 info!("Failed on dxgi factory creation: {:?}", hr);
754 return Err(hal::UnsupportedBackend)
755 }
756 Err(_) => return Err(hal::UnsupportedBackend),
757 };
758
759 Ok(Instance {
760 factory,
761 library: Arc::new(lib_main),
762 })
763 }
764
enumerate_adapters(&self) -> Vec<adapter::Adapter<Backend>>765 fn enumerate_adapters(&self) -> Vec<adapter::Adapter<Backend>> {
766 use self::memory::Properties;
767
768 // Try to use high performance order by default (returns None on Windows < 1803)
769 let (use_f6, factory6) = unsafe {
770 let (f6, hr) = self.factory.cast::<dxgi1_6::IDXGIFactory6>();
771 if winerror::SUCCEEDED(hr) {
772 // It's okay to decrement the refcount here because we
773 // have another reference to the factory already owned by `self`.
774 f6.destroy();
775 (true, f6)
776 } else {
777 (false, native::WeakPtr::null())
778 }
779 };
780
781 // Enumerate adapters
782 let mut cur_index = 0;
783 let mut adapters = Vec::new();
784 loop {
785 let adapter = if use_f6 {
786 let mut adapter2 = native::WeakPtr::<dxgi1_2::IDXGIAdapter2>::null();
787 let hr = unsafe {
788 factory6.EnumAdapterByGpuPreference(
789 cur_index,
790 2, // HIGH_PERFORMANCE
791 &dxgi1_2::IDXGIAdapter2::uuidof(),
792 adapter2.mut_void() as *mut *mut _,
793 )
794 };
795
796 if hr == winerror::DXGI_ERROR_NOT_FOUND {
797 break;
798 }
799
800 adapter2
801 } else {
802 let mut adapter1 = native::WeakPtr::<dxgi::IDXGIAdapter1>::null();
803 let hr1 = unsafe {
804 self.factory
805 .EnumAdapters1(cur_index, adapter1.mut_void() as *mut *mut _)
806 };
807
808 if hr1 == winerror::DXGI_ERROR_NOT_FOUND {
809 break;
810 }
811
812 let (adapter2, hr2) = unsafe { adapter1.cast::<dxgi1_2::IDXGIAdapter2>() };
813 if !winerror::SUCCEEDED(hr2) {
814 error!("Failed casting to Adapter2");
815 break;
816 }
817
818 unsafe {
819 adapter1.destroy();
820 }
821 adapter2
822 };
823
824 cur_index += 1;
825
826 // Check for D3D12 support
827 // Create temporary device to get physical device information
828 let device = match self.library.create_device(adapter, native::FeatureLevel::L11_0) {
829 Ok((device, hr)) if winerror::SUCCEEDED(hr) => device,
830 _ => continue,
831 };
832
833 // We have found a possible adapter
834 // acquire the device information
835 let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() };
836 unsafe {
837 adapter.GetDesc2(&mut desc);
838 }
839
840 let device_name = {
841 let len = desc.Description.iter().take_while(|&&c| c != 0).count();
842 let name = <OsString as OsStringExt>::from_wide(&desc.Description[.. len]);
843 name.to_string_lossy().into_owned()
844 };
845
846 let info = adapter::AdapterInfo {
847 name: device_name,
848 vendor: desc.VendorId as usize,
849 device: desc.DeviceId as usize,
850 device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 {
851 adapter::DeviceType::VirtualGpu
852 } else {
853 adapter::DeviceType::DiscreteGpu
854 },
855 };
856
857 let mut features: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS = unsafe { mem::zeroed() };
858 assert_eq!(winerror::S_OK, unsafe {
859 device.CheckFeatureSupport(
860 d3d12::D3D12_FEATURE_D3D12_OPTIONS,
861 &mut features as *mut _ as *mut _,
862 mem::size_of::<d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS>() as _,
863 )
864 });
865
866 let mut features_architecture: d3d12::D3D12_FEATURE_DATA_ARCHITECTURE =
867 unsafe { mem::zeroed() };
868 assert_eq!(winerror::S_OK, unsafe {
869 device.CheckFeatureSupport(
870 d3d12::D3D12_FEATURE_ARCHITECTURE,
871 &mut features_architecture as *mut _ as *mut _,
872 mem::size_of::<d3d12::D3D12_FEATURE_DATA_ARCHITECTURE>() as _,
873 )
874 });
875
876 let depth_bounds_test_supported = {
877 let mut features2: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS2 =
878 unsafe { mem::zeroed() };
879 let hr = unsafe {
880 device.CheckFeatureSupport(
881 d3d12::D3D12_FEATURE_D3D12_OPTIONS2,
882 &mut features2 as *mut _ as *mut _,
883 mem::size_of::<d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS2>() as _,
884 )
885 };
886 if hr == winerror::S_OK {
887 features2.DepthBoundsTestSupported != 0
888 } else {
889 false
890 }
891 };
892
893 let heterogeneous_resource_heaps =
894 features.ResourceHeapTier != d3d12::D3D12_RESOURCE_HEAP_TIER_1;
895
896 let uma = features_architecture.UMA == TRUE;
897 let cc_uma = features_architecture.CacheCoherentUMA == TRUE;
898
899 let (memory_architecture, heap_properties) = match (uma, cc_uma) {
900 (true, true) => (MemoryArchitecture::CacheCoherentUMA, &HEAPS_CCUMA),
901 (true, false) => (MemoryArchitecture::UMA, &HEAPS_UMA),
902 (false, _) => (MemoryArchitecture::NUMA, &HEAPS_NUMA),
903 };
904
905 // https://msdn.microsoft.com/en-us/library/windows/desktop/dn788678(v=vs.85).aspx
906 let base_memory_types: [adapter::MemoryType; NUM_HEAP_PROPERTIES] =
907 match memory_architecture {
908 MemoryArchitecture::NUMA => [
909 // DEFAULT
910 adapter::MemoryType {
911 properties: Properties::DEVICE_LOCAL,
912 heap_index: 0,
913 },
914 // UPLOAD
915 adapter::MemoryType {
916 properties: Properties::CPU_VISIBLE | Properties::COHERENT,
917 heap_index: 1,
918 },
919 // READBACK
920 adapter::MemoryType {
921 properties: Properties::CPU_VISIBLE
922 | Properties::COHERENT
923 | Properties::CPU_CACHED,
924 heap_index: 1,
925 },
926 ],
927 MemoryArchitecture::UMA => [
928 // DEFAULT
929 adapter::MemoryType {
930 properties: Properties::DEVICE_LOCAL,
931 heap_index: 0,
932 },
933 // UPLOAD
934 adapter::MemoryType {
935 properties: Properties::DEVICE_LOCAL
936 | Properties::CPU_VISIBLE
937 | Properties::COHERENT,
938 heap_index: 0,
939 },
940 // READBACK
941 adapter::MemoryType {
942 properties: Properties::DEVICE_LOCAL
943 | Properties::CPU_VISIBLE
944 | Properties::COHERENT
945 | Properties::CPU_CACHED,
946 heap_index: 0,
947 },
948 ],
949 MemoryArchitecture::CacheCoherentUMA => [
950 // DEFAULT
951 adapter::MemoryType {
952 properties: Properties::DEVICE_LOCAL,
953 heap_index: 0,
954 },
955 // UPLOAD
956 adapter::MemoryType {
957 properties: Properties::DEVICE_LOCAL
958 | Properties::CPU_VISIBLE
959 | Properties::COHERENT
960 | Properties::CPU_CACHED,
961 heap_index: 0,
962 },
963 // READBACK
964 adapter::MemoryType {
965 properties: Properties::DEVICE_LOCAL
966 | Properties::CPU_VISIBLE
967 | Properties::COHERENT
968 | Properties::CPU_CACHED,
969 heap_index: 0,
970 },
971 ],
972 };
973
974 let memory_types = if heterogeneous_resource_heaps {
975 base_memory_types.to_vec()
976 } else {
977 // We multiplicate the base memory types depending on the resource usage:
978 // 0.. 3: Reserved for futures use
979 // 4.. 6: Buffers
980 // 7.. 9: Images
981 // 10..12: Targets
982 //
983 // The supported memory types for a resource can be requested by asking for
984 // the memory requirements. Memory type indices are encoded as bitflags.
985 // `device::MEM_TYPE_MASK` (0b111) defines the bitmask for one base memory type group.
986 // The corresponding shift masks (`device::MEM_TYPE_BUFFER_SHIFT`,
987 // `device::MEM_TYPE_IMAGE_SHIFT`, `device::MEM_TYPE_TARGET_SHIFT`)
988 // denote the usage group.
989 let mut types = Vec::new();
990 for i in 0 .. MemoryGroup::NumGroups as _ {
991 types.extend(base_memory_types.iter().map(|mem_type| {
992 let mut ty = mem_type.clone();
993
994 // Images and Targets are not host visible as we can't create
995 // a corresponding buffer for mapping.
996 if i == MemoryGroup::ImageOnly as _ || i == MemoryGroup::TargetOnly as _ {
997 ty.properties.remove(Properties::CPU_VISIBLE);
998 // Coherent and cached can only be on memory types that are cpu visible
999 ty.properties.remove(Properties::COHERENT);
1000 ty.properties.remove(Properties::CPU_CACHED);
1001 }
1002 ty
1003 }));
1004 }
1005 types
1006 };
1007
1008 let memory_heaps = {
1009 // Get the IDXGIAdapter3 from the created device to query video memory information.
1010 let adapter_id = unsafe { device.GetAdapterLuid() };
1011 let adapter = {
1012 let mut adapter = native::WeakPtr::<dxgi1_4::IDXGIAdapter3>::null();
1013 unsafe {
1014 assert_eq!(
1015 winerror::S_OK,
1016 self.factory.EnumAdapterByLuid(
1017 adapter_id,
1018 &dxgi1_4::IDXGIAdapter3::uuidof(),
1019 adapter.mut_void(),
1020 )
1021 );
1022 }
1023 adapter
1024 };
1025
1026 let query_memory = |segment: dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP| unsafe {
1027 let mut mem_info: dxgi1_4::DXGI_QUERY_VIDEO_MEMORY_INFO = mem::uninitialized();
1028 assert_eq!(
1029 winerror::S_OK,
1030 adapter.QueryVideoMemoryInfo(0, segment, &mut mem_info,)
1031 );
1032 mem_info.Budget
1033 };
1034
1035 let local = query_memory(dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP_LOCAL);
1036 match memory_architecture {
1037 MemoryArchitecture::NUMA => {
1038 let non_local = query_memory(dxgi1_4::DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL);
1039 vec![local, non_local]
1040 }
1041 _ => vec![local],
1042 }
1043 };
1044 //TODO: find a way to get a tighter bound?
1045 let sample_count_mask = 0x3F;
1046
1047 let physical_device = PhysicalDevice {
1048 library: Arc::clone(&self.library),
1049 adapter,
1050 features:
1051 // TODO: add more features, based on
1052 // https://msdn.microsoft.com/de-de/library/windows/desktop/mt186615(v=vs.85).aspx
1053 Features::ROBUST_BUFFER_ACCESS |
1054 Features::IMAGE_CUBE_ARRAY |
1055 Features::GEOMETRY_SHADER |
1056 Features::TESSELLATION_SHADER |
1057 Features::NON_FILL_POLYGON_MODE |
1058 if depth_bounds_test_supported { Features::DEPTH_BOUNDS } else { Features::empty() } |
1059 //logic_op: false, // Optional on feature level 11_0
1060 Features::MULTI_DRAW_INDIRECT |
1061 Features::FORMAT_BC |
1062 Features::INSTANCE_RATE |
1063 Features::SAMPLER_MIP_LOD_BIAS |
1064 Features::SAMPLER_ANISOTROPY,
1065 limits: Limits { // TODO
1066 max_image_1d_size: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION as _,
1067 max_image_2d_size: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION as _,
1068 max_image_3d_size: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION as _,
1069 max_image_cube_size: d3d12::D3D12_REQ_TEXTURECUBE_DIMENSION as _,
1070 max_image_array_layers: d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as _,
1071 max_texel_elements: 0,
1072 max_patch_size: 0,
1073 max_viewports: d3d12::D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as _,
1074 max_viewport_dimensions: [d3d12::D3D12_VIEWPORT_BOUNDS_MAX as _; 2],
1075 max_framebuffer_extent: hal::image::Extent { //TODO
1076 width: 4096,
1077 height: 4096,
1078 depth: 1,
1079 },
1080 max_compute_work_group_count: [
1081 d3d12::D3D12_CS_THREAD_GROUP_MAX_X,
1082 d3d12::D3D12_CS_THREAD_GROUP_MAX_Y,
1083 d3d12::D3D12_CS_THREAD_GROUP_MAX_Z,
1084 ],
1085 max_compute_work_group_size: [
1086 d3d12::D3D12_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP,
1087 1, //TODO
1088 1, //TODO
1089 ],
1090 max_vertex_input_attributes: d3d12::D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT as _,
1091 max_vertex_input_bindings: 31, //TODO
1092 max_vertex_input_attribute_offset: 255, // TODO
1093 max_vertex_input_binding_stride: d3d12::D3D12_REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES as _,
1094 max_vertex_output_components: 16, // TODO
1095 min_texel_buffer_offset_alignment: 1, // TODO
1096 min_uniform_buffer_offset_alignment: 256, // Required alignment for CBVs
1097 min_storage_buffer_offset_alignment: 1, // TODO
1098 framebuffer_color_sample_counts: sample_count_mask,
1099 framebuffer_depth_sample_counts: sample_count_mask,
1100 framebuffer_stencil_sample_counts: sample_count_mask,
1101 max_color_attachments: d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as _,
1102 buffer_image_granularity: 1,
1103 non_coherent_atom_size: 1, //TODO: confirm
1104 max_sampler_anisotropy: 16.,
1105 optimal_buffer_copy_offset_alignment: d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as _,
1106 optimal_buffer_copy_pitch_alignment: d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT as _,
1107 min_vertex_input_binding_stride_alignment: 1,
1108 .. Limits::default() //TODO
1109 },
1110 format_properties: Arc::new(FormatProperties::new(device)),
1111 private_caps: Capabilities {
1112 heterogeneous_resource_heaps,
1113 memory_architecture,
1114 },
1115 heap_properties,
1116 memory_properties: adapter::MemoryProperties {
1117 memory_types,
1118 memory_heaps,
1119 },
1120 is_open: Arc::new(Mutex::new(false)),
1121 };
1122
1123 let queue_families = QUEUE_FAMILIES.to_vec();
1124
1125 adapters.push(adapter::Adapter {
1126 info,
1127 physical_device,
1128 queue_families,
1129 });
1130 }
1131 adapters
1132 }
1133
create_surface( &self, has_handle: &impl raw_window_handle::HasRawWindowHandle, ) -> Result<window::Surface, hal::window::InitError>1134 unsafe fn create_surface(
1135 &self,
1136 has_handle: &impl raw_window_handle::HasRawWindowHandle,
1137 ) -> Result<window::Surface, hal::window::InitError> {
1138 match has_handle.raw_window_handle() {
1139 raw_window_handle::RawWindowHandle::Windows(handle) => {
1140 Ok(self.create_surface_from_hwnd(handle.hwnd))
1141 }
1142 _ => Err(hal::window::InitError::UnsupportedWindowHandle),
1143 }
1144 }
1145
destroy_surface(&self, _surface: window::Surface)1146 unsafe fn destroy_surface(&self, _surface: window::Surface) {
1147 // TODO: Implement Surface cleanup
1148 }
1149 }
1150
1151 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
1152 pub enum Backend {}
1153 impl hal::Backend for Backend {
1154 type Instance = Instance;
1155 type PhysicalDevice = PhysicalDevice;
1156 type Device = Device;
1157
1158 type Surface = window::Surface;
1159 type Swapchain = window::Swapchain;
1160
1161 type QueueFamily = QueueFamily;
1162 type CommandQueue = CommandQueue;
1163 type CommandBuffer = command::CommandBuffer;
1164
1165 type Memory = resource::Memory;
1166 type CommandPool = pool::CommandPool;
1167
1168 type ShaderModule = resource::ShaderModule;
1169 type RenderPass = resource::RenderPass;
1170 type Framebuffer = resource::Framebuffer;
1171
1172 type Buffer = resource::Buffer;
1173 type BufferView = resource::BufferView;
1174 type Image = resource::Image;
1175 type ImageView = resource::ImageView;
1176 type Sampler = resource::Sampler;
1177
1178 type ComputePipeline = resource::ComputePipeline;
1179 type GraphicsPipeline = resource::GraphicsPipeline;
1180 type PipelineLayout = resource::PipelineLayout;
1181 type PipelineCache = ();
1182 type DescriptorSetLayout = resource::DescriptorSetLayout;
1183 type DescriptorPool = resource::DescriptorPool;
1184 type DescriptorSet = resource::DescriptorSet;
1185
1186 type Fence = resource::Fence;
1187 type Semaphore = resource::Semaphore;
1188 type Event = ();
1189 type QueryPool = resource::QueryPool;
1190 }
1191
validate_line_width(width: f32)1192 fn validate_line_width(width: f32) {
1193 // Note from the Vulkan spec:
1194 // > If the wide lines feature is not enabled, lineWidth must be 1.0
1195 // Simply assert and no-op because DX12 never exposes `Features::LINE_WIDTH`
1196 assert_eq!(width, 1.0);
1197 }
1198
1199 #[derive(Clone, Copy, Debug, Default)]
1200 struct FormatInfo {
1201 properties: f::Properties,
1202 sample_count_mask: u8,
1203 }
1204
1205 #[derive(Debug)]
1206 pub struct FormatProperties(Box<[Mutex<Option<FormatInfo>>]>, native::Device);
1207
1208 impl Drop for FormatProperties {
drop(&mut self)1209 fn drop(&mut self) {
1210 unsafe {
1211 self.1.destroy();
1212 }
1213 }
1214 }
1215
1216 impl FormatProperties {
new(device: native::Device) -> Self1217 fn new(device: native::Device) -> Self {
1218 let mut buf = Vec::with_capacity(f::NUM_FORMATS);
1219 buf.push(Mutex::new(Some(FormatInfo::default())));
1220 for _ in 1 .. f::NUM_FORMATS {
1221 buf.push(Mutex::new(None))
1222 }
1223 FormatProperties(buf.into_boxed_slice(), device)
1224 }
1225
get(&self, idx: usize) -> FormatInfo1226 fn get(&self, idx: usize) -> FormatInfo {
1227 let mut guard = self.0[idx].lock().unwrap();
1228 if let Some(info) = *guard {
1229 return info;
1230 }
1231 let format: f::Format = unsafe { mem::transmute(idx as u32) };
1232 let dxgi_format = match conv::map_format(format) {
1233 Some(format) => format,
1234 None => {
1235 let info = FormatInfo::default();
1236 *guard = Some(info);
1237 return info;
1238 }
1239 };
1240
1241 let properties = {
1242 let mut props = f::Properties::default();
1243 let mut data = d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT {
1244 Format: dxgi_format,
1245 Support1: unsafe { mem::zeroed() },
1246 Support2: unsafe { mem::zeroed() },
1247 };
1248 assert_eq!(winerror::S_OK, unsafe {
1249 self.1.CheckFeatureSupport(
1250 d3d12::D3D12_FEATURE_FORMAT_SUPPORT,
1251 &mut data as *mut _ as *mut _,
1252 mem::size_of::<d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT>() as _,
1253 )
1254 });
1255 let can_buffer = 0 != data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BUFFER;
1256 let can_image = 0
1257 != data.Support1
1258 & (d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE1D
1259 | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE2D
1260 | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE3D
1261 | d3d12::D3D12_FORMAT_SUPPORT1_TEXTURECUBE);
1262 let can_linear = can_image && !format.surface_desc().is_compressed();
1263 if can_image {
1264 props.optimal_tiling |= f::ImageFeature::SAMPLED | f::ImageFeature::BLIT_SRC;
1265 }
1266 if can_linear {
1267 props.linear_tiling |= f::ImageFeature::SAMPLED | f::ImageFeature::BLIT_SRC;
1268 }
1269 if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER != 0 {
1270 props.buffer_features |= f::BufferFeature::VERTEX;
1271 }
1272 if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE != 0 {
1273 props.optimal_tiling |= f::ImageFeature::SAMPLED_LINEAR;
1274 }
1275 if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_RENDER_TARGET != 0 {
1276 props.optimal_tiling |=
1277 f::ImageFeature::COLOR_ATTACHMENT | f::ImageFeature::BLIT_DST;
1278 if can_linear {
1279 props.linear_tiling |=
1280 f::ImageFeature::COLOR_ATTACHMENT | f::ImageFeature::BLIT_DST;
1281 }
1282 }
1283 if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BLENDABLE != 0 {
1284 props.optimal_tiling |= f::ImageFeature::COLOR_ATTACHMENT_BLEND;
1285 }
1286 if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL != 0 {
1287 props.optimal_tiling |= f::ImageFeature::DEPTH_STENCIL_ATTACHMENT;
1288 }
1289 if data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_LOAD != 0 {
1290 //TODO: check d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD ?
1291 if can_buffer {
1292 props.buffer_features |= f::BufferFeature::UNIFORM_TEXEL;
1293 }
1294 }
1295 if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_ATOMIC_ADD != 0 {
1296 //TODO: other atomic flags?
1297 if can_buffer {
1298 props.buffer_features |= f::BufferFeature::STORAGE_TEXEL_ATOMIC;
1299 }
1300 if can_image {
1301 props.optimal_tiling |= f::ImageFeature::STORAGE_ATOMIC;
1302 }
1303 }
1304 if data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE != 0 {
1305 if can_buffer {
1306 props.buffer_features |= f::BufferFeature::STORAGE_TEXEL;
1307 }
1308 if can_image {
1309 props.optimal_tiling |= f::ImageFeature::STORAGE;
1310 }
1311 }
1312 //TODO: blits, linear tiling
1313 props
1314 };
1315
1316 let mut sample_count_mask = 0;
1317 for i in 0 .. 6 {
1318 let mut data = d3d12::D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS {
1319 Format: dxgi_format,
1320 SampleCount: 1 << i,
1321 Flags: 0,
1322 NumQualityLevels: 0,
1323 };
1324 assert_eq!(winerror::S_OK, unsafe {
1325 self.1.CheckFeatureSupport(
1326 d3d12::D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
1327 &mut data as *mut _ as *mut _,
1328 mem::size_of::<d3d12::D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS>() as _,
1329 )
1330 });
1331 if data.NumQualityLevels != 0 {
1332 sample_count_mask |= 1 << i;
1333 }
1334 }
1335
1336 let info = FormatInfo {
1337 properties,
1338 sample_count_mask,
1339 };
1340 *guard = Some(info);
1341 info
1342 }
1343 }
1344