]> git.nega.tv - josh/narcissus/commitdiff
Add basic barriers and dress BLÅHAJ in a texture
authorJoshua Simmons <josh@nega.tv>
Wed, 16 Nov 2022 08:07:31 +0000 (09:07 +0100)
committerJoshua Simmons <josh@nega.tv>
Wed, 16 Nov 2022 22:58:18 +0000 (23:58 +0100)
narcissus-gpu/src/lib.rs
narcissus-gpu/src/vulkan.rs
narcissus/src/main.rs
narcissus/src/shaders/basic.frag.glsl
narcissus/src/shaders/basic.frag.spv
narcissus/src/shaders/basic.vert.glsl
narcissus/src/shaders/basic.vert.spv

index a6078363024b1d3bf051cf578ba1c391faa65b99..b9d1f4064ac8af3de0e0533c41b41460391e5361 100644 (file)
@@ -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<SamplerCompareOp>,
     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,
index 9cfd9bb7b05a4fad5fec29bc9a02d69485b38082..9b69e6a79a0a1284a2c3c0a1c42e92b93bc239a6 100644 (file)
@@ -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<T, F: FnMut(&mut u32, *mut T) -> vulkan_sys::Result>(mut f: F) -> Vec<
     v
 }
 
+impl From<Extent2d> for vk::Extent2d {
+    fn from(extent: Extent2d) -> Self {
+        vk::Extent2d {
+            width: extent.width,
+            height: extent.height,
+        }
+    }
+}
+
+impl From<Extent3d> for vk::Extent3d {
+    fn from(extent: Extent3d) -> Self {
+        vk::Extent3d {
+            width: extent.width,
+            height: extent.height,
+            depth: extent.depth,
+        }
+    }
+}
+
+impl From<Offset2d> for vk::Offset2d {
+    fn from(extent: Offset2d) -> Self {
+        vk::Offset2d {
+            x: extent.x,
+            y: extent.y,
+        }
+    }
+}
+
+impl From<Offset3d> 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(&copy.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(),
                     }
index c29415f62beba00bc6ef479b82fd44ae9f6784e5..b90ff9a0b17bd246dcc44d9f554ca92a55eeab08 100644 (file)
@@ -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::<Affine3>() * 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 },
             }],
         );
 
index ad841f7de8469f227ec5690287b859df73099861..26205a1914b7914848cb6d5a80ec9b4ae8b65c2b 100644 (file)
@@ -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
index 869b2117ca1854db9fcf14eea88e4e696d96291d..bc78ab3f5ceca86fb25f35dce99dc6edca17fa1c 100644 (file)
Binary files a/narcissus/src/shaders/basic.frag.spv and b/narcissus/src/shaders/basic.frag.spv differ
index c065c3fdbf7bf15226e4bd57072820d89ce2bf46..f7f36ac7ae8591075cfe9c8c43b913bca9347e56 100644 (file)
@@ -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);
 }
index fab7758c2cf485ec896a6dcf892a27a660ef0350..f9260ed64f9501b8e21323e0c4a02fb9c386bb7f 100644 (file)
Binary files a/narcissus/src/shaders/basic.vert.spv and b/narcissus/src/shaders/basic.vert.spv differ