pub mod texture;
use core::sync::atomic::{AtomicI32, AtomicU32, Ordering};
use anyhow::Context;
use asdf_overlay_event::{Event, SurfaceEvent, SurfaceInfo};
use once_cell::sync::Lazy;
use crate::{
event_sink::OverlayEventSink, interop::DxInterop, surface::texture::OverlayTextureSlot,
types::IntDashMap,
};
static SURFACES: Lazy<Surfaces> = Lazy::new(|| Surfaces {
map: IntDashMap::default(),
});
pub struct Surfaces {
map: IntDashMap<u64, SurfaceState>,
}
impl Surfaces {
pub fn iter() -> impl Iterator<Item = u64> {
SURFACES.map.iter().map(|r| *r.key())
}
pub fn state<R>(id: u64, f: impl FnOnce(&SurfaceState) -> R) -> Option<R> {
SURFACES.map.get(&id).map(|r| f(&r))
}
pub fn contains(id: u64) -> bool {
SURFACES.map.contains_key(&id)
}
pub fn reset() {
for state in SURFACES.map.iter() {
state.reset();
}
}
#[doc(hidden)]
pub fn with<R>(
id: u64,
setup_fn: impl FnOnce() -> anyhow::Result<SurfaceState>,
f: impl FnOnce(&SurfaceState) -> R,
) -> anyhow::Result<R> {
if let Some(backend) = SURFACES.map.get(&id) {
return Ok(f(&backend));
}
let backend = SURFACES
.map
.entry(id)
.or_try_insert_with(|| {
let state = setup_fn().context("failed to setup surface state")?;
let (width, height) = state.size();
OverlayEventSink::emit(Event::Surface {
id,
event: SurfaceEvent::Added {
width,
height,
info: state.info,
},
});
Ok::<_, anyhow::Error>(state)
})?
.downgrade();
Ok(f(backend.value()))
}
#[doc(hidden)]
pub fn cleanup_state(id: u64) {
SURFACES.map.remove(&id);
OverlayEventSink::emit(Event::Surface {
id,
event: SurfaceEvent::Destroyed,
});
}
}
#[non_exhaustive]
pub struct SurfaceState {
position: (AtomicI32, AtomicI32),
size: (AtomicU32, AtomicU32),
pub interop: DxInterop,
pub info: SurfaceInfo,
#[doc(hidden)]
pub texture: OverlayTextureSlot,
}
impl SurfaceState {
pub fn new(interop: DxInterop, size: (u32, u32), info: SurfaceInfo) -> anyhow::Result<Self> {
let surface = OverlayTextureSlot::new();
Ok(Self {
position: (AtomicI32::new(0), AtomicI32::new(0)),
size: (AtomicU32::new(size.0), AtomicU32::new(size.1)),
interop,
info,
texture: surface,
})
}
#[doc(hidden)]
pub fn texture_size(&self) -> Option<(u32, u32)> {
self.texture.get().as_ref().map(|surface| surface.size())
}
pub fn size(&self) -> (u32, u32) {
(
self.size.0.load(Ordering::Relaxed),
self.size.1.load(Ordering::Relaxed),
)
}
#[doc(hidden)]
pub fn resize(&self, width: u32, height: u32) {
self.size.0.store(width, Ordering::Relaxed);
self.size.1.store(height, Ordering::Relaxed);
}
pub fn position(&self) -> (i32, i32) {
(
self.position.0.load(Ordering::Relaxed),
self.position.1.load(Ordering::Relaxed),
)
}
pub fn reposition(&self, x: i32, y: i32) {
self.position.0.store(x, Ordering::Relaxed);
self.position.1.store(y, Ordering::Relaxed);
}
pub fn commit_overlay_texture(
&self,
handle: Option<SharedTextureHandle>,
) -> anyhow::Result<()> {
self.texture.update(&self.interop.device, handle)
}
pub fn reset(&self) {
self.reposition(0, 0);
_ = self.commit_overlay_texture(None);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SharedTextureHandle {
Kmt(u32),
Nt(u32),
}
impl SharedTextureHandle {
pub fn as_raw(&self) -> u32 {
match self {
Self::Kmt(handle) | Self::Nt(handle) => *handle,
}
}
}