basalt 0.19.1

A window/ui framework built upon vulkan.
Documentation
pub(crate) mod composer;
mod final_fs;
mod layer_desc_pool;
mod layer_fs;
mod layer_vs;
mod pipeline;
mod square_vs;

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use std::time::Duration;

use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer};
use vulkano::device::Device;
use vulkano::format::{Format, Format as VkFormat};
use vulkano::image::view::ImageView;
use vulkano::image::{ImageDimensions, SwapchainImage};

use self::composer::{Composer, ComposerEv, ComposerView};
use self::pipeline::{ItfPipeline, ItfPipelineInit};
use crate::atlas::Atlas;
use crate::image_view::BstImageView;
use crate::vulkano::image::ImageAccess;
use crate::{BstMSAALevel, BstOptions};

pub(super) struct ItfRenderer {
    composer: Arc<Composer>,
    msaa: BstMSAALevel,
    target_info: ItfDrawTargetInfo,
    composer_view: Option<Arc<ComposerView>>,
    pipeline: ItfPipeline,
    conservative_draw: bool,
}

pub(super) struct ItfRendererInit {
    pub options: BstOptions,
    pub device: Arc<Device>,
    pub itf_format: VkFormat,
    pub atlas: Arc<Atlas>,
    pub composer: Arc<Composer>,
}

#[derive(Clone, PartialEq, Eq)]
enum ItfDrawTargetInfo {
    None,
    Image {
        extent: [u32; 2],
        msaa: BstMSAALevel,
    },
    Swapchain {
        extent: [u32; 2],
        image_count: usize,
        hash: u64,
        msaa: BstMSAALevel,
    },
    SwapchainWithSource {
        extent: [u32; 2],
        image_count: usize,
        hash: u64,
        msaa: BstMSAALevel,
    },
}

impl ItfDrawTargetInfo {
    #[inline]
    fn extent(&self) -> [u32; 2] {
        match self {
            Self::None => unreachable!(),
            Self::Image {
                extent, ..
            } => *extent,
            Self::Swapchain {
                extent, ..
            } => *extent,
            Self::SwapchainWithSource {
                extent, ..
            } => *extent,
        }
    }

    #[inline]
    fn num_images(&self) -> usize {
        match self {
            Self::None => unreachable!(),
            Self::Image {
                ..
            } => 1,
            Self::Swapchain {
                image_count, ..
            } => *image_count,
            Self::SwapchainWithSource {
                image_count, ..
            } => *image_count,
        }
    }

    #[inline]
    fn msaa(&self) -> BstMSAALevel {
        match self {
            Self::None => unreachable!(),
            Self::Image {
                msaa, ..
            } => *msaa,
            Self::Swapchain {
                msaa, ..
            } => *msaa,
            Self::SwapchainWithSource {
                msaa, ..
            } => *msaa,
        }
    }
}

pub enum ItfDrawTarget {
    Image {
        extent: [u32; 2],
    },
    Swapchain {
        images: Vec<Arc<ImageView<SwapchainImage>>>,
        image_num: usize,
    },
    SwapchainWithSource {
        source: Arc<BstImageView>,
        images: Vec<Arc<ImageView<SwapchainImage>>>,
        image_num: usize,
    },
}

impl ItfDrawTarget {
    #[inline]
    fn image_num(&self) -> usize {
        match self {
            Self::Image {
                ..
            } => 0,
            Self::Swapchain {
                image_num, ..
            } => *image_num,
            Self::SwapchainWithSource {
                image_num, ..
            } => *image_num,
        }
    }

    #[inline]
    fn format(&self, default: VkFormat) -> Format {
        match self {
            Self::Image {
                ..
            } => default,
            Self::Swapchain {
                images, ..
            } => images[0].image().format(),
            Self::SwapchainWithSource {
                images, ..
            } => images[0].image().format(),
        }
    }

    #[inline]
    fn is_swapchain(&self) -> bool {
        match self {
            Self::Image {
                ..
            } => false,
            Self::Swapchain {
                ..
            } => true,
            Self::SwapchainWithSource {
                ..
            } => true,
        }
    }

    #[inline]
    fn swapchain_image(&self, i: usize) -> Arc<ImageView<SwapchainImage>> {
        match self {
            Self::Image {
                ..
            } => unreachable!(),
            Self::Swapchain {
                images, ..
            } => images[i].clone(),
            Self::SwapchainWithSource {
                images, ..
            } => images[i].clone(),
        }
    }

    #[inline]
    fn source_image(&self) -> Option<Arc<BstImageView>> {
        match self {
            Self::Image {
                ..
            } => None,
            Self::Swapchain {
                ..
            } => None,
            Self::SwapchainWithSource {
                source, ..
            } => Some(source.clone()),
        }
    }
}

impl ItfRenderer {
    pub fn new(init: ItfRendererInit) -> Self {
        let ItfRendererInit {
            options,
            device,
            itf_format,
            atlas,
            composer,
        } = init;

        Self {
            composer,
            msaa: options.msaa,
            conservative_draw: options.conservative_draw && options.app_loop,
            target_info: ItfDrawTargetInfo::None,
            composer_view: None,
            pipeline: ItfPipeline::new(ItfPipelineInit {
                options,
                device,
                atlas,
                itf_format,
            }),
        }
    }

    pub fn msaa_mut_ref(&mut self) -> &mut BstMSAALevel {
        &mut self.msaa
    }

    pub fn draw(
        &mut self,
        cmd: AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
        target: ItfDrawTarget,
    ) -> (
        AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
        Option<Arc<BstImageView>>,
    ) {
        let mut recreate_pipeline = false;

        let target_info = match &target {
            ItfDrawTarget::Image {
                extent, ..
            } => {
                ItfDrawTargetInfo::Image {
                    extent: *extent,
                    msaa: self.msaa,
                }
            },
            ItfDrawTarget::Swapchain {
                images, ..
            } => {
                let mut hasher = DefaultHasher::new();

                for img in images.iter() {
                    img.image().hash(&mut hasher);
                }

                let extent = match images[0].image().dimensions() {
                    ImageDimensions::Dim2d {
                        width,
                        height,
                        ..
                    } => [width, height],
                    _ => unreachable!(),
                };

                ItfDrawTargetInfo::Swapchain {
                    extent,
                    image_count: images.len(),
                    hash: hasher.finish(),
                    msaa: self.msaa,
                }
            },
            ItfDrawTarget::SwapchainWithSource {
                images,
                source,
                ..
            } => {
                let mut hasher = DefaultHasher::new();

                for img in images.iter() {
                    img.image().hash(&mut hasher);
                }

                (source as &(dyn ImageAccess)).hash(&mut hasher);

                let extent = match images[0].image().dimensions() {
                    ImageDimensions::Dim2d {
                        width,
                        height,
                        ..
                    } => [width, height],
                    _ => unreachable!(),
                };

                ItfDrawTargetInfo::SwapchainWithSource {
                    extent,
                    image_count: images.len(),
                    hash: hasher.finish(),
                    msaa: self.msaa,
                }
            },
        };

        if target_info != self.target_info {
            if self.target_info != ItfDrawTargetInfo::None
                && target_info.extent() != self.target_info.extent()
            {
                self.composer
                    .send_event(ComposerEv::Extent(target_info.extent()));
            }

            self.target_info = target_info;
            recreate_pipeline = true;
        }

        let wait_for = if self.conservative_draw {
            Some(Duration::from_secs(1))
        } else {
            None
        };

        self.composer_view = Some(
            self.composer
                .update_view(self.composer_view.take(), wait_for),
        );

        self.pipeline.draw(
            recreate_pipeline,
            self.composer_view.as_ref().unwrap(),
            target,
            &self.target_info,
            cmd,
        )
    }
}