use crate::{
    batch::{GroupIterator, OrderedTwoLevelBatch, TwoLevelBatch},
    mtl::{FullTextureSet, Material, StaticTextureSet},
    pipeline::{PipelineDescBuilder, PipelinesBuilder},
    pod::{SkinnedVertexArgs, VertexArgs},
    resources::Tint,
    skinning::JointTransforms,
    submodules::{DynamicVertexBuffer, EnvironmentSub, MaterialId, MaterialSub, SkinningSub},
    transparent::Transparent,
    types::{Backend, Mesh},
    util,
    visibility::Visibility,
};
use amethyst_assets::{AssetStorage, Handle};
use amethyst_core::{
    ecs::{Join, Read, ReadExpect, ReadStorage, SystemData, World},
    transform::Transform,
    Hidden, HiddenPropagate,
};
use derivative::Derivative;
use rendy::{
    command::{QueueId, RenderPassEncoder},
    factory::Factory,
    graph::{
        render::{PrepareResult, RenderGroup, RenderGroupDesc},
        GraphContext, NodeBuffer, NodeImage,
    },
    hal::{self, device::Device, pso},
    mesh::{AsVertex, VertexFormat},
    shader::{Shader, SpirvShader},
};
use smallvec::SmallVec;
use std::marker::PhantomData;
macro_rules! profile_scope_impl {
    ($string:expr) => {
        #[cfg(feature = "profiler")]
        let _profile_scope = thread_profiler::ProfileScope::new(format!(
            "{} {}: {}",
            module_path!(),
            <T as Base3DPassDef>::NAME,
            $string
        ));
    };
}
pub trait Base3DPassDef: 'static + std::fmt::Debug + Send + Sync {
    
    const NAME: &'static str;
    
    type TextureSet: for<'a> StaticTextureSet<'a>;
    
    fn vertex_shader() -> &'static SpirvShader;
    
    fn vertex_skinned_shader() -> &'static SpirvShader;
    
    fn fragment_shader() -> &'static SpirvShader;
    
    fn base_format() -> Vec<VertexFormat>;
    
    fn skinned_format() -> Vec<VertexFormat>;
}
#[derive(Clone, Derivative)]
#[derivative(Debug(bound = ""), Default(bound = ""))]
pub struct DrawBase3DDesc<B: Backend, T: Base3DPassDef> {
    skinning: bool,
    marker: PhantomData<(B, T)>,
}
impl<B: Backend, T: Base3DPassDef> DrawBase3DDesc<B, T> {
    
    pub fn new() -> Self {
        Default::default()
    }
    
    pub fn skinned() -> Self {
        Self {
            skinning: true,
            marker: PhantomData,
        }
    }
    
    pub fn with_skinning(mut self, skinned: bool) -> Self {
        self.skinning = skinned;
        self
    }
}
impl<B: Backend, T: Base3DPassDef> RenderGroupDesc<B, World> for DrawBase3DDesc<B, T> {
    fn build(
        self,
        _ctx: &GraphContext<B>,
        factory: &mut Factory<B>,
        _queue: QueueId,
        _aux: &World,
        framebuffer_width: u32,
        framebuffer_height: u32,
        subpass: hal::pass::Subpass<'_, B>,
        _buffers: Vec<NodeBuffer>,
        _images: Vec<NodeImage>,
    ) -> Result<Box<dyn RenderGroup<B, World>>, failure::Error> {
        profile_scope_impl!("build");
        let env = EnvironmentSub::new(
            factory,
            [
                hal::pso::ShaderStageFlags::VERTEX,
                hal::pso::ShaderStageFlags::FRAGMENT,
            ],
        )?;
        let materials = MaterialSub::new(factory)?;
        let skinning = SkinningSub::new(factory)?;
        let mut vertex_format_base = T::base_format();
        let mut vertex_format_skinned = T::skinned_format();
        let (mut pipelines, pipeline_layout) = build_pipelines::<B, T>(
            factory,
            subpass,
            framebuffer_width,
            framebuffer_height,
            &vertex_format_base,
            &vertex_format_skinned,
            self.skinning,
            false,
            vec![
                env.raw_layout(),
                materials.raw_layout(),
                skinning.raw_layout(),
            ],
        )?;
        vertex_format_base.sort();
        vertex_format_skinned.sort();
        Ok(Box::new(DrawBase3D::<B, T> {
            pipeline_basic: pipelines.remove(0),
            pipeline_skinned: pipelines.pop(),
            pipeline_layout,
            static_batches: Default::default(),
            skinned_batches: Default::default(),
            vertex_format_base,
            vertex_format_skinned,
            env,
            materials,
            skinning,
            models: DynamicVertexBuffer::new(),
            skinned_models: DynamicVertexBuffer::new(),
            marker: PhantomData,
        }))
    }
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct DrawBase3D<B: Backend, T: Base3DPassDef> {
    pipeline_basic: B::GraphicsPipeline,
    pipeline_skinned: Option<B::GraphicsPipeline>,
    pipeline_layout: B::PipelineLayout,
    static_batches: TwoLevelBatch<MaterialId, u32, SmallVec<[VertexArgs; 4]>>,
    skinned_batches: TwoLevelBatch<MaterialId, u32, SmallVec<[SkinnedVertexArgs; 4]>>,
    vertex_format_base: Vec<VertexFormat>,
    vertex_format_skinned: Vec<VertexFormat>,
    env: EnvironmentSub<B>,
    materials: MaterialSub<B, T::TextureSet>,
    skinning: SkinningSub<B>,
    models: DynamicVertexBuffer<B, VertexArgs>,
    skinned_models: DynamicVertexBuffer<B, SkinnedVertexArgs>,
    marker: PhantomData<T>,
}
impl<B: Backend, T: Base3DPassDef> RenderGroup<B, World> for DrawBase3D<B, T> {
    fn prepare(
        &mut self,
        factory: &Factory<B>,
        _queue: QueueId,
        index: usize,
        _subpass: hal::pass::Subpass<'_, B>,
        resources: &World,
    ) -> PrepareResult {
        profile_scope_impl!("prepare opaque");
        let (
            mesh_storage,
            visibility,
            transparent,
            hiddens,
            hiddens_prop,
            meshes,
            materials,
            transforms,
            joints,
            tints,
        ) = <(
            Read<'_, AssetStorage<Mesh>>,
            ReadExpect<'_, Visibility>,
            ReadStorage<'_, Transparent>,
            ReadStorage<'_, Hidden>,
            ReadStorage<'_, HiddenPropagate>,
            ReadStorage<'_, Handle<Mesh>>,
            ReadStorage<'_, Handle<Material>>,
            ReadStorage<'_, Transform>,
            ReadStorage<'_, JointTransforms>,
            ReadStorage<'_, Tint>,
        )>::fetch(resources);
        
        self.env.process(factory, index, resources);
        self.materials.maintain();
        self.static_batches.clear_inner();
        self.skinned_batches.clear_inner();
        let materials_ref = &mut self.materials;
        let skinning_ref = &mut self.skinning;
        let statics_ref = &mut self.static_batches;
        let skinned_ref = &mut self.skinned_batches;
        let static_input = || ((&materials, &meshes, &transforms, tints.maybe()), !&joints);
        let skinned_input = || (&materials, &meshes, &transforms, tints.maybe(), &joints);
        {
            profile_scope_impl!("prepare");
            (static_input(), &visibility.visible_unordered)
                .join()
                .map(|(((mat, mesh, tform, tint), _), _)| {
                    ((mat, mesh.id()), VertexArgs::from_object_data(tform, tint))
                })
                .for_each_group(|(mat, mesh_id), data| {
                    if mesh_storage.contains_id(mesh_id) {
                        if let Some((mat, _)) = materials_ref.insert(factory, resources, mat) {
                            statics_ref.insert(mat, mesh_id, data.drain(..));
                        }
                    }
                });
        }
        if self.pipeline_skinned.is_some() {
            profile_scope_impl!("prepare_skinning");
            (skinned_input(), &visibility.visible_unordered)
                .join()
                .map(|((mat, mesh, tform, tint, joints), _)| {
                    (
                        (mat, mesh.id()),
                        SkinnedVertexArgs::from_object_data(
                            tform,
                            tint,
                            skinning_ref.insert(joints),
                        ),
                    )
                })
                .for_each_group(|(mat, mesh_id), data| {
                    if mesh_storage.contains_id(mesh_id) {
                        if let Some((mat, _)) = materials_ref.insert(factory, resources, mat) {
                            skinned_ref.insert(mat, mesh_id, data.drain(..));
                        }
                    }
                });
        };
        {
            profile_scope_impl!("write");
            self.static_batches.prune();
            self.skinned_batches.prune();
            self.models.write(
                factory,
                index,
                self.static_batches.count() as u64,
                self.static_batches.data(),
            );
            self.skinned_models.write(
                factory,
                index,
                self.skinned_batches.count() as u64,
                self.skinned_batches.data(),
            );
            self.skinning.commit(factory, index);
        }
        PrepareResult::DrawRecord
    }
    fn draw_inline(
        &mut self,
        mut encoder: RenderPassEncoder<'_, B>,
        index: usize,
        _subpass: hal::pass::Subpass<'_, B>,
        resources: &World,
    ) {
        profile_scope_impl!("draw opaque");
        let mesh_storage = <Read<'_, AssetStorage<Mesh>>>::fetch(resources);
        let models_loc = self.vertex_format_base.len() as u32;
        let skin_models_loc = self.vertex_format_skinned.len() as u32;
        encoder.bind_graphics_pipeline(&self.pipeline_basic);
        self.env.bind(index, &self.pipeline_layout, 0, &mut encoder);
        if self.models.bind(index, models_loc, 0, &mut encoder) {
            let mut instances_drawn = 0;
            for (&mat_id, batches) in self.static_batches.iter() {
                if self.materials.loaded(mat_id) {
                    self.materials
                        .bind(&self.pipeline_layout, 1, mat_id, &mut encoder);
                    for (mesh_id, batch_data) in batches {
                        debug_assert!(mesh_storage.contains_id(*mesh_id));
                        if let Some(mesh) =
                            B::unwrap_mesh(unsafe { mesh_storage.get_by_id_unchecked(*mesh_id) })
                        {
                            mesh.bind_and_draw(
                                0,
                                &self.vertex_format_base,
                                instances_drawn..instances_drawn + batch_data.len() as u32,
                                &mut encoder,
                            )
                            .unwrap();
                        }
                        instances_drawn += batch_data.len() as u32;
                    }
                }
            }
        }
        if let Some(pipeline_skinned) = self.pipeline_skinned.as_ref() {
            encoder.bind_graphics_pipeline(pipeline_skinned);
            if self
                .skinned_models
                .bind(index, skin_models_loc, 0, &mut encoder)
            {
                self.skinning
                    .bind(index, &self.pipeline_layout, 2, &mut encoder);
                let mut instances_drawn = 0;
                for (&mat_id, batches) in self.skinned_batches.iter() {
                    if self.materials.loaded(mat_id) {
                        self.materials
                            .bind(&self.pipeline_layout, 1, mat_id, &mut encoder);
                        for (mesh_id, batch_data) in batches {
                            debug_assert!(mesh_storage.contains_id(*mesh_id));
                            if let Some(mesh) = B::unwrap_mesh(unsafe {
                                mesh_storage.get_by_id_unchecked(*mesh_id)
                            }) {
                                mesh.bind_and_draw(
                                    0,
                                    &self.vertex_format_skinned,
                                    instances_drawn..instances_drawn + batch_data.len() as u32,
                                    &mut encoder,
                                )
                                .unwrap();
                            }
                            instances_drawn += batch_data.len() as u32;
                        }
                    }
                }
            }
        }
    }
    fn dispose(mut self: Box<Self>, factory: &mut Factory<B>, _aux: &World) {
        profile_scope_impl!("dispose");
        unsafe {
            factory
                .device()
                .destroy_graphics_pipeline(self.pipeline_basic);
            if let Some(pipeline) = self.pipeline_skinned.take() {
                factory.device().destroy_graphics_pipeline(pipeline);
            }
            factory
                .device()
                .destroy_pipeline_layout(self.pipeline_layout);
        }
    }
}
#[derive(Clone, Derivative)]
#[derivative(Debug(bound = ""), Default(bound = ""))]
pub struct DrawBase3DTransparentDesc<B: Backend, T: Base3DPassDef> {
    skinning: bool,
    marker: PhantomData<(B, T)>,
}
impl<B: Backend, T: Base3DPassDef> DrawBase3DTransparentDesc<B, T> {
    
    pub fn new() -> Self {
        Self {
            skinning: false,
            marker: PhantomData,
        }
    }
    
    pub fn skinned() -> Self {
        Self {
            skinning: true,
            marker: PhantomData,
        }
    }
    
    pub fn with_skinning(mut self, skinned: bool) -> Self {
        self.skinning = skinned;
        self
    }
}
impl<B: Backend, T: Base3DPassDef> RenderGroupDesc<B, World> for DrawBase3DTransparentDesc<B, T> {
    fn build(
        self,
        _ctx: &GraphContext<B>,
        factory: &mut Factory<B>,
        _queue: QueueId,
        _aux: &World,
        framebuffer_width: u32,
        framebuffer_height: u32,
        subpass: hal::pass::Subpass<'_, B>,
        _buffers: Vec<NodeBuffer>,
        _images: Vec<NodeImage>,
    ) -> Result<Box<dyn RenderGroup<B, World>>, failure::Error> {
        let env = EnvironmentSub::new(
            factory,
            [
                hal::pso::ShaderStageFlags::VERTEX,
                hal::pso::ShaderStageFlags::FRAGMENT,
            ],
        )?;
        let materials = MaterialSub::new(factory)?;
        let skinning = SkinningSub::new(factory)?;
        let mut vertex_format_base = T::base_format();
        let mut vertex_format_skinned = T::skinned_format();
        let (mut pipelines, pipeline_layout) = build_pipelines::<B, T>(
            factory,
            subpass,
            framebuffer_width,
            framebuffer_height,
            &vertex_format_base,
            &vertex_format_skinned,
            self.skinning,
            true,
            vec![
                env.raw_layout(),
                materials.raw_layout(),
                skinning.raw_layout(),
            ],
        )?;
        vertex_format_base.sort();
        vertex_format_skinned.sort();
        Ok(Box::new(DrawBase3DTransparent::<B, T> {
            pipeline_basic: pipelines.remove(0),
            pipeline_skinned: pipelines.pop(),
            pipeline_layout,
            static_batches: Default::default(),
            skinned_batches: Default::default(),
            vertex_format_base,
            vertex_format_skinned,
            env,
            materials,
            skinning,
            models: DynamicVertexBuffer::new(),
            skinned_models: DynamicVertexBuffer::new(),
            change: Default::default(),
            marker: PhantomData,
        }))
    }
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct DrawBase3DTransparent<B: Backend, T: Base3DPassDef> {
    pipeline_basic: B::GraphicsPipeline,
    pipeline_skinned: Option<B::GraphicsPipeline>,
    pipeline_layout: B::PipelineLayout,
    static_batches: OrderedTwoLevelBatch<MaterialId, u32, VertexArgs>,
    skinned_batches: OrderedTwoLevelBatch<MaterialId, u32, SkinnedVertexArgs>,
    vertex_format_base: Vec<VertexFormat>,
    vertex_format_skinned: Vec<VertexFormat>,
    env: EnvironmentSub<B>,
    materials: MaterialSub<B, FullTextureSet>,
    skinning: SkinningSub<B>,
    models: DynamicVertexBuffer<B, VertexArgs>,
    skinned_models: DynamicVertexBuffer<B, SkinnedVertexArgs>,
    change: util::ChangeDetection,
    marker: PhantomData<(T)>,
}
impl<B: Backend, T: Base3DPassDef> RenderGroup<B, World> for DrawBase3DTransparent<B, T> {
    fn prepare(
        &mut self,
        factory: &Factory<B>,
        _queue: QueueId,
        index: usize,
        _subpass: hal::pass::Subpass<'_, B>,
        resources: &World,
    ) -> PrepareResult {
        profile_scope_impl!("prepare transparent");
        let (mesh_storage, visibility, meshes, materials, transforms, joints, tints) =
            <(
                Read<'_, AssetStorage<Mesh>>,
                ReadExpect<'_, Visibility>,
                ReadStorage<'_, Handle<Mesh>>,
                ReadStorage<'_, Handle<Material>>,
                ReadStorage<'_, Transform>,
                ReadStorage<'_, JointTransforms>,
                ReadStorage<'_, Tint>,
            )>::fetch(resources);
        
        self.env.process(factory, index, resources);
        self.materials.maintain();
        self.static_batches.swap_clear();
        self.skinned_batches.swap_clear();
        let materials_ref = &mut self.materials;
        let skinning_ref = &mut self.skinning;
        let statics_ref = &mut self.static_batches;
        let skinned_ref = &mut self.skinned_batches;
        let mut changed = false;
        let mut joined = ((&materials, &meshes, &transforms, tints.maybe()), !&joints).join();
        visibility
            .visible_ordered
            .iter()
            .filter_map(|e| joined.get_unchecked(e.id()))
            .map(|((mat, mesh, tform, tint), _)| {
                ((mat, mesh.id()), VertexArgs::from_object_data(tform, tint))
            })
            .for_each_group(|(mat, mesh_id), data| {
                if mesh_storage.contains_id(mesh_id) {
                    if let Some((mat, this_changed)) = materials_ref.insert(factory, resources, mat)
                    {
                        changed = changed || this_changed;
                        statics_ref.insert(mat, mesh_id, data.drain(..));
                    }
                }
            });
        if self.pipeline_skinned.is_some() {
            let mut joined = (&materials, &meshes, &transforms, tints.maybe(), &joints).join();
            visibility
                .visible_ordered
                .iter()
                .filter_map(|e| joined.get_unchecked(e.id()))
                .map(|(mat, mesh, tform, tint, joints)| {
                    (
                        (mat, mesh.id()),
                        SkinnedVertexArgs::from_object_data(
                            tform,
                            tint,
                            skinning_ref.insert(joints),
                        ),
                    )
                })
                .for_each_group(|(mat, mesh_id), data| {
                    if mesh_storage.contains_id(mesh_id) {
                        if let Some((mat, this_changed)) =
                            materials_ref.insert(factory, resources, mat)
                        {
                            changed = changed || this_changed;
                            skinned_ref.insert(mat, mesh_id, data.drain(..));
                        }
                    }
                });
        }
        self.models.write(
            factory,
            index,
            self.static_batches.count() as u64,
            Some(self.static_batches.data()),
        );
        self.skinned_models.write(
            factory,
            index,
            self.skinned_batches.count() as u64,
            Some(self.skinned_batches.data()),
        );
        self.skinning.commit(factory, index);
        changed = changed || self.static_batches.changed();
        changed = changed || self.skinned_batches.changed();
        self.change.prepare_result(index, changed)
    }
    fn draw_inline(
        &mut self,
        mut encoder: RenderPassEncoder<'_, B>,
        index: usize,
        _subpass: hal::pass::Subpass<'_, B>,
        resources: &World,
    ) {
        profile_scope_impl!("draw transparent");
        let mesh_storage = <Read<'_, AssetStorage<Mesh>>>::fetch(resources);
        let layout = &self.pipeline_layout;
        let encoder = &mut encoder;
        let models_loc = self.vertex_format_base.len() as u32;
        let skin_models_loc = self.vertex_format_skinned.len() as u32;
        encoder.bind_graphics_pipeline(&self.pipeline_basic);
        self.env.bind(index, layout, 0, encoder);
        if self.models.bind(index, models_loc, 0, encoder) {
            for (&mat, batches) in self.static_batches.iter() {
                if self.materials.loaded(mat) {
                    self.materials.bind(layout, 1, mat, encoder);
                    for (mesh, range) in batches {
                        debug_assert!(mesh_storage.contains_id(*mesh));
                        if let Some(mesh) =
                            B::unwrap_mesh(unsafe { mesh_storage.get_by_id_unchecked(*mesh) })
                        {
                            if let Err(error) = mesh.bind_and_draw(
                                0,
                                &self.vertex_format_base,
                                range.clone(),
                                encoder,
                            ) {
                                log::warn!(
                                    "Trying to draw a mesh that lacks {:?} vertex attributes. Pass {} requires attributes {:?}.",
                                    error.not_found.attributes,
                                    T::NAME,
                                    T::base_format(),
                                );
                            }
                        }
                    }
                }
            }
        }
        if let Some(pipeline_skinned) = self.pipeline_skinned.as_ref() {
            encoder.bind_graphics_pipeline(pipeline_skinned);
            if self.skinned_models.bind(index, skin_models_loc, 0, encoder) {
                self.skinning.bind(index, layout, 2, encoder);
                for (&mat, batches) in self.skinned_batches.iter() {
                    if self.materials.loaded(mat) {
                        self.materials.bind(layout, 1, mat, encoder);
                        for (mesh, range) in batches {
                            debug_assert!(mesh_storage.contains_id(*mesh));
                            if let Some(mesh) =
                                B::unwrap_mesh(unsafe { mesh_storage.get_by_id_unchecked(*mesh) })
                            {
                                if let Err(error) = mesh.bind_and_draw(
                                    0,
                                    &self.vertex_format_skinned,
                                    range.clone(),
                                    encoder,
                                ) {
                                    log::warn!(
                                        "Trying to draw a skinned mesh that lacks {:?} vertex attributes. Pass {} requires attributes {:?}.",
                                        error.not_found.attributes,
                                        T::NAME,
                                        T::skinned_format(),
                                    );
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    fn dispose(mut self: Box<Self>, factory: &mut Factory<B>, _aux: &World) {
        unsafe {
            factory
                .device()
                .destroy_graphics_pipeline(self.pipeline_basic);
            if let Some(pipeline) = self.pipeline_skinned.take() {
                factory.device().destroy_graphics_pipeline(pipeline);
            }
            factory
                .device()
                .destroy_pipeline_layout(self.pipeline_layout);
        }
    }
}
fn build_pipelines<B: Backend, T: Base3DPassDef>(
    factory: &Factory<B>,
    subpass: hal::pass::Subpass<'_, B>,
    framebuffer_width: u32,
    framebuffer_height: u32,
    vertex_format_base: &[VertexFormat],
    vertex_format_skinned: &[VertexFormat],
    skinning: bool,
    transparent: bool,
    layouts: Vec<&B::DescriptorSetLayout>,
) -> Result<(Vec<B::GraphicsPipeline>, B::PipelineLayout), failure::Error> {
    let pipeline_layout = unsafe {
        factory
            .device()
            .create_pipeline_layout(layouts, None as Option<(_, _)>)
    }?;
    let vertex_desc = vertex_format_base
        .iter()
        .map(|f| (f.clone(), pso::VertexInputRate::Vertex))
        .chain(Some((
            VertexArgs::vertex(),
            pso::VertexInputRate::Instance(1),
        )))
        .collect::<Vec<_>>();
    let shader_vertex_basic = unsafe { T::vertex_shader().module(factory).unwrap() };
    let shader_fragment = unsafe { T::fragment_shader().module(factory).unwrap() };
    let pipe_desc = PipelineDescBuilder::new()
        .with_vertex_desc(&vertex_desc)
        .with_shaders(util::simple_shader_set(
            &shader_vertex_basic,
            Some(&shader_fragment),
        ))
        .with_layout(&pipeline_layout)
        .with_subpass(subpass)
        .with_framebuffer_size(framebuffer_width, framebuffer_height)
        .with_face_culling(pso::Face::BACK)
        .with_depth_test(pso::DepthTest::On {
            fun: pso::Comparison::Less,
            write: !transparent,
        })
        .with_blend_targets(vec![pso::ColorBlendDesc(
            pso::ColorMask::ALL,
            if transparent {
                pso::BlendState::PREMULTIPLIED_ALPHA
            } else {
                pso::BlendState::Off
            },
        )]);
    let pipelines = if skinning {
        let shader_vertex_skinned = unsafe { T::vertex_skinned_shader().module(factory).unwrap() };
        let vertex_desc = vertex_format_skinned
            .iter()
            .map(|f| (f.clone(), pso::VertexInputRate::Vertex))
            .chain(Some((
                SkinnedVertexArgs::vertex(),
                pso::VertexInputRate::Instance(1),
            )))
            .collect::<Vec<_>>();
        let pipe = PipelinesBuilder::new()
            .with_pipeline(pipe_desc.clone())
            .with_child_pipeline(
                0,
                pipe_desc
                    .with_vertex_desc(&vertex_desc)
                    .with_shaders(util::simple_shader_set(
                        &shader_vertex_skinned,
                        Some(&shader_fragment),
                    )),
            )
            .build(factory, None);
        unsafe {
            factory.destroy_shader_module(shader_vertex_skinned);
        }
        pipe
    } else {
        PipelinesBuilder::new()
            .with_pipeline(pipe_desc)
            .build(factory, None)
    };
    unsafe {
        factory.destroy_shader_module(shader_vertex_basic);
        factory.destroy_shader_module(shader_fragment);
    }
    match pipelines {
        Err(e) => {
            unsafe {
                factory.device().destroy_pipeline_layout(pipeline_layout);
            }
            Err(e)
        }
        Ok(pipelines) => Ok((pipelines, pipeline_layout)),
    }
}