hal/dx12: swap chain configuration

This commit is contained in:
Dzmitry Malyshau
2021-07-05 15:37:02 -04:00
committed by Dzmitry Malyshau
parent eb565a59f7
commit 0942fb1346
3 changed files with 214 additions and 7 deletions

View File

@@ -244,7 +244,10 @@ impl crate::Adapter<super::Api> 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<super::Api> 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,
],
})
}
}

View File

@@ -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,
}
}

View File

@@ -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<dxgi1_4::IDXGISwapChain3>,
// need to associate raw image pointers with the swapchain so they can be properly released
// when the swapchain is destroyed
resources: Vec<native::Resource>,
waitable: winnt::HANDLE,
acquired_count: usize,
}
pub struct Surface {
factory: native::WeakPtr<dxgi1_4::IDXGIFactory4>,
@@ -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<Api> for Instance {
}
}
impl SwapChain {
unsafe fn release_resources(self) -> native::WeakPtr<dxgi1_4::IDXGISwapChain3> {
for resource in self.resources {
resource.destroy();
}
self.raw
}
unsafe fn wait(&mut self, timeout_ms: u32) -> Result<bool, crate::SurfaceError> {
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<Api> 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::<dxgi1_2::IDXGISwapChain1>::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::<dxgi1_4::IDXGISwapChain3>().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,