Skip to main content

asdf_overlay/
surface.rs

1//! Manage window states for rendering overlays.
2//! You can access states for specific window using [`Backends::with_backend`].
3//! This allows you to interact with the overlay state of a window, including its layout and rendering data.
4
5pub mod texture;
6
7use core::sync::atomic::{AtomicI32, AtomicU32, Ordering};
8
9use anyhow::Context;
10use asdf_overlay_event::{Event, SurfaceEvent, SurfaceInfo};
11use once_cell::sync::Lazy;
12
13use crate::{
14    event_sink::OverlayEventSink, interop::DxInterop, surface::texture::OverlayTextureSlot,
15    types::IntDashMap,
16};
17
18static SURFACES: Lazy<Surfaces> = Lazy::new(|| Surfaces {
19    map: IntDashMap::default(),
20});
21
22/// Global store for surface states.
23pub struct Surfaces {
24    map: IntDashMap<u64, SurfaceState>,
25}
26
27impl Surfaces {
28    /// Iterate over all surfaces.
29    pub fn iter() -> impl Iterator<Item = u64> {
30        SURFACES.map.iter().map(|r| *r.key())
31    }
32
33    /// Run closure with the specified surface, if it exists.
34    pub fn state<R>(id: u64, f: impl FnOnce(&SurfaceState) -> R) -> Option<R> {
35        SURFACES.map.get(&id).map(|r| f(&r))
36    }
37
38    pub fn contains(id: u64) -> bool {
39        SURFACES.map.contains_key(&id)
40    }
41
42    pub fn reset() {
43        for state in SURFACES.map.iter() {
44            state.reset();
45        }
46    }
47
48    #[doc(hidden)]
49    pub fn with<R>(
50        id: u64,
51        setup_fn: impl FnOnce() -> anyhow::Result<SurfaceState>,
52        f: impl FnOnce(&SurfaceState) -> R,
53    ) -> anyhow::Result<R> {
54        if let Some(backend) = SURFACES.map.get(&id) {
55            return Ok(f(&backend));
56        }
57
58        let backend = SURFACES
59            .map
60            .entry(id)
61            .or_try_insert_with(|| {
62                let state = setup_fn().context("failed to setup surface state")?;
63
64                let (width, height) = state.size();
65                OverlayEventSink::emit(Event::Surface {
66                    id,
67                    event: SurfaceEvent::Added {
68                        width,
69                        height,
70                        info: state.info,
71                    },
72                });
73
74                Ok::<_, anyhow::Error>(state)
75            })?
76            .downgrade();
77
78        Ok(f(backend.value()))
79    }
80
81    #[doc(hidden)]
82    pub fn cleanup_state(id: u64) {
83        SURFACES.map.remove(&id);
84
85        OverlayEventSink::emit(Event::Surface {
86            id,
87            event: SurfaceEvent::Destroyed,
88        });
89    }
90}
91
92/// Data associated to a specific window for overlay rendering.
93#[non_exhaustive]
94pub struct SurfaceState {
95    position: (AtomicI32, AtomicI32),
96    size: (AtomicU32, AtomicU32),
97
98    pub interop: DxInterop,
99    pub info: SurfaceInfo,
100
101    #[doc(hidden)]
102    pub texture: OverlayTextureSlot,
103}
104
105impl SurfaceState {
106    pub fn new(interop: DxInterop, size: (u32, u32), info: SurfaceInfo) -> anyhow::Result<Self> {
107        let surface = OverlayTextureSlot::new();
108
109        Ok(Self {
110            position: (AtomicI32::new(0), AtomicI32::new(0)),
111            size: (AtomicU32::new(size.0), AtomicU32::new(size.1)),
112            interop,
113            info,
114            texture: surface,
115        })
116    }
117
118    #[doc(hidden)]
119    pub fn texture_size(&self) -> Option<(u32, u32)> {
120        self.texture.get().as_ref().map(|surface| surface.size())
121    }
122
123    pub fn size(&self) -> (u32, u32) {
124        (
125            self.size.0.load(Ordering::Relaxed),
126            self.size.1.load(Ordering::Relaxed),
127        )
128    }
129
130    #[doc(hidden)]
131    pub fn resize(&self, width: u32, height: u32) {
132        self.size.0.store(width, Ordering::Relaxed);
133        self.size.1.store(height, Ordering::Relaxed);
134    }
135
136    pub fn position(&self) -> (i32, i32) {
137        (
138            self.position.0.load(Ordering::Relaxed),
139            self.position.1.load(Ordering::Relaxed),
140        )
141    }
142
143    pub fn reposition(&self, x: i32, y: i32) {
144        self.position.0.store(x, Ordering::Relaxed);
145        self.position.1.store(y, Ordering::Relaxed);
146    }
147
148    pub fn commit_overlay_texture(
149        &self,
150        handle: Option<SharedTextureHandle>,
151    ) -> anyhow::Result<()> {
152        self.texture.update(&self.interop.device, handle)
153    }
154
155    /// Reset the surface state to its initial state.
156    /// This will reset the position to (0, 0) and remove the overlay texture
157    pub fn reset(&self) {
158        self.reposition(0, 0);
159        _ = self.commit_overlay_texture(None);
160    }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
164pub enum SharedTextureHandle {
165    /// KMT handle.
166    Kmt(u32),
167
168    /// Owned NT handle.
169    Nt(u32),
170}
171
172impl SharedTextureHandle {
173    pub fn as_raw(&self) -> u32 {
174        match self {
175            Self::Kmt(handle) | Self::Nt(handle) => *handle,
176        }
177    }
178}