use vulkan_sys as vk;
use crate::{
- BindingType, BlendMode, BufferUsageFlags, ClearValue, CompareOp, CullingMode, FrontFace,
- ImageAspectFlags, ImageDimension, ImageFormat, ImageSubresourceLayers, ImageSubresourceRange,
- ImageTiling, ImageUsageFlags, IndexType, LoadOp, PolygonMode, ShaderStageFlags, StencilOp,
- StencilOpState, StoreOp, Topology,
+ BindingType, BlendMode, BufferUsageFlags, ClearValue, ColorSpace, CompareOp, CullingMode,
+ FrontFace, ImageAspectFlags, ImageDimension, ImageFormat, ImageSubresourceLayers,
+ ImageSubresourceRange, ImageTiling, ImageUsageFlags, IndexType, LoadOp, PolygonMode,
+ PresentMode, ShaderStageFlags, StencilOp, StencilOpState, StoreOp, Topology,
};
#[must_use]
}
}
+#[must_use]
+pub fn vulkan_color_space(color_space: ColorSpace) -> vk::ColorSpaceKHR {
+ match color_space {
+ ColorSpace::Srgb => vk::ColorSpaceKHR::SrgbNonlinearKhr,
+ }
+}
+
#[must_use]
pub fn vulkan_format(format: ImageFormat) -> vk::Format {
match format {
aspect_flags
}
+pub fn vulkan_present_mode(present_mode: PresentMode) -> vk::PresentModeKHR {
+ match present_mode {
+ PresentMode::Immediate => vk::PresentModeKHR::Immediate,
+ PresentMode::Mailbox => vk::PresentModeKHR::Mailbox,
+ PresentMode::Fifo => vk::PresentModeKHR::Fifo,
+ PresentMode::FifoRelaxed => vk::PresentModeKHR::FifoRelaxed,
+ }
+}
+
pub fn vulkan_buffer_usage_flags(usage: BufferUsageFlags) -> vk::BufferUsageFlags {
let mut usage_flags = vk::BufferUsageFlags::default();
if usage.contains(BufferUsageFlags::UNIFORM) {
usage_flags
}
+pub fn from_vulkan_image_usage_flags(usage: vk::ImageUsageFlags) -> ImageUsageFlags {
+ let mut usage_flags = ImageUsageFlags::default();
+ if usage.contains(vk::ImageUsageFlags::SAMPLED) {
+ usage_flags |= ImageUsageFlags::SAMPLED;
+ }
+ if usage.contains(vk::ImageUsageFlags::STORAGE) {
+ usage_flags |= ImageUsageFlags::STORAGE;
+ }
+ if usage.contains(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) {
+ usage_flags |= ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
+ }
+ if usage.contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) {
+ usage_flags |= ImageUsageFlags::COLOR_ATTACHMENT;
+ }
+ if usage.contains(vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC) {
+ usage_flags |= ImageUsageFlags::TRANSFER;
+ }
+ usage_flags
+}
+
#[must_use]
pub fn vulkan_clear_value(clear_value: ClearValue) -> vk::ClearValue {
match clear_value {
frame_counter::FrameCounter, Bind, BindGroupLayout, BindGroupLayoutDesc, Buffer, BufferArg,
BufferDesc, BufferImageCopy, BufferUsageFlags, CmdEncoder, ComputePipelineDesc, Device,
Extent2d, Extent3d, Frame, GlobalBarrier, GpuConcurrent, GraphicsPipelineDesc, Image,
- ImageBarrier, ImageBlit, ImageDesc, ImageDimension, ImageFormat, ImageLayout, ImageTiling,
- ImageUsageFlags, ImageViewDesc, IndexType, MemoryLocation, Offset2d, Offset3d,
- PersistentBuffer, Pipeline, Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc,
- SamplerFilter, SwapchainImage, SwapchainOutOfDateError, ThreadToken, TransientBuffer,
- TypedBind,
+ ImageBarrier, ImageBlit, ImageDesc, ImageDimension, ImageLayout, ImageTiling, ImageViewDesc,
+ IndexType, MemoryLocation, Offset2d, Offset3d, PersistentBuffer, Pipeline, Sampler,
+ SamplerAddressMode, SamplerCompareOp, SamplerDesc, SamplerFilter, SwapchainConfigurator,
+ SwapchainImage, SwapchainOutOfDateError, ThreadToken, TransientBuffer, TypedBind,
};
mod allocator;
window: &dyn AsRawWindow,
width: u32,
height: u32,
- usage: ImageUsageFlags,
- formats: &[ImageFormat],
- images: &mut [Image],
+ configurator: &mut dyn SwapchainConfigurator,
) -> Result<SwapchainImage, SwapchainOutOfDateError> {
- self.acquire_swapchain(frame, window, width, height, usage, formats, images)
+ self.acquire_swapchain(frame, window, width, height, configurator)
}
fn destroy_swapchain(&self, window: &dyn AsRawWindow) {
use crate::{
backend::vulkan::{
- vk_vec, vulkan_format, vulkan_image_usage_flags, VulkanImageHolder, VulkanImageSwapchain,
+ from_vulkan_image_usage_flags, vk_vec, vulkan_color_space, vulkan_format,
+ vulkan_image_usage_flags, vulkan_present_mode, VulkanImageHolder, VulkanImageSwapchain,
},
- vk_check, Frame, Image, ImageFormat, ImageUsageFlags, SwapchainImage, SwapchainOutOfDateError,
+ vk_check, ColorSpace, Frame, Image, ImageFormat, PresentMode, SwapchainConfigurator,
+ SwapchainImage, SwapchainOutOfDateError,
};
use super::{VulkanDevice, VulkanFrame, VULKAN_CONSTANTS};
}
pub struct VulkanSwapchain {
+ present_mode: vk::PresentModeKHR,
surface_format: vk::SurfaceFormatKHR,
-
- state: VulkanSwapchainState,
-
- _formats: Box<[vk::SurfaceFormatKHR]>,
- _present_modes: Box<[vk::PresentModeKHR]>,
+ usage_flags: vk::ImageUsageFlags,
capabilities: vk::SurfaceCapabilitiesKHR,
+ state: VulkanSwapchainState,
}
#[derive(Default, Clone, Copy, Debug)]
window: &dyn AsRawWindow,
width: u32,
height: u32,
- usage: ImageUsageFlags,
- formats: &[ImageFormat],
- images: &mut [Image],
+ configurator: &mut dyn SwapchainConfigurator,
) -> Result<SwapchainImage, SwapchainOutOfDateError> {
- assert!(
- !formats.is_empty(),
- "must provide at least one swapchain format"
- );
- assert!(
- formats.len() == images.len(),
- "number of requested formats and number of output images must match"
- );
-
- if formats.len() > 1 {
- assert!(
- self.wsi.support.swapchain_mutable_format,
- "VK_KHR_swapchain_mutable_format support required for multiple swapchain formats"
- );
- }
-
let raw_window = window.as_raw_window();
let mut surfaces = self.wsi.surfaces.lock();
let surface = *surfaces
}
});
- let formats = formats
- .iter()
- .copied()
- .map(vulkan_format)
- .collect::<Vec<_>>();
- let format = formats[0];
-
let mut swapchains = self.wsi.swapchains.lock();
let vulkan_swapchain = swapchains.entry(surface).or_insert_with(|| {
let mut supported = vk::Bool32::False;
"universal queue does not support presenting this surface"
);
- let surface_formats = vk_vec(|count, ptr| unsafe {
- self.wsi.surface_fn.get_physical_device_surface_formats(
- self.physical_device,
- surface,
- count,
- ptr,
- )
- })
- .into_boxed_slice();
-
- let present_modes = vk_vec(|count, ptr| unsafe {
+ let available_present_modes = vk_vec(|count, ptr| unsafe {
self.wsi
.surface_fn
.get_physical_device_surface_present_modes(
ptr,
)
})
- .into_boxed_slice();
+ .into_iter()
+ .filter_map(|present_mode| match present_mode {
+ vk::PresentModeKHR::Immediate => Some(PresentMode::Immediate),
+ vk::PresentModeKHR::Mailbox => Some(PresentMode::Mailbox),
+ vk::PresentModeKHR::Fifo => Some(PresentMode::Fifo),
+ vk::PresentModeKHR::FifoRelaxed => Some(PresentMode::FifoRelaxed),
+ vk::PresentModeKHR::SharedDemandRefresh => None,
+ vk::PresentModeKHR::SharedContinuousRefresh => None,
+ })
+ .collect::<Vec<_>>();
+
+ let supported_surface_formats = vk_vec(|count, ptr| unsafe {
+ self.wsi.surface_fn.get_physical_device_surface_formats(
+ self.physical_device,
+ surface,
+ count,
+ ptr,
+ )
+ })
+ .into_iter()
+ .filter_map(
+ |vk::SurfaceFormatKHR {
+ format,
+ color_space,
+ }| {
+ let color_space = match color_space {
+ vk::ColorSpaceKHR::SrgbNonlinearKhr => Some(ColorSpace::Srgb),
+ _ => None,
+ }?;
+ let format = match format {
+ vk::Format::R8_SRGB => Some(ImageFormat::R8_SRGB),
+ vk::Format::R8_UNORM => Some(ImageFormat::R8_UNORM),
+ vk::Format::R8G8B8A8_SRGB => Some(ImageFormat::RGBA8_SRGB),
+ vk::Format::R8G8B8A8_UNORM => Some(ImageFormat::RGBA8_UNORM),
+ vk::Format::R16G16B16A16_SFLOAT => Some(ImageFormat::RGBA16_FLOAT),
+ vk::Format::B8G8R8A8_SRGB => Some(ImageFormat::BGRA8_SRGB),
+ vk::Format::B8G8R8A8_UNORM => Some(ImageFormat::BGRA8_UNORM),
+ vk::Format::A2R10G10B10_UNORM_PACK32 => {
+ Some(ImageFormat::A2R10G10B10_UNORM)
+ }
+ vk::Format::A2B10G10R10_UNORM_PACK32 => {
+ Some(ImageFormat::A2B10G10R10_UNORM)
+ }
+ vk::Format::E5B9G9R9_UFLOAT_PACK32 => Some(ImageFormat::E5B9G9R9_UFLOAT),
+ vk::Format::D32_SFLOAT => Some(ImageFormat::DEPTH_F32),
+ _ => None,
+ }?;
+ Some((format, color_space))
+ },
+ )
+ .collect::<Vec<_>>();
let mut capabilities = vk::SurfaceCapabilitiesKHR::default();
vk_check!(self
&mut capabilities
));
- let surface_format = surface_formats
- .iter()
- .copied()
- .find(|&x| x.format == format)
- .expect("failed to find matching surface format");
+ let supported_usage_flags =
+ from_vulkan_image_usage_flags(capabilities.supported_usage_flags);
+
+ let present_mode = configurator.choose_present_mode(&available_present_modes);
+ let (usage_flags, surface_format) = configurator
+ .choose_surface_format(supported_usage_flags, &supported_surface_formats);
+ assert!(available_present_modes.contains(&present_mode));
+ assert!((!supported_usage_flags.as_raw() & usage_flags.as_raw()) == 0);
+ assert!(supported_surface_formats
+ .iter()
+ .any(|&supported_format| { supported_format == surface_format }));
+
+ let present_mode = vulkan_present_mode(present_mode);
+ let usage_flags = vulkan_image_usage_flags(usage_flags);
+ let surface_format = vk::SurfaceFormatKHR {
+ format: vulkan_format(surface_format.0),
+ color_space: vulkan_color_space(surface_format.1),
+ };
VulkanSwapchain {
+ present_mode,
surface_format,
+ usage_flags,
state: VulkanSwapchainState::Vacant,
- _formats: surface_formats,
- _present_modes: present_modes,
capabilities,
}
});
- assert_eq!(
- format, vulkan_swapchain.surface_format.format,
- "cannot change swapchain format after creation"
- );
-
let frame = self.frame(frame);
let mut image_pool = self.image_pool.lock();
match &mut vulkan_swapchain.state {
VulkanSwapchainState::Vacant => {
let mut new_swapchain = vk::SwapchainKHR::null();
-
- let format_list_create_info = vk::ImageFormatListCreateInfo {
- view_formats: formats.as_slice().into(),
- ..default()
- };
- let mut create_info = vk::SwapchainCreateInfoKHR {
+ let create_info = vk::SwapchainCreateInfoKHR {
surface,
min_image_count: vulkan_swapchain.capabilities.min_image_count,
image_format: vulkan_swapchain.surface_format.format,
image_color_space: vulkan_swapchain.surface_format.color_space,
image_extent: vk::Extent2d { width, height },
- image_usage: vulkan_image_usage_flags(usage),
+ image_usage: vulkan_swapchain.usage_flags,
image_array_layers: 1,
image_sharing_mode: vk::SharingMode::Exclusive,
pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
- present_mode: vk::PresentModeKHR::Fifo,
+ present_mode: vulkan_swapchain.present_mode,
clipped: vk::Bool32::True,
old_swapchain,
..default()
};
- // Ask for mutable if we need it.
- if formats.len() > 1 {
- create_info._next =
- &format_list_create_info as *const _ as *const core::ffi::c_void;
- create_info.flags |= vk::SwapchainCreateFlagsKHR::MUTABLE_FORMAT;
- }
-
vk_check!(self.wsi.swapchain_fn.create_swapchain(
self.device,
&create_info,
)
});
- let mut image_views =
- Vec::with_capacity(swapchain_images.len() * formats.len());
-
- for &swapchain_image in &swapchain_images {
- for &format in &formats {
+ let image_views = swapchain_images
+ .into_iter()
+ .map(|swapchain_image| {
let create_info = vk::ImageViewCreateInfo {
image: swapchain_image,
view_type: vk::ImageViewType::Type2d,
- format,
+ format: vulkan_swapchain.surface_format.format,
subresource_range: vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
},
));
- image_views.push(Image(handle));
- }
- }
-
- let image_views = image_views.into_boxed_slice();
+ Image(handle)
+ })
+ .collect::<Vec<_>>()
+ .into_boxed_slice();
vulkan_swapchain.state = VulkanSwapchainState::Occupied {
width,
present_info.image_index = image_index;
present_info.swapchain = swapchain;
- let base_index = image_index.widen() * formats.len();
- images.copy_from_slice(&image_views[base_index..base_index + formats.len()]);
-
- return Ok(SwapchainImage { width, height });
+ return Ok(SwapchainImage {
+ width,
+ height,
+ image: image_views[image_index.widen()],
+ });
}
}
}
if let Some(VulkanSwapchain {
surface_format: _,
+ present_mode: _,
+ usage_flags: _,
state,
- _formats: _,
- _present_modes: _,
capabilities: _,
}) = self.wsi.swapchains.lock().remove(&surface)
{
Device,
}
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum PresentMode {
+ Immediate,
+ Mailbox,
+ Fifo,
+ FifoRelaxed,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum ColorSpace {
+ Srgb,
+}
+
#[repr(C)]
pub struct Viewport {
pub x: f32,
TypeCube,
}
-#[derive(Clone, Copy, PartialEq, Eq)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(non_camel_case_types)]
pub enum ImageFormat {
R8_SRGB,
pub struct SwapchainImage {
pub width: u32,
pub height: u32,
+ pub image: Image,
+}
+
+pub trait SwapchainConfigurator {
+ fn choose_present_mode(&mut self, supported_present_modes: &[PresentMode]) -> PresentMode;
+ fn choose_surface_format(
+ &mut self,
+ supported_usage_flags: ImageUsageFlags,
+ supported_surface_formats: &[(ImageFormat, ColorSpace)],
+ ) -> (ImageUsageFlags, (ImageFormat, ColorSpace));
}
pub trait Device {
window: &dyn AsRawWindow,
width: u32,
height: u32,
- usage: ImageUsageFlags,
- formats: &[ImageFormat],
- images: &mut [Image],
+ configurator: &mut dyn SwapchainConfigurator,
) -> Result<SwapchainImage, SwapchainOutOfDateError>;
fn destroy_swapchain(&self, window: &dyn AsRawWindow);
use narcissus_core::{box_assume_init, default, rand::Pcg64, zeroed_box, BitIter};
use narcissus_font::{FontCollection, GlyphCache, HorizontalMetrics};
use narcissus_gpu::{
- create_device, Access, Bind, BufferImageCopy, BufferUsageFlags, ClearValue, CmdEncoder, Device,
- DeviceExt, Extent2d, Extent3d, Frame, Image, ImageAspectFlags, ImageBarrier, ImageDesc,
- ImageDimension, ImageFormat, ImageLayout, ImageSubresourceRange, ImageTiling, ImageUsageFlags,
- IndexType, LoadOp, MemoryLocation, Offset2d, PersistentBuffer, RenderingAttachment,
- RenderingDesc, Scissor, StoreOp, SwapchainImage, ThreadToken, TypedBind, Viewport,
+ create_device, Access, Bind, BufferImageCopy, BufferUsageFlags, ClearValue, CmdEncoder,
+ ColorSpace, Device, DeviceExt, Extent2d, Extent3d, Frame, Image, ImageAspectFlags,
+ ImageBarrier, ImageDesc, ImageDimension, ImageFormat, ImageLayout, ImageSubresourceRange,
+ ImageTiling, ImageUsageFlags, IndexType, LoadOp, MemoryLocation, Offset2d, PersistentBuffer,
+ PresentMode, RenderingAttachment, RenderingDesc, Scissor, StoreOp, SwapchainConfigurator,
+ SwapchainImage, ThreadToken, TypedBind, Viewport,
};
use narcissus_image as image;
use narcissus_maths::{
let mut last_frame = Instant::now();
let mut tick_accumulator = target_dt;
+ struct Configurator();
+ impl SwapchainConfigurator for Configurator {
+ fn choose_present_mode(&mut self, _available_present_modes: &[PresentMode]) -> PresentMode {
+ PresentMode::Fifo
+ }
+
+ fn choose_surface_format(
+ &mut self,
+ _available_usage_flags: ImageUsageFlags,
+ available_surface_formats: &[(ImageFormat, ColorSpace)],
+ ) -> (ImageUsageFlags, (ImageFormat, ColorSpace)) {
+ let image_usage_flags = ImageUsageFlags::STORAGE;
+
+ if let Some(&swapchain_format) =
+ available_surface_formats
+ .iter()
+ .find(|(image_format, _color_space)| {
+ image_format == &ImageFormat::A2R10G10B10_UNORM
+ })
+ {
+ return (image_usage_flags, swapchain_format);
+ }
+
+ if let Some(&swapchain_format) = available_surface_formats
+ .iter()
+ .find(|(image_format, _color_space)| image_format == &ImageFormat::BGRA8_UNORM)
+ {
+ return (image_usage_flags, swapchain_format);
+ }
+
+ // Default
+ (
+ image_usage_flags,
+ (ImageFormat::RGBA8_UNORM, ColorSpace::Srgb),
+ )
+ }
+ }
+ let mut swapchain_configurator = Configurator();
+
'main: loop {
let frame = gpu.begin_frame();
{
let frame = &frame;
- let formats = &[ImageFormat::A2R10G10B10_UNORM];
- let mut swapchain_images = [Image::default(); 1];
-
- let SwapchainImage { width, height } = loop {
+ let SwapchainImage {
+ width,
+ height,
+ image: swapchain_image,
+ } = loop {
let (_width, height) = window.extent();
let (drawable_width, drawable_height) = window.drawable_extent();
window.upcast(),
drawable_width,
drawable_height,
- ImageUsageFlags::COLOR_ATTACHMENT | ImageUsageFlags::STORAGE,
- formats,
- &mut swapchain_images,
+ &mut swapchain_configurator,
) {
break result;
}
};
- let [swapchain_image_unorm] = swapchain_images;
-
let tick_start = Instant::now();
'tick: loop {
'poll_events: while let Some(event) = app.poll_event() {
&game_state,
width,
height,
- swapchain_image_unorm,
+ swapchain_image,
);
}