diff --git a/Cargo.toml b/Cargo.toml index f7e573ba..c0b8cb3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members = [ "shader", "wsi", "chain", - "graph", +# "graph", "core", "texture", ] diff --git a/command/src/buffer/encoder.rs b/command/src/buffer/encoder.rs index 4975980f..b4796f2a 100644 --- a/command/src/buffer/encoder.rs +++ b/command/src/buffer/encoder.rs @@ -104,11 +104,9 @@ where self.capability.assert(); rendy_core::hal::command::CommandBuffer::bind_index_buffer( self.raw, - rendy_core::hal::buffer::IndexBufferView { - buffer, - range: rendy_core::hal::buffer::SubRange { offset, size: None }, - index_type, - }, + buffer, + rendy_core::hal::buffer::SubRange { offset, size: None }, + index_type, ) } @@ -126,11 +124,10 @@ where /// device limit. /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBindVertexBuffers.html - pub unsafe fn bind_vertex_buffers<'b>( - &mut self, - first_binding: u32, - buffers: impl IntoIterator, - ) where + pub unsafe fn bind_vertex_buffers<'b, I>(&mut self, first_binding: u32, buffers: I) + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, C: Supports, { self.capability.assert(); @@ -168,13 +165,15 @@ where /// # Safety /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBindDescriptorSets.html - pub unsafe fn bind_graphics_descriptor_sets<'b>( + pub unsafe fn bind_graphics_descriptor_sets<'b, I, J>( &mut self, layout: &B::PipelineLayout, first_set: u32, - sets: impl IntoIterator, - offsets: impl IntoIterator, + sets: I, + offsets: J, ) where + I: Iterator, + J: Iterator, C: Supports, { self.capability.assert(); @@ -205,13 +204,15 @@ where /// # Safety /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBindDescriptorSets.html - pub unsafe fn bind_compute_descriptor_sets<'b>( + pub unsafe fn bind_compute_descriptor_sets<'b, I, J>( &mut self, layout: &B::PipelineLayout, first_set: u32, - sets: impl IntoIterator, - offsets: impl IntoIterator, + sets: I, + offsets: J, ) where + I: Iterator, + J: Iterator, C: Supports, { self.capability.assert(); @@ -234,7 +235,7 @@ where &mut self, stages: std::ops::Range, dependencies: rendy_core::hal::memory::Dependencies, - barriers: impl IntoIterator>, + barriers: impl Iterator>, ) { rendy_core::hal::command::CommandBuffer::pipeline_barrier( self.raw, @@ -268,11 +269,9 @@ where /// Set viewports /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdSetViewport.html - pub unsafe fn set_viewports<'b>( - &mut self, - first_viewport: u32, - viewports: impl IntoIterator, - ) where + pub unsafe fn set_viewports(&mut self, first_viewport: u32, viewports: I) + where + I: Iterator, C: Supports, { self.capability.assert(); @@ -287,11 +286,9 @@ where /// `maxViewports` device limit. /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdSetScissor.html - pub unsafe fn set_scissors<'b>( - &mut self, - first_scissor: u32, - rects: impl IntoIterator, - ) where + pub unsafe fn set_scissors<'b, I>(&mut self, first_scissor: u32, rects: I) + where + I: Iterator, C: Supports, { self.capability.assert(); @@ -430,13 +427,11 @@ where /// Clear regions within bound framebuffer attachments /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdClearAttachments.html#vkCmdBeginRenderPass - pub unsafe fn clear_attachments( - &mut self, - clears: impl IntoIterator< - Item = impl std::borrow::Borrow, - >, - rects: impl IntoIterator>, - ) { + pub unsafe fn clear_attachments(&mut self, clears: I, rects: J) + where + I: Iterator, + J: Iterator, + { rendy_core::hal::command::CommandBuffer::clear_attachments(self.inner.raw, clears, rects); } @@ -631,10 +626,12 @@ where B: rendy_core::hal::Backend, { /// Execute commands from secondary buffers. - pub fn execute_commands( - &mut self, - submittables: impl IntoIterator>, - ) { + pub fn execute_commands(&mut self, submittables: I) + where + I: IntoIterator, + I::Item: Submittable, + I::IntoIter: ExactSizeIterator, + { let family = self.inner.family; unsafe { rendy_core::hal::command::CommandBuffer::execute_commands( @@ -711,12 +708,12 @@ where B: rendy_core::hal::Backend, { /// Beging recording render pass inline. - pub fn begin_render_pass_inline( + pub fn begin_render_pass_inline<'b>( &mut self, render_pass: &B::RenderPass, framebuffer: &B::Framebuffer, render_area: rendy_core::hal::pso::Rect, - clear_values: &[rendy_core::hal::command::ClearValue], + clear_values: impl Iterator>, ) -> RenderPassInlineEncoder<'_, B> where C: Supports, @@ -742,12 +739,12 @@ where } /// Beging recording render pass secondary. - pub fn begin_render_pass_secondary( + pub fn begin_render_pass_secondary<'b>( &mut self, render_pass: &B::RenderPass, framebuffer: &B::Framebuffer, render_area: rendy_core::hal::pso::Rect, - clear_values: &[rendy_core::hal::command::ClearValue], + clear_values: impl Iterator>, ) -> RenderPassSecondaryEncoder<'_, B> where C: Supports, @@ -771,10 +768,12 @@ where } /// Execute commands from secondary buffers. - pub fn execute_commands( - &mut self, - submittables: impl IntoIterator>, - ) { + pub fn execute_commands(&mut self, submittables: I) + where + I: IntoIterator, + I::Item: Submittable, + I::IntoIter: ExactSizeIterator, + { let family = self.inner.family; unsafe { rendy_core::hal::command::CommandBuffer::execute_commands( @@ -811,12 +810,9 @@ where /// length of the corresponding buffer. /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdCopyBuffer.html - pub unsafe fn copy_buffer( - &mut self, - src: &B::Buffer, - dst: &B::Buffer, - regions: impl IntoIterator, - ) where + pub unsafe fn copy_buffer(&mut self, src: &B::Buffer, dst: &B::Buffer, regions: I) + where + I: Iterator, C: Supports, { self.capability.assert(); @@ -831,13 +827,14 @@ where /// Same as `copy_buffer()` /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdCopyBufferToImage.html - pub unsafe fn copy_buffer_to_image( + pub unsafe fn copy_buffer_to_image( &mut self, src: &B::Buffer, dst: &B::Image, dst_layout: rendy_core::hal::image::Layout, - regions: impl IntoIterator, + regions: I, ) where + I: Iterator, C: Supports, { self.capability.assert(); @@ -858,14 +855,15 @@ where /// Same as `copy_buffer()` /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdCopyImage.html - pub unsafe fn copy_image( + pub unsafe fn copy_image( &mut self, src: &B::Image, src_layout: rendy_core::hal::image::Layout, dst: &B::Image, dst_layout: rendy_core::hal::image::Layout, - regions: impl IntoIterator, + regions: I, ) where + I: Iterator, C: Supports, { self.capability.assert(); @@ -887,13 +885,14 @@ where /// Same as `copy_buffer()` /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdCopyImageToBuffer.html - pub unsafe fn copy_image_to_buffer( + pub unsafe fn copy_image_to_buffer( &mut self, src: &B::Image, src_layout: rendy_core::hal::image::Layout, dst: &B::Buffer, - regions: impl IntoIterator, + regions: I, ) where + I: Iterator, C: Supports, { self.capability.assert(); @@ -914,15 +913,16 @@ where /// Same as `copy_buffer()` /// /// See: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBlitImage.html - pub unsafe fn blit_image( + pub unsafe fn blit_image( &mut self, src: &B::Image, src_layout: rendy_core::hal::image::Layout, dst: &B::Image, dst_layout: rendy_core::hal::image::Layout, filter: rendy_core::hal::image::Filter, - regions: impl IntoIterator, + regions: I, ) where + I: Iterator, C: Supports, { self.capability.assert(); diff --git a/command/src/family/queue.rs b/command/src/family/queue.rs index 94ece2d0..3e86d8cf 100644 --- a/command/src/family/queue.rs +++ b/command/src/family/queue.rs @@ -58,35 +58,31 @@ where impl IntoIterator + 'a)>, >, >, - fence: Option<&mut Fence>, + mut fence: Option<&mut Fence>, ) { assert!(fence.as_ref().map_or(true, |f| f.is_unsignaled())); let mut submissions = submissions.into_iter().peekable(); if submissions.peek().is_none() && fence.is_some() { self.raw.submit( - rendy_core::hal::queue::Submission { - command_buffers: std::iter::empty::<&'a B::CommandBuffer>(), - wait_semaphores: std::iter::empty::<(&'a B::Semaphore, _)>(), - signal_semaphores: std::iter::empty::<&'a B::Semaphore>(), - }, - fence.as_ref().map(|f| f.raw()), + std::iter::empty::<&'a B::CommandBuffer>(), + std::iter::empty::<(&'a B::Semaphore, _)>(), + std::iter::empty::<&'a B::Semaphore>(), + fence.as_mut().map(|f| f.raw_mut()), ); } else { let family = self.id.family; while let Some(submission) = submissions.next() { self.raw.submit( - rendy_core::hal::queue::Submission { - command_buffers: submission.submits.into_iter().map(|submit| { - assert_eq!(submit.family(), family); - submit.raw() - }), - wait_semaphores: submission.waits.into_iter().map(|w| (w.0.borrow(), w.1)), - signal_semaphores: submission.signals.into_iter().map(|s| s.borrow()), - }, + submission.submits.into_iter().map(|submit| { + assert_eq!(submit.family(), family); + submit.raw() + }), + submission.waits.into_iter().map(|w| (w.0.borrow(), w.1)), + submission.signals.into_iter().map(|s| s.borrow()), submissions .peek() - .map_or(fence.as_ref().map(|f| f.raw()), |_| None), + .map_or(fence.as_mut().map(|f| f.raw_mut()), |_| None), ); } } @@ -118,30 +114,27 @@ where impl IntoIterator + 'a)>, >, >, - fence: Option<&B::Fence>, + mut fence: Option<&mut B::Fence>, ) { let mut submissions = submissions.into_iter().peekable(); if submissions.peek().is_none() && fence.is_some() { self.raw.submit( - rendy_core::hal::queue::Submission { - command_buffers: std::iter::empty::<&'a B::CommandBuffer>(), - wait_semaphores: std::iter::empty::<(&'a B::Semaphore, _)>(), - signal_semaphores: std::iter::empty::<&'a B::Semaphore>(), - }, + std::iter::empty::<&'a B::CommandBuffer>(), + std::iter::empty::<(&'a B::Semaphore, _)>(), + std::iter::empty::<&'a B::Semaphore>(), fence, ); } else { let family = self.id.family; while let Some(submission) = submissions.next() { + let fence = fence.as_mut().map(|f| &mut **f); self.raw.submit( - rendy_core::hal::queue::Submission { - command_buffers: submission.submits.into_iter().map(|submit| { - assert_eq!(submit.family(), family); - submit.raw() - }), - wait_semaphores: submission.waits.into_iter().map(|w| (w.0.borrow(), w.1)), - signal_semaphores: submission.signals.into_iter().map(|s| s.borrow()), - }, + submission.submits.into_iter().map(|submit| { + assert_eq!(submit.family(), family); + submit.raw() + }), + submission.waits.into_iter().map(|w| (w.0.borrow(), w.1)), + submission.signals.into_iter().map(|s| s.borrow()), submissions.peek().map_or(fence, |_| None), ); } @@ -149,7 +142,7 @@ where } /// Wait for queue to finish all pending commands. - pub fn wait_idle(&self) -> Result<(), rendy_core::hal::device::OutOfMemory> { + pub fn wait_idle(&mut self) -> Result<(), rendy_core::hal::device::OutOfMemory> { self.raw.wait_idle() } } diff --git a/command/src/fence.rs b/command/src/fence.rs index 72b3b4e6..d29d2921 100644 --- a/command/src/fence.rs +++ b/command/src/fence.rs @@ -97,7 +97,7 @@ where self.assert_device_owner(device); match self.state { FenceState::Signaled => { - unsafe { device.reset_fence(&self.raw) }?; + unsafe { device.reset_fence(&mut self.raw) }?; self.state = FenceState::Unsignaled; Ok(()) } @@ -138,7 +138,7 @@ where &mut self, device: &Device, timeout_ns: u64, - ) -> Result, rendy_core::hal::device::OomOrDeviceLost> { + ) -> Result, rendy_core::hal::device::WaitError> { self.assert_device_owner(device); match self.state { @@ -182,6 +182,12 @@ where &self.raw } + /// Get mutable raw fence reference. + /// Use `mark_*` functions to reflect stage changes. + pub fn raw_mut(&mut self) -> &mut B::Fence { + &mut self.raw + } + /// Get submission epoch. /// Panics if not submitted. pub fn epoch(&self) -> FenceEpoch { diff --git a/command/src/pool.rs b/command/src/pool.rs index a742506c..cdd62ba6 100644 --- a/command/src/pool.rs +++ b/command/src/pool.rs @@ -105,8 +105,7 @@ where ) { let buffers = buffers .into_iter() - .map(|buffer| buffer.into_raw()) - .collect::>(); + .map(|buffer| buffer.into_raw()); self.raw.free(buffers); } diff --git a/core/Cargo.toml b/core/Cargo.toml index 61fd4544..98ccc95d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -26,9 +26,9 @@ vulkan = ["gfx-backend-vulkan"] no-slow-safety-checks = [] [dependencies] -gfx-hal = "0.5" -gfx-backend-empty = { version = "0.5", optional = true } -gfx-backend-gl = { version = "0.5", features = ["glutin"], default_features = false, optional = true } +gfx-hal = "0.7" +gfx-backend-empty = { version = "0.7", optional = true } +gfx-backend-gl = { version = "0.7", default_features = false, optional = true } lazy_static = "1.4.0" log = "0.4.11" parking_lot = "0.11.1" @@ -37,10 +37,10 @@ thread_profiler = "0.3.0" raw-window-handle = "0.3.3" [target.'cfg(all(target_os = "windows", not(target_arch = "wasm32")))'.dependencies] -gfx-backend-dx12 = { version = "0.5", optional = true } +gfx-backend-dx12 = { version = "0.7", optional = true } [target.'cfg(any(all(not(target_arch = "wasm32"), target_os = "macos"), all(target_arch = "aarch64", target_os = "ios")))'.dependencies] -gfx-backend-metal = { version = "0.5", optional = true } +gfx-backend-metal = { version = "0.7", optional = true } [target.'cfg(all(any(target_os = "windows", all(unix, not(any(target_os = "macos", target_os = "ios")))), not(target_arch = "wasm32")))'.dependencies] -gfx-backend-vulkan = { version = "0.5", features = ["x11"], optional = true } +gfx-backend-vulkan = { version = "0.7", optional = true } diff --git a/descriptor/Cargo.toml b/descriptor/Cargo.toml index 3771d0d4..f6e40bf7 100644 --- a/descriptor/Cargo.toml +++ b/descriptor/Cargo.toml @@ -11,7 +11,7 @@ categories = ["rendering"] description = "Rendy's descriptor allocator" [dependencies] -gfx-hal = "0.5" +gfx-hal = "0.7" log = "0.4.11" relevant = { version = "0.4.2", features = ["log"] } smallvec = "1.5.1" diff --git a/descriptor/src/allocator.rs b/descriptor/src/allocator.rs index eae135db..c627ead6 100644 --- a/descriptor/src/allocator.rs +++ b/descriptor/src/allocator.rs @@ -77,8 +77,7 @@ unsafe fn allocate_from_pool( let sets_were = allocation.len(); raw.allocate(std::iter::repeat(layout).take(count as usize), allocation) .map_err(|err| match err { - AllocationError::Host => OutOfMemory::Host, - AllocationError::Device => OutOfMemory::Device, + AllocationError::OutOfMemory(err) => err, err => { // We check pool for free descriptors and sets before calling this function, // so it can't be exhausted. @@ -190,7 +189,7 @@ where ); let raw = device.create_descriptor_pool( size as usize, - &pool_ranges, + pool_ranges.iter(), DescriptorPoolCreateFlags::empty(), )?; let allocate = size.min(count); diff --git a/descriptor/src/ranges.rs b/descriptor/src/ranges.rs index d936ab1d..90a9514d 100644 --- a/descriptor/src/ranges.rs +++ b/descriptor/src/ranges.rs @@ -276,3 +276,15 @@ impl<'a> Iterator for DescriptorRangesIter<'a> { } } } + +impl<'a> ExactSizeIterator for DescriptorRangesIter<'a> { + fn len(&self) -> usize { + let (lower, upper) = self.size_hint(); + // Note: This assertion is overly defensive, but it checks the invariant + // guaranteed by the trait. If this trait were rust-internal, + // we could use debug_assert!; assert_eq! will check all Rust user + // implementations too. + assert_eq!(upper, Some(lower)); + lower + } +} diff --git a/factory/src/blitter.rs b/factory/src/blitter.rs index d6b7b888..9e6f5c9c 100644 --- a/factory/src/blitter.rs +++ b/factory/src/blitter.rs @@ -24,11 +24,7 @@ pub struct Blitter { fn subresource_to_range( sub: &rendy_core::hal::image::SubresourceLayers, ) -> rendy_core::hal::image::SubresourceRange { - rendy_core::hal::image::SubresourceRange { - aspects: sub.aspects, - levels: sub.level..sub.level + 1, - layers: sub.layers.clone(), - } + sub.clone().into() } /// A region to be blitted including the source and destination images and states, @@ -355,7 +351,7 @@ pub unsafe fn blit_image( reg.into() }) - .collect::>(); + .collect::>(); // TODO: synchronize whatever possible on flush. // Currently all barriers are inlined due to dependencies between blits. @@ -369,7 +365,7 @@ pub unsafe fn blit_image( dst_image.raw(), rendy_core::hal::image::Layout::TransferDstOptimal, filter, - regions, + regions.iter().cloned(), ); read_barriers.encode_after(encoder); @@ -397,7 +393,7 @@ where B: rendy_core::hal::Backend, { unsafe fn flush(&mut self, family: &mut Family) { - for (queue, next) in self + for (queue, mut next) in self .next .drain(..) .enumerate() @@ -408,7 +404,7 @@ where family.queue_mut(queue).submit_raw_fence( Some(Submission::new().submits(once(submit))), - Some(&next.fence), + Some(&mut next.fence), ); self.pending.push_back(GraphicsOps { @@ -460,7 +456,7 @@ where /// `device` must be the same that was used with other methods of this instance. /// unsafe fn cleanup(&mut self, device: &Device) { - while let Some(pending) = self.pending.pop_front() { + while let Some(mut pending) = self.pending.pop_front() { match device.get_fence_status(&pending.fence) { Ok(false) => { self.pending.push_front(pending); @@ -471,7 +467,7 @@ where } Ok(true) => { device - .reset_fence(&pending.fence) + .reset_fence(&mut pending.fence) .expect("Can always reset signalled fence"); self.initial.push(GraphicsOps { command_buffer: pending.command_buffer.mark_complete().reset(), diff --git a/factory/src/config.rs b/factory/src/config.rs index 9eefe093..bb9f6567 100644 --- a/factory/src/config.rs +++ b/factory/src/config.rs @@ -126,7 +126,7 @@ pub unsafe trait HeapsConfigure { type Types: IntoIterator; /// Iterator over heaps. - type Heaps: IntoIterator; + type Heaps: IntoIterator; /// Configure. fn configure( @@ -149,7 +149,7 @@ pub struct BasicHeapsConfigure; unsafe impl HeapsConfigure for BasicHeapsConfigure { type Types = Vec<(rendy_core::hal::memory::Properties, u32, HeapsConfig)>; - type Heaps = Vec; + type Heaps = Vec; fn configure( &self, @@ -169,20 +169,20 @@ unsafe impl HeapsConfigure for BasicHeapsConfigure { .contains(rendy_core::hal::memory::Properties::CPU_VISIBLE) { Some(LinearConfig { - linear_size: min(_128mb, properties.memory_heaps[mt.heap_index] / 16), + linear_size: min(_128mb, properties.memory_heaps[mt.heap_index].size / 16), }) } else { None }, dynamic: Some(DynamicConfig { block_size_granularity: 256.min( - (properties.memory_heaps[mt.heap_index] / 4096).next_power_of_two(), + (properties.memory_heaps[mt.heap_index].size / 4096).next_power_of_two(), ), min_device_allocation: _1mb - .min(properties.memory_heaps[mt.heap_index] / 1048) + .min(properties.memory_heaps[mt.heap_index].size / 1048) .next_power_of_two(), max_chunk_size: _32mb.min( - (properties.memory_heaps[mt.heap_index] / 128).next_power_of_two(), + (properties.memory_heaps[mt.heap_index].size / 128).next_power_of_two(), ), }), }; @@ -204,12 +204,12 @@ unsafe impl HeapsConfigure for BasicHeapsConfigure { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SavedHeapsConfig { types: Vec<(rendy_core::hal::memory::Properties, u32, HeapsConfig)>, - heaps: Vec, + heaps: Vec, } unsafe impl HeapsConfigure for SavedHeapsConfig { type Types = Vec<(rendy_core::hal::memory::Properties, u32, HeapsConfig)>; - type Heaps = Vec; + type Heaps = Vec; fn configure( &self, diff --git a/factory/src/factory.rs b/factory/src/factory.rs index 4759c78b..cdf2a847 100644 --- a/factory/src/factory.rs +++ b/factory/src/factory.rs @@ -10,19 +10,19 @@ use { memory::{self, Heaps, MemoryUsage, TotalMemoryUtilization, Write}, resource::*, upload::{BufferState, ImageState, ImageStateOrLayout, Uploader}, - wsi::{Surface, SwapchainError, Target}, + wsi::Surface, }, rendy_core::{ hal::{ adapter::{Adapter, Gpu, PhysicalDevice}, buffer, device::{ - AllocationError, CreationError, Device as _, MapError, OomOrDeviceLost, + AllocationError, CreationError, Device as _, MapError, OutOfMemory, WaitFor, }, format, image, pso::DescriptorSetLayoutBinding, - window::{Extent2D, InitError, Surface as GfxSurface}, + window::{InitError, Surface as GfxSurface}, Backend, Features, Instance as _, Limits, }, HasRawWindowHandle, @@ -784,45 +784,6 @@ where drop(surface); } - /// Create target out of rendering surface. - /// - /// The compatibility of the surface with the queue family which will present to - /// this target must have *already* been checked using `Factory::surface_support`. - /// - /// # Panics - /// - /// Panics if `surface` was not created by this `Factory`. - pub fn create_target( - &self, - surface: Surface, - extent: Extent2D, - image_count: u32, - present_mode: rendy_core::hal::window::PresentMode, - usage: image::Usage, - ) -> Result, SwapchainError> { - profile_scope!("create_target"); - - unsafe { - surface.into_target( - &self.adapter.physical_device, - &self.device, - extent, - image_count, - present_mode, - usage, - ) - } - } - - /// Destroy target returning underlying surface back to the caller. - /// - /// # Safety - /// - /// Target images must not be used by pending commands or referenced anywhere. - pub unsafe fn destroy_target(&self, target: Target) -> Surface { - target.dispose(&self.device) - } - /// Get raw device. pub fn device(&self) -> &Device { &self.device @@ -859,37 +820,12 @@ where fence.reset(&self.device) } - /// Wait for the fence become signeled. - /// - /// # Safety - /// - /// Fences must be created by this `Factory`. - pub fn reset_fences<'a>( - &self, - fences: impl IntoIterator> + 'a)>, - ) -> Result<(), OutOfMemory> { - let fences = fences - .into_iter() - .map(|f| { - let f = f.borrow_mut(); - f.assert_device_owner(&self.device); - assert!(f.is_signaled()); - f - }) - .collect::>(); - unsafe { - self.device.reset_fences(fences.iter().map(|f| f.raw()))?; - fences.into_iter().for_each(|f| f.mark_reset()); - } - Ok(()) - } - /// Wait for the fence become signeled. pub fn wait_for_fence( &self, fence: &mut Fence, timeout_ns: u64, - ) -> Result { + ) -> Result { profile_scope!("wait_for_fence"); fence.assert_device_owner(&self.device); @@ -913,7 +849,7 @@ where fences: impl IntoIterator> + 'a)>, wait_for: WaitFor, timeout_ns: u64, - ) -> Result { + ) -> Result { profile_scope!("wait_for_fences"); let fences = fences @@ -1283,7 +1219,7 @@ where let heaps = unsafe { Heaps::new( types, - heaps, + heaps.iter().map(|h| h.size), adapter.physical_device.limits().non_coherent_atom_size as u64, ) }; diff --git a/factory/src/upload.rs b/factory/src/upload.rs index 75e06971..641e8658 100644 --- a/factory/src/upload.rs +++ b/factory/src/upload.rs @@ -202,7 +202,7 @@ where src: 0, dst: offset, size: staging.size(), - }), + }).iter().cloned(), ); next_upload.staging_buffers.push(staging); @@ -293,11 +293,7 @@ where let whole_level = image_offset == rendy_core::hal::image::Offset::ZERO && image_extent == whole_extent; - let image_range = rendy_core::hal::image::SubresourceRange { - aspects: image_layers.aspects, - levels: image_layers.level..image_layers.level + 1, - layers: image_layers.layers.clone(), - }; + let image_range: rendy_core::hal::image::SubresourceRange = image_layers.clone().into(); let (last_stage, mut last_access, last_layout) = match last { ImageStateOrLayout::State(last) => { @@ -361,7 +357,7 @@ where image_layers, image_offset, image_extent, - }), + }).iter().cloned(), ); next_upload.staging_buffers.push(staging); @@ -462,7 +458,7 @@ where family.queue_mut(queue).submit_raw_fence( Some(Submission::new().submits(once(barriers_submit).chain(once(submit)))), - Some(&next.fence), + Some(&mut next.fence), ); self.pending.push_back(PendingUploads { @@ -515,7 +511,7 @@ where /// `device` must be the same that was used with other methods of this instance. /// unsafe fn cleanup(&mut self, device: &Device) { - while let Some(pending) = self.pending.pop_front() { + while let Some(mut pending) = self.pending.pop_front() { match device.get_fence_status(&pending.fence) { Ok(false) => { self.pending.push_front(pending); @@ -526,7 +522,7 @@ where } Ok(true) => { device - .reset_fence(&pending.fence) + .reset_fence(&mut pending.fence) .expect("Can always reset signalled fence"); self.fences.push(pending.fence); self.command_buffers.push([ diff --git a/graph/src/graph/mod.rs b/graph/src/graph/mod.rs index bca493fc..6b5a630b 100644 --- a/graph/src/graph/mod.rs +++ b/graph/src/graph/mod.rs @@ -601,8 +601,10 @@ fn build_node<'a, B: Backend, T: ?Sized>( id, range: rendy_core::hal::image::SubresourceRange { aspects: image.format().surface_desc().aspects, - levels: 0..image.levels(), - layers: 0..image.layers(), + level_start: 0, + level_count: Some(image.levels()), + layer_start: 0, + layer_count: Some(image.layers()), }, layout: chains.images[&chain_id].links()[link] .submission_state(submission.id()) diff --git a/graph/src/node/present.rs b/graph/src/node/present.rs index e508cc56..760b568d 100644 --- a/graph/src/node/present.rs +++ b/graph/src/node/present.rs @@ -1,24 +1,51 @@ //! Defines present node. +use std::borrow::Borrow; + use crate::{ command::{ CommandBuffer, CommandPool, ExecutableState, Families, Family, FamilyId, Fence, MultiShot, - PendingState, Queue, SimultaneousUse, Submission, Submit, + PendingState, Queue, SimultaneousUse, Submission, Submit, IndividualReset, NoSimultaneousUse, }, factory::Factory, - frame::Frames, + frame::{ + cirque::{CirqueRef, CommandCirque}, + Frames, + }, graph::GraphContext, node::{ gfx_acquire_barriers, gfx_release_barriers, BufferAccess, DynNode, ImageAccess, NodeBuffer, NodeBuildError, NodeBuilder, NodeImage, }, - wsi::{Surface, Target}, + wsi::Surface, BufferId, ImageId, NodeId, }; +use rendy_core::hal; +use hal::format::{Format, ChannelType}; +use hal::image::FramebufferAttachment; +use hal::window::{PresentMode, Extent2D, SwapchainConfig}; + +fn make_swapchain_config(factory: &Factory, surface: &Surface, extent: Extent2D) -> (SwapchainConfig, FramebufferAttachment) { + let caps = factory.get_surface_capabilities(surface); + let formats = factory.get_surface_formats(surface); + + let format = formats.map_or(Format::Rgba8Srgb, |formats| { + formats + .iter() + .find(|format| format.base_format().1 == ChannelType::Srgb) + .map(|format| *format) + .unwrap_or(formats[0]) + }); + + let swapchain_config = rendy_core::hal::window::SwapchainConfig::from_caps(&caps, format, extent); + let framebuffer_attachment = swapchain_config.framebuffer_attachment(); + + (swapchain_config, framebuffer_attachment) +} + #[derive(Debug)] struct ForImage { - acquire: B::Semaphore, release: B::Semaphore, submit: Submit, buffer: CommandBuffer< @@ -35,233 +62,18 @@ impl ForImage { pool: &mut CommandPool, ) { drop(self.submit); - factory.destroy_semaphore(self.acquire); factory.destroy_semaphore(self.release); pool.free_buffers(Some(self.buffer.mark_complete())); } } -/// Node that presents images to the surface. -#[derive(Debug)] -pub struct PresentNode { - per_image: Vec>, - free_acquire: B::Semaphore, - target: Target, - pool: CommandPool, - input_image: NodeImage, - blit_filter: rendy_core::hal::image::Filter, -} - -// Raw pointer destroys Send/Sync autoimpl, but it's always from the same graph. -unsafe impl Sync for PresentNode {} -unsafe impl Send for PresentNode {} - -impl PresentNode -where - B: rendy_core::hal::Backend, -{ - /// Node builder. - /// By default attempts to use 3 images in the swapchain with present mode priority: - /// - /// Mailbox > Fifo > Relaxed > Immediate. - /// - /// You can query the real image count and present mode which will be used with - /// `PresentBuilder::image_count()` and `PresentBuilder::present_mode()`. - pub fn builder(factory: &Factory, surface: Surface, image: ImageId) -> PresentBuilder { - use rendy_core::hal::window::PresentMode; - - let caps = factory.get_surface_capabilities(&surface); - let image_count = 3 - .min(*caps.image_count.end()) - .max(*caps.image_count.start()); - - let present_mode = match () { - _ if caps.present_modes.contains(PresentMode::FIFO) => PresentMode::FIFO, - _ if caps.present_modes.contains(PresentMode::MAILBOX) => PresentMode::MAILBOX, - _ if caps.present_modes.contains(PresentMode::RELAXED) => PresentMode::RELAXED, - _ if caps.present_modes.contains(PresentMode::IMMEDIATE) => PresentMode::IMMEDIATE, - _ => panic!("No known present modes found"), - }; - - PresentBuilder { - surface, - image, - dependencies: Vec::new(), - image_count, - present_mode, - caps, - blit_filter: rendy_core::hal::image::Filter::Nearest, - } - } -} - -fn create_per_image_data( - ctx: &GraphContext, - input_image: &NodeImage, - pool: &mut CommandPool, - factory: &Factory, - target: &Target, - blit_filter: rendy_core::hal::image::Filter, -) -> Vec> { - let input_image_res = ctx.get_image(input_image.id).expect("Image does not exist"); - - let target_images = target.backbuffer(); - let buffers = pool.allocate_buffers(target_images.len()); - target_images - .iter() - .zip(buffers) - .map(|(target_image, buf_initial)| { - let mut buf_recording = buf_initial.begin(MultiShot(SimultaneousUse), ()); - let mut encoder = buf_recording.encoder(); - let (mut stages, mut barriers) = - gfx_acquire_barriers(ctx, None, Some(input_image)); - stages.start |= rendy_core::hal::pso::PipelineStage::TRANSFER; - stages.end |= rendy_core::hal::pso::PipelineStage::TRANSFER; - barriers.push(rendy_core::hal::memory::Barrier::Image { - states: ( - rendy_core::hal::image::Access::empty(), - rendy_core::hal::image::Layout::Undefined, - ) - ..( - rendy_core::hal::image::Access::TRANSFER_WRITE, - rendy_core::hal::image::Layout::TransferDstOptimal, - ), - families: None, - target: target_image.raw(), - range: rendy_core::hal::image::SubresourceRange { - aspects: rendy_core::hal::format::Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, - }); - log::trace!("Acquire {:?} : {:#?}", stages, barriers); - unsafe { - encoder.pipeline_barrier( - stages, - rendy_core::hal::memory::Dependencies::empty(), - barriers, - ); - } - - let extents_differ = target_image.kind().extent() != input_image_res.kind().extent(); - let formats_differ = target_image.format() != input_image_res.format(); - - if extents_differ || formats_differ - { - if formats_differ { - log::debug!("Present node is blitting because target format {:?} doesnt match image format {:?}", target_image.format(), input_image_res.format()); - } - if extents_differ { - log::debug!("Present node is blitting because target extent {:?} doesnt match image extent {:?}", target_image.kind().extent(), input_image_res.kind().extent()); - } - unsafe { - encoder.blit_image( - input_image_res.raw(), - input_image.layout, - target_image.raw(), - rendy_core::hal::image::Layout::TransferDstOptimal, - blit_filter, - Some(rendy_core::hal::command::ImageBlit { - src_subresource: rendy_core::hal::image::SubresourceLayers { - aspects: input_image.range.aspects, - level: 0, - layers: input_image.range.layers.start..input_image.range.layers.start + 1, - }, - src_bounds: rendy_core::hal::image::Offset::ZERO - .into_bounds(&input_image_res.kind().extent()), - dst_subresource: rendy_core::hal::image::SubresourceLayers { - aspects: rendy_core::hal::format::Aspects::COLOR, - level: 0, - layers: 0..1, - }, - dst_bounds: rendy_core::hal::image::Offset::ZERO - .into_bounds(&target_image.kind().extent()), - }), - ); - } - } else { - log::debug!("Present node is copying"); - unsafe { - encoder.copy_image( - input_image_res.raw(), - input_image.layout, - target_image.raw(), - rendy_core::hal::image::Layout::TransferDstOptimal, - Some(rendy_core::hal::command::ImageCopy { - src_subresource: rendy_core::hal::image::SubresourceLayers { - aspects: input_image.range.aspects, - level: 0, - layers: input_image.range.layers.start..input_image.range.layers.start + 1, - }, - src_offset: rendy_core::hal::image::Offset::ZERO, - dst_subresource: rendy_core::hal::image::SubresourceLayers { - aspects: rendy_core::hal::format::Aspects::COLOR, - level: 0, - layers: 0..1, - }, - dst_offset: rendy_core::hal::image::Offset::ZERO, - extent: rendy_core::hal::image::Extent { - width: target_image.kind().extent().width, - height: target_image.kind().extent().height, - depth: 1, - }, - }), - ); - } - } - - { - let (mut stages, mut barriers) = - gfx_release_barriers(ctx, None, Some(input_image)); - stages.start |= rendy_core::hal::pso::PipelineStage::TRANSFER; - stages.end |= rendy_core::hal::pso::PipelineStage::BOTTOM_OF_PIPE; - barriers.push(rendy_core::hal::memory::Barrier::Image { - states: ( - rendy_core::hal::image::Access::TRANSFER_WRITE, - rendy_core::hal::image::Layout::TransferDstOptimal, - ) - ..( - rendy_core::hal::image::Access::empty(), - rendy_core::hal::image::Layout::Present, - ), - families: None, - target: target_image.raw(), - range: rendy_core::hal::image::SubresourceRange { - aspects: rendy_core::hal::format::Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, - }); - - log::trace!("Release {:?} : {:#?}", stages, barriers); - unsafe { - encoder.pipeline_barrier( - stages, - rendy_core::hal::memory::Dependencies::empty(), - barriers, - ); - } - } - - let (submit, buffer) = buf_recording.finish().submit(); - - ForImage { - submit, - buffer, - acquire: factory.create_semaphore().unwrap(), - release: factory.create_semaphore().unwrap(), - } - }) - .collect() -} - /// Presentation node description. #[derive(Debug)] pub struct PresentBuilder { surface: Surface, image: ImageId, image_count: u32, - present_mode: rendy_core::hal::window::PresentMode, + present_mode: PresentMode, caps: rendy_core::hal::window::SurfaceCapabilities, dependencies: Vec, blit_filter: rendy_core::hal::image::Filter, @@ -321,10 +133,8 @@ where /// - Panics if none of the provided `PresentMode`s are supported. pub fn with_present_modes_priority(mut self, present_modes_priority: PF) -> Self where - PF: Fn(rendy_core::hal::window::PresentMode) -> Option, + PF: Fn(PresentMode) -> Option, { - use rendy_core::hal::window::PresentMode; - let priority_mode = [ PresentMode::FIFO, PresentMode::MAILBOX, @@ -355,7 +165,7 @@ where } /// Get present mode used by node. - pub fn present_mode(&self) -> rendy_core::hal::window::PresentMode { + pub fn present_mode(&self) -> PresentMode { self.present_mode } } @@ -403,13 +213,21 @@ where assert_eq!(buffers.len(), 0); assert_eq!(images.len(), 1); + let caps = factory.get_surface_capabilities(&self.surface); + let formats = factory.get_surface_formats(&self.surface); + let input_image = images.into_iter().next().unwrap(); - let extent = ctx - .get_image(input_image.id) - .expect("Context must contain node's image") - .kind() - .extent() - .into(); + + let extent; + let format; + { + let img = ctx + .get_image(input_image.id) + .expect("Context must contain node's image"); + + extent = img.kind().extent().into(); + format = img.format(); + } if !factory.surface_support(family.id(), &self.surface) { log::warn!( @@ -420,40 +238,95 @@ where return Err(NodeBuildError::QueueFamily(family.id())); } - let target = factory - .create_target( - self.surface, - extent, - self.image_count, - self.present_mode, - rendy_core::hal::image::Usage::TRANSFER_DST, - ) - .map_err(NodeBuildError::Swapchain)?; + let swapchain_config = rendy_core::hal::window::SwapchainConfig::from_caps(&caps, format, extent); + let fat = swapchain_config.framebuffer_attachment(); + + unsafe { + self.surface.configure_swapchain( + factory.device(), + swapchain_config, + ).map_err(NodeBuildError::Swapchain)?; + } let mut pool = factory .create_command_pool(family) .map_err(NodeBuildError::OutOfMemory)?; - let per_image = create_per_image_data( - ctx, - &input_image, - &mut pool, - factory, - &target, - self.blit_filter, - ); + let command_cirque = CommandCirque::new(); Ok(Box::new(PresentNode { - free_acquire: factory.create_semaphore().unwrap(), + release_idx: 0, + release: (0..self.image_count).map(|_| factory.create_semaphore().unwrap()).collect(), pool, - target, - per_image, + command_cirque, + surface: self.surface, + //per_image, input_image, blit_filter: self.blit_filter, + swapchain_config, })) } } +/// Node that presents images to the surface. +#[derive(Debug)] +pub struct PresentNode { + //per_image: Vec>, + release_idx: usize, + release: Vec, + surface: Surface, + + pool: CommandPool, + command_cirque: CommandCirque, + + input_image: NodeImage, + blit_filter: rendy_core::hal::image::Filter, + + swapchain_config: SwapchainConfig, +} + +// Raw pointer destroys Send/Sync autoimpl, but it's always from the same graph. +unsafe impl Sync for PresentNode {} +unsafe impl Send for PresentNode {} + +impl PresentNode +where + B: rendy_core::hal::Backend, +{ + /// Node builder. + /// By default attempts to use 3 images in the swapchain with present mode priority: + /// + /// Mailbox > Fifo > Relaxed > Immediate. + /// + /// You can query the real image count and present mode which will be used with + /// `PresentBuilder::image_count()` and `PresentBuilder::present_mode()`. + pub fn builder(factory: &Factory, surface: Surface, image: ImageId) -> PresentBuilder { + let caps = factory.get_surface_capabilities(&surface); + + let image_count = 3 + .min(*caps.image_count.end()) + .max(*caps.image_count.start()); + + let present_mode = match () { + _ if caps.present_modes.contains(PresentMode::FIFO) => PresentMode::FIFO, + _ if caps.present_modes.contains(PresentMode::MAILBOX) => PresentMode::MAILBOX, + _ if caps.present_modes.contains(PresentMode::RELAXED) => PresentMode::RELAXED, + _ if caps.present_modes.contains(PresentMode::IMMEDIATE) => PresentMode::IMMEDIATE, + _ => panic!("No known present modes found"), + }; + + PresentBuilder { + surface, + image, + dependencies: Vec::new(), + image_count, + present_mode, + caps, + blit_filter: rendy_core::hal::image::Filter::Nearest, + } + } +} + impl DynNode for PresentNode where B: rendy_core::hal::Backend, @@ -465,57 +338,44 @@ where factory: &Factory, queue: &mut Queue, _aux: &T, - _frames: &Frames, + frames: &Frames, waits: &[(&'a B::Semaphore, rendy_core::hal::pso::PipelineStage)], signals: &[&'a B::Semaphore], mut fence: Option<&mut Fence>, ) { - loop { - match self.target.next_image(&self.free_acquire) { - Ok(next) => { - log::trace!("Present: {:#?}", next); - let for_image = &mut self.per_image[next[0] as usize]; - core::mem::swap(&mut for_image.acquire, &mut self.free_acquire); - - queue.submit( - Some( - Submission::new() - .submits(Some(&for_image.submit)) - .wait(waits.iter().cloned().chain(Some(( - &for_image.acquire, - rendy_core::hal::pso::PipelineStage::TRANSFER, - )))) - .signal(signals.iter().cloned().chain(Some(&for_image.release))), - ), - fence.take(), - ); + let input_image_res = ctx.get_image(self.input_image.id).expect("Image does not exist"); - match next.present(queue.raw(), Some(&for_image.release)) { - Ok(_) => break, - Err(e) => { - log::debug!( - "Swapchain present error after next_image is acquired: {:?}", - e - ); - // recreate swapchain on next frame. - break; - } - } - } - Err(rendy_core::hal::window::AcquireError::OutOfDate) => { - // recreate swapchain and try again. - } + let swapchain_image = loop { + match self.surface.acquire_image(!0) { + Ok((swapchain_image, _suboptimal)) => { + break swapchain_image; + }, + Err(hal::window::AcquireError::OutOfDate(_)) => { + // recreate swapchain and try again + }, e => { e.unwrap(); - break; - } + unreachable!(); + }, } + // Recreate swapchain when OutOfDate // The code has to execute after match due to mutable aliasing issues. // TODO: use retired swapchains once available in hal and remove that wait factory.wait_idle().unwrap(); + let extent; + let format; + { + let img = ctx + .get_image(self.input_image.id) + .expect("Context must contain node's image"); + + extent = img.kind().extent().into(); + format = img.format(); + } + let extent = ctx .get_image(self.input_image.id) .expect("Context must contain node's image") @@ -523,32 +383,180 @@ where .extent() .into(); - self.target - .recreate(factory.physical(), factory.device(), extent) - .expect("Failed recreating swapchain"); + let (swapchain_config, _fat) = make_swapchain_config(factory, &self.surface, extent); - for data in self.per_image.drain(..) { - data.dispose(factory, &mut self.pool); + unsafe { + self.surface.configure_swapchain( + factory.device(), + swapchain_config, + ).unwrap(); } + }; - self.per_image = create_per_image_data( - ctx, - &self.input_image, - &mut self.pool, - factory, - &self.target, - self.blit_filter, - ); + self.release_idx += 1; + if self.release_idx >= self.release.len() { + self.release_idx = 0; } + + let submit = self.command_cirque.encode(frames, &mut self.pool, |mut cbuf| { + let index = cbuf.index(); + + cbuf.or_init(|cbuf| { + let mut cbuf = cbuf.begin(MultiShot(NoSimultaneousUse), ()); + let mut encoder = cbuf.encoder(); + + let (mut stages, mut barriers) = + gfx_acquire_barriers(ctx, None, Some(&self.input_image)); + stages.start |= rendy_core::hal::pso::PipelineStage::TRANSFER; + stages.end |= rendy_core::hal::pso::PipelineStage::TRANSFER; + barriers.push(rendy_core::hal::memory::Barrier::Image { + states: ( + rendy_core::hal::image::Access::empty(), + rendy_core::hal::image::Layout::Undefined, + ) + ..( + rendy_core::hal::image::Access::TRANSFER_WRITE, + rendy_core::hal::image::Layout::TransferDstOptimal, + ), + families: None, + target: swapchain_image.borrow(), + range: rendy_core::hal::image::SubresourceRange { + aspects: rendy_core::hal::format::Aspects::COLOR, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), + }, + }); + log::trace!("Acquire {:?} : {:#?}", stages, barriers); + unsafe { + encoder.pipeline_barrier( + stages, + rendy_core::hal::memory::Dependencies::empty(), + barriers.drain(..), + ); + } + + let extents_differ = self.swapchain_config.extent.to_extent() != input_image_res.kind().extent(); + let formats_differ = self.swapchain_config.format != input_image_res.format(); + + if extents_differ || formats_differ + { + if formats_differ { + log::debug!("Present node is blitting because target format {:?} doesnt match image format {:?}", self.swapchain_config.format, input_image_res.format()); + } + if extents_differ { + log::debug!("Present node is blitting because target extent {:?} doesnt match image extent {:?}", self.swapchain_config.extent.to_extent(), input_image_res.kind().extent()); + } + unsafe { + encoder.blit_image( + input_image_res.raw(), + self.input_image.layout, + swapchain_image.borrow().raw(), + rendy_core::hal::image::Layout::TransferDstOptimal, + self.blit_filter, + std::iter::once(rendy_core::hal::command::ImageBlit { + src_subresource: rendy_core::hal::image::SubresourceLayers { + aspects: self.input_image.range.aspects, + level: 0, + layers: self.input_image.range.layer_start..self.input_image.range.layer_start + 1, + }, + src_bounds: rendy_core::hal::image::Offset::ZERO + .into_bounds(&input_image_res.kind().extent()), + dst_subresource: rendy_core::hal::image::SubresourceLayers { + aspects: rendy_core::hal::format::Aspects::COLOR, + level: 0, + layers: 0..1, + }, + dst_bounds: rendy_core::hal::image::Offset::ZERO + .into_bounds(&self.swapchain_config.extent.to_extent()), + }), + ); + } + } else { + log::debug!("Present node is copying"); + unsafe { + encoder.copy_image( + input_image_res.raw(), + self.input_image.layout, + swapchain_image.borrow().raw(), + rendy_core::hal::image::Layout::TransferDstOptimal, + std::iter::once(rendy_core::hal::command::ImageCopy { + src_subresource: rendy_core::hal::image::SubresourceLayers { + aspects: self.input_image.range.aspects, + level: 0, + layers: self.input_image.range.layer_start..self.input_image.range.layer_start + 1, + }, + src_offset: rendy_core::hal::image::Offset::ZERO, + dst_subresource: rendy_core::hal::image::SubresourceLayers { + aspects: rendy_core::hal::format::Aspects::COLOR, + level: 0, + layers: 0..1, + }, + dst_offset: rendy_core::hal::image::Offset::ZERO, + extent: self.swapchain_config.extent.to_extent(), + }), + ); + } + } + + { + let (mut stages, mut barriers) = + gfx_release_barriers(ctx, None, Some(&self.input_image)); + stages.start |= rendy_core::hal::pso::PipelineStage::TRANSFER; + stages.end |= rendy_core::hal::pso::PipelineStage::BOTTOM_OF_PIPE; + barriers.push(rendy_core::hal::memory::Barrier::Image { + states: ( + rendy_core::hal::image::Access::TRANSFER_WRITE, + rendy_core::hal::image::Layout::TransferDstOptimal, + ) + ..( + rendy_core::hal::image::Access::empty(), + rendy_core::hal::image::Layout::Present, + ), + families: None, + target: swapchain_image.borrow(), + range: rendy_core::hal::image::SubresourceRange { + aspects: rendy_core::hal::format::Aspects::COLOR, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), + }, + }); + + log::trace!("Release {:?} : {:#?}", stages, barriers); + unsafe { + encoder.pipeline_barrier( + stages, + rendy_core::hal::memory::Dependencies::empty(), + barriers.drain(..), + ); + } + } + + cbuf.finish() + }) + }); + + queue.submit( + Some( + Submission::new() + .submits(std::iter::once(submit)) + .signal(signals.iter().cloned().chain(Some(&self.release[self.release_idx]))), + ), + fence.take(), + ); + + self.surface.present(queue.raw(), swapchain_image, Some(&mut self.release[self.release_idx])); } unsafe fn dispose(mut self: Box, factory: &mut Factory, _aux: &T) { - for data in self.per_image { - data.dispose(factory, &mut self.pool); + for semaphore in self.release { + factory.destroy_semaphore(semaphore); } - factory.destroy_semaphore(self.free_acquire); factory.destroy_command_pool(self.pool); - factory.destroy_target(self.target); + factory.destroy_surface(self.surface); } } diff --git a/memory/Cargo.toml b/memory/Cargo.toml index 22d32b54..5a25a8a1 100644 --- a/memory/Cargo.toml +++ b/memory/Cargo.toml @@ -14,7 +14,7 @@ description = "Rendy's memory manager" serde-1 = ["serde", "gfx-hal/serde"] [dependencies] -gfx-hal = "0.5" +gfx-hal = "0.7" log = "0.4.11" hibitset = { version = "0.6.3", default-features = false } relevant = { version = "0.4.2", features = ["log"] } diff --git a/memory/src/allocator/dedicated.rs b/memory/src/allocator/dedicated.rs index 6da4bc99..78065092 100644 --- a/memory/src/allocator/dedicated.rs +++ b/memory/src/allocator/dedicated.rs @@ -97,7 +97,7 @@ where } else { self.unmap(device); let ptr = device.map_memory( - self.memory.raw(), + self.memory.raw_mut(), gfx_hal::memory::Segment { offset: mapping_range.start, size: Some(mapping_range.end - mapping_range.start), @@ -116,7 +116,7 @@ where if self.mapping.take().is_some() { unsafe { // trace!("Unmap memory: {:#?}", self.memory); - device.unmap_memory(self.memory.raw()); + device.unmap_memory(self.memory.raw_mut()); } } } diff --git a/memory/src/allocator/dynamic.rs b/memory/src/allocator/dynamic.rs index a4517308..11966479 100644 --- a/memory/src/allocator/dynamic.rs +++ b/memory/src/allocator/dynamic.rs @@ -274,7 +274,7 @@ where // Allocate from device. let (memory, mapping) = unsafe { // Valid memory type specified. - let raw = device.allocate_memory(self.memory_type, chunk_size)?; + let mut raw = device.allocate_memory(self.memory_type, chunk_size)?; let mapping = if self .memory_properties @@ -282,7 +282,7 @@ where { log::trace!("Map new memory object"); match device.map_memory( - &raw, + &mut raw, gfx_hal::memory::Segment { offset: 0, size: Some(chunk_size), @@ -486,15 +486,21 @@ where match chunk.flavor { ChunkFlavor::Dedicated(boxed, _) => { let size = boxed.size(); + + let needs_unmap = self + .memory_properties + .contains(gfx_hal::memory::Properties::CPU_VISIBLE); + + if needs_unmap { + log::trace!("Unmap memory: {:#?}", boxed); + } + + let mut raw_boxed = boxed.into_raw(); unsafe { - if self - .memory_properties - .contains(gfx_hal::memory::Properties::CPU_VISIBLE) - { - log::trace!("Unmap memory: {:#?}", boxed); - device.unmap_memory(boxed.raw()); + if needs_unmap { + device.unmap_memory(&mut raw_boxed); } - device.free_memory(boxed.into_raw()); + device.free_memory(raw_boxed); } size } diff --git a/memory/src/allocator/linear.rs b/memory/src/allocator/linear.rs index 77d10913..71547b4b 100644 --- a/memory/src/allocator/linear.rs +++ b/memory/src/allocator/linear.rs @@ -1,4 +1,8 @@ -use std::{collections::VecDeque, ops::Range, ptr::NonNull}; +use std::{ + collections::VecDeque, + ops::Range, + ptr::NonNull, +}; use { crate::{ @@ -222,11 +226,14 @@ where unsafe { match Arc::try_unwrap(line.memory) { Ok(memory) => { + let size = memory.size(); + let mut raw_mem = memory.into_raw(); + // trace!("Unmap memory: {:#?}", line.memory); - device.unmap_memory(memory.raw()); + device.unmap_memory(&mut raw_mem); - freed += memory.size(); - device.free_memory(memory.into_raw()); + freed += size; + device.free_memory(raw_mem); } Err(_) => log::error!("Allocated `Line` was freed, but memory is still shared and never will be destroyed"), } @@ -295,10 +302,10 @@ where } let (memory, ptr) = unsafe { - let raw = device.allocate_memory(self.memory_type, self.linear_size)?; + let mut raw = device.allocate_memory(self.memory_type, self.linear_size)?; let ptr = match device.map_memory( - &raw, + &mut raw, gfx_hal::memory::Segment { offset: 0, size: Some(self.linear_size), diff --git a/memory/src/mapping/mod.rs b/memory/src/mapping/mod.rs index 126db55a..db91fc1d 100644 --- a/memory/src/mapping/mod.rs +++ b/memory/src/mapping/mod.rs @@ -187,7 +187,7 @@ where offset: aligned_sub_range.start, size: Some(aligned_sub_range.end - aligned_sub_range.start), }, - )))?; + )).iter().cloned())?; } let slice = mapped_slice::(ptr, size); @@ -243,7 +243,7 @@ where offset: aligned_sub_range.start, size: Some(aligned_sub_range.end - aligned_sub_range.start), }, - ))) + )).iter().cloned()) .expect("Should flush successfully"); }) } else { diff --git a/memory/src/memory.rs b/memory/src/memory.rs index e9f66293..ea82d754 100644 --- a/memory/src/memory.rs +++ b/memory/src/memory.rs @@ -29,6 +29,10 @@ where pub fn raw(&self) -> &B::Memory { &self.raw } + /// Get raw mutable memory. + pub fn raw_mut(&mut self) -> &mut B::Memory { + &mut self.raw + } /// Unwrap raw memory. pub fn into_raw(self) -> B::Memory { diff --git a/mesh/src/mesh.rs b/mesh/src/mesh.rs index 99e77616..ee0fcfce 100644 --- a/mesh/src/mesh.rs +++ b/mesh/src/mesh.rs @@ -141,277 +141,277 @@ impl<'a> MeshBuilder<'a> { } /// Set indices buffer to the `MeshBuilder` - pub fn with_indices(mut self, indices: I) -> Self - where - I: Into>, - { - self.set_indices(indices); - self - } + pub fn with_indices(mut self, indices: I) -> Self + where + I: Into>, + { + self.set_indices(indices); + self + } - /// Set indices buffer to the `MeshBuilder` - pub fn set_indices(&mut self, indices: I) -> &mut Self - where - I: Into>, - { - self.indices = match indices.into() { - Indices::None => None, - Indices::U16(i) => Some(RawIndices { - indices: cast_cow(i), - index_type: rendy_core::hal::IndexType::U16, - }), - Indices::U32(i) => Some(RawIndices { - indices: cast_cow(i), - index_type: rendy_core::hal::IndexType::U32, - }), - }; - self - } + /// Set indices buffer to the `MeshBuilder` + pub fn set_indices(&mut self, indices: I) -> &mut Self + where + I: Into>, + { + self.indices = match indices.into() { + Indices::None => None, + Indices::U16(i) => Some(RawIndices { + indices: cast_cow(i), + index_type: rendy_core::hal::IndexType::U16, + }), + Indices::U32(i) => Some(RawIndices { + indices: cast_cow(i), + index_type: rendy_core::hal::IndexType::U32, + }), + }; + self + } - /// Add another vertices to the `MeshBuilder` - pub fn with_vertices(mut self, vertices: D) -> Self - where - V: AsVertex + 'a, - D: Into>, - { - self.add_vertices(vertices); - self - } + /// Add another vertices to the `MeshBuilder` + pub fn with_vertices(mut self, vertices: D) -> Self + where + V: AsVertex + 'a, + D: Into>, + { + self.add_vertices(vertices); + self + } - /// Add another vertices to the `MeshBuilder` - pub fn add_vertices(&mut self, vertices: D) -> &mut Self - where - V: AsVertex + 'a, - D: Into>, - { - self.vertices.push(RawVertices { - vertices: cast_cow(vertices.into()), - format: V::vertex(), - }); - self - } + /// Add another vertices to the `MeshBuilder` + pub fn add_vertices(&mut self, vertices: D) -> &mut Self + where + V: AsVertex + 'a, + D: Into>, + { + self.vertices.push(RawVertices { + vertices: cast_cow(vertices.into()), + format: V::vertex(), + }); + self + } - /// Sets the primitive type of the mesh. - /// - /// By default, meshes are constructed as triangle lists. - pub fn with_prim_type(mut self, prim: rendy_core::hal::pso::Primitive) -> Self { - self.prim = prim; - self - } + /// Sets the primitive type of the mesh. + /// + /// By default, meshes are constructed as triangle lists. + pub fn with_prim_type(mut self, prim: rendy_core::hal::pso::Primitive) -> Self { + self.prim = prim; + self + } - /// Sets the primitive type of the mesh. - /// - /// By default, meshes are constructed as triangle lists. - pub fn set_prim_type(&mut self, prim: rendy_core::hal::pso::Primitive) -> &mut Self { - self.prim = prim; - self - } + /// Sets the primitive type of the mesh. + /// + /// By default, meshes are constructed as triangle lists. + pub fn set_prim_type(&mut self, prim: rendy_core::hal::pso::Primitive) -> &mut Self { + self.prim = prim; + self + } - /// Builds and returns the new mesh. - /// - /// A mesh expects all vertex buffers to have the same number of elements. - /// If those are not equal, the length of smallest vertex buffer is selected, - /// effectively discaring extra data from larger buffers. - /// - /// Note that contents of index buffer is not validated. - pub fn build(&self, queue: QueueId, factory: &Factory) -> Result, UploadError> - where - B: rendy_core::hal::Backend, - { - let align = factory.physical().limits().non_coherent_atom_size; - let mut len = self - .vertices - .iter() - .map(|v| v.vertices.len() as u32 / v.format.stride) - .min() - .unwrap_or(0); + /// Builds and returns the new mesh. + /// + /// A mesh expects all vertex buffers to have the same number of elements. + /// If those are not equal, the length of smallest vertex buffer is selected, + /// effectively discaring extra data from larger buffers. + /// + /// Note that contents of index buffer is not validated. + pub fn build(&self, queue: QueueId, factory: &Factory) -> Result, UploadError> + where + B: rendy_core::hal::Backend, + { + let align = factory.physical().limits().non_coherent_atom_size; + let mut len = self + .vertices + .iter() + .map(|v| v.vertices.len() as u32 / v.format.stride) + .min() + .unwrap_or(0); - let buffer_size = self - .vertices - .iter() - .map(|v| (v.format.stride * len) as usize) - .sum(); - - let aligned_size = align_by(align, buffer_size) as u64; - - let mut staging = factory - .create_buffer( - BufferInfo { - size: aligned_size, - usage: rendy_core::hal::buffer::Usage::TRANSFER_SRC, - }, - Upload, - ) - .map_err(UploadError::Create)?; - - let mut buffer = factory - .create_buffer( - BufferInfo { - size: buffer_size as _, - usage: rendy_core::hal::buffer::Usage::VERTEX - | rendy_core::hal::buffer::Usage::TRANSFER_DST, - }, - Data, - ) - .map_err(UploadError::Create)?; - - let mut mapped = staging - .map(factory, 0..aligned_size) - .map_err(UploadError::Map)?; - let mut writer = - unsafe { mapped.write(factory, 0..aligned_size) }.map_err(UploadError::Map)?; - let staging_slice = unsafe { writer.slice() }; - - let mut offset = 0usize; - let mut vertex_layouts: Vec<_> = self - .vertices - .iter() - .map(|RawVertices { vertices, format }| { - let size = (format.stride * len) as usize; - staging_slice[offset..offset + size].copy_from_slice(&vertices[0..size]); - let this_offset = offset as u64; - offset += size; - VertexBufferLayout { - offset: this_offset, - format: format.clone(), + let buffer_size = self + .vertices + .iter() + .map(|v| (v.format.stride * len) as usize) + .sum(); + + let aligned_size = align_by(align, buffer_size) as u64; + + let mut staging = factory + .create_buffer( + BufferInfo { + size: aligned_size, + usage: rendy_core::hal::buffer::Usage::TRANSFER_SRC, + }, + Upload, + ) + .map_err(UploadError::Create)?; + + let mut buffer = factory + .create_buffer( + BufferInfo { + size: buffer_size as _, + usage: rendy_core::hal::buffer::Usage::VERTEX + | rendy_core::hal::buffer::Usage::TRANSFER_DST, + }, + Data, + ) + .map_err(UploadError::Create)?; + + let mut mapped = staging + .map(factory, 0..aligned_size) + .map_err(UploadError::Map)?; + let mut writer = + unsafe { mapped.write(factory, 0..aligned_size) }.map_err(UploadError::Map)?; + let staging_slice = unsafe { writer.slice() }; + + let mut offset = 0usize; + let mut vertex_layouts: Vec<_> = self + .vertices + .iter() + .map(|RawVertices { vertices, format }| { + let size = (format.stride * len) as usize; + staging_slice[offset..offset + size].copy_from_slice(&vertices[0..size]); + let this_offset = offset as u64; + offset += size; + VertexBufferLayout { + offset: this_offset, + format: format.clone(), + } + }) + .collect(); + + drop(writer); + drop(mapped); + + vertex_layouts.sort_unstable_by(|a, b| a.format.cmp(&b.format)); + + let index_buffer = match self.indices { + None => None, + Some(RawIndices { + ref indices, + index_type, + }) => { + len = (indices.len() / index_stride(index_type)) as u32; + let mut buffer = factory + .create_buffer( + BufferInfo { + size: indices.len() as _, + usage: rendy_core::hal::buffer::Usage::INDEX + | rendy_core::hal::buffer::Usage::TRANSFER_DST, + }, + Data, + ) + .map_err(UploadError::Create)?; + unsafe { + // New buffer can't be touched by device yet. + factory.upload_buffer( + &mut buffer, + 0, + &indices, + None, + BufferState::new(queue) + .with_access(rendy_core::hal::buffer::Access::INDEX_BUFFER_READ) + .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT), + )?; + } + + Some(IndexBuffer { buffer, index_type }) } - }) - .collect(); - - drop(writer); - drop(mapped); - - vertex_layouts.sort_unstable_by(|a, b| a.format.cmp(&b.format)); - - let index_buffer = match self.indices { - None => None, - Some(RawIndices { - ref indices, - index_type, - }) => { - len = (indices.len() / index_stride(index_type)) as u32; - let mut buffer = factory - .create_buffer( - BufferInfo { - size: indices.len() as _, - usage: rendy_core::hal::buffer::Usage::INDEX - | rendy_core::hal::buffer::Usage::TRANSFER_DST, - }, - Data, - ) - .map_err(UploadError::Create)?; - unsafe { - // New buffer can't be touched by device yet. - factory.upload_buffer( + }; + + unsafe { + factory + .upload_from_staging_buffer( &mut buffer, 0, - &indices, + staging, None, BufferState::new(queue) - .with_access(rendy_core::hal::buffer::Access::INDEX_BUFFER_READ) + .with_access(rendy_core::hal::buffer::Access::VERTEX_BUFFER_READ) .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT), - )?; - } - - Some(IndexBuffer { buffer, index_type }) + ) + .map_err(UploadError::Upload)?; } - }; - unsafe { - factory - .upload_from_staging_buffer( - &mut buffer, - 0, - staging, - None, - BufferState::new(queue) - .with_access(rendy_core::hal::buffer::Access::VERTEX_BUFFER_READ) - .with_stage(rendy_core::hal::pso::PipelineStage::VERTEX_INPUT), - ) - .map_err(UploadError::Upload)?; + Ok(Mesh { + vertex_layouts, + index_buffer, + vertex_buffer: buffer, + prim: self.prim, + len, + }) } - - Ok(Mesh { - vertex_layouts, - index_buffer, - vertex_buffer: buffer, - prim: self.prim, - len, - }) } -} - -fn align_by(align: usize, value: usize) -> usize { - ((value + align - 1) / align) * align -} -/// Single mesh is a collection of buffer ranges that provides available attributes. -/// Usually exactly one mesh is used per draw call. -#[derive(Debug)] -pub struct Mesh { - vertex_buffer: Escape>, - vertex_layouts: Vec, - index_buffer: Option>, - prim: rendy_core::hal::pso::Primitive, - len: u32, -} - -impl Mesh -where - B: rendy_core::hal::Backend, -{ - /// Build new mesh with `MeshBuilder` - pub fn builder<'a>() -> MeshBuilder<'a> { - MeshBuilder::new() + fn align_by(align: usize, value: usize) -> usize { + ((value + align - 1) / align) * align } - /// rendy_core::hal::pso::Primitive type of the `Mesh` - pub fn primitive(&self) -> rendy_core::hal::pso::Primitive { - self.prim + /// Single mesh is a collection of buffer ranges that provides available attributes. + /// Usually exactly one mesh is used per draw call. + #[derive(Debug)] + pub struct Mesh { + vertex_buffer: Escape>, + vertex_layouts: Vec, + index_buffer: Option>, + prim: rendy_core::hal::pso::Primitive, + len: u32, } - /// Returns the number of vertices that will be drawn - /// in the mesh. For a mesh with no index buffer, - /// this is the same as the number of vertices, or for - /// a mesh with indices, this is the same as the number - /// of indices. - pub fn len(&self) -> u32 { - self.len - } + impl Mesh + where + B: rendy_core::hal::Backend, + { + /// Build new mesh with `MeshBuilder` + pub fn builder<'a>() -> MeshBuilder<'a> { + MeshBuilder::new() + } - fn get_vertex_iter<'a>( - &'a self, - formats: &[VertexFormat], - ) -> Result, Incompatible> { - debug_assert!(is_slice_sorted(formats), "Formats: {:#?}", formats); - debug_assert!(is_slice_sorted_by_key(&self.vertex_layouts, |l| &l.format)); - - let mut vertex = smallvec::SmallVec::<[_; 16]>::new(); - - let mut next = 0; - for format in formats { - if let Some(index) = find_compatible_buffer(&self.vertex_layouts[next..], format) { - next += index; - vertex.push(self.vertex_layouts[next].offset); - } else { - // Can't bind - return Err(Incompatible { - not_found: format.clone(), - in_formats: self - .vertex_layouts - .iter() - .map(|l| l.format.clone()) - .collect(), - }); - } + /// rendy_core::hal::pso::Primitive type of the `Mesh` + pub fn primitive(&self) -> rendy_core::hal::pso::Primitive { + self.prim } - let buffer = self.vertex_buffer.raw(); - Ok(vertex.into_iter().map(move |offset| (buffer, offset))) - } + /// Returns the number of vertices that will be drawn + /// in the mesh. For a mesh with no index buffer, + /// this is the same as the number of vertices, or for + /// a mesh with indices, this is the same as the number + /// of indices. + pub fn len(&self) -> u32 { + self.len + } + + fn get_vertex_iter<'a>( + &'a self, + formats: &[VertexFormat], + ) -> Result + ExactSizeIterator, Incompatible> { + debug_assert!(is_slice_sorted(formats), "Formats: {:#?}", formats); + debug_assert!(is_slice_sorted_by_key(&self.vertex_layouts, |l| &l.format)); + + let mut vertex = smallvec::SmallVec::<[_; 16]>::new(); + + let mut next = 0; + for format in formats { + if let Some(index) = find_compatible_buffer(&self.vertex_layouts[next..], format) { + next += index; + vertex.push(self.vertex_layouts[next].offset); + } else { + // Can't bind + return Err(Incompatible { + not_found: format.clone(), + in_formats: self + .vertex_layouts + .iter() + .map(|l| l.format.clone()) + .collect(), + }); + } + } + + let buffer = self.vertex_buffer.raw(); + Ok(vertex.into_iter().map(move |offset| (buffer, offset))) + } - /// Bind buffers to specified attribute locations. - pub fn bind( + /// Bind buffers to specified attribute locations. + pub fn bind( &self, first_binding: u32, formats: &[VertexFormat], diff --git a/rendy/Cargo.toml b/rendy/Cargo.toml index c2bf4021..1f75ad12 100644 --- a/rendy/Cargo.toml +++ b/rendy/Cargo.toml @@ -25,7 +25,7 @@ command = ["rendy-command"] descriptor = ["rendy-descriptor"] factory = ["rendy-factory", "command", "descriptor", "resource", "wsi"] frame = ["rendy-frame", "factory"] -graph = ["rendy-graph", "frame"] +#graph = ["rendy-graph", "frame"] init = ["rendy-init", "factory"] init-winit = ["rendy-init/winit"] memory = ["rendy-memory"] @@ -50,7 +50,7 @@ base = [ "descriptor", "factory", "frame", - "graph", +# "graph", "init", "memory", "mesh", @@ -88,7 +88,7 @@ rendy-command = { version = "0.5.1", path = "../command", optional = true } rendy-descriptor = { version = "0.5.1", path = "../descriptor", optional = true } rendy-factory = { version = "0.5.1", path = "../factory", optional = true } rendy-frame = { version = "0.5.1", path = "../frame", optional = true } -rendy-graph = { version = "0.5.1", path = "../graph", optional = true } +#rendy-graph = { version = "0.5.1", path = "../graph", optional = true } rendy-init = { version = "0.5.1", path = "../init", optional = true } rendy-memory = { version = "0.5.2", path = "../memory", optional = true } rendy-mesh = { version = "0.5.1", path = "../mesh", optional = true } diff --git a/rendy/examples/triangle_nograph/main.rs b/rendy/examples/triangle_nograph/main.rs new file mode 100644 index 00000000..ee35496b --- /dev/null +++ b/rendy/examples/triangle_nograph/main.rs @@ -0,0 +1,544 @@ +//! +//! The mighty triangle example. +//! This examples shows colord triangle on white background. +//! Nothing fancy. Just prove that `rendy` works. +//! + +use std::borrow::Borrow; + +use rendy::{ + command::{Families, QueueId, RenderPassEncoder, Submission}, + factory::{Config, Factory}, + //graph::{render::*, Graph, GraphBuilder, GraphContext, NodeBuffer, NodeImage}, + hal::{self, Backend, device::Device}, + init::winit::{ + dpi::PhysicalSize, + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::{Window, WindowBuilder}, + }, + init::AnyWindowedRendy, + memory::Dynamic, + mesh::PosColor, + resource::{Buffer, BufferInfo, DescriptorSetLayout, Escape, Handle}, + shader::{ShaderKind, SourceLanguage, SourceShaderInfo, SpirvShader}, + wsi::Surface, +}; + +#[cfg(feature = "spirv-reflection")] +use rendy::shader::SpirvReflection; + +#[cfg(not(feature = "spirv-reflection"))] +use rendy::mesh::AsVertex; + +lazy_static::lazy_static! { + static ref VERTEX: SpirvShader = SourceShaderInfo::new( + include_str!("shader.vert"), + concat!(env!("CARGO_MANIFEST_DIR"), "/examples/triangle/shader.vert").into(), + ShaderKind::Vertex, + SourceLanguage::GLSL, + "main", + ).precompile().unwrap(); + + static ref FRAGMENT: SpirvShader = SourceShaderInfo::new( + include_str!("shader.frag"), + concat!(env!("CARGO_MANIFEST_DIR"), "/examples/triangle/shader.frag").into(), + ShaderKind::Fragment, + SourceLanguage::GLSL, + "main", + ).precompile().unwrap(); + + static ref SHADERS: rendy::shader::ShaderSetBuilder = rendy::shader::ShaderSetBuilder::default() + .with_vertex(&*VERTEX).unwrap() + .with_fragment(&*FRAGMENT).unwrap(); +} + +#[cfg(feature = "spirv-reflection")] +lazy_static::lazy_static! { + static ref SHADER_REFLECTION: SpirvReflection = SHADERS.reflect().unwrap(); +} + +fn run2( + event_loop: EventLoop<()>, + mut factory: Factory, + mut families: Families, + mut surface: Surface, + window: Window, +) { + + use rendy::resource::Layout; + use rendy::core::hal::format::{Format, ChannelType}; + use rendy::core::hal::pass::{Attachment, AttachmentOps, AttachmentLoadOp, AttachmentStoreOp, SubpassDesc}; + use rendy::core::hal::window::PresentMode; + use rendy::core::hal::pso::{GraphicsPipelineDesc, VertexBufferDesc, AttributeDesc}; + use rendy::core::hal::command::RenderAttachmentInfo; + use rendy::command::{CommandPool, Graphics, IndividualReset, MultiShot, NoSimultaneousUse}; + use rendy::frame::{Frames, cirque::CommandCirque}; + + let (width, height) = window.inner_size().into(); + let suggested_extent = hal::window::Extent2D { width, height }; + let surface_extent = unsafe { + surface.extent(factory.physical()).unwrap_or(suggested_extent) + }; + println!("surface extent: {:?}", surface_extent); + + println!("families: {:?}", families); + + let family_id = families.with_capability::().unwrap(); + let queue_idx: usize = 0; + println!("selected family: {:?}", family_id); + + assert!(factory.surface_support(family_id, &surface)); + + //let framebuffer_width; + //let framebuffer_height; + //let framebuffer_layers; + + //let mut target; + //let views; + + let caps = factory.get_surface_capabilities(&surface); + let formats = factory.get_surface_formats(&surface); + + let present_mode = match () { + _ if caps.present_modes.contains(PresentMode::FIFO) => PresentMode::FIFO, + _ if caps.present_modes.contains(PresentMode::MAILBOX) => PresentMode::MAILBOX, + _ if caps.present_modes.contains(PresentMode::RELAXED) => PresentMode::RELAXED, + _ if caps.present_modes.contains(PresentMode::IMMEDIATE) => PresentMode::IMMEDIATE, + _ => panic!("No known present modes found"), + }; + + let img_count_caps = caps.image_count.clone(); + let image_count = 3.min(*img_count_caps.end()).max(*img_count_caps.start()); + + let format = formats.map_or(Format::Rgba8Srgb, |formats| { + formats + .iter() + .find(|format| format.base_format().1 == ChannelType::Srgb) + .map(|format| *format) + .unwrap_or(formats[0]) + }); + + let swapchain_config = rendy_core::hal::window::SwapchainConfig::from_caps(&caps, format, surface_extent); + let fat = swapchain_config.framebuffer_attachment(); + + unsafe { + surface.configure_swapchain( + factory.device(), + swapchain_config, + ).unwrap(); + } + + //target = factory + // .create_target( + // surface, + // surface_extent, + // image_count, + // present_mode, + // surface_usage, + // ).unwrap(); + + //framebuffer_width = target.extent().width; + //framebuffer_height = target.extent().height; + //framebuffer_layers = target.backbuffer()[0].layers(); + + //views = target.backbuffer().iter().map(|image| unsafe { + // factory + // .device() + // .create_image_view( + // image.raw(), + // rendy_core::hal::image::ViewKind::D2, + // image.format(), + // rendy_core::hal::format::Swizzle::NO, + // rendy_core::hal::image::SubresourceRange { + // aspects: image.format().surface_desc().aspects, + // level_start: 0, + // level_count: None, + // layer_start: 0, + // layer_count: None, + // }, + // ) + // .unwrap() + //}).collect::>(); + + let render_pass = unsafe { + factory.device().create_render_pass( + [ + Attachment { + format: Some(format), + samples: 1, + ops: AttachmentOps { + load: AttachmentLoadOp::Clear, + store: AttachmentStoreOp::Store, + }, + stencil_ops: AttachmentOps { + load: AttachmentLoadOp::DontCare, + store: AttachmentStoreOp::DontCare, + }, + layouts: Layout::Undefined..Layout::Present, + }, + ].iter().cloned(), + [ + SubpassDesc { + colors: &[ + (0, Layout::ColorAttachmentOptimal), + ], + depth_stencil: None, + inputs: &[], + resolves: &[], + preserves: &[], + }, + ].iter().cloned(), + None.iter().cloned(), + ).unwrap() + }; + + //struct PerImage { + // framebuffer: B::Framebuffer, + // //acquire: B::Semaphore, + // release: B::Semaphore, + // index: usize, + //} + + let fb = unsafe { + factory + .device() + .create_framebuffer( + &render_pass, + [fat].iter().cloned(), + rendy::core::hal::image::Extent { + width: surface_extent.width, + height: surface_extent.height, + depth: 1, + }, + ) + .unwrap() + }; + + //let mut framebuffers = views.iter().map(|view| unsafe { + // let fb = factory + // .device() + // .create_framebuffer( + // &render_pass, + // std::iter::once(view), + // rendy::core::hal::image::Extent { + // width: framebuffer_width, + // height: framebuffer_height, + // depth: framebuffer_layers as u32, + // }, + // ) + // .unwrap(); + // PerImage { + // framebuffer: fb, + // //acquire: factory.create_semaphore().unwrap(), + // release: factory.create_semaphore().unwrap(), + // index: 0, + // } + //}) + //.collect::>>(); + + let pipeline_layout = unsafe { + factory.device().create_pipeline_layout( + None.iter().cloned(), + None.iter().cloned(), + ).unwrap() + }; + + let mut shader_set = SHADERS + .build(&factory, Default::default()) + .unwrap(); + + #[cfg(feature = "spirv-reflection")] + let vbuf_size = SHADER_REFLECTION.attributes_range(..).unwrap().stride as u64 * 3; + + #[cfg(not(feature = "spirv-reflection"))] + let vbuf_size = PosColor::vertex().stride as u64 * 3; + + let mut vbuf = factory + .create_buffer( + BufferInfo { + size: vbuf_size, + usage: hal::buffer::Usage::VERTEX, + }, + Dynamic, + ) + .unwrap(); + + unsafe { + // Fresh buffer. + factory + .upload_visible_buffer( + &mut vbuf, + 0, + &[ + PosColor { + position: [0.0, -0.5, 0.0].into(), + color: [1.0, 0.0, 0.0, 1.0].into(), + }, + PosColor { + position: [0.5, 0.5, 0.0].into(), + color: [0.0, 1.0, 0.0, 1.0].into(), + }, + PosColor { + position: [-0.5, 0.5, 0.0].into(), + color: [0.0, 0.0, 1.0, 1.0].into(), + }, + ], + ) + .unwrap(); + } + + #[cfg(feature = "spirv-reflection")] + let (vert_elements, vert_stride, vert_rate) = SHADER_REFLECTION + .attributes_range(..) + .unwrap() + .gfx_vertex_input_desc(hal::pso::VertexInputRate::Vertex); + + #[cfg(not(feature = "spirv-reflection"))] + let (vert_elements, vert_stride, vert_rate) = + PosColor::vertex().gfx_vertex_input_desc(hal::pso::VertexInputRate::Vertex); + + let rect = rendy_core::hal::pso::Rect { + x: 0, + y: 0, + w: surface_extent.width as i16, + h: surface_extent.height as i16, + }; + + let graphics_pipeline = unsafe { + factory.device().create_graphics_pipeline( + &GraphicsPipelineDesc { + label: None, + + primitive_assembler: rendy::core::hal::pso::PrimitiveAssemblerDesc::Vertex { + buffers: &[ + VertexBufferDesc { + binding: 0, + stride: vert_stride, + rate: vert_rate, + }, + ], + attributes: &vert_elements.iter().enumerate().map(|(idx, elem)| { + AttributeDesc { + location: idx as u32, + binding: 0, + element: *elem, + } + }).collect::>(), + input_assembler: rendy::core::hal::pso::InputAssemblerDesc { + primitive: rendy::core::hal::pso::Primitive::TriangleList, + with_adjacency: false, + restart_index: None, + }, + vertex: shader_set.raw_vertex().unwrap().unwrap(), + tessellation: None, + geometry: shader_set.raw_geometry().unwrap(), + }, + rasterizer: rendy::core::hal::pso::Rasterizer::FILL, + fragment: shader_set.raw_fragment().unwrap(), + + blender: rendy::core::hal::pso::BlendDesc { + logic_op: None, + targets: vec![ + rendy::core::hal::pso::ColorBlendDesc { + mask: rendy::core::hal::pso::ColorMask::ALL, + blend: None, + }, + ], + }, + depth_stencil: rendy::core::hal::pso::DepthStencilDesc { + depth: None, + depth_bounds: false, + stencil: None, + }, + multisampling: None, + baked_states: rendy_core::hal::pso::BakedStates { + viewport: Some(rendy_core::hal::pso::Viewport { + rect, + depth: (0.0.into())..(1.0.into()), + }), + scissor: Some(rect), + blend_color: None, + depth_bounds: None, + }, + layout: &pipeline_layout, + subpass: rendy_core::hal::pass::Subpass { + index: 0, + main_pass: &render_pass, + }, + flags: rendy::core::hal::pso::PipelineCreationFlags::empty(), + parent: rendy::core::hal::pso::BasePipeline::None, + }, + None, + ).unwrap() + }; + + let clears = vec![ + hal::command::ClearValue { + color: hal::command::ClearColor { + float32: [1.0, 1.0, 1.0, 1.0], + }, + }, + ]; + + let mut command_pool: CommandPool<_, Graphics, IndividualReset> = factory + .create_command_pool(families.family(family_id)) + .unwrap() + .with_capability() + .unwrap(); + + let mut command_cirque: CommandCirque<_, Graphics> = CommandCirque::new(); + + let mut frames = Frames::new(); + + let mut free_acquire = factory.create_semaphore().unwrap(); + let mut release: Vec<_> = (0..image_count).map(|_| factory.create_semaphore().unwrap()).collect(); + + + + + + + + let started = std::time::Instant::now(); + + let mut frame = 0u64; + let mut elapsed = started.elapsed(); + + let mut release_idx: usize = 0; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Poll; + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + _ => {} + }, + Event::MainEventsCleared => { + factory.maintain(&mut families); + frame += 1; + //if let Some(ref mut graph) = graph { + //graph.run(&mut factory, &mut families, &()); + //} + + release_idx += 1; + if release_idx >= image_count as _ { + release_idx = 0; + } + + let family = families.family_mut(family_id); + + let sc_image = match unsafe { surface.acquire_image(!0) } { + Ok((image, _suboptimal)) => { + log::trace!("Presentable image acquired: {:#?}", image); + image + }, + Err(err) => { + log::debug!("Swapchain acquisition error: {:#?}", err); + return; + }, + }; + + let submit = command_cirque.encode(&frames, &mut command_pool, |mut cbuf| { + let index = cbuf.index(); + + cbuf.or_init(|cbuf| { + let mut cbuf = cbuf.begin(MultiShot(NoSimultaneousUse), ()); + let mut encoder = cbuf.encoder(); + + let area = rendy_core::hal::pso::Rect { + x: 0, + y: 0, + w: surface_extent.width as _, + h: surface_extent.height as _, + }; + + let mut pass_encoder = encoder.begin_render_pass_inline( + &render_pass, + &fb, + area, + std::iter::once(RenderAttachmentInfo { + image_view: sc_image.borrow(), + clear_value: hal::command::ClearValue { + color: hal::command::ClearColor { + float32: [1.0, 1.0, 1.0, 1.0], + }, + }, + }), + ); + + pass_encoder.bind_graphics_pipeline(&graphics_pipeline); + unsafe { + pass_encoder.bind_vertex_buffers(0, Some((vbuf.raw(), 0))); + pass_encoder.draw(0..3, 0..1); + } + + drop(pass_encoder); + + cbuf.finish() + }) + }); + + { + let queue = family.queue_mut(0); + unsafe { + queue.submit( + Some( + Submission::new() + .submits(Some(submit)) + .signal(std::iter::once(&release[release_idx])) + ), + None, + ); + } + + log::trace!("Present"); + if let Err(err) = unsafe { surface.present(queue.raw(), sc_image, Some(&mut release[release_idx])) } { + log::debug!("Swapchain presentation error: {:#?}", err); + } + + } + + elapsed = started.elapsed(); + if elapsed >= std::time::Duration::new(5, 0) { + *control_flow = ControlFlow::Exit + } + } + _ => {} + } + + if *control_flow == ControlFlow::Exit { + let elapsed_ns = elapsed.as_secs() * 1_000_000_000 + elapsed.subsec_nanos() as u64; + + log::info!( + "Elapsed: {:?}. Frames: {}. FPS: {}", + elapsed, + frame, + frame * 1_000_000_000 / elapsed_ns + ); + + shader_set.dispose(&factory); + } + }); + + + + +} + +fn main() { + env_logger::Builder::from_default_env() + .filter_module("triangle", log::LevelFilter::Trace) + .init(); + + let config: Config = Default::default(); + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .with_inner_size(PhysicalSize::new(960, 640)) + .with_title("Rendy example"); + + let rendy = AnyWindowedRendy::init_auto(&config, window, &event_loop).unwrap(); + rendy::with_any_windowed_rendy!((rendy)(mut factory, mut families, surface, window) => { + run2(event_loop, factory, families, surface, window); + }); +} diff --git a/rendy/examples/triangle_nograph/shader.frag b/rendy/examples/triangle_nograph/shader.frag new file mode 100644 index 00000000..c1ceb77c --- /dev/null +++ b/rendy/examples/triangle_nograph/shader.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(early_fragment_tests) in; + +layout(location = 0) in vec4 frag_color; +layout(location = 0) out vec4 color; + +void main() { + color = frag_color; +} diff --git a/rendy/examples/triangle_nograph/shader.vert b/rendy/examples/triangle_nograph/shader.vert new file mode 100644 index 00000000..1562324a --- /dev/null +++ b/rendy/examples/triangle_nograph/shader.vert @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 pos; +layout(location = 1) in vec4 color; +layout(location = 0) out vec4 frag_color; + +void main() { + frag_color = color; + gl_Position = vec4(pos, 1.0); +} diff --git a/resource/src/buffer.rs b/resource/src/buffer.rs index 7575a620..daf69492 100644 --- a/resource/src/buffer.rs +++ b/resource/src/buffer.rs @@ -1,6 +1,10 @@ //! Buffer usage, creation-info and wrappers. -pub use rendy_core::hal::buffer::*; +use rendy_core::hal; +pub use hal::buffer::*; +use hal::{ + device::Device as _, Backend, +}; use { crate::{ @@ -9,7 +13,6 @@ use { CreationError, }, relevant::Relevant, - rendy_core::hal::{device::Device as _, Backend}, }; /// Buffer info. @@ -69,7 +72,7 @@ where let block = heaps .allocate( device, - reqs.type_mask as u32, + reqs.type_mask, memory_usage, reqs.size, reqs.alignment, diff --git a/resource/src/image.rs b/resource/src/image.rs index 0efd94fe..a3f03e5a 100644 --- a/resource/src/image.rs +++ b/resource/src/image.rs @@ -73,10 +73,10 @@ where memory_usage: impl MemoryUsage, ) -> Result { assert!( - info.levels <= info.kind.num_levels(), + info.levels <= info.kind.compute_num_levels(), "Number of mip leves ({}) cannot be greater than {} for given kind {:?}", info.levels, - info.kind.num_levels(), + info.kind.compute_num_levels(), info.kind, ); @@ -96,7 +96,7 @@ where let block = heaps .allocate( device, - reqs.type_mask as u32, + reqs.type_mask, memory_usage, reqs.size, reqs.alignment, @@ -249,8 +249,10 @@ where info.swizzle, SubresourceRange { aspects: info.range.aspects, - layers: info.range.layers.clone(), - levels: info.range.levels.clone(), + level_start: info.range.level_start, + level_count: info.range.level_count, + layer_start: info.range.layer_start, + layer_count: info.range.layer_count, }, ) .map_err(CreationError::Create)? diff --git a/resource/src/set.rs b/resource/src/set.rs index 983f4145..e771c8dc 100644 --- a/resource/src/set.rs +++ b/resource/src/set.rs @@ -44,7 +44,7 @@ where info: DescriptorSetInfo, ) -> Result { let raw = device - .create_descriptor_set_layout(&info.bindings, std::iter::empty::())?; + .create_descriptor_set_layout(info.bindings.iter().cloned(), std::iter::empty::<&B::Sampler>())?; Ok(DescriptorSetLayout { device: device.id(), diff --git a/shader/Cargo.toml b/shader/Cargo.toml index abb3d9f5..adf9b58e 100644 --- a/shader/Cargo.toml +++ b/shader/Cargo.toml @@ -23,3 +23,4 @@ rendy-core = { version = "0.5.1", path = "../core" } shaderc = { version = "0.7.0", optional = true } serde = { version = "1.0.118", optional = true, features = ["derive"] } spirv-reflect = { version = "0.2.3", optional = true } +gfx-auxil = "0.8" diff --git a/shader/src/lib.rs b/shader/src/lib.rs index 795796e1..e10ef36e 100644 --- a/shader/src/lib.rs +++ b/shader/src/lib.rs @@ -27,6 +27,8 @@ pub use self::reflect::{ReflectError, ReflectTypeError, RetrievalKind, SpirvRefl use rendy_core::hal::{pso::ShaderStageFlags, Backend}; use std::collections::HashMap; +use gfx_auxil::read_spirv; + /// Error type returned by this module. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ShaderError {} @@ -99,7 +101,7 @@ mod serde_spirv { { // Via the serde::Deserialize impl for &[u8]. let bytes: &[u8] = serde::Deserialize::deserialize(deserializer)?; - rendy_core::hal::pso::read_spirv(std::io::Cursor::new(bytes)) + read_spirv(std::io::Cursor::new(bytes)) .map_err(serde::de::Error::custom) } } @@ -123,7 +125,7 @@ impl SpirvShader { entrypoint: &str, ) -> std::io::Result { Ok(Self::new( - rendy_core::hal::pso::read_spirv(std::io::Cursor::new(spirv))?, + read_spirv(std::io::Cursor::new(spirv))?, stage, entrypoint, )) @@ -176,34 +178,74 @@ impl ShaderSet { Ok(self) } - /// Returns the `GraphicsShaderSet` structure to provide all the runtime information needed to use the shaders in this set in rendy_core::hal. - pub fn raw<'a>( + pub fn raw_vertex<'a>( &'a self, - ) -> Result, ShaderError> { - Ok(rendy_core::hal::pso::GraphicsShaderSet { - vertex: self - .shaders - .get(&ShaderStageFlags::VERTEX) - .expect("ShaderSet doesn't contain vertex shader") - .get_entry_point()? - .unwrap(), - fragment: match self.shaders.get(&ShaderStageFlags::FRAGMENT) { - Some(fragment) => fragment.get_entry_point()?, - None => None, - }, - domain: match self.shaders.get(&ShaderStageFlags::DOMAIN) { - Some(domain) => domain.get_entry_point()?, - None => None, - }, - hull: match self.shaders.get(&ShaderStageFlags::HULL) { - Some(hull) => hull.get_entry_point()?, - None => None, - }, - geometry: match self.shaders.get(&ShaderStageFlags::GEOMETRY) { - Some(geometry) => geometry.get_entry_point()?, - None => None, - }, - }) + ) -> Result>, ShaderError> { + if let Some(e) = self.shaders.get(&ShaderStageFlags::VERTEX) { + Ok(Some(e.get_entry_point()?.unwrap())) + } else { + Ok(None) + } + } + + pub fn raw_fragment<'a>( + &'a self, + ) -> Result>, ShaderError> { + if let Some(e) = self.shaders.get(&ShaderStageFlags::FRAGMENT) { + Ok(Some(e.get_entry_point()?.unwrap())) + } else { + Ok(None) + } + } + + pub fn raw_domain<'a>( + &'a self, + ) -> Result>, ShaderError> { + if let Some(e) = self.shaders.get(&ShaderStageFlags::DOMAIN) { + Ok(Some(e.get_entry_point()?.unwrap())) + } else { + Ok(None) + } + } + + pub fn raw_hull<'a>( + &'a self, + ) -> Result>, ShaderError> { + if let Some(e) = self.shaders.get(&ShaderStageFlags::HULL) { + Ok(Some(e.get_entry_point()?.unwrap())) + } else { + Ok(None) + } + } + + pub fn raw_geometry<'a>( + &'a self, + ) -> Result>, ShaderError> { + if let Some(e) = self.shaders.get(&ShaderStageFlags::GEOMETRY) { + Ok(Some(e.get_entry_point()?.unwrap())) + } else { + Ok(None) + } + } + + pub fn raw_mesh<'a>( + &'a self, + ) -> Result>, ShaderError> { + if let Some(e) = self.shaders.get(&ShaderStageFlags::MESH) { + Ok(Some(e.get_entry_point()?.unwrap())) + } else { + Ok(None) + } + } + + pub fn raw_task<'a>( + &'a self, + ) -> Result>, ShaderError> { + if let Some(e) = self.shaders.get(&ShaderStageFlags::TASK) { + Ok(Some(e.get_entry_point()?.unwrap())) + } else { + Ok(None) + } } /// Must be called to perform a drop of the Backend ShaderModule object otherwise the shader will never be destroyed in memory. @@ -262,7 +304,7 @@ impl ShaderSetBuilder { if self.vertex.is_none() && self.compute.is_none() { let msg = "A vertex or compute shader must be provided".to_string(); - return Err(rendy_core::hal::device::ShaderError::InterfaceMismatch(msg)); + return Err(rendy_core::hal::device::ShaderError::CompilationFailed(msg)); } type ShaderTy = ( Vec, diff --git a/texture/src/texture.rs b/texture/src/texture.rs index d71b2bb3..43396934 100644 --- a/texture/src/texture.rs +++ b/texture/src/texture.rs @@ -447,8 +447,10 @@ impl<'a> TextureBuilder<'a> { image.clone(), image::SubresourceRange { aspects: info.format.surface_desc().aspects, - levels: 1..mip_levels, - layers: 0..info.kind.num_layers(), + level_start: 1, + level_count: Some(mip_levels - 1), // TODO TODO VALIDATE is this right? + layer_start: 0, + layer_count: Some(info.kind.num_layers()), }, image::Layout::Undefined, next_state, @@ -467,8 +469,10 @@ impl<'a> TextureBuilder<'a> { swizzle: double_swizzle(self.swizzle, transform_swizzle), range: image::SubresourceRange { aspects: info.format.surface_desc().aspects, - levels: 0..info.levels, - layers: 0..info.kind.num_layers(), + level_start: 0, + level_count: Some(info.levels), + layer_start: 0, + layer_count: Some(info.kind.num_layers()), }, }, ) diff --git a/wsi/src/lib.rs b/wsi/src/lib.rs index 7e818382..e4d0b05b 100644 --- a/wsi/src/lib.rs +++ b/wsi/src/lib.rs @@ -15,7 +15,12 @@ use { rendy_core::hal::{ device::Device as _, format::Format, - window::{Extent2D, Surface as _, SurfaceCapabilities}, + window::{ + Extent2D, Surface as _, SurfaceCapabilities, SwapchainConfig, + PresentationSurface, Suboptimal, AcquireError, SwapchainError, + PresentError, + }, + queue::CommandQueue, Backend, Instance as _, }, rendy_core::{ @@ -24,48 +29,6 @@ use { rendy_resource::{Image, ImageInfo}, }; -/// Error creating a new swapchain. -#[derive(Debug)] -pub enum SwapchainError { - /// Internal error in gfx-hal. - Create(rendy_core::hal::window::CreationError), - /// Present mode is not supported. - BadPresentMode(rendy_core::hal::window::PresentMode), - /// Image count is not supported. - BadImageCount(rendy_core::hal::window::SwapImageIndex), -} - -impl std::fmt::Display for SwapchainError { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - SwapchainError::Create(err) => write!( - fmt, - "Failed to create swapchain because of a window creation error: {:?}", - err - ), - SwapchainError::BadPresentMode(present_mode) => write!( - fmt, - "Failed to create swapchain because requested present mode is not supported: {:?}", - present_mode - ), - SwapchainError::BadImageCount(image_count) => write!( - fmt, - "Failed to create swapchain because requested image count is not supported: {:?}", - image_count - ), - } - } -} - -impl std::error::Error for SwapchainError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - SwapchainError::Create(err) => Some(err), - SwapchainError::BadPresentMode(_) => None, - SwapchainError::BadImageCount(_) => None, - } - } -} /// Rendering target bound to window. pub struct Surface { raw: B::Surface, @@ -180,353 +143,48 @@ where self.raw.capabilities(physical_device) } - /// Cast surface into render target. - pub unsafe fn into_target( - mut self, - physical_device: &B::PhysicalDevice, - device: &Device, - suggest_extent: Extent2D, - image_count: u32, - present_mode: rendy_core::hal::window::PresentMode, - usage: rendy_core::hal::image::Usage, - ) -> Result, SwapchainError> { - assert_eq!( - device.id().instance, - self.instance, - "Resource is not owned by specified instance" - ); - - let (swapchain, backbuffer, extent) = create_swapchain( - &mut self, - physical_device, - device, - suggest_extent, - image_count, - present_mode, - usage, - )?; - - Ok(Target { - device: device.id(), - relevant: relevant::Relevant, - surface: self, - swapchain: Some(swapchain), - backbuffer: Some(backbuffer), - extent, - present_mode, - usage, - }) - } -} - -unsafe fn create_swapchain( - surface: &mut Surface, - physical_device: &B::PhysicalDevice, - device: &Device, - suggest_extent: Extent2D, - image_count: u32, - present_mode: rendy_core::hal::window::PresentMode, - usage: rendy_core::hal::image::Usage, -) -> Result<(B::Swapchain, Vec>, Extent2D), SwapchainError> { - let capabilities = surface.capabilities(physical_device); - let format = surface.format(physical_device); - - if !capabilities.present_modes.contains(present_mode) { - log::warn!( - "Present mode is not supported. Supported: {:#?}, requested: {:#?}", - capabilities.present_modes, - present_mode, - ); - return Err(SwapchainError::BadPresentMode(present_mode)); - } - - log::trace!( - "Surface present modes: {:#?}. Pick {:#?}", - capabilities.present_modes, - present_mode - ); - - log::trace!("Surface chosen format {:#?}", format); - - if image_count < *capabilities.image_count.start() - || image_count > *capabilities.image_count.end() - { - log::warn!( - "Image count not supported. Supported: {:#?}, requested: {:#?}", - capabilities.image_count, - image_count - ); - return Err(SwapchainError::BadImageCount(image_count)); - } - - log::trace!( - "Surface capabilities: {:#?}. Pick {} images", - capabilities.image_count, - image_count - ); - - assert!( - capabilities.usage.contains(usage), - "Surface supports {:?}, but {:?} was requested", - capabilities.usage, - usage - ); - - let extent = capabilities.current_extent.unwrap_or(suggest_extent); - - let (swapchain, images) = device - .create_swapchain( - &mut surface.raw, - rendy_core::hal::window::SwapchainConfig { - present_mode, - format, - extent, - image_count, - image_layers: 1, - image_usage: usage, - composite_alpha_mode: [ - rendy_core::hal::window::CompositeAlphaMode::INHERIT, - rendy_core::hal::window::CompositeAlphaMode::OPAQUE, - rendy_core::hal::window::CompositeAlphaMode::PREMULTIPLIED, - rendy_core::hal::window::CompositeAlphaMode::POSTMULTIPLIED, - ] - .iter() - .cloned() - .find(|&bit| capabilities.composite_alpha_modes.contains(bit)) - .expect("No CompositeAlphaMode modes supported"), - }, - None, - ) - .map_err(SwapchainError::Create)?; - - let backbuffer = images - .into_iter() - .map(|image| { - Image::create_from_swapchain( - device.id(), - ImageInfo { - kind: rendy_core::hal::image::Kind::D2(extent.width, extent.height, 1, 1), - levels: 1, - format, - tiling: rendy_core::hal::image::Tiling::Optimal, - view_caps: rendy_core::hal::image::ViewCapabilities::empty(), - usage, - }, - image, - ) - }) - .collect(); - - Ok((swapchain, backbuffer, extent)) -} - -/// Rendering target bound to window. -/// With swapchain created. -pub struct Target { - device: DeviceId, - surface: Surface, - swapchain: Option, - backbuffer: Option>>, - extent: Extent2D, - present_mode: rendy_core::hal::window::PresentMode, - usage: rendy_core::hal::image::Usage, - relevant: relevant::Relevant, -} - -device_owned!(Target); - -impl std::fmt::Debug for Target -where - B: Backend, -{ - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - fmt.debug_struct("Target") - .field("backbuffer", &self.backbuffer) - .finish() - } -} - -impl Target -where - B: Backend, -{ - /// Dispose of target. - /// - /// # Safety - /// - /// Swapchain must be not in use. - pub unsafe fn dispose(mut self, device: &Device) -> Surface { - self.assert_device_owner(device); - - match self.backbuffer { - Some(images) => { - images - .into_iter() - .for_each(|image| image.dispose_swapchain_image(device.id())); - } - _ => {} - }; - - self.relevant.dispose(); - if let Some(s) = self.swapchain.take() { - device.destroy_swapchain(s) - } - self.surface - } - - /// Get raw surface handle. - pub fn surface(&self) -> &Surface { - &self.surface - } - - /// Get raw surface handle. - pub fn swapchain(&self) -> &B::Swapchain { - self.swapchain.as_ref().expect("Swapchain already disposed") - } - - /// Recreate swapchain. - /// - /// #Safety - /// - /// Current swapchain must be not in use. - pub unsafe fn recreate( + /// Set up the swapchain associated with the surface to have the given format. + pub unsafe fn configure_swapchain( &mut self, - physical_device: &B::PhysicalDevice, - device: &Device, - suggest_extent: Extent2D, + device: &B::Device, + config: SwapchainConfig, ) -> Result<(), SwapchainError> { - self.assert_device_owner(device); - - let image_count = match self.backbuffer.take() { - Some(images) => { - let count = images.len(); - images - .into_iter() - .for_each(|image| image.dispose_swapchain_image(device.id())); - count - } - None => 0, - }; - - if let Some(s) = self.swapchain.take() { - device.destroy_swapchain(s) - } - - let (swapchain, backbuffer, extent) = create_swapchain( - &mut self.surface, - physical_device, - device, - suggest_extent, - image_count as u32, - self.present_mode, - self.usage, - )?; - - self.swapchain.replace(swapchain); - self.backbuffer.replace(backbuffer); - self.extent = extent; - - Ok(()) + self.raw.configure_swapchain(device, config) } - /// Get swapchain impl trait. - /// - /// # Safety + /// Remove the associated swapchain from this surface. /// - /// Trait usage should not violate this type valid usage. - pub unsafe fn swapchain_mut(&mut self) -> &mut impl rendy_core::hal::window::Swapchain { - self.swapchain.as_mut().expect("Swapchain already disposed") - } - - /// Get raw handlers for the swapchain images. - pub fn backbuffer(&self) -> &Vec> { - self.backbuffer - .as_ref() - .expect("Swapchain already disposed") - } - - /// Get render target size. - pub fn extent(&self) -> Extent2D { - self.extent - } - - /// Get image usage flags. - pub fn usage(&self) -> rendy_core::hal::image::Usage { - self.usage - } - - /// Acquire next image. - pub unsafe fn next_image( + /// This has to be done before the surface is dropped. + pub unsafe fn unconfigure_swapchain( &mut self, - signal: &B::Semaphore, - ) -> Result, rendy_core::hal::window::AcquireError> { - let index = rendy_core::hal::window::Swapchain::acquire_image( - // Missing swapchain is equivalent to OutOfDate, as it has to be recreated anyway. - self.swapchain - .as_mut() - .ok_or(rendy_core::hal::window::AcquireError::OutOfDate)?, - !0, - Some(signal), - None, - )? - .0; - - Ok(NextImages { - targets: std::iter::once((&*self, index)).collect(), - }) + device: &B::Device, + ) { + self.raw.unconfigure_swapchain(device) } -} - -/// Represents acquire frames that will be presented next. -#[derive(Debug)] -pub struct NextImages<'a, B: Backend> { - targets: smallvec::SmallVec<[(&'a Target, u32); 8]>, -} -impl<'a, B> NextImages<'a, B> -where - B: Backend, -{ - /// Get indices. - pub fn indices(&self) -> impl IntoIterator + '_ { - self.targets.iter().map(|(_s, i)| *i) + /// 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. + pub unsafe fn acquire_image( + &mut self, + timeout_ns: u64, + ) -> Result<(>::SwapchainImage, Option), AcquireError> { + self.raw.acquire_image(timeout_ns) } - /// Present images by the queue. - /// - /// # TODO - /// - /// Use specific presentation error type. - pub unsafe fn present<'b>( - self, - queue: &mut impl rendy_core::hal::queue::CommandQueue, - wait: impl IntoIterator + 'b)>, - ) -> Result, rendy_core::hal::window::PresentError> - where - 'a: 'b, - { + pub unsafe fn present( + &mut self, + queue: &mut impl CommandQueue, + image: >::SwapchainImage, + wait_semaphore: Option<&mut B::Semaphore>, + ) -> Result, PresentError> { queue.present( - self.targets.iter().map(|(target, index)| { - ( - target - .swapchain - .as_ref() - .expect("Swapchain already disposed"), - *index, - ) - }), - wait, + &mut self.raw, + image, + wait_semaphore, ) } } - -impl<'a, B> std::ops::Index for NextImages<'a, B> -where - B: Backend, -{ - type Output = u32; - - fn index(&self, index: usize) -> &u32 { - &self.targets[index].1 - } -}