use crate::backends::metal::RafxTextureMetal;
use crate::metal::{RafxDeviceContextMetal, RafxFenceMetal, RafxRawImageMetal, RafxSemaphoreMetal};
use crate::{
RafxExtents3D, RafxFormat, RafxResourceType, RafxResult, RafxSampleCount, RafxSwapchainDef,
RafxSwapchainImage, RafxTexture, RafxTextureDef, RafxTextureDimensions,
};
use rafx_base::trust_cell::TrustCell;
use raw_window_handle::HasRawWindowHandle;
const SWAPCHAIN_IMAGE_COUNT: u32 = 3;
pub struct RafxSwapchainMetal {
device_context: RafxDeviceContextMetal,
layer: metal_rs::MetalLayer,
drawable: TrustCell<Option<metal_rs::MetalDrawable>>,
swapchain_def: RafxSwapchainDef,
format: RafxFormat,
next_swapchain_image_index: u32,
}
unsafe impl Send for RafxSwapchainMetal {}
unsafe impl Sync for RafxSwapchainMetal {}
impl RafxSwapchainMetal {
pub fn swapchain_def(&self) -> &RafxSwapchainDef {
&self.swapchain_def
}
pub fn image_count(&self) -> usize {
SWAPCHAIN_IMAGE_COUNT as usize
}
pub fn format(&self) -> RafxFormat {
self.format
}
pub fn metal_layer(&self) -> &metal_rs::MetalLayerRef {
self.layer.as_ref()
}
pub(crate) fn take_drawable(&self) -> Option<metal_rs::MetalDrawable> {
self.drawable.borrow_mut().take()
}
pub fn new(
device_context: &RafxDeviceContextMetal,
raw_window_handle: &dyn HasRawWindowHandle,
swapchain_def: &RafxSwapchainDef,
) -> RafxResult<RafxSwapchainMetal> {
let layer = match raw_window_handle.raw_window_handle() {
#[cfg(target_os = "macos")]
raw_window_handle::RawWindowHandle::MacOS(handle) => unsafe {
raw_window_metal::macos::metal_layer_from_handle(handle)
},
#[cfg(target_os = "ios")]
raw_window_handle::RawWindowHandle::IOS(handle) => unsafe {
raw_window_metal::ios::metal_layer_from_handle(handle)
},
_ => return Err("Cannot create RafxSurfaceMetal on this operating system".into()),
};
let layer = match layer {
raw_window_metal::Layer::Allocated(x) => Some(x),
raw_window_metal::Layer::Existing(x) => Some(x),
raw_window_metal::Layer::None => None,
}
.unwrap();
let layer = unsafe { std::mem::transmute::<_, &metal_rs::MetalLayerRef>(layer).to_owned() };
layer.set_device(device_context.device());
layer.set_pixel_format(metal_rs::MTLPixelFormat::BGRA8Unorm_sRGB);
layer.set_presents_with_transaction(false);
layer.set_display_sync_enabled(swapchain_def.enable_vsync);
layer.set_drawable_size(metal_rs::CGSize::new(
swapchain_def.width as f64,
swapchain_def.height as f64,
));
let swapchain_def = swapchain_def.clone();
Ok(RafxSwapchainMetal {
device_context: device_context.clone(),
layer,
drawable: Default::default(),
swapchain_def,
next_swapchain_image_index: 0,
format: RafxFormat::B8G8R8A8_SRGB,
})
}
pub fn rebuild(
&mut self,
swapchain_def: &RafxSwapchainDef,
) -> RafxResult<()> {
self.layer.set_drawable_size(metal_rs::CGSize::new(
swapchain_def.width as f64,
swapchain_def.height as f64,
));
self.swapchain_def = swapchain_def.clone();
Ok(())
}
pub fn acquire_next_image_fence(
&mut self,
_fence: &RafxFenceMetal,
) -> RafxResult<RafxSwapchainImage> {
self.acquire_next_image()
}
pub fn acquire_next_image_semaphore(
&mut self,
_semaphore: &RafxSemaphoreMetal,
) -> RafxResult<RafxSwapchainImage> {
self.acquire_next_image()
}
pub fn acquire_next_image(&mut self) -> RafxResult<RafxSwapchainImage> {
objc::rc::autoreleasepool(|| {
let drawable = self
.layer
.next_drawable()
.ok_or("Timed out while trying to acquire drawable".to_string())?;
let mut old_drawable = self.drawable.borrow_mut();
assert!(old_drawable.is_none());
*old_drawable = Some(drawable.to_owned());
let raw_image = RafxRawImageMetal::Ref(drawable.texture().to_owned());
let image = RafxTextureMetal::from_existing(
&self.device_context,
Some(raw_image),
&RafxTextureDef {
extents: RafxExtents3D {
width: self.swapchain_def.width,
height: self.swapchain_def.height,
depth: 1,
},
array_length: 1,
mip_count: 1,
format: self.format,
resource_type: RafxResourceType::UNDEFINED,
sample_count: RafxSampleCount::SampleCount1,
dimensions: RafxTextureDimensions::Dim2D,
},
)?;
let swapchain_image_index = self.next_swapchain_image_index;
self.next_swapchain_image_index += 1;
if self.next_swapchain_image_index >= SWAPCHAIN_IMAGE_COUNT {
self.next_swapchain_image_index = 0;
}
Ok(RafxSwapchainImage {
texture: RafxTexture::Metal(image),
swapchain_image_index,
})
})
}
}