use bevy::ecs::query::ROQueryItem;
use bevy::ecs::system::{SystemParam, SystemParamItem};
use bevy::prelude::{Commands, Rect, Resource, With};
#[cfg(feature = "svg")]
use bevy::prelude::{Mesh, Vec3};
use bevy::render::globals::{GlobalsBuffer, GlobalsUniform};
#[cfg(feature = "svg")]
use bevy::render::mesh::VertexAttributeValues;
use bevy::render::render_phase::{
    DrawFunctionId, PhaseItem, RenderCommand, RenderCommandResult, SetItemPipeline,
};
use bevy::render::render_resource::{
    CachedRenderPipelineId, DynamicUniformBuffer, ShaderType, SpecializedRenderPipeline,
    SpecializedRenderPipelines,
};
use bevy::render::view::ViewTarget;
use bevy::utils::FloatOrd;
use bevy::{
    ecs::system::lifetimeless::{Read, SRes},
    math::{Mat4, Quat, Vec2, Vec4},
    prelude::{Component, Entity, FromWorld, Handle, Query, Res, ResMut, World},
    render::{
        color::Color,
        render_asset::RenderAssets,
        render_phase::{DrawFunctions, TrackedRenderPass},
        render_resource::{
            BindGroup, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry, BindingResource,
            BindingType, BlendState, BufferBindingType, BufferSize, BufferUsages, BufferVec,
            ColorTargetState, ColorWrites, Extent3d, FragmentState, FrontFace, MultisampleState,
            PipelineCache, PolygonMode, PrimitiveState, PrimitiveTopology,
            RenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor, ShaderStages,
            TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
            TextureViewDescriptor, TextureViewDimension, VertexAttribute, VertexBufferLayout,
            VertexFormat, VertexState, VertexStepMode,
        },
        renderer::{RenderDevice, RenderQueue},
        texture::{BevyDefault, GpuImage, Image},
    },
    utils::HashMap,
};
#[cfg(feature = "svg")]
use bevy_svg::prelude::Svg;
use bytemuck::{Pod, Zeroable};
use kayak_font::{bevy::FontTextureCache, KayakFont};
use std::marker::PhantomData;

use super::UNIFIED_SHADER_HANDLE;
use crate::layout::LayoutCache;
use crate::prelude::Corner;
use crate::render::extract::{UIExtractedView, UIViewUniform, UIViewUniformOffset, UIViewUniforms};
use crate::render::opacity_layer::OpacityLayerManager;
#[cfg(feature = "svg")]
use crate::render::svg::RenderSvgs;
use crate::render::ui_pass::{
    TransparentOpacityUI, TransparentUI, TransparentUIGeneric, UIRenderPhase,
};

#[derive(Resource, Clone)]
pub struct UnifiedPipeline {
    pub view_layout: BindGroupLayout,
    pub types_layout: BindGroupLayout,
    pub image_layout: BindGroupLayout,
    empty_font_texture: GpuImage,
    default_image: (GpuImage, BindGroup),
}

// const QUAD_VERTEX_POSITIONS: &[Vec3] = &[
//     Vec3::from_array([0.0, 1.0, 0.0]),
//     Vec3::from_array([1.0, 0.0, 0.0]),
//     Vec3::from_array([0.0, 0.0, 0.0]),
//     Vec3::from_array([0.0, 1.0, 0.0]),
//     Vec3::from_array([1.0, 1.0, 0.0]),
//     Vec3::from_array([1.0, 0.0, 0.0]),
// ];

const QUAD_INDICES: [usize; 6] = [0, 2, 3, 0, 1, 2];

const QUAD_VERTEX_POSITIONS: [Vec2; 4] = [
    Vec2::new(0.0, 0.0),
    Vec2::new(1.0, 0.0),
    Vec2::new(1.0, 1.0),
    Vec2::new(0.0, 1.0),
];

#[derive(Debug, Component, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UnifiedPipelineKey {
    pub msaa: u32,
    pub hdr: bool,
}

impl FromWorld for UnifiedPipeline {
    fn from_world(world: &mut World) -> Self {
        let world = world.cell();
        let render_device = world.resource::<RenderDevice>();

        let view_layout = render_device.create_bind_group_layout(
            "ui_view_layout",
            &[
                BindGroupLayoutEntry {
                    binding: 0,
                    visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
                    ty: BindingType::Buffer {
                        ty: BufferBindingType::Uniform,
                        has_dynamic_offset: true,
                        min_binding_size: Some(UIViewUniform::min_size()),
                    },
                    count: None,
                },
                BindGroupLayoutEntry {
                    binding: 1,
                    visibility: ShaderStages::VERTEX_FRAGMENT,
                    ty: BindingType::Buffer {
                        ty: BufferBindingType::Uniform,
                        has_dynamic_offset: false,
                        min_binding_size: Some(GlobalsUniform::min_size()),
                    },
                    count: None,
                },
            ],
        );

        let types_layout = render_device.create_bind_group_layout(
            "ui_types_layout",
            &[BindGroupLayoutEntry {
                binding: 0,
                visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
                ty: BindingType::Buffer {
                    ty: BufferBindingType::Uniform,
                    has_dynamic_offset: true,
                    // TODO: change this to ViewUniform::std140_size_static once crevice fixes this!
                    // Context: https://github.com/LPGhatguy/crevice/issues/29
                    min_binding_size: BufferSize::new(16),
                },
                count: None,
            }],
        );

        let image_layout = render_device.create_bind_group_layout(
            "image_layout",
            &[
                BindGroupLayoutEntry {
                    binding: 0,
                    visibility: ShaderStages::FRAGMENT,
                    ty: BindingType::Texture {
                        multisampled: false,
                        sample_type: TextureSampleType::Float { filterable: true },
                        view_dimension: TextureViewDimension::D2,
                    },
                    count: None,
                },
                BindGroupLayoutEntry {
                    binding: 1,
                    visibility: ShaderStages::FRAGMENT,
                    ty: BindingType::Sampler(SamplerBindingType::Filtering),
                    count: None,
                },
                BindGroupLayoutEntry {
                    binding: 2,
                    visibility: ShaderStages::FRAGMENT,
                    ty: BindingType::Texture {
                        multisampled: false,
                        sample_type: TextureSampleType::Float { filterable: true },
                        view_dimension: TextureViewDimension::D2Array,
                    },
                    count: None,
                },
                BindGroupLayoutEntry {
                    binding: 3,
                    visibility: ShaderStages::FRAGMENT,
                    ty: BindingType::Sampler(SamplerBindingType::Filtering),
                    count: None,
                },
            ],
        );

        let empty_font_texture = FontTextureCache::get_empty(&render_device);

        let texture_descriptor = TextureDescriptor {
            label: Some("empty_texture"),
            size: Extent3d {
                width: 1,
                height: 1,
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: TextureDimension::D2,
            view_formats: &[TextureFormat::Rgba8UnormSrgb],
            format: TextureFormat::Rgba8UnormSrgb,
            usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
        };

        let sampler_descriptor = SamplerDescriptor::default();

        let texture = render_device.create_texture(&texture_descriptor);
        let sampler = render_device.create_sampler(&sampler_descriptor);

        let texture_view = texture.create_view(&TextureViewDescriptor {
            label: Some("empty_texture_view"),
            format: Some(TextureFormat::Rgba8UnormSrgb),
            dimension: Some(TextureViewDimension::D2),
            aspect: bevy::render::render_resource::TextureAspect::All,
            base_mip_level: 0,
            base_array_layer: 0,
            mip_level_count: None,
            array_layer_count: None,
        });

        let image = GpuImage {
            texture,
            sampler,
            texture_view,
            mip_level_count: 1,
            size: Vec2::new(1.0, 1.0),
            texture_format: TextureFormat::Rgba8UnormSrgb,
        };

        let binding = render_device.create_bind_group(
            Some("default_image_bind_group"),
            &image_layout,
            &[
                BindGroupEntry {
                    binding: 0,
                    resource: BindingResource::TextureView(&image.texture_view),
                },
                BindGroupEntry {
                    binding: 1,
                    resource: BindingResource::Sampler(&image.sampler),
                },
                BindGroupEntry {
                    binding: 2,
                    resource: BindingResource::TextureView(&empty_font_texture.texture_view),
                },
                BindGroupEntry {
                    binding: 3,
                    resource: BindingResource::Sampler(&empty_font_texture.sampler),
                },
            ],
        );

        UnifiedPipeline {
            view_layout,
            empty_font_texture,
            types_layout,
            image_layout,
            default_image: (image, binding),
        }
    }
}

impl SpecializedRenderPipeline for UnifiedPipeline {
    type Key = UnifiedPipelineKey;

    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
        let vertex_buffer_layout = VertexBufferLayout {
            array_stride: 60,
            step_mode: VertexStepMode::Vertex,
            attributes: vec![
                VertexAttribute {
                    format: VertexFormat::Float32x3,
                    offset: 0,
                    shader_location: 0,
                },
                VertexAttribute {
                    format: VertexFormat::Float32x4,
                    offset: 12,
                    shader_location: 1,
                },
                VertexAttribute {
                    format: VertexFormat::Float32x4,
                    offset: 28,
                    shader_location: 2,
                },
                VertexAttribute {
                    format: VertexFormat::Float32x4,
                    offset: 44,
                    shader_location: 3,
                },
            ],
        };

        RenderPipelineDescriptor {
            vertex: VertexState {
                shader: UNIFIED_SHADER_HANDLE,
                entry_point: "vertex".into(),
                shader_defs: vec![],
                buffers: vec![vertex_buffer_layout],
            },
            fragment: Some(FragmentState {
                shader: UNIFIED_SHADER_HANDLE,
                shader_defs: vec![],
                entry_point: "fragment".into(),
                targets: vec![Some(ColorTargetState {
                    format: if key.hdr {
                        ViewTarget::TEXTURE_FORMAT_HDR
                    } else {
                        TextureFormat::bevy_default()
                    },
                    blend: Some(BlendState::ALPHA_BLENDING),
                    // Some(BlendState {
                    //     color: BlendComponent {
                    //         src_factor: BlendFactor::SrcAlpha,
                    //         dst_factor: BlendFactor::OneMinusSrcAlpha,
                    //         operation: BlendOperation::Add,
                    //     },
                    //     alpha: BlendComponent {
                    //         src_factor: BlendFactor::OneMinusDstAlpha,
                    //         dst_factor: BlendFactor::One,
                    //         operation: BlendOperation::Add,
                    //     },
                    // }),
                    write_mask: ColorWrites::ALL,
                })],
            }),
            layout: vec![
                self.view_layout.clone(),
                self.image_layout.clone(),
                self.types_layout.clone(),
            ],
            primitive: PrimitiveState {
                front_face: FrontFace::Ccw,
                cull_mode: None,
                polygon_mode: PolygonMode::Fill,
                conservative: false,
                topology: PrimitiveTopology::TriangleList,
                strip_index_format: None,
                unclipped_depth: false,
            },
            depth_stencil: None,
            multisample: MultisampleState {
                count: key.msaa,
                mask: !0,
                alpha_to_coverage_enabled: false,
            },
            label: Some("unified_pipeline".into()),
            push_constant_ranges: vec![],
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
pub enum UIQuadType {
    Quad,
    BoxShadow,
    Text,
    TextSubpixel,
    Image,
    Clip,
    OpacityLayer,
    DrawOpacityLayer,
    None,
}

impl UIQuadType {
    pub fn get_type_index(&self, quad_type_offsets: &QuadTypeOffsets) -> u32 {
        match self {
            UIQuadType::Quad => quad_type_offsets.quad_type_offset,
            UIQuadType::Text => quad_type_offsets.text_type_offset,
            UIQuadType::TextSubpixel => quad_type_offsets.text_sub_pixel_type_offset,
            UIQuadType::Image => quad_type_offsets.image_type_offset,
            UIQuadType::BoxShadow => quad_type_offsets.box_shadow_type_offset,
            UIQuadType::Clip => 100000,
            UIQuadType::None => 100001,
            UIQuadType::OpacityLayer => 100002,
            UIQuadType::DrawOpacityLayer => quad_type_offsets.image_type_offset,
        }
    }
}

#[derive(Debug, Component, Clone)]
pub struct ExtractedQuad {
    pub org_entity: Entity,
    pub camera_entity: Entity,
    pub rect: Rect,
    pub color: Color,
    pub char_id: u32,
    pub z_index: f32,
    pub font_handle: Option<Handle<KayakFont>>,
    pub quad_type: UIQuadType,
    pub type_index: u32,
    pub border_radius: Corner<f32>,
    pub image: Option<Handle<Image>>,
    pub uv_min: Option<Vec2>,
    pub uv_max: Option<Vec2>,
    #[cfg(feature = "svg")]
    pub svg_handle: (Option<Handle<Svg>>, Option<Color>),
    pub opacity_layer: u32,
    pub c: char,
}

impl Default for ExtractedQuad {
    fn default() -> Self {
        Self {
            org_entity: Entity::PLACEHOLDER,
            camera_entity: Entity::PLACEHOLDER,
            rect: Default::default(),
            color: Default::default(),
            char_id: Default::default(),
            z_index: Default::default(),
            font_handle: Default::default(),
            quad_type: UIQuadType::Quad,
            type_index: Default::default(),
            border_radius: Default::default(),
            image: Default::default(),
            uv_min: Default::default(),
            uv_max: Default::default(),
            #[cfg(feature = "svg")]
            svg_handle: Default::default(),
            opacity_layer: 0,
            c: ' ',
        }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct QuadVertex {
    pub position: [f32; 3],
    pub color: [f32; 4],
    pub uv: [f32; 4],
    pub pos_size: [f32; 4],
}

unsafe impl Zeroable for QuadVertex {}
unsafe impl Pod for QuadVertex {}

#[repr(C)]
#[derive(Copy, Clone, ShaderType)]
struct QuadType {
    pub t: i32,
    pub _padding_1: i32,
    pub _padding_2: i32,
    pub _padding_3: i32,
}

#[derive(Resource)]
pub struct QuadMeta {
    pub vertices: BufferVec<QuadVertex>,
    types_buffer: DynamicUniformBuffer<QuadType>,
    types_bind_group: Option<BindGroup>,
}

impl Default for QuadMeta {
    fn default() -> Self {
        Self {
            vertices: BufferVec::new(BufferUsages::VERTEX),
            types_buffer: DynamicUniformBuffer::default(),
            types_bind_group: None,
        }
    }
}

#[derive(Debug)]
pub enum QuadOrMaterial {
    Quad(ExtractedQuad),
    Material(Entity),
}

impl QuadOrMaterial {
    pub fn get_entity(&self) -> Entity {
        match self {
            QuadOrMaterial::Material(entity) => *entity,
            QuadOrMaterial::Quad(quad) => quad.org_entity,
        }
    }
}

impl Default for QuadOrMaterial {
    fn default() -> Self {
        Self::Quad(ExtractedQuad::default())
    }
}

#[derive(Resource, Default, Debug)]
pub struct ExtractedQuads {
    layers: Vec<ZLayer>,
    current_layer: usize,
    current_index: usize,
    children: HashMap<usize, Vec<usize>>,
    parents: HashMap<usize, usize>,
}

impl ExtractedQuads {
    pub fn clear(&mut self) {
        self.current_index = 0;
        self.current_layer = 0;
        self.layers.clear();
        self.children.clear();
        self.parents.clear();
    }

    pub fn push(&mut self, quad: QuadOrMaterial) {
        let layer = self.layers.get_mut(self.current_layer).unwrap();
        layer.quads.push(quad);
    }

    pub fn extend(&mut self, quads: Vec<QuadOrMaterial>) {
        let layer = self.layers.get_mut(self.current_layer).unwrap();
        layer.quads.extend(quads);
    }
    pub fn new_layer(&mut self, z_index: Option<f32>) {
        let layer = ZLayer {
            custom_z: z_index.unwrap_or(0.0),
            parent_id: self.current_layer,
            ..Default::default()
        };

        self.current_index = self.layers.len();
        if let Some(c) = self.children.get_mut(&self.current_layer) {
            c.push(self.current_index);
        }
        self.parents.insert(self.current_index, self.current_layer);
        self.layers.push(layer);
        self.current_layer = self.current_index;
        self.children.insert(self.current_index, vec![]);
    }

    pub fn pop_stack(&mut self) {
        let layer = self.layers.get_mut(self.current_layer).unwrap();
        self.current_layer = layer.parent_id;
    }

    pub(crate) fn resolve(&mut self, commands: &mut Commands, layout_cache: &mut LayoutCache) {
        let mut stack = vec![0];

        #[allow(clippy::manual_while_let_some)]
        while !stack.is_empty() {
            let layer_id = stack.pop().unwrap();
            let parent_id = self.layers.get(layer_id).map(|l| l.parent_id).unwrap();
            let parent_z = {
                self.layers
                    .get(parent_id)
                    .map(|l| l.custom_z)
                    .unwrap_or(0.0)
            };
            let children = self.children.get(&layer_id).cloned().unwrap_or_default();
            stack.extend(children);
            let layer = &mut self.layers[layer_id];
            let quad_count = layer.quads.len();
            layer.z = (layer_id.max(parent_id) as f32) + parent_z + layer.custom_z;
            layer.custom_z = if layer.custom_z > 0.0 {
                layer.custom_z
            } else {
                parent_z
            };
            for (i, quad) in layer.quads.iter_mut().enumerate() {
                let current_z = layer.z + (((i + 1) as f32 / quad_count as f32) * 0.999);
                match quad {
                    QuadOrMaterial::Material(entity) => {
                        commands.entity(*entity).insert(MaterialZ(current_z));
                    }
                    QuadOrMaterial::Quad(quad) => {
                        quad.z_index = current_z;
                        if quad.org_entity != Entity::PLACEHOLDER {
                            if let Some(layout) = layout_cache
                                .rect
                                .get_mut(&crate::node::WrappedIndex(quad.org_entity))
                            {
                                layout.z_index = Some(quad.z_index);
                            }
                        }
                    }
                }

                // Propagate z up the tree until we hit a parent with a Z value set.
                // let mut has_z = false;
                // let mut current_node = WrappedIndex(quad.get_entity());
                // while !has_z {
                //     if let Some(parent) = tree.get_parent(current_node) {
                //         if let Some(layout) = layout_cache.rect.get_mut(&parent) {
                //             if layout.z_index.is_none() {
                //                 layout.z_index = Some(current_z);
                //                 current_node = parent;
                //                 continue;
                //             }
                //         }
                //     }
                //     has_z = true;
                // }
            }
        }
    }

    pub fn debug(&self) {
        let mut items = vec![];
        let mut stack = vec![0];
        #[allow(clippy::manual_while_let_some)]
        while !stack.is_empty() {
            let layer_id = stack.pop().unwrap();
            let parent_id = self.layers.get(layer_id).map(|l| l.parent_id).unwrap_or(0);
            // let parent_z = {
            //     self.layers
            //         .get(self.parents.get(&parent_id).map(|id| *id).unwrap_or(0))
            //         .map(|l| l.z)
            //         .unwrap_or(0.0)
            // };
            let children = self.children.get(&layer_id).cloned().unwrap_or_default();
            stack.extend(children);
            let layer = &self.layers[layer_id];

            let qt = layer
                .quads
                .first()
                .map(|q| match q {
                    QuadOrMaterial::Quad(q) => q.quad_type,
                    _ => UIQuadType::None,
                })
                .unwrap_or(UIQuadType::None);
            // let rect = layer
            //     .quads
            //     .first()
            //     .map(|q| match q {
            //         QuadOrMaterial::Quad(q) => q.rect,
            //         _ => Rect::default(),
            //     })
            //     .unwrap_or(Rect::default());
            if qt != UIQuadType::None {
                // items.push((layer.z, format!("{}type: {:?}, layer_id: {}, parent_id: {}, parent_z: {}, z: {}, rect: {:?}", " ".repeat(parent_id + 1), qt, layer_id, parent_id, parent_z, layer.z, rect)));
            }
            if !layer.quads.is_empty() {
                // println!("{}Quads:", " ".repeat(parent_id + 1));
                let mut last_type = UIQuadType::None;
                for quad in layer.quads.iter() {
                    #[allow(clippy::single_match)]
                    match quad {
                        QuadOrMaterial::Quad(q) => {
                            if last_type != q.quad_type {
                                items.push((
                                    q.z_index,
                                    format!(
                                        "{}Q: {:?}, c: {}, rect: {:?}, z: {}",
                                        " ".repeat(parent_id + 1),
                                        q.quad_type,
                                        q.c,
                                        q.rect,
                                        q.z_index
                                    ),
                                ));
                                last_type = q.quad_type;
                            }
                        }
                        _ => {}
                    }
                }
            }
        }

        items.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());

        for (_, item) in items.iter() {
            println!("{}", item);
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = &ExtractedQuad> + '_ {
        self.layers
            .iter()
            .flat_map(|layer| &layer.quads)
            .filter_map(|quad| match &quad {
                QuadOrMaterial::Material(_) => None,
                QuadOrMaterial::Quad(quad) => match quad.quad_type {
                    UIQuadType::None => None,
                    _ => Some(quad),
                },
            })
    }
}

#[derive(Component)]
pub struct MaterialZ(pub f32);

#[derive(Default, Debug)]
pub struct ZLayer {
    pub z: f32,
    pub custom_z: f32,
    pub parent_id: usize,
    pub quads: Vec<QuadOrMaterial>,
}

#[derive(Debug, Component, PartialEq, Clone)]
pub struct QuadBatch {
    pub image_handle_id: Option<Handle<Image>>,
    pub font_handle_id: Option<Handle<KayakFont>>,
    pub quad_type: UIQuadType,
    pub type_id: u32,
    pub z_index: f32,
}

#[derive(Default, Resource)]
pub struct ImageBindGroups {
    values: HashMap<Handle<Image>, BindGroup>,
    font_values: HashMap<Handle<KayakFont>, BindGroup>,
    previous_sizes: HashMap<Handle<Image>, Vec2>,
}

#[derive(Component, Debug)]
pub struct UIViewBindGroup {
    pub value: BindGroup,
}

pub fn queue_ui_view_bind_groups(
    mut commands: Commands,
    render_device: Res<RenderDevice>,
    unified_pipeline: Res<UnifiedPipeline>,
    view_uniforms: Res<UIViewUniforms>,
    views: Query<Entity, With<UIExtractedView>>,
    globals_buffer: Res<GlobalsBuffer>,
) {
    if let (Some(view_binding), Some(globals)) = (
        view_uniforms.uniforms.binding(),
        globals_buffer.buffer.binding(),
    ) {
        for entity in &views {
            let view_bind_group = render_device.create_bind_group(
                Some("ui_view_bind_group"),
                &unified_pipeline.view_layout,
                &[
                    BindGroupEntry {
                        binding: 0,
                        resource: view_binding.clone(),
                    },
                    BindGroupEntry {
                        binding: 1,
                        resource: globals.clone(),
                    },
                ],
            );
            commands.entity(entity).insert(UIViewBindGroup {
                value: view_bind_group,
            });
        }
    }
}

#[derive(Resource, Default, Debug, Clone, Copy)]
pub struct QuadTypeOffsets {
    pub quad_type_offset: u32,
    pub text_sub_pixel_type_offset: u32,
    pub text_type_offset: u32,
    pub image_type_offset: u32,
    pub box_shadow_type_offset: u32,
}

pub fn queue_quad_types(
    mut commands: Commands,
    quad_pipeline: Res<UnifiedPipeline>,
    render_device: Res<RenderDevice>,
    render_queue: Res<RenderQueue>,
    mut quad_meta: ResMut<QuadMeta>,
) {
    quad_meta.types_buffer.clear();
    // sprite_meta.types_buffer.reserve(2, &render_device);
    let quad_type_offset = quad_meta.types_buffer.push(&QuadType {
        t: 0,
        _padding_1: 0,
        _padding_2: 0,
        _padding_3: 0,
    });
    let text_sub_pixel_type_offset = quad_meta.types_buffer.push(&QuadType {
        t: 1,
        _padding_1: 0,
        _padding_2: 0,
        _padding_3: 0,
    });
    let text_type_offset = quad_meta.types_buffer.push(&QuadType {
        t: 2,
        _padding_1: 0,
        _padding_2: 0,
        _padding_3: 0,
    });
    let image_type_offset = quad_meta.types_buffer.push(&QuadType {
        t: 3,
        _padding_1: 0,
        _padding_2: 0,
        _padding_3: 0,
    });
    let box_shadow_type_offset = quad_meta.types_buffer.push(&QuadType {
        t: 4,
        _padding_1: 0,
        _padding_2: 0,
        _padding_3: 0,
    });
    let quad_type_offsets = QuadTypeOffsets {
        quad_type_offset,
        text_sub_pixel_type_offset,
        text_type_offset,
        image_type_offset,
        box_shadow_type_offset,
    };
    commands.insert_resource(quad_type_offsets);

    quad_meta
        .types_buffer
        .write_buffer(&render_device, &render_queue);

    if let Some(type_binding) = quad_meta.types_buffer.binding() {
        quad_meta.types_bind_group = Some(render_device.create_bind_group(
            Some("quad_type_bind_group"),
            &quad_pipeline.types_layout,
            &[BindGroupEntry {
                binding: 0,
                resource: type_binding,
            }],
        ));
    }
}

#[derive(Resource, Default)]
pub struct PreviousClip {
    pub rect: Rect,
}

#[derive(Resource, Default)]
pub struct PreviousIndex {
    pub index: u32,
    pub last_clip: Rect,
}

#[derive(SystemParam)]
pub struct QueueQuads<'w, 's> {
    #[cfg(feature = "svg")]
    render_svgs: Res<'w, RenderSvgs>,
    opacity_layers: Res<'w, OpacityLayerManager>,
    commands: Commands<'w, 's>,
    draw_functions: Res<'w, DrawFunctions<TransparentUI>>,
    draw_functions_opacity: Res<'w, DrawFunctions<TransparentOpacityUI>>,
    render_device: Res<'w, RenderDevice>,
    render_queue: Res<'w, RenderQueue>,
    quad_meta: ResMut<'w, QuadMeta>,
    quad_pipeline: Res<'w, UnifiedPipeline>,
    pipelines: ResMut<'w, SpecializedRenderPipelines<UnifiedPipeline>>,
    pipeline_cache: Res<'w, PipelineCache>,
    extracted_quads: Res<'w, ExtractedQuads>,
    views: Query<
        'w,
        's,
        (
            Entity,
            &'static mut UIRenderPhase<TransparentUI>,
            &'static mut UIRenderPhase<TransparentOpacityUI>,
            &'static UIExtractedView,
        ),
    >,
    image_bind_groups: ResMut<'w, ImageBindGroups>,
    unified_pipeline: Res<'w, UnifiedPipeline>,
    gpu_images: Res<'w, RenderAssets<Image>>,
    font_texture_cache: Res<'w, FontTextureCache>,
    quad_type_offsets: Res<'w, QuadTypeOffsets>,
    prev_clip: ResMut<'w, PreviousClip>,
    prev_index: ResMut<'w, PreviousIndex>,
    view_uniforms: Res<'w, UIViewUniforms>,
}

pub fn queue_quads(queue_quads: QueueQuads) {
    let QueueQuads {
        #[cfg(feature = "svg")]
        render_svgs,
        opacity_layers,
        mut commands,
        draw_functions,
        draw_functions_opacity,
        render_device,
        render_queue,
        mut quad_meta,
        quad_pipeline,
        mut pipelines,
        pipeline_cache,
        extracted_quads,
        mut views,
        mut image_bind_groups,
        unified_pipeline,
        gpu_images,
        font_texture_cache,
        quad_type_offsets,
        mut prev_clip,
        mut prev_index,
        view_uniforms,
    } = queue_quads;

    if view_uniforms.uniforms.buffer().is_none() {
        return;
    }

    let mut extracted_quads = extracted_quads.iter().collect::<Vec<_>>();

    extracted_quads.sort_unstable_by(|a, b| a.z_index.partial_cmp(&b.z_index).unwrap());

    // let mut last_type = UIQuadType::None;
    // for (t, z, rect) in extracted_quads.iter().map(|q| (q.quad_type, q.z_index, q.rect)) {
    //     if !(t == UIQuadType::Text && last_type == UIQuadType::Text) {
    //         println!("type: {:?}, z: {}, rect: {:?}", t, z, rect);
    //         last_type = t;
    //     }
    // }

    let extracted_sprite_len = extracted_quads.len();
    // don't create buffers when there are no quads
    if extracted_sprite_len == 0 {
        return;
    }

    quad_meta.vertices.clear();
    quad_meta.vertices.reserve(
        extracted_sprite_len * QUAD_VERTEX_POSITIONS.len(),
        &render_device,
    );

    // Sort sprites by z for correct transparency and then by handle to improve batching
    // NOTE: This can be done independent of views by reasonably assuming that all 2D views look along the negative-z axis in world space
    let mut current_batch = QuadBatch {
        image_handle_id: None,
        font_handle_id: None,
        quad_type: UIQuadType::None,
        type_id: quad_type_offsets.quad_type_offset,
        z_index: 0.0,
    };
    let mut current_batch_entity = Entity::PLACEHOLDER;

    // Vertex buffer indices
    let mut index = 0;
    let mut item_start = 0;
    let mut item_end = 0;
    let mut old_item_start = 0;
    let mut current_clip = Rect::default();
    let mut last_clip = Rect::default();

    let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap();
    let draw_opacity_quad = draw_functions_opacity
        .read()
        .get_id::<DrawUITransparent>()
        .unwrap();
    for (camera_entity, mut transparent_phase, mut opacity_transparent_phase, view) in
        views.iter_mut()
    {
        let key = UnifiedPipelineKey {
            msaa: 1,
            hdr: view.hdr,
        };
        let spec_pipeline = pipelines.specialize(&pipeline_cache, &quad_pipeline, key);

        let mut last_quad = ExtractedQuad::default();

        for quad in extracted_quads.iter() {
            if quad.quad_type == UIQuadType::Clip {
                prev_clip.rect = quad.rect;
            }

            queue_quads_inner(
                &mut commands,
                &render_device,
                &font_texture_cache,
                &opacity_layers,
                &mut image_bind_groups,
                &gpu_images,
                &unified_pipeline,
                #[cfg(feature = "svg")]
                &render_svgs,
                &mut transparent_phase,
                &mut opacity_transparent_phase,
                draw_opacity_quad,
                draw_quad,
                spec_pipeline,
                &mut quad_meta,
                quad,
                camera_entity,
                *quad_type_offsets,
                &mut current_batch,
                &mut current_batch_entity,
                &mut index,
                &mut item_start,
                &mut item_end,
                &last_quad,
                &mut current_clip,
                &mut old_item_start,
                &mut last_clip,
            );

            last_quad = (*quad).clone();
        }

        #[allow(clippy::nonminimal_bool)]
        if last_quad.quad_type != UIQuadType::Clip
            && last_quad.quad_type != UIQuadType::OpacityLayer
            && last_quad.quad_type != UIQuadType::Clip
            && current_batch_entity != Entity::PLACEHOLDER
        {
            commands
                .entity(current_batch_entity)
                .insert(current_batch.clone());

            if last_quad.opacity_layer > 0 && last_quad.quad_type != UIQuadType::DrawOpacityLayer {
                opacity_transparent_phase.add(TransparentOpacityUI {
                    draw_function: draw_opacity_quad,
                    pipeline: spec_pipeline,
                    entity: current_batch_entity,
                    sort_key: FloatOrd(last_quad.z_index),
                    quad_type: last_quad.quad_type,
                    type_index: last_quad.quad_type.get_type_index(&quad_type_offsets),
                    rect: last_clip,
                    batch_range: Some(old_item_start..item_end),
                    opacity_layer: last_quad.opacity_layer,
                    dynamic_offset: None,
                });
            } else {
                transparent_phase.add(TransparentUI {
                    draw_function: draw_quad,
                    pipeline: spec_pipeline,
                    entity: current_batch_entity,
                    sort_key: FloatOrd(last_quad.z_index),
                    quad_type: last_quad.quad_type,
                    type_index: last_quad.quad_type.get_type_index(&quad_type_offsets),
                    rect: last_clip,
                    batch_range: Some(old_item_start..item_end),
                    dynamic_offset: None,
                });
            }
        }
    }

    quad_meta
        .vertices
        .write_buffer(&render_device, &render_queue);

    prev_index.index = index;
    prev_index.last_clip = last_clip;
}

pub fn queue_quads_inner(
    commands: &mut Commands,
    render_device: &RenderDevice,
    font_texture_cache: &FontTextureCache,
    opacity_layers: &OpacityLayerManager,
    image_bind_groups: &mut ImageBindGroups,
    gpu_images: &RenderAssets<Image>,
    unified_pipeline: &UnifiedPipeline,
    #[cfg(feature = "svg")] render_svgs: &RenderSvgs,
    transparent_phase: &mut UIRenderPhase<TransparentUI>,
    opacity_transparent_phase: &mut UIRenderPhase<TransparentOpacityUI>,
    draw_opacity_quad: DrawFunctionId,
    draw_quad: DrawFunctionId,
    spec_pipeline: CachedRenderPipelineId,
    quad_meta: &mut QuadMeta,
    quad: &ExtractedQuad,
    camera_entity: Entity,
    quad_type_offsets: QuadTypeOffsets,
    current_batch: &mut QuadBatch,
    current_batch_entity: &mut Entity,
    index: &mut u32,
    item_start: &mut u32,
    item_end: &mut u32,
    old_quad: &ExtractedQuad,
    current_clip: &mut Rect,
    old_item_start: &mut u32,
    last_clip: &mut Rect,
) {
    if camera_entity != quad.camera_entity {
        return;
    }

    // Ignore opacity layers
    if quad.quad_type == UIQuadType::OpacityLayer || quad.quad_type == UIQuadType::None {
        return;
    }

    if (current_clip.width() < 1.0 || current_clip.height() < 1.0)
        && quad.quad_type != UIQuadType::Clip
    {
        return;
    }

    if quad.quad_type == UIQuadType::Clip {
        // *last_clip = *current_clip;
        *current_clip = quad.rect;
    }

    let mut new_batch = QuadBatch {
        image_handle_id: quad.image.clone(),
        font_handle_id: quad.font_handle.clone(),
        quad_type: quad.quad_type,
        type_id: quad.quad_type.get_type_index(&quad_type_offsets),
        z_index: quad.z_index,
        // z_index: 0.0,
    };
    let sprite_rect = quad.rect;

    if (new_batch != *current_batch || current_batch.quad_type != quad.quad_type)
        || old_quad.quad_type == UIQuadType::Clip
        || quad.quad_type == UIQuadType::Clip
        || matches!(new_batch.quad_type, UIQuadType::DrawOpacityLayer)
    {
        if *current_batch_entity != Entity::PLACEHOLDER
            && old_quad.quad_type != UIQuadType::Clip
            && old_quad.quad_type != UIQuadType::OpacityLayer
        {
            // handle old batch
            commands
                .entity(*current_batch_entity)
                .insert(current_batch.clone());
        }

        // batch ended insert transparent phase object:
        if current_batch.quad_type != UIQuadType::Clip
            && current_batch.quad_type != UIQuadType::OpacityLayer
            && old_quad.quad_type != UIQuadType::Clip
            && *current_batch_entity != Entity::PLACEHOLDER
        {
            if old_quad.opacity_layer > 0 && old_quad.quad_type != UIQuadType::DrawOpacityLayer {
                opacity_transparent_phase.add(TransparentOpacityUI {
                    draw_function: draw_opacity_quad,
                    pipeline: spec_pipeline,
                    entity: *current_batch_entity,
                    sort_key: FloatOrd(old_quad.z_index),
                    quad_type: old_quad.quad_type,
                    type_index: current_batch.type_id,
                    rect: *current_clip,
                    batch_range: Some(*old_item_start..*item_end),
                    opacity_layer: old_quad.opacity_layer,
                    dynamic_offset: None,
                });
            } else {
                transparent_phase.add(TransparentUI {
                    draw_function: draw_quad,
                    pipeline: spec_pipeline,
                    entity: *current_batch_entity,
                    sort_key: FloatOrd(old_quad.z_index),
                    quad_type: old_quad.quad_type,
                    type_index: current_batch.type_id,
                    rect: *last_clip,
                    batch_range: Some(*old_item_start..*item_end),
                    dynamic_offset: None,
                });
            }

            *item_start = *index;
            *old_item_start = *item_end;
        }

        if let Some(image_handle) = quad.image.as_ref() {
            if let Some(gpu_image) = gpu_images.get(image_handle) {
                image_bind_groups
                    .values
                    .entry(image_handle.clone_weak())
                    .or_insert_with(|| {
                        render_device.create_bind_group(
                            Some("ui_image_bind_group"),
                            &unified_pipeline.image_layout,
                            &[
                                BindGroupEntry {
                                    binding: 0,
                                    resource: BindingResource::TextureView(&gpu_image.texture_view),
                                },
                                BindGroupEntry {
                                    binding: 1,
                                    resource: BindingResource::Sampler(&gpu_image.sampler),
                                },
                                BindGroupEntry {
                                    binding: 2,
                                    resource: BindingResource::TextureView(
                                        &unified_pipeline.empty_font_texture.texture_view,
                                    ),
                                },
                                BindGroupEntry {
                                    binding: 3,
                                    resource: BindingResource::Sampler(
                                        &unified_pipeline.empty_font_texture.sampler,
                                    ),
                                },
                            ],
                        )
                    });
            } else {
                // Skip unloaded texture.
                return;
            }
        }

        if let Some(font_handle) = quad.font_handle.as_ref() {
            if let Some(gpu_image) = font_texture_cache.get_gpu_image(font_handle, gpu_images) {
                new_batch.font_handle_id = Some(font_handle.clone_weak());
                image_bind_groups
                    .font_values
                    .entry(font_handle.clone_weak())
                    .or_insert_with(|| {
                        render_device.create_bind_group(
                            Some("ui_text_bind_group"),
                            &unified_pipeline.image_layout,
                            &[
                                BindGroupEntry {
                                    binding: 0,
                                    resource: BindingResource::TextureView(
                                        &unified_pipeline.default_image.0.texture_view,
                                    ),
                                },
                                BindGroupEntry {
                                    binding: 1,
                                    resource: BindingResource::Sampler(
                                        &unified_pipeline.default_image.0.sampler,
                                    ),
                                },
                                BindGroupEntry {
                                    binding: 2,
                                    resource: BindingResource::TextureView(&gpu_image.texture_view),
                                },
                                BindGroupEntry {
                                    binding: 3,
                                    resource: BindingResource::Sampler(&gpu_image.sampler),
                                },
                            ],
                        )
                    });
            }
        }

        if quad.quad_type == UIQuadType::DrawOpacityLayer {
            if let Some(layer) = opacity_layers.camera_layers.get(&camera_entity) {
                let image_handle = layer.get_image_handle(quad.opacity_layer);
                if let Some(gpu_image) = gpu_images.get(&image_handle) {
                    let new_image = if let Some(prev_size) =
                        image_bind_groups.previous_sizes.get(&image_handle)
                    {
                        if gpu_image.size != *prev_size {
                            image_bind_groups
                                .previous_sizes
                                .insert(image_handle.clone_weak(), gpu_image.size);
                            true
                        } else {
                            false
                        }
                    } else {
                        image_bind_groups
                            .previous_sizes
                            .insert(image_handle.clone_weak(), gpu_image.size);
                        true
                    };

                    if new_image {
                        image_bind_groups.values.insert(
                            image_handle.clone_weak(),
                            render_device.create_bind_group(
                                Some("draw_opacity_layer_bind_group"),
                                &unified_pipeline.image_layout,
                                &[
                                    BindGroupEntry {
                                        binding: 0,
                                        resource: BindingResource::TextureView(
                                            &gpu_image.texture_view,
                                        ),
                                    },
                                    BindGroupEntry {
                                        binding: 1,
                                        resource: BindingResource::Sampler(&gpu_image.sampler),
                                    },
                                    BindGroupEntry {
                                        binding: 2,
                                        resource: BindingResource::TextureView(
                                            &unified_pipeline.empty_font_texture.texture_view,
                                        ),
                                    },
                                    BindGroupEntry {
                                        binding: 3,
                                        resource: BindingResource::Sampler(
                                            &unified_pipeline.empty_font_texture.sampler,
                                        ),
                                    },
                                ],
                            ),
                        );
                    }

                    new_batch.image_handle_id = Some(image_handle.clone_weak());
                    // bevy::prelude::info!("Attaching opacity layer with index: {} with view: {:?}", quad.opacity_layer, gpu_image.texture_view);
                } else {
                    return;
                }
            }
        }

        // Start new batch
        *current_batch = new_batch;
        *last_clip = *current_clip;
        if current_batch.quad_type != UIQuadType::Clip
            && current_batch.quad_type != UIQuadType::OpacityLayer
        {
            *current_batch_entity = commands.spawn(current_batch.clone()).id();
        }
    }

    if matches!(current_batch.quad_type, UIQuadType::Clip) {
        return;
    }

    #[cfg(feature = "svg")]
    if let (Some(svg_handle), color) = (quad.svg_handle.0.as_ref(), quad.svg_handle.1.as_ref()) {
        if let Some((svg, mesh)) = render_svgs.get(&svg_handle.id()) {
            let new_height = (svg.view_box.h as f32 / svg.view_box.w as f32) * sprite_rect.size().x;
            let svg_scale_x = sprite_rect.size().x / svg.view_box.w as f32;
            let svg_scale_y = new_height / svg.view_box.h as f32;
            let positions = mesh
                .attribute(Mesh::ATTRIBUTE_POSITION)
                .unwrap()
                .as_float3()
                .unwrap();
            let colors = match mesh.attribute(Mesh::ATTRIBUTE_COLOR).unwrap() {
                VertexAttributeValues::Float32x4(d) => Some(d),
                _ => None,
            }
            .unwrap();
            let indices = mesh.indices().unwrap();

            for index in indices.iter() {
                let position = positions[index];
                let color = if let Some(color) = color {
                    [color.r(), color.g(), color.b(), color.a()]
                } else {
                    colors[index]
                };
                let world = Mat4::from_scale_rotation_translation(
                    Vec3::new(svg_scale_x, svg_scale_y, 1.0), //sprite_rect.size().extend(1.0),
                    Quat::default(),
                    sprite_rect.min.extend(0.0),
                );
                let final_position = (world
                    * Vec4::new(
                        position[0],  // - 34.5,
                        -position[1], // - 95.0,
                        position[2],
                        1.0,
                    ))
                .truncate();

                quad_meta.vertices.push(QuadVertex {
                    position: final_position.into(),
                    color,
                    uv: [0.0; 4],
                    pos_size: [
                        sprite_rect.min.x,
                        sprite_rect.min.y,
                        sprite_rect.size().x,
                        new_height,
                    ],
                });
            }
            *index += indices.len() as u32;
            *item_end = *index;
        }
        return;
    }

    let color = quad.color.as_linear_rgba_f32();

    let uv_min = quad.uv_min.unwrap_or(Vec2::ZERO);
    let uv_max = quad.uv_max.unwrap_or(Vec2::ONE);

    let bottom_left = Vec4::new(
        uv_min.x,
        uv_min.y,
        quad.char_id as f32,
        quad.border_radius.bottom_left,
    );
    let top_left = Vec4::new(
        uv_min.x,
        uv_max.y,
        quad.char_id as f32,
        quad.border_radius.top_left,
    );
    let top_right = Vec4::new(
        uv_max.x,
        uv_max.y,
        quad.char_id as f32,
        quad.border_radius.top_right,
    );
    let bottom_right = Vec4::new(
        uv_max.x,
        uv_min.y,
        quad.char_id as f32,
        quad.border_radius.bottom_right,
    );

    let uvs: [[f32; 4]; 6] = [
        top_left.into(),
        bottom_right.into(),
        bottom_left.into(),
        top_left.into(),
        top_right.into(),
        bottom_right.into(),
    ];

    const QUAD_INDICES: [usize; 6] = [0, 2, 3, 0, 1, 2];

    const QUAD_VERTEX_POSITIONS: [Vec2; 4] = [
        Vec2::new(0.0, 0.0),
        Vec2::new(1.0, 0.0),
        Vec2::new(1.0, 1.0),
        Vec2::new(0.0, 1.0),
    ];

    for (index, vertex_index) in QUAD_INDICES.iter().enumerate() {
        let vertex_position = QUAD_VERTEX_POSITIONS[*vertex_index];
        let world = Mat4::from_scale_rotation_translation(
            sprite_rect.size().extend(1.0),
            Quat::default(),
            sprite_rect.min.extend(0.0),
        );
        let final_position = (world * vertex_position.extend(0.0).extend(1.0)).truncate();
        quad_meta.vertices.push(QuadVertex {
            position: final_position.into(),
            color,
            uv: uvs[index],
            pos_size: [
                sprite_rect.min.x,
                sprite_rect.min.y,
                sprite_rect.size().x,
                sprite_rect.size().y,
            ],
        });
    }

    *index += QUAD_INDICES.len() as u32;
    *item_end = *index;
}

pub type DrawUI = (
    SetItemPipeline,
    SetUIViewBindGroup<TransparentUI, 0>,
    DrawUIDraw<TransparentUI>,
);

pub type DrawUITransparent = (
    SetItemPipeline,
    SetUIViewBindGroup<TransparentOpacityUI, 0>,
    DrawUIDraw<TransparentOpacityUI>,
);

pub struct SetUIViewBindGroup<T, const I: usize> {
    phantom: PhantomData<T>,
}
impl<T: PhaseItem, const I: usize> RenderCommand<T> for SetUIViewBindGroup<T, I> {
    type Param = ();
    type ViewQuery = (Read<UIViewUniformOffset>, Read<UIViewBindGroup>);
    type ItemQuery = ();

    #[inline]
    fn render<'w>(
        _item: &T,
        (view_uniform, ui_view_bind_group): ROQueryItem<'w, Self::ViewQuery>,
        _: Option<()>,
        _: SystemParamItem<'w, '_, Self::Param>,
        pass: &mut TrackedRenderPass<'w>,
    ) -> RenderCommandResult {
        pass.set_bind_group(I, &ui_view_bind_group.value, &[view_uniform.offset]);
        RenderCommandResult::Success
    }
}

#[derive(Default)]
pub struct DrawUIDraw<T> {
    phantom: PhantomData<T>,
}

impl<T: PhaseItem + TransparentUIGeneric> RenderCommand<T> for DrawUIDraw<T> {
    type Param = (SRes<QuadMeta>, SRes<UnifiedPipeline>, SRes<ImageBindGroups>);

    type ViewQuery = Read<UIExtractedView>;
    type ItemQuery = Read<QuadBatch>;

    fn render<'w>(
        item: &T,
        view: bevy::ecs::query::ROQueryItem<'w, Self::ViewQuery>,
        batch: Option<bevy::ecs::query::ROQueryItem<'w, Self::ItemQuery>>,
        param: bevy::ecs::system::SystemParamItem<'w, '_, Self::Param>,
        pass: &mut TrackedRenderPass<'w>,
    ) -> RenderCommandResult {
        let Some(batch) = batch else {
            return RenderCommandResult::Failure;
        };
        let (quad_meta, unified_pipeline, image_bind_groups) = param;

        let quad_meta = quad_meta.into_inner();
        let window_size = (view.viewport.z as f32, view.viewport.w as f32);
        let rect = item.get_rect();
        let x = rect.min.x as u32;
        let y = rect.min.y as u32;
        let mut width = rect.width() as u32;
        let mut height = rect.height() as u32;

        width = width.min(window_size.0 as u32);
        height = height.min(window_size.1 as u32);
        if !(width == 0 || height == 0 || x > window_size.0 as u32 || y > window_size.1 as u32) {
            if x + width >= window_size.0 as u32 {
                width = window_size.0 as u32 - x;
            }
            if y + height >= window_size.1 as u32 {
                height = window_size.1 as u32 - y;
            }
            pass.set_scissor_rect(x, y, width, height);
        }

        let vertices_slice = quad_meta.vertices.buffer().unwrap().slice(..);
        pass.set_vertex_buffer(0, vertices_slice);

        pass.set_bind_group(
            2,
            quad_meta.types_bind_group.as_ref().unwrap(),
            &[item.get_type_index()],
        );

        let unified_pipeline = unified_pipeline.into_inner();

        let image_bind_groups = image_bind_groups.into_inner();
        if let Some(image_handle) = batch.image_handle_id.as_ref() {
            if let Some(bind_group) = image_bind_groups.values.get(image_handle) {
                pass.set_bind_group(1, bind_group, &[]);
            } else {
                pass.set_bind_group(1, &unified_pipeline.default_image.1, &[]);
            }
        } else if let Some(bind_group) = batch
            .font_handle_id
            .as_ref()
            .and_then(|h| image_bind_groups.font_values.get(h))
        {
            pass.set_bind_group(1, bind_group, &[]);
        } else {
            pass.set_bind_group(1, &unified_pipeline.default_image.1, &[]);
        }
        pass.draw(item.batch_range().clone(), 0..1);

        RenderCommandResult::Success
    }
}