From 71538c8701d42fd8831a75d13ae7f7cc34aa34ca Mon Sep 17 00:00:00 2001 From: Joshua Simmons Date: Wed, 16 Nov 2022 09:07:31 +0100 Subject: [PATCH] =?utf8?q?Add=20basic=20barriers=20and=20dress=20BL=C3=85H?= =?utf8?q?AJ=20in=20a=20texture?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- narcissus-gpu/src/lib.rs | 233 ++++++++++- narcissus-gpu/src/vulkan.rs | 546 ++++++++++++++++++++++++-- narcissus/src/main.rs | 165 +++++++- narcissus/src/shaders/basic.frag.glsl | 10 +- narcissus/src/shaders/basic.frag.spv | Bin 432 -> 836 bytes narcissus/src/shaders/basic.vert.glsl | 8 +- narcissus/src/shaders/basic.vert.spv | Bin 2364 -> 2464 bytes 7 files changed, 904 insertions(+), 58 deletions(-) diff --git a/narcissus-gpu/src/lib.rs b/narcissus-gpu/src/lib.rs index a607836..b9d1f40 100644 --- a/narcissus-gpu/src/lib.rs +++ b/narcissus-gpu/src/lib.rs @@ -6,6 +6,32 @@ use narcissus_core::{flags_def, thread_token_def, Handle, PhantomUnsend}; mod delay_queue; mod vulkan; +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub struct Offset2d { + pub x: i32, + pub y: i32, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub struct Extent2d { + pub width: u32, + pub height: u32, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub struct Offset3d { + pub x: i32, + pub y: i32, + pub z: i32, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub struct Extent3d { + pub width: u32, + pub height: u32, + pub depth: u32, +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, Default)] pub struct Texture(Handle); @@ -40,10 +66,8 @@ pub struct Viewport { #[repr(C)] pub struct Scissor { - pub x: i32, - pub y: i32, - pub width: u32, - pub height: u32, + pub offset: Offset2d, + pub extent: Extent2d, } flags_def!(ShaderStageFlags); @@ -72,6 +96,13 @@ pub enum TextureFormat { DEPTH_F32, } +flags_def!(TextureAspectFlags); +impl TextureAspectFlags { + pub const COLOR: Self = Self(1 << 0); + pub const DEPTH: Self = Self(1 << 1); + pub const STENCIL: Self = Self(1 << 2); +} + flags_def!(TextureUsageFlags); impl TextureUsageFlags { pub const SAMPLED: Self = Self(1 << 0); @@ -82,6 +113,21 @@ impl TextureUsageFlags { pub const TRANSFER_DST: Self = Self(1 << 5); } +pub struct TextureSubresourceLayers { + pub aspect: TextureAspectFlags, + pub mip_level: u32, + pub base_array_layer: u32, + pub array_layer_count: u32, +} + +pub struct TextureSubresourceRange { + pub aspect: TextureAspectFlags, + pub base_mip_level: u32, + pub mip_level_count: u32, + pub base_array_layer: u32, + pub array_layer_count: u32, +} + flags_def!(BufferUsageFlags); impl BufferUsageFlags { pub const UNIFORM: Self = Self(1 << 0); @@ -102,6 +148,7 @@ pub struct TextureDesc { pub usage: TextureUsageFlags, pub dimension: TextureDimension, pub format: TextureFormat, + pub initial_layout: TextureLayout, pub width: u32, pub height: u32, pub depth: u32, @@ -113,10 +160,16 @@ pub struct TextureViewDesc { pub texture: Texture, pub dimension: TextureDimension, pub format: TextureFormat, - pub base_mip: u32, - pub mip_count: u32, - pub base_layer: u32, - pub layer_count: u32, + pub subresource_range: TextureSubresourceRange, +} + +pub struct BufferTextureCopy { + pub buffer_offset: u64, + pub buffer_row_length: u32, + pub buffer_image_height: u32, + pub texture_subresource_layers: TextureSubresourceLayers, + pub texture_offset: Offset3d, + pub texture_extent: Extent3d, } pub struct ShaderDesc<'a> { @@ -134,7 +187,6 @@ pub enum SamplerFilter { #[derive(Clone, Copy, PartialEq, Eq)] pub enum SamplerCompareOp { - None, Less, LessEq, Greater, @@ -150,7 +202,7 @@ pub enum SamplerAddressMode { pub struct SamplerDesc { pub filter: SamplerFilter, pub address_mode: SamplerAddressMode, - pub compare_op: SamplerCompareOp, + pub compare_op: Option, pub mip_lod_bias: f32, pub min_lod: f32, pub max_lod: f32, @@ -345,6 +397,142 @@ pub enum TypedBind<'a> { StorageBuffer(&'a [Buffer]), } +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Access { + /// No access. + None, + + /// Read as an indirect buffer for drawing or dispatch. + IndirectBuffer, + /// Read as an index buffer. + IndexBuffer, + /// Read as a vertex buffer. + VertexBuffer, + + /// Read as a uniform buffer in a vertex shader. + VertexShaderUniformBufferRead, + /// Read as a sampled image or uniform texel buffer in a vertex shader. + VertexShaderSampledImageRead, + /// Read as any other resource in a vertex shader. + VertexShaderOtherRead, + + /// Read as a uniform buffer in a fragment shader. + FragmentShaderUniformBufferRead, + /// Read as a sampled image or uniform texel buffer in a fragment shader. + FragmentShaderSampledImageRead, + /// Read as any other resource in a fragment shader. + FragmentShaderOtherRead, + + /// Read as a color attachement. + ColorAttachmentRead, + /// Read as a depth-stencil attachment. + DepthStencilAttachmentRead, + + /// Read as a uniform buffer in any shader. + ShaderUniformBufferRead, + /// Read as a uniform buffer or vertex buffer in any shader. + ShaderUniformBufferOrVertexBufferRead, + /// Read as a sampled image or uniform texel buffer in any shader. + ShaderSampledImageRead, + /// Read as any other resource (excluding attachments) in any shader. + ShaderOtherRead, + + /// Read as the source of a transfer operation. + TransferRead, + /// Read on the host. + HostRead, + + /// Read by the presentation engine. + PresentRead, + + /// 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. + ColorAttachmentWrite, + /// Written as a depth-stencil attachment during rendering. + DepthStencilAttachmentWrite, + + /// Written as any resource in any shader. + ShaderWrite, + + /// Written as the destination of a transfer operation. + TransferWrite, + /// Pre-initialized on the host before device access starts. + HostPreInitializedWrite, + /// Written on the host. + HostWrite, + + /// Read or written as a color attachment during rendering. + ColorAttachmentReadWrite, + + /// Covers any access. Slow mode like snail. + General, +} + +impl Access { + /// Check whether this access type exclusively reads. + pub fn is_read(self) -> bool { + match self { + Access::None => true, + Access::IndirectBuffer => true, + Access::IndexBuffer => true, + Access::VertexBuffer => true, + Access::VertexShaderUniformBufferRead => true, + Access::VertexShaderSampledImageRead => true, + Access::VertexShaderOtherRead => true, + Access::FragmentShaderUniformBufferRead => true, + Access::FragmentShaderSampledImageRead => true, + Access::FragmentShaderOtherRead => true, + Access::ColorAttachmentRead => true, + Access::DepthStencilAttachmentRead => true, + Access::ShaderUniformBufferRead => true, + Access::ShaderUniformBufferOrVertexBufferRead => true, + Access::ShaderSampledImageRead => true, + Access::ShaderOtherRead => true, + Access::TransferRead => true, + Access::HostRead => true, + Access::PresentRead => true, + Access::VertexShaderWrite => false, + Access::FragmentShaderWrite => false, + Access::ColorAttachmentWrite => false, + Access::DepthStencilAttachmentWrite => false, + Access::ShaderWrite => false, + Access::TransferWrite => false, + Access::HostPreInitializedWrite => false, + Access::HostWrite => false, + Access::ColorAttachmentReadWrite => false, + Access::General => false, + } + } + + /// Check whether this access type contains a write. + pub fn is_write(self) -> bool { + !self.is_read() + } +} + +pub enum TextureLayout { + Optimal, + General, +} + +pub struct GlobalBarrier<'a> { + pub prev_access: &'a [Access], + pub next_access: &'a [Access], +} + +pub struct TextureBarrier<'a> { + pub prev_access: &'a [Access], + pub next_access: &'a [Access], + pub prev_layout: TextureLayout, + pub next_layout: TextureLayout, + pub texture: Texture, + pub subresource_range: TextureSubresourceRange, +} + thread_token_def!(ThreadToken, GpuConcurrent, 8); pub struct Frame<'a> { @@ -397,6 +585,7 @@ pub trait Device { ) -> (u32, u32, Texture); fn destroy_window(&self, window: Window); + #[must_use] fn create_cmd_buffer<'a, 'thread>( &'a self, frame: &'a Frame, @@ -423,14 +612,30 @@ pub trait Device { fn cmd_set_pipeline(&self, cmd_buffer: &mut CmdBuffer, pipeline: Pipeline); - fn cmd_begin_rendering(&self, cmd_buffer: &mut CmdBuffer, desc: &RenderingDesc); - - fn cmd_end_rendering(&self, cmd_buffer: &mut CmdBuffer); - fn cmd_set_viewports(&self, cmd_buffer: &mut CmdBuffer, viewports: &[Viewport]); fn cmd_set_scissors(&self, cmd_buffer: &mut CmdBuffer, scissors: &[Scissor]); + fn cmd_barrier( + &self, + cmd_buffer: &mut CmdBuffer, + global_barrier: Option<&GlobalBarrier>, + texture_barriers: &[TextureBarrier], + ); + + fn cmd_copy_buffer_to_texture( + &self, + cmd_buffer: &mut CmdBuffer, + src_buffer: Buffer, + dst_texture: Texture, + dst_texture_layout: TextureLayout, + copies: &[BufferTextureCopy], + ); + + fn cmd_begin_rendering(&self, cmd_buffer: &mut CmdBuffer, desc: &RenderingDesc); + + fn cmd_end_rendering(&self, cmd_buffer: &mut CmdBuffer); + fn cmd_draw( &self, cmd_buffer: &mut CmdBuffer, diff --git a/narcissus-gpu/src/vulkan.rs b/narcissus-gpu/src/vulkan.rs index 9cfd9bb..9b69e6a 100644 --- a/narcissus-gpu/src/vulkan.rs +++ b/narcissus-gpu/src/vulkan.rs @@ -16,13 +16,15 @@ use narcissus_core::{ use vulkan_sys as vk; use crate::{ - delay_queue::DelayQueue, Bind, BindGroupLayout, BindGroupLayoutDesc, BindingType, Buffer, - BufferDesc, BufferUsageFlags, ClearValue, CmdBuffer, CompareOp, ComputePipelineDesc, - CullingMode, Device, Frame, FrontFace, GpuConcurrent, GraphicsPipelineDesc, IndexType, LoadOp, - MemoryLocation, Pipeline, PolygonMode, Sampler, SamplerAddressMode, SamplerCompareOp, - SamplerDesc, SamplerFilter, ShaderStageFlags, StencilOp, StencilOpState, StoreOp, Texture, - TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken, - Topology, TypedBind, + delay_queue::DelayQueue, Access, Bind, BindGroupLayout, BindGroupLayoutDesc, BindingType, + Buffer, BufferDesc, BufferTextureCopy, BufferUsageFlags, ClearValue, CmdBuffer, CompareOp, + ComputePipelineDesc, CullingMode, Device, Extent2d, Extent3d, Frame, FrontFace, GlobalBarrier, + GpuConcurrent, GraphicsPipelineDesc, IndexType, LoadOp, MemoryLocation, Offset2d, Offset3d, + Pipeline, PolygonMode, Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc, + SamplerFilter, ShaderStageFlags, StencilOp, StencilOpState, StoreOp, Texture, + TextureAspectFlags, TextureBarrier, TextureDesc, TextureDimension, TextureFormat, + TextureLayout, TextureSubresourceLayers, TextureSubresourceRange, TextureUsageFlags, + TextureViewDesc, ThreadToken, Topology, TypedBind, }; const NUM_FRAMES: usize = 2; @@ -59,6 +61,44 @@ fn vk_vec vulkan_sys::Result>(mut f: F) -> Vec< v } +impl From for vk::Extent2d { + fn from(extent: Extent2d) -> Self { + vk::Extent2d { + width: extent.width, + height: extent.height, + } + } +} + +impl From for vk::Extent3d { + fn from(extent: Extent3d) -> Self { + vk::Extent3d { + width: extent.width, + height: extent.height, + depth: extent.depth, + } + } +} + +impl From for vk::Offset2d { + fn from(extent: Offset2d) -> Self { + vk::Offset2d { + x: extent.x, + y: extent.y, + } + } +} + +impl From for vk::Offset3d { + fn from(extent: Offset3d) -> Self { + vk::Offset3d { + x: extent.x, + y: extent.y, + z: extent.z, + } + } +} + #[must_use] fn vulkan_bool32(b: bool) -> vk::Bool32 { const VALUES: [vk::Bool32; 2] = [vk::Bool32::False, vk::Bool32::True]; @@ -76,8 +116,7 @@ fn vulkan_format(format: TextureFormat) -> vk::Format { } } -#[must_use] -fn vulkan_aspect(format: TextureFormat) -> vk::ImageAspectFlags { +fn vulkan_aspect_for_format(format: TextureFormat) -> vk::ImageAspectFlags { match format { TextureFormat::BGRA8_SRGB | TextureFormat::BGRA8_UNORM @@ -87,6 +126,20 @@ fn vulkan_aspect(format: TextureFormat) -> vk::ImageAspectFlags { } } +fn vulkan_aspect(aspect: TextureAspectFlags) -> vk::ImageAspectFlags { + let mut aspect_flags = default(); + if aspect.contains(TextureAspectFlags::COLOR) { + aspect_flags |= vk::ImageAspectFlags::COLOR; + } + if aspect.contains(TextureAspectFlags::DEPTH) { + aspect_flags |= vk::ImageAspectFlags::DEPTH; + } + if aspect.contains(TextureAspectFlags::STENCIL) { + aspect_flags |= vk::ImageAspectFlags::STENCIL; + } + aspect_flags +} + #[must_use] fn vulkan_clear_value(clear_value: ClearValue) -> vk::ClearValue { match clear_value { @@ -254,6 +307,349 @@ fn vulkan_image_view_type( } } +fn vulkan_subresource_layers( + subresource_layers: &TextureSubresourceLayers, +) -> vk::ImageSubresourceLayers { + vk::ImageSubresourceLayers { + aspect_mask: vulkan_aspect(subresource_layers.aspect), + mip_level: subresource_layers.mip_level, + base_array_layer: subresource_layers.base_array_layer, + layer_count: subresource_layers.array_layer_count, + } +} + +fn vulkan_subresource_range(subresource: &TextureSubresourceRange) -> vk::ImageSubresourceRange { + vk::ImageSubresourceRange { + aspect_mask: vulkan_aspect(subresource.aspect), + base_mip_level: subresource.base_mip_level, + level_count: subresource.mip_level_count, + base_array_layer: subresource.base_array_layer, + layer_count: subresource.array_layer_count, + } +} + +struct VulkanAccessInfo { + stages: vk::PipelineStageFlags2, + access: vk::AccessFlags2, + layout: vk::ImageLayout, +} + +#[must_use] +fn vulkan_access_info(access: Access) -> VulkanAccessInfo { + match access { + Access::None => VulkanAccessInfo { + stages: default(), + access: default(), + layout: vk::ImageLayout::Undefined, + }, + + Access::IndirectBuffer => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::DRAW_INDIRECT, + access: vk::AccessFlags2::INDIRECT_COMMAND_READ, + layout: vk::ImageLayout::Undefined, + }, + Access::IndexBuffer => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::VERTEX_INPUT, + access: vk::AccessFlags2::INDEX_READ, + layout: vk::ImageLayout::Undefined, + }, + Access::VertexBuffer => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::VERTEX_INPUT, + access: vk::AccessFlags2::VERTEX_ATTRIBUTE_READ, + layout: vk::ImageLayout::Undefined, + }, + + Access::VertexShaderUniformBufferRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::VERTEX_SHADER, + access: vk::AccessFlags2::UNIFORM_READ, + layout: vk::ImageLayout::Undefined, + }, + Access::VertexShaderSampledImageRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::VERTEX_SHADER, + access: vk::AccessFlags2::SHADER_READ, + layout: vk::ImageLayout::ReadOnlyOptimal, + }, + Access::VertexShaderOtherRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::VERTEX_SHADER, + access: vk::AccessFlags2::SHADER_READ, + layout: vk::ImageLayout::General, + }, + + Access::FragmentShaderUniformBufferRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::FRAGMENT_SHADER, + access: vk::AccessFlags2::UNIFORM_READ, + layout: vk::ImageLayout::Undefined, + }, + Access::FragmentShaderSampledImageRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::FRAGMENT_SHADER, + access: vk::AccessFlags2::SHADER_READ, + layout: vk::ImageLayout::ReadOnlyOptimal, + }, + Access::FragmentShaderOtherRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::FRAGMENT_SHADER, + access: vk::AccessFlags2::SHADER_READ, + layout: vk::ImageLayout::General, + }, + + Access::ColorAttachmentRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT, + access: vk::AccessFlags2::COLOR_ATTACHMENT_READ, + layout: vk::ImageLayout::AttachmentOptimal, + }, + Access::DepthStencilAttachmentRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT, + access: vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ, + layout: vk::ImageLayout::AttachmentOptimal, + }, + + Access::ShaderUniformBufferRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::ALL_COMMANDS, + access: vk::AccessFlags2::UNIFORM_READ, + layout: vk::ImageLayout::Undefined, + }, + Access::ShaderUniformBufferOrVertexBufferRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::ALL_COMMANDS, + access: vk::AccessFlags2::UNIFORM_READ | vk::AccessFlags2::VERTEX_ATTRIBUTE_READ, + layout: vk::ImageLayout::Undefined, + }, + Access::ShaderSampledImageRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::ALL_COMMANDS, + access: vk::AccessFlags2::SHADER_READ, + layout: vk::ImageLayout::ReadOnlyOptimal, + }, + Access::ShaderOtherRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::ALL_COMMANDS, + access: vk::AccessFlags2::SHADER_READ, + layout: vk::ImageLayout::General, + }, + + Access::TransferRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::TRANSFER, + access: vk::AccessFlags2::TRANSFER_READ, + layout: vk::ImageLayout::TransferSrcOptimal, + }, + Access::HostRead => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::HOST, + access: vk::AccessFlags2::HOST_READ, + layout: vk::ImageLayout::General, + }, + + Access::PresentRead => VulkanAccessInfo { + stages: default(), + access: default(), + layout: vk::ImageLayout::PresentSrcKhr, + }, + + Access::VertexShaderWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::VERTEX_SHADER, + access: vk::AccessFlags2::SHADER_WRITE, + layout: vk::ImageLayout::General, + }, + Access::FragmentShaderWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::FRAGMENT_SHADER, + access: vk::AccessFlags2::SHADER_WRITE, + layout: vk::ImageLayout::General, + }, + Access::ColorAttachmentWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT, + access: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE, + layout: vk::ImageLayout::ColorAttachmentOptimal, + }, + Access::DepthStencilAttachmentWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS + | vk::PipelineStageFlags2::LATE_FRAGMENT_TESTS, + access: vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE, + layout: vk::ImageLayout::DepthAttachmentOptimal, + }, + Access::ShaderWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::ALL_COMMANDS, + access: vk::AccessFlags2::SHADER_WRITE, + layout: vk::ImageLayout::General, + }, + Access::TransferWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::TRANSFER, + access: vk::AccessFlags2::TRANSFER_WRITE, + layout: vk::ImageLayout::TransferDstOptimal, + }, + Access::HostPreInitializedWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::HOST, + access: vk::AccessFlags2::HOST_WRITE, + layout: vk::ImageLayout::Preinitialized, + }, + Access::HostWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::HOST, + access: vk::AccessFlags2::HOST_WRITE, + layout: vk::ImageLayout::General, + }, + Access::ColorAttachmentReadWrite => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT, + access: vk::AccessFlags2::COLOR_ATTACHMENT_READ + | vk::AccessFlags2::COLOR_ATTACHMENT_WRITE, + layout: vk::ImageLayout::AttachmentOptimal, + }, + Access::General => VulkanAccessInfo { + stages: vk::PipelineStageFlags2::ALL_COMMANDS, + access: vk::AccessFlags2::COLOR_ATTACHMENT_READ + | vk::AccessFlags2::COLOR_ATTACHMENT_WRITE, + layout: vk::ImageLayout::General, + }, + } +} + +fn vulkan_memory_barrier(barrier: &GlobalBarrier) -> vk::MemoryBarrier2 { + let mut src_stage_mask = default(); + let mut src_access_mask = default(); + let mut dst_stage_mask = default(); + let mut dst_access_mask = default(); + + for &access in barrier.prev_access { + debug_assert!( + access.is_read() || barrier.prev_access.len() == 1, + "write access types must be on their own" + ); + + let info = vulkan_access_info(access); + src_stage_mask |= info.stages; + + // For writes, add availability operations. + if access.is_write() { + src_access_mask |= info.access; + } + } + + for &access in barrier.next_access { + debug_assert!( + access.is_read() || barrier.prev_access.len() == 1, + "write access types must be on their own" + ); + + let info = vulkan_access_info(access); + dst_stage_mask |= info.stages; + + // Add visibility operations if necessary. + // + // If the src access mask is zero, this is a Write-After-Read hazard (or for some reason, a + // Read-After-Read), so the dst access mask can be safely zeroed as these don't need + // visibility. + if src_access_mask != default() { + dst_access_mask |= info.access; + } + } + + if src_stage_mask == default() { + src_stage_mask = vk::PipelineStageFlags2::TOP_OF_PIPE; + } + + if dst_stage_mask == default() { + dst_stage_mask = vk::PipelineStageFlags2::BOTTOM_OF_PIPE; + } + + vk::MemoryBarrier2 { + src_stage_mask, + src_access_mask, + dst_stage_mask, + dst_access_mask, + ..default() + } +} + +fn vulkan_image_memory_barrier( + barrier: &TextureBarrier, + image: vk::Image, + subresource_range: vk::ImageSubresourceRange, +) -> vk::ImageMemoryBarrier2 { + let mut src_stage_mask = default(); + let mut src_access_mask = default(); + let mut dst_stage_mask = default(); + let mut dst_access_mask = default(); + let mut old_layout = vk::ImageLayout::Undefined; + let mut new_layout = vk::ImageLayout::Undefined; + + for &access in barrier.prev_access { + debug_assert!( + access.is_read() || barrier.prev_access.len() == 1, + "write access types must be on their own" + ); + + let info = vulkan_access_info(access); + src_stage_mask |= info.stages; + + // For writes, add availability operations. + if access.is_write() { + src_access_mask |= info.access; + } + + let layout = match barrier.prev_layout { + TextureLayout::Optimal => info.layout, + TextureLayout::General => { + if access == Access::PresentRead { + vk::ImageLayout::PresentSrcKhr + } else { + vk::ImageLayout::General + } + } + }; + + debug_assert!( + old_layout == vk::ImageLayout::Undefined || old_layout == layout, + "mixed image layout" + ); + + old_layout = layout; + } + + for &access in barrier.next_access { + debug_assert!( + access.is_read() || barrier.prev_access.len() == 1, + "write access types must be on their own" + ); + + let info = vulkan_access_info(access); + dst_stage_mask |= info.stages; + + // Add visibility operations if necessary. + // + // If the src access mask is zero, this is a Write-After-Read hazard (or for some reason, a + // Read-After-Read), so the dst access mask can be safely zeroed as these don't need + // visibility. + if src_access_mask != default() { + dst_access_mask |= info.access; + } + + let layout = match barrier.next_layout { + TextureLayout::Optimal => info.layout, + TextureLayout::General => { + if access == Access::PresentRead { + vk::ImageLayout::PresentSrcKhr + } else { + vk::ImageLayout::General + } + } + }; + + debug_assert!( + new_layout == vk::ImageLayout::Undefined || new_layout == layout, + "mixed image layout" + ); + + new_layout = layout; + } + + vk::ImageMemoryBarrier2 { + src_stage_mask, + src_access_mask, + dst_stage_mask, + dst_access_mask, + old_layout, + new_layout, + src_queue_family_index: 0, + dst_queue_family_index: 0, + image, + subresource_range, + ..default() + } +} + struct VulkanBuffer { memory: VulkanMemory, buffer: vk::Buffer, @@ -288,6 +684,14 @@ enum VulkanTextureHolder { } impl VulkanTextureHolder { + fn image(&self) -> vk::Image { + match self { + VulkanTextureHolder::Unique(x) => x.texture.image, + VulkanTextureHolder::Shared(_) => panic!(), + VulkanTextureHolder::Swapchain(_) => panic!(), + } + } + fn image_view(&self) -> vk::ImageView { match self { VulkanTextureHolder::Unique(x) => x.view, @@ -1173,7 +1577,7 @@ impl<'driver> Device for VulkanDevice<'driver> { }; let view_type = vulkan_image_view_type(desc.layer_count, desc.dimension); - let aspect_mask = vulkan_aspect(desc.format); + let aspect_mask = vulkan_aspect_for_format(desc.format); let create_info = vk::ImageViewCreateInfo { image, view_type, @@ -1227,21 +1631,16 @@ impl<'driver> Device for VulkanDevice<'driver> { } } - let view_type = vulkan_image_view_type(desc.layer_count, desc.dimension); - let aspect_mask = vulkan_aspect(desc.format); + let subresource_range = vulkan_subresource_range(&desc.subresource_range); + let view_type = + vulkan_image_view_type(desc.subresource_range.array_layer_count, desc.dimension); let format = vulkan_format(desc.format); let create_info = vk::ImageViewCreateInfo { image: arc_texture.image, view_type, format, - subresource_range: vk::ImageSubresourceRange { - aspect_mask, - base_mip_level: desc.base_mip, - level_count: desc.mip_count, - base_array_layer: desc.base_layer, - layer_count: desc.layer_count, - }, + subresource_range, ..default() }; @@ -1288,11 +1687,11 @@ impl<'driver> Device for VulkanDevice<'driver> { }; let (compare_enable, compare_op) = match desc.compare_op { - SamplerCompareOp::None => (vk::Bool32::False, vk::CompareOp::Always), - SamplerCompareOp::Less => (vk::Bool32::True, vk::CompareOp::Less), - SamplerCompareOp::LessEq => (vk::Bool32::True, vk::CompareOp::LessOrEqual), - SamplerCompareOp::Greater => (vk::Bool32::True, vk::CompareOp::Greater), - SamplerCompareOp::GreaterEq => (vk::Bool32::True, vk::CompareOp::GreaterOrEqual), + None => (vk::Bool32::False, vk::CompareOp::Always), + Some(SamplerCompareOp::Less) => (vk::Bool32::True, vk::CompareOp::Less), + Some(SamplerCompareOp::LessEq) => (vk::Bool32::True, vk::CompareOp::LessOrEqual), + Some(SamplerCompareOp::Greater) => (vk::Bool32::True, vk::CompareOp::Greater), + Some(SamplerCompareOp::GreaterEq) => (vk::Bool32::True, vk::CompareOp::GreaterOrEqual), }; let mut sampler = vk::Sampler::null(); @@ -2006,6 +2405,103 @@ impl<'driver> Device for VulkanDevice<'driver> { } } + fn cmd_barrier( + &self, + cmd_buffer: &mut CmdBuffer, + global_barrier: Option<&GlobalBarrier>, + texture_barriers: &[TextureBarrier], + ) { + let arena = HybridArena::<4096>::new(); + + let memory_barriers = arena.alloc_slice_fill_iter( + global_barrier + .iter() + .map(|global_barrier| vulkan_memory_barrier(global_barrier)), + ); + + let image_memory_barriers = + arena.alloc_slice_fill_iter(texture_barriers.iter().map(|texture_barrier| { + let image = self + .texture_pool + .lock() + .get(texture_barrier.texture.0) + .expect("invalid texture handle") + .image(); + + // TODO: This needs to be pulled from somewhere useful. + let subresource_range = vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }; + vulkan_image_memory_barrier(texture_barrier, image, subresource_range) + })); + + let command_buffer = self.cmd_buffer_mut(cmd_buffer).command_buffer; + unsafe { + self.device_fn.cmd_pipeline_barrier2( + command_buffer, + &vk::DependencyInfo { + memory_barriers: memory_barriers.into(), + image_memory_barriers: image_memory_barriers.into(), + ..default() + }, + ) + } + } + + fn cmd_copy_buffer_to_texture( + &self, + cmd_buffer: &mut CmdBuffer, + src_buffer: Buffer, + dst_texture: Texture, + dst_texture_layout: TextureLayout, + copies: &[BufferTextureCopy], + ) { + let arena = HybridArena::<4096>::new(); + + let regions = arena.alloc_slice_fill_iter(copies.iter().map(|copy| vk::BufferImageCopy { + buffer_offset: copy.buffer_offset, + buffer_row_length: copy.buffer_row_length, + buffer_image_height: copy.buffer_image_height, + image_subresource: vulkan_subresource_layers(©.texture_subresource_layers), + image_offset: copy.texture_offset.into(), + image_extent: copy.texture_extent.into(), + })); + + let src_buffer = self + .buffer_pool + .lock() + .get(src_buffer.0) + .expect("invalid buffer handle") + .buffer; + + let dst_image = self + .texture_pool + .lock() + .get(dst_texture.0) + .expect("invalid texture handle") + .image(); + + let dst_image_layout = match dst_texture_layout { + TextureLayout::Optimal => vk::ImageLayout::TransferDstOptimal, + TextureLayout::General => vk::ImageLayout::General, + }; + + let command_buffer = self.cmd_buffer_mut(cmd_buffer).command_buffer; + unsafe { + self.device_fn.cmd_copy_buffer_to_image( + command_buffer, + src_buffer, + dst_image, + dst_image_layout, + regions, + ) + } + } + fn cmd_set_bind_group( &self, frame: &Frame, @@ -2086,7 +2582,7 @@ impl<'driver> Device for VulkanDevice<'driver> { .unwrap() .image_view(); vk::DescriptorImageInfo { - image_layout: vk::ImageLayout::ColorAttachmentOptimal, + image_layout: vk::ImageLayout::ShaderReadOnlyOptimal, // TODO: Determine appropriate layout here. image_view, sampler: vk::Sampler::null(), } diff --git a/narcissus/src/main.rs b/narcissus/src/main.rs index c29415f..b90ff9a 100644 --- a/narcissus/src/main.rs +++ b/narcissus/src/main.rs @@ -3,12 +3,14 @@ use std::{path::Path, time::Instant}; use narcissus_app::{create_app, Event, Key, WindowDesc}; use narcissus_core::{cstr, default, obj, rand::Pcg64, Image}; use narcissus_gpu::{ - create_vulkan_device, Bind, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, Buffer, - BufferDesc, BufferUsageFlags, ClearValue, CompareOp, CullingMode, Device, FrontFace, - GraphicsPipelineDesc, GraphicsPipelineLayout, IndexType, LoadOp, MemoryLocation, PolygonMode, - RenderingAttachment, RenderingDesc, Scissor, ShaderDesc, ShaderStageFlags, StoreOp, - TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, ThreadToken, Topology, - TypedBind, Viewport, + create_vulkan_device, Access, Bind, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, + Buffer, BufferDesc, BufferTextureCopy, BufferUsageFlags, ClearValue, CompareOp, CullingMode, + Device, Extent2d, Extent3d, FrontFace, GraphicsPipelineDesc, GraphicsPipelineLayout, IndexType, + LoadOp, MemoryLocation, Offset2d, Offset3d, PolygonMode, RenderingAttachment, RenderingDesc, + SamplerAddressMode, SamplerDesc, SamplerFilter, Scissor, ShaderDesc, ShaderStageFlags, StoreOp, + Texture, TextureAspectFlags, TextureBarrier, TextureDesc, TextureDimension, TextureFormat, + TextureLayout, TextureSubresourceLayers, TextureSubresourceRange, TextureUsageFlags, + ThreadToken, Topology, TypedBind, Viewport, }; use narcissus_maths::{ sin_cos_pi_f32, vec2, vec3, vec4, Affine3, Deg, HalfTurn, Mat3, Mat4, Point3, Vec2, Vec3, @@ -39,6 +41,7 @@ struct Vertex { } unsafe impl Blittable for Vertex {} +unsafe impl Blittable for u8 {} unsafe impl Blittable for u16 {} unsafe impl Blittable for Affine3 {} @@ -143,6 +146,102 @@ where buffer } +fn create_texture_with_data( + device: &dyn Device, + thread_token: &mut ThreadToken, + width: u32, + height: u32, + data: &[u8], +) -> Texture { + let frame = device.begin_frame(); + + let buffer = create_buffer_with_data(device, BufferUsageFlags::TRANSFER_SRC, data); + let texture = device.create_texture(&TextureDesc { + memory_location: MemoryLocation::PreferDevice, + usage: TextureUsageFlags::SAMPLED | TextureUsageFlags::TRANSFER_DST, + dimension: TextureDimension::Type2d, + format: TextureFormat::RGBA8_SRGB, + initial_layout: TextureLayout::Optimal, + width, + height, + depth: 1, + layer_count: 1, + mip_levels: 1, + }); + + let mut cmd_buffer = device.create_cmd_buffer(&frame, thread_token); + + device.cmd_barrier( + &mut cmd_buffer, + None, + &[TextureBarrier { + prev_access: &[Access::None], + next_access: &[Access::TransferWrite], + prev_layout: TextureLayout::Optimal, + next_layout: TextureLayout::Optimal, + texture, + subresource_range: TextureSubresourceRange { + aspect: TextureAspectFlags::COLOR, + base_mip_level: 0, + mip_level_count: 1, + base_array_layer: 0, + array_layer_count: 1, + }, + }], + ); + + device.cmd_copy_buffer_to_texture( + &mut cmd_buffer, + buffer, + texture, + TextureLayout::Optimal, + &[BufferTextureCopy { + buffer_offset: 0, + buffer_row_length: 0, + buffer_image_height: 0, + texture_subresource_layers: TextureSubresourceLayers { + aspect: TextureAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + array_layer_count: 1, + }, + texture_offset: Offset3d { x: 0, y: 0, z: 0 }, + texture_extent: Extent3d { + width, + height, + depth: 1, + }, + }], + ); + + device.cmd_barrier( + &mut cmd_buffer, + None, + &[TextureBarrier { + prev_access: &[Access::TransferWrite], + next_access: &[Access::FragmentShaderSampledImageRead], + prev_layout: TextureLayout::Optimal, + next_layout: TextureLayout::Optimal, + texture, + subresource_range: TextureSubresourceRange { + aspect: TextureAspectFlags::COLOR, + base_mip_level: 0, + mip_level_count: 1, + base_array_layer: 0, + array_layer_count: 1, + }, + }], + ); + + device.submit(&frame, cmd_buffer); + + device.destroy_buffer(&frame, buffer); + + device.end_frame(frame); + + texture +} + struct MappedBuffer<'a> { device: &'a dyn Device, buffer: Buffer, @@ -206,7 +305,7 @@ impl<'a> Drop for MappedBuffer<'a> { } pub fn main() { - let _blÃ¥haj_image = load_img("narcissus/data/blÃ¥haj.png"); + let blÃ¥haj_image = load_img("narcissus/data/blÃ¥haj.png"); let (blÃ¥haj_vertices, blÃ¥haj_indices) = load_obj("narcissus/data/blÃ¥haj.obj"); let app = create_app(); @@ -248,6 +347,18 @@ pub fn main() { binding_type: BindingType::StorageBuffer, count: 1, }, + BindGroupLayoutEntryDesc { + slot: 2, + stages: ShaderStageFlags::ALL, + binding_type: BindingType::Sampler, + count: 1, + }, + BindGroupLayoutEntryDesc { + slot: 3, + stages: ShaderStageFlags::ALL, + binding_type: BindingType::Texture, + count: 1, + }, ], }); @@ -291,6 +402,14 @@ pub fn main() { blÃ¥haj_indices.as_slice(), ); + let blÃ¥haj_texture = create_texture_with_data( + device.as_ref(), + &mut thread_token, + blÃ¥haj_image.width() as u32, + blÃ¥haj_image.height() as u32, + blÃ¥haj_image.as_slice(), + ); + let mut uniforms = MappedBuffer::new( device.as_ref(), BufferUsageFlags::UNIFORM, @@ -303,6 +422,15 @@ pub fn main() { std::mem::size_of::() * MAX_SHARKS, ); + let sampler = device.create_sampler(&SamplerDesc { + filter: SamplerFilter::Point, + address_mode: SamplerAddressMode::Clamp, + compare_op: None, + mip_lod_bias: 0.0, + min_lod: 0.0, + max_lod: 1000.0, + }); + let mut depth_width = 0; let mut depth_height = 0; let mut depth_image = default(); @@ -356,13 +484,13 @@ pub fn main() { device.acquire_swapchain(&frame, main_window, TextureFormat::BGRA8_SRGB); let frame_start = Instant::now() - start_time; - let frame_start = frame_start.as_secs_f32() * 0.125; + let frame_start = frame_start.as_secs_f32() * 0.01; for (i, transform) in shark_transforms.iter_mut().enumerate() { let direction = if i & 1 == 0 { 1.0 } else { -1.0 }; - let (s, _) = sin_cos_pi_f32(frame_start + (i as f32) * 0.125); + let (s, _) = sin_cos_pi_f32(frame_start + (i as f32) * 0.0125); transform.translate.y = s; - transform.matrix *= Mat3::from_axis_rotation(Vec3::Y, HalfTurn::new(0.005 * direction)) + transform.matrix *= Mat3::from_axis_rotation(Vec3::Y, HalfTurn::new(0.002 * direction)) } transforms.write_slice(&shark_transforms); @@ -386,6 +514,7 @@ pub fn main() { usage: TextureUsageFlags::DEPTH_STENCIL, dimension: TextureDimension::Type2d, format: TextureFormat::DEPTH_F32, + initial_layout: TextureLayout::Optimal, width, height, depth: 1, @@ -430,6 +559,16 @@ pub fn main() { array_element: 0, typed: TypedBind::StorageBuffer(&[transforms.buffer()]), }, + Bind { + binding: 2, + array_element: 0, + typed: TypedBind::Sampler(&[sampler]), + }, + Bind { + binding: 3, + array_element: 0, + typed: TypedBind::Texture(&[blÃ¥haj_texture]), + }, ], ); @@ -464,10 +603,8 @@ pub fn main() { device.cmd_set_scissors( &mut cmd_buffer, &[Scissor { - x: 0, - y: 0, - width, - height, + offset: Offset2d { x: 0, y: 0 }, + extent: Extent2d { width, height }, }], ); diff --git a/narcissus/src/shaders/basic.frag.glsl b/narcissus/src/shaders/basic.frag.glsl index ad841f7..26205a1 100644 --- a/narcissus/src/shaders/basic.frag.glsl +++ b/narcissus/src/shaders/basic.frag.glsl @@ -1,8 +1,14 @@ #version 460 -layout(location = 0) in vec3 fragColor; +layout(set = 1, binding = 2) uniform sampler texSampler; +layout(set = 1, binding = 3) uniform texture2D tex; + +layout(location = 0) in vec2 texcoord; +layout(location = 1) in vec3 normal; layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(fragColor, 1.0); + float NdotL = max(dot(normal, vec3(0.0, 1.0, 0.0)), 0.2f); + vec3 rgb = texture(sampler2D(tex, texSampler), vec2(texcoord.x, texcoord.y)).rgb; + outColor = vec4(rgb, 1.0); } \ No newline at end of file diff --git a/narcissus/src/shaders/basic.frag.spv b/narcissus/src/shaders/basic.frag.spv index 869b2117ca1854db9fcf14eea88e4e696d96291d..bc78ab3f5ceca86fb25f35dce99dc6edca17fa1c 100644 GIT binary patch literal 836 zcmYk4OG`pg6otQg&$)_4R)m_`L$kMGFO4878aZ)b1T_wtG^k1afPQw9Al7ytaD(Vt zd+o>C$B_5Rlrk1rV#F*~$;p64O7=aTJWU2)-*1EA=uC<#!GzK*Qz3$VK3?BHm1qEM zpbMM=D+J4zficnYJ230*SXy-zR_%N~^~yaj?bd8n(`!b;YviWu60DzU>I6za2j~;+ zch%~D?weGofpPLP9|?De)IA~Xjbr&#w8&{%|47R@Vd;dmzoJcG6Rbyr$hObbj+&wU zG*i54h2>RTjc7L!r;rcbsOMPqyRMbjls|Az^ZGtd?loA6t>@uSnd5bGmEdE}hd`OetHxAy~ci%IiOKN3a_y_eaJx8UHz= B9}xfm literal 432 zcmXw#KWhR(6vThGyLV?a{s--X!BUVEi3EaL+E^%l0wIk>(!>wo$4(OnGwwY&ZfD-? zn|T|(`C%Ph6DI%;KdN zRtD{{AX(dUzT0+1=?wBxX~r*s5?JTi-F*K@>AQU}r)s!C=H$_E!^~;N1GV#y{FZt% Q+*ghb(fg?PN5MM<|MbBRGXMYp diff --git a/narcissus/src/shaders/basic.vert.glsl b/narcissus/src/shaders/basic.vert.glsl index c065c3f..f7f36ac 100644 --- a/narcissus/src/shaders/basic.vert.glsl +++ b/narcissus/src/shaders/basic.vert.glsl @@ -22,7 +22,8 @@ layout(std430, set = 1, binding = 1) readonly buffer transformBuffer { TransformData transforms[]; }; -layout(location = 0) out vec3 fragColor; +layout(location = 0) out vec2 texcoord; +layout(location = 1) out vec3 normal; void main() { TransformData td = transforms[gl_InstanceIndex]; @@ -36,7 +37,8 @@ void main() { vec3 modelOff = vec3(td.transform[2].y, td.transform[2].z, td.transform[2].w); vec3 posWorld = transpose(modelRot) * vd.position.xyz + modelOff; vec4 posClip = transpose(viewProj) * vec4(posWorld, 1.0); - gl_Position = posClip; - fragColor = vd.normal.xyz * 0.5 + 0.5; + + normal = vd.normal.xyz; + texcoord = vec2(vd.texcoord.x, 1.0 - vd.texcoord.y); } diff --git a/narcissus/src/shaders/basic.vert.spv b/narcissus/src/shaders/basic.vert.spv index fab7758c2cf485ec896a6dcf892a27a660ef0350..f9260ed64f9501b8e21323e0c4a02fb9c386bb7f 100644 GIT binary patch delta 532 zcmY+B!AiqG5Qe`^vIz=7Fo+iqf+vMugo0YCl!9Ir1fN1YhzM#ZvEaQdR8KY@JxZ@W zgg!u0%@)I3b~&-$b*h zvrfW2kOC2~Nx}mVgXg{QUy8YXdo_3hHu#G(EE(_$h$|X43rqtWR*SiXy)aWZNDKF6 zll~6;t9b#$V4d@kRR+PKPmASHW3y& z>nqu$GvzRK7a6O7*KCtGOoavI{I)2$!z^{@4yQmHDtE2g&^bSl6#2|W)IZ&kkyOoU o*eleTgpnsDfn?wj6VG@L+@(|2i9JB%a^hnOZXvQS6bC520c