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);
#[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);
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);
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);
pub usage: TextureUsageFlags,
pub dimension: TextureDimension,
pub format: TextureFormat,
+ pub initial_layout: TextureLayout,
pub width: u32,
pub height: u32,
pub depth: u32,
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> {
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SamplerCompareOp {
- None,
Less,
LessEq,
Greater,
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,
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> {
) -> (u32, u32, Texture);
fn destroy_window(&self, window: Window);
+ #[must_use]
fn create_cmd_buffer<'a, 'thread>(
&'a self,
frame: &'a Frame,
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,
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;
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];
}
}
-#[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
}
}
+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 {
}
}
+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,
}
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,
};
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,
}
}
- 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()
};
};
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();
}
}
+ 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,
.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(),
}
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,
}
unsafe impl Blittable for Vertex {}
+unsafe impl Blittable for u8 {}
unsafe impl Blittable for u16 {}
unsafe impl Blittable for Affine3 {}
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,
}
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();
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,
+ },
],
});
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,
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();
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);
usage: TextureUsageFlags::DEPTH_STENCIL,
dimension: TextureDimension::Type2d,
format: TextureFormat::DEPTH_F32,
+ initial_layout: TextureLayout::Optimal,
width,
height,
depth: 1,
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]),
+ },
],
);
device.cmd_set_scissors(
&mut cmd_buffer,
&[Scissor {
- x: 0,
- y: 0,
- width,
- height,
+ offset: Offset2d { x: 0, y: 0 },
+ extent: Extent2d { width, height },
}],
);