diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 6527f58830..52b29d4c21 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -244,7 +244,10 @@ impl crate::Adapter for super::Adapter { })?; Ok(crate::OpenDevice { - device: super::Device { raw: self.device }, + device: super::Device { + raw: self.device, + present_queue: queue, + }, queue: super::Queue { raw: queue }, }) } @@ -371,7 +374,11 @@ impl crate::Adapter for super::Adapter { | crate::TextureUses::COPY_SRC | crate::TextureUses::COPY_DST, present_modes, - composite_alpha_modes: vec![crate::CompositeAlphaMode::Opaque], + composite_alpha_modes: vec![ + crate::CompositeAlphaMode::Opaque, + crate::CompositeAlphaMode::PreMultiplied, + crate::CompositeAlphaMode::PostMultiplied, + ], }) } } diff --git a/wgpu-hal/src/dx12/conv.rs b/wgpu-hal/src/dx12/conv.rs index efe4b08797..f049f8c319 100644 --- a/wgpu-hal/src/dx12/conv.rs +++ b/wgpu-hal/src/dx12/conv.rs @@ -1,4 +1,4 @@ -use winapi::shared::dxgiformat; +use winapi::shared::{dxgi1_2, dxgiformat}; pub(super) fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { use wgt::TextureFormat as Tf; @@ -95,3 +95,22 @@ pub(super) fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI | Tf::Astc12x12RgbaUnormSrgb => unreachable!(), } } + +pub fn map_texture_format_nosrgb(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT { + // NOTE: DXGI doesn't allow sRGB format on the swapchain, but + // creating RTV of swapchain buffers with sRGB works + match format { + wgt::TextureFormat::Bgra8UnormSrgb => dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM, + wgt::TextureFormat::Rgba8UnormSrgb => dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM, + _ => map_texture_format(format), + } +} + +pub fn map_acomposite_alpha_mode(mode: crate::CompositeAlphaMode) -> dxgi1_2::DXGI_ALPHA_MODE { + use crate::CompositeAlphaMode as Cam; + match mode { + Cam::Opaque => dxgi1_2::DXGI_ALPHA_MODE_IGNORE, + Cam::PreMultiplied => dxgi1_2::DXGI_ALPHA_MODE_PREMULTIPLIED, + Cam::PostMultiplied => dxgi1_2::DXGI_ALPHA_MODE_STRAIGHT, + } +} diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 669d5e2392..8aef919369 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -12,9 +12,10 @@ mod command; mod conv; mod device; -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, ptr, sync::Arc}; use winapi::{ - shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, windef, winerror}, + shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, dxgitype, windef, winerror}, + um::{d3d12, synchapi, winbase, winnt}, Interface as _, }; @@ -101,7 +102,54 @@ impl Drop for Instance { unsafe impl Send for Instance {} unsafe impl Sync for Instance {} -struct SwapChain {} +#[derive(Copy, Clone)] +struct DualHandle { + cpu: native::CpuDescriptor, + gpu: native::GpuDescriptor, + /// How large the block allocated to this handle is. + size: u64, +} + +type DescriptorIndex = u64; + +struct DescriptorHeap { + raw: native::DescriptorHeap, + handle_size: u64, + total_handles: u64, + start: DualHandle, +} + +impl DescriptorHeap { + fn at(&self, index: DescriptorIndex, size: u64) -> DualHandle { + assert!(index < self.total_handles); + DualHandle { + cpu: self.cpu_descriptor_at(index), + gpu: self.gpu_descriptor_at(index), + size, + } + } + + fn cpu_descriptor_at(&self, index: u64) -> native::CpuDescriptor { + native::CpuDescriptor { + ptr: self.start.cpu.ptr + (self.handle_size * index) as usize, + } + } + + fn gpu_descriptor_at(&self, index: u64) -> native::GpuDescriptor { + native::GpuDescriptor { + ptr: self.start.gpu.ptr + self.handle_size * index, + } + } +} + +struct SwapChain { + raw: native::WeakPtr, + // need to associate raw image pointers with the swapchain so they can be properly released + // when the swapchain is destroyed + resources: Vec, + waitable: winnt::HANDLE, + acquired_count: usize, +} pub struct Surface { factory: native::WeakPtr, @@ -144,6 +192,7 @@ unsafe impl Sync for Adapter {} pub struct Device { raw: native::Device, + present_queue: native::CommandQueue, } unsafe impl Send for Device {} @@ -309,16 +358,148 @@ impl crate::Instance for Instance { } } +impl SwapChain { + unsafe fn release_resources(self) -> native::WeakPtr { + for resource in self.resources { + resource.destroy(); + } + self.raw + } + + unsafe fn wait(&mut self, timeout_ms: u32) -> Result { + match synchapi::WaitForSingleObject(self.waitable, timeout_ms) { + winbase::WAIT_ABANDONED | winbase::WAIT_FAILED => Err(crate::SurfaceError::Lost), + winbase::WAIT_OBJECT_0 => Ok(true), + winerror::WAIT_TIMEOUT => Ok(false), + other => { + log::error!("Unexpected wait status: 0x{:x}", other); + Err(crate::SurfaceError::Lost) + } + } + } +} + impl crate::Surface for Surface { unsafe fn configure( &mut self, device: &Device, config: &crate::SurfaceConfiguration, ) -> Result<(), crate::SurfaceError> { + let mut flags = dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + match config.present_mode { + wgt::PresentMode::Immediate => { + flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } + _ => {} + } + + let non_srgb_format = conv::map_texture_format_nosrgb(config.format); + + let swap_chain = match self.swap_chain.take() { + Some(sc) => { + // can't have image resources in flight used by GPU + //device.wait_idle().unwrap(); + + let raw = sc.release_resources(); + let result = raw.ResizeBuffers( + config.swap_chain_size, + config.extent.width, + config.extent.height, + non_srgb_format, + flags, + ); + if let Some(err) = result.to_error() { + log::error!("ResizeBuffers failed: {}", err); + return Err(crate::SurfaceError::Other("window is in use")); + } + raw + } + None => { + let mut swap_chain1 = native::WeakPtr::::null(); + + let raw_desc = dxgi1_2::DXGI_SWAP_CHAIN_DESC1 { + AlphaMode: conv::map_acomposite_alpha_mode(config.composite_alpha_mode), + BufferCount: config.swap_chain_size, + Width: config.extent.width, + Height: config.extent.height, + Format: non_srgb_format, + Flags: flags, + BufferUsage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Scaling: dxgi1_2::DXGI_SCALING_STRETCH, + Stereo: 0, + SwapEffect: dxgi::DXGI_SWAP_EFFECT_FLIP_DISCARD, + }; + + let hr = self.factory.CreateSwapChainForHwnd( + device.present_queue.as_mut_ptr() as *mut _, + self.wnd_handle, + &raw_desc, + ptr::null(), + ptr::null_mut(), + swap_chain1.mut_void() as *mut *mut _, + ); + + if let Some(err) = hr.to_error() { + log::error!("SwapChain creation error: {}", err); + return Err(crate::SurfaceError::Other("swap chain creation")); + } + + match swap_chain1.cast::().check() { + Ok(swap_chain3) => { + swap_chain1.destroy(); + swap_chain3 + } + Err(err) => { + log::error!("Unable to cast swap chain: {}", err); + return Err(crate::SurfaceError::Other("swap chain cast to 3")); + } + } + } + }; + + // Disable automatic Alt+Enter handling by DXGI. + const DXGI_MWA_NO_WINDOW_CHANGES: u32 = 1; + const DXGI_MWA_NO_ALT_ENTER: u32 = 2; + self.factory.MakeWindowAssociation( + self.wnd_handle, + DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER, + ); + + swap_chain.SetMaximumFrameLatency(config.swap_chain_size); + let waitable = swap_chain.GetFrameLatencyWaitableObject(); + + let mut resources = vec![native::Resource::null(); config.swap_chain_size as usize]; + for (i, res) in resources.iter_mut().enumerate() { + swap_chain.GetBuffer(i as _, &d3d12::ID3D12Resource::uuidof(), res.mut_void()); + } + + self.swap_chain = Some(SwapChain { + raw: swap_chain, + resources, + waitable, + acquired_count: 0, + //format: config.format, + //size: config.extent, + //mode: config.present_mode, + }); + Ok(()) } - unsafe fn unconfigure(&mut self, device: &Device) {} + unsafe fn unconfigure(&mut self, device: &Device) { + if let Some(mut sc) = self.swap_chain.take() { + let _ = sc.wait(winbase::INFINITE); + //TODO: this shouldn't be needed, + // but it complains that the queue is still used otherwise + //let _ = device.wait_idle(); + let raw = sc.release_resources(); + raw.destroy(); + } + } unsafe fn acquire_texture( &mut self,