use core::sync::atomic::{AtomicBool, Ordering};
use anyhow::Context;
use parking_lot::{RwLock, RwLockReadGuard};
use windows::{
Win32::{
Foundation::{CloseHandle, HANDLE},
Graphics::{
Direct3D11::{D3D11_TEXTURE2D_DESC, ID3D11Device, ID3D11Device1, ID3D11Texture2D},
Dxgi::{Common::DXGI_FORMAT, IDXGIKeyedMutex},
},
},
core::Interface,
};
use crate::surface::SharedTextureHandle;
pub struct OverlaySurface {
texture: ID3D11Texture2D,
handle: SharedTextureHandle,
mutex: Option<IDXGIKeyedMutex>,
size: (u32, u32),
format: DXGI_FORMAT,
}
impl OverlaySurface {
pub(crate) fn open(device: &ID3D11Device, handle: SharedTextureHandle) -> anyhow::Result<Self> {
unsafe {
let texture = match handle {
SharedTextureHandle::Kmt(handle) => {
let mut slot = None::<ID3D11Texture2D>;
device
.OpenSharedResource(HANDLE(handle as _), &mut slot)
.context("failed to open KMT shared texture")?;
slot.unwrap()
}
SharedTextureHandle::Nt(handle) => device
.cast::<ID3D11Device1>()?
.OpenSharedResource1::<ID3D11Texture2D>(HANDLE(handle as _))
.context("failed to open NT shared texture")?,
};
let mut desc = D3D11_TEXTURE2D_DESC::default();
texture.GetDesc(&mut desc);
let mutex = texture.cast::<IDXGIKeyedMutex>().ok();
Ok(Self {
texture,
handle,
mutex,
size: (desc.Width, desc.Height),
format: desc.Format,
})
}
}
#[inline]
pub const fn mutex(&self) -> Option<&IDXGIKeyedMutex> {
self.mutex.as_ref()
}
#[inline]
pub const fn size(&self) -> (u32, u32) {
self.size
}
#[inline]
pub const fn format(&self) -> DXGI_FORMAT {
self.format
}
#[inline]
pub const fn texture(&self) -> &ID3D11Texture2D {
&self.texture
}
#[inline]
pub fn shared_handle(&self) -> SharedTextureHandle {
self.handle
}
}
impl Drop for OverlaySurface {
fn drop(&mut self) {
if let SharedTextureHandle::Nt(handle) = self.handle {
unsafe {
_ = CloseHandle(HANDLE(handle as _));
}
}
}
}
pub struct OverlayTextureSlot {
inner: RwLock<Option<OverlaySurface>>,
updated: AtomicBool,
}
impl OverlayTextureSlot {
pub(crate) const fn new() -> Self {
Self {
inner: RwLock::new(None),
updated: AtomicBool::new(true),
}
}
#[doc(hidden)]
pub fn get(&self) -> RwLockReadGuard<'_, Option<OverlaySurface>> {
self.inner.read()
}
#[inline]
pub fn invalidate(&self) {
self.updated.store(true, Ordering::Relaxed);
}
pub(super) fn update(
&self,
device: &ID3D11Device,
handle: Option<SharedTextureHandle>,
) -> anyhow::Result<()> {
self.updated.store(true, Ordering::Relaxed);
let Some(handle) = handle else {
*self.inner.write() = None;
return Ok(());
};
*self.inner.write() = Some(OverlaySurface::open(device, handle)?);
Ok(())
}
#[inline]
pub fn take_update(&self) -> bool {
self.updated.swap(false, Ordering::Relaxed)
}
}