From 5fd5f4a008dde2dd5f6753449a6d0a101fcd96f3 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 21 Jun 2018 08:45:54 -0400 Subject: [PATCH 1/6] [mtl] hack persistent swapchain textures --- src/backend/metal/src/window.rs | 73 +++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/src/backend/metal/src/window.rs b/src/backend/metal/src/window.rs index b97b11f65cf..99a5798b722 100644 --- a/src/backend/metal/src/window.rs +++ b/src/backend/metal/src/window.rs @@ -15,6 +15,7 @@ use objc::runtime::{Object}; use core_graphics::base::CGFloat; use core_graphics::geometry::CGRect; use cocoa::foundation::{NSRect}; +use foreign_types::{ForeignType, ForeignTypeRef}; pub type CAMetalLayer = *mut Object; @@ -39,17 +40,20 @@ impl Drop for SurfaceInner { } } +#[derive(Debug)] +struct Frame { + drawable: Option, + texture: metal::Texture, +} + pub struct SwapchainInner { - frames: Vec>, + frames: Vec, } impl ops::Index for SwapchainInner { type Output = metal::TextureRef; fn index(&self, index: hal::FrameImage) -> &Self::Output { - self.frames[index as usize] - .as_ref() - .map(|&(_, ref tex)| tex) - .expect("Frame texture is not resident!") + &self.frames[index as usize].texture } } @@ -64,8 +68,8 @@ impl fmt::Debug for SwapchainInner { impl Drop for SwapchainInner { fn drop(&mut self) { - for maybe in self.frames.drain(..) { - if let Some((drawable, _)) = maybe { + for mut frame in self.frames.drain(..) { + if let Some(drawable) = frame.drawable.take() { unsafe { msg_send![drawable, release]; } @@ -85,14 +89,16 @@ unsafe impl Sync for Swapchain {} impl Swapchain { pub(crate) fn present(&self, index: hal::FrameImage) { - let (drawable, _) = self.inner + let drawable = self.inner .write() .unwrap() .frames[index as usize] + .drawable .take() - .expect("Frame is not ready to present!"); + .unwrap(); unsafe { msg_send![drawable, present]; + //TODO: delay the actual release msg_send![drawable, release]; } } @@ -116,7 +122,7 @@ impl hal::Surface for Surface { }; let caps = hal::SurfaceCapabilities { - image_count: 1 .. max_frames as hal::FrameImage, + image_count: 2 .. max_frames as hal::FrameImage, current_extent: None, extents: Extent2D { width: 4, height: 4} .. Extent2D { width: 4096, height: 4096 }, max_image_layers: 1, @@ -157,6 +163,8 @@ impl Device { surface: &mut Surface, config: SwapchainConfig, ) -> (Swapchain, Backbuffer) { + let _ap = AutoreleasePool::new(); // for the drawable + let mtl_format = self.private_caps .map_format(config.color_format) .expect("unsupported backbuffer format"); @@ -177,8 +185,10 @@ impl Device { msg_send![render_layer, setDevice: device_raw]; msg_send![render_layer, setPixelFormat: mtl_format]; msg_send![render_layer, setFramebufferOnly: framebuffer_only]; + msg_send![render_layer, setMaximumDrawableCount: config.image_count as u64]; //TODO: only set it where supported msg_send![render_layer, setDisplaySyncEnabled: display_sync]; + //msg_send![render_layer, setPresentsWithTransaction: true]; // Update render layer size let view_points_size: CGRect = msg_send![nsview, bounds]; @@ -202,7 +212,18 @@ impl Device { let pixel_height = (view_size.height * scale_factor) as u64; let inner = SwapchainInner { - frames: (0 .. config.image_count).map(|_| None).collect(), + frames: (0 .. config.image_count) + .map(|_| unsafe { + let drawable: *mut Object = msg_send![render_layer, nextDrawable]; + assert!(!drawable.is_null()); + let texture: metal::Texture = msg_send![drawable, texture]; + //HACK: not retaining the texture here + Frame { + drawable: None, //Note: careful! + texture, + } + }) + .collect(), }; let swapchain = Swapchain { @@ -236,6 +257,8 @@ impl Device { impl hal::Swapchain for Swapchain { fn acquire_frame(&mut self, sync: hal::FrameSync) -> Result { + let _ap = AutoreleasePool::new(); // for the drawable + unsafe { match sync { hal::FrameSync::Semaphore(semaphore) => { @@ -246,24 +269,20 @@ impl hal::Swapchain for Swapchain { } } + let layer_ref = self.surface.render_layer.lock().unwrap(); + let drawable: CADrawable = unsafe { + msg_send![*layer_ref, nextDrawable] + }; + let texture_temp: &metal::TextureRef = unsafe { + msg_send![drawable, retain]; + msg_send![drawable, texture] + }; let mut inner = self.inner.write().unwrap(); let index = inner.frames - .iter_mut() - .position(|d| d.is_none()) - .expect("No frame available to acquire!"); - - debug!("acquired frame {}", index); - let layer = self.surface.render_layer.lock().unwrap(); - - let _ap = AutoreleasePool::new(); // for the drawable - inner.frames[index] = Some(unsafe { - let drawable: *mut Object = msg_send![*layer, nextDrawable]; - assert!(!drawable.is_null()); - let texture: metal::Texture = msg_send![drawable, texture]; - msg_send![drawable, retain]; - msg_send![texture, retain]; - (drawable, texture) - }); + .iter() + .position(|f| f.texture.as_ptr() == texture_temp.as_ptr()) + .expect(&format!("Surface lost? ptr {:?}, frames {:?}", texture_temp, inner.frames)); + inner.frames[index].drawable = Some(drawable); Ok(index as _) } From 37b01d0e2fe70473ffbc443fe44222558aba50fd Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 21 Jun 2018 14:21:00 -0400 Subject: [PATCH 2/6] Rename FrameImage and acquire_frame --- examples/quad/main.rs | 4 ++-- src/backend/dx11/src/lib.rs | 6 +++--- src/backend/dx12/src/lib.rs | 4 ++-- src/backend/dx12/src/window.rs | 2 +- src/backend/empty/src/lib.rs | 4 ++-- src/backend/gl/src/queue.rs | 2 +- src/backend/gl/src/window/glutin.rs | 2 +- src/backend/metal/src/command.rs | 4 ++-- src/backend/metal/src/native.rs | 4 ++-- src/backend/metal/src/window.rs | 10 +++++----- src/backend/vulkan/src/lib.rs | 4 ++-- src/backend/vulkan/src/window.rs | 2 +- src/hal/src/lib.rs | 2 +- src/hal/src/queue/mod.rs | 6 +++--- src/hal/src/window.rs | 22 +++++++++++----------- src/window/dxgi/src/lib.rs | 2 +- src/window/glfw/src/lib.rs | 2 +- src/window/sdl/src/lib.rs | 2 +- 18 files changed, 42 insertions(+), 42 deletions(-) diff --git a/examples/quad/main.rs b/examples/quad/main.rs index 1bbadd7c763..c3822007d16 100644 --- a/examples/quad/main.rs +++ b/examples/quad/main.rs @@ -463,8 +463,8 @@ fn main() { device.reset_fence(&frame_fence); command_pool.reset(); - let frame: hal::FrameImage = { - match swap_chain.acquire_frame(FrameSync::Semaphore(&mut frame_semaphore)) { + let frame: hal::SwapImageIndex = { + match swap_chain.acquire_image(FrameSync::Semaphore(&mut frame_semaphore)) { Ok(i) => i, Err(_) => { recreate_swapchain = true; diff --git a/src/backend/dx11/src/lib.rs b/src/backend/dx11/src/lib.rs index 2e787631dd9..c1b23c7e7ef 100644 --- a/src/backend/dx11/src/lib.rs +++ b/src/backend/dx11/src/lib.rs @@ -15,7 +15,7 @@ extern crate winit; extern crate wio; use hal::{buffer, command, error, format, image, memory, query, pso, Features, Limits, QueueType}; -use hal::{DrawCount, FrameImage, IndexCount, InstanceCount, VertexCount, VertexOffset, WorkGroupCount}; +use hal::{DrawCount, SwapImageIndex, IndexCount, InstanceCount, VertexCount, VertexOffset, WorkGroupCount}; use hal::queue::{QueueFamilyId, Queues}; use hal::backend::RawQueueGroup; use hal::range::RangeArg; @@ -639,7 +639,7 @@ unsafe impl Send for Swapchain { } unsafe impl Sync for Swapchain { } impl hal::Swapchain for Swapchain { - fn acquire_frame(&mut self, _sync: hal::FrameSync) -> Result { + fn acquire_image(&mut self, _sync: hal::FrameSync) -> Result { // TODO: non-`_DISCARD` swap effects have more than one buffer, `FLIP` // effects are dxgi 1.3 (w10+?) in which case there is // `GetCurrentBackBufferIndex()` on the swapchain @@ -681,7 +681,7 @@ impl hal::queue::RawCommandQueue for CommandQueue { fn present(&mut self, swapchains: IS, _wait_semaphores: IW) -> Result<(), ()> where - IS: IntoIterator, + IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow, diff --git a/src/backend/dx12/src/lib.rs b/src/backend/dx12/src/lib.rs index 1305344120d..5e5adf7db3a 100644 --- a/src/backend/dx12/src/lib.rs +++ b/src/backend/dx12/src/lib.rs @@ -24,7 +24,7 @@ mod pool; mod root_constants; mod window; -use hal::{error, format as f, image, memory, Features, FrameImage, Limits, QueueType}; +use hal::{error, format as f, image, memory, Features, SwapImageIndex, Limits, QueueType}; use hal::queue::{QueueFamilyId, Queues}; use descriptors_cpu::DescriptorCpuPool; @@ -468,7 +468,7 @@ impl hal::queue::RawCommandQueue for CommandQueue { fn present(&mut self, swapchains: IS, _wait_semaphores: IW) -> Result<(), ()> where - IS: IntoIterator, + IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow, diff --git a/src/backend/dx12/src/window.rs b/src/backend/dx12/src/window.rs index fc02913a957..d49ae974c83 100644 --- a/src/backend/dx12/src/window.rs +++ b/src/backend/dx12/src/window.rs @@ -109,7 +109,7 @@ pub struct Swapchain { } impl hal::Swapchain for Swapchain { - fn acquire_frame(&mut self, _sync: hal::FrameSync) -> Result { + fn acquire_image(&mut self, _sync: hal::FrameSync) -> Result { // TODO: sync if false { diff --git a/src/backend/empty/src/lib.rs b/src/backend/empty/src/lib.rs index d1095520639..4e2d0618f55 100644 --- a/src/backend/empty/src/lib.rs +++ b/src/backend/empty/src/lib.rs @@ -98,7 +98,7 @@ impl queue::RawCommandQueue for RawCommandQueue { fn present(&mut self, _: IS, _: IW) -> Result<(), ()> where - IS: IntoIterator, + IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow<()>, @@ -785,7 +785,7 @@ impl hal::Surface for Surface { /// Dummy swapchain. pub struct Swapchain; impl hal::Swapchain for Swapchain { - fn acquire_frame(&mut self, _: hal::FrameSync) -> Result { + fn acquire_image(&mut self, _: hal::FrameSync) -> Result { unimplemented!() } } diff --git a/src/backend/gl/src/queue.rs b/src/backend/gl/src/queue.rs index 0b1c265169d..980375fef3e 100644 --- a/src/backend/gl/src/queue.rs +++ b/src/backend/gl/src/queue.rs @@ -722,7 +722,7 @@ impl hal::queue::RawCommandQueue for CommandQueue { #[cfg(feature = "glutin")] fn present(&mut self, swapchains: IS, _wait_semaphores: IW) -> Result<(), ()> where - IS: IntoIterator, + IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow, diff --git a/src/backend/gl/src/window/glutin.rs b/src/backend/gl/src/window/glutin.rs index bf8df6fc5f0..9dec2f6016c 100644 --- a/src/backend/gl/src/window/glutin.rs +++ b/src/backend/gl/src/window/glutin.rs @@ -65,7 +65,7 @@ pub struct Swapchain { } impl hal::Swapchain for Swapchain { - fn acquire_frame(&mut self, _sync: hal::FrameSync) -> Result { + fn acquire_image(&mut self, _sync: hal::FrameSync) -> Result { // TODO: sync Ok(0) } diff --git a/src/backend/metal/src/command.rs b/src/backend/metal/src/command.rs index 05fdc81994f..6b7d9c62132 100644 --- a/src/backend/metal/src/command.rs +++ b/src/backend/metal/src/command.rs @@ -10,7 +10,7 @@ use std::{iter, mem}; use std::slice; use hal::{buffer, command as com, error, memory, pool, pso}; -use hal::{DrawCount, FrameImage, VertexCount, VertexOffset, InstanceCount, IndexCount, WorkGroupCount}; +use hal::{DrawCount, SwapImageIndex, VertexCount, VertexOffset, InstanceCount, IndexCount, WorkGroupCount}; use hal::backend::FastHashMap; use hal::format::{Aspects, Format, FormatDesc}; use hal::image::{Extent, Filter, Layout, Level, SubresourceRange}; @@ -1322,7 +1322,7 @@ impl RawCommandQueue for CommandQueue { fn present(&mut self, swapchains: IS, _wait_semaphores: IW) -> Result<(), ()> where - IS: IntoIterator, + IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow, diff --git a/src/backend/metal/src/native.rs b/src/backend/metal/src/native.rs index fe1bf3e33dd..26e0211e19e 100644 --- a/src/backend/metal/src/native.rs +++ b/src/backend/metal/src/native.rs @@ -162,7 +162,7 @@ unsafe impl Sync for ComputePipeline {} #[derive(Clone, Debug)] pub struct Frame { pub swapchain: Arc>, - pub index: hal::FrameImage, + pub index: hal::SwapImageIndex, } #[derive(Clone, Debug)] @@ -199,7 +199,7 @@ pub enum ImageGuard<'a> { Texture(&'a metal::TextureRef), Frame { swapchain: RwLockReadGuard<'a, SwapchainInner>, - index: hal::FrameImage, + index: hal::SwapImageIndex, }, } diff --git a/src/backend/metal/src/window.rs b/src/backend/metal/src/window.rs index 99a5798b722..8434a5e146c 100644 --- a/src/backend/metal/src/window.rs +++ b/src/backend/metal/src/window.rs @@ -50,9 +50,9 @@ pub struct SwapchainInner { frames: Vec, } -impl ops::Index for SwapchainInner { +impl ops::Index for SwapchainInner { type Output = metal::TextureRef; - fn index(&self, index: hal::FrameImage) -> &Self::Output { + fn index(&self, index: hal::SwapImageIndex) -> &Self::Output { &self.frames[index as usize].texture } } @@ -88,7 +88,7 @@ unsafe impl Send for Swapchain {} unsafe impl Sync for Swapchain {} impl Swapchain { - pub(crate) fn present(&self, index: hal::FrameImage) { + pub(crate) fn present(&self, index: hal::SwapImageIndex) { let drawable = self.inner .write() .unwrap() @@ -122,7 +122,7 @@ impl hal::Surface for Surface { }; let caps = hal::SurfaceCapabilities { - image_count: 2 .. max_frames as hal::FrameImage, + image_count: 2 .. max_frames as hal::SwapImageIndex, current_extent: None, extents: Extent2D { width: 4, height: 4} .. Extent2D { width: 4096, height: 4096 }, max_image_layers: 1, @@ -256,7 +256,7 @@ impl Device { } impl hal::Swapchain for Swapchain { - fn acquire_frame(&mut self, sync: hal::FrameSync) -> Result { + fn acquire_image(&mut self, sync: hal::FrameSync) -> Result { let _ap = AutoreleasePool::new(); // for the drawable unsafe { diff --git a/src/backend/vulkan/src/lib.rs b/src/backend/vulkan/src/lib.rs index afbe44339f5..98f17577739 100644 --- a/src/backend/vulkan/src/lib.rs +++ b/src/backend/vulkan/src/lib.rs @@ -26,7 +26,7 @@ use ash::version::{EntryV1_0, DeviceV1_0, InstanceV1_0, V1_0}; use ash::vk; use hal::{format, image, memory, queue}; -use hal::{Features, FrameImage, Limits, PatchSize, QueueType}; +use hal::{Features, SwapImageIndex, Limits, PatchSize, QueueType}; use hal::error::{DeviceCreationError, HostExecutionError}; use std::{fmt, mem, ptr}; @@ -703,7 +703,7 @@ impl hal::queue::RawCommandQueue for CommandQueue { fn present(&mut self, swapchains: IS, wait_semaphores: IW) -> Result<(), ()> where - IS: IntoIterator, + IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow, diff --git a/src/backend/vulkan/src/window.rs b/src/backend/vulkan/src/window.rs index fc37c551c33..19d85462517 100644 --- a/src/backend/vulkan/src/window.rs +++ b/src/backend/vulkan/src/window.rs @@ -386,7 +386,7 @@ pub struct Swapchain { impl hal::Swapchain for Swapchain { - fn acquire_frame(&mut self, sync: hal::FrameSync) -> Result { + fn acquire_image(&mut self, sync: hal::FrameSync) -> Result { let (semaphore, fence) = match sync { hal::FrameSync::Semaphore(semaphore) => (semaphore.0, vk::Fence::null()), hal::FrameSync::Fence(fence) => (vk::Semaphore::null(), fence.0), diff --git a/src/hal/src/lib.rs b/src/hal/src/lib.rs index af81050e8f5..8fe5fc2b72d 100644 --- a/src/hal/src/lib.rs +++ b/src/hal/src/lib.rs @@ -36,7 +36,7 @@ pub use self::queue::{ Capability, Supports, General, Graphics, Compute, Transfer, }; pub use self::window::{ - Backbuffer, FrameImage, FrameSync, PresentMode, + Backbuffer, SwapImageIndex, FrameSync, PresentMode, Surface, SurfaceCapabilities, Swapchain, SwapchainConfig, }; diff --git a/src/hal/src/queue/mod.rs b/src/hal/src/queue/mod.rs index 3708b68532b..ca140b1430d 100644 --- a/src/hal/src/queue/mod.rs +++ b/src/hal/src/queue/mod.rs @@ -15,7 +15,7 @@ use std::borrow::Borrow; use std::marker::PhantomData; use error::HostExecutionError; -use window::FrameImage; +use window::SwapImageIndex; use Backend; pub use self::capability::{ @@ -65,7 +65,7 @@ pub trait RawCommandQueue: Any + Send + Sync { fn present(&mut self, swapchains: IS, wait_semaphores: IW) -> Result<(), ()> where Self: Sized, - IS: IntoIterator, + IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow; @@ -121,7 +121,7 @@ impl CommandQueue { /// list more than once. pub fn present(&mut self, swapchains: IS, wait_semaphores: IW) -> Result<(), ()> where - IS: IntoIterator, + IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow diff --git a/src/hal/src/window.rs b/src/hal/src/window.rs index 22ad0e10e83..3cc244a833b 100644 --- a/src/hal/src/window.rs +++ b/src/hal/src/window.rs @@ -34,7 +34,7 @@ //! let acquisition_semaphore = device.create_semaphore(); //! let render_semaphore = device.create_semaphore(); //! -//! let frame = swapchain.acquire_frame(FrameSync::Semaphore(&acquisition_semaphore)); +//! let frame = swapchain.acquire_image(FrameSync::Semaphore(&acquisition_semaphore)); //! // render the scene.. //! // `render_semaphore` will be signalled once rendering has been finished //! swapchain.present(&mut present_queue, 0, &[render_semaphore]); @@ -89,7 +89,7 @@ pub struct SurfaceCapabilities { /// /// - `image_count.start` must be at least 1. /// - `image_count.end` must be larger of equal to `image_count.start`. - pub image_count: Range, + pub image_count: Range, /// Current extent of the surface. /// @@ -138,9 +138,9 @@ pub trait Surface: Any + Send + Sync { /// /// The swapchain is a series of one or more images, usually /// with one being drawn on while the other is displayed by -/// the GPU (aka double-buffering). A `Frame` refers to a -/// particular image in the swapchain. -pub type FrameImage = u32; +/// the GPU (aka double-buffering). A `SwapImageIndex` refers +/// to a particular image in the swapchain. +pub type SwapImageIndex = u32; /// Synchronization primitives which will be signalled once a frame got retrieved. /// @@ -199,7 +199,7 @@ pub struct SwapchainConfig { /// Depth stencil format of the backbuffer images (optional). pub depth_stencil_format: Option, /// Number of images in the swapchain. - pub image_count: FrameImage, + pub image_count: SwapImageIndex, /// Image usage of the backbuffer images. pub image_usage: image::Usage, } @@ -301,7 +301,7 @@ pub enum Backbuffer { /// The `Swapchain` is the backend representation of the surface. /// It consists of multiple buffers, which will be presented on the surface. pub trait Swapchain: Any + Send + Sync { - /// Acquire a new frame for rendering. This needs to be called before presenting. + /// Acquire a new swapchain image for rendering. This needs to be called before presenting. /// /// Will fail if the swapchain needs recreation. /// @@ -317,9 +317,9 @@ pub trait Swapchain: Any + Send + Sync { /// ```no_run /// /// ``` - fn acquire_frame(&mut self, sync: FrameSync) -> Result; + fn acquire_image(&mut self, sync: FrameSync) -> Result; - /// Present one acquired frame. + /// Present one acquired image. /// /// # Safety /// @@ -334,7 +334,7 @@ pub trait Swapchain: Any + Send + Sync { fn present<'a, C, IW>( &'a self, present_queue: &mut CommandQueue, - frame_index: FrameImage, + image_index: SwapImageIndex, wait_semaphores: IW, ) -> Result<(), ()> where @@ -343,6 +343,6 @@ pub trait Swapchain: Any + Send + Sync { IW: IntoIterator, IW::Item: Borrow, { - present_queue.present(Some((self, frame_index)), wait_semaphores) + present_queue.present(Some((self, image_index)), wait_semaphores) } } diff --git a/src/window/dxgi/src/lib.rs b/src/window/dxgi/src/lib.rs index a3a9bb76b07..d361ada3255 100644 --- a/src/window/dxgi/src/lib.rs +++ b/src/window/dxgi/src/lib.rs @@ -215,7 +215,7 @@ impl core::Swapchain for Swapchain11 { &self.images } - fn acquire_frame(&mut self, sync: core::FrameSync) -> Result { + fn acquire_image(&mut self, sync: core::FrameSync) -> Result { // TODO: sync Ok(core::Frame::new(0)) } diff --git a/src/window/glfw/src/lib.rs b/src/window/glfw/src/lib.rs index cd62a2ec56c..2b42e85166b 100644 --- a/src/window/glfw/src/lib.rs +++ b/src/window/glfw/src/lib.rs @@ -54,7 +54,7 @@ impl<'a> core::Swapchain for Swapchain { &self.backbuffer } - fn acquire_frame(&mut self, sync: core::FrameSync) -> Result { + fn acquire_image(&mut self, sync: core::FrameSync) -> Result { // TODO: fence sync Ok(core::Frame::new(0)) } diff --git a/src/window/sdl/src/lib.rs b/src/window/sdl/src/lib.rs index 54e88c37ce5..4ea9f54708d 100644 --- a/src/window/sdl/src/lib.rs +++ b/src/window/sdl/src/lib.rs @@ -100,7 +100,7 @@ impl core::Swapchain for Swapchain { &self.backbuffer } - fn acquire_frame(&mut self, sync: core::FrameSync) -> Result { + fn acquire_image(&mut self, sync: core::FrameSync) -> Result { // TODO: fence sync Ok(core::Frame::new(0)) } From 79d0986f0f01722999d4c683bbc9332cb2f2388a Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 21 Jun 2018 15:00:30 -0400 Subject: [PATCH 3/6] [mtl] remove the deferred image resolution (yay) --- src/backend/metal/src/command.rs | 126 +++++++++---------------------- src/backend/metal/src/device.rs | 80 +++++++------------- src/backend/metal/src/native.rs | 80 ++------------------ src/backend/metal/src/soft.rs | 25 +++--- src/backend/metal/src/window.rs | 116 +++++++++++----------------- 5 files changed, 123 insertions(+), 304 deletions(-) diff --git a/src/backend/metal/src/command.rs b/src/backend/metal/src/command.rs index 6b7d9c62132..47158b9f799 100644 --- a/src/backend/metal/src/command.rs +++ b/src/backend/metal/src/command.rs @@ -533,7 +533,7 @@ impl State { #[derive(Clone, Debug)] struct StageResources { buffers: Vec>, - textures: Vec>, + textures: Vec>, samplers: Vec>, push_constants_buffer_id: Option, } @@ -562,12 +562,12 @@ impl StageResources { self.buffers[slot] = Some((buffer.to_owned(), offset)); } - fn add_textures(&mut self, start: usize, roots: &[Option<(native::ImageRoot, Layout)>]) { - while self.textures.len() < start + roots.len() { + fn add_textures(&mut self, start: usize, textures: &[Option<(metal::Texture, Layout)>]) { + while self.textures.len() < start + textures.len() { self.textures.push(None) } - for (out, root) in self.textures[start..].iter_mut().zip(roots.iter()) { - *out = root.as_ref().map(|&(ref root, _)| root.clone()); + for (out, tex) in self.textures[start..].iter_mut().zip(textures.iter()) { + *out = tex.as_ref().map(|&(ref t, _)| t.to_owned()); } } @@ -748,14 +748,12 @@ impl CommandSink { } } - fn begin_render_pass<'a, F, I>( + fn begin_render_pass<'a, I>( &mut self, keep_open: bool, descriptor: &'a metal::RenderPassDescriptorRef, - frames: F, init_commands: I, ) where - F: Iterator, I: Iterator>, { self.stop_encoding(); @@ -763,7 +761,6 @@ impl CommandSink { match *self { CommandSink::Immediate { ref cmd_buffer, ref mut encoder_state, .. } => { let _ap = AutoreleasePool::new(); - resolve_frames(descriptor, frames); let encoder = cmd_buffer.new_render_command_encoder(descriptor); for command in init_commands { exec_render(encoder, command); @@ -778,7 +775,6 @@ impl CommandSink { *is_encoding = keep_open; passes.push(soft::Pass::Render { desc: descriptor.to_owned(), - frames: frames.collect(), commands: init_commands.map(soft::RenderCommand::own).collect(), }); } @@ -960,14 +956,6 @@ fn exec_render<'a>(encoder: &metal::RenderCommandEncoderRef, command: soft::Rend } } Cmd::BindTexture { stage, index, texture } => { - let guard; - let texture = match texture { - Some(root) => { - guard = root.resolve(); - Some(&*guard) - } - None => None, - }; match stage { pso::Stage::Vertex => encoder.set_vertex_texture(index as _, texture), @@ -1081,12 +1069,12 @@ fn exec_blit<'a>(encoder: &metal::BlitCommandEncoderRef, command: soft::BlitComm let layers = region.src_subresource.layers.zip(region.dst_subresource.layers); for (src_layer, dst_layer) in layers { encoder.copy_from_texture( - &*src.resolve(), + src, src_layer as _, region.src_subresource.level as _, src_offset, size, - &*dst.resolve(), + dst, dst_layer as _, region.dst_subresource.level as _, dst_offset, @@ -1107,7 +1095,7 @@ fn exec_blit<'a>(encoder: &metal::BlitCommandEncoderRef, command: soft::BlitComm row_pitch as NSUInteger, slice_pitch as NSUInteger, extent, - &*dst.resolve(), + dst, layer as NSUInteger, r.level as NSUInteger, origin, @@ -1124,7 +1112,7 @@ fn exec_blit<'a>(encoder: &metal::BlitCommandEncoderRef, command: soft::BlitComm for layer in r.layers.clone() { let offset = region.buffer_offset + slice_pitch as NSUInteger * (layer - r.layers.start) as NSUInteger; encoder.copy_from_texture_to_buffer( - &*src.resolve(), + src, layer as NSUInteger, r.level as NSUInteger, origin, @@ -1150,14 +1138,6 @@ fn exec_compute<'a>(encoder: &metal::ComputeCommandEncoderRef, command: soft::Co encoder.set_bytes(index as _, (words.len() * WORD_SIZE) as u64, words.as_ptr() as _); } Cmd::BindTexture { index, texture } => { - let guard; - let texture = match texture { - Some(ref root) => { - guard = root.resolve(); - Some(&*guard) - } - None => None, - }; encoder.set_texture(index as _, texture); } Cmd::BindSampler { index, sampler } => { @@ -1175,28 +1155,11 @@ fn exec_compute<'a>(encoder: &metal::ComputeCommandEncoderRef, command: soft::Co } } -fn resolve_frames(desc: &metal::RenderPassDescriptorRef, frames: I) -where - I: IntoIterator, - I::Item: Borrow<(usize, native::Frame)>, -{ - for f in frames { - let (index, ref frame) = *f.borrow(); - let swapchain = frame.swapchain.read().unwrap(); - desc - .color_attachments() - .object_at(index as _) - .unwrap() - .set_texture(Some(&swapchain[frame.index])) - } -} - fn record_commands(command_buf: &metal::CommandBufferRef, passes: &[soft::Pass]) { let _ap = AutoreleasePool::new(); // for encoder creation for pass in passes { match *pass { - soft::Pass::Render { ref desc, ref frames, ref commands } => { - resolve_frames(desc, frames); + soft::Pass::Render { ref desc, ref commands } => { let encoder = command_buf.new_render_command_encoder(desc); for command in commands { exec_render(&encoder, command.as_ref()); @@ -1618,7 +1581,6 @@ impl com::RawCommandBuffer for CommandBuffer { for subresource_range in subresource_ranges { let sub = subresource_range.borrow(); - let mut frame = None; let num_layers = (sub.layers.end - sub.layers.start) as u64; let layers = if CLEAR_IMAGE_ARRAY { 0 .. 1 @@ -1626,14 +1588,13 @@ impl com::RawCommandBuffer for CommandBuffer { sub.layers.clone() }; let texture = if CLEAR_IMAGE_ARRAY && sub.layers.start > 0 { - let image_raw = image.root.as_ref().resolve(); // aliasing is necessary for bulk-clearing all layers starting with 0 - let tex = image_raw.new_texture_view_from_slice( + let tex = image.raw.new_texture_view_from_slice( image.mtl_format, image.mtl_type, NSRange { location: 0, - length: image_raw.mipmap_level_count(), + length: image.raw.mipmap_level_count(), }, NSRange { location: sub.layers.start as _, @@ -1641,15 +1602,9 @@ impl com::RawCommandBuffer for CommandBuffer { }, ); retained_textures.push(tex); - retained_textures.last().map(|tex| tex.as_ref()) + retained_textures.last().unwrap() } else { - match image.root { - native::ImageRoot::Texture(ref tex) => Some(tex.as_ref()), - native::ImageRoot::Frame(ref f) => { - frame = Some((0usize, f.clone())); - None - } - } + &*image.raw }; for layer in layers { @@ -1669,7 +1624,7 @@ impl com::RawCommandBuffer for CommandBuffer { .color_attachments() .object_at(0) .unwrap(); - attachment.set_texture(texture); + attachment.set_texture(Some(texture)); attachment.set_level(level as _); attachment.set_store_action(metal::MTLStoreAction::Store); if !CLEAR_IMAGE_ARRAY { @@ -1688,7 +1643,7 @@ impl com::RawCommandBuffer for CommandBuffer { let attachment = descriptor .depth_attachment() .unwrap(); - attachment.set_texture(texture); + attachment.set_texture(Some(texture)); attachment.set_level(level as _); attachment.set_store_action(metal::MTLStoreAction::Store); if !CLEAR_IMAGE_ARRAY { @@ -1707,7 +1662,7 @@ impl com::RawCommandBuffer for CommandBuffer { let attachment = descriptor .stencil_attachment() .unwrap(); - attachment.set_texture(texture); + attachment.set_texture(Some(texture)); attachment.set_level(level as _); attachment.set_store_action(metal::MTLStoreAction::Store); if !CLEAR_IMAGE_ARRAY { @@ -1723,7 +1678,7 @@ impl com::RawCommandBuffer for CommandBuffer { sink.as_mut() .unwrap() - .begin_render_pass(false, descriptor, frame.clone().into_iter(), None.into_iter()); + .begin_render_pass(false, descriptor, None.into_iter()); // no actual pass body - everything is in the attachment clear operations } } @@ -1964,15 +1919,6 @@ impl com::RawCommandBuffer for CommandBuffer { let vertices = &mut self.temp.blit_vertices; vertices.clear(); - let mut frame = None; - let dst_texture = match dst.root { - native::ImageRoot::Texture(ref tex) => Some(tex.as_ref()), - native::ImageRoot::Frame(ref f) => { - frame = Some((0usize, f.clone())); - None - } - }; - for region in regions { let r = region.borrow(); @@ -2077,7 +2023,7 @@ impl com::RawCommandBuffer for CommandBuffer { soft::RenderCommand::BindTexture { stage: pso::Stage::Fragment, index: 0, - texture: Some(src.root.as_ref()) + texture: Some(&*src.raw) }, ]; @@ -2132,21 +2078,21 @@ impl com::RawCommandBuffer for CommandBuffer { .color_attachments() .object_at(0) .unwrap(); - attachment.set_texture(dst_texture); + attachment.set_texture(Some(&dst.raw)); attachment.set_level(level as _); } if aspects.contains(Aspects::DEPTH) { let attachment = descriptor .depth_attachment() .unwrap(); - attachment.set_texture(dst_texture); + attachment.set_texture(Some(&dst.raw)); attachment.set_level(level as _); } if aspects.contains(Aspects::STENCIL) { let attachment = descriptor .stencil_attachment() .unwrap(); - attachment.set_texture(dst_texture); + attachment.set_texture(Some(&dst.raw)); attachment.set_level(level as _); } @@ -2156,7 +2102,7 @@ impl com::RawCommandBuffer for CommandBuffer { .chain(&extra) .cloned(); - inner.sink().begin_render_pass(false, descriptor, frame.clone().into_iter(), commands); + inner.sink().begin_render_pass(false, descriptor, commands); } } @@ -2380,14 +2326,10 @@ impl com::RawCommandBuffer for CommandBuffer { }; self.state.framebuffer_inner = framebuffer.inner.clone(); - let frames = framebuffer.inner.colors - .iter() - .enumerate() - .filter_map(|(index, ref cat)| cat.frame.clone().map(|f| (index, f))); let init_commands = self.state.make_render_commands(full_aspects); inner .sink() - .begin_render_pass(true, &descriptor, frames, init_commands); + .begin_render_pass(true, &descriptor, init_commands); } fn next_subpass(&mut self, _contents: com::SubpassContents) { @@ -2918,18 +2860,18 @@ impl com::RawCommandBuffer for CommandBuffer { } = *self.inner.borrow_mut(); let new_src = if src.mtl_format == dst.mtl_format { - src.root.clone() + &*src.raw } else { assert_eq!(src.format_desc.bits, dst.format_desc.bits); - let tex = src.root.as_ref().resolve().new_texture_view(dst.mtl_format); - retained_textures.push(tex.clone()); - native::ImageRoot::Texture(tex) + let tex = src.raw.new_texture_view(dst.mtl_format); + retained_textures.push(tex); + retained_textures.last().unwrap() }; let commands = regions.into_iter().map(|region| { soft::BlitCommand::CopyImage { - src: new_src.as_ref(), - dst: dst.root.as_ref(), + src: new_src, + dst: &*dst.raw, region: region.borrow().clone(), } }); @@ -2952,7 +2894,7 @@ impl com::RawCommandBuffer for CommandBuffer { let commands = regions.into_iter().map(|region| { soft::BlitCommand::CopyBufferToImage { src: &*src.raw, - dst: dst.root.as_ref(), + dst: &*dst.raw, dst_desc: dst.format_desc, region: region.borrow().clone(), } @@ -2976,9 +2918,9 @@ impl com::RawCommandBuffer for CommandBuffer { // FIXME: layout let commands = regions.into_iter().map(|region| { soft::BlitCommand::CopyImageToBuffer { - src: src.root.as_ref(), + src: &*src.raw, src_desc: src.format_desc, - dst: dst.raw.as_ref(), + dst: &*dst.raw, region: region.borrow().clone(), } }); diff --git a/src/backend/metal/src/device.rs b/src/backend/metal/src/device.rs index afe6622b0ae..dc6e68227db 100644 --- a/src/backend/metal/src/device.rs +++ b/src/backend/metal/src/device.rs @@ -1132,28 +1132,15 @@ impl hal::Device for Device { inner.aspects |= aspects; let at = attachment.borrow(); - let texture = match at.root { - native::ImageRoot::Texture(ref tex) => tex, - native::ImageRoot::Frame(ref frame) => { - // we don't have the actual MTLTexture for the frame at this point - inner.colors.push(native::ColorAttachment { - mtl_format: at.mtl_format, - channel: format.base_format().1.into(), - frame: Some(frame.clone()), - }); - continue; - } - }; if aspects.contains(format::Aspects::COLOR) { descriptor .color_attachments() .object_at(inner.colors.len()) .expect("too many color attachments") - .set_texture(Some(texture)); + .set_texture(Some(&at.raw)); inner.colors.push(native::ColorAttachment { mtl_format: at.mtl_format, channel: format.base_format().1.into(), - frame: None, }); } if aspects.contains(format::Aspects::DEPTH) { @@ -1162,7 +1149,7 @@ impl hal::Device for Device { descriptor .depth_attachment() .unwrap() - .set_texture(Some(texture)); + .set_texture(Some(&at.raw)); } if aspects.contains(format::Aspects::STENCIL) { if let Some(old_format) = inner.depth_stencil { @@ -1173,7 +1160,7 @@ impl hal::Device for Device { descriptor .stencil_attachment() .unwrap() - .set_texture(Some(texture)); + .set_texture(Some(&at.raw)); } } @@ -1445,18 +1432,17 @@ impl hal::Device for Device { vec[array_offset] = Some(sampler.0.clone()); } (&pso::Descriptor::Image(image, layout), &mut n::DescriptorSetBinding::Image(ref mut vec)) => { - vec[array_offset] = Some((image.root.clone(), layout)); + vec[array_offset] = Some((image.raw.clone(), layout)); } (&pso::Descriptor::Image(image, layout), &mut n::DescriptorSetBinding::Combined(ref mut vec)) => { - vec[array_offset].0 = Some((image.root.clone(), layout)); + vec[array_offset].0 = Some((image.raw.clone(), layout)); } (&pso::Descriptor::CombinedImageSampler(image, layout, sampler), &mut n::DescriptorSetBinding::Combined(ref mut vec)) => { - vec[array_offset] = (Some((image.root.clone(), layout)), Some(sampler.0.clone())); + vec[array_offset] = (Some((image.raw.clone(), layout)), Some(sampler.0.clone())); } (&pso::Descriptor::UniformTexelBuffer(view), &mut n::DescriptorSetBinding::Image(ref mut vec)) | (&pso::Descriptor::StorageTexelBuffer(view), &mut n::DescriptorSetBinding::Image(ref mut vec)) => { - let root = native::ImageRoot::Texture(view.raw.clone()); - vec[array_offset] = Some((root, image::Layout::General)); + vec[array_offset] = Some((view.raw.clone(), image::Layout::General)); } (&pso::Descriptor::Buffer(buffer, ref range), &mut n::DescriptorSetBinding::Buffer(ref mut vec)) => { let buf_length = buffer.raw.length(); @@ -1489,8 +1475,7 @@ impl hal::Device for Device { encoder.set_sampler_states(&[&sampler.0], write.binding as _); } pso::Descriptor::Image(image, _layout) => { - let guard = image.root.as_ref().resolve(); - encoder.set_textures(&[&*guard], write.binding as _); + encoder.set_textures(&[&image.raw], write.binding as _); } pso::Descriptor::Buffer(buffer, ref range) => { encoder.set_buffer(&buffer.raw, range.start.unwrap_or(0), write.binding as _); @@ -1907,7 +1892,7 @@ impl hal::Device for Device { }; Ok(n::Image { - root: native::ImageRoot::Texture(raw), + raw, extent: image.extent, num_layers: image.num_layers, format_desc, @@ -1942,35 +1927,28 @@ impl hal::Device for Device { }, }; - let root = match image.root { - native::ImageRoot::Texture(ref raw) => { - let view = raw.new_texture_view_from_slice( - mtl_format, - conv::map_texture_type(kind), - NSRange { - location: range.levels.start as _, - length: (range.levels.end - range.levels.start) as _, - }, - NSRange { - location: range.layers.start as _, - length: (range.layers.end - range.layers.start) as _, - }, - ); - native::ImageRoot::Texture(view) - } - native::ImageRoot::Frame(ref frame) => { - assert_eq!(mtl_format, image.mtl_format); - assert_eq!(kind, image::ViewKind::D2); - assert_eq!(range, image::SubresourceRange { - aspects: format::Aspects::COLOR, - levels: 0 .. 1, - layers: 0 .. 1, - }); - native::ImageRoot::Frame(frame.clone()) - } + let view = if mtl_format == image.mtl_format && kind == image::ViewKind::D2 && + swizzle == format::Swizzle::NO && image.num_layers.is_none() + { + // Some images are marked as framebuffer-only, and we can't create aliases of them + //TODO: check more things? + image.raw.clone() + } else { + image.raw.new_texture_view_from_slice( + mtl_format, + conv::map_texture_type(kind), + NSRange { + location: range.levels.start as _, + length: (range.levels.end - range.levels.start) as _, + }, + NSRange { + location: range.layers.start as _, + length: (range.layers.end - range.layers.start) as _, + }, + ) }; - Ok(n::ImageView { root, mtl_format }) + Ok(n::ImageView { raw: view, mtl_format }) } fn destroy_image_view(&self, _view: n::ImageView) { diff --git a/src/backend/metal/src/native.rs b/src/backend/metal/src/native.rs index 26e0211e19e..f7b47e4e190 100644 --- a/src/backend/metal/src/native.rs +++ b/src/backend/metal/src/native.rs @@ -1,11 +1,10 @@ use Backend; use internal::Channel; -use window::SwapchainInner; use std::collections::HashMap; -use std::ops::{Deref, Range}; +use std::ops::Range; use std::os::raw::{c_void, c_long}; -use std::sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard}; +use std::sync::{Arc, Condvar, Mutex}; use hal::{self, image, pso}; use hal::backend::FastHashMap; @@ -44,7 +43,6 @@ unsafe impl Sync for RenderPass {} pub struct ColorAttachment { pub mtl_format: metal::MTLPixelFormat, pub channel: Channel, - pub frame: Option, } #[derive(Clone, Debug)] @@ -159,75 +157,9 @@ pub struct ComputePipeline { unsafe impl Send for ComputePipeline {} unsafe impl Sync for ComputePipeline {} -#[derive(Clone, Debug)] -pub struct Frame { - pub swapchain: Arc>, - pub index: hal::SwapImageIndex, -} - -#[derive(Clone, Debug)] -pub enum ImageRoot { - Texture(metal::Texture), - Frame(Frame), -} - -#[derive(Clone)] -pub enum ImageRootRef<'a> { - Texture(&'a metal::TextureRef), - Frame(&'a Frame), -} - -impl ImageRoot { - pub(crate) fn as_ref(&self) -> ImageRootRef { - match *self { - ImageRoot::Texture(ref tex) => ImageRootRef::Texture(tex), - ImageRoot::Frame(ref frame) => ImageRootRef::Frame(frame), - } - } -} - -impl<'a> ImageRootRef<'a> { - pub fn own(self) -> ImageRoot { - match self { - ImageRootRef::Texture(tex) => ImageRoot::Texture(tex.to_owned()), - ImageRootRef::Frame(frame) => ImageRoot::Frame(frame.clone()), - } - } -} - -pub enum ImageGuard<'a> { - Texture(&'a metal::TextureRef), - Frame { - swapchain: RwLockReadGuard<'a, SwapchainInner>, - index: hal::SwapImageIndex, - }, -} - -impl<'a> Deref for ImageGuard<'a> { - type Target = metal::TextureRef; - fn deref(&self) -> &Self::Target { - match *self { - ImageGuard::Texture(tex) => tex, - ImageGuard::Frame { ref swapchain, index } => &swapchain[index], - } - } -} - -impl<'a> ImageRootRef<'a> { - pub fn resolve(&self) -> ImageGuard<'a> { - match *self { - ImageRootRef::Texture(ref tex) => ImageGuard::Texture(tex), - ImageRootRef::Frame(ref frame) => ImageGuard::Frame { - swapchain: frame.swapchain.read().unwrap(), - index: frame.index, - }, - } - } -} - #[derive(Debug)] pub struct Image { - pub(crate) root: ImageRoot, + pub(crate) raw: metal::Texture, pub(crate) extent: image::Extent, pub(crate) num_layers: Option, pub(crate) format_desc: FormatDesc, @@ -264,7 +196,7 @@ unsafe impl Sync for BufferView {} #[derive(Debug)] pub struct ImageView { - pub(crate) root: ImageRoot, + pub(crate) raw: metal::Texture, pub(crate) mtl_format: metal::MTLPixelFormat, } @@ -474,8 +406,8 @@ pub struct BufferBinding { #[derive(Clone, Debug)] pub enum DescriptorSetBinding { Sampler(Vec>), - Image(Vec>), - Combined(Vec<(Option<(ImageRoot, image::Layout)>, Option)>), + Image(Vec>), + Combined(Vec<(Option<(metal::Texture, image::Layout)>, Option)>), Buffer(Vec), //InputAttachment(Vec<(metal::Texture, image::Layout)>), } diff --git a/src/backend/metal/src/soft.rs b/src/backend/metal/src/soft.rs index 9acc5953594..abf9933c4ab 100644 --- a/src/backend/metal/src/soft.rs +++ b/src/backend/metal/src/soft.rs @@ -1,5 +1,5 @@ -use command::{IndexBuffer}; -use native::{Frame, ImageRoot, ImageRootRef, RasterizerState}; +use command::IndexBuffer; +use native::RasterizerState; use hal; use metal; @@ -22,7 +22,7 @@ pub enum Own {} impl Resources for Own { type Data = Vec; type Buffer = metal::Buffer; - type Texture = ImageRoot; + type Texture = metal::Texture; type Sampler = metal::SamplerState; type DepthStencil = metal::DepthStencilState; type RenderPipeline = metal::RenderPipelineState; @@ -31,7 +31,7 @@ impl Resources for Own { impl<'a> Resources for &'a Own { type Data = &'a [u32]; type Buffer = &'a metal::BufferRef; - type Texture = ImageRootRef<'a>; + type Texture = &'a metal::TextureRef; type Sampler = &'a metal::SamplerStateRef; type DepthStencil = &'a metal::DepthStencilStateRef; type RenderPipeline = &'a metal::RenderPipelineStateRef; @@ -122,7 +122,7 @@ impl RenderCommand { BindTexture { stage, index, ref texture } => BindTexture { stage, index, - texture: texture.as_ref().map(ImageRoot::as_ref), + texture: texture.as_ref().map(Borrow::borrow), }, BindSampler { stage, index, ref sampler } => BindSampler { stage, @@ -181,7 +181,7 @@ impl<'a> RenderCommand<&'a Own> { BindTexture { stage, index, texture } => BindTexture { stage, index, - texture: texture.map(ImageRootRef::own), + texture: texture.map(ToOwned::to_owned), }, BindSampler { stage, index, sampler } => BindSampler { stage, @@ -283,18 +283,18 @@ impl<'a> BlitCommand<&'a Own> { region, }, CopyImage { src, dst, region } => CopyImage { - src: src.own(), - dst: dst.own(), + src: src.to_owned(), + dst: dst.to_owned(), region, }, CopyBufferToImage { src, dst, dst_desc, region } => CopyBufferToImage { src: src.to_owned(), - dst: dst.own(), + dst: dst.to_owned(), dst_desc, region, }, CopyImageToBuffer { src, src_desc, dst, region } => CopyImageToBuffer { - src: src.own(), + src: src.to_owned(), src_desc, dst: dst.to_owned(), region, @@ -351,7 +351,7 @@ impl ComputeCommand { }, BindTexture { index, ref texture } => BindTexture { index, - texture: texture.as_ref().map(ImageRoot::as_ref), + texture: texture.as_ref().map(Borrow::borrow), }, BindSampler { index, ref sampler } => BindSampler { index, @@ -386,7 +386,7 @@ impl<'a> ComputeCommand<&'a Own> { }, BindTexture { index, texture } => BindTexture { index, - texture: texture.map(ImageRootRef::own), + texture: texture.map(ToOwned::to_owned), }, BindSampler { index, sampler } => BindSampler { index, @@ -411,7 +411,6 @@ impl<'a> ComputeCommand<&'a Own> { pub enum Pass { Render { desc: metal::RenderPassDescriptor, - frames: Vec<(usize, Frame)>, commands: Vec>, }, Blit(Vec>), diff --git a/src/backend/metal/src/window.rs b/src/backend/metal/src/window.rs index 8434a5e146c..217649a7f54 100644 --- a/src/backend/metal/src/window.rs +++ b/src/backend/metal/src/window.rs @@ -3,8 +3,8 @@ use internal::Channel; use native; use device::{Device, PhysicalDevice}; -use std::{fmt, ops}; -use std::sync::{Arc, Mutex, RwLock}; +use std::cell::Cell; +use std::sync::{Arc, Mutex}; use hal::{self, format, image}; use hal::{Backbuffer, SwapchainConfig}; @@ -26,6 +26,7 @@ pub struct Surface { pub(crate) apply_pixel_scale: bool, } +//TODO: double-check who needs it shared pub(crate) struct SurfaceInner { pub(crate) nsview: *mut Object, pub(crate) render_layer: Mutex, @@ -42,44 +43,22 @@ impl Drop for SurfaceInner { #[derive(Debug)] struct Frame { - drawable: Option, + drawable: Cell>, texture: metal::Texture, } -pub struct SwapchainInner { - frames: Vec, -} - -impl ops::Index for SwapchainInner { - type Output = metal::TextureRef; - fn index(&self, index: hal::SwapImageIndex) -> &Self::Output { - &self.frames[index as usize].texture - } -} - -unsafe impl Send for SwapchainInner {} -unsafe impl Sync for SwapchainInner {} - -impl fmt::Debug for SwapchainInner { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Swapchain with {} image", self.frames.len()) - } -} - -impl Drop for SwapchainInner { +impl Drop for Frame { fn drop(&mut self) { - for mut frame in self.frames.drain(..) { - if let Some(drawable) = frame.drawable.take() { - unsafe { - msg_send![drawable, release]; - } + if let Some(drawable) = self.drawable.get() { + unsafe { + msg_send![drawable, release]; } } } } pub struct Swapchain { - inner: Arc>, + frames: Vec, surface: Arc, _size_pixels: (u64, u64), } @@ -89,12 +68,8 @@ unsafe impl Sync for Swapchain {} impl Swapchain { pub(crate) fn present(&self, index: hal::SwapImageIndex) { - let drawable = self.inner - .write() - .unwrap() - .frames[index as usize] - .drawable - .take() + let drawable = self.frames[index as usize].drawable + .replace(None) .unwrap(); unsafe { msg_send![drawable, present]; @@ -115,14 +90,9 @@ impl hal::Surface for Surface { fn compatibility( &self, _: &PhysicalDevice, ) -> (hal::SurfaceCapabilities, Option>, Vec) { - let render_layer_borrow = self.inner.render_layer.lock().unwrap(); - let render_layer = *render_layer_borrow; - let max_frames: u64 = unsafe { - msg_send![render_layer, maximumDrawableCount] - }; - let caps = hal::SurfaceCapabilities { - image_count: 2 .. max_frames as hal::SwapImageIndex, + //Note: this is hardcoded in `CAMetalLayer` documentation + image_count: 2 .. 3, current_extent: None, extents: Extent2D { width: 4, height: 4} .. Extent2D { width: 4096, height: 4096 }, max_image_layers: 1, @@ -142,7 +112,8 @@ impl hal::Surface for Surface { } fn supports_queue_family(&self, _queue_family: &QueueFamily) -> bool { - true // TODO: Not sure this is the case, don't know associativity of IOSurface + // we only expose one family atm, so it's compatible + true } } @@ -211,33 +182,23 @@ impl Device { let pixel_width = (view_size.width * scale_factor) as u64; let pixel_height = (view_size.height * scale_factor) as u64; - let inner = SwapchainInner { - frames: (0 .. config.image_count) - .map(|_| unsafe { - let drawable: *mut Object = msg_send![render_layer, nextDrawable]; - assert!(!drawable.is_null()); - let texture: metal::Texture = msg_send![drawable, texture]; - //HACK: not retaining the texture here - Frame { - drawable: None, //Note: careful! - texture, - } - }) - .collect(), - }; - - let swapchain = Swapchain { - inner: Arc::new(RwLock::new(inner)), - surface: surface.inner.clone(), - _size_pixels: (pixel_width, pixel_height), - }; + let frames = (0 .. config.image_count) + .map(|_| unsafe { + let drawable: *mut Object = msg_send![render_layer, nextDrawable]; + assert!(!drawable.is_null()); + let texture: metal::Texture = msg_send![drawable, texture]; + //HACK: not retaining the texture here + Frame { + drawable: Cell::new(None), //Note: careful! + texture, + } + }) + .collect::>(); - let images = (0 .. config.image_count) - .map(|index| native::Image { - root: native::ImageRoot::Frame(native::Frame { - swapchain: swapchain.inner.clone(), - index, - }), + let images = frames + .iter() + .map(|frame| native::Image { + raw: frame.texture.clone(), //Note: careful! extent: image::Extent { width: pixel_width as _, height: pixel_height as _, @@ -251,6 +212,12 @@ impl Device { }) .collect(); + let swapchain = Swapchain { + frames, + surface: surface.inner.clone(), + _size_pixels: (pixel_width, pixel_height), + }; + (swapchain, Backbuffer::Images(images)) } } @@ -277,12 +244,13 @@ impl hal::Swapchain for Swapchain { msg_send![drawable, retain]; msg_send![drawable, texture] }; - let mut inner = self.inner.write().unwrap(); - let index = inner.frames + + let index = self.frames .iter() .position(|f| f.texture.as_ptr() == texture_temp.as_ptr()) - .expect(&format!("Surface lost? ptr {:?}, frames {:?}", texture_temp, inner.frames)); - inner.frames[index].drawable = Some(drawable); + .expect("Surface lost?"); + let old = self.frames[index].drawable.replace(Some(drawable)); + assert_eq!(old, None); Ok(index as _) } From 1cedfc22a422639d7905ccd72ab6bf5dd2672890 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 21 Jun 2018 16:01:49 -0400 Subject: [PATCH 4/6] [mtl] use metal::Drawable and present via command buffers --- src/backend/metal/src/command.rs | 8 +++++- src/backend/metal/src/window.rs | 48 ++++++++++---------------------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/backend/metal/src/command.rs b/src/backend/metal/src/command.rs index 47158b9f799..b9186bf50db 100644 --- a/src/backend/metal/src/command.rs +++ b/src/backend/metal/src/command.rs @@ -1290,12 +1290,18 @@ impl RawCommandQueue for CommandQueue { IW: IntoIterator, IW::Item: Borrow, { + let queue = self.shared.queue.lock().unwrap(); + let command_buffer = queue.raw.new_command_buffer(); + for (swapchain, index) in swapchains { // TODO: wait for semaphores debug!("presenting frame {}", index); - swapchain.borrow().present(index); + let drawable = swapchain.borrow().take_drawable(index); + command_buffer.present_drawable(&drawable); } + command_buffer.commit(); + let shared_capture_manager = CaptureManager::shared(); if shared_capture_manager.is_capturing() { shared_capture_manager.stop_capture(); diff --git a/src/backend/metal/src/window.rs b/src/backend/metal/src/window.rs index 217649a7f54..614424c682d 100644 --- a/src/backend/metal/src/window.rs +++ b/src/backend/metal/src/window.rs @@ -3,7 +3,7 @@ use internal::Channel; use native; use device::{Device, PhysicalDevice}; -use std::cell::Cell; +use std::mem; use std::sync::{Arc, Mutex}; use hal::{self, format, image}; @@ -19,7 +19,6 @@ use foreign_types::{ForeignType, ForeignTypeRef}; pub type CAMetalLayer = *mut Object; -pub type CADrawable = *mut Object; pub struct Surface { pub(crate) inner: Arc, @@ -43,20 +42,10 @@ impl Drop for SurfaceInner { #[derive(Debug)] struct Frame { - drawable: Cell>, + drawable: Mutex>, texture: metal::Texture, } -impl Drop for Frame { - fn drop(&mut self) { - if let Some(drawable) = self.drawable.get() { - unsafe { - msg_send![drawable, release]; - } - } - } -} - pub struct Swapchain { frames: Vec, surface: Arc, @@ -67,15 +56,12 @@ unsafe impl Send for Swapchain {} unsafe impl Sync for Swapchain {} impl Swapchain { - pub(crate) fn present(&self, index: hal::SwapImageIndex) { - let drawable = self.frames[index as usize].drawable - .replace(None) - .unwrap(); - unsafe { - msg_send![drawable, present]; - //TODO: delay the actual release - msg_send![drawable, release]; - } + pub(crate) fn take_drawable(&self, index: hal::SwapImageIndex) -> metal::Drawable { + self.frames[index as usize].drawable + .lock() + .unwrap() + .take() + .expect("Drawable has not been acquired!") } } @@ -184,12 +170,11 @@ impl Device { let frames = (0 .. config.image_count) .map(|_| unsafe { - let drawable: *mut Object = msg_send![render_layer, nextDrawable]; - assert!(!drawable.is_null()); + let drawable: &metal::DrawableRef = msg_send![render_layer, nextDrawable]; let texture: metal::Texture = msg_send![drawable, texture]; //HACK: not retaining the texture here Frame { - drawable: Cell::new(None), //Note: careful! + drawable: Mutex::new(None), texture, } }) @@ -237,20 +222,17 @@ impl hal::Swapchain for Swapchain { } let layer_ref = self.surface.render_layer.lock().unwrap(); - let drawable: CADrawable = unsafe { - msg_send![*layer_ref, nextDrawable] - }; - let texture_temp: &metal::TextureRef = unsafe { - msg_send![drawable, retain]; - msg_send![drawable, texture] + let (drawable, texture_temp): (metal::Drawable, &metal::TextureRef) = unsafe { + let drawable: &metal::DrawableRef = msg_send![*layer_ref, nextDrawable]; + (drawable.to_owned(), msg_send![drawable, texture]) }; let index = self.frames .iter() .position(|f| f.texture.as_ptr() == texture_temp.as_ptr()) .expect("Surface lost?"); - let old = self.frames[index].drawable.replace(Some(drawable)); - assert_eq!(old, None); + let old = mem::replace(&mut *self.frames[index].drawable.lock().unwrap(), Some(drawable)); + assert!(old.is_none()); Ok(index as _) } From 3d7d45c3c62b8ec13bb5b8189b03172427dc8b9a Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 21 Jun 2018 22:44:30 -0400 Subject: [PATCH 5/6] [mtl] improved semaphore synchronization --- src/backend/metal/src/command.rs | 40 ++++++-- src/backend/metal/src/device.rs | 19 +++- src/backend/metal/src/lib.rs | 1 + src/backend/metal/src/native.rs | 48 +++++++-- src/backend/metal/src/window.rs | 164 +++++++++++++++++++++++++------ 5 files changed, 220 insertions(+), 52 deletions(-) diff --git a/src/backend/metal/src/command.rs b/src/backend/metal/src/command.rs index b9186bf50db..91b3672ce72 100644 --- a/src/backend/metal/src/command.rs +++ b/src/backend/metal/src/command.rs @@ -1197,6 +1197,22 @@ impl CommandQueue { shared, } } + + fn wait(&mut self, wait_semaphores: I) + where + I: IntoIterator, + I::Item: Borrow, + { + for semaphore in wait_semaphores { + let sem = semaphore.borrow(); + if let Some(ref system) = sem.system { + system.wait(!0); + } + if let Some(swap_image) = sem.image_ready.lock().unwrap().take() { + swap_image.wait_until_ready(); + } + } + } } impl RawCommandQueue for CommandQueue { @@ -1208,17 +1224,20 @@ impl RawCommandQueue for CommandQueue { IC::Item: Borrow, { debug!("submitting with fence {:?}", fence); - // FIXME: wait for semaphores! - // FIXME: multiple buffers signaling! - let signal_block = if !submit.signal_semaphores.is_empty() { - let semaphores_copy: Vec<_> = submit.signal_semaphores.iter().map(|semaphore| { - semaphore.0 - }).collect(); + self.wait(submit.wait_semaphores.iter().map(|&(s, _)| s)); + + let system_semaphores = submit.signal_semaphores + .into_iter() + .filter_map(|semaphore| { + semaphore.system.clone() + }) + .collect::>(); + let signal_block = if !system_semaphores.is_empty() { //Note: careful with those `ConcreteBlock::copy()` calls! Some(ConcreteBlock::new(move |_cb: *mut ()| -> () { - for semaphore in semaphores_copy.iter() { - native::dispatch_semaphore_signal(*semaphore); + for semaphore in &system_semaphores { + semaphore.signal(); } }).copy()) } else { @@ -1283,18 +1302,19 @@ impl RawCommandQueue for CommandQueue { } } - fn present(&mut self, swapchains: IS, _wait_semaphores: IW) -> Result<(), ()> + fn present(&mut self, swapchains: IS, wait_semaphores: IW) -> Result<(), ()> where IS: IntoIterator, S: Borrow, IW: IntoIterator, IW::Item: Borrow, { + self.wait(wait_semaphores); + let queue = self.shared.queue.lock().unwrap(); let command_buffer = queue.raw.new_command_buffer(); for (swapchain, index) in swapchains { - // TODO: wait for semaphores debug!("presenting frame {}", index); let drawable = swapchain.borrow().take_drawable(index); command_buffer.present_drawable(&drawable); diff --git a/src/backend/metal/src/device.rs b/src/backend/metal/src/device.rs index dc6e68227db..8ba25356187 100644 --- a/src/backend/metal/src/device.rs +++ b/src/backend/metal/src/device.rs @@ -264,6 +264,7 @@ impl PhysicalDevice { let private_caps = { let device = &*shared.device.lock().unwrap(); PrivateCapabilities { + exposed_queues: 1, resource_heaps: Self::supports_any(device, RESOURCE_HEAP_SUPPORT), argument_buffers: Self::supports_any(device, ARGUMENT_BUFFER_SUPPORT) && false, //TODO shared_textures: !Self::is_mac(device), @@ -328,7 +329,9 @@ impl hal::PhysicalDevice for PhysicalDevice { } let mut queue_group = hal::backend::RawQueueGroup::new(family); - queue_group.add_queue(command::CommandQueue::new(self.shared.clone())); + for _ in 0 .. self.private_caps.exposed_queues { + queue_group.add_queue(command::CommandQueue::new(self.shared.clone())); + } let device = Device { shared: self.shared.clone(), @@ -1325,7 +1328,16 @@ impl hal::Device for Device { } fn create_semaphore(&self) -> n::Semaphore { - unsafe { n::Semaphore(n::dispatch_semaphore_create(1)) } // Returns retained + n::Semaphore { + // Semaphore synchronization between command buffers of the same queue + // is useless, don't bother even creating one. + system: if self.private_caps.exposed_queues > 1 { + Some(n::SystemSemaphore::new()) + } else { + None + }, + image_ready: Arc::new(Mutex::new(None)), + } } fn create_descriptor_pool(&self, _max_sets: usize, descriptor_ranges: I) -> n::DescriptorPool @@ -1524,8 +1536,7 @@ impl hal::Device for Device { fn destroy_framebuffer(&self, _buffer: n::Framebuffer) { } - fn destroy_semaphore(&self, semaphore: n::Semaphore) { - unsafe { n::dispatch_release(semaphore.0) } + fn destroy_semaphore(&self, _semaphore: n::Semaphore) { } fn allocate_memory(&self, memory_type: hal::MemoryTypeId, size: u64) -> Result { diff --git a/src/backend/metal/src/lib.rs b/src/backend/metal/src/lib.rs index 0956f6e52fe..b32d177f096 100644 --- a/src/backend/metal/src/lib.rs +++ b/src/backend/metal/src/lib.rs @@ -198,6 +198,7 @@ impl hal::Backend for Backend { #[derive(Clone, Copy, Debug)] struct PrivateCapabilities { + exposed_queues: usize, resource_heaps: bool, argument_buffers: bool, shared_textures: bool, diff --git a/src/backend/metal/src/native.rs b/src/backend/metal/src/native.rs index f7b47e4e190..2a3e8675444 100644 --- a/src/backend/metal/src/native.rs +++ b/src/backend/metal/src/native.rs @@ -1,5 +1,6 @@ use Backend; use internal::Channel; +use window::SwapchainImage; use std::collections::HashMap; use std::ops::Range; @@ -210,10 +211,10 @@ unsafe impl Send for Sampler {} unsafe impl Sync for Sampler {} #[derive(Debug)] -pub struct Semaphore(pub(crate) *mut c_void); - -unsafe impl Send for Semaphore {} -unsafe impl Sync for Semaphore {} +pub struct Semaphore { + pub(crate) system: Option, + pub(crate) image_ready: Arc>>, +} #[derive(Debug)] pub struct Buffer { @@ -481,21 +482,50 @@ pub struct FenceInner { pub type Fence = Arc; extern "C" { - #[allow(dead_code)] - pub fn dispatch_semaphore_wait( + fn dispatch_semaphore_wait( semaphore: *mut c_void, timeout: u64, ) -> c_long; - pub fn dispatch_semaphore_signal( + fn dispatch_semaphore_signal( semaphore: *mut c_void, ) -> c_long; - pub fn dispatch_semaphore_create( + fn dispatch_semaphore_create( value: c_long, ) -> *mut c_void; - pub fn dispatch_release( + fn dispatch_release( object: *mut c_void, ); } + +#[derive(Clone, Debug)] +pub struct SystemSemaphore(*mut c_void); +unsafe impl Send for SystemSemaphore {} +unsafe impl Sync for SystemSemaphore {} + +impl Drop for SystemSemaphore { + fn drop(&mut self) { + unsafe { + dispatch_release(self.0) + } + } +} +impl SystemSemaphore { + pub(crate) fn new() -> Self { + SystemSemaphore(unsafe { + dispatch_semaphore_create(1) + }) + } + pub(crate) fn signal(&self) { + unsafe { + dispatch_semaphore_signal(self.0); + } + } + pub(crate) fn wait(&self, timeout: u64) { + unsafe { + dispatch_semaphore_wait(self.0, timeout); + } + } +} diff --git a/src/backend/metal/src/window.rs b/src/backend/metal/src/window.rs index 614424c682d..e638df0c586 100644 --- a/src/backend/metal/src/window.rs +++ b/src/backend/metal/src/window.rs @@ -3,8 +3,7 @@ use internal::Channel; use native; use device::{Device, PhysicalDevice}; -use std::mem; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, MutexGuard}; use hal::{self, format, image}; use hal::{Backbuffer, SwapchainConfig}; @@ -25,7 +24,7 @@ pub struct Surface { pub(crate) apply_pixel_scale: bool, } -//TODO: double-check who needs it shared +#[derive(Debug)] pub(crate) struct SurfaceInner { pub(crate) nsview: *mut Object, pub(crate) render_layer: Mutex, @@ -40,29 +39,109 @@ impl Drop for SurfaceInner { } } +impl SurfaceInner { + fn next_frame<'a>(&self, frames: &'a [Frame]) -> (usize, MutexGuard<'a, FrameInner>) { + let _ap = AutoreleasePool::new(); + let layer_ref = self.render_layer.lock().unwrap(); + + let (drawable, texture_temp): (metal::Drawable, &metal::TextureRef) = unsafe { + let drawable: &metal::DrawableRef = msg_send![*layer_ref, nextDrawable]; + (drawable.to_owned(), msg_send![drawable, texture]) + }; + + let index = frames + .iter() + .position(|f| f.texture.as_ptr() == texture_temp.as_ptr()) + .expect("Surface lost?"); + + let mut frame = frames[index].inner.lock().unwrap(); + assert!(frame.drawable.is_none()); + frame.drawable = Some(drawable); + + debug!("Surface next frame is {}", index); + (index, frame) + } +} + +#[derive(Debug)] +struct FrameInner { + drawable: Option, + /// If there is a `drawable`, availability indicates if it's free for grabs. + /// If there is `None`, `available == false` means that the frame has already + /// been acquired and the `drawable` will appear at some point. + available: bool, + last_frame: usize, +} + #[derive(Debug)] struct Frame { - drawable: Mutex>, + inner: Mutex, texture: metal::Texture, } +unsafe impl Send for Frame {} +unsafe impl Sync for Frame {} + pub struct Swapchain { - frames: Vec, + frames: Arc>, surface: Arc, _size_pixels: (u64, u64), + last_frame: usize, } -unsafe impl Send for Swapchain {} -unsafe impl Sync for Swapchain {} - impl Swapchain { + /// Returns the drawable for the specified swapchain image index, + /// marks the index as free for future use. pub(crate) fn take_drawable(&self, index: hal::SwapImageIndex) -> metal::Drawable { - self.frames[index as usize].drawable + let mut frame = self + .frames[index as usize] + .inner .lock() - .unwrap() + .unwrap(); + assert!(!frame.available); + frame.available = true; + frame.drawable .take() .expect("Drawable has not been acquired!") } + + fn signal_sync(&self, sync: hal::FrameSync) { + match sync { + hal::FrameSync::Semaphore(semaphore) => { + if let Some(ref system) = semaphore.system { + system.signal(); + } + } + hal::FrameSync::Fence(fence) => { + *fence.mutex.lock().unwrap() = true; + } + } + } +} + +#[derive(Debug)] +pub struct SwapchainImage { + frames: Arc>, + surface: Arc, + index: hal::SwapImageIndex, +} + +impl SwapchainImage { + /// Waits until the specified swapchain index is available for rendering. + pub fn wait_until_ready(&self) { + // check the target frame first + { + let frame = self.frames[self.index as usize].inner.lock().unwrap(); + assert!(!frame.available); + if frame.drawable.is_some() { + return + } + } + // wait for new frames to come until we meet the chosen one + while self.surface.next_frame(&self.frames).0 != self.index as usize { + } + debug!("Swapchain image is ready") + } } @@ -174,7 +253,11 @@ impl Device { let texture: metal::Texture = msg_send![drawable, texture]; //HACK: not retaining the texture here Frame { - drawable: Mutex::new(None), + inner: Mutex::new(FrameInner { + drawable: None, + available: true, + last_frame: 0, + }), texture, } }) @@ -198,9 +281,10 @@ impl Device { .collect(); let swapchain = Swapchain { - frames, + frames: Arc::new(frames), surface: surface.inner.clone(), _size_pixels: (pixel_width, pixel_height), + last_frame: 0, }; (swapchain, Backbuffer::Images(images)) @@ -209,30 +293,52 @@ impl Device { impl hal::Swapchain for Swapchain { fn acquire_image(&mut self, sync: hal::FrameSync) -> Result { - let _ap = AutoreleasePool::new(); // for the drawable + let mut oldest_index = 0; + let mut oldest_frame = self.last_frame; + + self.last_frame += 1; + + for (index, frame_arc) in self.frames.iter().enumerate() { + let mut frame = frame_arc.inner.lock().unwrap(); + if frame.available && frame.drawable.is_some() { + frame.available = false; + frame.last_frame = self.last_frame; + self.signal_sync(sync); + return Ok(index as _); + } + if frame.last_frame < oldest_frame { + oldest_frame = frame.last_frame; + oldest_index = index; + } + } - unsafe { + let blocking = false; + + let (index, mut frame) = if blocking { + self.surface.next_frame(&self.frames) + } else { match sync { hal::FrameSync::Semaphore(semaphore) => { - // FIXME: this is definitely wrong - native::dispatch_semaphore_signal(semaphore.0); - }, - hal::FrameSync::Fence(_fence) => unimplemented!(), + let mut sw_image = semaphore.image_ready.lock().unwrap(); + assert!(sw_image.is_none()); + *sw_image = Some(SwapchainImage { + frames: self.frames.clone(), + surface: self.surface.clone(), + index: oldest_index as _, + }); + } + hal::FrameSync::Fence(_fence) => { + //TODO: need presentation handlers always created and setting a bool + unimplemented!() + } } - } - let layer_ref = self.surface.render_layer.lock().unwrap(); - let (drawable, texture_temp): (metal::Drawable, &metal::TextureRef) = unsafe { - let drawable: &metal::DrawableRef = msg_send![*layer_ref, nextDrawable]; - (drawable.to_owned(), msg_send![drawable, texture]) + let frame = self.frames[oldest_index].inner.lock().unwrap(); + (oldest_index, frame) }; - let index = self.frames - .iter() - .position(|f| f.texture.as_ptr() == texture_temp.as_ptr()) - .expect("Surface lost?"); - let old = mem::replace(&mut *self.frames[index].drawable.lock().unwrap(), Some(drawable)); - assert!(old.is_none()); + frame.last_frame = self.last_frame; + frame.available = false; Ok(index as _) } From 1f4cbc49445d06e7053333347c18fb48acdf0db2 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 22 Jun 2018 07:52:36 -0400 Subject: [PATCH 6/6] [mtl] swapchain resize handling --- examples/quad/main.rs | 1 + src/backend/metal/src/device.rs | 5 +- src/backend/metal/src/window.rs | 95 +++++++++++++++++++++++---------- 3 files changed, 72 insertions(+), 29 deletions(-) diff --git a/examples/quad/main.rs b/examples/quad/main.rs index c3822007d16..306e34a9c10 100644 --- a/examples/quad/main.rs +++ b/examples/quad/main.rs @@ -599,6 +599,7 @@ fn swapchain_stuff( println!("Surface format: {:?}", format); let swap_config = SwapchainConfig::new() .with_color(format) + .with_image_count(caps.image_count.start) .with_image_usage(i::Usage::COLOR_ATTACHMENT); let (swap_chain, backbuffer) = device.create_swapchain(surface, swap_config, None, &extent); diff --git a/src/backend/metal/src/device.rs b/src/backend/metal/src/device.rs index 8ba25356187..ccd73903a1c 100644 --- a/src/backend/metal/src/device.rs +++ b/src/backend/metal/src/device.rs @@ -2024,9 +2024,12 @@ impl hal::Device for Device { &self, surface: &mut Surface, config: hal::SwapchainConfig, - _old_swapchain: Option, + old_swapchain: Option, _extent: &window::Extent2D, ) -> (Swapchain, hal::Backbuffer) { + if let Some(_swapchain) = old_swapchain { + //swapchain is dropped here + } self.build_swapchain(surface, config) } diff --git a/src/backend/metal/src/window.rs b/src/backend/metal/src/window.rs index e638df0c586..a614fe66a05 100644 --- a/src/backend/metal/src/window.rs +++ b/src/backend/metal/src/window.rs @@ -44,11 +44,12 @@ impl SurfaceInner { let _ap = AutoreleasePool::new(); let layer_ref = self.render_layer.lock().unwrap(); - let (drawable, texture_temp): (metal::Drawable, &metal::TextureRef) = unsafe { - let drawable: &metal::DrawableRef = msg_send![*layer_ref, nextDrawable]; - (drawable.to_owned(), msg_send![drawable, texture]) + let (drawable, texture_temp): (&metal::DrawableRef, &metal::TextureRef) = unsafe { + let drawable = msg_send![*layer_ref, nextDrawable]; + (drawable, msg_send![drawable, texture]) }; + trace!("looking for {:?}", texture_temp); let index = frames .iter() .position(|f| f.texture.as_ptr() == texture_temp.as_ptr()) @@ -56,9 +57,9 @@ impl SurfaceInner { let mut frame = frames[index].inner.lock().unwrap(); assert!(frame.drawable.is_none()); - frame.drawable = Some(drawable); + frame.drawable = Some(drawable.to_owned()); - debug!("Surface next frame is {}", index); + debug!("next is frame[{}]", index); (index, frame) } } @@ -82,11 +83,29 @@ struct Frame { unsafe impl Send for Frame {} unsafe impl Sync for Frame {} +impl Drop for Frame { + fn drop(&mut self) { + info!("dropping Frame"); + } +} + pub struct Swapchain { frames: Arc>, surface: Arc, - _size_pixels: (u64, u64), + size_pixels: (image::Size, image::Size), last_frame: usize, + image_ready_callbacks: Vec>>>, +} + +impl Drop for Swapchain { + fn drop(&mut self) { + info!("dropping Swapchain"); + for ir in self.image_ready_callbacks.drain(..) { + if ir.lock().unwrap().take().is_some() { + debug!("\twith a callback"); + } + } + } } impl Swapchain { @@ -147,7 +166,7 @@ impl SwapchainImage { impl hal::Surface for Surface { fn kind(&self) -> image::Kind { - let (width, height) = self.pixel_dimensions(); + let (width, height) = self.inner.pixel_dimensions(); image::Kind::D2(width, height, 1, 1) } @@ -157,7 +176,7 @@ impl hal::Surface for Surface { ) -> (hal::SurfaceCapabilities, Option>, Vec) { let caps = hal::SurfaceCapabilities { //Note: this is hardcoded in `CAMetalLayer` documentation - image_count: 2 .. 3, + image_count: 2 .. 4, current_extent: None, extents: Extent2D { width: 4, height: 4} .. Extent2D { width: 4096, height: 4096 }, max_image_layers: 1, @@ -182,12 +201,12 @@ impl hal::Surface for Surface { } } -impl Surface { +impl SurfaceInner { fn pixel_dimensions(&self) -> (image::Size, image::Size) { unsafe { // NSView bounds are measured in DIPs - let bounds: NSRect = msg_send![self.inner.nsview, bounds]; - let bounds_pixel: NSRect = msg_send![self.inner.nsview, convertRectToBacking:bounds]; + let bounds: NSRect = msg_send![self.nsview, bounds]; + let bounds_pixel: NSRect = msg_send![self.nsview, convertRectToBacking:bounds]; (bounds_pixel.size.width as _, bounds_pixel.size.height as _) } } @@ -199,7 +218,7 @@ impl Device { surface: &mut Surface, config: SwapchainConfig, ) -> (Swapchain, Backbuffer) { - let _ap = AutoreleasePool::new(); // for the drawable + info!("build_swapchain {:?}", config); let mtl_format = self.private_caps .map_format(config.color_format) @@ -224,7 +243,6 @@ impl Device { msg_send![render_layer, setMaximumDrawableCount: config.image_count as u64]; //TODO: only set it where supported msg_send![render_layer, setDisplaySyncEnabled: display_sync]; - //msg_send![render_layer, setPresentsWithTransaction: true]; // Update render layer size let view_points_size: CGRect = msg_send![nsview, bounds]; @@ -244,21 +262,30 @@ impl Device { (view_points_size.size, scale_factor) }; - let pixel_width = (view_size.width * scale_factor) as u64; - let pixel_height = (view_size.height * scale_factor) as u64; + let pixel_width = (view_size.width * scale_factor) as image::Size; + let pixel_height = (view_size.height * scale_factor) as image::Size; let frames = (0 .. config.image_count) - .map(|_| unsafe { - let drawable: &metal::DrawableRef = msg_send![render_layer, nextDrawable]; - let texture: metal::Texture = msg_send![drawable, texture]; - //HACK: not retaining the texture here + .map(|index| { + let _ap = AutoreleasePool::new(); // for the drawable & texture + let (drawable, texture) = unsafe { + let drawable: &metal::DrawableRef = msg_send![render_layer, nextDrawable]; + assert!(!drawable.as_ptr().is_null()); + let texture: &metal::TextureRef = msg_send![drawable, texture]; + (drawable, texture) + }; + if index == 0 { + // when resizing, this trick frees up the currently shown frame + drawable.present(); + } + trace!("\tframe[{}] = {:?}", index, texture); Frame { inner: Mutex::new(FrameInner { - drawable: None, + drawable: Some(drawable.to_owned()), available: true, last_frame: 0, }), - texture, + texture: texture.to_owned(), } }) .collect::>(); @@ -266,10 +293,10 @@ impl Device { let images = frames .iter() .map(|frame| native::Image { - raw: frame.texture.clone(), //Note: careful! + raw: frame.texture.clone(), extent: image::Extent { - width: pixel_width as _, - height: pixel_height as _, + width: pixel_width, + height: pixel_height, depth: 1, }, num_layers: None, @@ -283,8 +310,9 @@ impl Device { let swapchain = Swapchain { frames: Arc::new(frames), surface: surface.inner.clone(), - _size_pixels: (pixel_width, pixel_height), + size_pixels: (pixel_width, pixel_height), last_frame: 0, + image_ready_callbacks: Vec::new(), }; (swapchain, Backbuffer::Images(images)) @@ -293,14 +321,22 @@ impl Device { impl hal::Swapchain for Swapchain { fn acquire_image(&mut self, sync: hal::FrameSync) -> Result { + self.last_frame += 1; + + //TODO: figure out a proper story of HiDPI + if false && self.surface.pixel_dimensions() != self.size_pixels { + return Err(()) + } + let mut oldest_index = 0; let mut oldest_frame = self.last_frame; - self.last_frame += 1; - for (index, frame_arc) in self.frames.iter().enumerate() { let mut frame = frame_arc.inner.lock().unwrap(); - if frame.available && frame.drawable.is_some() { + if !frame.available { + continue + } + if frame.drawable.is_some() { frame.available = false; frame.last_frame = self.last_frame; self.signal_sync(sync); @@ -317,8 +353,10 @@ impl hal::Swapchain for Swapchain { let (index, mut frame) = if blocking { self.surface.next_frame(&self.frames) } else { + self.image_ready_callbacks.retain(|ir| ir.lock().unwrap().is_some()); match sync { hal::FrameSync::Semaphore(semaphore) => { + self.image_ready_callbacks.push(Arc::clone(&semaphore.image_ready)); let mut sw_image = semaphore.image_ready.lock().unwrap(); assert!(sw_image.is_none()); *sw_image = Some(SwapchainImage { @@ -337,6 +375,7 @@ impl hal::Swapchain for Swapchain { (oldest_index, frame) }; + assert!(frame.available); frame.last_frame = self.last_frame; frame.available = false;