jay-compositor 1.15.0

The Jay compositor
use crate::gfx_api::AlphaMode;
use crate::gfx_apis::vulkan::VulkanError;
use crate::gfx_apis::vulkan::descriptor::VulkanDescriptorSetLayout;
use crate::gfx_apis::vulkan::device::VulkanDevice;
use crate::gfx_apis::vulkan::shaders::VulkanShader;
use arrayvec::ArrayVec;
use ash::vk;
use ash::vk::BlendFactor;
use ash::vk::BlendOp;
use ash::vk::ColorComponentFlags;
use ash::vk::CullModeFlags;
use ash::vk::DescriptorSetAndBindingMappingEXT;
use ash::vk::DynamicState;
use ash::vk::FrontFace;
use ash::vk::GraphicsPipelineCreateInfo;
use ash::vk::Handle;
use ash::vk::Pipeline;
use ash::vk::PipelineCache;
use ash::vk::PipelineColorBlendAttachmentState;
use ash::vk::PipelineColorBlendStateCreateInfo;
use ash::vk::PipelineCreateFlags;
use ash::vk::PipelineCreateFlags2;
use ash::vk::PipelineCreateFlags2CreateInfo;
use ash::vk::PipelineDynamicStateCreateInfo;
use ash::vk::PipelineInputAssemblyStateCreateInfo;
use ash::vk::PipelineLayout;
use ash::vk::PipelineLayoutCreateInfo;
use ash::vk::PipelineMultisampleStateCreateInfo;
use ash::vk::PipelineRasterizationStateCreateInfo;
use ash::vk::PipelineRenderingCreateInfo;
use ash::vk::PipelineShaderStageCreateInfo;
use ash::vk::PipelineVertexInputStateCreateInfo;
use ash::vk::PipelineViewportStateCreateInfo;
use ash::vk::PolygonMode;
use ash::vk::PrimitiveTopology;
use ash::vk::PushConstantRange;
use ash::vk::SampleCountFlags;
use ash::vk::ShaderDescriptorSetAndBindingMappingInfoEXT;
use ash::vk::ShaderStageFlags;
use ash::vk::SpecializationInfo;
use ash::vk::SpecializationMapEntry;
use run_on_drop::on_drop;
use std::rc::Rc;
use std::slice;

pub(super) struct VulkanPipeline {
    vert: Rc<VulkanShader>,
    _frag: Rc<VulkanShader>,
    pub(super) pipeline_layout: PipelineLayout,
    pub(super) pipeline: Pipeline,
    _descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
}

pub(super) struct PipelineCreateInfo<'a> {
    pub(super) format: vk::Format,
    pub(super) vert: Rc<VulkanShader>,
    pub(super) frag: Rc<VulkanShader>,
    pub(super) blend: bool,
    pub(super) src_has_alpha: bool,
    pub(super) has_alpha_mult: bool,
    pub(super) alpha_mode: AlphaMode,
    pub(super) eotf: u32,
    pub(super) inv_eotf: u32,
    pub(super) descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
    pub(super) has_color_management_data: bool,
    pub(super) frag_descriptor_mappings: &'a [DescriptorSetAndBindingMappingEXT<'static>],
    pub(super) grayscale: bool,
}

impl VulkanDevice {
    pub(super) fn create_pipeline<P>(
        &self,
        info: PipelineCreateInfo<'_>,
    ) -> Result<Rc<VulkanPipeline>, VulkanError> {
        self.create_pipeline2(info, size_of::<P>())
    }

    pub(super) fn create_pipeline2(
        &self,
        info: PipelineCreateInfo<'_>,
        push_size: usize,
    ) -> Result<Rc<VulkanPipeline>, VulkanError> {
        let mut pipeline_layout = PipelineLayout::null();
        if self.descriptor_heap.is_none() {
            let mut push_constant_ranges = ArrayVec::<_, 1>::new();
            if push_size > 0 {
                push_constant_ranges.push(
                    PushConstantRange::default()
                        .stage_flags(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)
                        .offset(0)
                        .size(push_size as u32),
                );
            }
            let mut descriptor_set_layouts = ArrayVec::<_, 2>::new();
            descriptor_set_layouts.extend(info.descriptor_set_layouts.iter().map(|l| l.layout));
            let create_info = PipelineLayoutCreateInfo::default()
                .push_constant_ranges(&push_constant_ranges)
                .set_layouts(&descriptor_set_layouts);
            let layout = unsafe { self.device.create_pipeline_layout(&create_info, None) };
            pipeline_layout = layout.map_err(VulkanError::CreatePipelineLayout)?;
        }
        let destroy_layout = on_drop(|| {
            if !pipeline_layout.is_null() {
                unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) }
            }
        });
        let mut frag_spec_data = ArrayVec::<_, { 7 * 4 }>::new();
        let mut frag_spec_entries = ArrayVec::<_, 7>::new();
        let mut frag_spec_entry = |data: &[u8]| {
            let entry = SpecializationMapEntry::default()
                .constant_id(frag_spec_entries.len() as _)
                .size(data.len() as _)
                .offset(frag_spec_data.len() as _);
            frag_spec_entries.push(entry);
            frag_spec_data.extend(data.iter().copied());
        };
        frag_spec_entry(&(info.src_has_alpha as u32).to_ne_bytes());
        frag_spec_entry(&(info.has_alpha_mult as u32).to_ne_bytes());
        frag_spec_entry(&info.eotf.to_ne_bytes());
        frag_spec_entry(&info.inv_eotf.to_ne_bytes());
        frag_spec_entry(&(info.has_color_management_data as u32).to_ne_bytes());
        frag_spec_entry(&info.alpha_mode.to_vulkan().to_ne_bytes());
        frag_spec_entry(&(info.grayscale as u32).to_ne_bytes());
        let frag_spec = SpecializationInfo::default()
            .map_entries(&frag_spec_entries)
            .data(&frag_spec_data);
        let pipeline = {
            let mut stages = [
                PipelineShaderStageCreateInfo::default()
                    .stage(ShaderStageFlags::VERTEX)
                    .module(info.vert.module)
                    .name(c"main"),
                PipelineShaderStageCreateInfo::default()
                    .stage(ShaderStageFlags::FRAGMENT)
                    .module(info.frag.module)
                    .specialization_info(&frag_spec)
                    .name(c"main"),
            ];
            let mut frag_mapping_info = ShaderDescriptorSetAndBindingMappingInfoEXT::default()
                .mappings(info.frag_descriptor_mappings);
            if self.descriptor_heap.is_some() {
                stages[1] = stages[1].push_next(&mut frag_mapping_info);
            }
            let input_assembly_state = PipelineInputAssemblyStateCreateInfo::default()
                .topology(PrimitiveTopology::TRIANGLE_STRIP);
            let vertex_input_state = PipelineVertexInputStateCreateInfo::default();
            let rasterization_state = PipelineRasterizationStateCreateInfo::default()
                .polygon_mode(PolygonMode::FILL)
                .cull_mode(CullModeFlags::NONE)
                .line_width(1.0)
                .front_face(FrontFace::COUNTER_CLOCKWISE);
            let multisampling_state = PipelineMultisampleStateCreateInfo::default()
                .sample_shading_enable(false)
                .rasterization_samples(SampleCountFlags::TYPE_1);
            let mut blending = PipelineColorBlendAttachmentState::default()
                .color_write_mask(ColorComponentFlags::RGBA);
            if info.blend {
                blending = blending
                    .blend_enable(true)
                    .src_color_blend_factor(BlendFactor::ONE)
                    .dst_color_blend_factor(BlendFactor::ONE_MINUS_SRC_ALPHA)
                    .color_blend_op(BlendOp::ADD)
                    .src_alpha_blend_factor(BlendFactor::ONE)
                    .dst_alpha_blend_factor(BlendFactor::ONE_MINUS_SRC_ALPHA)
                    .alpha_blend_op(BlendOp::ADD);
            }
            let color_blend_state = PipelineColorBlendStateCreateInfo::default()
                .attachments(slice::from_ref(&blending));
            let dynamic_states = [DynamicState::VIEWPORT, DynamicState::SCISSOR];
            let dynamic_state =
                PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
            let viewport_state = PipelineViewportStateCreateInfo::default()
                .viewport_count(1)
                .scissor_count(1);
            let mut pipeline_rendering_create_info = PipelineRenderingCreateInfo::default()
                .color_attachment_formats(slice::from_ref(&info.format));
            let mut flags = PipelineCreateFlags::empty();
            let mut flags2 = PipelineCreateFlags2::empty();
            if self.descriptor_buffer.is_some() {
                flags |= PipelineCreateFlags::DESCRIPTOR_BUFFER_EXT;
                flags2 |= PipelineCreateFlags2::DESCRIPTOR_BUFFER_EXT;
            }
            if self.descriptor_heap.is_some() {
                flags2 |= PipelineCreateFlags2::DESCRIPTOR_HEAP_EXT;
            }
            let mut flags_create_info = PipelineCreateFlags2CreateInfo::default().flags(flags2);
            let mut create_info = GraphicsPipelineCreateInfo::default()
                .push_next(&mut pipeline_rendering_create_info)
                .flags(flags)
                .stages(&stages)
                .input_assembly_state(&input_assembly_state)
                .vertex_input_state(&vertex_input_state)
                .rasterization_state(&rasterization_state)
                .multisample_state(&multisampling_state)
                .color_blend_state(&color_blend_state)
                .dynamic_state(&dynamic_state)
                .viewport_state(&viewport_state)
                .layout(pipeline_layout);
            if self.descriptor_heap.is_some() {
                create_info = create_info.push_next(&mut flags_create_info);
            }
            let pipelines = unsafe {
                self.device.create_graphics_pipelines(
                    PipelineCache::null(),
                    slice::from_ref(&create_info),
                    None,
                )
            };
            let mut pipelines = pipelines
                .map_err(|e| e.1)
                .map_err(VulkanError::CreatePipeline)?;
            assert_eq!(pipelines.len(), 1);
            pipelines.pop().unwrap()
        };
        destroy_layout.forget();
        Ok(Rc::new(VulkanPipeline {
            vert: info.vert,
            _frag: info.frag,
            pipeline_layout,
            pipeline,
            _descriptor_set_layouts: info.descriptor_set_layouts,
        }))
    }
}

impl Drop for VulkanPipeline {
    fn drop(&mut self) {
        unsafe {
            let device = &self.vert.device.device;
            device.destroy_pipeline(self.pipeline, None);
            if !self.pipeline_layout.is_null() {
                device.destroy_pipeline_layout(self.pipeline_layout, None);
            }
        }
    }
}