use crate::{
BindingType, BlendMode, BufferUsageFlags, ClearValue, CompareOp, CullingMode, FrontFace,
ImageAspectFlags, ImageDimension, ImageFormat, ImageSubresourceLayers, ImageSubresourceRange,
- ImageTiling, IndexType, LoadOp, PolygonMode, ShaderStageFlags, StencilOp, StencilOpState,
- StoreOp, Topology,
+ ImageTiling, ImageUsageFlags, IndexType, LoadOp, PolygonMode, ShaderStageFlags, StencilOp,
+ StencilOpState, StoreOp, Topology,
};
#[must_use]
usage_flags
}
+pub fn vulkan_image_usage_flags(usage: ImageUsageFlags) -> vk::ImageUsageFlags {
+ let mut usage_flags = vk::ImageUsageFlags::default();
+ if usage.contains(ImageUsageFlags::SAMPLED) {
+ usage_flags |= vk::ImageUsageFlags::SAMPLED;
+ }
+ if usage.contains(ImageUsageFlags::STORAGE) {
+ usage_flags |= vk::ImageUsageFlags::STORAGE;
+ }
+ if usage.contains(ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) {
+ usage_flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
+ }
+ if usage.contains(ImageUsageFlags::COLOR_ATTACHMENT) {
+ usage_flags |= vk::ImageUsageFlags::COLOR_ATTACHMENT;
+ }
+ if usage.contains(ImageUsageFlags::TRANSFER) {
+ usage_flags |= vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC;
+ }
+ usage_flags
+}
+
#[must_use]
pub fn vulkan_clear_value(clear_value: ClearValue) -> vk::ClearValue {
match clear_value {
ImageBarrier, ImageBlit, ImageDesc, ImageDimension, ImageFormat, ImageLayout, ImageTiling,
ImageUsageFlags, ImageViewDesc, IndexType, MemoryLocation, Offset2d, Offset3d,
PersistentBuffer, Pipeline, Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc,
- SamplerFilter, SwapchainOutOfDateError, ThreadToken, TransientBuffer, TypedBind,
+ SamplerFilter, SwapchainImage, SwapchainOutOfDateError, ThreadToken, TransientBuffer,
+ TypedBind,
};
mod allocator;
};
let tiling = vulkan_image_tiling(desc.tiling);
-
- let mut usage = default();
- if desc.usage.contains(ImageUsageFlags::SAMPLED) {
- usage |= vk::ImageUsageFlags::SAMPLED;
- }
- if desc.usage.contains(ImageUsageFlags::STORAGE) {
- usage |= vk::ImageUsageFlags::STORAGE;
- }
- if desc
- .usage
- .contains(ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
- {
- usage |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
- }
- if desc.usage.contains(ImageUsageFlags::COLOR_ATTACHMENT) {
- usage |= vk::ImageUsageFlags::COLOR_ATTACHMENT;
- }
- if desc.usage.contains(ImageUsageFlags::TRANSFER) {
- usage |= vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC;
- }
+ let usage = vulkan_image_usage_flags(desc.usage);
let queue_family_indices = &[self.universal_queue_family_index];
let create_info = vk::ImageCreateInfo {
window: &dyn AsRawWindow,
width: u32,
height: u32,
- format: ImageFormat,
- ) -> Result<(u32, u32, Image), SwapchainOutOfDateError> {
- self.acquire_swapchain(frame, window, width, height, format)
+ usage: ImageUsageFlags,
+ formats: &[ImageFormat],
+ images: &mut [Image],
+ ) -> Result<SwapchainImage, SwapchainOutOfDateError> {
+ self.acquire_swapchain(frame, window, width, height, usage, formats, images)
}
fn destroy_swapchain(&self, window: &dyn AsRawWindow) {
use vulkan_sys as vk;
use crate::{
- backend::vulkan::{vk_vec, vulkan_format, VulkanImageHolder, VulkanImageSwapchain},
- vk_check, Frame, Image, ImageFormat, SwapchainOutOfDateError,
+ backend::vulkan::{
+ vk_vec, vulkan_format, vulkan_image_usage_flags, VulkanImageHolder, VulkanImageSwapchain,
+ },
+ vk_check, Frame, Image, ImageFormat, ImageUsageFlags, SwapchainImage, SwapchainOutOfDateError,
};
use super::{VulkanDevice, VulkanFrame, VULKAN_CONSTANTS};
xcb: bool,
surface_maintenance1: bool,
swapchain_maintenance1: bool,
+ swapchain_mutable_format: bool,
}
struct RecycleSwapchainSemaphore {
wsi_support.swapchain_maintenance1 = true;
enabled_extensions.push(extension_name);
}
+ "VK_KHR_swapchain_mutable_format" => {
+ wsi_support.swapchain_mutable_format = true;
+ enabled_extensions.push(extension_name);
+ }
_ => {}
}
}
assert!(khr_swapchain_support);
+ assert!(wsi_support.swapchain_mutable_format);
}
pub fn new(
window: &dyn AsRawWindow,
width: u32,
height: u32,
- format: ImageFormat,
- ) -> Result<(u32, u32, Image), SwapchainOutOfDateError> {
+ usage: ImageUsageFlags,
+ formats: &[ImageFormat],
+ images: &mut [Image],
+ ) -> 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
.xcb_surface_fn
.as_ref()
.unwrap()
- .create_xcb_surface(self.instance, &create_info, None, &mut surface,));
+ .create_xcb_surface(self.instance, &create_info, None, &mut surface));
surface
}
RawWindow::Xlib(xlib) => {
.xlib_surface_fn
.as_ref()
.unwrap()
- .create_xlib_surface(self.instance, &create_info, None, &mut surface,));
+ .create_xlib_surface(self.instance, &create_info, None, &mut surface));
surface
}
RawWindow::Wayland(wayland) => {
.wayland_surface_fn
.as_ref()
.unwrap()
- .create_wayland_surface(self.instance, &create_info, None, &mut surface,));
+ .create_wayland_surface(self.instance, &create_info, None, &mut surface));
surface
}
});
- let format = vulkan_format(format);
+ 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(|| {
"universal queue does not support presenting this surface"
);
- let formats = vk_vec(|count, ptr| unsafe {
+ let surface_formats = vk_vec(|count, ptr| unsafe {
self.wsi.surface_fn.get_physical_device_surface_formats(
self.physical_device,
surface,
&mut capabilities
));
- let surface_format = formats
+ let surface_format = surface_formats
.iter()
.copied()
.find(|&x| x.format == format)
VulkanSwapchain {
surface_format,
state: VulkanSwapchainState::Vacant,
- _formats: formats,
+ _formats: surface_formats,
_present_modes: present_modes,
capabilities,
}
});
- assert_eq!(format, vulkan_swapchain.surface_format.format);
+ 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 create_info = vk::SwapchainCreateInfoKHR {
+
+ let format_list_create_info = vk::ImageFormatListCreateInfo {
+ view_formats: formats.as_slice().into(),
+ ..default()
+ };
+ let mut 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: vk::ImageUsageFlags::COLOR_ATTACHMENT
- | vk::ImageUsageFlags::TRANSFER_SRC
- | vk::ImageUsageFlags::TRANSFER_DST,
+ image_usage: vulkan_image_usage_flags(usage),
image_array_layers: 1,
image_sharing_mode: vk::SharingMode::Exclusive,
pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
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,
));
assert!(!new_swapchain.is_null());
- let images = vk_vec(|count, ptr| unsafe {
+ let swapchain_images = vk_vec(|count, ptr| unsafe {
self.wsi.swapchain_fn.get_swapchain_images(
self.device,
new_swapchain,
)
});
- let image_views = images
- .iter()
- .map(|&image| {
+ let mut image_views =
+ Vec::with_capacity(swapchain_images.len() * formats.len());
+
+ for &swapchain_image in &swapchain_images {
+ for &format in &formats {
let create_info = vk::ImageViewCreateInfo {
- image,
+ image: swapchain_image,
view_type: vk::ImageViewType::Type2d,
- format: vulkan_swapchain.surface_format.format,
+ format,
subresource_range: vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
let handle = image_pool.insert(VulkanImageHolder::Swapchain(
VulkanImageSwapchain {
surface,
- image,
+ image: swapchain_image,
view,
},
));
- Image(handle)
- })
- .collect::<Box<_>>();
+
+ image_views.push(Image(handle));
+ }
+ }
+
+ let image_views = image_views.into_boxed_slice();
vulkan_swapchain.state = VulkanSwapchainState::Occupied {
width,
present_info.acquire = acquire;
present_info.image_index = image_index;
present_info.swapchain = swapchain;
- let view = image_views[image_index.widen()];
- return Ok((width, height, view));
+ 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 });
}
}
}
impl std::error::Error for SwapchainOutOfDateError {}
+pub struct SwapchainImage {
+ pub width: u32,
+ pub height: u32,
+}
+
pub trait Device {
fn create_buffer(&self, desc: &BufferDesc) -> Buffer;
fn create_persistent_buffer<'device>(
window: &dyn AsRawWindow,
width: u32,
height: u32,
- format: ImageFormat,
- ) -> Result<(u32, u32, Image), SwapchainOutOfDateError>;
+ usage: ImageUsageFlags,
+ formats: &[ImageFormat],
+ images: &mut [Image],
+ ) -> Result<SwapchainImage, SwapchainOutOfDateError>;
fn destroy_swapchain(&self, window: &dyn AsRawWindow);
DeviceExt, Extent2d, Extent3d, Frame, Image, ImageAspectFlags, ImageBarrier, ImageDesc,
ImageDimension, ImageFormat, ImageLayout, ImageTiling, ImageUsageFlags, IndexType, LoadOp,
MemoryLocation, Offset2d, PersistentBuffer, RenderingAttachment, RenderingDesc, Scissor,
- StoreOp, ThreadToken, TypedBind, Viewport,
+ StoreOp, SwapchainImage, ThreadToken, TypedBind, Viewport,
};
use narcissus_image as image;
use narcissus_maths::{
usage: ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER,
host_mapped: false,
dimension: ImageDimension::Type2d,
- format: ImageFormat::R8_UNORM,
+ format: ImageFormat::R8_SRGB,
tiling: ImageTiling::Optimal,
width: ui_state.glyph_cache.width() as u32,
height: ui_state.glyph_cache.height() as u32,
height: 600,
});
- let scale = 2.0;
+ let scale = 1.0;
let thread_token = ThreadToken::new();
let thread_token = &thread_token;
{
let frame = &frame;
- let (width, height, swapchain_image) = loop {
- let (width, height) = window.drawable_extent();
+ let formats = &[ImageFormat::RGBA8_SRGB];
+ let mut swapchain_images = [Image::default(); 1];
+
+ let SwapchainImage { width, height } = loop {
+ let (_width, height) = window.extent();
+ let (drawable_width, drawable_height) = window.drawable_extent();
+
+ ui_state.scale = drawable_height as f32 / height as f32;
if let Ok(result) = gpu.acquire_swapchain(
frame,
window.upcast(),
- width,
- height,
- ImageFormat::BGRA8_SRGB,
+ drawable_width,
+ drawable_height,
+ ImageUsageFlags::COLOR_ATTACHMENT,
+ formats,
+ &mut swapchain_images,
) {
break result;
}
};
+ let [swapchain_image_srgb] = swapchain_images;
+
let tick_start = Instant::now();
'tick: loop {
'poll_events: while let Some(event) = app.poll_event() {
&game_state,
width,
height,
- swapchain_image,
+ swapchain_image_srgb,
);
}
},
bind_group_layouts: &[uniforms_bind_group_layout, storage_bind_group_layout],
layout: GraphicsPipelineLayout {
- color_attachment_formats: &[ImageFormat::BGRA8_SRGB],
+ color_attachment_formats: &[ImageFormat::RGBA8_SRGB],
depth_attachment_format: Some(ImageFormat::DEPTH_F32),
stencil_attachment_format: None,
},
},
bind_group_layouts: &[bind_group_layout],
layout: GraphicsPipelineLayout {
- color_attachment_formats: &[ImageFormat::BGRA8_SRGB],
+ color_attachment_formats: &[ImageFormat::RGBA8_SRGB],
depth_attachment_format: Some(ImageFormat::DEPTH_F32),
stencil_attachment_format: None,
},