]> git.nega.tv - josh/narcissus/commitdiff
narcissus-gpu: Configure swapchain with available modes
authorJosh Simmons <josh@nega.tv>
Tue, 14 May 2024 19:49:53 +0000 (21:49 +0200)
committerJosh Simmons <josh@nega.tv>
Tue, 14 May 2024 19:49:53 +0000 (21:49 +0200)
engine/narcissus-gpu/src/backend/vulkan/convert.rs
engine/narcissus-gpu/src/backend/vulkan/mod.rs
engine/narcissus-gpu/src/backend/vulkan/wsi.rs
engine/narcissus-gpu/src/lib.rs
title/shark/src/main.rs

index d2ee3ebc4ab3482a12e4f2b84bef3f0dc205dec9..e94f74766792f0fe3515d80b6042c105bdae6404 100644 (file)
@@ -4,10 +4,10 @@ use narcissus_core::default;
 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]
@@ -18,6 +18,13 @@ pub fn vulkan_bool32(b: bool) -> vk::Bool32 {
     }
 }
 
+#[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 {
@@ -65,6 +72,15 @@ pub fn vulkan_aspect(aspect: ImageAspectFlags) -> vk::ImageAspectFlags {
     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) {
@@ -102,6 +118,26 @@ pub fn vulkan_image_usage_flags(usage: ImageUsageFlags) -> vk::ImageUsageFlags {
     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 {
index b7e1b0295b2fb64299a59fe004d1f68c2b05d0f8..a5743c2daa733c15c090961b08bc9e81c3db3afb 100644 (file)
@@ -19,11 +19,10 @@ use crate::{
     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;
@@ -2414,11 +2413,9 @@ impl Device for VulkanDevice {
         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) {
index 33352efa96c9adac4b0781e1fccd45a82ddd5256..a806230152c37da9bd3ec04aacb816c2f7460d5c 100644 (file)
@@ -12,9 +12,11 @@ use vulkan_sys as vk;
 
 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};
@@ -38,13 +40,11 @@ enum VulkanSwapchainState {
 }
 
 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)]
@@ -214,26 +214,8 @@ impl VulkanDevice {
         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
@@ -286,13 +268,6 @@ impl VulkanDevice {
                 }
             });
 
-        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;
@@ -309,17 +284,7 @@ impl VulkanDevice {
                 "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(
@@ -329,7 +294,57 @@ impl VulkanDevice {
                         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
@@ -341,26 +356,34 @@ impl VulkanDevice {
                     &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();
 
@@ -395,35 +418,23 @@ impl VulkanDevice {
             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,
@@ -441,15 +452,13 @@ impl VulkanDevice {
                         )
                     });
 
-                    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,
@@ -475,11 +484,10 @@ impl VulkanDevice {
                                 },
                             ));
 
-                            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,
@@ -569,10 +577,11 @@ impl VulkanDevice {
                     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()],
+                    });
                 }
             }
         }
@@ -587,9 +596,9 @@ impl VulkanDevice {
 
         if let Some(VulkanSwapchain {
             surface_format: _,
+            present_mode: _,
+            usage_flags: _,
             state,
-            _formats: _,
-            _present_modes: _,
             capabilities: _,
         }) = self.wsi.swapchains.lock().remove(&surface)
         {
index 491c788a14c79cda62b066831a193b81444f8291..95404a447da8e90063436545f1f960ec7a156411 100644 (file)
@@ -79,6 +79,19 @@ pub enum MemoryLocation {
     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,
@@ -120,7 +133,7 @@ pub enum ImageDimension {
     TypeCube,
 }
 
-#[derive(Clone, Copy, PartialEq, Eq)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
 #[allow(non_camel_case_types)]
 pub enum ImageFormat {
     R8_SRGB,
@@ -704,6 +717,16 @@ impl std::error::Error for SwapchainOutOfDateError {}
 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 {
@@ -732,9 +755,7 @@ 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);
index 624197f609642d6d4f9c2bb90b82d2596b4f1d90..01b4affb5933edcba8e14e80359d76399be49fa6 100644 (file)
@@ -11,11 +11,12 @@ use narcissus_app::{create_app, Event, Key, PressedState, WindowDesc};
 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::{
@@ -1364,15 +1365,55 @@ pub fn main() {
     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();
 
@@ -1383,16 +1424,12 @@ pub fn main() {
                     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() {
@@ -1475,7 +1512,7 @@ pub fn main() {
                 &game_state,
                 width,
                 height,
-                swapchain_image_unorm,
+                swapchain_image,
             );
         }