Skip to main content

asdf_overlay/surface/
texture.rs

1//! Overlay surface abstraction.
2//!
3//! The surface texture must be Direct3D 11 texture created with shared flags.
4//! Direct3D 11 was chosen, because it is well supported on almost every gpus nowadays.
5//!
6//! If you create surface texture with keyed mutex, it will uses it for synchronization.
7//! You must keep mutex key to `0` otherwise, it will wait indefinitely when rendering overlay.
8//! You can still have surface texture without keyed mutex,
9//! however you must flush it manually on changes and will have worse performance.
10
11use core::sync::atomic::{AtomicBool, Ordering};
12
13use anyhow::Context;
14use parking_lot::{RwLock, RwLockReadGuard};
15use windows::{
16    Win32::{
17        Foundation::{CloseHandle, HANDLE},
18        Graphics::{
19            Direct3D11::{D3D11_TEXTURE2D_DESC, ID3D11Device, ID3D11Device1, ID3D11Texture2D},
20            Dxgi::{Common::DXGI_FORMAT, IDXGIKeyedMutex},
21        },
22    },
23    core::Interface,
24};
25
26use crate::surface::SharedTextureHandle;
27
28/// Overlay surface texture.
29pub struct OverlaySurface {
30    texture: ID3D11Texture2D,
31    handle: SharedTextureHandle,
32    mutex: Option<IDXGIKeyedMutex>,
33    size: (u32, u32),
34    format: DXGI_FORMAT,
35}
36
37impl OverlaySurface {
38    /// Open Direct3D 11 shared texture by consuming `handle`, with given `device`.
39    pub(crate) fn open(device: &ID3D11Device, handle: SharedTextureHandle) -> anyhow::Result<Self> {
40        unsafe {
41            let texture = match handle {
42                SharedTextureHandle::Kmt(handle) => {
43                    let mut slot = None::<ID3D11Texture2D>;
44                    device
45                        .OpenSharedResource(HANDLE(handle as _), &mut slot)
46                        .context("failed to open KMT shared texture")?;
47
48                    slot.unwrap()
49                }
50
51                SharedTextureHandle::Nt(handle) => device
52                    .cast::<ID3D11Device1>()?
53                    .OpenSharedResource1::<ID3D11Texture2D>(HANDLE(handle as _))
54                    .context("failed to open NT shared texture")?,
55            };
56
57            let mut desc = D3D11_TEXTURE2D_DESC::default();
58            texture.GetDesc(&mut desc);
59
60            let mutex = texture.cast::<IDXGIKeyedMutex>().ok();
61            Ok(Self {
62                texture,
63                handle,
64                mutex,
65                size: (desc.Width, desc.Height),
66                format: desc.Format,
67            })
68        }
69    }
70
71    #[inline]
72    /// [`IDXGIKeyedMutex`] of the surface texture.
73    pub const fn mutex(&self) -> Option<&IDXGIKeyedMutex> {
74        self.mutex.as_ref()
75    }
76
77    #[inline]
78    /// Size of the overlay surface in phyiscal pixel units.
79    pub const fn size(&self) -> (u32, u32) {
80        self.size
81    }
82
83    #[inline]
84    /// Format of the overlay surface.
85    pub const fn format(&self) -> DXGI_FORMAT {
86        self.format
87    }
88
89    #[inline]
90    /// [`ID3D11Texture2D`] of the surface texture.
91    pub const fn texture(&self) -> &ID3D11Texture2D {
92        &self.texture
93    }
94
95    #[inline]
96    /// Shared handle of the surface texture.
97    pub fn shared_handle(&self) -> SharedTextureHandle {
98        self.handle
99    }
100}
101
102impl Drop for OverlaySurface {
103    fn drop(&mut self) {
104        if let SharedTextureHandle::Nt(handle) = self.handle {
105            unsafe {
106                _ = CloseHandle(HANDLE(handle as _));
107            }
108        }
109    }
110}
111
112pub struct OverlayTextureSlot {
113    inner: RwLock<Option<OverlaySurface>>,
114    updated: AtomicBool,
115}
116
117impl OverlayTextureSlot {
118    pub(crate) const fn new() -> Self {
119        Self {
120            inner: RwLock::new(None),
121            updated: AtomicBool::new(true),
122        }
123    }
124
125    #[doc(hidden)]
126    pub fn get(&self) -> RwLockReadGuard<'_, Option<OverlaySurface>> {
127        self.inner.read()
128    }
129
130    #[inline]
131    pub fn invalidate(&self) {
132        self.updated.store(true, Ordering::Relaxed);
133    }
134
135    pub(super) fn update(
136        &self,
137        device: &ID3D11Device,
138        handle: Option<SharedTextureHandle>,
139    ) -> anyhow::Result<()> {
140        self.updated.store(true, Ordering::Relaxed);
141        let Some(handle) = handle else {
142            *self.inner.write() = None;
143            return Ok(());
144        };
145
146        *self.inner.write() = Some(OverlaySurface::open(device, handle)?);
147        Ok(())
148    }
149
150    #[inline]
151    pub fn take_update(&self) -> bool {
152        self.updated.swap(false, Ordering::Relaxed)
153    }
154}