use std::sync::atomic::{AtomicU64, Ordering};
use windows::Win32::{
Foundation::{CloseHandle, GENERIC_ALL, HANDLE},
Graphics::Direct3D12::{
D3D12_FENCE_FLAG_SHARED, ID3D12CommandQueue, ID3D12Device, ID3D12Fence,
},
};
use crate::{
HostWgpuContext, ImportedTexture, InteropBackend, InteropError, InteropSynchronizer,
NativeFrame, SyncMechanism,
};
pub struct Dx12FenceSynchronizer {
fence: ID3D12Fence,
queue: ID3D12CommandQueue,
shared_handle: HANDLE,
next_value: AtomicU64,
}
unsafe impl Send for Dx12FenceSynchronizer {}
unsafe impl Sync for Dx12FenceSynchronizer {}
impl Dx12FenceSynchronizer {
pub fn new(host: &HostWgpuContext) -> Result<Self, InteropError> {
if host.backend != InteropBackend::Dx12 {
return Err(InteropError::BackendMismatch {
expected: "Dx12",
actual: "non-Dx12",
});
}
let (fence, queue, shared_handle) = unsafe {
let hal_device = host.device.as_hal::<wgpu::wgc::api::Dx12>().ok_or(
InteropError::BackendMismatch {
expected: "Dx12",
actual: "non-Dx12",
},
)?;
let d3d_device: ID3D12Device = hal_device.raw_device().clone();
drop(hal_device);
let hal_queue = host.queue.as_hal::<wgpu::wgc::api::Dx12>().ok_or(
InteropError::BackendMismatch {
expected: "Dx12",
actual: "non-Dx12",
},
)?;
let queue: ID3D12CommandQueue = hal_queue.as_raw().clone();
drop(hal_queue);
let fence: ID3D12Fence = d3d_device
.CreateFence::<ID3D12Fence>(0, D3D12_FENCE_FLAG_SHARED)
.map_err(|err| InteropError::Dx12(format!("CreateFence: {}", err)))?;
let shared_handle = d3d_device
.CreateSharedHandle(&fence, None, GENERIC_ALL.0, None)
.map_err(|err| InteropError::Dx12(format!("CreateSharedHandle: {}", err)))?;
(fence, queue, shared_handle)
};
Ok(Self {
fence,
queue,
shared_handle,
next_value: AtomicU64::new(0),
})
}
pub fn shared_handle(&self) -> HANDLE {
self.shared_handle
}
pub fn advance(&self) -> u64 {
self.next_value.fetch_add(1, Ordering::SeqCst) + 1
}
pub fn current_value(&self) -> u64 {
self.next_value.load(Ordering::SeqCst)
}
}
impl InteropSynchronizer for Dx12FenceSynchronizer {
fn producer_complete(
&self,
frame: &NativeFrame,
mechanism: SyncMechanism,
) -> Result<(), InteropError> {
match mechanism {
SyncMechanism::ExplicitFence => {
let value = match frame {
NativeFrame::Dx12SharedTexture(dx_frame) if dx_frame.fence_value > 0 => {
dx_frame.fence_value
}
_ => self.next_value.load(Ordering::SeqCst),
};
if value == 0 {
return Ok(());
}
unsafe {
self.queue
.Wait(&self.fence, value)
.map_err(|err| InteropError::Dx12(format!("Wait: {}", err)))?;
}
Ok(())
}
SyncMechanism::None | SyncMechanism::ImplicitGlFlush => Ok(()),
other => Err(InteropError::UnsupportedSynchronization(other)),
}
}
fn consumer_ready(
&self,
_texture: &ImportedTexture,
mechanism: SyncMechanism,
) -> Result<(), InteropError> {
match mechanism {
SyncMechanism::None
| SyncMechanism::ImplicitGlFlush
| SyncMechanism::ExplicitFence => Ok(()),
other => Err(InteropError::UnsupportedSynchronization(other)),
}
}
}
impl Drop for Dx12FenceSynchronizer {
fn drop(&mut self) {
if !self.shared_handle.is_invalid() {
unsafe {
let _ = CloseHandle(self.shared_handle);
}
}
}
}