mod state;
mod tape;
use core::ffi::c_void;
pub use tape::{Reader, Tape};
use windows::core::{IUnknown, Interface};
#[derive(Clone)]
pub struct Object(Option<IUnknown>);
impl Object {
pub unsafe fn copy(raw: *mut c_void) -> Self {
Self(unsafe { IUnknown::from_raw_borrowed(&raw) }.cloned())
}
pub fn raw(&self) -> *mut c_void {
self.0.as_ref().map_or(core::ptr::null_mut(), Interface::as_raw)
}
}
use crate::vtables::recorded_context::tag;
use std::sync::{Arc, Mutex};
use windows::Win32::Graphics::Direct3D11::*;
mod private;
mod uploads;
mod watch;
use uploads::Mapped;
#[derive(Default)]
pub struct Recorder {
current: Mutex<Current>,
}
#[derive(Default)]
struct Current {
initial: Option<Tape>,
inherited: bool,
commands: Tape,
so: Option<Tape>,
gaps: Vec<&'static str>,
maps: std::collections::HashMap<(usize, u32), Mapped>,
shadows: std::collections::HashMap<(usize, u32), watch::Shadow>,
}
pub struct Plan {
pub frame: u64,
initial: Tape,
commands: Tape,
pub gaps: Vec<&'static str>,
}
impl Recorder {
pub unsafe fn begin(&self, context: *mut c_void) {
let mut s = self.current.lock().unwrap_or_else(|e| e.into_inner());
if s.initial.is_none() {
s.initial = Some(if s.inherited {
unsafe { state::snapshot(context, s.so.as_ref()) }
} else {
Tape::default()
});
if s.inherited && s.so.is_some() {
s.gaps.push("inherited stream-output cursor is not observable");
}
}
}
pub fn record(&self, write: impl Fn(&mut Tape)) {
let mut s = self.current.lock().unwrap_or_else(|e| e.into_inner());
write(&mut s.commands);
match s.commands.last() {
Some(tag::SOSetTargets) => {
let mut so = Tape::default();
write(&mut so);
s.so = Some(so);
}
Some(tag::ClearState) => s.so = None,
Some(tag::ExecuteCommandList) => s
.gaps
.push("nested command-list readbacks cannot be reconstructed on this runtime"),
_ => {}
}
}
pub fn gap(&self, reason: &'static str) {
let mut s = self.current.lock().unwrap_or_else(|e| e.into_inner());
if !s.gaps.contains(&reason) {
s.gaps.push(reason);
}
}
pub unsafe fn finish(&self, list: *mut c_void, restore: bool) {
let plan = {
let mut s = self.current.lock().unwrap_or_else(|e| e.into_inner());
if !s.maps.is_empty() {
s.gaps.push("FinishCommandList with an outstanding map");
}
s.shadows.clear();
let plan = Plan {
frame: crate::dxgi::frame(),
initial: s.initial.take().unwrap_or_default(),
commands: {
let next = Tape::sized_like(&s.commands);
std::mem::replace(&mut s.commands, next)
},
gaps: std::mem::take(&mut s.gaps),
};
s.inherited = restore;
if !restore {
s.so = None;
}
Arc::new(plan)
};
unsafe { private::attach(list, plan) };
}
}
impl Plan {
pub unsafe fn replay(&self, context: *mut c_void) -> windows::core::Result<()> {
unsafe { self.initial.replay(context) }?;
unsafe { self.commands.replay(context) }
}
}
pub unsafe fn plan(list: *mut c_void) -> Option<Arc<Plan>> {
unsafe { private::get(list) }
}