use bevy_ecs::prelude::*;
use tracing::{debug, info};
use windows::System::{DispatcherQueue, DispatcherQueueController};
use windows::UI::Composition::{CompositionGraphicsDevice, Compositor};
use windows::Win32::Graphics::Direct2D::ID2D1Device;
use windows::Win32::System::WinRT::DQTAT_COM_NONE;
use windows::core::Result;
use crate::com::wuc::{CompositorInteropExt, create_dispatcher_queue_controller};
struct WucGraphicsResourceInner {
compositor: Compositor,
graphics_device: CompositionGraphicsDevice,
#[allow(dead_code)]
dq_controller: Option<DispatcherQueueController>,
}
#[derive(Resource)]
pub struct WucGraphicsResource {
inner: Option<WucGraphicsResourceInner>,
}
unsafe impl Send for WucGraphicsResource {}
unsafe impl Sync for WucGraphicsResource {}
impl std::fmt::Debug for WucGraphicsResource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WucGraphicsResource")
.field("is_valid", &self.is_valid())
.finish()
}
}
impl WucGraphicsResource {
pub fn new(d2d_device: &ID2D1Device) -> Result<Self> {
info!("[WucGraphicsResource] Initialization started");
let dq_controller = match DispatcherQueue::GetForCurrentThread() {
Ok(_existing) => {
debug!("[WucGraphicsResource] 既存スレッド DispatcherQueue を再利用");
None
}
Err(_) => Some(create_dispatcher_queue_controller(DQTAT_COM_NONE)?),
};
let compositor = Compositor::new()?;
let interop: windows::Win32::System::WinRT::Composition::ICompositorInterop =
windows::core::Interface::cast(&compositor)?;
let graphics_device = interop.create_graphics_device(d2d_device)?;
info!("[WucGraphicsResource] Initialization completed");
Ok(Self {
inner: Some(WucGraphicsResourceInner {
compositor,
graphics_device,
dq_controller,
}),
})
}
pub fn new_empty() -> Self {
Self { inner: None }
}
pub fn invalidate(&mut self) {
if self.inner.is_some() {
debug!("[WucGraphicsResource] Invalidated");
}
self.inner = None;
}
pub fn is_valid(&self) -> bool {
self.inner.is_some()
}
pub fn compositor(&self) -> Option<&Compositor> {
self.inner.as_ref().map(|i| &i.compositor)
}
pub fn graphics_device(&self) -> Option<&CompositionGraphicsDevice> {
self.inner.as_ref().map(|i| &i.graphics_device)
}
}
#[cfg(test)]
mod tests {
use crate::ecs::graphics::GraphicsCore;
use crate::ecs::graphics::wuc_resource::WucGraphicsResource;
use windows::Win32::System::Com::{COINIT_MULTITHREADED, CoInitializeEx};
#[test]
fn wuc_graphics_resource_lifecycle() {
unsafe {
let _ = CoInitializeEx(None, COINIT_MULTITHREADED);
}
let core = GraphicsCore::new().expect("GraphicsCore::new 失敗(HARDWARE デバイス生成)");
let d2d = core.d2d_device().expect("d2d_device が None");
let resource = WucGraphicsResource::new(d2d).expect("WucGraphicsResource::new 失敗");
assert!(resource.is_valid(), "new 後は is_valid が true");
assert!(resource.compositor().is_some(), "compositor() が Some");
assert!(
resource.graphics_device().is_some(),
"graphics_device() が Some"
);
let mut resource = resource;
resource.invalidate();
assert!(!resource.is_valid(), "invalidate 後は is_valid が false");
assert!(
resource.compositor().is_none(),
"invalidate 後 compositor() が None"
);
assert!(
resource.graphics_device().is_none(),
"invalidate 後 graphics_device() が None"
);
let empty = WucGraphicsResource::new_empty();
assert!(!empty.is_valid(), "new_empty は is_valid が false");
let live = WucGraphicsResource::new(d2d).expect("再生成失敗");
assert!(live.is_valid());
drop(live);
println!("[TEST PASS] WucGraphicsResource lifecycle (new/invalidate/new_empty/drop)");
}
}