From 73eb1f43526e6dfa4c51a2b52b80b607ad93ae3f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 12 Aug 2019 08:00:02 -0400 Subject: [PATCH] Alternative swapchain model --- examples/colour-uniform/main.rs | 2 +- examples/quad/main.rs | 231 ++++++++-------------------- src/backend/dx11/src/conv.rs | 10 ++ src/backend/dx11/src/device.rs | 153 +++++++++--------- src/backend/dx11/src/lib.rs | 111 ++++++++++++- src/backend/empty/src/lib.rs | 29 ++++ src/backend/gl/Cargo.toml | 1 + src/backend/gl/src/command.rs | 24 +-- src/backend/gl/src/conv.rs | 97 ++++++++---- src/backend/gl/src/device.rs | 97 +++++------- src/backend/gl/src/lib.rs | 8 +- src/backend/gl/src/native.rs | 8 +- src/backend/gl/src/queue.rs | 99 +++++++----- src/backend/gl/src/window/dummy.rs | 32 +++- src/backend/gl/src/window/glutin.rs | 84 +++++++++- src/backend/gl/src/window/web.rs | 12 +- src/backend/gl/src/window/wgl.rs | 5 +- src/backend/metal/src/command.rs | 25 ++- src/backend/metal/src/device.rs | 24 +-- src/backend/metal/src/native.rs | 6 +- src/backend/metal/src/window.rs | 172 +++++++++++++++------ src/backend/vulkan/src/device.rs | 45 ++++-- src/backend/vulkan/src/lib.rs | 49 ++++++ src/backend/vulkan/src/native.rs | 28 +++- src/backend/vulkan/src/window.rs | 222 +++++++++++++++++++++++--- src/hal/src/lib.rs | 4 +- src/hal/src/queue/mod.rs | 10 +- src/hal/src/window.rs | 72 ++++++--- 28 files changed, 1121 insertions(+), 539 deletions(-) diff --git a/examples/colour-uniform/main.rs b/examples/colour-uniform/main.rs index 15c61409893..a83391cbc17 100644 --- a/examples/colour-uniform/main.rs +++ b/examples/colour-uniform/main.rs @@ -118,7 +118,7 @@ trait SurfaceTrait { impl SurfaceTrait for ::Surface { #[cfg(feature = "gl")] fn get_context_t(&self) -> &back::glutin::RawContext { - self.get_context() + self.context() } } diff --git a/examples/quad/main.rs b/examples/quad/main.rs index 8885e1fb506..9667374af5e 100644 --- a/examples/quad/main.rs +++ b/examples/quad/main.rs @@ -49,8 +49,13 @@ use hal::{ window, }; -use std::io::Cursor; -use std::mem::ManuallyDrop; +use std::{ + borrow::Borrow, + io::Cursor, + iter, + mem::{self, ManuallyDrop}, + ptr, +}; #[cfg_attr(rustfmt, rustfmt_skip)] const DIMS: window::Extent2D = window::Extent2D { width: 1024, height: 768 }; @@ -177,14 +182,13 @@ fn main() { println!("resized to {:?}", dims); #[cfg(feature = "gl")] { - let context = renderer.surface.get_context(); + let context = renderer.surface.context(); context.resize(dims.to_physical(window.hidpi_factor())); } - let dimensions = window::Extent2D { + renderer.dimensions = window::Extent2D { width: dims.width as u32, height: dims.height as u32, }; - renderer.dimensions = dimensions; renderer.recreate_swapchain(); } _ => {} @@ -206,8 +210,6 @@ struct Renderer { format: hal::format::Format, swap_chain: Option, dimensions: window::Extent2D, - framebuffers: Vec, - frame_images: Vec<(B::Image, B::ImageView)>, viewport: hal::pso::Viewport, render_pass: ManuallyDrop, pipeline: ManuallyDrop, @@ -215,8 +217,6 @@ struct Renderer { desc_set: B::DescriptorSet, set_layout: ManuallyDrop, submission_complete_semaphores: Vec, - image_acquire_semaphores: Vec, - free_acquire_semaphore: Option, submission_complete_fences: Vec, cmd_pools: Vec, cmd_buffers: Vec, @@ -313,7 +313,7 @@ where // Buffer allocations println!("Memory types: {:?}", memory_types); - let buffer_stride = std::mem::size_of::() as u64; + let buffer_stride = mem::size_of::() as u64; let buffer_len = QUAD.len() as u64 * buffer_stride; assert_ne!(buffer_len, 0); @@ -536,12 +536,10 @@ where let swap_config = window::SwapchainConfig::from_caps(&caps, format, DIMS); println!("{:?}", swap_config); - let extent = swap_config.extent.to_extent(); - - let (swap_chain, backbuffer) = - unsafe { device.create_swapchain(&mut surface, swap_config, None) } - .expect("Can't create swapchain"); - let swap_chain = Some(swap_chain); + unsafe { + surface.configure_swapchain(&device, swap_config) + .expect("Can't configure swapchain"); + }; let render_pass = { let attachment = pass::Attachment { @@ -577,47 +575,10 @@ where ) }; - let (frame_images, framebuffers) = { - let pairs = backbuffer - .into_iter() - .map(|image| unsafe { - let rtv = device - .create_image_view( - &image, - i::ViewKind::D2, - format, - Swizzle::NO, - COLOR_RANGE.clone(), - ) - .unwrap(); - (image, rtv) - }) - .collect::>(); - let fbos = pairs - .iter() - .map(|&(_, ref rtv)| unsafe { - device - .create_framebuffer(&render_pass, Some(rtv), extent) - .unwrap() - }) - .collect::>(); - (pairs, fbos) - }; - // Define maximum number of frames we want to be able to be "in flight" (being computed // simultaneously) at once let frames_in_flight = 3; - // Number of image acquisition semaphores is based on the number of swapchain images, not frames in flight, - // plus one extra which we can guarantee is unused at any given time by swapping it out with the ones - // in the rest of the queue. - let mut image_acquire_semaphores = Vec::with_capacity(frame_images.len()); - let free_acquire_semaphore = Option::Some( - device - .create_semaphore() - .expect("Could not create semaphore"), - ); - // The number of the rest of the resources is based on the frames in flight. let mut submission_complete_semaphores = Vec::with_capacity(frames_in_flight); let mut submission_complete_fences = Vec::with_capacity(frames_in_flight); @@ -647,14 +608,6 @@ where } } - for _ in 0 .. frame_images.len() { - image_acquire_semaphores.push( - device - .create_semaphore() - .expect("Could not create semaphore"), - ); - } - for i in 0 .. frames_in_flight { submission_complete_semaphores.push( device @@ -672,7 +625,7 @@ where let pipeline_layout = ManuallyDrop::new( unsafe { device.create_pipeline_layout( - std::iter::once(&*set_layout), + iter::once(&*set_layout), &[(pso::ShaderStageFlags::VERTEX, 0 .. 8)], ) } @@ -731,7 +684,7 @@ where }); pipeline_desc.vertex_buffers.push(pso::VertexBufferDesc { binding: 0, - stride: std::mem::size_of::() as u32, + stride: mem::size_of::() as u32, rate: VertexInputRate::Vertex, }); @@ -770,8 +723,8 @@ where rect: pso::Rect { x: 0, y: 0, - w: extent.width as _, - h: extent.height as _, + w: DIMS.width as _, + h: DIMS.height as _, }, depth: 0.0 .. 1.0, }; @@ -789,9 +742,7 @@ where adapter, format, dimensions, - swap_chain, - framebuffers, - frame_images, + swap_chain: None, viewport, render_pass, pipeline, @@ -799,8 +750,6 @@ where desc_set, set_layout, submission_complete_semaphores, - image_acquire_semaphores, - free_acquire_semaphore, submission_complete_fences, cmd_pools, cmd_buffers, @@ -818,8 +767,6 @@ where } fn recreate_swapchain(&mut self) { - self.device.wait_idle().unwrap(); - let (caps, formats, _present_modes) = self .surface .compatibility(&mut self.adapter.physical_device); @@ -830,68 +777,19 @@ where println!("{:?}", swap_config); let extent = swap_config.extent.to_extent(); - let (new_swap_chain, new_backbuffer) = unsafe { - self.device - .create_swapchain(&mut self.surface, swap_config, self.swap_chain.take()) - } - .expect("Can't create swapchain"); - unsafe { - // Clean up the old framebuffers and images - for framebuffer in self.framebuffers.drain(..) { - self.device.destroy_framebuffer(framebuffer); - } - for (_, rtv) in self.frame_images.drain(..) { - self.device.destroy_image_view(rtv); - } + self.surface.configure_swapchain(&self.device, swap_config) + .expect("Can't create swapchain"); } - self.swap_chain = Some(new_swap_chain); - - let (new_frame_images, new_framebuffers) = { - let pairs = new_backbuffer - .into_iter() - .map(|image| unsafe { - let rtv = self - .device - .create_image_view( - &image, - i::ViewKind::D2, - self.format, - Swizzle::NO, - COLOR_RANGE.clone(), - ) - .unwrap(); - (image, rtv) - }) - .collect::>(); - let fbos = pairs - .iter() - .map(|&(_, ref rtv)| unsafe { - self.device - .create_framebuffer(&self.render_pass, Some(rtv), extent) - .unwrap() - }) - .collect(); - (pairs, fbos) - }; - - self.framebuffers = new_framebuffers; - self.frame_images = new_frame_images; self.viewport.rect.w = extent.width as _; self.viewport.rect.h = extent.height as _; } fn render(&mut self) { - // Use guaranteed unused acquire semaphore to get the index of the next frame we will render to - // by using acquire_image - let swap_image = unsafe { - match self.swap_chain.as_mut().unwrap().acquire_image( - !0, - self.free_acquire_semaphore.as_ref(), - None, - ) { - Ok((i, _)) => i as usize, + let surface_image = unsafe { + match self.surface.acquire_image(!0) { + Ok((image, _)) => image, Err(_) => { self.recreate_swapchain(); return; @@ -899,11 +797,17 @@ where } }; - // Swap the acquire semaphore with the one previously associated with the image we are acquiring - core::mem::swap( - self.free_acquire_semaphore.as_mut().unwrap(), - &mut self.image_acquire_semaphores[swap_image], - ); + let framebuffer = unsafe { + self.device.create_framebuffer( + &self.render_pass, + iter::once(surface_image.borrow()), + i::Extent { + width: self.dimensions.width, + height: self.dimensions.height, + depth: 1, + }, + ).unwrap() + }; // Compute index into our resource ring buffers based on the frame number // and number of frames in flight. Pay close attention to where this index is needed @@ -916,11 +820,12 @@ where // updated with a CPU->GPU data copy are not in use by the GPU, so we can perform those updates. // In this case there are none to be done, however. unsafe { + let fence = &self.submission_complete_fences[frame_idx]; self.device - .wait_for_fence(&self.submission_complete_fences[frame_idx], !0) + .wait_for_fence(fence, !0) .expect("Failed to wait for fence"); self.device - .reset_fence(&self.submission_complete_fences[frame_idx]) + .reset_fence(fence) .expect("Failed to reset fence"); self.cmd_pools[frame_idx].reset(false); } @@ -933,17 +838,17 @@ where cmd_buffer.set_viewports(0, &[self.viewport.clone()]); cmd_buffer.set_scissors(0, &[self.viewport.rect]); cmd_buffer.bind_graphics_pipeline(&self.pipeline); - cmd_buffer.bind_vertex_buffers(0, Some((&*self.vertex_buffer, 0))); + cmd_buffer.bind_vertex_buffers(0, iter::once((&*self.vertex_buffer, 0))); cmd_buffer.bind_graphics_descriptor_sets( &self.pipeline_layout, 0, - Some(&self.desc_set), + iter::once(&self.desc_set), &[], ); cmd_buffer.begin_render_pass( &self.render_pass, - &self.framebuffers[swap_image], + &framebuffer, self.viewport.rect, &[command::ClearValue { color: command::ClearColor { @@ -956,12 +861,9 @@ where cmd_buffer.finish(); let submission = Submission { - command_buffers: Some(&*cmd_buffer), - wait_semaphores: Some(( - &self.image_acquire_semaphores[swap_image], - PipelineStage::COLOR_ATTACHMENT_OUTPUT, - )), - signal_semaphores: Some(&self.submission_complete_semaphores[frame_idx]), + command_buffers: iter::once(&*cmd_buffer), + wait_semaphores: None, + signal_semaphores: iter::once(&self.submission_complete_semaphores[frame_idx]), }; self.queue_group.queues[0].submit( submission, @@ -969,14 +871,16 @@ where ); // present frame - if let Err(_) = self.swap_chain.as_ref().unwrap().present( - &mut self.queue_group.queues[0], - swap_image as window::SwapImageIndex, + if let Err(_) = self.queue_group.queues[0].present_surface( + &mut self.surface, + surface_image, Some(&self.submission_complete_semaphores[frame_idx]), ) { self.recreate_swapchain(); return; } + + self.device.destroy_framebuffer(framebuffer); } // Increment our frame @@ -993,34 +897,29 @@ where unsafe { // TODO: When ManuallyDrop::take (soon to be renamed to ManuallyDrop::read) is stabilized we should use that instead. self.device - .destroy_descriptor_pool(ManuallyDrop::into_inner(std::ptr::read(&self.desc_pool))); + .destroy_descriptor_pool(ManuallyDrop::into_inner(ptr::read(&self.desc_pool))); self.device - .destroy_descriptor_set_layout(ManuallyDrop::into_inner(std::ptr::read( + .destroy_descriptor_set_layout(ManuallyDrop::into_inner(ptr::read( &self.set_layout, ))); self.device - .destroy_buffer(ManuallyDrop::into_inner(std::ptr::read( + .destroy_buffer(ManuallyDrop::into_inner(ptr::read( &self.vertex_buffer, ))); self.device - .destroy_buffer(ManuallyDrop::into_inner(std::ptr::read( + .destroy_buffer(ManuallyDrop::into_inner(ptr::read( &self.image_upload_buffer, ))); self.device - .destroy_image(ManuallyDrop::into_inner(std::ptr::read(&self.image_logo))); + .destroy_image(ManuallyDrop::into_inner(ptr::read(&self.image_logo))); self.device - .destroy_image_view(ManuallyDrop::into_inner(std::ptr::read(&self.image_srv))); + .destroy_image_view(ManuallyDrop::into_inner(ptr::read(&self.image_srv))); self.device - .destroy_sampler(ManuallyDrop::into_inner(std::ptr::read(&self.sampler))); - self.device - .destroy_semaphore(self.free_acquire_semaphore.take().unwrap()); + .destroy_sampler(ManuallyDrop::into_inner(ptr::read(&self.sampler))); for p in self.cmd_pools.drain(..) { self.device.destroy_command_pool(p); } - for s in self.image_acquire_semaphores.drain(..) { - self.device.destroy_semaphore(s); - } for s in self.submission_complete_semaphores.drain(..) { self.device.destroy_semaphore(s); } @@ -1028,31 +927,25 @@ where self.device.destroy_fence(f); } self.device - .destroy_render_pass(ManuallyDrop::into_inner(std::ptr::read(&self.render_pass))); + .destroy_render_pass(ManuallyDrop::into_inner(ptr::read(&self.render_pass))); self.device - .free_memory(ManuallyDrop::into_inner(std::ptr::read( + .free_memory(ManuallyDrop::into_inner(ptr::read( &self.buffer_memory, ))); self.device - .free_memory(ManuallyDrop::into_inner(std::ptr::read(&self.image_memory))); + .free_memory(ManuallyDrop::into_inner(ptr::read(&self.image_memory))); self.device - .free_memory(ManuallyDrop::into_inner(std::ptr::read( + .free_memory(ManuallyDrop::into_inner(ptr::read( &self.image_upload_memory, ))); self.device - .destroy_graphics_pipeline(ManuallyDrop::into_inner(std::ptr::read( + .destroy_graphics_pipeline(ManuallyDrop::into_inner(ptr::read( &self.pipeline, ))); self.device - .destroy_pipeline_layout(ManuallyDrop::into_inner(std::ptr::read( + .destroy_pipeline_layout(ManuallyDrop::into_inner(ptr::read( &self.pipeline_layout, ))); - for framebuffer in self.framebuffers.drain(..) { - self.device.destroy_framebuffer(framebuffer); - } - for (_, rtv) in self.frame_images.drain(..) { - self.device.destroy_image_view(rtv); - } self.device .destroy_swapchain(self.swap_chain.take().unwrap()); diff --git a/src/backend/dx11/src/conv.rs b/src/backend/dx11/src/conv.rs index bdacc07b606..9be7cf4de21 100644 --- a/src/backend/dx11/src/conv.rs +++ b/src/backend/dx11/src/conv.rs @@ -127,6 +127,16 @@ pub fn map_format(format: Format) -> Option { Some(format) } +pub fn map_format_nosrgb(format: Format) -> Option { + // NOTE: DXGI doesn't allow sRGB format on the swapchain, but + // creating RTV of swapchain buffers with sRGB works + match format { + Format::Bgra8Srgb => Some(DXGI_FORMAT_B8G8R8A8_UNORM), + Format::Rgba8Srgb => Some(DXGI_FORMAT_R8G8B8A8_UNORM), + _ => map_format(format), + } +} + #[derive(Debug, Clone)] pub struct DecomposedDxgiFormat { pub typeless: DXGI_FORMAT, diff --git a/src/backend/dx11/src/device.rs b/src/backend/dx11/src/device.rs index 18e8d87c7e4..6d0e9eb663a 100644 --- a/src/backend/dx11/src/device.rs +++ b/src/backend/dx11/src/device.rs @@ -5,11 +5,12 @@ use hal::range::RangeArg; use hal::window::SwapchainConfig; use hal::{buffer, device, error, format, image, mapping, memory, pass, pool, pso, query}; -use winapi::shared::dxgi::{IDXGISwapChain, DXGI_SWAP_CHAIN_DESC, DXGI_SWAP_EFFECT_DISCARD}; +use winapi::shared::dxgi::{IDXGIFactory, IDXGISwapChain, DXGI_SWAP_CHAIN_DESC, DXGI_SWAP_EFFECT_DISCARD}; use winapi::shared::minwindef::TRUE; +use winapi::shared::windef::HWND; use winapi::shared::{dxgiformat, dxgitype, winerror}; use winapi::um::{d3d11, d3d11sdklayers, d3dcommon}; -use winapi::Interface; +use winapi::Interface as _; use wio::com::ComPtr; @@ -582,7 +583,7 @@ impl Device { } } - fn view_image_as_render_target( + pub(crate) fn view_image_as_render_target( &self, info: &ViewInfo, ) -> Result, image::ViewError> { @@ -694,6 +695,64 @@ impl Device { Err(image::ViewError::Unsupported) } } + + pub(crate) fn create_swapchain_impl( + &self, + config: &hal::SwapchainConfig, + window_handle: HWND, + factory: ComPtr, + ) -> Result<(ComPtr, dxgiformat::DXGI_FORMAT), hal::window::CreationError> { + // TODO: use IDXGIFactory2 for >=11.1 + // TODO: this function should be able to fail (Result)? + + debug!("{:#?}", config); + let non_srgb_format = conv::map_format_nosrgb(config.format).unwrap(); + + let mut desc = DXGI_SWAP_CHAIN_DESC { + BufferDesc: dxgitype::DXGI_MODE_DESC { + Width: config.extent.width, + Height: config.extent.height, + // TODO: should this grab max value of all monitor hz? vsync + // will clamp to current monitor anyways? + RefreshRate: dxgitype::DXGI_RATIONAL { + Numerator: 1, + Denominator: 60, + }, + Format: non_srgb_format, + ScanlineOrdering: dxgitype::DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, + Scaling: dxgitype::DXGI_MODE_SCALING_UNSPECIFIED, + }, + // TODO: msaa on backbuffer? + SampleDesc: dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT + | dxgitype::DXGI_USAGE_SHADER_INPUT, + BufferCount: config.image_count, + OutputWindow: window_handle, + // TODO: + Windowed: TRUE, + // TODO: + SwapEffect: DXGI_SWAP_EFFECT_DISCARD, + Flags: 0, + }; + + let dxgi_swapchain = { + let mut swapchain: *mut IDXGISwapChain = ptr::null_mut(); + let hr = unsafe { + factory.CreateSwapChain( + self.raw.as_raw() as *mut _, + &mut desc as *mut _, + &mut swapchain as *mut *mut _ as *mut *mut _, + ) + }; + assert_eq!(hr, winerror::S_OK); + + unsafe { ComPtr::from_raw(swapchain) } + }; + Ok((dxgi_swapchain, non_srgb_format)) + } } impl device::Device for Device { @@ -2475,90 +2534,32 @@ impl device::Device for Device { config: SwapchainConfig, _old_swapchain: Option, ) -> Result<(Swapchain, Vec), hal::window::CreationError> { - // TODO: use IDXGIFactory2 for >=11.1 - // TODO: this function should be able to fail (Result)? - - use conv::map_format; - - debug!("{:#?}", config); - - let (non_srgb_format, format) = { - // NOTE: DXGI doesn't allow sRGB format on the swapchain, but - // creating RTV of swapchain buffers with sRGB works - let format = match config.format { - format::Format::Bgra8Srgb => format::Format::Bgra8Unorm, - format::Format::Rgba8Srgb => format::Format::Rgba8Unorm, - format => format, - }; - - ( - map_format(format).unwrap(), - map_format(config.format).unwrap(), - ) - }; - let decomposed = conv::DecomposedDxgiFormat::from_dxgi_format(format); - - let mut desc = DXGI_SWAP_CHAIN_DESC { - BufferDesc: dxgitype::DXGI_MODE_DESC { - Width: surface.width, - Height: surface.height, - // TODO: should this grab max value of all monitor hz? vsync - // will clamp to current monitor anyways? - RefreshRate: dxgitype::DXGI_RATIONAL { - Numerator: 1, - Denominator: 60, - }, - Format: non_srgb_format, - ScanlineOrdering: dxgitype::DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, - Scaling: dxgitype::DXGI_MODE_SCALING_UNSPECIFIED, - }, - // TODO: msaa on backbuffer? - SampleDesc: dxgitype::DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - BufferUsage: dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT - | dxgitype::DXGI_USAGE_SHADER_INPUT, - BufferCount: config.image_count, - OutputWindow: surface.wnd_handle, - // TODO: - Windowed: TRUE, - // TODO: - SwapEffect: DXGI_SWAP_EFFECT_DISCARD, - Flags: 0, - }; - let swapchain = { - let mut swapchain: *mut IDXGISwapChain = ptr::null_mut(); - let hr = unsafe { - surface.factory.CreateSwapChain( - self.raw.as_raw() as *mut _, - &mut desc as *mut _, - &mut swapchain as *mut *mut _ as *mut *mut _, - ) - }; - assert_eq!(hr, winerror::S_OK); - - unsafe { ComPtr::from_raw(swapchain) } - }; + let (dxgi_swapchain, non_srgb_format) = self.create_swapchain_impl( + &config, + surface.wnd_handle, + surface.factory.clone(), + )?; let resource = { let mut resource: *mut d3d11::ID3D11Resource = ptr::null_mut(); - - let hr = unsafe { - swapchain.GetBuffer( + assert_eq!( + winerror::S_OK, + dxgi_swapchain.GetBuffer( 0 as _, &d3d11::ID3D11Resource::uuidof(), &mut resource as *mut *mut _ as *mut *mut _, ) - }; - assert_eq!(hr, winerror::S_OK); + ); resource }; - let kind = image::Kind::D2(surface.width, surface.height, 1, 1); + let kind = image::Kind::D2(config.extent.width, config.extent.height, 1, 1); + let decomposed = conv::DecomposedDxgiFormat::from_dxgi_format( + conv::map_format(config.format).unwrap() + ); let mut view_info = ViewInfo { - resource: resource, + resource, kind, caps: image::ViewCapabilities::empty(), view_kind: image::ViewKind::D2, @@ -2613,7 +2614,7 @@ impl device::Device for Device { Ok(( Swapchain { - dxgi_swapchain: swapchain, + dxgi_swapchain, }, images, )) diff --git a/src/backend/dx11/src/lib.rs b/src/backend/dx11/src/lib.rs index 0ee0b243f2d..a1e78a506d5 100644 --- a/src/backend/dx11/src/lib.rs +++ b/src/backend/dx11/src/lib.rs @@ -42,8 +42,8 @@ use hal::{ use range_alloc::RangeAllocator; +use winapi::Interface as _; use winapi::shared::{dxgiformat, winerror}; - use winapi::shared::dxgi::{IDXGIAdapter, IDXGIFactory, IDXGISwapChain}; use winapi::shared::minwindef::{FALSE, UINT}; use winapi::shared::windef::{HWND, RECT}; @@ -151,6 +151,7 @@ impl Instance { wnd_handle: hwnd as *mut _, width: width, height: height, + presentation: None, } } @@ -681,6 +682,12 @@ impl adapter::PhysicalDevice for PhysicalDevice { } } +struct Presentation { + swapchain: ComPtr, + view: ComPtr, + format: format::Format, +} + #[derive(Derivative)] #[derivative(Debug)] pub struct Surface { @@ -689,6 +696,8 @@ pub struct Surface { wnd_handle: HWND, width: u32, height: u32, + #[derivative(Debug = "ignore")] + presentation: Option, } unsafe impl Send for Surface {} @@ -697,10 +706,6 @@ unsafe impl Sync for Surface {} impl window::Surface for Surface { fn supports_queue_family(&self, _queue_family: &QueueFamily) -> bool { true - /*match queue_family { - &QueueFamily::Present => true, - _ => false - }*/ } fn compatibility( @@ -745,6 +750,89 @@ impl window::Surface for Surface { } } +impl hal::PresentationSurface for Surface { + type SwapchainImage = ImageView; + + unsafe fn configure_swapchain( + &mut self, device: &device::Device, config: hal::SwapchainConfig + ) -> Result<(), hal::window::CreationError> { + assert!(image::Usage::COLOR_ATTACHMENT.contains(config.image_usage)); + + let swapchain = match self.presentation.take() { + Some(present) => { + let non_srgb_format = conv::map_format_nosrgb(config.format).unwrap(); + drop(present.view); + assert_eq!( + winerror::S_OK, + present.swapchain.ResizeBuffers( + config.image_count, + config.extent.width, + config.extent.height, + non_srgb_format, + 0, + ) + ); + present.swapchain + } + None => { + let (swapchain, _) = device.create_swapchain_impl(&config, self.wnd_handle, self.factory.clone())?; + swapchain + } + }; + + let mut resource: *mut d3d11::ID3D11Resource = ptr::null_mut(); + assert_eq!( + winerror::S_OK, + swapchain.GetBuffer( + 0 as _, + &d3d11::ID3D11Resource::uuidof(), + &mut resource as *mut *mut _ as *mut *mut _, + ) + ); + + let kind = image::Kind::D2(config.extent.width, config.extent.height, 1, 1); + let format = conv::map_format(config.format).unwrap(); + let decomposed = conv::DecomposedDxgiFormat::from_dxgi_format(format); + + let view_info = ViewInfo { + resource, + kind, + caps: image::ViewCapabilities::empty(), + view_kind: image::ViewKind::D2, + format: decomposed.rtv.unwrap(), + range: image::SubresourceRange { + aspects: format::Aspects::COLOR, + levels: 0 .. 1, + layers: 0 .. 1, + }, + }; + let view = device.view_image_as_render_target(&view_info).unwrap(); + + self.presentation = Some(Presentation { + swapchain, + view, + format: config.format, + }); + Ok(()) + } + + unsafe fn acquire_image( + &mut self, + _timeout_ns: u64, //TODO: use the timeout + ) -> Result<(ImageView, Option), hal::AcquireError> { + let present = self.presentation.as_ref().unwrap(); + let image_view = ImageView { + format: present.format, + rtv_handle: Some(present.view.clone()), + dsv_handle: None, + srv_handle: None, + uav_handle: None, + }; + Ok((image_view, None)) + } +} + + #[derive(Derivative)] #[derivative(Debug)] pub struct Swapchain { @@ -851,14 +939,21 @@ impl queue::CommandQueue for CommandQueue { Iw: IntoIterator, { for (swapchain, _idx) in swapchains { - unsafe { - swapchain.borrow().dxgi_swapchain.Present(1, 0); - } + swapchain.borrow().dxgi_swapchain.Present(1, 0); } Ok(None) } + unsafe fn present_surface( + &mut self, + surface: &mut Surface, + _image: ImageView, + ) -> Result, hal::window::PresentError> { + surface.presentation.as_ref().unwrap().swapchain.Present(1, 0); + Ok(None) + } + fn wait_idle(&self) -> Result<(), error::HostExecutionError> { // unimplemented!() Ok(()) diff --git a/src/backend/empty/src/lib.rs b/src/backend/empty/src/lib.rs index 1e64fa2c5be..c71b6a45d8f 100644 --- a/src/backend/empty/src/lib.rs +++ b/src/backend/empty/src/lib.rs @@ -139,6 +139,15 @@ impl queue::CommandQueue for CommandQueue { unimplemented!() } + unsafe fn present_surface( + &mut self, + _surface: &mut Surface, + _image: (), + _wait_semaphore: Option<&()>, + ) -> Result, window::PresentError> { + unimplemented!() + } + fn wait_idle(&self) -> Result<(), error::HostExecutionError> { unimplemented!() } @@ -898,6 +907,26 @@ impl window::Surface for Surface { unimplemented!() } } +impl window::PresentationSurface for Surface { + type SwapchainImage = (); + + unsafe fn configure_swapchain( + &mut self, _: &Device, _: window::SwapchainConfig + ) -> Result<(), window::CreationError> { + unimplemented!() + } + + unsafe fn unconfigure_swapchain(&mut self, _: &Device) { + unimplemented!() + } + + unsafe fn acquire_image( + &mut self, + _: u64, + ) -> Result<((), Option), window::AcquireError> { + unimplemented!() + } +} /// Dummy swapchain. #[derive(Debug)] diff --git a/src/backend/gl/Cargo.toml b/src/backend/gl/Cargo.toml index 809870c8362..570e1f7e2b8 100644 --- a/src/backend/gl/Cargo.toml +++ b/src/backend/gl/Cargo.toml @@ -21,6 +21,7 @@ default = [] wgl = [] [dependencies] +arrayvec = "0.4" bitflags = "1" log = { version = "0.4" } gfx-hal = { path = "../../hal", version = "0.3" } diff --git a/src/backend/gl/src/command.rs b/src/backend/gl/src/command.rs index 857bb1c1adc..ebbd972c3e4 100644 --- a/src/backend/gl/src/command.rs +++ b/src/backend/gl/src/command.rs @@ -120,7 +120,7 @@ pub enum Command { pixel_type: n::DataType, data: command::BufferImageCopy, }, - CopyBufferToSurface(n::RawBuffer, n::Surface, command::BufferImageCopy), + CopyBufferToRenderbuffer(n::RawBuffer, n::Renderbuffer, command::BufferImageCopy), CopyTextureToBuffer { src_texture: n::Texture, texture_target: n::TextureTarget, @@ -129,16 +129,16 @@ pub enum Command { dst_buffer: n::RawBuffer, data: command::BufferImageCopy, }, - CopySurfaceToBuffer(n::Surface, n::RawBuffer, command::BufferImageCopy), + CopyRenderbufferToBuffer(n::Renderbuffer, n::RawBuffer, command::BufferImageCopy), CopyImageToTexture( n::ImageKind, n::Texture, n::TextureTarget, command::ImageCopy, ), - CopyImageToSurface { + CopyImageToRenderbuffer { src_image: n::ImageKind, - dst_surface: n::Surface, + dst_renderbuffer: n::Renderbuffer, dst_format: n::TextureFormat, data: command::ImageCopy, }, @@ -715,7 +715,7 @@ impl command::CommandBuffer for CommandBuffer { // TODO: reset color mask // 2. ClearBuffer let view = match image.kind { - n::ImageKind::Surface { surface, .. } => n::ImageView::Surface(surface), + n::ImageKind::Renderbuffer { renderbuffer, .. } => n::ImageView::Renderbuffer(renderbuffer), n::ImageKind::Texture { texture, target, .. } => { @@ -750,7 +750,7 @@ impl command::CommandBuffer for CommandBuffer { n::ImageKind::Texture { texture, target, .. } => (texture, target), //TODO - n::ImageKind::Surface { .. } => unimplemented!(), + n::ImageKind::Renderbuffer { .. } => unimplemented!(), }; self.push_cmd(Command::BindTexture(0, tex, target)); @@ -1171,9 +1171,9 @@ impl command::CommandBuffer for CommandBuffer { for region in regions { let r = region.borrow().clone(); let cmd = match dst.kind { - n::ImageKind::Surface { surface, format } => Command::CopyImageToSurface { + n::ImageKind::Renderbuffer { renderbuffer, format } => Command::CopyImageToRenderbuffer { src_image: src.kind, - dst_surface: surface, + dst_renderbuffer: renderbuffer, dst_format: format, data: r, }, @@ -1206,8 +1206,8 @@ impl command::CommandBuffer for CommandBuffer { let mut r = region.borrow().clone(); r.buffer_offset += src_range.start; let cmd = match dst.kind { - n::ImageKind::Surface { surface, .. } => { - Command::CopyBufferToSurface(src_raw, surface, r) + n::ImageKind::Renderbuffer { renderbuffer, .. } => { + Command::CopyBufferToRenderbuffer(src_raw, renderbuffer, r) } n::ImageKind::Texture { texture, @@ -1248,8 +1248,8 @@ impl command::CommandBuffer for CommandBuffer { let mut r = region.borrow().clone(); r.buffer_offset += dst_range.start; let cmd = match src.kind { - n::ImageKind::Surface { surface, .. } => { - Command::CopySurfaceToBuffer(surface, dst_raw, r) + n::ImageKind::Renderbuffer { renderbuffer, .. } => { + Command::CopyRenderbufferToBuffer(renderbuffer, dst_raw, r) } n::ImageKind::Texture { texture, diff --git a/src/backend/gl/src/conv.rs b/src/backend/gl/src/conv.rs index dd44aaf8345..0cf93aa830b 100644 --- a/src/backend/gl/src/conv.rs +++ b/src/backend/gl/src/conv.rs @@ -59,42 +59,73 @@ pub fn primitive_to_gl_primitive(primitive: Primitive) -> u32 { } } -pub fn format_to_gl_format(format: Format) -> Option<(i32, u32, VertexAttribFunction)> { +pub struct FormatDescription { + pub tex_internal: u32, + pub tex_external: u32, + pub data_type: u32, + pub num_components: u8, + pub va_fun: VertexAttribFunction, +} + +impl FormatDescription { + fn new( + tex_internal: u32, + tex_external: u32, + data_type: u32, + num_components: u8, + va_fun: VertexAttribFunction, + ) -> Self { + FormatDescription { tex_internal, tex_external, data_type, num_components, va_fun } + } +} + +pub fn describe_format(format: Format) -> Option { use crate::hal::format::Format::*; use crate::native::VertexAttribFunction::*; let _ = Double; //mark as used - // TODO: Add more formats and error handling for `None` - let format = match format { - R8Uint => (1, glow::UNSIGNED_BYTE, Integer), - R8Sint => (1, glow::BYTE, Integer), - Rg8Uint => (2, glow::UNSIGNED_BYTE, Integer), - Rg8Sint => (2, glow::BYTE, Integer), - Rgba8Uint => (4, glow::UNSIGNED_BYTE, Integer), - Rgba8Sint => (4, glow::BYTE, Integer), - R16Uint => (1, glow::UNSIGNED_SHORT, Integer), - R16Sint => (1, glow::SHORT, Integer), - R16Sfloat => (1, glow::HALF_FLOAT, Float), - Rg16Uint => (2, glow::UNSIGNED_SHORT, Integer), - Rg16Sint => (2, glow::SHORT, Integer), - Rg16Sfloat => (2, glow::HALF_FLOAT, Float), - Rgba16Uint => (4, glow::UNSIGNED_SHORT, Integer), - Rgba16Sint => (4, glow::SHORT, Integer), - Rgba16Sfloat => (4, glow::HALF_FLOAT, Float), - R32Uint => (1, glow::UNSIGNED_INT, Integer), - R32Sint => (1, glow::INT, Integer), - R32Sfloat => (1, glow::FLOAT, Float), - Rg32Uint => (2, glow::UNSIGNED_INT, Integer), - Rg32Sint => (2, glow::INT, Integer), - Rg32Sfloat => (2, glow::FLOAT, Float), - Rgb32Uint => (3, glow::UNSIGNED_INT, Integer), - Rgb32Sint => (3, glow::INT, Integer), - Rgb32Sfloat => (3, glow::FLOAT, Float), - Rgba32Uint => (4, glow::UNSIGNED_INT, Integer), - Rgba32Sint => (4, glow::INT, Integer), - Rgba32Sfloat => (4, glow::FLOAT, Float), - _ => return None, - }; + // TODO: Add more formats and error handling for `None` + Some(match format { + R8Uint => FormatDescription::new(glow::R8UI, glow::RED_INTEGER, glow::UNSIGNED_BYTE, 1, Integer), + R8Sint => FormatDescription::new(glow::R8I, glow::RED_INTEGER, glow::BYTE, 1, Integer), + R8Unorm => FormatDescription::new(glow::R8, glow::RED, glow::UNSIGNED_BYTE, 1, Float), + Rg8Uint => FormatDescription::new(glow::RG8UI, glow::RG_INTEGER, glow::UNSIGNED_BYTE, 2, Integer), + Rg8Sint => FormatDescription::new(glow::RG8I, glow::RG_INTEGER, glow::BYTE, 2, Integer), + Rgba8Uint => FormatDescription::new(glow::RGBA8UI, glow::RGBA_INTEGER, glow::UNSIGNED_BYTE, 4, Integer), + Rgba8Sint => FormatDescription::new(glow::RGBA8I, glow::RGBA_INTEGER, glow::BYTE, 4, Integer), + Rgba8Unorm => FormatDescription::new(glow::RGBA8, glow::RGBA, glow::UNSIGNED_BYTE, 4, Float), + Rgba8Srgb => FormatDescription::new(glow::SRGB8_ALPHA8, glow::RGBA, glow::UNSIGNED_BYTE, 4, Float), + Bgra8Unorm => FormatDescription::new(glow::RGBA8, glow::BGRA, glow::UNSIGNED_BYTE, 4, Float), + Bgra8Srgb => FormatDescription::new(glow::SRGB8_ALPHA8, glow::BGRA, glow::UNSIGNED_BYTE, 4, Float), + R16Uint => FormatDescription::new(glow::R16UI, glow::RED_INTEGER, glow::UNSIGNED_SHORT, 1, Integer), + R16Sint => FormatDescription::new(glow::R16I, glow::RED_INTEGER, glow::SHORT, 1, Integer), + R16Sfloat => FormatDescription::new(glow::R16, glow::RED, glow::HALF_FLOAT, 1, Float), + Rg16Uint => FormatDescription::new(glow::RG16UI, glow::RG_INTEGER, glow::UNSIGNED_SHORT, 2, Integer), + Rg16Sint => FormatDescription::new(glow::RG16I, glow::RG_INTEGER, glow::SHORT, 2, Integer), + Rg16Sfloat => FormatDescription::new(glow::RG16, glow::RG, glow::HALF_FLOAT, 2, Float), + Rgba16Uint => FormatDescription::new(glow::RGBA16UI, glow::RGBA_INTEGER, glow::UNSIGNED_SHORT, 4, Integer), + Rgba16Sint => FormatDescription::new(glow::RGBA16I, glow::RGBA_INTEGER, glow::SHORT, 4, Integer), + Rgba16Sfloat => FormatDescription::new(glow::RGBA16, glow::RGBA, glow::HALF_FLOAT, 4, Float), + R32Uint => FormatDescription::new(glow::R32UI, glow::RED_INTEGER, glow::UNSIGNED_INT, 1, Integer), + R32Sint => FormatDescription::new(glow::R32I, glow::RED_INTEGER, glow::INT, 1, Integer), + R32Sfloat => FormatDescription::new(glow::R32F, glow::RED, glow::FLOAT, 1, Float), + Rg32Uint => FormatDescription::new(glow::RG32UI, glow::RG_INTEGER, glow::UNSIGNED_INT, 2, Integer), + Rg32Sint => FormatDescription::new(glow::R32I, glow::RG_INTEGER, glow::INT, 2, Integer), + Rg32Sfloat => FormatDescription::new(glow::RG32F, glow::RG, glow::FLOAT, 2, Float), + Rgb32Uint => FormatDescription::new(glow::RGB32UI, glow::RGB_INTEGER, glow::UNSIGNED_INT, 3, Integer), + Rgb32Sint => FormatDescription::new(glow::RGB32I, glow::RGB_INTEGER, glow::INT, 3, Integer), + Rgb32Sfloat => FormatDescription::new(glow::RGB32F, glow::RGB, glow::FLOAT, 3, Float), + Rgba32Uint => FormatDescription::new(glow::RGBA32UI, glow::RGBA_INTEGER, glow::UNSIGNED_INT, 4, Integer), + Rgba32Sint => FormatDescription::new(glow::RGBA32I, glow::RGBA_INTEGER, glow::INT, 4, Integer), + Rgba32Sfloat => FormatDescription::new(glow::RGBA32F, glow::RGBA, glow::FLOAT, 4, Float), + D32Sfloat => FormatDescription::new( + glow::DEPTH32F_STENCIL8, + glow::DEPTH_STENCIL, + glow::FLOAT_32_UNSIGNED_INT_24_8_REV, + 1, + Float, + ), - Some(format) + _ => return None, + }) } diff --git a/src/backend/gl/src/device.rs b/src/backend/gl/src/device.rs index 20f49ae5021..babe1087414 100644 --- a/src/backend/gl/src/device.rs +++ b/src/backend/gl/src/device.rs @@ -4,7 +4,7 @@ use std::ops::Range; use std::slice; use std::sync::{Arc, Mutex, RwLock}; -use glow::Context; +use glow::Context as _; use hal::{ self as c, @@ -25,6 +25,7 @@ use hal::{ window::{Extent2D, SwapchainConfig}, }; +use arrayvec::ArrayVec; use spirv_cross::{glsl, spirv, ErrorCode as SpirvErrorCode}; use crate::{ @@ -125,8 +126,8 @@ impl Device { fn bind_target_compat(gl: &GlContainer, point: u32, attachment: u32, view: &n::ImageView) { match *view { - n::ImageView::Surface(surface) => unsafe { - gl.framebuffer_renderbuffer(point, attachment, glow::RENDERBUFFER, Some(surface)); + n::ImageView::Renderbuffer(rb) => unsafe { + gl.framebuffer_renderbuffer(point, attachment, glow::RENDERBUFFER, Some(rb)); }, n::ImageView::Texture(texture, textype, level) => unsafe { gl.bind_texture(textype, Some(texture)); @@ -148,8 +149,8 @@ impl Device { fn bind_target(gl: &GlContainer, point: u32, attachment: u32, view: &n::ImageView) { match *view { - n::ImageView::Surface(surface) => unsafe { - gl.framebuffer_renderbuffer(point, attachment, glow::RENDERBUFFER, Some(surface)); + n::ImageView::Renderbuffer(rb) => unsafe { + gl.framebuffer_renderbuffer(point, attachment, glow::RENDERBUFFER, Some(rb)); }, n::ImageView::Texture(texture, _, level) => unsafe { gl.framebuffer_texture(point, attachment, Some(texture), level as _); @@ -892,15 +893,14 @@ impl d::Device for Device { .attributes .iter() .map(|&a| { - let (size, format, vertex_attrib_fn) = - conv::format_to_gl_format(a.element.format).unwrap(); + let fd = conv::describe_format(a.element.format).unwrap(); n::AttributeDesc { location: a.location, offset: a.element.offset, binding: a.binding, - size, - format, - vertex_attrib_fn, + size: fd.num_components as _, + format: fd.data_type, + vertex_attrib_fn: fd.va_fun, } }) .collect(), @@ -1287,20 +1287,7 @@ impl d::Device for Device { ) -> Result { let gl = &self.share.context; - let (int_format, iformat, itype) = match format { - Format::Rgba8Unorm => (glow::RGBA8, glow::RGBA, glow::UNSIGNED_BYTE), - Format::Bgra8Unorm => (glow::RGBA8, glow::BGRA, glow::UNSIGNED_BYTE), - Format::Rgba8Srgb => (glow::SRGB8_ALPHA8, glow::RGBA, glow::UNSIGNED_BYTE), - Format::Bgra8Srgb => (glow::SRGB8_ALPHA8, glow::BGRA, glow::UNSIGNED_BYTE), - Format::R8Unorm => (glow::R8, glow::RED, glow::UNSIGNED_BYTE), - Format::D32Sfloat => ( - glow::DEPTH32F_STENCIL8, - glow::DEPTH_STENCIL, - glow::FLOAT_32_UNSIGNED_INT_24_8_REV, - ), - _ => unimplemented!(), - }; - + let desc = conv::describe_format(format).unwrap(); let channel = format.base_format().1; let image = if num_levels > 1 @@ -1308,14 +1295,14 @@ impl d::Device for Device { || usage.contains(i::Usage::SAMPLED) { let name = gl.create_texture().unwrap(); - match kind { + let target = match kind { i::Kind::D2(w, h, 1, 1) => { gl.bind_texture(glow::TEXTURE_2D, Some(name)); if self.share.private_caps.image_storage { gl.tex_storage_2d( glow::TEXTURE_2D, num_levels as _, - int_format, + desc.tex_internal, w as _, h as _, ); @@ -1331,24 +1318,19 @@ impl d::Device for Device { gl.tex_image_2d( glow::TEXTURE_2D, i as _, - int_format as _, + desc.tex_internal as i32, w as _, h as _, 0, - iformat, - itype, + desc.tex_external, + desc.data_type, None, ); w = std::cmp::max(w / 2, 1); h = std::cmp::max(h / 2, 1); } } - n::ImageKind::Texture { - texture: name, - target: glow::TEXTURE_2D, - format: iformat, - pixel_type: itype, - } + glow::TEXTURE_2D } i::Kind::D2(w, h, l, 1) => { gl.bind_texture(glow::TEXTURE_2D_ARRAY, Some(name)); @@ -1356,7 +1338,7 @@ impl d::Device for Device { gl.tex_storage_3d( glow::TEXTURE_2D_ARRAY, num_levels as _, - int_format, + desc.tex_internal, w as _, h as _, l as _, @@ -1373,40 +1355,41 @@ impl d::Device for Device { gl.tex_image_3d( glow::TEXTURE_2D_ARRAY, i as _, - int_format as _, + desc.tex_internal as i32, w as _, h as _, l as _, 0, - iformat, - itype, + desc.tex_external, + desc.data_type, None, ); w = std::cmp::max(w / 2, 1); h = std::cmp::max(h / 2, 1); } } - n::ImageKind::Texture { - texture: name, - target: glow::TEXTURE_2D_ARRAY, - format: iformat, - pixel_type: itype, - } + glow::TEXTURE_2D_ARRAY } _ => unimplemented!(), + }; + n::ImageKind::Texture { + texture: name, + target, + format: desc.tex_external, + pixel_type: desc.data_type, } } else { let name = gl.create_renderbuffer().unwrap(); match kind { i::Kind::D2(w, h, 1, 1) => { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(name)); - gl.renderbuffer_storage(glow::RENDERBUFFER, int_format, w as _, h as _); + gl.renderbuffer_storage(glow::RENDERBUFFER, desc.tex_internal, w as _, h as _); } _ => unimplemented!(), }; - n::ImageKind::Surface { - surface: name, - format: iformat, + n::ImageKind::Renderbuffer { + renderbuffer: name, + format: desc.tex_external, } }; @@ -1470,9 +1453,9 @@ impl d::Device for Device { assert_eq!(swizzle, Swizzle::NO); //TODO: check format match image.kind { - n::ImageKind::Surface { surface, .. } => { + n::ImageKind::Renderbuffer { renderbuffer, .. } => { if range.levels.start == 0 && range.layers.start == 0 { - Ok(n::ImageView::Surface(surface)) + Ok(n::ImageView::Renderbuffer(renderbuffer)) } else if level != 0 { Err(i::ViewError::Level(level)) //TODO } else { @@ -1578,7 +1561,7 @@ impl d::Device for Device { | n::ImageView::TextureLayer(tex, textype, _, _) => { bindings.push(n::DescSetBindings::Texture(binding, *tex, *textype)) } - n::ImageView::Surface(_) => unimplemented!(), + n::ImageView::Renderbuffer(_) => unimplemented!(), } match sampler { n::FatSampler::Sampler(sampler) => { @@ -1593,7 +1576,7 @@ impl d::Device for Device { | n::ImageView::TextureLayer(tex, textype, _, _) => { bindings.push(n::DescSetBindings::Texture(binding, *tex, *textype)) } - n::ImageView::Surface(_) => panic!( + n::ImageView::Renderbuffer(_) => panic!( "Texture was created with only render target usage which is invalid." ), }, @@ -1825,7 +1808,7 @@ impl d::Device for Device { unsafe fn destroy_image(&self, image: n::Image) { let gl = &self.share.context; match image.kind { - n::ImageKind::Surface { surface, .. } => gl.delete_renderbuffer(surface), + n::ImageKind::Renderbuffer { renderbuffer, .. } => gl.delete_renderbuffer(renderbuffer), n::ImageKind::Texture { texture, .. } => gl.delete_texture(texture), } } @@ -1884,7 +1867,7 @@ impl d::Device for Device { context }; - let mut fbos = Vec::new(); + let mut fbos = ArrayVec::new(); let mut images = Vec::new(); for _ in 0 .. config.image_count { @@ -1905,12 +1888,12 @@ impl d::Device for Device { .unwrap(); match image.kind { - n::ImageKind::Surface { surface, .. } => { + n::ImageKind::Renderbuffer { renderbuffer, .. } => { gl.framebuffer_renderbuffer( glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::RENDERBUFFER, - Some(surface), + Some(renderbuffer), ); } n::ImageKind::Texture { diff --git a/src/backend/gl/src/lib.rs b/src/backend/gl/src/lib.rs index 182bd94500a..0c16bd9748d 100644 --- a/src/backend/gl/src/lib.rs +++ b/src/backend/gl/src/lib.rs @@ -33,17 +33,17 @@ mod state; mod window; #[cfg(all(not(target_arch = "wasm32"), feature = "glutin"))] -pub use window::glutin::{config_context, Headless, Surface, Swapchain}; +pub use crate::window::glutin::{config_context, Headless, Surface, SurfaceImage, Swapchain}; #[cfg(target_arch = "wasm32")] -pub use window::web::{Surface, Swapchain, Window}; +pub use window::web::{Surface, SurfaceImage, Swapchain, Window}; #[cfg(feature = "wgl")] pub use window::wgl::Instance; #[cfg(feature = "wgl")] -use window::wgl::{DeviceContext, Surface, Swapchain}; +use window::wgl::{DeviceContext, Surface, SurfaceImage, Swapchain}; #[cfg(not(any(target_arch = "wasm32", feature = "glutin", feature = "wgl")))] -pub use window::dummy::{Surface, Swapchain}; +pub use window::dummy::{Surface, SurfaceImage, Swapchain}; #[cfg(not(target_arch = "wasm32"))] pub use glow::native::Context as GlContext; diff --git a/src/backend/gl/src/native.rs b/src/backend/gl/src/native.rs index a70831bd077..dc79d9ab2a9 100644 --- a/src/backend/gl/src/native.rs +++ b/src/backend/gl/src/native.rs @@ -17,7 +17,7 @@ pub type VertexArray = ::VertexArray; pub type RawBuffer = ::Buffer; pub type Shader = ::Shader; pub type Program = ::Program; -pub type Surface = ::Renderbuffer; +pub type Renderbuffer = ::Renderbuffer; pub type Texture = ::Texture; pub type Sampler = ::Sampler; pub type UniformLocation = ::UniformLocation; @@ -181,8 +181,8 @@ pub struct Image { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum ImageKind { - Surface { - surface: Surface, + Renderbuffer { + renderbuffer: Renderbuffer, format: TextureFormat, }, Texture { @@ -203,7 +203,7 @@ pub enum FatSampler { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum ImageView { - Surface(Surface), + Renderbuffer(Renderbuffer), Texture(Texture, TextureTarget, i::Level), TextureLayer(Texture, TextureTarget, i::Level, i::Layer), } diff --git a/src/backend/gl/src/queue.rs b/src/backend/gl/src/queue.rs index e967c88d841..e4ba7983f55 100644 --- a/src/backend/gl/src/queue.rs +++ b/src/backend/gl/src/queue.rs @@ -1,4 +1,3 @@ -use crate::Starc; use std::borrow::Borrow; use std::{mem, slice}; @@ -16,6 +15,10 @@ use crate::{ Backend, GlContext, Share, + Starc, + Swapchain, + Surface, + SurfaceImage, }; // State caching system for command queue. @@ -163,8 +166,8 @@ impl CommandQueue { fn bind_target(&mut self, point: u32, attachment: u32, view: &native::ImageView) { let gl = &self.share.context; match view { - &native::ImageView::Surface(surface) => unsafe { - gl.framebuffer_renderbuffer(point, attachment, glow::RENDERBUFFER, Some(surface)); + &native::ImageView::Renderbuffer(renderbuffer) => unsafe { + gl.framebuffer_renderbuffer(point, attachment, glow::RENDERBUFFER, Some(renderbuffer)); }, &native::ImageView::Texture(texture, _, level) => unsafe { gl.framebuffer_texture(point, attachment, Some(texture), level as i32); @@ -201,6 +204,39 @@ impl CommandQueue { &data[ptr.offset as usize .. (ptr.offset + ptr.size) as usize] } + fn present_by_copy(&self, swapchain: &Swapchain, index: hal::window::SwapImageIndex) { + let gl = &self.share.context; + let extent = swapchain.extent; + + #[cfg(feature = "wgl")] + swapchain.make_current(); + + unsafe { + gl.bind_framebuffer( + glow::READ_FRAMEBUFFER, + Some(swapchain.fbos[index as usize]), + ); + gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None); + gl.blit_framebuffer( + 0, + 0, + extent.width as _, + extent.height as _, + 0, + 0, + extent.width as _, + extent.height as _, + glow::COLOR_BUFFER_BIT, + glow::LINEAR, + ); + } + + #[cfg(all(feature = "glutin", not(target_arch = "wasm32")))] + swapchain.context.swap_buffers().unwrap(); + #[cfg(all(feature = "wgl", not(target_arch = "wasm32")))] + swapchain.swap_buffers(); + } + // Reset the state to match our _expected_ state before executing // a command buffer. fn reset_state(&mut self) { @@ -666,7 +702,7 @@ impl CommandQueue { gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, None); }, - com::Command::CopyBufferToSurface(..) => { + com::Command::CopyBufferToRenderbuffer(..) => { unimplemented!() //TODO: use FBO } com::Command::CopyTextureToBuffer { @@ -698,15 +734,15 @@ impl CommandQueue { ); gl.bind_buffer(glow::PIXEL_PACK_BUFFER, None); }, - com::Command::CopySurfaceToBuffer(..) => { + com::Command::CopyRenderbufferToBuffer(..) => { unimplemented!() //TODO: use FBO } com::Command::CopyImageToTexture(..) => { unimplemented!() //TODO: use FBO } - com::Command::CopyImageToSurface { + com::Command::CopyImageToRenderbuffer { src_image, - dst_surface, + dst_renderbuffer, dst_format, ref data, } => { @@ -720,8 +756,8 @@ impl CommandQueue { match src_image { native::ImageKind::Texture { .. } => unimplemented!(), - native::ImageKind::Surface { - surface: src_surface, + native::ImageKind::Renderbuffer { + renderbuffer: src_renderbuffer, format: src_format, } => { if src_format != dst_format { @@ -735,7 +771,7 @@ impl CommandQueue { glow::READ_FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::RENDERBUFFER, - Some(src_surface), + Some(src_renderbuffer), ); let dst_fbo = gl.create_framebuffer().unwrap(); @@ -744,7 +780,7 @@ impl CommandQueue { glow::DRAW_FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::RENDERBUFFER, - Some(dst_surface), + Some(dst_renderbuffer), ); gl.blit_framebuffer( @@ -1105,37 +1141,24 @@ impl hal::queue::CommandQueue for CommandQueue { S: 'a + Borrow, Iw: IntoIterator, { - let gl = &self.share.context; - for (swapchain, index) in swapchains { - let extent = swapchain.borrow().extent; + self.present_by_copy(swapchain.borrow(), index); + } - #[cfg(feature = "wgl")] - swapchain.borrow().make_current(); + #[cfg(all(feature = "wgl", not(target_arch = "wasm32")))] + self.share.instance_context.make_current(); - gl.bind_framebuffer( - glow::READ_FRAMEBUFFER, - Some(swapchain.borrow().fbos[index as usize]), - ); - gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None); - gl.blit_framebuffer( - 0, - 0, - extent.width as _, - extent.height as _, - 0, - 0, - extent.width as _, - extent.height as _, - glow::COLOR_BUFFER_BIT, - glow::LINEAR, - ); + Ok(None) + } - #[cfg(all(feature = "glutin", not(target_arch = "wasm32")))] - swapchain.borrow().context.swap_buffers().unwrap(); - #[cfg(all(feature = "wgl", not(target_arch = "wasm32")))] - swapchain.borrow().swap_buffers(); - } + unsafe fn present_surface( + &mut self, + surface: &mut Surface, + _image: SurfaceImage, + _wait_semaphore: Option<&native::Semaphore>, + ) -> Result, hal::window::PresentError> { + let swapchain = surface.swapchain.as_ref().expect("No swapchain is configured!"); + self.present_by_copy(swapchain, 0); #[cfg(all(feature = "wgl", not(target_arch = "wasm32")))] self.share.instance_context.make_current(); diff --git a/src/backend/gl/src/window/dummy.rs b/src/backend/gl/src/window/dummy.rs index 7b8f2b692bb..e26a16301b6 100644 --- a/src/backend/gl/src/window/dummy.rs +++ b/src/backend/gl/src/window/dummy.rs @@ -1,8 +1,11 @@ -use crate::{native, Backend, PhysicalDevice, QueueFamily}; +use crate::{native, Device, Backend, PhysicalDevice, QueueFamily}; +use arrayvec::ArrayVec; use hal::window; #[derive(Debug)] -pub struct Surface; +pub struct Surface { + pub(crate) swapchain: Option, +} impl window::Surface for Surface { fn compatibility( @@ -21,10 +24,33 @@ impl window::Surface for Surface { } } +pub type SurfaceImage = native::ImageView; + +impl window::PresentationSurface for Surface { + type SwapchainImage = native::ImageView; + + unsafe fn configure_swapchain( + &mut self, _: &Device, _: window::SwapchainConfig + ) -> Result<(), window::CreationError> { + unimplemented!() + } + + unsafe fn unconfigure_swapchain(&mut self, _: &Device) { + unimplemented!() + } + + unsafe fn acquire_image( + &mut self, + _: u64, + ) -> Result<(Self::SwapchainImage, Option), window::AcquireError> { + unimplemented!() + } +} + #[derive(Debug)] pub struct Swapchain { pub(crate) extent: window::Extent2D, - pub(crate) fbos: Vec, + pub(crate) fbos: ArrayVec<[native::RawFrameBuffer; 0]>, } impl window::Swapchain for Swapchain { diff --git a/src/backend/gl/src/window/glutin.rs b/src/backend/gl/src/window/glutin.rs index 9347bae4dca..e6cf0f35d48 100644 --- a/src/backend/gl/src/window/glutin.rs +++ b/src/backend/gl/src/window/glutin.rs @@ -47,11 +47,15 @@ //! } //! ``` -use crate::{native, Backend as B, GlContainer, PhysicalDevice, QueueFamily, Starc}; +use crate::{conv, native, Backend as B, Device, GlContainer, PhysicalDevice, QueueFamily, Starc}; use hal::{adapter::Adapter, format as f, image, window}; +use arrayvec::ArrayVec; +use glow::Context as _; use glutin; +use std::{borrow::Borrow, iter}; + fn get_window_extent(window: &glutin::window::Window) -> image::Extent { let px = window.inner_size().to_physical(window.hidpi_factor()); image::Extent { @@ -68,7 +72,7 @@ pub struct Swapchain { // Extent because the window lies pub(crate) extent: window::Extent2D, /// - pub(crate) fbos: Vec, + pub(crate) fbos: ArrayVec<[native::RawFrameBuffer; 3]>, } impl window::Swapchain for Swapchain { @@ -86,22 +90,22 @@ impl window::Swapchain for Swapchain { //TODO: if we make `Surface` a `WindowBuilder` instead of `RawContext`, // we could spawn window + GL context when a swapchain is requested // and actually respect the swapchain configuration provided by the user. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Surface { pub(crate) context: Starc>, + pub(crate) swapchain: Option, + renderbuffer: Option, } impl Surface { pub fn from_context(context: glutin::RawContext) -> Self { Surface { + renderbuffer: None, + swapchain: None, context: Starc::new(context), } } - pub fn get_context(&self) -> &glutin::RawContext { - &*self.context - } - pub fn context(&self) -> &glutin::RawContext { &self.context } @@ -121,6 +125,72 @@ impl Surface { } } +#[derive(Debug)] +pub struct SurfaceImage { + view: native::ImageView, +} + +impl Borrow for SurfaceImage { + fn borrow(&self) -> &native::ImageView { + &self.view + } +} + +impl window::PresentationSurface for Surface { + type SwapchainImage = SurfaceImage; + + unsafe fn configure_swapchain( + &mut self, device: &Device, config: window::SwapchainConfig + ) -> Result<(), window::CreationError> { + + let gl = &device.share.context; + + if self.renderbuffer.is_none() { + self.renderbuffer = Some(gl.create_renderbuffer().unwrap()); + } + + let desc = conv::describe_format(config.format).unwrap(); + gl.bind_renderbuffer(glow::RENDERBUFFER, self.renderbuffer); + gl.renderbuffer_storage( + glow::RENDERBUFFER, + desc.tex_internal, + config.extent.width as i32, + config.extent.height as i32, + ); + + if let Some(old) = self.swapchain.take() { + for fbo in old.fbos { + gl.delete_framebuffer(fbo); + } + } + let fbo = gl.create_framebuffer().unwrap(); + self.swapchain = Some(Swapchain { + context: self.context.clone(), + extent: window::Extent2D { + width: config.extent.width, + height: config.extent.height, + }, + fbos: iter::once(fbo).collect(), + }); + + Ok(()) + } + + unsafe fn unconfigure_swapchain(&mut self, _device: &Device) { + //empty + } + + unsafe fn acquire_image( + &mut self, + _timeout_ns: u64, + ) -> Result<(Self::SwapchainImage, Option), window::AcquireError> { + let image = SurfaceImage { + view: native::ImageView::Renderbuffer(self.renderbuffer.unwrap()), + }; + Ok((image, None)) + } +} + impl window::Surface for Surface { fn compatibility( &self, diff --git a/src/backend/gl/src/window/web.rs b/src/backend/gl/src/window/web.rs index 85b39cbdaf6..91d3ab8ff19 100644 --- a/src/backend/gl/src/window/web.rs +++ b/src/backend/gl/src/window/web.rs @@ -1,6 +1,8 @@ use crate::{native, Backend as B, GlContainer, PhysicalDevice, QueueFamily}; use hal::{adapter::Adapter, format as f, image, window}; +use arrayvec::ArrayVec; + struct PixelFormat { color_bits: u32, @@ -42,7 +44,7 @@ impl Window { #[derive(Clone, Debug)] pub struct Swapchain { pub(crate) extent: window::Extent2D, - pub(crate) fbos: Vec, + pub(crate) fbos: ArrayVec<[native::RawFrameBuffer; 3]>, } impl window::Swapchain for Swapchain { @@ -58,11 +60,15 @@ impl window::Swapchain for Swapchain { } #[derive(Copy, Clone, Debug)] -pub struct Surface; +pub struct Surface { + pub(crate) swapchain: Option, +} impl Surface { pub fn from_window(_window: &Window) -> Self { - Surface + Surface { + swapchain: None, + } } fn swapchain_formats(&self) -> Vec { diff --git a/src/backend/gl/src/window/wgl.rs b/src/backend/gl/src/window/wgl.rs index 0677d1c924a..b77c1615fba 100644 --- a/src/backend/gl/src/window/wgl.rs +++ b/src/backend/gl/src/window/wgl.rs @@ -9,6 +9,7 @@ use std::{ use hal::{adapter::Adapter, format as f, image, window}; +use arrayvec::ArrayVec; use lazy_static::lazy_static; use winapi::shared::minwindef::*; use winapi::shared::windef::*; @@ -178,6 +179,7 @@ impl Instance { pub fn create_surface_from_hwnd(&self, hwnd: *mut c_void) -> Surface { Surface { hwnd: hwnd as *mut _, + swapchain: None, } } @@ -211,6 +213,7 @@ impl hal::Instance for Instance { #[derive(Debug)] pub struct Surface { pub(crate) hwnd: HWND, + pub(crate) swapchain: Option, } // TODO: high -msiglreith @@ -267,7 +270,7 @@ impl window::Surface for Surface { #[derive(Debug)] pub struct Swapchain { - pub(crate) fbos: Vec, + pub(crate) fbos: ArrayVec<[native::RawFrameBuffer; 3]>, pub(crate) context: PresentContext, pub(crate) extent: window::Extent2D, } diff --git a/src/backend/metal/src/command.rs b/src/backend/metal/src/command.rs index 51f700e80bc..023b6305202 100644 --- a/src/backend/metal/src/command.rs +++ b/src/backend/metal/src/command.rs @@ -2289,6 +2289,27 @@ impl hal::queue::CommandQueue for CommandQueue { Ok(None) } + unsafe fn present_surface( + &mut self, + _surface: &mut window::Surface, + image: window::SurfaceImage, + wait_semaphore: Option<&native::Semaphore>, + ) -> Result, PresentError> { + self.wait(wait_semaphore); + + let queue = self.shared.queue.lock(); + let drawable = image.into_drawable(); + autoreleasepool(|| { + let command_buffer = queue.raw.new_command_buffer(); + command_buffer.set_label("present"); + self.record_empty(command_buffer); + + command_buffer.present_drawable(&drawable); + command_buffer.commit(); + }); + Ok(None) + } + fn wait_idle(&self) -> Result<(), error::HostExecutionError> { QueueInner::wait_idle(&self.shared.queue); Ok(()) @@ -3379,7 +3400,7 @@ impl com::CommandBuffer for CommandBuffer { for (i, &(at_id, op_flags, resolve_id)) in subpass.colors.iter().enumerate() { let rat = &render_pass.attachments[at_id]; - let texture = &framebuffer.attachments[at_id]; + let texture = framebuffer.attachments[at_id].as_ref(); let desc = descriptor.color_attachments().object_at(i as _).unwrap(); combined_aspects |= Aspects::COLOR; @@ -3405,7 +3426,7 @@ impl com::CommandBuffer for CommandBuffer { if let Some((at_id, op_flags)) = subpass.depth_stencil { let rat = &render_pass.attachments[at_id]; - let texture = &framebuffer.attachments[at_id]; + let texture = framebuffer.attachments[at_id].as_ref(); let aspects = rat.format.unwrap().surface_desc().aspects; combined_aspects |= aspects; diff --git a/src/backend/metal/src/device.rs b/src/backend/metal/src/device.rs index e3845d4c740..1fd953832b0 100644 --- a/src/backend/metal/src/device.rs +++ b/src/backend/metal/src/device.rs @@ -1651,7 +1651,7 @@ impl hal::device::Device for Device { extent, attachments: attachments .into_iter() - .map(|at| at.borrow().raw.clone()) + .map(|at| at.borrow().texture.clone()) .collect(), }) } @@ -2031,11 +2031,11 @@ impl hal::device::Device for Device { data.samplers[counters.samplers as usize] = Some(AsNative::from(sam.raw.as_ref().unwrap().as_ref())); } - pso::Descriptor::Image(tex, il) => { + pso::Descriptor::Image(view, il) => { data.textures[counters.textures as usize] = - Some((AsNative::from(tex.raw.as_ref()), il)); + Some((AsNative::from(view.texture.as_ref()), il)); } - pso::Descriptor::CombinedImageSampler(tex, il, sam) => { + pso::Descriptor::CombinedImageSampler(view, il, sam) => { if !layout .content .contains(n::DescriptorContent::IMMUTABLE_SAMPLER) @@ -2044,7 +2044,7 @@ impl hal::device::Device for Device { Some(AsNative::from(sam.raw.as_ref().unwrap().as_ref())); } data.textures[counters.textures as usize] = - Some((AsNative::from(tex.raw.as_ref()), il)); + Some((AsNative::from(view.texture.as_ref()), il)); } pso::Descriptor::UniformTexelBuffer(view) | pso::Descriptor::StorageTexelBuffer(view) => { @@ -2098,8 +2098,9 @@ impl hal::device::Device for Device { arg_index += 1; } pso::Descriptor::Image(image, _layout) => { - encoder.set_texture(&image.raw, arg_index); - data.ptr = (&**image.raw).as_ptr(); + let tex_ref = image.texture.as_ref(); + encoder.set_texture(tex_ref, arg_index); + data.ptr = (&**tex_ref).as_ptr(); arg_index += 1; } pso::Descriptor::CombinedImageSampler(image, _il, sampler) => { @@ -2121,8 +2122,9 @@ impl hal::device::Device for Device { arg_index + binding.count as NSUInteger, ); } - encoder.set_texture(&image.raw, arg_index); - data.ptr = (&**image.raw).as_ptr(); + let tex_ref = image.texture.as_ref(); + encoder.set_texture(tex_ref, arg_index); + data.ptr = (&**tex_ref).as_ptr(); } pso::Descriptor::UniformTexelBuffer(view) | pso::Descriptor::StorageTexelBuffer(view) => { @@ -2670,7 +2672,7 @@ impl hal::device::Device for Device { conv::map_texture_type(kind) }; - let view = if mtl_format == image.mtl_format + let texture = if mtl_format == image.mtl_format && mtl_type == image.mtl_type && swizzle == format::Swizzle::NO && range == full_range @@ -2694,7 +2696,7 @@ impl hal::device::Device for Device { }; Ok(n::ImageView { - raw: view, + texture, mtl_format, }) } diff --git a/src/backend/metal/src/native.rs b/src/backend/metal/src/native.rs index 4b43b92dc0b..ac6ddf1c0bb 100644 --- a/src/backend/metal/src/native.rs +++ b/src/backend/metal/src/native.rs @@ -98,9 +98,6 @@ pub struct RenderPass { pub(crate) subpasses: Vec, } -unsafe impl Send for RenderPass {} -unsafe impl Sync for RenderPass {} - #[derive(Debug)] pub struct Framebuffer { pub(crate) extent: image::Extent, @@ -110,6 +107,7 @@ pub struct Framebuffer { unsafe impl Send for Framebuffer {} unsafe impl Sync for Framebuffer {} + #[derive(Clone, Debug)] pub struct ResourceData { pub buffers: T, @@ -388,7 +386,7 @@ unsafe impl Sync for BufferView {} #[derive(Debug)] pub struct ImageView { - pub(crate) raw: metal::Texture, + pub(crate) texture: metal::Texture, pub(crate) mtl_format: metal::MTLPixelFormat, } diff --git a/src/backend/metal/src/window.rs b/src/backend/metal/src/window.rs index d971e151f7d..05d70cf8bb2 100644 --- a/src/backend/metal/src/window.rs +++ b/src/backend/metal/src/window.rs @@ -4,6 +4,7 @@ use crate::{ native, Backend, QueueFamily, + Shared, }; use hal::{format, image, window as w}; @@ -16,6 +17,7 @@ use objc::rc::autoreleasepool; use objc::runtime::Object; use parking_lot::{Mutex, MutexGuard}; +use std::borrow::Borrow; use std::ptr::NonNull; use std::sync::Arc; use std::thread; @@ -32,6 +34,7 @@ const SIGNPOST_ID: u32 = 0x100; #[derive(Debug)] pub struct Surface { inner: Arc, + swapchain_format: metal::MTLPixelFormat, main_thread_id: thread::ThreadId, } @@ -77,10 +80,65 @@ impl SurfaceInner { self.enable_signposts = enable_signposts; Surface { inner: Arc::new(self), + swapchain_format: metal::MTLPixelFormat::Invalid, main_thread_id: thread::current().id(), } } + fn configure(&self, shared: &Shared, config: &w::SwapchainConfig) -> metal::MTLPixelFormat { + info!("build swapchain {:?}", config); + + let caps = &shared.private_caps; + let mtl_format = caps + .map_format(config.format) + .expect("unsupported backbuffer format"); + + let render_layer_borrow = self.render_layer.lock(); + let render_layer = *render_layer_borrow; + let framebuffer_only = config.image_usage == image::Usage::COLOR_ATTACHMENT; + let display_sync = config.present_mode != w::PresentMode::Immediate; + let is_mac = caps.os_is_mac; + let can_set_next_drawable_timeout = if is_mac { + caps.has_version_at_least(10, 13) + } else { + caps.has_version_at_least(11, 0) + }; + let can_set_display_sync = is_mac && caps.has_version_at_least(10, 13); + let drawable_size = CGSize::new(config.extent.width as f64, config.extent.height as f64); + + let device_raw = shared.device.lock().as_ptr(); + unsafe { + // On iOS, unless the user supplies a view with a CAMetalLayer, we + // create one as a sublayer. However, when the view changes size, + // its sublayers are not automatically resized, and we must resize + // it here. The drawable size and the layer size don't correlate + #[cfg(target_os = "ios")] + { + if let Some(view) = surface.inner.view { + let main_layer: *mut Object = msg_send![view.as_ptr(), layer]; + let bounds: CGRect = msg_send![main_layer, bounds]; + let () = msg_send![render_layer, setFrame: bounds]; + } + } + let () = msg_send![render_layer, setDevice: device_raw]; + let () = msg_send![render_layer, setPixelFormat: mtl_format]; + let () = msg_send![render_layer, setFramebufferOnly: framebuffer_only]; + + // this gets ignored on iOS for certain OS/device combinations (iphone5s iOS 10.3) + let () = msg_send![render_layer, setMaximumDrawableCount: config.image_count as u64]; + + let () = msg_send![render_layer, setDrawableSize: drawable_size]; + if can_set_next_drawable_timeout { + let () = msg_send![render_layer, setAllowsNextDrawableTimeout:false]; + } + if can_set_display_sync { + let () = msg_send![render_layer, setDisplaySyncEnabled: display_sync]; + } + }; + + mtl_format + } + fn next_frame<'a>( &self, frames: &'a [Frame], @@ -293,6 +351,27 @@ impl SwapchainImage { } } +#[derive(Debug)] +pub struct SurfaceImage { + view: native::ImageView, + drawable: metal::Drawable, +} + +unsafe impl Send for SurfaceImage {} +unsafe impl Sync for SurfaceImage {} + +impl SurfaceImage { + pub(crate) fn into_drawable(self) -> metal::Drawable { + self.drawable + } +} + +impl Borrow for SurfaceImage { + fn borrow(&self) -> &native::ImageView { + &self.view + } +} + impl w::Surface for Surface { fn supports_queue_family(&self, _queue_family: &QueueFamily) -> bool { // we only expose one family atm, so it's compatible @@ -365,6 +444,44 @@ impl w::Surface for Surface { } } +impl w::PresentationSurface for Surface { + type SwapchainImage = SurfaceImage; + + unsafe fn configure_swapchain( + &mut self, device: &Device, config: w::SwapchainConfig + ) -> Result<(), w::CreationError> { + assert!(image::Usage::COLOR_ATTACHMENT.contains(config.image_usage)); + self.swapchain_format = self.inner.configure(&device.shared, &config); + Ok(()) + } + + unsafe fn unconfigure_swapchain(&mut self, _device: &Device) { + self.swapchain_format = metal::MTLPixelFormat::Invalid; + } + + unsafe fn acquire_image( + &mut self, + _timeout_ns: u64, //TODO: use the timeout + ) -> Result<(Self::SwapchainImage, Option), w::AcquireError> { + let render_layer_borrow = self.inner.render_layer.lock(); + let (drawable, texture) = autoreleasepool(|| { + let drawable: &metal::DrawableRef = msg_send![*render_layer_borrow, nextDrawable]; + assert!(!drawable.as_ptr().is_null()); + let texture: &metal::TextureRef = msg_send![drawable, texture]; + (drawable.to_owned(), texture.to_owned()) + }); + + let image = SurfaceImage { + view: native::ImageView { + texture, + mtl_format: self.swapchain_format, + }, + drawable, + }; + Ok((image, None)) + } +} + impl Device { pub(crate) fn build_swapchain( &self, @@ -372,69 +489,22 @@ impl Device { config: w::SwapchainConfig, old_swapchain: Option, ) -> (Swapchain, Vec) { - info!("build_swapchain {:?}", config); if let Some(ref sc) = old_swapchain { sc.clear_drawables(); } - let caps = &self.shared.private_caps; - let mtl_format = caps - .map_format(config.format) - .expect("unsupported backbuffer format"); - - let render_layer_borrow = surface.inner.render_layer.lock(); - let render_layer = *render_layer_borrow; - let format_desc = config.format.surface_desc(); - let framebuffer_only = config.image_usage == image::Usage::COLOR_ATTACHMENT; - let display_sync = config.present_mode != w::PresentMode::Immediate; - let is_mac = caps.os_is_mac; - let can_set_next_drawable_timeout = if is_mac { - caps.has_version_at_least(10, 13) - } else { - caps.has_version_at_least(11, 0) - }; - let can_set_display_sync = is_mac && caps.has_version_at_least(10, 13); - let drawable_size = CGSize::new(config.extent.width as f64, config.extent.height as f64); + let mtl_format = surface.inner.configure(&self.shared, &config); let cmd_queue = self.shared.queue.lock(); - - unsafe { - // On iOS, unless the user supplies a view with a CAMetalLayer, we - // create one as a sublayer. However, when the view changes size, - // its sublayers are not automatically resized, and we must resize - // it here. The drawable size and the layer size don't correlate - #[cfg(target_os = "ios")] - { - if let Some(view) = surface.inner.view { - let main_layer: *mut Object = msg_send![view.as_ptr(), layer]; - let bounds: CGRect = msg_send![main_layer, bounds]; - let () = msg_send![render_layer, setFrame: bounds]; - } - } - - let device_raw = self.shared.device.lock().as_ptr(); - let () = msg_send![render_layer, setDevice: device_raw]; - let () = msg_send![render_layer, setPixelFormat: mtl_format]; - let () = msg_send![render_layer, setFramebufferOnly: framebuffer_only]; - - // this gets ignored on iOS for certain OS/device combinations (iphone5s iOS 10.3) - let () = msg_send![render_layer, setMaximumDrawableCount: config.image_count as u64]; - - let () = msg_send![render_layer, setDrawableSize: drawable_size]; - if can_set_next_drawable_timeout { - let () = msg_send![render_layer, setAllowsNextDrawableTimeout:false]; - } - if can_set_display_sync { - let () = msg_send![render_layer, setDisplaySyncEnabled: display_sync]; - } - }; + let format_desc = config.format.surface_desc(); + let render_layer_borrow = surface.inner.render_layer.lock(); let frames = (0 .. config.image_count) .map(|index| { autoreleasepool(|| { // for the drawable & texture let (drawable, texture) = unsafe { - let drawable: &metal::DrawableRef = msg_send![render_layer, nextDrawable]; + let drawable: &metal::DrawableRef = msg_send![*render_layer_borrow, nextDrawable]; assert!(!drawable.as_ptr().is_null()); let texture: &metal::TextureRef = msg_send![drawable, texture]; (drawable, texture) diff --git a/src/backend/vulkan/src/device.rs b/src/backend/vulkan/src/device.rs index 7798dd3e17e..730b018d7df 100644 --- a/src/backend/vulkan/src/device.rs +++ b/src/backend/vulkan/src/device.rs @@ -1033,18 +1033,26 @@ impl d::Device for Device { T: IntoIterator, T::Item: Borrow, { - let attachments_raw = attachments - .into_iter() - .map(|attachment| attachment.borrow().view) - .collect::>(); + let mut framebuffers_ptr = None; + let mut raw_attachments = SmallVec::<[_; 4]>::new(); + for attachment in attachments { + let at = attachment.borrow(); + raw_attachments.push(at.view); + match at.owner { + n::ImageViewOwner::User => {} + n::ImageViewOwner::Surface(ref fbo_ptr) => { + framebuffers_ptr = Some(Arc::clone(&fbo_ptr.0)); + } + } + } let info = vk::FramebufferCreateInfo { s_type: vk::StructureType::FRAMEBUFFER_CREATE_INFO, p_next: ptr::null(), flags: vk::FramebufferCreateFlags::empty(), render_pass: renderpass.raw, - attachment_count: attachments_raw.len() as u32, - p_attachments: attachments_raw.as_ptr(), + attachment_count: raw_attachments.len() as u32, + p_attachments: raw_attachments.as_ptr(), width: extent.width, height: extent.height, layers: extent.depth, @@ -1053,7 +1061,16 @@ impl d::Device for Device { let result = self.raw.0.create_framebuffer(&info, None); match result { - Ok(raw) => Ok(n::Framebuffer { raw }), + Ok(raw) => Ok(n::Framebuffer { + raw, + owned: match framebuffers_ptr { + Some(fbo_ptr) => { + fbo_ptr.lock().unwrap().framebuffers.push(raw); + true + } + None => false, + }, + }), Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(d::OutOfMemory::OutOfHostMemory), Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(d::OutOfMemory::OutOfDeviceMemory), _ => unreachable!(), @@ -1375,6 +1392,7 @@ impl d::Device for Device { image: image.raw, view, range, + owner: n::ImageViewOwner::User, }), Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(d::OutOfMemory::OutOfHostMemory.into()), Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { @@ -2057,7 +2075,9 @@ impl d::Device for Device { } unsafe fn destroy_framebuffer(&self, fb: n::Framebuffer) { - self.raw.0.destroy_framebuffer(fb.raw, None); + if fb.owned { + self.raw.0.destroy_framebuffer(fb.raw, None); + } } unsafe fn destroy_buffer(&self, buffer: n::Buffer) { @@ -2073,7 +2093,14 @@ impl d::Device for Device { } unsafe fn destroy_image_view(&self, view: n::ImageView) { - self.raw.0.destroy_image_view(view.view, None); + match view.owner { + n::ImageViewOwner::User => { + self.raw.0.destroy_image_view(view.view, None); + } + n::ImageViewOwner::Surface(_fbo_cache) => { + //TODO: mark as deleted? + } + } } unsafe fn destroy_sampler(&self, sampler: n::Sampler) { diff --git a/src/backend/vulkan/src/lib.rs b/src/backend/vulkan/src/lib.rs index 03df4f98646..de21502d816 100644 --- a/src/backend/vulkan/src/lib.rs +++ b/src/backend/vulkan/src/lib.rs @@ -1216,6 +1216,55 @@ impl queue::CommandQueue for CommandQueue { } } + unsafe fn present_surface( + &mut self, + surface: &mut window::Surface, + image: window::SurfaceImage, + wait_semaphore: Option<&native::Semaphore>, + ) -> Result, PresentError> { + let ssc = surface.swapchain.as_ref().unwrap(); + let submit_info = vk::SubmitInfo { + s_type: vk::StructureType::SUBMIT_INFO, + p_next: ptr::null(), + wait_semaphore_count: 0, + p_wait_semaphores: wait_semaphore.map_or(ptr::null(), |s| &s.0 as *const _), + p_wait_dst_stage_mask: &vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, + command_buffer_count: 0, + p_command_buffers: ptr::null(), + signal_semaphore_count: 1, + p_signal_semaphores: &ssc.semaphore.0, + }; + self.device.0 + .queue_submit(*self.raw, &[submit_info], vk::Fence::null()) + .unwrap(); + + let present_info = vk::PresentInfoKHR { + s_type: vk::StructureType::PRESENT_INFO_KHR, + p_next: ptr::null(), + wait_semaphore_count: 1, + p_wait_semaphores: &ssc.semaphore.0, + swapchain_count: 1, + p_swapchains: &ssc.swapchain.raw, + p_image_indices: &image.index, + p_results: ptr::null_mut(), + }; + + match self.swapchain_fn.queue_present_khr(*self.raw, &present_info) { + vk::Result::SUCCESS => Ok(None), + vk::Result::SUBOPTIMAL_KHR => Ok(Some(Suboptimal)), + vk::Result::ERROR_OUT_OF_HOST_MEMORY => { + Err(PresentError::OutOfMemory(OutOfMemory::OutOfHostMemory)) + } + vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => { + Err(PresentError::OutOfMemory(OutOfMemory::OutOfDeviceMemory)) + } + vk::Result::ERROR_DEVICE_LOST => Err(PresentError::DeviceLost(DeviceLost)), + vk::Result::ERROR_OUT_OF_DATE_KHR => Err(PresentError::OutOfDate), + vk::Result::ERROR_SURFACE_LOST_KHR => Err(PresentError::SurfaceLost(SurfaceLost)), + _ => panic!("Failed to present frame"), + } + } + fn wait_idle(&self) -> Result<(), HostExecutionError> { unsafe { self.device diff --git a/src/backend/vulkan/src/native.rs b/src/backend/vulkan/src/native.rs index e3d7b9a789d..de3de2197a2 100644 --- a/src/backend/vulkan/src/native.rs +++ b/src/backend/vulkan/src/native.rs @@ -1,10 +1,16 @@ -use crate::hal::image::SubresourceRange; -use crate::hal::pso; -use crate::{Backend, RawDevice}; -use ash::version::DeviceV1_0; -use ash::vk; -use std::borrow::Borrow; -use std::sync::Arc; +use crate::{Backend, RawDevice, window::FramebufferCachePtr}; +use hal::{ + image::SubresourceRange, + pso, +}; +use ash::{ + version::DeviceV1_0, + vk, +}; +use std::{ + borrow::Borrow, + sync::Arc, +}; #[derive(Debug, Hash)] pub struct Semaphore(pub vk::Semaphore); @@ -47,11 +53,18 @@ pub struct Image { pub(crate) extent: vk::Extent3D, } +#[derive(Debug, Hash, PartialEq, Eq)] +pub enum ImageViewOwner { + User, + Surface(FramebufferCachePtr), +} + #[derive(Debug, Hash, PartialEq, Eq)] pub struct ImageView { pub(crate) image: vk::Image, pub(crate) view: vk::ImageView, pub(crate) range: SubresourceRange, + pub(crate) owner: ImageViewOwner, } #[derive(Debug, Hash)] @@ -66,6 +79,7 @@ pub struct RenderPass { #[derive(Debug, Hash)] pub struct Framebuffer { pub(crate) raw: vk::Framebuffer, + pub(crate) owned: bool, } #[derive(Debug)] diff --git a/src/backend/vulkan/src/window.rs b/src/backend/vulkan/src/window.rs index 2f2dba3f4f1..444d98d26a1 100644 --- a/src/backend/vulkan/src/window.rs +++ b/src/backend/vulkan/src/window.rs @@ -1,17 +1,59 @@ -use std::os::raw::c_void; -use std::ptr; -use std::sync::Arc; - -use ash::extensions::khr; -use ash::vk; +use std::{ + borrow::Borrow, + hash, + os::raw::c_void, + ptr, + sync::{Arc, Mutex}, + time::Instant, +}; + +use ash::{ + extensions::khr, + version::DeviceV1_0 as _, + vk, +}; use hal::{format::Format, window as w}; -#[cfg(feature = "winit")] -use winit; - use crate::{conv, native}; -use crate::{Backend, Instance, PhysicalDevice, QueueFamily, RawInstance, VK_ENTRY}; +use crate::{Backend, Instance, Device, PhysicalDevice, QueueFamily, RawDevice, RawInstance, VK_ENTRY}; + + +#[derive(Debug, Default)] +pub struct FramebufferCache { + pub framebuffers: Vec, +} + +#[derive(Debug, Default)] +pub struct FramebufferCachePtr(pub Arc>); + +impl hash::Hash for FramebufferCachePtr { + fn hash(&self, hasher: &mut H) { + (self.0.as_ref() as *const Mutex).hash(hasher) + } +} +impl PartialEq for FramebufferCachePtr { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} +impl Eq for FramebufferCachePtr {} + +#[derive(Debug)] +struct SurfaceFrame { + image: vk::Image, + view: vk::ImageView, + framebuffers: FramebufferCachePtr, +} + +#[derive(Debug)] +pub struct SurfaceSwapchain { + pub(crate) swapchain: Swapchain, + device: Arc, + fence: native::Fence, + pub(crate) semaphore: native::Semaphore, + frames: Vec, +} #[derive(Derivative)] #[derivative(Debug)] @@ -20,6 +62,9 @@ pub struct Surface { // For vkDestroySurfaceKHR: Host access to surface must be externally synchronized #[derivative(Debug = "ignore")] pub(crate) raw: Arc, + + pub(crate) swapchain: Option, + leftovers: Vec, } pub struct RawSurface { @@ -309,7 +354,11 @@ impl Instance { instance: self.raw.clone(), }); - Surface { raw } + Surface { + raw, + swapchain: None, + leftovers: Vec::new(), + } } } @@ -413,6 +462,148 @@ impl w::Surface for Surface { } } +#[derive(Debug)] +pub struct SurfaceImage { + pub(crate) index: w::SwapImageIndex, + view: native::ImageView, +} + +impl Borrow for SurfaceImage { + fn borrow(&self) -> &native::ImageView { + &self.view + } +} + +impl w::PresentationSurface for Surface { + type SwapchainImage = SurfaceImage; + + unsafe fn configure_swapchain( + &mut self, device: &Device, config: w::SwapchainConfig + ) -> Result<(), w::CreationError> { + use hal::device::Device as _; + + let format = config.format; + let old = match self.swapchain.take() { + Some(ssc) => { + let cloned = Swapchain { + raw: ssc.swapchain.raw, + functor: ssc.swapchain.functor.clone(), + }; + self.leftovers.push(ssc); + Some(cloned) + } + None => None, + }; + + let (swapchain, images) = device.create_swapchain(self, config, old)?; + self.swapchain = Some(SurfaceSwapchain { + swapchain, + device: Arc::clone(&device.raw), + fence: device.create_fence(false).unwrap(), + semaphore: device.create_semaphore().unwrap(), + frames: images + .iter() + .map(|image| { + let view = device + .create_image_view( + image, + hal::image::ViewKind::D2, + format, + hal::format::Swizzle::NO, + hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + layers: 0 .. 1, + levels: 0 .. 1, + }, + ) + .unwrap(); + SurfaceFrame { + image: view.image, + view: view.view, + framebuffers: Default::default(), + } + }) + .collect(), + }); + + Ok(()) + } + + unsafe fn unconfigure_swapchain(&mut self, device: &Device) { + self.leftovers.extend(self.swapchain.take()); + let _ = device.raw.0.device_wait_idle(); + for ssc in self.leftovers.drain(..) { + device.raw.0.destroy_fence(ssc.fence.0, None); + device.raw.0.destroy_semaphore(ssc.semaphore.0, None); + for frame in ssc.frames { + device.raw.0.destroy_image_view(frame.view, None); + for framebuffer in frame.framebuffers.0.lock().unwrap().framebuffers.drain(..) { + device.raw.0.destroy_framebuffer(framebuffer, None); + } + } + ssc.swapchain.functor.destroy_swapchain(ssc.swapchain.raw, None); + } + } + + unsafe fn acquire_image( + &mut self, + mut timeout_ns: u64, + ) -> Result<(Self::SwapchainImage, Option), w::AcquireError> { + use hal::window::Swapchain as _; + + let ssc = self.swapchain.as_mut().unwrap(); + let moment = Instant::now(); + let (index, suboptimal) = ssc.swapchain.acquire_image(timeout_ns, None, Some(&ssc.fence))?; + timeout_ns -= moment.elapsed().as_nanos() as u64; + let fences = &[ssc.fence.0]; + + match ssc.device.0.wait_for_fences(fences, true, timeout_ns) { + Ok(()) => { + ssc.device.0.reset_fences(fences).unwrap(); + let frame = &ssc.frames[index as usize]; + // We have just waited for the frame to be fully available on CPU. + // All the associated framebuffers are expected to be destroyed by now. + for framebuffer in frame.framebuffers.0.lock().unwrap().framebuffers.drain(..) { + ssc.device.0.destroy_framebuffer(framebuffer, None); + } + let image = Self::SwapchainImage { + index, + view: native::ImageView { + image: frame.image, + view: frame.view, + range: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + layers: 0 .. 1, + levels: 0 .. 1, + }, + owner: native::ImageViewOwner::Surface( + FramebufferCachePtr(Arc::clone(&frame.framebuffers.0)) + ), + }, + }; + Ok((image, suboptimal)) + }, + Err(vk::Result::NOT_READY) => Err(w::AcquireError::NotReady), + Err(vk::Result::TIMEOUT) => Err(w::AcquireError::Timeout), + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Err(w::AcquireError::OutOfDate), + Err(vk::Result::ERROR_SURFACE_LOST_KHR) => { + Err(w::AcquireError::SurfaceLost(hal::device::SurfaceLost)) + } + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(w::AcquireError::OutOfMemory( + hal::device::OutOfMemory::OutOfHostMemory, + )), + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(w::AcquireError::OutOfMemory( + hal::device::OutOfMemory::OutOfDeviceMemory, + )), + Err(vk::Result::ERROR_DEVICE_LOST) => { + Err(w::AcquireError::DeviceLost(hal::device::DeviceLost)) + } + _ => unreachable!(), + } + } +} + + #[derive(Derivative)] #[derivative(Debug)] pub struct Swapchain { @@ -437,13 +628,8 @@ impl w::Swapchain for Swapchain { .acquire_next_image(self.raw, timeout_ns, semaphore, fence); match index { - Ok((i, suboptimal)) => { - if suboptimal { - Ok((i, Some(w::Suboptimal))) - } else { - Ok((i, None)) - } - } + Ok((i, true)) => Ok((i, Some(w::Suboptimal))), + Ok((i, false)) => Ok((i, None)), Err(vk::Result::NOT_READY) => Err(w::AcquireError::NotReady), Err(vk::Result::TIMEOUT) => Err(w::AcquireError::Timeout), Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Err(w::AcquireError::OutOfDate), diff --git a/src/hal/src/lib.rs b/src/hal/src/lib.rs index 5b3d63f585a..49f10e7318e 100644 --- a/src/hal/src/lib.rs +++ b/src/hal/src/lib.rs @@ -46,7 +46,7 @@ pub mod prelude { pool::CommandPool as _, pso::DescriptorPool as _, queue::{CommandQueue as _, QueueFamily as _}, - window::{Surface as _, Swapchain as _}, + window::{PresentationSurface as _, Surface as _, Swapchain as _}, Instance as _, }; } @@ -478,7 +478,7 @@ pub trait Backend: 'static + Sized + Eq + Clone + Hash + fmt::Debug + Any + Send type PhysicalDevice: adapter::PhysicalDevice; type Device: device::Device; - type Surface: window::Surface; + type Surface: window::PresentationSurface; type Swapchain: window::Swapchain; type QueueFamily: queue::QueueFamily; diff --git a/src/hal/src/queue/mod.rs b/src/hal/src/queue/mod.rs index 1887968d374..8f7476482fb 100644 --- a/src/hal/src/queue/mod.rs +++ b/src/hal/src/queue/mod.rs @@ -10,7 +10,7 @@ pub mod family; use crate::error::HostExecutionError; use crate::pso; -use crate::window::{PresentError, Suboptimal, SwapImageIndex}; +use crate::window::{PresentError, PresentationSurface, Suboptimal, SwapImageIndex}; use crate::Backend; use std::{any::Any, borrow::Borrow, fmt, iter}; @@ -134,6 +134,14 @@ pub trait CommandQueue: fmt::Debug + Any + Send + Sync { self.present::<_, _, B::Semaphore, _>(swapchains, iter::empty()) } + /// Present the a + unsafe fn present_surface( + &mut self, + surface: &mut B::Surface, + image: >::SwapchainImage, + wait_semaphore: Option<&B::Semaphore>, + ) -> Result, PresentError>; + /// Wait for the queue to idle. fn wait_idle(&self) -> Result<(), HostExecutionError>; } diff --git a/src/hal/src/window.rs b/src/hal/src/window.rs index 4de9f96518f..b8e145cb349 100644 --- a/src/hal/src/window.rs +++ b/src/hal/src/window.rs @@ -169,8 +169,25 @@ pub struct SurfaceCapabilities { pub composite_alpha: CompositeAlpha, } -/// A `Surface` abstracts the surface of a native window, which will be presented -/// on the display. +impl SurfaceCapabilities { + fn clamped_extent(&self, default_extent: Extent2D) -> Extent2D { + match self.current_extent { + Some(current) => current, + None => { + let (min_width, max_width) = (self.extents.start().width, self.extents.end().width); + let (min_height, max_height) = (self.extents.start().height, self.extents.end().height); + + // clamp the default_extent to within the allowed surface sizes + let width = min(max_width, max(default_extent.width, min_width)); + let height = min(max_height, max(default_extent.height, min_height)); + + Extent2D { width, height } + } + } + } +} + +/// A `Surface` abstracts the surface of a native window. pub trait Surface: fmt::Debug + Any + Send + Sync { /// Check if the queue family supports presentation to this surface. /// @@ -194,6 +211,40 @@ pub trait Surface: fmt::Debug + Any + Send + Sync { ) -> (SurfaceCapabilities, Option>, Vec); } +/// A surface trait that exposes the ability to present images on the +/// associtated swap chain. +pub trait PresentationSurface: Surface { + /// An opaque type wrapping the swapchain image. + type SwapchainImage: Borrow + fmt::Debug + Send + Sync; + + /// Set up the swapchain associated with the surface to have the given format. + unsafe fn configure_swapchain( + &mut self, device: &B::Device, config: SwapchainConfig + ) -> Result<(), CreationError>; + + /// Remove the associated swapchain from this surface. + /// + /// This has to be done before the surface is dropped. + unsafe fn unconfigure_swapchain(&mut self, device: &B::Device); + + /// Acquire a new swapchain image for rendering. + /// + /// May fail according to one of the reasons indicated in `AcquireError` enum. + /// + /// # Synchronization + /// + /// The acquired image is available to render. No synchronization is required. + /// + /// # Examples + /// + /// ```no_run + /// + /// ``` + unsafe fn acquire_image( + &mut self, timeout_ns: u64, + ) -> Result<(Self::SwapchainImage, Option), AcquireError>; +} + /// Index of an image in the swapchain. /// /// The swapchain is a series of one or more images, usually @@ -307,21 +358,6 @@ impl SwapchainConfig { /// returned from a physical device query. If the surface does not /// specify a current size, default_extent is clamped and used instead. pub fn from_caps(caps: &SurfaceCapabilities, format: Format, default_extent: Extent2D) -> Self { - let clamped_extent = match caps.current_extent { - Some(current) => current, - None => { - let (min_width, max_width) = (caps.extents.start().width, caps.extents.end().width); - let (min_height, max_height) = - (caps.extents.start().height, caps.extents.end().height); - - // clamp the default_extent to within the allowed surface sizes - let width = min(max_width, max(default_extent.width, min_width)); - let height = min(max_height, max(default_extent.height, min_height)); - - Extent2D { width, height } - } - }; - let composite_alpha = if caps.composite_alpha.contains(CompositeAlpha::INHERIT) { CompositeAlpha::INHERIT } else if caps.composite_alpha.contains(CompositeAlpha::OPAQUE) { @@ -334,7 +370,7 @@ impl SwapchainConfig { present_mode: PresentMode::Fifo, composite_alpha, format, - extent: clamped_extent, + extent: caps.clamped_extent(default_extent), image_count: *caps.image_count.start(), image_layers: 1, image_usage: image::Usage::COLOR_ATTACHMENT,