use std::sync::OnceLock;
use std::sync::mpsc::{Sender, channel};
use windows::Win32::System::Com::{COINIT_MULTITHREADED, CoInitializeEx};
use windows::Win32::System::WinRT::DQTAT_COM_NONE;
use wintf::com::wuc::create_dispatcher_queue_controller;
type Job = Box<dyn FnOnce() + Send>;
static OWNER: OnceLock<Sender<Job>> = OnceLock::new();
fn owner_sender() -> &'static Sender<Job> {
OWNER.get_or_init(|| {
let (tx, rx) = channel::<Job>();
std::thread::Builder::new()
.name("wuc-gpu-owner".into())
.spawn(move || {
unsafe {
let _ = CoInitializeEx(None, COINIT_MULTITHREADED);
}
let _resident = create_dispatcher_queue_controller(DQTAT_COM_NONE)
.expect("常駐 DispatcherQueueController の生成失敗(オーナースレッド)");
while let Ok(job) = rx.recv() {
job();
}
})
.expect("spawn wuc-gpu-owner thread 失敗");
tx
})
}
pub fn on_gpu_owner_thread<T, F>(f: F) -> T
where
T: Send + 'static,
F: FnOnce() -> T + Send + 'static,
{
let (rtx, rrx) = channel::<std::thread::Result<T>>();
let job: Job = Box::new(move || {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
let _ = rtx.send(result);
});
owner_sender()
.send(job)
.expect("GPU オーナースレッドへのジョブ送信失敗(スレッド死亡?)");
match rrx
.recv()
.expect("GPU オーナースレッドからの結果受信失敗(スレッド死亡?)")
{
Ok(value) => value,
Err(panic_payload) => std::panic::resume_unwind(panic_payload),
}
}
mod fixture_self_tests {
use windows::UI::Composition::Compositor;
use wintf::ecs::{GraphicsCore, WucGraphicsResource};
use super::on_gpu_owner_thread;
fn make_and_drop_full_stack() {
let core = GraphicsCore::new().expect("GraphicsCore::new");
let d2d = core.d2d_device().expect("d2d_device");
let wuc = WucGraphicsResource::new(d2d).expect("WucGraphicsResource::new");
assert!(wuc.is_valid());
let _visual = wuc
.compositor()
.expect("compositor")
.CreateSpriteVisual()
.expect("CreateSpriteVisual");
}
#[test]
fn owner_thread_single_full_cycle() {
on_gpu_owner_thread(make_and_drop_full_stack);
}
#[test]
fn owner_thread_two_full_cycles_no_crash() {
on_gpu_owner_thread(make_and_drop_full_stack);
on_gpu_owner_thread(make_and_drop_full_stack);
}
#[test]
fn owner_thread_returns_value() {
let n = on_gpu_owner_thread(|| {
let _c = Compositor::new().expect("Compositor::new");
42usize
});
assert_eq!(n, 42);
}
}