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