From 52cf82082edde00eec8b69ddc0ef0b50350873b6 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Thu, 16 Sep 2021 15:14:14 +0200 Subject: [PATCH 1/2] synchronization overhaul based on vk-sync-rs --- demo/src/main.rs | 2 +- proc/src/pass/instance.rs | 16 +- src/access.rs | 281 ++++++++++++++++++---------------- src/backend/vulkan/access.rs | 207 +++++++++++++++++++++++++ src/backend/vulkan/convert.rs | 111 ++------------ src/backend/vulkan/encode.rs | 103 +++++-------- src/backend/vulkan/mod.rs | 1 + src/backend/vulkan/sync.rs | 202 ++++++++++++++++++++++++ src/buffer.rs | 82 +++++----- src/descriptor/image.rs | 10 +- src/descriptor/mod.rs | 6 +- src/encode.rs | 75 ++++----- src/image.rs | 155 ++++++++++++------- src/memory.rs | 15 +- src/render_pass.rs | 10 +- src/view.rs | 74 ++++----- 16 files changed, 843 insertions(+), 507 deletions(-) create mode 100644 src/backend/vulkan/sync.rs diff --git a/demo/src/main.rs b/demo/src/main.rs index 31f3d0c..e4561d8 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -16,7 +16,7 @@ pub struct Pipeline { #[sierra::pass] #[subpass(color = target)] pub struct Main { - #[attachment(clear(const sierra::ClearColor(0.3, 0.1, 0.8, 1.0)), store(const sierra::Layout::Present))] + #[attachment(clear(const sierra::ClearColor(0.3, 0.1, 0.8, 1.0)), store(const sierra::RawLayout::Present))] target: sierra::Image, } diff --git a/proc/src/pass/instance.rs b/proc/src/pass/instance.rs index d275e54..05b8acd 100644 --- a/proc/src/pass/instance.rs +++ b/proc/src/pass/instance.rs @@ -18,7 +18,7 @@ pub(super) fn generate(input: &Input) -> TokenStream { let initial_layout = initial_layout(&a.load_op); let check_final_layout = final_layout(&a.store_op).map(|final_layout| { - quote::quote!(else if a.final_layout != ::sierra::Layout::from(#final_layout) { + quote::quote!(else if a.final_layout != ::sierra::RawLayout::from(#final_layout) { tracing::debug!("Final layout is incompatible. Old {:?}, new {:?}", a.final_layout, #final_layout); render_pass_compatible = false; } ) @@ -34,8 +34,8 @@ pub(super) fn generate(input: &Input) -> TokenStream { tracing::debug!("Samples count is incompatible. Old {:?}, new {:?}", a.samples, ::sierra::Attachment::samples(&input.#member)); render_pass_compatible = false; } - } else if a.initial_layout != ::std::option::Option::map(#initial_layout, ::sierra::Layout::from) { - tracing::debug!("Initial layout is incompatible. Old {:?}, new {:?}", a.initial_layout, ::std::option::Option::map(#initial_layout, ::sierra::Layout::from)); + } else if a.initial_layout != ::std::option::Option::map(#initial_layout, ::sierra::RawLayout::from) { + tracing::debug!("Initial layout is incompatible. Old {:?}, new {:?}", a.initial_layout, ::std::option::Option::map(#initial_layout, ::sierra::RawLayout::from)); render_pass_compatible = false; } #check_final_layout ) @@ -65,10 +65,10 @@ pub(super) fn generate(input: &Input) -> TokenStream { .iter() .filter_map(|s| { if s.colors.iter().any(|&c| c == index) { - Some(quote::quote!(::sierra::Layout::ColorAttachmentOptimal)) + Some(quote::quote!(::sierra::RawLayout::ColorAttachmentOptimal)) } else if s.depth == Some(index) { Some(quote::quote!( - ::sierra::Layout::DepthStencilAttachmentOptimal + ::sierra::RawLayout::DepthStencilAttachmentOptimal )) } else { None @@ -76,7 +76,7 @@ pub(super) fn generate(input: &Input) -> TokenStream { }) .rev() .next() - .unwrap_or_else(|| quote::quote!(::sierra::Layout::General)) + .unwrap_or_else(|| quote::quote!(::sierra::RawLayout::General)) } Some(layout) => layout, }; @@ -107,7 +107,7 @@ pub(super) fn generate(input: &Input) -> TokenStream { .colors .iter() .map( - |&c| quote::quote!(colors.push((#c, ::sierra::Layout::ColorAttachmentOptimal));), + |&c| quote::quote!(colors.push((#c, ::sierra::RawLayout::ColorAttachmentOptimal));), ) .collect::(); @@ -122,7 +122,7 @@ pub(super) fn generate(input: &Input) -> TokenStream { #push_colors colors }, - depth: Some((#depth, ::sierra::Layout::DepthStencilAttachmentOptimal)), + depth: Some((#depth, ::sierra::RawLayout::DepthStencilAttachmentOptimal)), }); ) } diff --git a/src/access.rs b/src/access.rs index 74cbd3e..7ab610d 100644 --- a/src/access.rs +++ b/src/access.rs @@ -1,139 +1,148 @@ -bitflags::bitflags! { - /// Flags for access types. - pub struct AccessFlags: u32 { - /// Read access to indirect command data - /// read as part of an indirect build, - /// trace, drawing or dispatch command. - const INDIRECT_COMMAND_READ = 0x00000001; - - /// Read access to an index buffer as part of an indexed drawing command - const INDEX_READ = 0x00000002; - - /// Read access to a vertex buffer as part of a drawing command - const VERTEX_ATTRIBUTE_READ = 0x00000004; - - /// Read access to a uniform buffer.. - const UNIFORM_READ = 0x00000008; - - /// Read access to an input attachment - /// within a render pass during fragment shading. - const INPUT_ATTACHMENT_READ = 0x00000010; - - /// Read access to a storage buffer, physical storage buffer, - /// shader binding table, uniform texel buffer, storage texel buffer, - /// sampled image, or storage image. - const SHADER_READ = 0x00000020; - - /// Write access to a storage buffer, physical storage buffer, - /// storage texel buffer, or storage image. - const SHADER_WRITE = 0x00000040; - - /// Read access to a color attachment, - /// such as via blending, logic operations, - /// or via certain subpass load operations.\ - /// It does not include advanced blend operations. - const COLOR_ATTACHMENT_READ = 0x00000080; - - /// Write access to a color, resolve, - /// or depth/stencil resolve attachment - /// during a render pass - /// or via certain subpass load and store operations. - const COLOR_ATTACHMENT_WRITE = 0x00000100; - - /// Read access to a depth/stencil attachment, - /// via depth or stencil operations - /// or via certain subpass load operations. - const DEPTH_STENCIL_ATTACHMENT_READ = 0x00000200; - - /// Write access to a depth/stencil attachment, - /// via depth or stencil operations - /// or via certain subpass load and store operations. - const DEPTH_STENCIL_ATTACHMENT_WRITE = 0x00000400; - - /// Read access to an image or buffer in a copy operation. - const TRANSFER_READ = 0x00000800; - - /// Write access to an image or buffer in a clear or copy operation. - const TRANSFER_WRITE = 0x00001000; - - /// Read access by a host operation. - const HOST_READ = 0x00002000; - - /// Write access by a host operation. - const HOST_WRITE = 0x00004000; - - /// All read accesses. - const MEMORY_READ = 0x00008000; - - /// All write accesses. - const MEMORY_WRITE = 0x00010000; - - // /// Write access to a transform feedback buffer made - // /// when transform feedback is active. - // const TRANSFORM_FEEDBACK_WRITE = 0x00020000; - - // /// Read access to a transform feedback counter buffer - // /// which is read when vkCmdBeginTransformFeedbackEXT executes. - // const TRANSFORM_FEEDBACK_COUNTER_READ = 0x00040000; - - // /// Write access to a transform feedback counter buffer - // /// which is written when vkCmdEndTransformFeedbackEXT executes. - // const TRANSFORM_FEEDBACK_COUNTER_WRITE = 0x00080000; - - /// Read access to a predicate as part of conditional rendering. - const CONDITIONAL_RENDERING_READ = 0x00100000; - - /// Similar to [`COLOR_ATTACHMENT_READ`], - /// but also includes advanced blend operations. - const COLOR_ATTACHMENT_READ_NONCOHERENT = 0x00200000; - - /// Read access to an acceleration structure - /// as part of a trace, build, or copy command, - /// or to an acceleration structure scratch buffer - // as part of a build command. - const ACCELERATION_STRUCTURE_READ = 0x00400000; - - /// Write access to an acceleration structure - /// or acceleration structure scratch buffer - /// as part of a build or copy command. - const ACCELERATION_STRUCTURE_WRITE = 0x00800000; - - /// Read access to a fragment density map attachment - /// during dynamic fragment density map operations. - const FRAGMENT_DENSITY_MAP_READ = 0x01000000; - - /// Read access to a fragment shading rate attachment - /// or shading rate image during rasterization. - const FRAGMENT_SHADING_RATE_ATTACHMENT_READ = 0x02000000; - } +// Modified from vk-sync-rs, originally Copyright 2019 Graham Wihlidal +// licensed under MIT license. +// +// https://github.com/gwihlidal/vk-sync-rs/blob/master/LICENSE-MIT + +/// Defines all potential resource usages +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash,)] +pub enum Access { + /// No access. Useful primarily for initialization + None, + + /// Read as an indirect buffer for drawing or dispatch + IndirectBuffer, + + /// Read as an index buffer for drawing + IndexBuffer, + + /// Read as a vertex buffer for drawing + VertexBuffer, + + /// Read as a uniform buffer in a vertex shader + VertexShaderReadUniformBuffer, + + /// Read as a sampled image/uniform texel buffer in a vertex shader + VertexShaderReadSampledImageOrUniformTexelBuffer, + + /// Read as any other resource in a vertex shader + VertexShaderReadOther, + + /// Read as a uniform buffer in a fragment shader + FragmentShaderReadUniformBuffer, + + /// Read as a sampled image/uniform texel buffer in a fragment shader + FragmentShaderReadSampledImageOrUniformTexelBuffer, + + /// Read as an input attachment with a color format in a fragment shader + FragmentShaderReadColorInputAttachment, + + /// Read as an input attachment with a depth/stencil format in a fragment shader + FragmentShaderReadDepthStencilInputAttachment, + + /// Read as any other resource in a fragment shader + FragmentShaderReadOther, + + /// Read by blending/logic operations or subpass load operations + ColorAttachmentRead, + + /// Read by depth/stencil tests or subpass load operations + DepthStencilAttachmentRead, + + /// Read as a uniform buffer in a compute shader + ComputeShaderReadUniformBuffer, + + /// Read as a sampled image/uniform texel buffer in a compute shader + ComputeShaderReadSampledImageOrUniformTexelBuffer, + + /// Read as any other resource in a compute shader + ComputeShaderReadOther, + + /// Read as a uniform buffer in any shader + AnyShaderReadUniformBuffer, + + /// Read as a uniform buffer in any shader, or a vertex buffer + AnyShaderReadUniformBufferOrVertexBuffer, + + /// Read as a sampled image in any shader + AnyShaderReadSampledImageOrUniformTexelBuffer, + + /// Read as any other resource (excluding attachments) in any shader + AnyShaderReadOther, + + /// Read as the source of a transfer operation + TransferRead, + + /// Read on the host + HostRead, + + /// Read by the presentation engine (i.e. `vkQueuePresentKHR`) + Present, + + /// Written as any resource in a vertex shader + VertexShaderWrite, + + /// Written as any resource in a fragment shader + FragmentShaderWrite, + + /// Written as a color attachment during rendering, or via a subpass store op + ColorAttachmentWrite, + + /// Written as a depth/stencil attachment during rendering, or via a subpass store op + DepthStencilAttachmentWrite, + + /// Written as a depth aspect of a depth/stencil attachment during rendering, whilst the + /// stencil aspect is read-only. Requires `VK_KHR_maintenance2` to be enabled. + DepthAttachmentWriteStencilReadOnly, + + /// Written as a stencil aspect of a depth/stencil attachment during rendering, whilst the + /// depth aspect is read-only. Requires `VK_KHR_maintenance2` to be enabled. + StencilAttachmentWriteDepthReadOnly, + + /// Written as any resource in a compute shader + ComputeShaderWrite, + + /// Written as any resource in any shader + AnyShaderWrite, + + /// Written as the destination of a transfer operation + TransferWrite, + + /// Written on the host + HostWrite, + + /// Read or written as a color attachment during rendering + ColorAttachmentReadWrite, + + /// Covers any access - useful for debug, generally avoid for performance reasons + General, +} + +impl Default for Access { + fn default() -> Self { + Access::None + } } -impl AccessFlags { - pub fn is_read(self) -> bool { - self.intersects( - Self::SHADER_READ - | Self::COLOR_ATTACHMENT_READ - | Self::DEPTH_STENCIL_ATTACHMENT_READ - | Self::TRANSFER_READ - | Self::HOST_READ - | Self::MEMORY_READ - // | Self::TRANSFORM_FEEDBACK_READ - // | Self::TRANSFORM_FEEDBACK_COUNTER_READ - | Self::ACCELERATION_STRUCTURE_READ, - ) - } - - pub fn is_write(self) -> bool { - self.intersects( - Self::SHADER_WRITE - | Self::COLOR_ATTACHMENT_WRITE - | Self::DEPTH_STENCIL_ATTACHMENT_WRITE - | Self::TRANSFER_WRITE - | Self::HOST_WRITE - | Self::MEMORY_WRITE - // | Self::TRANSFORM_FEEDBACK_WRITE - // | Self::TRANSFORM_FEEDBACK_COUNTER_WRITE - | Self::ACCELERATION_STRUCTURE_WRITE, - ) - } +impl Access { + pub fn is_write(self) -> bool { + match self { + Access::VertexShaderWrite => true, + Access::FragmentShaderWrite => true, + Access::ColorAttachmentWrite => true, + Access::DepthStencilAttachmentWrite => true, + Access::DepthAttachmentWriteStencilReadOnly => true, + Access::StencilAttachmentWriteDepthReadOnly => true, + Access::ComputeShaderWrite => true, + Access::AnyShaderWrite => true, + Access::TransferWrite => true, + Access::HostWrite => true, + Access::ColorAttachmentReadWrite => true, + Access::General => true, + _ => false, + } + } + + pub fn is_read_only(self) -> bool { + !self.is_write() + } } diff --git a/src/backend/vulkan/access.rs b/src/backend/vulkan/access.rs index d9c38f7..14df8e4 100644 --- a/src/backend/vulkan/access.rs +++ b/src/backend/vulkan/access.rs @@ -1,5 +1,7 @@ use erupt::vk1_0; +use crate::Access; + fn supported_stages_inner(access: vk1_0::AccessFlags) -> vk1_0::PipelineStageFlags { type AF = vk1_0::AccessFlags; type PS = vk1_0::PipelineStageFlags; @@ -68,3 +70,208 @@ pub(crate) fn supported_access(stages: vk1_0::PipelineStageFlags) -> vk1_0::Acce result } + +pub(crate) struct AccessInfo { + pub(crate) stage_mask: vk1_0::PipelineStageFlags, + pub(crate) access_mask: vk1_0::AccessFlags, + pub(crate) image_layout: vk1_0::ImageLayout, +} + +pub(crate) trait GetAccessInfo { + fn access_info(self) -> AccessInfo; +} + +impl GetAccessInfo for Access { + fn access_info(self) -> AccessInfo { + match self { + Access::None => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::empty(), + access_mask: vk1_0::AccessFlags::empty(), + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::IndirectBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::DRAW_INDIRECT, + access_mask: vk1_0::AccessFlags::INDIRECT_COMMAND_READ, + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::IndexBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::VERTEX_INPUT, + access_mask: vk1_0::AccessFlags::INDEX_READ, + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::VertexBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::VERTEX_INPUT, + access_mask: vk1_0::AccessFlags::VERTEX_ATTRIBUTE_READ, + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::VertexShaderReadUniformBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::VERTEX_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::VertexShaderReadSampledImageOrUniformTexelBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::VERTEX_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + }, + Access::VertexShaderReadOther => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::VERTEX_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::FragmentShaderReadUniformBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::FRAGMENT_SHADER, + access_mask: vk1_0::AccessFlags::UNIFORM_READ, + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::FragmentShaderReadSampledImageOrUniformTexelBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::FRAGMENT_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + }, + Access::FragmentShaderReadColorInputAttachment => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::FRAGMENT_SHADER, + access_mask: vk1_0::AccessFlags::INPUT_ATTACHMENT_READ, + image_layout: vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + }, + Access::FragmentShaderReadDepthStencilInputAttachment => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::FRAGMENT_SHADER, + access_mask: vk1_0::AccessFlags::INPUT_ATTACHMENT_READ, + image_layout: vk1_0::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL, + }, + Access::FragmentShaderReadOther => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::FRAGMENT_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::ColorAttachmentRead => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, + access_mask: vk1_0::AccessFlags::COLOR_ATTACHMENT_READ, + image_layout: vk1_0::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, + }, + Access::DepthStencilAttachmentRead => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::EARLY_FRAGMENT_TESTS + | vk1_0::PipelineStageFlags::LATE_FRAGMENT_TESTS, + access_mask: vk1_0::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ, + image_layout: vk1_0::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL, + }, + Access::ComputeShaderReadUniformBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::COMPUTE_SHADER, + access_mask: vk1_0::AccessFlags::UNIFORM_READ, + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::ComputeShaderReadSampledImageOrUniformTexelBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::COMPUTE_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + }, + Access::ComputeShaderReadOther => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::COMPUTE_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::AnyShaderReadUniformBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::ALL_COMMANDS, + access_mask: vk1_0::AccessFlags::UNIFORM_READ, + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::AnyShaderReadUniformBufferOrVertexBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::ALL_COMMANDS, + access_mask: vk1_0::AccessFlags::UNIFORM_READ + | vk1_0::AccessFlags::VERTEX_ATTRIBUTE_READ, + image_layout: vk1_0::ImageLayout::UNDEFINED, + }, + Access::AnyShaderReadSampledImageOrUniformTexelBuffer => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::ALL_COMMANDS, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + }, + Access::AnyShaderReadOther => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::ALL_COMMANDS, + access_mask: vk1_0::AccessFlags::SHADER_READ, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::TransferRead => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::TRANSFER, + access_mask: vk1_0::AccessFlags::TRANSFER_READ, + image_layout: vk1_0::ImageLayout::TRANSFER_SRC_OPTIMAL, + }, + Access::HostRead => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::HOST, + access_mask: vk1_0::AccessFlags::HOST_READ, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::Present => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::empty(), + access_mask: vk1_0::AccessFlags::empty(), + image_layout: vk1_0::ImageLayout::PRESENT_SRC_KHR, + }, + Access::VertexShaderWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::VERTEX_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_WRITE, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::FragmentShaderWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::FRAGMENT_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_WRITE, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::ColorAttachmentWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, + access_mask: vk1_0::AccessFlags::COLOR_ATTACHMENT_WRITE, + image_layout: vk1_0::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, + }, + Access::DepthStencilAttachmentWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::EARLY_FRAGMENT_TESTS + | vk1_0::PipelineStageFlags::LATE_FRAGMENT_TESTS, + access_mask: vk1_0::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE, + image_layout: vk1_0::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + }, + Access::DepthAttachmentWriteStencilReadOnly => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::EARLY_FRAGMENT_TESTS + | vk1_0::PipelineStageFlags::LATE_FRAGMENT_TESTS, + access_mask: vk1_0::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE + | vk1_0::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ, + image_layout: vk1_0::ImageLayout::DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, + }, + Access::StencilAttachmentWriteDepthReadOnly => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::EARLY_FRAGMENT_TESTS + | vk1_0::PipelineStageFlags::LATE_FRAGMENT_TESTS, + access_mask: vk1_0::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE + | vk1_0::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ, + image_layout: vk1_0::ImageLayout::DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, + }, + Access::ComputeShaderWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::COMPUTE_SHADER, + access_mask: vk1_0::AccessFlags::SHADER_WRITE, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::AnyShaderWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::ALL_COMMANDS, + access_mask: vk1_0::AccessFlags::SHADER_WRITE, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::TransferWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::TRANSFER, + access_mask: vk1_0::AccessFlags::TRANSFER_WRITE, + image_layout: vk1_0::ImageLayout::TRANSFER_DST_OPTIMAL, + }, + Access::HostWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::HOST, + access_mask: vk1_0::AccessFlags::HOST_WRITE, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + Access::ColorAttachmentReadWrite => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, + access_mask: vk1_0::AccessFlags::COLOR_ATTACHMENT_READ + | vk1_0::AccessFlags::COLOR_ATTACHMENT_WRITE, + image_layout: vk1_0::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, + }, + Access::General => AccessInfo { + stage_mask: vk1_0::PipelineStageFlags::ALL_COMMANDS, + access_mask: vk1_0::AccessFlags::MEMORY_READ | vk1_0::AccessFlags::MEMORY_WRITE, + image_layout: vk1_0::ImageLayout::GENERAL, + }, + } + } +} \ No newline at end of file diff --git a/src/backend/vulkan/convert.rs b/src/backend/vulkan/convert.rs index 4a4be2e..c778af8 100644 --- a/src/backend/vulkan/convert.rs +++ b/src/backend/vulkan/convert.rs @@ -1,10 +1,10 @@ use crate::{ - out_of_host_memory, AccelerationStructureBuildFlags, AccelerationStructureLevel, AccessFlags, + out_of_host_memory, AccelerationStructureBuildFlags, AccelerationStructureLevel, AspectFlags, BlendFactor, BlendOp, BorderColor, BufferCopy, BufferImageCopy, BufferUsage, CompareOp, ComponentMask, CompositeAlphaFlags, Culling, DescriptorBindingFlags, DescriptorSetLayoutFlags, DescriptorType, DeviceAddress, Extent2d, Extent3d, Filter, Format, FrontFace, GeometryFlags, ImageBlit, ImageCopy, ImageExtent, ImageUsage, ImageViewKind, - IndexType, Layout, LoadOp, LogicOp, MemoryUsage, MipmapMode, Offset2d, Offset3d, OutOfMemory, + IndexType, RawLayout, LoadOp, LogicOp, MemoryUsage, MipmapMode, Offset2d, Offset3d, OutOfMemory, PipelineStageFlags, PolygonMode, PresentMode, PrimitiveTopology, QueueCapabilityFlags, Rect2d, SamplerAddressMode, Samples, ShaderStage, ShaderStageFlags, StencilOp, StoreOp, Subresource, SubresourceLayers, SubresourceRange, SurfaceTransformFlags, VertexInputRate, Viewport, @@ -990,26 +990,26 @@ impl ToErupt for ComponentMask { } } -impl ToErupt for Layout { +impl ToErupt for RawLayout { fn to_erupt(self) -> vk1_0::ImageLayout { match self { - Layout::General => vk1_0::ImageLayout::GENERAL, - Layout::ColorAttachmentOptimal => vk1_0::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, - Layout::DepthStencilAttachmentOptimal => { + RawLayout::General => vk1_0::ImageLayout::GENERAL, + RawLayout::ColorAttachmentOptimal => vk1_0::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, + RawLayout::DepthStencilAttachmentOptimal => { vk1_0::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL } - Layout::DepthStencilReadOnlyOptimal => { + RawLayout::DepthStencilReadOnlyOptimal => { vk1_0::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL } - Layout::ShaderReadOnlyOptimal => vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, - Layout::TransferSrcOptimal => vk1_0::ImageLayout::TRANSFER_SRC_OPTIMAL, - Layout::TransferDstOptimal => vk1_0::ImageLayout::TRANSFER_DST_OPTIMAL, - Layout::Present => vk1_0::ImageLayout::PRESENT_SRC_KHR, + RawLayout::ShaderReadOnlyOptimal => vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + RawLayout::TransferSrcOptimal => vk1_0::ImageLayout::TRANSFER_SRC_OPTIMAL, + RawLayout::TransferDstOptimal => vk1_0::ImageLayout::TRANSFER_DST_OPTIMAL, + RawLayout::Present => vk1_0::ImageLayout::PRESENT_SRC_KHR, } } } -impl ToErupt for Option { +impl ToErupt for Option { fn to_erupt(self) -> vk1_0::ImageLayout { match self { None => vk1_0::ImageLayout::UNDEFINED, @@ -1334,93 +1334,6 @@ impl ToErupt for DescriptorSetLayoutFlags } } -impl ToErupt for AccessFlags { - fn to_erupt(self) -> vk1_0::AccessFlags { - let mut result = vk1_0::AccessFlags::empty(); - - if self.contains(Self::INDIRECT_COMMAND_READ) { - result |= vk1_0::AccessFlags::INDIRECT_COMMAND_READ; - } - if self.contains(Self::INDEX_READ) { - result |= vk1_0::AccessFlags::INDEX_READ; - } - if self.contains(Self::VERTEX_ATTRIBUTE_READ) { - result |= vk1_0::AccessFlags::VERTEX_ATTRIBUTE_READ; - } - if self.contains(Self::UNIFORM_READ) { - result |= vk1_0::AccessFlags::UNIFORM_READ; - } - if self.contains(Self::INPUT_ATTACHMENT_READ) { - result |= vk1_0::AccessFlags::INPUT_ATTACHMENT_READ; - } - if self.contains(Self::SHADER_READ) { - result |= vk1_0::AccessFlags::SHADER_READ; - } - if self.contains(Self::SHADER_WRITE) { - result |= vk1_0::AccessFlags::SHADER_WRITE; - } - if self.contains(Self::COLOR_ATTACHMENT_READ) { - result |= vk1_0::AccessFlags::COLOR_ATTACHMENT_READ; - } - if self.contains(Self::COLOR_ATTACHMENT_WRITE) { - result |= vk1_0::AccessFlags::COLOR_ATTACHMENT_WRITE; - } - if self.contains(Self::DEPTH_STENCIL_ATTACHMENT_READ) { - result |= vk1_0::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ; - } - if self.contains(Self::DEPTH_STENCIL_ATTACHMENT_WRITE) { - result |= vk1_0::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE; - } - if self.contains(Self::TRANSFER_READ) { - result |= vk1_0::AccessFlags::TRANSFER_READ; - } - if self.contains(Self::TRANSFER_WRITE) { - result |= vk1_0::AccessFlags::TRANSFER_WRITE; - } - if self.contains(Self::HOST_READ) { - result |= vk1_0::AccessFlags::HOST_READ; - } - if self.contains(Self::HOST_WRITE) { - result |= vk1_0::AccessFlags::HOST_WRITE; - } - if self.contains(Self::MEMORY_READ) { - result |= vk1_0::AccessFlags::MEMORY_READ; - } - if self.contains(Self::MEMORY_WRITE) { - result |= vk1_0::AccessFlags::MEMORY_WRITE; - } - // if self.contains(Self::TRANSFORM_FEEDBACK_WRITE) { - // result |= vk1_0::AccessFlags::TRANSFORM_FEEDBACK_WRITE_EXT; - // } - // if self.contains(Self::TRANSFORM_FEEDBACK_COUNTER_READ) { - // result |= vk1_0::AccessFlags::TRANSFORM_FEEDBACK_COUNTER_READ_EXT; - // } - // if self.contains(Self::TRANSFORM_FEEDBACK_COUNTER_WRITE) { - // result |= vk1_0::AccessFlags::TRANSFORM_FEEDBACK_COUNTER_WRITE_EXT; - // } - if self.contains(Self::CONDITIONAL_RENDERING_READ) { - result |= vk1_0::AccessFlags::CONDITIONAL_RENDERING_READ_EXT; - } - if self.contains(Self::COLOR_ATTACHMENT_READ_NONCOHERENT) { - result |= vk1_0::AccessFlags::COLOR_ATTACHMENT_READ_NONCOHERENT_EXT; - } - if self.contains(Self::ACCELERATION_STRUCTURE_READ) { - result |= vk1_0::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR; - } - if self.contains(Self::ACCELERATION_STRUCTURE_WRITE) { - result |= vk1_0::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR; - } - if self.contains(Self::FRAGMENT_DENSITY_MAP_READ) { - result |= vk1_0::AccessFlags::FRAGMENT_DENSITY_MAP_READ_EXT; - } - if self.contains(Self::FRAGMENT_SHADING_RATE_ATTACHMENT_READ) { - result |= vk1_0::AccessFlags::FRAGMENT_SHADING_RATE_ATTACHMENT_READ_KHR; - } - - result - } -} - impl FromErupt for CompositeAlphaFlags { fn from_erupt(value: CompositeAlphaFlagsKHR) -> Self { let mut result = CompositeAlphaFlags::empty(); diff --git a/src/backend/vulkan/encode.rs b/src/backend/vulkan/encode.rs index 04d4692..f1565f3 100644 --- a/src/backend/vulkan/encode.rs +++ b/src/backend/vulkan/encode.rs @@ -1,9 +1,9 @@ use { super::{ - access::supported_access, convert::{oom_error_from_erupt, ToErupt}, device::WeakDevice, epochs::References, + sync, }, crate::{ accel::{AccelerationStructureGeometry, AccelerationStructureLevel, IndexData}, @@ -685,82 +685,53 @@ impl CommandBuffer { }, Command::PipelineBarrier { - src, - dst, images, buffers, - memory, + global, } => unsafe { - for barrier in images { + let mut src_stages = vk1_0::PipelineStageFlags::empty(); + let mut dst_stages = vk1_0::PipelineStageFlags::empty(); + + let image_barriers = scope.to_scope_from_iter(images.iter().map(|barrier| { assert_owner!(barrier.image, device); self.references.add_image(barrier.image.clone()); - } - for barrier in buffers { + let (src_stg, dst_stg, image_barrier) = sync::get_image_memory_barrier(barrier); + + src_stages |= src_stg; + dst_stages |= dst_stg; + + image_barrier + })); + + let buffer_barriers = scope.to_scope_from_iter(buffers.iter().map(|barrier| { assert_owner!(barrier.buffer, device); self.references.add_buffer(barrier.buffer.clone()); - } + + let (src_stg, dst_stg, buffer_barrier) = sync::get_buffer_memory_barrier(barrier); + + src_stages |= src_stg; + dst_stages |= dst_stg; + + buffer_barrier + })); + + let global = global.map(|global| { + let (src_stg, dst_stg, global_barrier) = sync::get_global_barrier(&global); + + src_stages |= src_stg; + dst_stages |= dst_stg; + + global_barrier + }); logical.cmd_pipeline_barrier( self.handle, - src.to_erupt(), - dst.to_erupt(), + src_stages, + dst_stages, None, - &[vk1_0::MemoryBarrierBuilder::new() - .src_access_mask( - memory - .as_ref() - .map_or(supported_access(src.to_erupt()), |m| m.src.to_erupt()), - ) - .dst_access_mask( - memory - .as_ref() - .map_or(supported_access(dst.to_erupt()), |m| m.dst.to_erupt()), - )], - scope.to_scope_from_iter(buffers.iter().map(|buffer| { - vk1_0::BufferMemoryBarrierBuilder::new() - .buffer(buffer.buffer.handle()) - .offset(buffer.offset) - .size(buffer.size) - .src_access_mask(buffer.old_access.to_erupt()) - .dst_access_mask(buffer.new_access.to_erupt()) - .src_queue_family_index( - buffer - .family_transfer - .as_ref() - .map(|r| r.0) - .unwrap_or(vk1_0::QUEUE_FAMILY_IGNORED), - ) - .dst_queue_family_index( - buffer - .family_transfer - .as_ref() - .map(|r| r.1) - .unwrap_or(vk1_0::QUEUE_FAMILY_IGNORED), - ) - })), - scope.to_scope_from_iter(images.iter().map(|image| { - vk1_0::ImageMemoryBarrierBuilder::new() - .image(image.image.handle()) - .subresource_range(image.range.to_erupt()) - .src_access_mask(image.old_access.to_erupt()) - .dst_access_mask(image.new_access.to_erupt()) - .old_layout(image.old_layout.to_erupt()) - .new_layout(image.new_layout.to_erupt()) - .src_queue_family_index( - image - .family_transfer - .as_ref() - .map(|r| r.0) - .unwrap_or(vk1_0::QUEUE_FAMILY_IGNORED), - ) - .dst_queue_family_index( - image - .family_transfer - .as_ref() - .map(|r| r.1) - .unwrap_or(vk1_0::QUEUE_FAMILY_IGNORED), - ) - })), + scope.to_scope_from_iter(global.into_iter()), + buffer_barriers, + image_barriers, ) }, Command::PushConstants { diff --git a/src/backend/vulkan/mod.rs b/src/backend/vulkan/mod.rs index 7ce61ec..a0ab16f 100644 --- a/src/backend/vulkan/mod.rs +++ b/src/backend/vulkan/mod.rs @@ -14,6 +14,7 @@ mod physical; mod queue; mod resources; mod surface; +mod sync; mod swapchain; pub use self::{ diff --git a/src/backend/vulkan/sync.rs b/src/backend/vulkan/sync.rs new file mode 100644 index 0000000..270ae83 --- /dev/null +++ b/src/backend/vulkan/sync.rs @@ -0,0 +1,202 @@ +// Modified from vk-sync-rs, originally Copyright 2019 Graham Wihlidal +// licensed under MIT license. +// +// https://github.com/gwihlidal/vk-sync-rs/blob/master/LICENSE-MIT + +use crate::{BufferMemoryBarrier, GlobalMemoryBarrier, ImageMemoryBarrier, Layout}; +use super::{access::GetAccessInfo, convert::ToErupt}; + +use erupt::vk1_0; + +/// Mapping function that translates a global barrier into a set of source and +/// destination pipeline stages, and a memory barrier, that can be used with +/// Vulkan synchronization methods. +pub fn get_global_barrier<'a>( + barrier: &GlobalMemoryBarrier, +) -> ( + vk1_0::PipelineStageFlags, + vk1_0::PipelineStageFlags, + vk1_0::MemoryBarrierBuilder<'a>, +) { + let mut src_stages = vk1_0::PipelineStageFlags::empty(); + let mut dst_stages = vk1_0::PipelineStageFlags::empty(); + + let mut src_access_mask = vk1_0::AccessFlags::empty(); + let mut dst_access_mask = vk1_0::AccessFlags::empty(); + + for prev_access in barrier.prev_accesses { + let previous_info = prev_access.access_info(); + + src_stages |= previous_info.stage_mask; + + // Add appropriate availability operations - for writes only. + if prev_access.is_write() { + src_access_mask |= previous_info.access_mask; + } + } + + for next_access in barrier.next_accesses { + let next_info = next_access.access_info(); + + dst_stages |= next_info.stage_mask; + + // Add visibility operations as necessary. + // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), + // so the dst access mask can be safely zeroed as these don't need visibility. + if src_access_mask != vk1_0::AccessFlags::empty() { + dst_access_mask |= next_info.access_mask; + } + } + + // Ensure that the stage masks are valid if no stages were determined + if src_stages == vk1_0::PipelineStageFlags::empty() { + src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; + } + + if dst_stages == vk1_0::PipelineStageFlags::empty() { + dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; + } + + let memory_barrier = vk1_0::MemoryBarrierBuilder::new() + .src_access_mask(src_access_mask) + .dst_access_mask(dst_access_mask); + + (src_stages, dst_stages, memory_barrier) +} + +/// Mapping function that translates a buffer barrier into a set of source and +/// destination pipeline stages, and a buffer memory barrier, that can be used +/// with Vulkan synchronization methods. +pub fn get_buffer_memory_barrier<'a>( + barrier: &BufferMemoryBarrier, +) -> ( + vk1_0::PipelineStageFlags, + vk1_0::PipelineStageFlags, + vk1_0::BufferMemoryBarrierBuilder<'a>, +) { + let mut src_stages = vk1_0::PipelineStageFlags::empty(); + let mut dst_stages = vk1_0::PipelineStageFlags::empty(); + + let mut src_access_mask = vk1_0::AccessFlags::empty(); + let mut dst_access_mask = vk1_0::AccessFlags::empty(); + + let (src_queue_family_index, dst_queue_family_index) = barrier + .family_transfer + .unwrap_or((vk1_0::QUEUE_FAMILY_IGNORED, vk1_0::QUEUE_FAMILY_IGNORED)); + + + let previous_info = barrier.prev_access.access_info(); + + src_stages |= previous_info.stage_mask; + + // Add appropriate availability operations - for writes only. + if barrier.prev_access.is_write() { + src_access_mask |= previous_info.access_mask; + } + + let next_info = barrier.next_access.access_info(); + + dst_stages |= next_info.stage_mask; + + // Add visibility operations as necessary. + // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), + // so the dst access mask can be safely zeroed as these don't need visibility. + if src_access_mask != vk1_0::AccessFlags::empty() { + dst_access_mask |= next_info.access_mask; + } + + // Ensure that the stage masks are valid if no stages were determined + if src_stages == vk1_0::PipelineStageFlags::empty() { + src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; + } + + if dst_stages == vk1_0::PipelineStageFlags::empty() { + dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; + } + + let buffer_barrier = vk1_0::BufferMemoryBarrierBuilder::new() + .src_queue_family_index(src_queue_family_index) + .dst_queue_family_index(dst_queue_family_index) + .buffer(barrier.buffer.handle()) + .offset(barrier.offset as u64) + .size(barrier.size as u64); + + (src_stages, dst_stages, buffer_barrier) +} + +/// Mapping function that translates an image barrier into a set of source and +/// destination pipeline stages, and an image memory barrier, that can be used +/// with Vulkan synchronization methods. +pub fn get_image_memory_barrier<'a>( + barrier: &ImageMemoryBarrier, +) -> ( + vk1_0::PipelineStageFlags, + vk1_0::PipelineStageFlags, + vk1_0::ImageMemoryBarrierBuilder<'a>, +) { + let mut src_stages = vk1_0::PipelineStageFlags::empty(); + let mut dst_stages = vk1_0::PipelineStageFlags::empty(); + + let mut src_access_mask = vk1_0::AccessFlags::empty(); + let mut dst_access_mask = vk1_0::AccessFlags::empty(); + + let (src_queue_family_index, dst_queue_family_index) = barrier + .family_transfer + .unwrap_or((vk1_0::QUEUE_FAMILY_IGNORED, vk1_0::QUEUE_FAMILY_IGNORED)); + + let previous_info = barrier.prev_access.access_info(); + + src_stages |= previous_info.stage_mask; + + // Add appropriate availability operations - for writes only. + if barrier.prev_access.is_write() { + src_access_mask |= previous_info.access_mask; + } + + let old_layout = if let Some(barrier_old_layout) = barrier.old_layout { + match barrier_old_layout { + Layout::Optimal => previous_info.image_layout, + Layout::Manual(layout) => layout.to_erupt(), + } + } else { + vk1_0::ImageLayout::UNDEFINED + }; + + let next_info = barrier.next_access.access_info(); + + dst_stages |= next_info.stage_mask; + + // Add visibility operations as necessary. + // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), + // so the dst access mask can be safely zeroed as these don't need visibility. + if src_access_mask != vk1_0::AccessFlags::empty() { + dst_access_mask |= next_info.access_mask; + } + + let new_layout = match barrier.new_layout { + Layout::Optimal => next_info.image_layout, + Layout::Manual(layout) => layout.to_erupt(), + }; + + // Ensure that the stage masks are valid if no stages were determined + if src_stages == vk1_0::PipelineStageFlags::empty() { + src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; + } + + if dst_stages == vk1_0::PipelineStageFlags::empty() { + dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; + } + + let image_barrier = vk1_0::ImageMemoryBarrierBuilder::new() + .src_queue_family_index(src_queue_family_index) + .dst_queue_family_index(dst_queue_family_index) + .image(barrier.image.handle()) + .subresource_range(barrier.range.to_erupt()) + .src_access_mask(src_access_mask) + .dst_access_mask(dst_access_mask) + .old_layout(old_layout) + .new_layout(new_layout); + + + (src_stages, dst_stages, image_barrier) +} diff --git a/src/buffer.rs b/src/buffer.rs index 33acf1d..9a8b371 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,10 +1,9 @@ pub use crate::backend::{Buffer, MappableBuffer}; use crate::{ - access::AccessFlags, + access::Access, align_up, encode::Encoder, queue::{Ownership, QueueId}, - stage::PipelineStageFlags, }; bitflags::bitflags! { @@ -114,48 +113,56 @@ pub struct StridedBufferRange { pub stride: u64, } +/// Buffer barriers should only be used when a queue family ownership transfer +/// is required - prefer global barriers at all other times. +/// +/// Access types are defined in the same way as for a global memory barrier, but +/// they only affect the buffer range identified by `buffer`, `offset` and `size`, +/// rather than all resources. +/// +/// A buffer barrier defining a queue ownership transfer needs to be executed +/// twice - once by a queue in the source queue family, and then once again by a +/// queue in the desination queue family, with a semaphore guaranteeing +/// execution order between them. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub struct BufferMemoryBarrier<'a> { pub buffer: &'a Buffer, pub offset: u64, pub size: u64, - pub old_access: AccessFlags, - pub new_access: AccessFlags, + pub prev_access: Access, + pub next_access: Access, pub family_transfer: Option<(u32, u32)>, } + /// Buffer range with access mask, -/// specifying how it may be accessed "before". +/// specifying how it has been accessed "before". /// -/// Note that "before" is loosely defined, -/// as whatever previous owners do. -/// Which should be translated to "earlier GPU operations" +/// Note that "before" is loosely defined +/// as whatever previous owners did with it, +/// i.e. "earlier GPU operations which used this resource," /// but this crate doesn't attempt to enforce that. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct BufferRangeState { pub range: BufferRange, - pub access: AccessFlags, - pub stages: PipelineStageFlags, + pub prev_access: Access, pub family: Ownership, } impl BufferRangeState { /// - pub fn access<'a>( - &'a mut self, - access: AccessFlags, - stages: PipelineStageFlags, + pub fn access<'access>( + &'access mut self, + next_access: Access, queue: QueueId, - encoder: &mut Encoder<'a>, - ) -> &'a BufferRange { + encoder: &mut Encoder<'access>, + ) -> &'access BufferRange { match self.family { Ownership::NotOwned => encoder.buffer_barriers( - self.stages, - stages, encoder.scope().to_scope([BufferMemoryBarrier { buffer: &self.range.buffer, - old_access: self.access, - new_access: access, + prev_access: self.prev_access, + next_access, family_transfer: None, offset: self.range.offset, size: self.range.size, @@ -165,12 +172,10 @@ impl BufferRangeState { assert_eq!(family, queue.family, "Wrong queue family owns the buffer"); encoder.buffer_barriers( - self.stages, - stages, encoder.scope().to_scope([BufferMemoryBarrier { buffer: &self.range.buffer, - old_access: self.access, - new_access: access, + prev_access: self.prev_access, + next_access, family_transfer: None, offset: self.range.offset, size: self.range.size, @@ -184,12 +189,10 @@ impl BufferRangeState { ); encoder.buffer_barriers( - self.stages, - stages, encoder.scope().to_scope([BufferMemoryBarrier { buffer: &self.range.buffer, - old_access: self.access, - new_access: access, + prev_access: self.prev_access, + next_access, family_transfer: Some((from, to)), offset: self.range.offset, size: self.range.size, @@ -200,25 +203,21 @@ impl BufferRangeState { self.family = Ownership::Owned { family: queue.family, }; - self.stages = stages; - self.access = access; + self.prev_access = next_access; &self.range } - pub fn overwrite<'a>( - &'a mut self, - access: AccessFlags, - stages: PipelineStageFlags, + pub fn overwrite<'access>( + &'access mut self, + next_access: Access, queue: QueueId, - encoder: &mut Encoder<'a>, - ) -> &'a BufferRange { + encoder: &mut Encoder<'access>, + ) -> &'access BufferRange { encoder.buffer_barriers( - self.stages, - stages, encoder.scope().to_scope([BufferMemoryBarrier { buffer: &self.range.buffer, - old_access: AccessFlags::empty(), - new_access: access, + prev_access: Access::None, + next_access, family_transfer: None, offset: self.range.offset, size: self.range.size, @@ -227,8 +226,7 @@ impl BufferRangeState { self.family = Ownership::Owned { family: queue.family, }; - self.stages = stages; - self.access = access; + self.prev_access = next_access; &self.range } } diff --git a/src/descriptor/image.rs b/src/descriptor/image.rs index dc366d1..babae7d 100644 --- a/src/descriptor/image.rs +++ b/src/descriptor/image.rs @@ -1,7 +1,7 @@ use { super::{DescriptorBindingFlags, ImageViewDescriptor, TypedDescriptorBinding}, crate::{ - image::{Image, Layout}, + image::{Image, RawLayout}, view::{ImageView, ImageViewInfo}, Device, OutOfMemory, }, @@ -20,7 +20,7 @@ impl TypedDescriptorBinding for Image { let view = device.create_image_view(ImageViewInfo::new(self.clone()))?; Ok([ImageViewDescriptor { view, - layout: Layout::ShaderReadOnlyOptimal, + layout: RawLayout::ShaderReadOnlyOptimal, }]) } } @@ -37,7 +37,7 @@ impl TypedDescriptorBinding for ImageView { fn get_descriptors(&self, _device: &Device) -> Result<[ImageViewDescriptor; 1], OutOfMemory> { Ok([ImageViewDescriptor { view: self.clone(), - layout: Layout::ShaderReadOnlyOptimal, + layout: RawLayout::ShaderReadOnlyOptimal, }]) } } @@ -74,7 +74,7 @@ impl TypedDescriptorBinding for [ImageView; N] { unsafe { result.push_unchecked(ImageViewDescriptor { view: me.clone(), - layout: Layout::ShaderReadOnlyOptimal, + layout: RawLayout::ShaderReadOnlyOptimal, }); } } @@ -120,7 +120,7 @@ impl TypedDescriptorBinding for arrayvec::ArrayVec unsafe { result.push_unchecked(ImageViewDescriptor { view: me.clone(), - layout: Layout::ShaderReadOnlyOptimal, + layout: RawLayout::ShaderReadOnlyOptimal, }); } } diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index f1cb9eb..bac929e 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -15,7 +15,7 @@ use crate::{ buffer::BufferRange, encode::Encoder, // image::Image, - image::Layout, + image::RawLayout, // image::{ImageExtent, SubresourceRange}, sampler::Sampler, view::ImageView, @@ -74,7 +74,7 @@ pub struct ImageViewDescriptor { pub view: ImageView, /// View's layout when descriptor is accessed. - pub layout: Layout, + pub layout: RawLayout, } /// Image view, layout and sampler.\ @@ -86,7 +86,7 @@ pub struct CombinedImageSampler { pub view: ImageView, /// View's layout when descriptor is accessed. - pub layout: Layout, + pub layout: RawLayout, /// Descriptor sampler resource. pub sampler: Sampler, diff --git a/src/encode.rs b/src/encode.rs index 844bbae..d39ee6c 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -2,13 +2,13 @@ pub use crate::backend::CommandBuffer; use { crate::{ accel::AccelerationStructureBuildGeometryInfo, - access::AccessFlags, + access::Access, arith_le, buffer::{Buffer, BufferMemoryBarrier}, descriptor::{DescriptorSet, UpdatedPipelineDescriptors}, framebuffer::{Framebuffer, FramebufferError}, - image::{Image, ImageBlit, ImageMemoryBarrier, Layout, SubresourceLayers}, - memory::MemoryBarrier, + image::{Image, ImageBlit, ImageMemoryBarrier, RawLayout, SubresourceLayers}, + memory::GlobalMemoryBarrier, pipeline::{ ComputePipeline, DynamicGraphicsPipeline, GraphicsPipeline, PipelineLayout, RayTracingPipeline, ShaderBindingTable, TypedPipelineLayout, Viewport, @@ -17,7 +17,6 @@ use { render_pass::{ClearValue, RenderPass, RenderPassInstance}, sampler::Filter, shader::ShaderStageFlags, - stage::PipelineStageFlags, Device, Extent3d, IndexType, Offset3d, OutOfMemory, Rect2d, }, arrayvec::ArrayVec, @@ -149,34 +148,32 @@ pub enum Command<'a> { CopyImage { src_image: &'a Image, - src_layout: Layout, + src_layout: RawLayout, dst_image: &'a Image, - dst_layout: Layout, + dst_layout: RawLayout, regions: &'a [ImageCopy], }, CopyBufferImage { src_buffer: &'a Buffer, dst_image: &'a Image, - dst_layout: Layout, + dst_layout: RawLayout, regions: &'a [BufferImageCopy], }, BlitImage { src_image: &'a Image, - src_layout: Layout, + src_layout: RawLayout, dst_image: &'a Image, - dst_layout: Layout, + dst_layout: RawLayout, regions: &'a [ImageBlit], filter: Filter, }, PipelineBarrier { - src: PipelineStageFlags, - dst: PipelineStageFlags, + global: Option>, images: &'a [ImageMemoryBarrier<'a>], buffers: &'a [BufferMemoryBarrier<'a>], - memory: Option, }, PushConstants { @@ -610,9 +607,9 @@ impl<'a> Encoder<'a> { pub fn copy_image( &mut self, src_image: &'a Image, - src_layout: Layout, + src_layout: RawLayout, dst_image: &'a Image, - dst_layout: Layout, + dst_layout: RawLayout, regions: &'a [ImageCopy], ) { self.inner.commands.push( @@ -631,7 +628,7 @@ impl<'a> Encoder<'a> { &mut self, src_buffer: &'a Buffer, dst_image: &'a Image, - dst_layout: Layout, + dst_layout: RawLayout, regions: &'a [BufferImageCopy], ) { self.inner.commands.push( @@ -648,9 +645,9 @@ impl<'a> Encoder<'a> { pub fn blit_image( &mut self, src_image: &'a Image, - src_layout: Layout, + src_layout: RawLayout, dst_image: &'a Image, - dst_layout: Layout, + dst_layout: RawLayout, regions: &'a [ImageBlit], filter: Filter, ) { @@ -677,23 +674,35 @@ impl<'a> Encoder<'a> { .push(self.inner.scope, Command::Dispatch { x, y, z }); } - pub fn memory_barrier( + pub fn pipeline_barrier( &mut self, - src: PipelineStageFlags, - src_acc: AccessFlags, - dst: PipelineStageFlags, - dst_acc: AccessFlags, + global: Option>, + images: &'a [ImageMemoryBarrier<'a>], + buffers: &'a [BufferMemoryBarrier<'a>], + ) { + self.inner.commands.push( + self.inner.scope, + Command::PipelineBarrier { + images, + buffers, + global, + } + ) + } + + pub fn global_barrier( + &mut self, + prev_accesses: &'a [Access], + next_accesses: &'a [Access], ) { self.inner.commands.push( self.inner.scope, Command::PipelineBarrier { - src, - dst, images: &[], buffers: &[], - memory: Some(MemoryBarrier { - src: src_acc, - dst: dst_acc, + global: Some(GlobalMemoryBarrier { + prev_accesses, + next_accesses, }), }, ); @@ -701,36 +710,28 @@ impl<'a> Encoder<'a> { pub fn image_barriers( &mut self, - src: PipelineStageFlags, - dst: PipelineStageFlags, images: &'a [ImageMemoryBarrier<'a>], ) { self.inner.commands.push( self.inner.scope, Command::PipelineBarrier { - src, - dst, images, buffers: &[], - memory: None, + global: None, }, ); } pub fn buffer_barriers( &mut self, - src: PipelineStageFlags, - dst: PipelineStageFlags, buffers: &'a [BufferMemoryBarrier<'a>], ) { self.inner.commands.push( self.inner.scope, Command::PipelineBarrier { - src, - dst, images: &[], buffers, - memory: None, + global: None, }, ); } diff --git a/src/image.rs b/src/image.rs index a3b8e8c..fe3ea58 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,7 +1,7 @@ pub use { self::Samples::*, crate::{ - access::AccessFlags, + access::Access, backend::Image, encode::Encoder, queue::{Ownership, QueueId}, @@ -74,6 +74,26 @@ impl ImageUsage { } } +/// Defines a handful of layout options for images. +/// Rather than a list of all possible image layouts, this reduced list is +/// correlated with the access types to map to the correct actual layouts. +/// `Optimal` is usually preferred. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] +pub enum Layout { + /// Choose the most optimal layout for each usage. Performs layout transitions as appropriate for the access. + Optimal, + + /// Manually choose a RawLayout regardless of usage or access provided. + Manual(RawLayout), +} + +impl Default for Layout { + fn default() -> Self { + Layout::Optimal + } +} + /// Image layout defines how texel are placed in memory. /// Operations can be used in one or more layouts. /// User is responsible to insert layout transition commands to ensure @@ -82,7 +102,7 @@ impl ImageUsage { /// Additionally render pass can change layout of its attachments. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] -pub enum Layout { +pub enum RawLayout { /// Can be used with all device operations. /// Only presentation is not possible in this layout. /// Operations may perform slower in this layout. @@ -115,7 +135,7 @@ pub enum Layout { Present, } -impl Default for Layout { +impl Default for RawLayout { fn default() -> Self { Self::General } @@ -465,12 +485,23 @@ pub struct ImageBlit { pub dst_offsets: [Offset3d; 2], } +/// Transitions `image` between layouts. Shorthand helper for creating +/// an [`ImageMemoryBarrier`] when a layout transition with simple accesses +/// is desired. Some adavanced use cases should use [`ImageMemoryBarrier`] directly +/// instead. +/// +/// If `old_layout` is empty, the contents of the image become +/// undefined after the barrier is executed, which can result in a performance +/// boost over attempting to preserve the contents. This is particularly useful +/// for transient images where the contents are going to be immediately overwritten. +/// A good example of when to use this is when an application re-uses a presented +/// image after acquiring the next swap chain image. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub struct LayoutTransition<'a> { pub image: &'a Image, - pub old_access: AccessFlags, + pub prev_access: Access, + pub next_access: Access, pub old_layout: Option, - pub new_access: AccessFlags, pub new_layout: Layout, pub range: SubresourceRange, } @@ -478,49 +509,72 @@ pub struct LayoutTransition<'a> { impl<'a> LayoutTransition<'a> { pub fn transition_whole( image: &'a Image, - access: Range, + access: Range, layout: Range, ) -> Self { LayoutTransition { range: SubresourceRange::whole(image.info()), image, - old_access: access.start, - new_access: access.end, + prev_access: access.start, + next_access: access.end, old_layout: Some(layout.start), new_layout: layout.end, } } - pub fn initialize_whole(image: &'a Image, access: AccessFlags, layout: Layout) -> Self { + pub fn initialize_whole(image: &'a Image, next_access: Access, layout: Layout) -> Self { LayoutTransition { range: SubresourceRange::whole(image.info()), image, - old_access: AccessFlags::empty(), + prev_access: Access::None, + next_access, old_layout: None, - new_access: access, new_layout: layout, } } } -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +/// Image barriers should only be used when a queue family ownership transfer +/// or an image layout transition is required - prefer global barriers at all +/// other times. +/// +/// In general it is better to use image barriers with `Layout::Optimal` +/// than it is to use global barriers with images using the +/// `RawLayout::General` layout. +/// +/// Access types are defined in the same way as for a global memory barrier, but +/// they only affect the image subresource range identified by `image` and +/// `range`, rather than all resources. +/// +/// An image barrier defining a queue ownership transfer needs to be executed +/// twice - once by a queue in the source queue family, and then once again by a +/// queue in the destination queue family, with a semaphore guaranteeing +/// execution order between them. +/// +/// If `old_layout` is empty, the contents of the image become +/// undefined after the barrier is executed, which can result in a performance +/// boost over attempting to preserve the contents. This is particularly useful +/// for transient images where the contents are going to be immediately overwritten. +/// A good example of when to use this is when an application re-uses a presented +/// image after acquiring the next swap chain image. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ImageMemoryBarrier<'a> { pub image: &'a Image, - pub old_access: AccessFlags, + pub prev_access: Access, + pub next_access: Access, pub old_layout: Option, - pub new_access: AccessFlags, pub new_layout: Layout, pub family_transfer: Option<(u32, u32)>, pub range: SubresourceRange, } -impl<'a> From> for ImageMemoryBarrier<'a> { - fn from(value: LayoutTransition<'a>) -> Self { +impl<'a> From<&'a LayoutTransition<'a>> for ImageMemoryBarrier<'a> { + fn from(value: &'a LayoutTransition<'a>) -> Self { ImageMemoryBarrier { image: value.image, - old_access: value.old_access, + prev_access: value.prev_access, + next_access: value.next_access, old_layout: value.old_layout, - new_access: value.new_access, new_layout: value.new_layout, family_transfer: None, range: value.range, @@ -537,16 +591,15 @@ pub struct ImageSubresourceRange { /// Image region with access mask, /// specifying how it may be accessed "before". /// -/// Note that "before" is loosely defined, +/// Note that "before" is loosely defined /// as whatever previous owners do. /// Which should be translated to "earlier GPU operations" /// but this crate doesn't attempt to enforce that. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ImageSubresourceState { pub subresource: ImageSubresourceRange, - pub access: AccessFlags, - pub stages: PipelineStageFlags, - pub layout: Option, + pub prev_access: Access, + pub old_layout: Option, pub family: Ownership, } @@ -554,22 +607,19 @@ impl ImageSubresourceState { /// pub fn access<'a>( &'a mut self, - access: AccessFlags, - stages: PipelineStageFlags, - layout: Layout, + next_access: Access, + new_layout: Layout, queue: QueueId, encoder: &mut Encoder<'a>, ) -> &'a Self { match self.family { Ownership::NotOwned => encoder.image_barriers( - self.stages, - stages, encoder.scope().to_scope([ImageMemoryBarrier { image: &self.subresource.image, - old_access: self.access, - new_access: access, - old_layout: self.layout, - new_layout: layout, + prev_access: self.prev_access, + next_access, + old_layout: self.old_layout, + new_layout, family_transfer: None, range: self.subresource.range, }]), @@ -578,14 +628,12 @@ impl ImageSubresourceState { assert_eq!(family, queue.family, "Wrong queue family owns the buffer"); encoder.image_barriers( - self.stages, - stages, encoder.scope().to_scope([ImageMemoryBarrier { image: &self.subresource.image, - old_access: self.access, - new_access: access, - old_layout: self.layout, - new_layout: layout, + prev_access: self.prev_access, + next_access, + old_layout: self.old_layout, + new_layout, family_transfer: None, range: self.subresource.range, }]), @@ -598,44 +646,38 @@ impl ImageSubresourceState { ); encoder.image_barriers( - self.stages, - stages, encoder.scope().to_scope([ImageMemoryBarrier { image: &self.subresource.image, - old_access: self.access, - new_access: access, - old_layout: self.layout, - new_layout: layout, + prev_access: self.prev_access, + next_access, + old_layout: self.old_layout, + new_layout, family_transfer: Some((from, to)), range: self.subresource.range, }]), ) } } - self.stages = stages; - self.access = access; - self.layout = Some(layout); + self.prev_access = next_access; + self.old_layout = Some(new_layout); self } /// pub fn overwrite<'a>( &'a mut self, - access: AccessFlags, - stages: PipelineStageFlags, - layout: Layout, + next_access: Access, + new_layout: Layout, queue: QueueId, encoder: &mut Encoder<'a>, ) -> &'a ImageSubresourceRange { encoder.image_barriers( - self.stages, - stages, encoder.scope().to_scope([ImageMemoryBarrier { image: &self.subresource.image, - old_access: AccessFlags::empty(), - new_access: access, + prev_access: Access::None, + next_access, old_layout: None, - new_layout: layout, + new_layout, family_transfer: None, range: self.subresource.range, }]), @@ -643,9 +685,8 @@ impl ImageSubresourceState { self.family = Ownership::Owned { family: queue.family, }; - self.stages = stages; - self.access = access; - self.layout = Some(layout); + self.prev_access = next_access; + self.old_layout = Some(new_layout); &self.subresource } } diff --git a/src/memory.rs b/src/memory.rs index eedcbb3..188deb7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,4 +1,4 @@ -use crate::access::AccessFlags; +use crate::access::Access; bitflags::bitflags! { /// Memory usage type. @@ -25,8 +25,15 @@ bitflags::bitflags! { } } +/// Global barriers define a set of accesses on multiple resources at once. +/// If a buffer or image doesn't require a queue ownership transfer, or an image +/// doesn't require a layout transition (e.g. you're using one of the +/// `ImageLayout::General*` layouts) then a global barrier should be preferred. +/// +/// Simply define the previous and next access types of resources affected. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct MemoryBarrier { - pub src: AccessFlags, - pub dst: AccessFlags, +pub struct GlobalMemoryBarrier<'a> { + pub prev_accesses: &'a [Access], + pub next_accesses: &'a [Access], + } diff --git a/src/render_pass.rs b/src/render_pass.rs index dbab8c2..94904c3 100644 --- a/src/render_pass.rs +++ b/src/render_pass.rs @@ -3,7 +3,7 @@ use crate::{ encode::{Encoder, RenderPassEncoder}, format::Format, framebuffer::FramebufferError, - image::{Layout, Samples}, + image::{RawLayout, Samples}, stage::PipelineStageFlags, Device, OutOfMemory, }; @@ -58,13 +58,13 @@ pub struct AttachmentInfo { feature = "serde-1", serde(skip_serializing_if = "Option::is_none", default) )] - pub initial_layout: Option, + pub initial_layout: Option, #[cfg_attr( feature = "serde-1", serde(skip_serializing_if = "is_default", default) )] - pub final_layout: Layout, + pub final_layout: RawLayout, } /// Specifies how render pass treats attachment content at the beginning. @@ -128,7 +128,7 @@ pub struct Subpass { feature = "serde-1", serde(skip_serializing_if = "Vec::is_empty", default) )] - pub colors: Vec<(u32, Layout)>, + pub colors: Vec<(u32, RawLayout)>, /// Index of an attachment that is used as depth attachment in this /// subpass. @@ -136,7 +136,7 @@ pub struct Subpass { feature = "serde-1", serde(skip_serializing_if = "Option::is_none", default) )] - pub depth: Option<(u32, Layout)>, + pub depth: Option<(u32, RawLayout)>, } /// Defines memory dependency between two subpasses diff --git a/src/view.rs b/src/view.rs index d982367..9d2671f 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,11 +1,10 @@ pub use crate::backend::ImageView; use crate::{ - access::AccessFlags, + access::Access, backend::Device, encode::Encoder, image::{Image, ImageExtent, ImageMemoryBarrier, Layout, SubresourceRange}, queue::{Ownership, QueueId}, - stage::PipelineStageFlags, OutOfMemory, }; @@ -82,18 +81,17 @@ impl MakeImageView for Image { } /// Image region with access mask, -/// specifying how it may be accessed "before". +/// specifying how it was accessed "before". /// -/// Note that "before" is loosely defined, -/// as whatever previous owners do. -/// Which should be translated to "earlier GPU operations" +/// Note that "before" is loosely defined +/// as whatever previous owners did with it, +/// i.e. "earlier GPU operations which used this resource," /// but this crate doesn't attempt to enforce that. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ImageViewState { pub view: ImageView, - pub access: AccessFlags, - pub stages: PipelineStageFlags, - pub layout: Option, + pub prev_access: Access, + pub old_layout: Option, pub family: Ownership, } @@ -101,22 +99,19 @@ impl ImageViewState { /// pub fn access<'a>( &'a mut self, - access: AccessFlags, - stages: PipelineStageFlags, - layout: Layout, + next_access: Access, + new_layout: Layout, queue: QueueId, encoder: &mut Encoder<'a>, ) -> &'a ImageView { match self.family { Ownership::NotOwned => encoder.image_barriers( - self.stages, - stages, encoder.scope().to_scope([ImageMemoryBarrier { image: &self.view.info().image, - old_access: self.access, - new_access: access, - old_layout: self.layout, - new_layout: layout, + prev_access: self.prev_access, + next_access, + old_layout: self.old_layout, + new_layout, family_transfer: None, range: self.view.info().range, }]), @@ -125,14 +120,12 @@ impl ImageViewState { assert_eq!(family, queue.family, "Wrong queue family owns the buffer"); encoder.image_barriers( - self.stages, - stages, encoder.scope().to_scope([ImageMemoryBarrier { image: &self.view.info().image, - old_access: self.access, - new_access: access, - old_layout: self.layout, - new_layout: layout, + prev_access: self.prev_access, + next_access, + old_layout: self.old_layout, + new_layout, family_transfer: None, range: self.view.info().range, }]), @@ -145,14 +138,12 @@ impl ImageViewState { ); encoder.image_barriers( - self.stages, - stages, encoder.scope().to_scope([ImageMemoryBarrier { image: &self.view.info().image, - old_access: self.access, - new_access: access, - old_layout: self.layout, - new_layout: layout, + prev_access: self.prev_access, + next_access, + old_layout: self.old_layout, + new_layout, family_transfer: Some((from, to)), range: self.view.info().range, }]), @@ -162,30 +153,26 @@ impl ImageViewState { self.family = Ownership::Owned { family: queue.family, }; - self.stages = stages; - self.access = access; - self.layout = Some(layout); + self.prev_access = next_access; + self.old_layout = Some(new_layout); &self.view } /// pub fn overwrite<'a>( &'a mut self, - access: AccessFlags, - stages: PipelineStageFlags, - layout: Layout, + next_access: Access, + new_layout: Layout, queue: QueueId, encoder: &mut Encoder<'a>, ) -> &'a ImageView { encoder.image_barriers( - self.stages, - stages, encoder.scope().to_scope([ImageMemoryBarrier { image: &self.view.info().image, - old_access: AccessFlags::empty(), - new_access: access, + prev_access: Access::None, + next_access, old_layout: None, - new_layout: layout, + new_layout, family_transfer: None, range: self.view.info().range, }]), @@ -193,9 +180,8 @@ impl ImageViewState { self.family = Ownership::Owned { family: queue.family, }; - self.stages = stages; - self.access = access; - self.layout = Some(layout); + self.prev_access = next_access; + self.old_layout = Some(new_layout); &self.view } } From e051136f9310c2755c07e6ef57bfd9ff7b0ea611 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Sat, 18 Sep 2021 22:21:41 +0200 Subject: [PATCH 2/2] naming and docs change for image layout types + format --- demo/src/main.rs | 2 +- proc/src/pass/instance.rs | 16 +- src/access.rs | 198 +++++++++++------------ src/backend/vulkan/access.rs | 12 +- src/backend/vulkan/convert.rs | 22 +-- src/backend/vulkan/sync.rs | 292 +++++++++++++++++----------------- src/descriptor/image.rs | 10 +- src/descriptor/mod.rs | 6 +- src/encode.rs | 22 +-- src/image.rs | 114 +++++++------ src/render_pass.rs | 10 +- src/view.rs | 8 +- 12 files changed, 353 insertions(+), 359 deletions(-) diff --git a/demo/src/main.rs b/demo/src/main.rs index e4561d8..31f3d0c 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -16,7 +16,7 @@ pub struct Pipeline { #[sierra::pass] #[subpass(color = target)] pub struct Main { - #[attachment(clear(const sierra::ClearColor(0.3, 0.1, 0.8, 1.0)), store(const sierra::RawLayout::Present))] + #[attachment(clear(const sierra::ClearColor(0.3, 0.1, 0.8, 1.0)), store(const sierra::Layout::Present))] target: sierra::Image, } diff --git a/proc/src/pass/instance.rs b/proc/src/pass/instance.rs index 05b8acd..d275e54 100644 --- a/proc/src/pass/instance.rs +++ b/proc/src/pass/instance.rs @@ -18,7 +18,7 @@ pub(super) fn generate(input: &Input) -> TokenStream { let initial_layout = initial_layout(&a.load_op); let check_final_layout = final_layout(&a.store_op).map(|final_layout| { - quote::quote!(else if a.final_layout != ::sierra::RawLayout::from(#final_layout) { + quote::quote!(else if a.final_layout != ::sierra::Layout::from(#final_layout) { tracing::debug!("Final layout is incompatible. Old {:?}, new {:?}", a.final_layout, #final_layout); render_pass_compatible = false; } ) @@ -34,8 +34,8 @@ pub(super) fn generate(input: &Input) -> TokenStream { tracing::debug!("Samples count is incompatible. Old {:?}, new {:?}", a.samples, ::sierra::Attachment::samples(&input.#member)); render_pass_compatible = false; } - } else if a.initial_layout != ::std::option::Option::map(#initial_layout, ::sierra::RawLayout::from) { - tracing::debug!("Initial layout is incompatible. Old {:?}, new {:?}", a.initial_layout, ::std::option::Option::map(#initial_layout, ::sierra::RawLayout::from)); + } else if a.initial_layout != ::std::option::Option::map(#initial_layout, ::sierra::Layout::from) { + tracing::debug!("Initial layout is incompatible. Old {:?}, new {:?}", a.initial_layout, ::std::option::Option::map(#initial_layout, ::sierra::Layout::from)); render_pass_compatible = false; } #check_final_layout ) @@ -65,10 +65,10 @@ pub(super) fn generate(input: &Input) -> TokenStream { .iter() .filter_map(|s| { if s.colors.iter().any(|&c| c == index) { - Some(quote::quote!(::sierra::RawLayout::ColorAttachmentOptimal)) + Some(quote::quote!(::sierra::Layout::ColorAttachmentOptimal)) } else if s.depth == Some(index) { Some(quote::quote!( - ::sierra::RawLayout::DepthStencilAttachmentOptimal + ::sierra::Layout::DepthStencilAttachmentOptimal )) } else { None @@ -76,7 +76,7 @@ pub(super) fn generate(input: &Input) -> TokenStream { }) .rev() .next() - .unwrap_or_else(|| quote::quote!(::sierra::RawLayout::General)) + .unwrap_or_else(|| quote::quote!(::sierra::Layout::General)) } Some(layout) => layout, }; @@ -107,7 +107,7 @@ pub(super) fn generate(input: &Input) -> TokenStream { .colors .iter() .map( - |&c| quote::quote!(colors.push((#c, ::sierra::RawLayout::ColorAttachmentOptimal));), + |&c| quote::quote!(colors.push((#c, ::sierra::Layout::ColorAttachmentOptimal));), ) .collect::(); @@ -122,7 +122,7 @@ pub(super) fn generate(input: &Input) -> TokenStream { #push_colors colors }, - depth: Some((#depth, ::sierra::RawLayout::DepthStencilAttachmentOptimal)), + depth: Some((#depth, ::sierra::Layout::DepthStencilAttachmentOptimal)), }); ) } diff --git a/src/access.rs b/src/access.rs index 7ab610d..95573fe 100644 --- a/src/access.rs +++ b/src/access.rs @@ -4,145 +4,145 @@ // https://github.com/gwihlidal/vk-sync-rs/blob/master/LICENSE-MIT /// Defines all potential resource usages -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash,)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Access { - /// No access. Useful primarily for initialization - None, + /// No access. Useful primarily for initialization + None, - /// Read as an indirect buffer for drawing or dispatch - IndirectBuffer, + /// Read as an indirect buffer for drawing or dispatch + IndirectBuffer, - /// Read as an index buffer for drawing - IndexBuffer, + /// Read as an index buffer for drawing + IndexBuffer, - /// Read as a vertex buffer for drawing - VertexBuffer, + /// Read as a vertex buffer for drawing + VertexBuffer, - /// Read as a uniform buffer in a vertex shader - VertexShaderReadUniformBuffer, + /// Read as a uniform buffer in a vertex shader + VertexShaderReadUniformBuffer, - /// Read as a sampled image/uniform texel buffer in a vertex shader - VertexShaderReadSampledImageOrUniformTexelBuffer, + /// Read as a sampled image/uniform texel buffer in a vertex shader + VertexShaderReadSampledImageOrUniformTexelBuffer, - /// Read as any other resource in a vertex shader - VertexShaderReadOther, + /// Read as any other resource in a vertex shader + VertexShaderReadOther, - /// Read as a uniform buffer in a fragment shader - FragmentShaderReadUniformBuffer, + /// Read as a uniform buffer in a fragment shader + FragmentShaderReadUniformBuffer, - /// Read as a sampled image/uniform texel buffer in a fragment shader - FragmentShaderReadSampledImageOrUniformTexelBuffer, + /// Read as a sampled image/uniform texel buffer in a fragment shader + FragmentShaderReadSampledImageOrUniformTexelBuffer, - /// Read as an input attachment with a color format in a fragment shader - FragmentShaderReadColorInputAttachment, + /// Read as an input attachment with a color format in a fragment shader + FragmentShaderReadColorInputAttachment, - /// Read as an input attachment with a depth/stencil format in a fragment shader - FragmentShaderReadDepthStencilInputAttachment, + /// Read as an input attachment with a depth/stencil format in a fragment shader + FragmentShaderReadDepthStencilInputAttachment, - /// Read as any other resource in a fragment shader - FragmentShaderReadOther, + /// Read as any other resource in a fragment shader + FragmentShaderReadOther, - /// Read by blending/logic operations or subpass load operations - ColorAttachmentRead, + /// Read by blending/logic operations or subpass load operations + ColorAttachmentRead, - /// Read by depth/stencil tests or subpass load operations - DepthStencilAttachmentRead, + /// Read by depth/stencil tests or subpass load operations + DepthStencilAttachmentRead, - /// Read as a uniform buffer in a compute shader - ComputeShaderReadUniformBuffer, + /// Read as a uniform buffer in a compute shader + ComputeShaderReadUniformBuffer, - /// Read as a sampled image/uniform texel buffer in a compute shader - ComputeShaderReadSampledImageOrUniformTexelBuffer, + /// Read as a sampled image/uniform texel buffer in a compute shader + ComputeShaderReadSampledImageOrUniformTexelBuffer, - /// Read as any other resource in a compute shader - ComputeShaderReadOther, + /// Read as any other resource in a compute shader + ComputeShaderReadOther, - /// Read as a uniform buffer in any shader - AnyShaderReadUniformBuffer, + /// Read as a uniform buffer in any shader + AnyShaderReadUniformBuffer, - /// Read as a uniform buffer in any shader, or a vertex buffer - AnyShaderReadUniformBufferOrVertexBuffer, + /// Read as a uniform buffer in any shader, or a vertex buffer + AnyShaderReadUniformBufferOrVertexBuffer, - /// Read as a sampled image in any shader - AnyShaderReadSampledImageOrUniformTexelBuffer, + /// Read as a sampled image in any shader + AnyShaderReadSampledImageOrUniformTexelBuffer, - /// Read as any other resource (excluding attachments) in any shader - AnyShaderReadOther, + /// Read as any other resource (excluding attachments) in any shader + AnyShaderReadOther, - /// Read as the source of a transfer operation - TransferRead, + /// Read as the source of a transfer operation + TransferRead, - /// Read on the host - HostRead, + /// Read on the host + HostRead, - /// Read by the presentation engine (i.e. `vkQueuePresentKHR`) - Present, + /// Read by the presentation engine (i.e. `vkQueuePresentKHR`) + Present, - /// Written as any resource in a vertex shader - VertexShaderWrite, + /// Written as any resource in a vertex shader + VertexShaderWrite, - /// Written as any resource in a fragment shader - FragmentShaderWrite, + /// Written as any resource in a fragment shader + FragmentShaderWrite, - /// Written as a color attachment during rendering, or via a subpass store op - ColorAttachmentWrite, + /// Written as a color attachment during rendering, or via a subpass store op + ColorAttachmentWrite, - /// Written as a depth/stencil attachment during rendering, or via a subpass store op - DepthStencilAttachmentWrite, + /// Written as a depth/stencil attachment during rendering, or via a subpass store op + DepthStencilAttachmentWrite, - /// Written as a depth aspect of a depth/stencil attachment during rendering, whilst the - /// stencil aspect is read-only. Requires `VK_KHR_maintenance2` to be enabled. - DepthAttachmentWriteStencilReadOnly, + /// Written as a depth aspect of a depth/stencil attachment during rendering, whilst the + /// stencil aspect is read-only. Requires `VK_KHR_maintenance2` to be enabled. + DepthAttachmentWriteStencilReadOnly, - /// Written as a stencil aspect of a depth/stencil attachment during rendering, whilst the - /// depth aspect is read-only. Requires `VK_KHR_maintenance2` to be enabled. - StencilAttachmentWriteDepthReadOnly, + /// Written as a stencil aspect of a depth/stencil attachment during rendering, whilst the + /// depth aspect is read-only. Requires `VK_KHR_maintenance2` to be enabled. + StencilAttachmentWriteDepthReadOnly, - /// Written as any resource in a compute shader - ComputeShaderWrite, + /// Written as any resource in a compute shader + ComputeShaderWrite, - /// Written as any resource in any shader - AnyShaderWrite, + /// Written as any resource in any shader + AnyShaderWrite, - /// Written as the destination of a transfer operation - TransferWrite, + /// Written as the destination of a transfer operation + TransferWrite, - /// Written on the host - HostWrite, + /// Written on the host + HostWrite, - /// Read or written as a color attachment during rendering - ColorAttachmentReadWrite, + /// Read or written as a color attachment during rendering + ColorAttachmentReadWrite, - /// Covers any access - useful for debug, generally avoid for performance reasons - General, + /// Covers any access - useful for debug, generally avoid for performance reasons + General, } impl Default for Access { - fn default() -> Self { - Access::None - } + fn default() -> Self { + Access::None + } } impl Access { - pub fn is_write(self) -> bool { - match self { - Access::VertexShaderWrite => true, - Access::FragmentShaderWrite => true, - Access::ColorAttachmentWrite => true, - Access::DepthStencilAttachmentWrite => true, - Access::DepthAttachmentWriteStencilReadOnly => true, - Access::StencilAttachmentWriteDepthReadOnly => true, - Access::ComputeShaderWrite => true, - Access::AnyShaderWrite => true, - Access::TransferWrite => true, - Access::HostWrite => true, - Access::ColorAttachmentReadWrite => true, - Access::General => true, - _ => false, - } - } - - pub fn is_read_only(self) -> bool { - !self.is_write() - } + pub fn is_write(self) -> bool { + match self { + Access::VertexShaderWrite => true, + Access::FragmentShaderWrite => true, + Access::ColorAttachmentWrite => true, + Access::DepthStencilAttachmentWrite => true, + Access::DepthAttachmentWriteStencilReadOnly => true, + Access::StencilAttachmentWriteDepthReadOnly => true, + Access::ComputeShaderWrite => true, + Access::AnyShaderWrite => true, + Access::TransferWrite => true, + Access::HostWrite => true, + Access::ColorAttachmentReadWrite => true, + Access::General => true, + _ => false, + } + } + + pub fn is_read_only(self) -> bool { + !self.is_write() + } } diff --git a/src/backend/vulkan/access.rs b/src/backend/vulkan/access.rs index 14df8e4..36705ec 100644 --- a/src/backend/vulkan/access.rs +++ b/src/backend/vulkan/access.rs @@ -72,9 +72,9 @@ pub(crate) fn supported_access(stages: vk1_0::PipelineStageFlags) -> vk1_0::Acce } pub(crate) struct AccessInfo { - pub(crate) stage_mask: vk1_0::PipelineStageFlags, - pub(crate) access_mask: vk1_0::AccessFlags, - pub(crate) image_layout: vk1_0::ImageLayout, + pub(crate) stage_mask: vk1_0::PipelineStageFlags, + pub(crate) access_mask: vk1_0::AccessFlags, + pub(crate) image_layout: vk1_0::ImageLayout, } pub(crate) trait GetAccessInfo { @@ -82,7 +82,7 @@ pub(crate) trait GetAccessInfo { } impl GetAccessInfo for Access { - fn access_info(self) -> AccessInfo { + fn access_info(self) -> AccessInfo { match self { Access::None => AccessInfo { stage_mask: vk1_0::PipelineStageFlags::empty(), @@ -273,5 +273,5 @@ impl GetAccessInfo for Access { image_layout: vk1_0::ImageLayout::GENERAL, }, } - } -} \ No newline at end of file + } +} diff --git a/src/backend/vulkan/convert.rs b/src/backend/vulkan/convert.rs index c778af8..bd727f6 100644 --- a/src/backend/vulkan/convert.rs +++ b/src/backend/vulkan/convert.rs @@ -4,7 +4,7 @@ use crate::{ CompareOp, ComponentMask, CompositeAlphaFlags, Culling, DescriptorBindingFlags, DescriptorSetLayoutFlags, DescriptorType, DeviceAddress, Extent2d, Extent3d, Filter, Format, FrontFace, GeometryFlags, ImageBlit, ImageCopy, ImageExtent, ImageUsage, ImageViewKind, - IndexType, RawLayout, LoadOp, LogicOp, MemoryUsage, MipmapMode, Offset2d, Offset3d, OutOfMemory, + IndexType, Layout, LoadOp, LogicOp, MemoryUsage, MipmapMode, Offset2d, Offset3d, OutOfMemory, PipelineStageFlags, PolygonMode, PresentMode, PrimitiveTopology, QueueCapabilityFlags, Rect2d, SamplerAddressMode, Samples, ShaderStage, ShaderStageFlags, StencilOp, StoreOp, Subresource, SubresourceLayers, SubresourceRange, SurfaceTransformFlags, VertexInputRate, Viewport, @@ -990,26 +990,26 @@ impl ToErupt for ComponentMask { } } -impl ToErupt for RawLayout { +impl ToErupt for Layout { fn to_erupt(self) -> vk1_0::ImageLayout { match self { - RawLayout::General => vk1_0::ImageLayout::GENERAL, - RawLayout::ColorAttachmentOptimal => vk1_0::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, - RawLayout::DepthStencilAttachmentOptimal => { + Layout::General => vk1_0::ImageLayout::GENERAL, + Layout::ColorAttachmentOptimal => vk1_0::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, + Layout::DepthStencilAttachmentOptimal => { vk1_0::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL } - RawLayout::DepthStencilReadOnlyOptimal => { + Layout::DepthStencilReadOnlyOptimal => { vk1_0::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL } - RawLayout::ShaderReadOnlyOptimal => vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, - RawLayout::TransferSrcOptimal => vk1_0::ImageLayout::TRANSFER_SRC_OPTIMAL, - RawLayout::TransferDstOptimal => vk1_0::ImageLayout::TRANSFER_DST_OPTIMAL, - RawLayout::Present => vk1_0::ImageLayout::PRESENT_SRC_KHR, + Layout::ShaderReadOnlyOptimal => vk1_0::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + Layout::TransferSrcOptimal => vk1_0::ImageLayout::TRANSFER_SRC_OPTIMAL, + Layout::TransferDstOptimal => vk1_0::ImageLayout::TRANSFER_DST_OPTIMAL, + Layout::Present => vk1_0::ImageLayout::PRESENT_SRC_KHR, } } } -impl ToErupt for Option { +impl ToErupt for Option { fn to_erupt(self) -> vk1_0::ImageLayout { match self { None => vk1_0::ImageLayout::UNDEFINED, diff --git a/src/backend/vulkan/sync.rs b/src/backend/vulkan/sync.rs index 270ae83..1c6ce0f 100644 --- a/src/backend/vulkan/sync.rs +++ b/src/backend/vulkan/sync.rs @@ -3,8 +3,8 @@ // // https://github.com/gwihlidal/vk-sync-rs/blob/master/LICENSE-MIT -use crate::{BufferMemoryBarrier, GlobalMemoryBarrier, ImageMemoryBarrier, Layout}; use super::{access::GetAccessInfo, convert::ToErupt}; +use crate::{BufferMemoryBarrier, GlobalMemoryBarrier, ImageMemoryBarrier, SyncLayout}; use erupt::vk1_0; @@ -12,191 +12,189 @@ use erupt::vk1_0; /// destination pipeline stages, and a memory barrier, that can be used with /// Vulkan synchronization methods. pub fn get_global_barrier<'a>( - barrier: &GlobalMemoryBarrier, + barrier: &GlobalMemoryBarrier, ) -> ( - vk1_0::PipelineStageFlags, - vk1_0::PipelineStageFlags, - vk1_0::MemoryBarrierBuilder<'a>, + vk1_0::PipelineStageFlags, + vk1_0::PipelineStageFlags, + vk1_0::MemoryBarrierBuilder<'a>, ) { - let mut src_stages = vk1_0::PipelineStageFlags::empty(); - let mut dst_stages = vk1_0::PipelineStageFlags::empty(); + let mut src_stages = vk1_0::PipelineStageFlags::empty(); + let mut dst_stages = vk1_0::PipelineStageFlags::empty(); - let mut src_access_mask = vk1_0::AccessFlags::empty(); - let mut dst_access_mask = vk1_0::AccessFlags::empty(); + let mut src_access_mask = vk1_0::AccessFlags::empty(); + let mut dst_access_mask = vk1_0::AccessFlags::empty(); - for prev_access in barrier.prev_accesses { - let previous_info = prev_access.access_info(); + for prev_access in barrier.prev_accesses { + let previous_info = prev_access.access_info(); - src_stages |= previous_info.stage_mask; + src_stages |= previous_info.stage_mask; - // Add appropriate availability operations - for writes only. - if prev_access.is_write() { - src_access_mask |= previous_info.access_mask; - } - } + // Add appropriate availability operations - for writes only. + if prev_access.is_write() { + src_access_mask |= previous_info.access_mask; + } + } - for next_access in barrier.next_accesses { - let next_info = next_access.access_info(); + for next_access in barrier.next_accesses { + let next_info = next_access.access_info(); - dst_stages |= next_info.stage_mask; + dst_stages |= next_info.stage_mask; - // Add visibility operations as necessary. - // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), - // so the dst access mask can be safely zeroed as these don't need visibility. - if src_access_mask != vk1_0::AccessFlags::empty() { - dst_access_mask |= next_info.access_mask; - } - } + // Add visibility operations as necessary. + // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), + // so the dst access mask can be safely zeroed as these don't need visibility. + if src_access_mask != vk1_0::AccessFlags::empty() { + dst_access_mask |= next_info.access_mask; + } + } - // Ensure that the stage masks are valid if no stages were determined - if src_stages == vk1_0::PipelineStageFlags::empty() { - src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; - } + // Ensure that the stage masks are valid if no stages were determined + if src_stages == vk1_0::PipelineStageFlags::empty() { + src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; + } - if dst_stages == vk1_0::PipelineStageFlags::empty() { - dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; - } + if dst_stages == vk1_0::PipelineStageFlags::empty() { + dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; + } - let memory_barrier = vk1_0::MemoryBarrierBuilder::new() - .src_access_mask(src_access_mask) - .dst_access_mask(dst_access_mask); + let memory_barrier = vk1_0::MemoryBarrierBuilder::new() + .src_access_mask(src_access_mask) + .dst_access_mask(dst_access_mask); - (src_stages, dst_stages, memory_barrier) + (src_stages, dst_stages, memory_barrier) } /// Mapping function that translates a buffer barrier into a set of source and /// destination pipeline stages, and a buffer memory barrier, that can be used /// with Vulkan synchronization methods. pub fn get_buffer_memory_barrier<'a>( - barrier: &BufferMemoryBarrier, + barrier: &BufferMemoryBarrier, ) -> ( - vk1_0::PipelineStageFlags, - vk1_0::PipelineStageFlags, - vk1_0::BufferMemoryBarrierBuilder<'a>, + vk1_0::PipelineStageFlags, + vk1_0::PipelineStageFlags, + vk1_0::BufferMemoryBarrierBuilder<'a>, ) { - let mut src_stages = vk1_0::PipelineStageFlags::empty(); - let mut dst_stages = vk1_0::PipelineStageFlags::empty(); + let mut src_stages = vk1_0::PipelineStageFlags::empty(); + let mut dst_stages = vk1_0::PipelineStageFlags::empty(); - let mut src_access_mask = vk1_0::AccessFlags::empty(); - let mut dst_access_mask = vk1_0::AccessFlags::empty(); + let mut src_access_mask = vk1_0::AccessFlags::empty(); + let mut dst_access_mask = vk1_0::AccessFlags::empty(); - let (src_queue_family_index, dst_queue_family_index) = barrier - .family_transfer - .unwrap_or((vk1_0::QUEUE_FAMILY_IGNORED, vk1_0::QUEUE_FAMILY_IGNORED)); + let (src_queue_family_index, dst_queue_family_index) = barrier + .family_transfer + .unwrap_or((vk1_0::QUEUE_FAMILY_IGNORED, vk1_0::QUEUE_FAMILY_IGNORED)); + let previous_info = barrier.prev_access.access_info(); - let previous_info = barrier.prev_access.access_info(); + src_stages |= previous_info.stage_mask; - src_stages |= previous_info.stage_mask; + // Add appropriate availability operations - for writes only. + if barrier.prev_access.is_write() { + src_access_mask |= previous_info.access_mask; + } - // Add appropriate availability operations - for writes only. - if barrier.prev_access.is_write() { - src_access_mask |= previous_info.access_mask; - } + let next_info = barrier.next_access.access_info(); - let next_info = barrier.next_access.access_info(); + dst_stages |= next_info.stage_mask; - dst_stages |= next_info.stage_mask; + // Add visibility operations as necessary. + // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), + // so the dst access mask can be safely zeroed as these don't need visibility. + if src_access_mask != vk1_0::AccessFlags::empty() { + dst_access_mask |= next_info.access_mask; + } - // Add visibility operations as necessary. - // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), - // so the dst access mask can be safely zeroed as these don't need visibility. - if src_access_mask != vk1_0::AccessFlags::empty() { - dst_access_mask |= next_info.access_mask; - } + // Ensure that the stage masks are valid if no stages were determined + if src_stages == vk1_0::PipelineStageFlags::empty() { + src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; + } - // Ensure that the stage masks are valid if no stages were determined - if src_stages == vk1_0::PipelineStageFlags::empty() { - src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; - } + if dst_stages == vk1_0::PipelineStageFlags::empty() { + dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; + } - if dst_stages == vk1_0::PipelineStageFlags::empty() { - dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; - } + let buffer_barrier = vk1_0::BufferMemoryBarrierBuilder::new() + .src_queue_family_index(src_queue_family_index) + .dst_queue_family_index(dst_queue_family_index) + .buffer(barrier.buffer.handle()) + .offset(barrier.offset as u64) + .size(barrier.size as u64); - let buffer_barrier = vk1_0::BufferMemoryBarrierBuilder::new() - .src_queue_family_index(src_queue_family_index) - .dst_queue_family_index(dst_queue_family_index) - .buffer(barrier.buffer.handle()) - .offset(barrier.offset as u64) - .size(barrier.size as u64); - - (src_stages, dst_stages, buffer_barrier) + (src_stages, dst_stages, buffer_barrier) } /// Mapping function that translates an image barrier into a set of source and /// destination pipeline stages, and an image memory barrier, that can be used /// with Vulkan synchronization methods. pub fn get_image_memory_barrier<'a>( - barrier: &ImageMemoryBarrier, + barrier: &ImageMemoryBarrier, ) -> ( - vk1_0::PipelineStageFlags, - vk1_0::PipelineStageFlags, - vk1_0::ImageMemoryBarrierBuilder<'a>, + vk1_0::PipelineStageFlags, + vk1_0::PipelineStageFlags, + vk1_0::ImageMemoryBarrierBuilder<'a>, ) { - let mut src_stages = vk1_0::PipelineStageFlags::empty(); - let mut dst_stages = vk1_0::PipelineStageFlags::empty(); - - let mut src_access_mask = vk1_0::AccessFlags::empty(); - let mut dst_access_mask = vk1_0::AccessFlags::empty(); - - let (src_queue_family_index, dst_queue_family_index) = barrier - .family_transfer - .unwrap_or((vk1_0::QUEUE_FAMILY_IGNORED, vk1_0::QUEUE_FAMILY_IGNORED)); - - let previous_info = barrier.prev_access.access_info(); - - src_stages |= previous_info.stage_mask; - - // Add appropriate availability operations - for writes only. - if barrier.prev_access.is_write() { - src_access_mask |= previous_info.access_mask; - } - - let old_layout = if let Some(barrier_old_layout) = barrier.old_layout { - match barrier_old_layout { - Layout::Optimal => previous_info.image_layout, - Layout::Manual(layout) => layout.to_erupt(), - } - } else { - vk1_0::ImageLayout::UNDEFINED - }; - - let next_info = barrier.next_access.access_info(); - - dst_stages |= next_info.stage_mask; - - // Add visibility operations as necessary. - // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), - // so the dst access mask can be safely zeroed as these don't need visibility. - if src_access_mask != vk1_0::AccessFlags::empty() { - dst_access_mask |= next_info.access_mask; - } - - let new_layout = match barrier.new_layout { - Layout::Optimal => next_info.image_layout, - Layout::Manual(layout) => layout.to_erupt(), - }; - - // Ensure that the stage masks are valid if no stages were determined - if src_stages == vk1_0::PipelineStageFlags::empty() { - src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; - } - - if dst_stages == vk1_0::PipelineStageFlags::empty() { - dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; - } - - let image_barrier = vk1_0::ImageMemoryBarrierBuilder::new() - .src_queue_family_index(src_queue_family_index) - .dst_queue_family_index(dst_queue_family_index) - .image(barrier.image.handle()) - .subresource_range(barrier.range.to_erupt()) - .src_access_mask(src_access_mask) - .dst_access_mask(dst_access_mask) - .old_layout(old_layout) - .new_layout(new_layout); - - - (src_stages, dst_stages, image_barrier) + let mut src_stages = vk1_0::PipelineStageFlags::empty(); + let mut dst_stages = vk1_0::PipelineStageFlags::empty(); + + let mut src_access_mask = vk1_0::AccessFlags::empty(); + let mut dst_access_mask = vk1_0::AccessFlags::empty(); + + let (src_queue_family_index, dst_queue_family_index) = barrier + .family_transfer + .unwrap_or((vk1_0::QUEUE_FAMILY_IGNORED, vk1_0::QUEUE_FAMILY_IGNORED)); + + let previous_info = barrier.prev_access.access_info(); + + src_stages |= previous_info.stage_mask; + + // Add appropriate availability operations - for writes only. + if barrier.prev_access.is_write() { + src_access_mask |= previous_info.access_mask; + } + + let old_layout = if let Some(barrier_old_layout) = barrier.old_layout { + match barrier_old_layout { + SyncLayout::Optimal => previous_info.image_layout, + SyncLayout::Manual(layout) => layout.to_erupt(), + } + } else { + vk1_0::ImageLayout::UNDEFINED + }; + + let next_info = barrier.next_access.access_info(); + + dst_stages |= next_info.stage_mask; + + // Add visibility operations as necessary. + // If the src access mask, this is a WAR hazard (or for some reason a "RAR"), + // so the dst access mask can be safely zeroed as these don't need visibility. + if src_access_mask != vk1_0::AccessFlags::empty() { + dst_access_mask |= next_info.access_mask; + } + + let new_layout = match barrier.new_layout { + SyncLayout::Optimal => next_info.image_layout, + SyncLayout::Manual(layout) => layout.to_erupt(), + }; + + // Ensure that the stage masks are valid if no stages were determined + if src_stages == vk1_0::PipelineStageFlags::empty() { + src_stages = vk1_0::PipelineStageFlags::TOP_OF_PIPE; + } + + if dst_stages == vk1_0::PipelineStageFlags::empty() { + dst_stages = vk1_0::PipelineStageFlags::BOTTOM_OF_PIPE; + } + + let image_barrier = vk1_0::ImageMemoryBarrierBuilder::new() + .src_queue_family_index(src_queue_family_index) + .dst_queue_family_index(dst_queue_family_index) + .image(barrier.image.handle()) + .subresource_range(barrier.range.to_erupt()) + .src_access_mask(src_access_mask) + .dst_access_mask(dst_access_mask) + .old_layout(old_layout) + .new_layout(new_layout); + + (src_stages, dst_stages, image_barrier) } diff --git a/src/descriptor/image.rs b/src/descriptor/image.rs index babae7d..dc366d1 100644 --- a/src/descriptor/image.rs +++ b/src/descriptor/image.rs @@ -1,7 +1,7 @@ use { super::{DescriptorBindingFlags, ImageViewDescriptor, TypedDescriptorBinding}, crate::{ - image::{Image, RawLayout}, + image::{Image, Layout}, view::{ImageView, ImageViewInfo}, Device, OutOfMemory, }, @@ -20,7 +20,7 @@ impl TypedDescriptorBinding for Image { let view = device.create_image_view(ImageViewInfo::new(self.clone()))?; Ok([ImageViewDescriptor { view, - layout: RawLayout::ShaderReadOnlyOptimal, + layout: Layout::ShaderReadOnlyOptimal, }]) } } @@ -37,7 +37,7 @@ impl TypedDescriptorBinding for ImageView { fn get_descriptors(&self, _device: &Device) -> Result<[ImageViewDescriptor; 1], OutOfMemory> { Ok([ImageViewDescriptor { view: self.clone(), - layout: RawLayout::ShaderReadOnlyOptimal, + layout: Layout::ShaderReadOnlyOptimal, }]) } } @@ -74,7 +74,7 @@ impl TypedDescriptorBinding for [ImageView; N] { unsafe { result.push_unchecked(ImageViewDescriptor { view: me.clone(), - layout: RawLayout::ShaderReadOnlyOptimal, + layout: Layout::ShaderReadOnlyOptimal, }); } } @@ -120,7 +120,7 @@ impl TypedDescriptorBinding for arrayvec::ArrayVec unsafe { result.push_unchecked(ImageViewDescriptor { view: me.clone(), - layout: RawLayout::ShaderReadOnlyOptimal, + layout: Layout::ShaderReadOnlyOptimal, }); } } diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index bac929e..f1cb9eb 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -15,7 +15,7 @@ use crate::{ buffer::BufferRange, encode::Encoder, // image::Image, - image::RawLayout, + image::Layout, // image::{ImageExtent, SubresourceRange}, sampler::Sampler, view::ImageView, @@ -74,7 +74,7 @@ pub struct ImageViewDescriptor { pub view: ImageView, /// View's layout when descriptor is accessed. - pub layout: RawLayout, + pub layout: Layout, } /// Image view, layout and sampler.\ @@ -86,7 +86,7 @@ pub struct CombinedImageSampler { pub view: ImageView, /// View's layout when descriptor is accessed. - pub layout: RawLayout, + pub layout: Layout, /// Descriptor sampler resource. pub sampler: Sampler, diff --git a/src/encode.rs b/src/encode.rs index d39ee6c..8d7f31a 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -7,7 +7,7 @@ use { buffer::{Buffer, BufferMemoryBarrier}, descriptor::{DescriptorSet, UpdatedPipelineDescriptors}, framebuffer::{Framebuffer, FramebufferError}, - image::{Image, ImageBlit, ImageMemoryBarrier, RawLayout, SubresourceLayers}, + image::{Image, ImageBlit, ImageMemoryBarrier, Layout, SubresourceLayers}, memory::GlobalMemoryBarrier, pipeline::{ ComputePipeline, DynamicGraphicsPipeline, GraphicsPipeline, PipelineLayout, @@ -148,24 +148,24 @@ pub enum Command<'a> { CopyImage { src_image: &'a Image, - src_layout: RawLayout, + src_layout: Layout, dst_image: &'a Image, - dst_layout: RawLayout, + dst_layout: Layout, regions: &'a [ImageCopy], }, CopyBufferImage { src_buffer: &'a Buffer, dst_image: &'a Image, - dst_layout: RawLayout, + dst_layout: Layout, regions: &'a [BufferImageCopy], }, BlitImage { src_image: &'a Image, - src_layout: RawLayout, + src_layout: Layout, dst_image: &'a Image, - dst_layout: RawLayout, + dst_layout: Layout, regions: &'a [ImageBlit], filter: Filter, }, @@ -607,9 +607,9 @@ impl<'a> Encoder<'a> { pub fn copy_image( &mut self, src_image: &'a Image, - src_layout: RawLayout, + src_layout: Layout, dst_image: &'a Image, - dst_layout: RawLayout, + dst_layout: Layout, regions: &'a [ImageCopy], ) { self.inner.commands.push( @@ -628,7 +628,7 @@ impl<'a> Encoder<'a> { &mut self, src_buffer: &'a Buffer, dst_image: &'a Image, - dst_layout: RawLayout, + dst_layout: Layout, regions: &'a [BufferImageCopy], ) { self.inner.commands.push( @@ -645,9 +645,9 @@ impl<'a> Encoder<'a> { pub fn blit_image( &mut self, src_image: &'a Image, - src_layout: RawLayout, + src_layout: Layout, dst_image: &'a Image, - dst_layout: RawLayout, + dst_layout: Layout, regions: &'a [ImageBlit], filter: Filter, ) { diff --git a/src/image.rs b/src/image.rs index fe3ea58..4a7100a 100644 --- a/src/image.rs +++ b/src/image.rs @@ -74,24 +74,26 @@ impl ImageUsage { } } -/// Defines a handful of layout options for images. -/// Rather than a list of all possible image layouts, this reduced list is -/// correlated with the access types to map to the correct actual layouts. +/// When performing a synchronization operation which may transition +/// an image's layout, chooses whether to automatically compute the optimal +/// layout based on specified [`Access`]es or whether to transition manually +/// to a specified [`Layout`]. +/// /// `Optimal` is usually preferred. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] -pub enum Layout { - /// Choose the most optimal layout for each usage. Performs layout transitions as appropriate for the access. - Optimal, +pub enum SyncLayout { + /// Choose the most optimal layout for each usage. Performs layout transitions as appropriate for the access. + Optimal, /// Manually choose a RawLayout regardless of usage or access provided. - Manual(RawLayout), + Manual(Layout), } -impl Default for Layout { - fn default() -> Self { - Layout::Optimal - } +impl Default for SyncLayout { + fn default() -> Self { + SyncLayout::Optimal + } } /// Image layout defines how texel are placed in memory. @@ -102,7 +104,7 @@ impl Default for Layout { /// Additionally render pass can change layout of its attachments. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] -pub enum RawLayout { +pub enum Layout { /// Can be used with all device operations. /// Only presentation is not possible in this layout. /// Operations may perform slower in this layout. @@ -135,7 +137,7 @@ pub enum RawLayout { Present, } -impl Default for RawLayout { +impl Default for Layout { fn default() -> Self { Self::General } @@ -501,8 +503,8 @@ pub struct LayoutTransition<'a> { pub image: &'a Image, pub prev_access: Access, pub next_access: Access, - pub old_layout: Option, - pub new_layout: Layout, + pub old_layout: Option, + pub new_layout: SyncLayout, pub range: SubresourceRange, } @@ -510,7 +512,7 @@ impl<'a> LayoutTransition<'a> { pub fn transition_whole( image: &'a Image, access: Range, - layout: Range, + layout: Range, ) -> Self { LayoutTransition { range: SubresourceRange::whole(image.info()), @@ -522,7 +524,7 @@ impl<'a> LayoutTransition<'a> { } } - pub fn initialize_whole(image: &'a Image, next_access: Access, layout: Layout) -> Self { + pub fn initialize_whole(image: &'a Image, next_access: Access, layout: SyncLayout) -> Self { LayoutTransition { range: SubresourceRange::whole(image.info()), image, @@ -562,8 +564,8 @@ pub struct ImageMemoryBarrier<'a> { pub image: &'a Image, pub prev_access: Access, pub next_access: Access, - pub old_layout: Option, - pub new_layout: Layout, + pub old_layout: Option, + pub new_layout: SyncLayout, pub family_transfer: Option<(u32, u32)>, pub range: SubresourceRange, } @@ -599,7 +601,7 @@ pub struct ImageSubresourceRange { pub struct ImageSubresourceState { pub subresource: ImageSubresourceRange, pub prev_access: Access, - pub old_layout: Option, + pub old_layout: Option, pub family: Ownership, } @@ -608,13 +610,13 @@ impl ImageSubresourceState { pub fn access<'a>( &'a mut self, next_access: Access, - new_layout: Layout, + new_layout: SyncLayout, queue: QueueId, encoder: &mut Encoder<'a>, ) -> &'a Self { match self.family { - Ownership::NotOwned => encoder.image_barriers( - encoder.scope().to_scope([ImageMemoryBarrier { + Ownership::NotOwned => { + encoder.image_barriers(encoder.scope().to_scope([ImageMemoryBarrier { image: &self.subresource.image, prev_access: self.prev_access, next_access, @@ -622,22 +624,20 @@ impl ImageSubresourceState { new_layout, family_transfer: None, range: self.subresource.range, - }]), - ), + }])) + } Ownership::Owned { family } => { assert_eq!(family, queue.family, "Wrong queue family owns the buffer"); - encoder.image_barriers( - encoder.scope().to_scope([ImageMemoryBarrier { - image: &self.subresource.image, - prev_access: self.prev_access, - next_access, - old_layout: self.old_layout, - new_layout, - family_transfer: None, - range: self.subresource.range, - }]), - ) + encoder.image_barriers(encoder.scope().to_scope([ImageMemoryBarrier { + image: &self.subresource.image, + prev_access: self.prev_access, + next_access, + old_layout: self.old_layout, + new_layout, + family_transfer: None, + range: self.subresource.range, + }])) } Ownership::Transition { from, to } => { assert_eq!( @@ -645,17 +645,15 @@ impl ImageSubresourceState { "Image is being transitioned to wrong queue family" ); - encoder.image_barriers( - encoder.scope().to_scope([ImageMemoryBarrier { - image: &self.subresource.image, - prev_access: self.prev_access, - next_access, - old_layout: self.old_layout, - new_layout, - family_transfer: Some((from, to)), - range: self.subresource.range, - }]), - ) + encoder.image_barriers(encoder.scope().to_scope([ImageMemoryBarrier { + image: &self.subresource.image, + prev_access: self.prev_access, + next_access, + old_layout: self.old_layout, + new_layout, + family_transfer: Some((from, to)), + range: self.subresource.range, + }])) } } self.prev_access = next_access; @@ -667,21 +665,19 @@ impl ImageSubresourceState { pub fn overwrite<'a>( &'a mut self, next_access: Access, - new_layout: Layout, + new_layout: SyncLayout, queue: QueueId, encoder: &mut Encoder<'a>, ) -> &'a ImageSubresourceRange { - encoder.image_barriers( - encoder.scope().to_scope([ImageMemoryBarrier { - image: &self.subresource.image, - prev_access: Access::None, - next_access, - old_layout: None, - new_layout, - family_transfer: None, - range: self.subresource.range, - }]), - ); + encoder.image_barriers(encoder.scope().to_scope([ImageMemoryBarrier { + image: &self.subresource.image, + prev_access: Access::None, + next_access, + old_layout: None, + new_layout, + family_transfer: None, + range: self.subresource.range, + }])); self.family = Ownership::Owned { family: queue.family, }; diff --git a/src/render_pass.rs b/src/render_pass.rs index 94904c3..dbab8c2 100644 --- a/src/render_pass.rs +++ b/src/render_pass.rs @@ -3,7 +3,7 @@ use crate::{ encode::{Encoder, RenderPassEncoder}, format::Format, framebuffer::FramebufferError, - image::{RawLayout, Samples}, + image::{Layout, Samples}, stage::PipelineStageFlags, Device, OutOfMemory, }; @@ -58,13 +58,13 @@ pub struct AttachmentInfo { feature = "serde-1", serde(skip_serializing_if = "Option::is_none", default) )] - pub initial_layout: Option, + pub initial_layout: Option, #[cfg_attr( feature = "serde-1", serde(skip_serializing_if = "is_default", default) )] - pub final_layout: RawLayout, + pub final_layout: Layout, } /// Specifies how render pass treats attachment content at the beginning. @@ -128,7 +128,7 @@ pub struct Subpass { feature = "serde-1", serde(skip_serializing_if = "Vec::is_empty", default) )] - pub colors: Vec<(u32, RawLayout)>, + pub colors: Vec<(u32, Layout)>, /// Index of an attachment that is used as depth attachment in this /// subpass. @@ -136,7 +136,7 @@ pub struct Subpass { feature = "serde-1", serde(skip_serializing_if = "Option::is_none", default) )] - pub depth: Option<(u32, RawLayout)>, + pub depth: Option<(u32, Layout)>, } /// Defines memory dependency between two subpasses diff --git a/src/view.rs b/src/view.rs index 9d2671f..6f23281 100644 --- a/src/view.rs +++ b/src/view.rs @@ -3,7 +3,7 @@ use crate::{ access::Access, backend::Device, encode::Encoder, - image::{Image, ImageExtent, ImageMemoryBarrier, Layout, SubresourceRange}, + image::{Image, ImageExtent, ImageMemoryBarrier, SyncLayout, SubresourceRange}, queue::{Ownership, QueueId}, OutOfMemory, }; @@ -91,7 +91,7 @@ impl MakeImageView for Image { pub struct ImageViewState { pub view: ImageView, pub prev_access: Access, - pub old_layout: Option, + pub old_layout: Option, pub family: Ownership, } @@ -100,7 +100,7 @@ impl ImageViewState { pub fn access<'a>( &'a mut self, next_access: Access, - new_layout: Layout, + new_layout: SyncLayout, queue: QueueId, encoder: &mut Encoder<'a>, ) -> &'a ImageView { @@ -162,7 +162,7 @@ impl ImageViewState { pub fn overwrite<'a>( &'a mut self, next_access: Access, - new_layout: Layout, + new_layout: SyncLayout, queue: QueueId, encoder: &mut Encoder<'a>, ) -> &'a ImageView {