use crate::{
delay_queue::DelayQueue, frame_counter::FrameCounter, Access, Bind, BindGroupLayout,
- BindGroupLayoutDesc, BindingType, Buffer, BufferDesc, BufferImageCopy, BufferUsageFlags,
- ClearValue, CmdBuffer, CompareOp, ComputePipelineDesc, CullingMode, Device, Extent2d, Extent3d,
- Frame, FrontFace, GlobalBarrier, GpuConcurrent, GraphicsPipelineDesc, Image, ImageAspectFlags,
- ImageBarrier, ImageDesc, ImageDimension, ImageFormat, ImageLayout, ImageSubresourceLayers,
- ImageSubresourceRange, ImageUsageFlags, ImageViewDesc, IndexType, LoadOp, MemoryLocation,
- Offset2d, Offset3d, Pipeline, PolygonMode, Sampler, SamplerAddressMode, SamplerCompareOp,
- SamplerDesc, SamplerFilter, ShaderStageFlags, StencilOp, StencilOpState, StoreOp,
- SwapchainOutOfDateError, ThreadToken, Topology, TypedBind,
+ BindGroupLayoutDesc, BindingType, BlendMode, Buffer, BufferDesc, BufferImageCopy,
+ BufferUsageFlags, ClearValue, CmdBuffer, CompareOp, ComputePipelineDesc, CullingMode, Device,
+ Extent2d, Extent3d, Frame, FrontFace, GlobalBarrier, GpuConcurrent, GraphicsPipelineDesc,
+ Image, ImageAspectFlags, ImageBarrier, ImageBlit, ImageDesc, ImageDimension, ImageFormat,
+ ImageLayout, ImageSubresourceLayers, ImageSubresourceRange, ImageUsageFlags, ImageViewDesc,
+ IndexType, LoadOp, MemoryLocation, Offset2d, Offset3d, Pipeline, PolygonMode, Sampler,
+ SamplerAddressMode, SamplerCompareOp, SamplerDesc, SamplerFilter, ShaderStageFlags, StencilOp,
+ StencilOpState, StoreOp, SwapchainOutOfDateError, ThreadToken, Topology, TypedBind,
};
const NUM_FRAMES: usize = 2;
}
}
+#[must_use]
+fn vulkan_blend_mode(blend_mode: BlendMode) -> vk::PipelineColorBlendAttachmentState {
+ match blend_mode {
+ BlendMode::Opaque => vk::PipelineColorBlendAttachmentState {
+ color_write_mask: vk::ColorComponentFlags::R
+ | vk::ColorComponentFlags::G
+ | vk::ColorComponentFlags::B
+ | vk::ColorComponentFlags::A,
+ ..default()
+ },
+ BlendMode::Mask => todo!(),
+ BlendMode::Translucent => vk::PipelineColorBlendAttachmentState {
+ blend_enable: vk::Bool32::True,
+ src_color_blend_factor: vk::BlendFactor::SrcAlpha,
+ dst_color_blend_factor: vk::BlendFactor::OneMinusSrcAlpha,
+ color_blend_op: vk::BlendOp::Add,
+ src_alpha_blend_factor: vk::BlendFactor::One,
+ dst_alpha_blend_factor: vk::BlendFactor::Zero,
+ alpha_blend_op: vk::BlendOp::Add,
+ color_write_mask: vk::ColorComponentFlags::R
+ | vk::ColorComponentFlags::G
+ | vk::ColorComponentFlags::B
+ | vk::ColorComponentFlags::A,
+ },
+ BlendMode::Premultiplied => vk::PipelineColorBlendAttachmentState {
+ blend_enable: vk::Bool32::True,
+ src_color_blend_factor: vk::BlendFactor::One,
+ dst_color_blend_factor: vk::BlendFactor::OneMinusSrcAlpha,
+ color_blend_op: vk::BlendOp::Add,
+ src_alpha_blend_factor: vk::BlendFactor::One,
+ dst_alpha_blend_factor: vk::BlendFactor::Zero,
+ alpha_blend_op: vk::BlendOp::Add,
+ color_write_mask: vk::ColorComponentFlags::R
+ | vk::ColorComponentFlags::G
+ | vk::ColorComponentFlags::B
+ | vk::ColorComponentFlags::A,
+ },
+ BlendMode::Additive => todo!(),
+ BlendMode::Modulate => todo!(),
+ }
+}
+
#[must_use]
fn vulkan_image_view_type(layer_count: u32, image_dimension: ImageDimension) -> vk::ImageViewType {
match (layer_count, image_dimension) {
match self {
VulkanImageHolder::Unique(x) => x.image.image,
VulkanImageHolder::Shared(_) => panic!(),
- VulkanImageHolder::Swapchain(_) => panic!(),
+ VulkanImageHolder::Swapchain(x) => x.image,
}
}
if desc.usage.contains(ImageUsageFlags::STORAGE) {
usage |= vk::ImageUsageFlags::STORAGE;
}
- if desc.usage.contains(ImageUsageFlags::DEPTH_STENCIL) {
+ if desc
+ .usage
+ .contains(ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
+ {
usage |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
}
- if desc.usage.contains(ImageUsageFlags::RENDER_TARGET) {
+ if desc.usage.contains(ImageUsageFlags::COLOR_ATTACHMENT) {
usage |= vk::ImageUsageFlags::COLOR_ATTACHMENT;
}
if desc.usage.contains(ImageUsageFlags::TRANSFER_DST) {
front,
..default()
};
- let color_blend_attachments = &[vk::PipelineColorBlendAttachmentState {
- color_write_mask: vk::ColorComponentFlags::R
- | vk::ColorComponentFlags::G
- | vk::ColorComponentFlags::B
- | vk::ColorComponentFlags::A,
- ..default()
- }];
+ let color_blend_attachments = &[vulkan_blend_mode(desc.blend_mode)];
let color_blend_state = vk::PipelineColorBlendStateCreateInfo {
attachments: color_blend_attachments.into(),
..default()
buffer_offset: copy.buffer_offset,
buffer_row_length: copy.buffer_row_length,
buffer_image_height: copy.buffer_image_height,
- image_subresource: vulkan_subresource_layers(©.image_subresource_layers),
+ image_subresource: vulkan_subresource_layers(©.image_subresource),
image_offset: copy.image_offset.into(),
image_extent: copy.image_extent.into(),
}));
}
}
+ fn cmd_blit_image(
+ &self,
+ cmd_buffer: &mut CmdBuffer,
+ src_image: Image,
+ src_image_layout: ImageLayout,
+ dst_image: Image,
+ dst_image_layout: ImageLayout,
+ regions: &[ImageBlit],
+ ) {
+ let arena = HybridArena::<4096>::new();
+
+ let regions = arena.alloc_slice_fill_iter(regions.iter().map(|blit| vk::ImageBlit {
+ src_subresource: vulkan_subresource_layers(&blit.src_subresource),
+ src_offsets: [blit.src_offset_min.into(), blit.src_offset_max.into()],
+ dst_subresource: vulkan_subresource_layers(&blit.dst_subresource),
+ dst_offsets: [blit.dst_offset_min.into(), blit.dst_offset_max.into()],
+ }));
+
+ let src_image = self
+ .image_pool
+ .lock()
+ .get(src_image.0)
+ .expect("invalid src image handle")
+ .image();
+
+ let src_image_layout = match src_image_layout {
+ ImageLayout::Optimal => vk::ImageLayout::TransferSrcOptimal,
+ ImageLayout::General => vk::ImageLayout::General,
+ };
+
+ let dst_image = self
+ .image_pool
+ .lock()
+ .get(dst_image.0)
+ .expect("invalid dst image handle")
+ .image();
+
+ let dst_image_layout = match dst_image_layout {
+ ImageLayout::Optimal => vk::ImageLayout::TransferDstOptimal,
+ ImageLayout::General => vk::ImageLayout::General,
+ };
+
+ let command_buffer = self.cmd_buffer_mut(cmd_buffer).command_buffer;
+ unsafe {
+ self.device_fn.cmd_blit_image(
+ command_buffer,
+ src_image,
+ src_image_layout,
+ dst_image,
+ dst_image_layout,
+ regions,
+ vk::Filter::Linear,
+ );
+ }
+ }
+
fn cmd_set_bind_group(
&self,
frame: &Frame,
.get_mut(&swapchain)
.expect("presenting a swapchain that hasn't been acquired this frame");
- assert!(!present_swapchain.acquire.is_null());
+ assert!(
+ !present_swapchain.acquire.is_null(),
+ "acquiring a swapchain image multiple times"
+ );
present_swapchain.release = self.request_transient_semaphore(frame);
wait_semaphores.push(vk::SemaphoreSubmitInfo {
{
let frame = self.frame_mut(&mut frame);
+ self.swapchains.lock();
+
let present_swapchains = frame.present_swapchains.get_mut();
if !present_swapchains.is_empty() {
+ for present_info in present_swapchains.values() {
+ assert!(
+ !present_info.release.is_null(),
+ "swapchain image was acquired, but not consumed"
+ );
+ }
+
let windows = arena.alloc_slice_fill_iter(present_swapchains.keys().copied());
let wait_semaphores =
arena.alloc_slice_fill_iter(present_swapchains.values().map(|x| x.release));
image_format: vulkan_swapchain.surface_format.format,
image_color_space: vulkan_swapchain.surface_format.color_space,
image_extent: vk::Extent2d { width, height },
- image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT,
+ image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT
+ | vk::ImageUsageFlags::TRANSFER_SRC
+ | vk::ImageUsageFlags::TRANSFER_DST,
image_array_layers: 1,
image_sharing_mode: vk::SharingMode::Exclusive,
pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Pipeline(Handle);
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum MemoryLocation {
HostMapped,
Device,
pub extent: Extent2d,
}
+impl Scissor {
+ pub const fn new(x: i32, y: i32, width: u32, height: u32) -> Self {
+ Self {
+ offset: Offset2d { x, y },
+ extent: Extent2d { width, height },
+ }
+ }
+}
+
flags_def!(ShaderStageFlags);
impl ShaderStageFlags {
pub const VERTEX: Self = Self(1 << 0);
impl ImageUsageFlags {
pub const SAMPLED: Self = Self(1 << 0);
pub const STORAGE: Self = Self(1 << 1);
- pub const DEPTH_STENCIL: Self = Self(1 << 2);
- pub const RENDER_TARGET: Self = Self(1 << 3);
+ pub const COLOR_ATTACHMENT: Self = Self(1 << 2);
+ pub const DEPTH_STENCIL_ATTACHMENT: Self = Self(1 << 3);
pub const TRANSFER_SRC: Self = Self(1 << 4);
pub const TRANSFER_DST: Self = Self(1 << 5);
}
pub buffer_offset: u64,
pub buffer_row_length: u32,
pub buffer_image_height: u32,
- pub image_subresource_layers: ImageSubresourceLayers,
+ pub image_subresource: ImageSubresourceLayers,
pub image_offset: Offset3d,
pub image_extent: Extent3d,
}
+pub struct ImageBlit {
+ pub src_subresource: ImageSubresourceLayers,
+ pub src_offset_min: Offset3d,
+ pub src_offset_max: Offset3d,
+ pub dst_subresource: ImageSubresourceLayers,
+ pub dst_offset_min: Offset3d,
+ pub dst_offset_max: Offset3d,
+}
+
pub struct ShaderDesc<'a> {
pub entry: &'a CStr,
pub code: &'a [u8],
CounterClockwise,
}
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum BlendMode {
+ Opaque,
+ Mask,
+ Translucent,
+ Premultiplied,
+ Additive,
+ Modulate,
+}
+
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum CompareOp {
Never,
pub polygon_mode: PolygonMode,
pub culling_mode: CullingMode,
pub front_face: FrontFace,
+ pub blend_mode: BlendMode,
pub depth_bias: Option<DepthBias>,
pub depth_compare_op: CompareOp,
pub depth_test_enable: bool,
copies: &[BufferImageCopy],
);
+ fn cmd_blit_image(
+ &self,
+ cmd_buffer: &mut CmdBuffer,
+ src_image: Image,
+ src_image_layout: ImageLayout,
+ dst_image: Image,
+ dst_image_layout: ImageLayout,
+ regions: &[ImageBlit],
+ );
+
fn cmd_begin_rendering(&self, cmd_buffer: &mut CmdBuffer, desc: &RenderingDesc);
fn cmd_end_rendering(&self, cmd_buffer: &mut CmdBuffer);
use narcissus_core::{cstr, default, obj, rand::Pcg64};
use narcissus_gpu::{
create_device, Access, Bind, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType,
- Buffer, BufferDesc, BufferImageCopy, BufferUsageFlags, ClearValue, CompareOp, CullingMode,
- Device, Extent2d, Extent3d, FrontFace, GraphicsPipelineDesc, GraphicsPipelineLayout, Image,
- ImageBarrier, ImageDesc, ImageDimension, ImageFormat, ImageLayout, ImageUsageFlags, IndexType,
- LoadOp, MemoryLocation, Offset2d, Offset3d, PolygonMode, RenderingAttachment, RenderingDesc,
- SamplerAddressMode, SamplerDesc, SamplerFilter, Scissor, ShaderDesc, ShaderStageFlags, StoreOp,
- ThreadToken, Topology, TypedBind, Viewport,
+ BlendMode, Buffer, BufferDesc, BufferImageCopy, BufferUsageFlags, ClearValue, CompareOp,
+ CullingMode, Device, Extent2d, Extent3d, FrontFace, GraphicsPipelineDesc,
+ GraphicsPipelineLayout, Image, ImageBarrier, ImageDesc, ImageDimension, ImageFormat,
+ ImageLayout, ImageUsageFlags, IndexType, LoadOp, MemoryLocation, Offset2d, Offset3d,
+ PolygonMode, RenderingAttachment, RenderingDesc, SamplerAddressMode, SamplerDesc,
+ SamplerFilter, Scissor, ShaderDesc, ShaderStageFlags, StoreOp, ThreadToken, Topology,
+ TypedBind, Viewport,
};
use narcissus_image as image;
use narcissus_maths::{
buffer_offset: 0,
buffer_row_length: 0,
buffer_image_height: 0,
- image_subresource_layers: default(),
+ image_subresource: default(),
image_offset: Offset3d { x: 0, y: 0, z: 0 },
image_extent: Extent3d {
width,
polygon_mode: PolygonMode::Fill,
culling_mode: CullingMode::Back,
front_face: FrontFace::CounterClockwise,
+ blend_mode: BlendMode::Opaque,
depth_bias: None,
depth_compare_op: CompareOp::GreaterOrEqual,
depth_test_enable: true,
device.destroy_image(&frame, depth_image);
depth_image = device.create_image(&ImageDesc {
location: MemoryLocation::HostMapped,
- usage: ImageUsageFlags::DEPTH_STENCIL,
+ usage: ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
dimension: ImageDimension::Type2d,
format: ImageFormat::DEPTH_F32,
initial_layout: ImageLayout::Optimal,