use std::ffi::c_void;
use std::ptr::NonNull;
use std::sync::Arc;
use super::layer::{create_owned_layer, find_metal_layer};
use super::PresentSink;
#[derive(bevy::prelude::Resource, Clone, bevy::render::extract_resource::ExtractResource)]
pub struct MetalFxDualPresent {
layer: usize,
queue: usize,
pub sink: Arc<PresentSink>,
pub enabled: bool,
pub refresh_interval: f64,
pub single_present: bool,
}
impl Default for MetalFxDualPresent {
fn default() -> Self {
Self {
layer: 0,
queue: 0,
sink: PresentSink::new(),
enabled: false,
refresh_interval: 1.0 / 120.0,
single_present: false,
}
}
}
impl MetalFxDualPresent {
pub fn new(sink: Arc<PresentSink>, enabled: bool) -> Self {
Self {
sink,
enabled,
..Default::default()
}
}
pub fn with_single_present(mut self, single: bool) -> Self {
self.single_present = single;
self
}
pub fn with_refresh_interval(mut self, seconds: f64) -> Self {
self.refresh_interval = seconds;
self
}
pub fn layer(&self) -> Option<NonNull<c_void>> {
if !self.enabled {
return None;
}
NonNull::new(self.layer as *mut c_void)
}
pub fn queue(&self) -> Option<NonNull<c_void>> {
NonNull::new(self.queue as *mut c_void)
}
pub fn has_layer(&self) -> bool {
self.layer != 0
}
}
pub fn capture_metal_layer(
mut state: bevy::prelude::ResMut<MetalFxDualPresent>,
windows: bevy::prelude::Query<&bevy::window::RawHandleWrapper>,
) {
if state.layer != 0 || !state.enabled {
return;
}
let Ok(handle) = windows.single() else {
return;
};
let raw_window_handle::RawWindowHandle::AppKit(appkit) = handle.get_window_handle() else {
return;
};
let found = unsafe { find_metal_layer(appkit.ns_view.as_ptr()) };
match found {
Some(wgpu_layer) => {
let Some((owned, queue)) =
(unsafe { create_owned_layer(wgpu_layer, appkit.ns_view.as_ptr()) })
else {
return;
};
state.layer = owned.as_ptr() as usize;
state.queue = queue.as_ptr() as usize;
log::info!("MetalFX dual presentation: active on an owned layer");
}
None => {
static WARNED: std::sync::Once = std::sync::Once::new();
WARNED.call_once(|| {
log::warn!(
"MetalFX dual presentation: no CAMetalLayer found on the window's NSView; \
interpolated frames will be computed but not presented"
);
});
}
}
}