Skip to main content

bevy_sprite_render/render/
mod.rs

1use core::ops::Range;
2
3use bevy_asset::{load_embedded_asset, AssetEvent, AssetId, AssetServer, Handle};
4use bevy_camera::CompositingSpace;
5use bevy_color::{ColorToComponents, LinearRgba};
6use bevy_core_pipeline::{
7    core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT},
8    tonemapping::{
9        get_lut_bind_group_layout_entries, get_lut_bindings, DebandDither, Tonemapping,
10        TonemappingLuts,
11    },
12};
13use bevy_derive::{Deref, DerefMut};
14use bevy_ecs::{
15    prelude::*,
16    query::ROQueryItem,
17    system::{lifetimeless::*, SystemParamItem},
18};
19use bevy_image::Image;
20use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4};
21use bevy_mesh::VertexBufferLayout;
22use bevy_platform::collections::HashMap;
23use bevy_render::{
24    camera::ExtractedCamera,
25    view::{RenderVisibleEntities, ResolvedCompositingSpace, RetainedViewEntity},
26};
27use bevy_render::{
28    render_asset::RenderAssets,
29    render_phase::{
30        DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand, RenderCommandResult,
31        SetItemPipeline, TrackedRenderPass, ViewSortedRenderPhases,
32    },
33    render_resource::{
34        binding_types::{sampler, texture_2d, uniform_buffer},
35        *,
36    },
37    renderer::{RenderDevice, RenderQueue},
38    texture::{FallbackImage, GpuImage},
39    view::{
40        texture_format_from_code, texture_format_to_code, ExtractedView, Msaa, ViewUniform,
41        ViewUniformOffset, ViewUniforms,
42    },
43    Extract,
44};
45use bevy_shader::{Shader, ShaderDefVal};
46use bevy_sprite::Sprite;
47use bevy_transform::components::GlobalTransform;
48use bevy_utils::default;
49use bytemuck::{Pod, Zeroable};
50use fixedbitset::FixedBitSet;
51
52#[derive(impl bevy_ecs::component::Component for SpritePipeline where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<SpritePipeline>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<SpritePipeline>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for SpritePipeline where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
53pub struct SpritePipeline {
54    view_layout: BindGroupLayoutDescriptor,
55    material_layout: BindGroupLayoutDescriptor,
56    shader: Handle<Shader>,
57}
58
59pub fn init_sprite_pipeline(mut commands: Commands, asset_server: Res<AssetServer>) {
60    let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
61    let view_layout = BindGroupLayoutDescriptor::new(
62        "sprite_view_layout",
63        &BindGroupLayoutEntries::sequential(
64            ShaderStages::VERTEX_FRAGMENT,
65            (
66                uniform_buffer::<ViewUniform>(true),
67                tonemapping_lut_entries[0].visibility(ShaderStages::FRAGMENT),
68                tonemapping_lut_entries[1].visibility(ShaderStages::FRAGMENT),
69            ),
70        ),
71    );
72
73    let material_layout = BindGroupLayoutDescriptor::new(
74        "sprite_material_layout",
75        &BindGroupLayoutEntries::sequential(
76            ShaderStages::FRAGMENT,
77            (
78                texture_2d(TextureSampleType::Float { filterable: true }),
79                sampler(SamplerBindingType::Filtering),
80            ),
81        ),
82    );
83
84    commands.insert_resource(SpritePipeline {
85        view_layout,
86        material_layout,
87        shader: {
    let (path, asset_server) =
        {
            let path =
                {
                    {
                        let crate_name =
                            "bevy_sprite_render::render".split(':').next().unwrap();
                        ::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
                            "src".as_ref(), "src/render/mod.rs".as_ref(),
                            "sprite.wesl".as_ref())
                    }
                };
            let path =
                ::bevy_asset::AssetPath::from_path_buf(path).with_source("embedded");
            let asset_server =
                ::bevy_asset::io::embedded::GetAssetServer::get_asset_server(asset_server.as_ref());
            (path, asset_server)
        };
    asset_server.load(path)
}load_embedded_asset!(asset_server.as_ref(), "sprite.wesl"),
88    });
89}
90
91#[repr(transparent)]
pub struct SpritePipelineKey(<SpritePipelineKey as
    ::bitflags::__private::PublicFlags>::Internal);
#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for SpritePipelineKey {
}
#[automatically_derived]impl ::core::clone::Clone for SpritePipelineKey {
    #[inline]
    fn clone(&self) -> Self {
        let _:
                ::core::clone::AssertParamIsClone<<SpritePipelineKey as
                ::bitflags::__private::PublicFlags>::Internal>;
        *self
    }
}
#[automatically_derived]impl ::core::marker::Copy for SpritePipelineKey { }
#[automatically_derived]impl ::core::fmt::Debug for SpritePipelineKey {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "SpritePipelineKey", &&self.0)
    }
}
#[automatically_derived]impl ::core::marker::StructuralPartialEq for
    SpritePipelineKey {
}
#[automatically_derived]impl ::core::cmp::PartialEq for SpritePipelineKey {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
#[automatically_derived]impl ::core::cmp::Eq for SpritePipelineKey {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _:
                ::core::cmp::AssertParamIsEq<<SpritePipelineKey as
                ::bitflags::__private::PublicFlags>::Internal>;
    }
}
#[automatically_derived]impl ::core::hash::Hash for SpritePipelineKey {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
impl SpritePipelineKey {
    pub const NONE: Self = Self::from_bits_retain(0);
    pub const TONEMAP_IN_SHADER: Self = Self::from_bits_retain(1 << 0);
    pub const DEBAND_DITHER: Self = Self::from_bits_retain(1 << 1);
    pub const SRGB_COMPOSITING: Self = Self::from_bits_retain(1 << 2);
    pub const OKLAB_COMPOSITING: Self = Self::from_bits_retain(1 << 3);
    pub const COLOR_TARGET_FORMAT_RESERVED_BITS: Self =
        Self::from_bits_retain(Self::COLOR_TARGET_FORMAT_MASK_BITS <<
                Self::COLOR_TARGET_FORMAT_SHIFT_BITS);
    pub const MSAA_RESERVED_BITS: Self =
        Self::from_bits_retain(Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS);
    pub const TONEMAP_METHOD_RESERVED_BITS: Self =
        Self::from_bits_retain(Self::TONEMAP_METHOD_MASK_BITS <<
                Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_LINEAR: Self =
        Self::from_bits_retain(0 << Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_REINHARD: Self =
        Self::from_bits_retain(1 << Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_REINHARD_LUMINANCE: Self =
        Self::from_bits_retain(2 << Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_ACES_FITTED: Self =
        Self::from_bits_retain(3 << Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_AGX: Self =
        Self::from_bits_retain(4 << Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM: Self =
        Self::from_bits_retain(5 << Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_TONY_MC_MAPFACE: Self =
        Self::from_bits_retain(6 << Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_BLENDER_FILMIC: Self =
        Self::from_bits_retain(7 << Self::TONEMAP_METHOD_SHIFT_BITS);
    pub const TONEMAP_METHOD_PBR_NEUTRAL: Self =
        Self::from_bits_retain(8 << Self::TONEMAP_METHOD_SHIFT_BITS);
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
impl ::bitflags::Flags for SpritePipelineKey {
    const FLAGS: &'static [::bitflags::Flag<SpritePipelineKey>] =
        {
            mod __bitflags_flag_names {
                #[allow(unused_imports)]
                use super::*;
                pub(super) const NONE: &'static str = "NONE";
                pub(super) const TONEMAP_IN_SHADER: &'static str =
                    "TONEMAP_IN_SHADER";
                pub(super) const DEBAND_DITHER: &'static str =
                    "DEBAND_DITHER";
                pub(super) const SRGB_COMPOSITING: &'static str =
                    "SRGB_COMPOSITING";
                pub(super) const OKLAB_COMPOSITING: &'static str =
                    "OKLAB_COMPOSITING";
                pub(super) const COLOR_TARGET_FORMAT_RESERVED_BITS:
                    &'static str =
                    "COLOR_TARGET_FORMAT_RESERVED_BITS";
                pub(super) const MSAA_RESERVED_BITS: &'static str =
                    "MSAA_RESERVED_BITS";
                pub(super) const TONEMAP_METHOD_RESERVED_BITS: &'static str =
                    "TONEMAP_METHOD_RESERVED_BITS";
                pub(super) const TONEMAP_METHOD_LINEAR: &'static str =
                    "TONEMAP_METHOD_LINEAR";
                pub(super) const TONEMAP_METHOD_REINHARD: &'static str =
                    "TONEMAP_METHOD_REINHARD";
                pub(super) const TONEMAP_METHOD_REINHARD_LUMINANCE:
                    &'static str =
                    "TONEMAP_METHOD_REINHARD_LUMINANCE";
                pub(super) const TONEMAP_METHOD_ACES_FITTED: &'static str =
                    "TONEMAP_METHOD_ACES_FITTED";
                pub(super) const TONEMAP_METHOD_AGX: &'static str =
                    "TONEMAP_METHOD_AGX";
                pub(super) const
                    TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM:
                    &'static str =
                    "TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM";
                pub(super) const TONEMAP_METHOD_TONY_MC_MAPFACE: &'static str
                    =
                    "TONEMAP_METHOD_TONY_MC_MAPFACE";
                pub(super) const TONEMAP_METHOD_BLENDER_FILMIC: &'static str =
                    "TONEMAP_METHOD_BLENDER_FILMIC";
                pub(super) const TONEMAP_METHOD_PBR_NEUTRAL: &'static str =
                    "TONEMAP_METHOD_PBR_NEUTRAL";
            }
            &[{
                            ::bitflags::Flag::new(__bitflags_flag_names::NONE,
                                SpritePipelineKey::NONE)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_IN_SHADER,
                                SpritePipelineKey::TONEMAP_IN_SHADER)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::DEBAND_DITHER,
                                SpritePipelineKey::DEBAND_DITHER)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::SRGB_COMPOSITING,
                                SpritePipelineKey::SRGB_COMPOSITING)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::OKLAB_COMPOSITING,
                                SpritePipelineKey::OKLAB_COMPOSITING)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::COLOR_TARGET_FORMAT_RESERVED_BITS,
                                SpritePipelineKey::COLOR_TARGET_FORMAT_RESERVED_BITS)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::MSAA_RESERVED_BITS,
                                SpritePipelineKey::MSAA_RESERVED_BITS)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_RESERVED_BITS,
                                SpritePipelineKey::TONEMAP_METHOD_RESERVED_BITS)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_LINEAR,
                                SpritePipelineKey::TONEMAP_METHOD_LINEAR)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_REINHARD,
                                SpritePipelineKey::TONEMAP_METHOD_REINHARD)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_REINHARD_LUMINANCE,
                                SpritePipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_ACES_FITTED,
                                SpritePipelineKey::TONEMAP_METHOD_ACES_FITTED)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_AGX,
                                SpritePipelineKey::TONEMAP_METHOD_AGX)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM,
                                SpritePipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_TONY_MC_MAPFACE,
                                SpritePipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_BLENDER_FILMIC,
                                SpritePipelineKey::TONEMAP_METHOD_BLENDER_FILMIC)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::TONEMAP_METHOD_PBR_NEUTRAL,
                                SpritePipelineKey::TONEMAP_METHOD_PBR_NEUTRAL)
                        }]
        };
    type Bits = u32;
    fn bits(&self) -> u32 { SpritePipelineKey::bits(self) }
    fn from_bits_retain(bits: u32) -> SpritePipelineKey {
        SpritePipelineKey::from_bits_retain(bits)
    }
    fn all_named() -> SpritePipelineKey {
        const ALL_NAMED: u32 =
            {
                let mut truncated = <u32 as ::bitflags::Bits>::EMPTY;
                let mut i = 0;
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SpritePipelineKey as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                let _ = i;
                truncated
            };
        SpritePipelineKey::from_bits_retain(ALL_NAMED)
    }
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
const _: () =
    {
        #[repr(transparent)]
        pub struct InternalBitFlags(u32);
        #[automatically_derived]
        #[doc(hidden)]
        unsafe impl ::core::clone::TrivialClone for InternalBitFlags { }
        #[automatically_derived]
        impl ::core::clone::Clone for InternalBitFlags {
            #[inline]
            fn clone(&self) -> Self {
                let _: ::core::clone::AssertParamIsClone<u32>;
                *self
            }
        }
        #[automatically_derived]
        impl ::core::marker::Copy for InternalBitFlags { }
        #[automatically_derived]
        impl ::core::marker::StructuralPartialEq for InternalBitFlags { }
        #[automatically_derived]
        impl ::core::cmp::PartialEq for InternalBitFlags {
            #[inline]
            fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
        }
        #[automatically_derived]
        impl ::core::cmp::Eq for InternalBitFlags {
            #[inline]
            #[doc(hidden)]
            #[coverage(off)]
            fn assert_fields_are_eq(&self) {
                let _: ::core::cmp::AssertParamIsEq<u32>;
            }
        }
        #[automatically_derived]
        impl ::core::cmp::PartialOrd for InternalBitFlags {
            #[inline]
            fn partial_cmp(&self, other: &Self)
                -> ::core::option::Option<::core::cmp::Ordering> {
                ::core::option::Option::Some(::core::cmp::Ord::cmp(self,
                        other))
            }
        }
        #[automatically_derived]
        impl ::core::cmp::Ord for InternalBitFlags {
            #[inline]
            fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
                ::core::cmp::Ord::cmp(&self.0, &other.0)
            }
        }
        #[automatically_derived]
        impl ::core::hash::Hash for InternalBitFlags {
            #[inline]
            fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
                ::core::hash::Hash::hash(&self.0, state)
            }
        }
        impl ::bitflags::__private::PublicFlags for SpritePipelineKey {
            type Primitive = u32;
            type Internal = InternalBitFlags;
        }
        impl ::bitflags::__private::core::default::Default for
            InternalBitFlags {
            #[inline]
            fn default() -> Self { InternalBitFlags::empty() }
        }
        impl ::bitflags::__private::core::fmt::Debug for InternalBitFlags {
            fn fmt(&self,
                f: &mut ::bitflags::__private::core::fmt::Formatter<'_>)
                -> ::bitflags::__private::core::fmt::Result {
                if self.is_empty() {
                    f.write_fmt(format_args!("{0:#x}",
                            <u32 as ::bitflags::Bits>::EMPTY))
                } else {
                    ::bitflags::__private::core::fmt::Display::fmt(self, f)
                }
            }
        }
        impl ::bitflags::__private::core::fmt::Display for InternalBitFlags {
            fn fmt(&self,
                f: &mut ::bitflags::__private::core::fmt::Formatter<'_>)
                -> ::bitflags::__private::core::fmt::Result {
                ::bitflags::parser::to_writer(&SpritePipelineKey(*self), f)
            }
        }
        impl ::bitflags::__private::core::str::FromStr for InternalBitFlags {
            type Err = ::bitflags::parser::ParseError;
            fn from_str(s: &str)
                ->
                    ::bitflags::__private::core::result::Result<Self,
                    Self::Err> {
                ::bitflags::parser::from_str::<SpritePipelineKey>(s).map(|flags|
                        flags.0)
            }
        }
        impl ::bitflags::__private::core::convert::AsRef<u32> for
            InternalBitFlags {
            fn as_ref(&self) -> &u32 { &self.0 }
        }
        impl ::bitflags::__private::core::convert::From<u32> for
            InternalBitFlags {
            fn from(bits: u32) -> Self { Self::from_bits_retain(bits) }
        }
        impl InternalBitFlags {
            /// Get a flags value with all bits unset.
            #[inline]
            pub const fn empty() -> Self {
                Self(<u32 as ::bitflags::Bits>::EMPTY)
            }
            /// Get a flags value with all known bits set.
            #[inline]
            pub const fn all() -> Self {
                const ALL: InternalBitFlags =
                    {
                        let mut truncated = <u32 as ::bitflags::Bits>::EMPTY;
                        let mut _i = 0;
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SpritePipelineKey as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        InternalBitFlags(truncated)
                    };
                ALL
            }
            /// Get the underlying bits value.
            ///
            /// The returned value is exactly the bits set in this flags value.
            #[inline]
            pub const fn bits(&self) -> u32 { self.0 }
            /// Convert from a bits value.
            ///
            /// This method will return `None` if any unknown bits are set.
            #[inline]
            pub const fn from_bits(bits: u32)
                -> ::bitflags::__private::core::option::Option<Self> {
                let truncated = Self::from_bits_truncate(bits).0;
                if truncated == bits {
                    ::bitflags::__private::core::option::Option::Some(Self(bits))
                } else { ::bitflags::__private::core::option::Option::None }
            }
            /// Convert from a bits value, unsetting any unknown bits.
            #[inline]
            pub const fn from_bits_truncate(bits: u32) -> Self {
                Self(bits & Self::all().0)
            }
            /// Convert from a bits value exactly.
            #[inline]
            pub const fn from_bits_retain(bits: u32) -> Self { Self(bits) }
            /// Get a flags value with the bits of a flag with the given name set.
            ///
            /// This method will return `None` if `name` is empty or doesn't
            /// correspond to any named flag.
            #[inline]
            pub fn from_name(name: &str)
                -> ::bitflags::__private::core::option::Option<Self> {
                mod __bitflags_flag_names {
                    #[allow(unused_imports)]
                    use super::*;
                    pub(super) const NONE: &'static str = "NONE";
                    pub(super) const TONEMAP_IN_SHADER: &'static str =
                        "TONEMAP_IN_SHADER";
                    pub(super) const DEBAND_DITHER: &'static str =
                        "DEBAND_DITHER";
                    pub(super) const SRGB_COMPOSITING: &'static str =
                        "SRGB_COMPOSITING";
                    pub(super) const OKLAB_COMPOSITING: &'static str =
                        "OKLAB_COMPOSITING";
                    pub(super) const COLOR_TARGET_FORMAT_RESERVED_BITS:
                        &'static str =
                        "COLOR_TARGET_FORMAT_RESERVED_BITS";
                    pub(super) const MSAA_RESERVED_BITS: &'static str =
                        "MSAA_RESERVED_BITS";
                    pub(super) const TONEMAP_METHOD_RESERVED_BITS: &'static str
                        =
                        "TONEMAP_METHOD_RESERVED_BITS";
                    pub(super) const TONEMAP_METHOD_LINEAR: &'static str =
                        "TONEMAP_METHOD_LINEAR";
                    pub(super) const TONEMAP_METHOD_REINHARD: &'static str =
                        "TONEMAP_METHOD_REINHARD";
                    pub(super) const TONEMAP_METHOD_REINHARD_LUMINANCE:
                        &'static str =
                        "TONEMAP_METHOD_REINHARD_LUMINANCE";
                    pub(super) const TONEMAP_METHOD_ACES_FITTED: &'static str =
                        "TONEMAP_METHOD_ACES_FITTED";
                    pub(super) const TONEMAP_METHOD_AGX: &'static str =
                        "TONEMAP_METHOD_AGX";
                    pub(super) const
                        TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM:
                        &'static str =
                        "TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM";
                    pub(super) const TONEMAP_METHOD_TONY_MC_MAPFACE:
                        &'static str =
                        "TONEMAP_METHOD_TONY_MC_MAPFACE";
                    pub(super) const TONEMAP_METHOD_BLENDER_FILMIC: &'static str
                        =
                        "TONEMAP_METHOD_BLENDER_FILMIC";
                    pub(super) const TONEMAP_METHOD_PBR_NEUTRAL: &'static str =
                        "TONEMAP_METHOD_PBR_NEUTRAL";
                }
                {
                    {
                        if name == __bitflags_flag_names::NONE {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::NONE.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::TONEMAP_IN_SHADER {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_IN_SHADER.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::DEBAND_DITHER {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::DEBAND_DITHER.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::SRGB_COMPOSITING {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::SRGB_COMPOSITING.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::OKLAB_COMPOSITING {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::OKLAB_COMPOSITING.bits()));
                        }
                    };
                };
                {
                    {
                        if name ==
                                __bitflags_flag_names::COLOR_TARGET_FORMAT_RESERVED_BITS {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::COLOR_TARGET_FORMAT_RESERVED_BITS.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::MSAA_RESERVED_BITS {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::MSAA_RESERVED_BITS.bits()));
                        }
                    };
                };
                {
                    {
                        if name ==
                                __bitflags_flag_names::TONEMAP_METHOD_RESERVED_BITS {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_RESERVED_BITS.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::TONEMAP_METHOD_LINEAR {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_LINEAR.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::TONEMAP_METHOD_REINHARD {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_REINHARD.bits()));
                        }
                    };
                };
                {
                    {
                        if name ==
                                __bitflags_flag_names::TONEMAP_METHOD_REINHARD_LUMINANCE {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::TONEMAP_METHOD_ACES_FITTED
                            {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_ACES_FITTED.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::TONEMAP_METHOD_AGX {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_AGX.bits()));
                        }
                    };
                };
                {
                    {
                        if name ==
                                __bitflags_flag_names::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
                            {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM.bits()));
                        }
                    };
                };
                {
                    {
                        if name ==
                                __bitflags_flag_names::TONEMAP_METHOD_TONY_MC_MAPFACE {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE.bits()));
                        }
                    };
                };
                {
                    {
                        if name ==
                                __bitflags_flag_names::TONEMAP_METHOD_BLENDER_FILMIC {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_BLENDER_FILMIC.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::TONEMAP_METHOD_PBR_NEUTRAL
                            {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_PBR_NEUTRAL.bits()));
                        }
                    };
                };
                let _ = name;
                ::bitflags::__private::core::option::Option::None
            }
            /// Whether all bits in `self` are unset.
            #[inline]
            pub const fn is_empty(&self) -> bool {
                self.0 == <u32 as ::bitflags::Bits>::EMPTY
            }
            /// Whether all known bits in this flags value are set.
            #[inline]
            pub const fn is_all(&self) -> bool {
                Self::all().0 | self.0 == self.0
            }
            /// Whether any set bits in `other` are also set in `self`.
            #[inline]
            pub const fn intersects(&self, other: Self) -> bool {
                self.0 & other.0 != <u32 as ::bitflags::Bits>::EMPTY
            }
            /// Whether all set bits in `other` are also set in `self`.
            #[inline]
            pub const fn contains(&self, other: Self) -> bool {
                self.0 & other.0 == other.0
            }
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            pub fn insert(&mut self, other: Self) {
                *self = Self(self.0).union(other);
            }
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `remove` won't truncate `other`, but the `!` operator will.
            #[inline]
            pub fn remove(&mut self, other: Self) {
                *self = Self(self.0).difference(other);
            }
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            pub fn toggle(&mut self, other: Self) {
                *self = Self(self.0).symmetric_difference(other);
            }
            /// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
            #[inline]
            pub fn set(&mut self, other: Self, value: bool) {
                if value { self.insert(other); } else { self.remove(other); }
            }
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn intersection(self, other: Self) -> Self {
                Self(self.0 & other.0)
            }
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn union(self, other: Self) -> Self {
                Self(self.0 | other.0)
            }
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            #[must_use]
            pub const fn difference(self, other: Self) -> Self {
                Self(self.0 & !other.0)
            }
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn symmetric_difference(self, other: Self) -> Self {
                Self(self.0 ^ other.0)
            }
            /// The bitwise negation (`!`) of the bits in `self`, truncating the result.
            #[inline]
            #[must_use]
            pub const fn complement(self) -> Self {
                Self::from_bits_truncate(!self.0)
            }
        }
        impl ::bitflags::__private::core::fmt::Binary for InternalBitFlags {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::Binary::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::Octal for InternalBitFlags {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::Octal::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::LowerHex for InternalBitFlags {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::LowerHex::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::UpperHex for InternalBitFlags {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::UpperHex::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::ops::BitOr for InternalBitFlags {
            type Output = Self;
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            fn bitor(self, other: InternalBitFlags) -> Self {
                self.union(other)
            }
        }
        impl ::bitflags::__private::core::ops::BitOrAssign for
            InternalBitFlags {
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            fn bitor_assign(&mut self, other: Self) { self.insert(other); }
        }
        impl ::bitflags::__private::core::ops::BitXor for InternalBitFlags {
            type Output = Self;
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            fn bitxor(self, other: Self) -> Self {
                self.symmetric_difference(other)
            }
        }
        impl ::bitflags::__private::core::ops::BitXorAssign for
            InternalBitFlags {
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
        }
        impl ::bitflags::__private::core::ops::BitAnd for InternalBitFlags {
            type Output = Self;
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            fn bitand(self, other: Self) -> Self { self.intersection(other) }
        }
        impl ::bitflags::__private::core::ops::BitAndAssign for
            InternalBitFlags {
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            fn bitand_assign(&mut self, other: Self) {
                *self =
                    Self::from_bits_retain(self.bits()).intersection(other);
            }
        }
        impl ::bitflags::__private::core::ops::Sub for InternalBitFlags {
            type Output = Self;
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            fn sub(self, other: Self) -> Self { self.difference(other) }
        }
        impl ::bitflags::__private::core::ops::SubAssign for InternalBitFlags
            {
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            fn sub_assign(&mut self, other: Self) { self.remove(other); }
        }
        impl ::bitflags::__private::core::ops::Not for InternalBitFlags {
            type Output = Self;
            /// The bitwise negation (`!`) of the bits in `self`, truncating the result.
            #[inline]
            fn not(self) -> Self { self.complement() }
        }
        impl ::bitflags::__private::core::iter::Extend<InternalBitFlags> for
            InternalBitFlags {
            /// The bitwise or (`|`) of the bits in each flags value.
            fn extend<T: ::bitflags::__private::core::iter::IntoIterator<Item
                = Self>>(&mut self, iterator: T) {
                for item in iterator { self.insert(item) }
            }
        }
        impl ::bitflags::__private::core::iter::FromIterator<InternalBitFlags>
            for InternalBitFlags {
            /// The bitwise or (`|`) of the bits in each flags value.
            fn from_iter<T: ::bitflags::__private::core::iter::IntoIterator<Item
                = Self>>(iterator: T) -> Self {
                use ::bitflags::__private::core::iter::Extend;
                let mut result = Self::empty();
                result.extend(iterator);
                result
            }
        }
        impl InternalBitFlags {
            /// Yield a set of contained flags values.
            ///
            /// Each yielded flags value will correspond to a defined named flag. Any unknown bits
            /// will be yielded together as a final flags value.
            #[inline]
            pub const fn iter(&self)
                -> ::bitflags::iter::Iter<SpritePipelineKey> {
                ::bitflags::iter::Iter::__private_const_new(<SpritePipelineKey
                        as ::bitflags::Flags>::FLAGS,
                    SpritePipelineKey::from_bits_retain(self.bits()),
                    SpritePipelineKey::from_bits_retain(self.bits()))
            }
            /// Yield a set of contained named flags values.
            ///
            /// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
            /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
            #[inline]
            pub const fn iter_names(&self)
                -> ::bitflags::iter::IterNames<SpritePipelineKey> {
                ::bitflags::iter::IterNames::__private_const_new(<SpritePipelineKey
                        as ::bitflags::Flags>::FLAGS,
                    SpritePipelineKey::from_bits_retain(self.bits()),
                    SpritePipelineKey::from_bits_retain(self.bits()))
            }
        }
        impl ::bitflags::__private::core::iter::IntoIterator for
            InternalBitFlags {
            type Item = SpritePipelineKey;
            type IntoIter = ::bitflags::iter::Iter<SpritePipelineKey>;
            fn into_iter(self) -> Self::IntoIter { self.iter() }
        }
        impl InternalBitFlags {
            /// Returns a mutable reference to the raw value of the flags currently stored.
            #[inline]
            pub fn bits_mut(&mut self) -> &mut u32 { &mut self.0 }
        }
        impl ::bitflags::__private::serde::Serialize for InternalBitFlags {
            fn serialize<S: ::bitflags::__private::serde::Serializer>(&self,
                serializer: S)
                ->
                    ::bitflags::__private::core::result::Result<S::Ok,
                    S::Error> {
                ::bitflags::serde::serialize(&SpritePipelineKey::from_bits_retain(self.bits()),
                    serializer)
            }
        }
        impl<'de> ::bitflags::__private::serde::Deserialize<'de> for
            InternalBitFlags {
            fn deserialize<D: ::bitflags::__private::serde::Deserializer<'de>>(deserializer:
                    D)
                ->
                    ::bitflags::__private::core::result::Result<Self,
                    D::Error> {
                let flags: SpritePipelineKey =
                    ::bitflags::serde::deserialize(deserializer)?;
                ::bitflags::__private::core::result::Result::Ok(flags.0)
            }
        }
        impl SpritePipelineKey {
            /// Get a flags value with all bits unset.
            #[inline]
            pub const fn empty() -> Self { Self(InternalBitFlags::empty()) }
            /// Get a flags value with all known bits set.
            #[inline]
            pub const fn all() -> Self { Self(InternalBitFlags::all()) }
            /// Get the underlying bits value.
            ///
            /// The returned value is exactly the bits set in this flags value.
            #[inline]
            pub const fn bits(&self) -> u32 { self.0.bits() }
            /// Convert from a bits value.
            ///
            /// This method will return `None` if any unknown bits are set.
            #[inline]
            pub const fn from_bits(bits: u32)
                -> ::bitflags::__private::core::option::Option<Self> {
                match InternalBitFlags::from_bits(bits) {
                    ::bitflags::__private::core::option::Option::Some(bits) =>
                        ::bitflags::__private::core::option::Option::Some(Self(bits)),
                    ::bitflags::__private::core::option::Option::None =>
                        ::bitflags::__private::core::option::Option::None,
                }
            }
            /// Convert from a bits value, unsetting any unknown bits.
            #[inline]
            pub const fn from_bits_truncate(bits: u32) -> Self {
                Self(InternalBitFlags::from_bits_truncate(bits))
            }
            /// Convert from a bits value exactly.
            #[inline]
            pub const fn from_bits_retain(bits: u32) -> Self {
                Self(InternalBitFlags::from_bits_retain(bits))
            }
            /// Get a flags value with the bits of a flag with the given name set.
            ///
            /// This method will return `None` if `name` is empty or doesn't
            /// correspond to any named flag.
            #[inline]
            pub fn from_name(name: &str)
                -> ::bitflags::__private::core::option::Option<Self> {
                match InternalBitFlags::from_name(name) {
                    ::bitflags::__private::core::option::Option::Some(bits) =>
                        ::bitflags::__private::core::option::Option::Some(Self(bits)),
                    ::bitflags::__private::core::option::Option::None =>
                        ::bitflags::__private::core::option::Option::None,
                }
            }
            /// Whether all bits in `self` are unset.
            #[inline]
            pub const fn is_empty(&self) -> bool { self.0.is_empty() }
            /// Whether all known bits in this flags value are set.
            #[inline]
            pub const fn is_all(&self) -> bool { self.0.is_all() }
            /// Whether any set bits in `other` are also set in `self`.
            #[inline]
            pub const fn intersects(&self, other: Self) -> bool {
                self.0.intersects(other.0)
            }
            /// Whether all set bits in `other` are also set in `self`.
            #[inline]
            pub const fn contains(&self, other: Self) -> bool {
                self.0.contains(other.0)
            }
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            pub fn insert(&mut self, other: Self) { self.0.insert(other.0) }
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `remove` won't truncate `other`, but the `!` operator will.
            #[inline]
            pub fn remove(&mut self, other: Self) { self.0.remove(other.0) }
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            pub fn toggle(&mut self, other: Self) { self.0.toggle(other.0) }
            /// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
            #[inline]
            pub fn set(&mut self, other: Self, value: bool) {
                self.0.set(other.0, value)
            }
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn intersection(self, other: Self) -> Self {
                Self(self.0.intersection(other.0))
            }
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn union(self, other: Self) -> Self {
                Self(self.0.union(other.0))
            }
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            #[must_use]
            pub const fn difference(self, other: Self) -> Self {
                Self(self.0.difference(other.0))
            }
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn symmetric_difference(self, other: Self) -> Self {
                Self(self.0.symmetric_difference(other.0))
            }
            /// The bitwise negation (`!`) of the bits in `self`, truncating the result.
            #[inline]
            #[must_use]
            pub const fn complement(self) -> Self {
                Self(self.0.complement())
            }
        }
        impl ::bitflags::__private::core::fmt::Binary for SpritePipelineKey {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::Binary::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::Octal for SpritePipelineKey {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::Octal::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::LowerHex for SpritePipelineKey
            {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::LowerHex::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::UpperHex for SpritePipelineKey
            {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::UpperHex::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::ops::BitOr for SpritePipelineKey {
            type Output = Self;
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            fn bitor(self, other: SpritePipelineKey) -> Self {
                self.union(other)
            }
        }
        impl ::bitflags::__private::core::ops::BitOrAssign for
            SpritePipelineKey {
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            fn bitor_assign(&mut self, other: Self) { self.insert(other); }
        }
        impl ::bitflags::__private::core::ops::BitXor for SpritePipelineKey {
            type Output = Self;
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            fn bitxor(self, other: Self) -> Self {
                self.symmetric_difference(other)
            }
        }
        impl ::bitflags::__private::core::ops::BitXorAssign for
            SpritePipelineKey {
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
        }
        impl ::bitflags::__private::core::ops::BitAnd for SpritePipelineKey {
            type Output = Self;
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            fn bitand(self, other: Self) -> Self { self.intersection(other) }
        }
        impl ::bitflags::__private::core::ops::BitAndAssign for
            SpritePipelineKey {
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            fn bitand_assign(&mut self, other: Self) {
                *self =
                    Self::from_bits_retain(self.bits()).intersection(other);
            }
        }
        impl ::bitflags::__private::core::ops::Sub for SpritePipelineKey {
            type Output = Self;
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            fn sub(self, other: Self) -> Self { self.difference(other) }
        }
        impl ::bitflags::__private::core::ops::SubAssign for SpritePipelineKey
            {
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            fn sub_assign(&mut self, other: Self) { self.remove(other); }
        }
        impl ::bitflags::__private::core::ops::Not for SpritePipelineKey {
            type Output = Self;
            /// The bitwise negation (`!`) of the bits in `self`, truncating the result.
            #[inline]
            fn not(self) -> Self { self.complement() }
        }
        impl ::bitflags::__private::core::iter::Extend<SpritePipelineKey> for
            SpritePipelineKey {
            /// The bitwise or (`|`) of the bits in each flags value.
            fn extend<T: ::bitflags::__private::core::iter::IntoIterator<Item
                = Self>>(&mut self, iterator: T) {
                for item in iterator { self.insert(item) }
            }
        }
        impl ::bitflags::__private::core::iter::FromIterator<SpritePipelineKey>
            for SpritePipelineKey {
            /// The bitwise or (`|`) of the bits in each flags value.
            fn from_iter<T: ::bitflags::__private::core::iter::IntoIterator<Item
                = Self>>(iterator: T) -> Self {
                use ::bitflags::__private::core::iter::Extend;
                let mut result = Self::empty();
                result.extend(iterator);
                result
            }
        }
        impl SpritePipelineKey {
            /// Yield a set of contained flags values.
            ///
            /// Each yielded flags value will correspond to a defined named flag. Any unknown bits
            /// will be yielded together as a final flags value.
            #[inline]
            pub const fn iter(&self)
                -> ::bitflags::iter::Iter<SpritePipelineKey> {
                ::bitflags::iter::Iter::__private_const_new(<SpritePipelineKey
                        as ::bitflags::Flags>::FLAGS,
                    SpritePipelineKey::from_bits_retain(self.bits()),
                    SpritePipelineKey::from_bits_retain(self.bits()))
            }
            /// Yield a set of contained named flags values.
            ///
            /// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
            /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
            #[inline]
            pub const fn iter_names(&self)
                -> ::bitflags::iter::IterNames<SpritePipelineKey> {
                ::bitflags::iter::IterNames::__private_const_new(<SpritePipelineKey
                        as ::bitflags::Flags>::FLAGS,
                    SpritePipelineKey::from_bits_retain(self.bits()),
                    SpritePipelineKey::from_bits_retain(self.bits()))
            }
        }
        impl ::bitflags::__private::core::iter::IntoIterator for
            SpritePipelineKey {
            type Item = SpritePipelineKey;
            type IntoIter = ::bitflags::iter::Iter<SpritePipelineKey>;
            fn into_iter(self) -> Self::IntoIter { self.iter() }
        }
    };bitflags::bitflags! {
92    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
93    #[repr(transparent)]
94    // NOTE: Apparently quadro drivers support up to 64x MSAA.
95    // MSAA uses the highest 3 bits for the MSAA log2(sample count) to support up to 128x MSAA.
96    pub struct SpritePipelineKey: u32 {
97        const NONE                              = 0;
98        const TONEMAP_IN_SHADER                 = 1 << 0;
99        const DEBAND_DITHER                     = 1 << 1;
100        const SRGB_COMPOSITING                  = 1 << 2;
101        const OKLAB_COMPOSITING                 = 1 << 3;
102        const COLOR_TARGET_FORMAT_RESERVED_BITS = Self::COLOR_TARGET_FORMAT_MASK_BITS << Self::COLOR_TARGET_FORMAT_SHIFT_BITS;
103        const MSAA_RESERVED_BITS                = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
104        const TONEMAP_METHOD_RESERVED_BITS      = Self::TONEMAP_METHOD_MASK_BITS << Self::TONEMAP_METHOD_SHIFT_BITS;
105        const TONEMAP_METHOD_LINEAR             = 0 << Self::TONEMAP_METHOD_SHIFT_BITS;
106        const TONEMAP_METHOD_REINHARD           = 1 << Self::TONEMAP_METHOD_SHIFT_BITS;
107        const TONEMAP_METHOD_REINHARD_LUMINANCE = 2 << Self::TONEMAP_METHOD_SHIFT_BITS;
108        const TONEMAP_METHOD_ACES_FITTED        = 3 << Self::TONEMAP_METHOD_SHIFT_BITS;
109        const TONEMAP_METHOD_AGX                = 4 << Self::TONEMAP_METHOD_SHIFT_BITS;
110        const TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM = 5 << Self::TONEMAP_METHOD_SHIFT_BITS;
111        const TONEMAP_METHOD_TONY_MC_MAPFACE    = 6 << Self::TONEMAP_METHOD_SHIFT_BITS;
112        const TONEMAP_METHOD_BLENDER_FILMIC     = 7 << Self::TONEMAP_METHOD_SHIFT_BITS;
113        const TONEMAP_METHOD_PBR_NEUTRAL        = 8 << Self::TONEMAP_METHOD_SHIFT_BITS;
114
115    }
116}
117
118impl SpritePipelineKey {
119    const COLOR_TARGET_FORMAT_MASK_BITS: u32 = bevy_render::view::COLOR_TARGET_FORMAT_MASK_BITS;
120    const COLOR_TARGET_FORMAT_SHIFT_BITS: u32 = 4;
121    const MSAA_MASK_BITS: u32 = 0b111;
122    const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones();
123    const TONEMAP_METHOD_MASK_BITS: u32 = 0b1111;
124    const TONEMAP_METHOD_SHIFT_BITS: u32 =
125        Self::MSAA_SHIFT_BITS - Self::TONEMAP_METHOD_MASK_BITS.count_ones();
126
127    #[inline]
128    pub const fn from_msaa_samples(msaa_samples: u32) -> Self {
129        let msaa_bits =
130            (msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
131        Self::from_bits_retain(msaa_bits)
132    }
133
134    #[inline]
135    pub const fn msaa_samples(&self) -> u32 {
136        1 << ((self.bits() >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
137    }
138
139    /// Create a pipeline key from the view's color target format.
140    #[inline]
141    pub fn from_target_format(format: TextureFormat) -> Self {
142        let code = texture_format_to_code(format)
143            .expect("Texture format is not supported by the pipeline") as u32;
144        Self::from_bits_retain(
145            (code & Self::COLOR_TARGET_FORMAT_MASK_BITS) << Self::COLOR_TARGET_FORMAT_SHIFT_BITS,
146        )
147    }
148
149    /// Color target format of the main pass for this pipeline key.
150    #[inline]
151    pub fn target_format(&self) -> TextureFormat {
152        let code = ((self.bits() >> Self::COLOR_TARGET_FORMAT_SHIFT_BITS)
153            & Self::COLOR_TARGET_FORMAT_MASK_BITS) as u8;
154        texture_format_from_code(code)
155            .expect("Unknown bits in `COLOR_TARGET_FORMAT_MASK_BITS` of the pipeline key")
156    }
157
158    /// Key bits for a view's resolved [`CompositingSpace`].
159    #[inline]
160    pub fn from_compositing_space(space: Option<CompositingSpace>) -> Self {
161        match space {
162            Some(CompositingSpace::Srgb) => Self::SRGB_COMPOSITING,
163            Some(CompositingSpace::Oklab) => Self::OKLAB_COMPOSITING,
164            Some(CompositingSpace::Linear) | None => Self::NONE,
165        }
166    }
167}
168
169impl SpecializedRenderPipeline for SpritePipeline {
170    type Key = SpritePipelineKey;
171
172    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
173        let mut shader_defs = Vec::new();
174        if key.contains(SpritePipelineKey::TONEMAP_IN_SHADER) {
175            shader_defs.push("TONEMAP_IN_SHADER".into());
176            shader_defs.push(ShaderDefVal::UInt(
177                "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
178                1,
179            ));
180            shader_defs.push(ShaderDefVal::UInt(
181                "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
182                2,
183            ));
184
185            let method = key.intersection(SpritePipelineKey::TONEMAP_METHOD_RESERVED_BITS);
186
187            if method == SpritePipelineKey::TONEMAP_METHOD_LINEAR {
188                shader_defs.push("TONEMAP_METHOD_LINEAR".into());
189            } else if method == SpritePipelineKey::TONEMAP_METHOD_REINHARD {
190                shader_defs.push("TONEMAP_METHOD_REINHARD".into());
191            } else if method == SpritePipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
192                shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
193            } else if method == SpritePipelineKey::TONEMAP_METHOD_ACES_FITTED {
194                shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
195            } else if method == SpritePipelineKey::TONEMAP_METHOD_AGX {
196                shader_defs.push("TONEMAP_METHOD_AGX".into());
197            } else if method == SpritePipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
198            {
199                shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
200            } else if method == SpritePipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
201                shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
202            } else if method == SpritePipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
203                shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
204            } else if method == SpritePipelineKey::TONEMAP_METHOD_PBR_NEUTRAL {
205                shader_defs.push("TONEMAP_METHOD_PBR_NEUTRAL".into());
206            }
207
208            // Debanding is tied to tonemapping in the shader, cannot run without it.
209            if key.contains(SpritePipelineKey::DEBAND_DITHER) {
210                shader_defs.push("DEBAND_DITHER".into());
211            }
212        }
213
214        if key.contains(SpritePipelineKey::SRGB_COMPOSITING) {
215            shader_defs.push("SRGB_OUTPUT".into());
216        }
217        if key.contains(SpritePipelineKey::OKLAB_COMPOSITING) {
218            shader_defs.push("OKLAB_OUTPUT".into());
219        }
220
221        let format = key.target_format();
222
223        let instance_rate_vertex_buffer_layout = VertexBufferLayout {
224            array_stride: 80,
225            step_mode: VertexStepMode::Instance,
226            attributes: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [VertexAttribute {
                    format: VertexFormat::Float32x4,
                    offset: 0,
                    shader_location: 0,
                },
                VertexAttribute {
                    format: VertexFormat::Float32x4,
                    offset: 16,
                    shader_location: 1,
                },
                VertexAttribute {
                    format: VertexFormat::Float32x4,
                    offset: 32,
                    shader_location: 2,
                },
                VertexAttribute {
                    format: VertexFormat::Float32x4,
                    offset: 48,
                    shader_location: 3,
                },
                VertexAttribute {
                    format: VertexFormat::Float32x4,
                    offset: 64,
                    shader_location: 4,
                }]))vec![
227                // @location(0) i_model_transpose_col0: vec4<f32>,
228                VertexAttribute {
229                    format: VertexFormat::Float32x4,
230                    offset: 0,
231                    shader_location: 0,
232                },
233                // @location(1) i_model_transpose_col1: vec4<f32>,
234                VertexAttribute {
235                    format: VertexFormat::Float32x4,
236                    offset: 16,
237                    shader_location: 1,
238                },
239                // @location(2) i_model_transpose_col2: vec4<f32>,
240                VertexAttribute {
241                    format: VertexFormat::Float32x4,
242                    offset: 32,
243                    shader_location: 2,
244                },
245                // @location(3) i_color: vec4<f32>,
246                VertexAttribute {
247                    format: VertexFormat::Float32x4,
248                    offset: 48,
249                    shader_location: 3,
250                },
251                // @location(4) i_uv_offset_scale: vec4<f32>,
252                VertexAttribute {
253                    format: VertexFormat::Float32x4,
254                    offset: 64,
255                    shader_location: 4,
256                },
257            ],
258        };
259
260        RenderPipelineDescriptor {
261            vertex: VertexState {
262                shader: self.shader.clone(),
263                shader_defs: shader_defs.clone(),
264                buffers: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [instance_rate_vertex_buffer_layout]))vec![instance_rate_vertex_buffer_layout],
265                ..default()
266            },
267            fragment: Some(FragmentState {
268                shader: self.shader.clone(),
269                shader_defs,
270                targets: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Some(ColorTargetState {
                        format,
                        blend: Some(BlendState::ALPHA_BLENDING),
                        write_mask: ColorWrites::ALL,
                    })]))vec![Some(ColorTargetState {
271                    format,
272                    blend: Some(BlendState::ALPHA_BLENDING),
273                    write_mask: ColorWrites::ALL,
274                })],
275                ..default()
276            }),
277            layout: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [self.view_layout.clone(), self.material_layout.clone()]))vec![self.view_layout.clone(), self.material_layout.clone()],
278            // Sprites are always alpha blended so they never need to write to depth.
279            // They just need to read it in case an opaque mesh2d
280            // that wrote to depth is present.
281            depth_stencil: Some(DepthStencilState {
282                format: CORE_2D_DEPTH_FORMAT,
283                depth_write_enabled: Some(false),
284                depth_compare: Some(CompareFunction::GreaterEqual),
285                stencil: StencilState {
286                    front: StencilFaceState::IGNORE,
287                    back: StencilFaceState::IGNORE,
288                    read_mask: 0,
289                    write_mask: 0,
290                },
291                bias: DepthBiasState {
292                    constant: 0,
293                    slope_scale: 0.0,
294                    clamp: 0.0,
295                },
296            }),
297            multisample: MultisampleState {
298                count: key.msaa_samples(),
299                mask: !0,
300                alpha_to_coverage_enabled: false,
301            },
302            label: Some("sprite_pipeline".into()),
303            ..default()
304        }
305    }
306}
307
308pub struct ExtractedSlice {
309    pub offset: Vec2,
310    pub rect: Rect,
311    pub size: Vec2,
312}
313
314pub struct ExtractedSprite {
315    pub main_entity: Entity,
316    pub render_entity: Entity,
317    pub transform: GlobalTransform,
318    pub color: LinearRgba,
319    /// Change the on-screen size of the sprite
320    /// Asset ID of the [`Image`] of this sprite
321    /// PERF: storing an `AssetId` instead of `Handle<Image>` enables some optimizations (`ExtractedSprite` becomes `Copy` and doesn't need to be dropped)
322    pub image_handle_id: AssetId<Image>,
323    pub flip_y: bool,
324    pub kind: ExtractedSpriteKind,
325}
326
327pub enum ExtractedSpriteKind {
328    /// A single sprite with a custom size
329    Single { custom_size: Vec2 },
330    /// Indexes into the list of [`ExtractedSlice`]s stored in the [`ExtractedSlices`] resource
331    /// Used for elements composed from multiple sprites such as text or nine-patched borders
332    Slices { indices: Range<usize> },
333}
334
335#[derive(impl bevy_ecs::component::Component for ExtractedSprites where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<ExtractedSprites>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<ExtractedSprites>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for ExtractedSprites where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, #[automatically_derived]impl ::core::default::Default for ExtractedSprites {
    #[inline]
    fn default() -> Self {
        ExtractedSprites { sprites: ::core::default::Default::default() }
    }
}Default)]
336pub struct ExtractedSprites {
337    pub sprites: Vec<ExtractedSprite>,
338}
339
340#[derive(impl bevy_ecs::component::Component for ExtractedSlices where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<ExtractedSlices>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<ExtractedSlices>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for ExtractedSlices where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, #[automatically_derived]impl ::core::default::Default for ExtractedSlices {
    #[inline]
    fn default() -> Self {
        ExtractedSlices { slices: ::core::default::Default::default() }
    }
}Default)]
341pub struct ExtractedSlices {
342    pub slices: Vec<ExtractedSlice>,
343}
344
345#[derive(impl bevy_ecs::component::Component for SpriteAssetEvents where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<SpriteAssetEvents>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<SpriteAssetEvents>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for SpriteAssetEvents where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, #[automatically_derived]impl ::core::default::Default for SpriteAssetEvents {
    #[inline]
    fn default() -> Self {
        SpriteAssetEvents { images: ::core::default::Default::default() }
    }
}Default)]
346pub struct SpriteAssetEvents {
347    pub images: Vec<AssetEvent<Image>>,
348}
349
350pub fn extract_sprite_events(
351    mut events: ResMut<SpriteAssetEvents>,
352    mut image_events: Extract<MessageReader<AssetEvent<Image>>>,
353) {
354    let SpriteAssetEvents { ref mut images } = *events;
355    images.clear();
356
357    for event in image_events.read() {
358        images.push(*event);
359    }
360}
361
362#[repr(C)]
363#[derive(#[automatically_derived]impl ::core::marker::Copy for SpriteInstance { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for SpriteInstance {
}
#[automatically_derived]impl ::core::clone::Clone for SpriteInstance {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<[Vec4; 3]>;
        let _: ::core::clone::AssertParamIsClone<[f32; 4]>;
        let _: ::core::clone::AssertParamIsClone<[f32; 4]>;
        *self
    }
}Clone, const _: () =
    {
        if !(::core::mem::size_of::<SpriteInstance>() ==
                    (::core::mem::size_of::<[Vec4; 3]>() +
                                ::core::mem::size_of::<[f32; 4]>() +
                            ::core::mem::size_of::<[f32; 4]>())) {
            {
                ::std::rt::begin_panic("derive(Pod) was applied to a type with padding");
            }
        };
    };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Pod>() {}
                assert_impl::<[Vec4; 3]>();
            }
        };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Pod>() {}
                assert_impl::<[f32; 4]>();
            }
        };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Pod>() {}
                assert_impl::<[f32; 4]>();
            }
        };
unsafe impl ::bytemuck::Pod for SpriteInstance {}Pod, const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Zeroable>() {}
                assert_impl::<[Vec4; 3]>();
            }
        };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Zeroable>() {}
                assert_impl::<[f32; 4]>();
            }
        };
const _: fn() =
    ||
        {
            #[allow(clippy :: missing_const_for_fn)]
            #[doc(hidden)]
            fn check() {
                fn assert_impl<T: ::bytemuck::Zeroable>() {}
                assert_impl::<[f32; 4]>();
            }
        };
unsafe impl ::bytemuck::Zeroable for SpriteInstance {}Zeroable)]
364struct SpriteInstance {
365    // Affine 4x3 transposed to 3x4
366    pub i_model_transpose: [Vec4; 3],
367    pub i_color: [f32; 4],
368    pub i_uv_offset_scale: [f32; 4],
369}
370
371impl SpriteInstance {
372    #[inline]
373    fn from(transform: &Affine3A, color: &LinearRgba, uv_offset_scale: &Vec4) -> Self {
374        let transpose_model_3x3 = transform.matrix3.transpose();
375        Self {
376            i_model_transpose: [
377                transpose_model_3x3.x_axis.extend(transform.translation.x),
378                transpose_model_3x3.y_axis.extend(transform.translation.y),
379                transpose_model_3x3.z_axis.extend(transform.translation.z),
380            ],
381            i_color: color.to_f32_array(),
382            i_uv_offset_scale: uv_offset_scale.to_array(),
383        }
384    }
385}
386
387#[derive(impl bevy_ecs::component::Component for SpriteMeta where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<SpriteMeta>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<SpriteMeta>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for SpriteMeta where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
388pub struct SpriteMeta {
389    sprite_index_buffer: RawBufferVec<u32>,
390    sprite_instance_buffer: RawBufferVec<SpriteInstance>,
391}
392
393impl Default for SpriteMeta {
394    fn default() -> Self {
395        Self {
396            sprite_index_buffer: RawBufferVec::<u32>::new(BufferUsages::INDEX),
397            sprite_instance_buffer: RawBufferVec::<SpriteInstance>::new(BufferUsages::VERTEX),
398        }
399    }
400}
401
402#[derive(impl bevy_ecs::component::Component for SpriteViewBindGroup where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::Table;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {}
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}Component)]
403pub struct SpriteViewBindGroup {
404    pub value: BindGroup,
405}
406
407#[derive(impl bevy_ecs::component::Component for SpriteBatches where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<SpriteBatches>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<SpriteBatches>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for SpriteBatches where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, impl ::core::ops::Deref for SpriteBatches {
    type Target = HashMap<(RetainedViewEntity, Entity), SpriteBatch>;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for SpriteBatches {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut, #[automatically_derived]impl ::core::default::Default for SpriteBatches {
    #[inline]
    fn default() -> Self {
        SpriteBatches(::core::default::Default::default())
    }
}Default)]
408pub struct SpriteBatches(HashMap<(RetainedViewEntity, Entity), SpriteBatch>);
409
410#[derive(#[automatically_derived]impl ::core::marker::StructuralPartialEq for
    SpriteBatch {
}
#[automatically_derived]impl ::core::cmp::PartialEq for SpriteBatch {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.image_handle_id == other.image_handle_id &&
            self.range == other.range
    }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for SpriteBatch {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<AssetId<Image>>;
        let _: ::core::cmp::AssertParamIsEq<Range<u32>>;
    }
}Eq, #[automatically_derived]impl ::core::clone::Clone for SpriteBatch {
    #[inline]
    fn clone(&self) -> Self {
        SpriteBatch {
            image_handle_id: ::core::clone::Clone::clone(&self.image_handle_id),
            range: ::core::clone::Clone::clone(&self.range),
        }
    }
}Clone, #[automatically_derived]impl ::core::fmt::Debug for SpriteBatch {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "SpriteBatch",
            "image_handle_id", &self.image_handle_id, "range", &&self.range)
    }
}Debug)]
411pub struct SpriteBatch {
412    image_handle_id: AssetId<Image>,
413    range: Range<u32>,
414}
415
416#[derive(impl bevy_ecs::component::Component for ImageBindGroups where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::SparseSet;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {
        let resource_component_id =
            if let ::core::option::Option::Some(id) =
                    required_components.components_registrator().component_id::<ImageBindGroups>()
                {
                id
            } else {
                required_components.components_registrator().register_component::<ImageBindGroups>()
            };
        required_components.register_required::<bevy_ecs::resource::IsResource>(move
                ||
                bevy_ecs::resource::IsResource::new(resource_component_id));
    }
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}
impl bevy_ecs::resource::Resource for ImageBindGroups where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, #[automatically_derived]impl ::core::default::Default for ImageBindGroups {
    #[inline]
    fn default() -> Self {
        ImageBindGroups { values: ::core::default::Default::default() }
    }
}Default)]
417pub struct ImageBindGroups {
418    values: HashMap<AssetId<Image>, BindGroup>,
419}
420
421pub fn queue_sprites(
422    mut view_entities: Local<FixedBitSet>,
423    draw_functions: Res<DrawFunctions<Transparent2d>>,
424    sprite_pipeline: Res<SpritePipeline>,
425    mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>,
426    pipeline_cache: Res<PipelineCache>,
427    extracted_sprites: Res<ExtractedSprites>,
428    mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
429    mut cameras: Query<(
430        &RenderVisibleEntities,
431        &ExtractedCamera,
432        &ExtractedView,
433        &Msaa,
434        Option<&Tonemapping>,
435        Option<&DebandDither>,
436        Option<&ResolvedCompositingSpace>,
437    )>,
438) {
439    let draw_sprite_function = draw_functions.read().id::<DrawSprite>();
440
441    for (visible_entities, camera, view, msaa, tonemapping, dither, resolved_space) in &mut cameras
442    {
443        let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)
444        else {
445            continue;
446        };
447
448        let mut view_key = SpritePipelineKey::from_target_format(view.target_format)
449            | SpritePipelineKey::from_msaa_samples(msaa.samples())
450            | SpritePipelineKey::from_compositing_space(ResolvedCompositingSpace::space(
451                resolved_space,
452            ));
453
454        if !camera.hdr
455            && let Some(tonemapping) = tonemapping
456            && tonemapping.is_enabled()
457        {
458            view_key |= SpritePipelineKey::TONEMAP_IN_SHADER;
459            view_key |= match tonemapping {
460                Tonemapping::None | Tonemapping::Linear => SpritePipelineKey::TONEMAP_METHOD_LINEAR,
461                Tonemapping::Reinhard => SpritePipelineKey::TONEMAP_METHOD_REINHARD,
462                Tonemapping::ReinhardLuminance => {
463                    SpritePipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE
464                }
465                Tonemapping::AcesFitted => SpritePipelineKey::TONEMAP_METHOD_ACES_FITTED,
466                Tonemapping::AgX => SpritePipelineKey::TONEMAP_METHOD_AGX,
467                Tonemapping::SomewhatBoringDisplayTransform => {
468                    SpritePipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
469                }
470                Tonemapping::TonyMcMapface => SpritePipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
471                Tonemapping::BlenderFilmic => SpritePipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
472                Tonemapping::KhronosPbrNeutral => SpritePipelineKey::TONEMAP_METHOD_PBR_NEUTRAL,
473            };
474            if let Some(DebandDither::Enabled) = dither {
475                view_key |= SpritePipelineKey::DEBAND_DITHER;
476            }
477        }
478
479        let pipeline = pipelines.specialize(&pipeline_cache, &sprite_pipeline, view_key);
480
481        view_entities.clear();
482        if let Some(visible_entities) = visible_entities.get::<Sprite>() {
483            view_entities.extend(
484                visible_entities
485                    .iter_visible()
486                    .map(|(_, e)| e.index_u32() as usize),
487            );
488        }
489
490        transparent_phase
491            .items
492            .reserve(extracted_sprites.sprites.len());
493
494        for (index, extracted_sprite) in extracted_sprites.sprites.iter().enumerate() {
495            let view_index = extracted_sprite.main_entity.index_u32();
496
497            if !view_entities.contains(view_index as usize) {
498                continue;
499            }
500
501            // These items will be sorted by depth with other phase items
502            let sort_key = FloatOrd(extracted_sprite.transform.translation().z);
503
504            // Add the item to the render phase
505            transparent_phase.add_transient(Transparent2d {
506                draw_function: draw_sprite_function,
507                pipeline,
508                entity: (
509                    extracted_sprite.render_entity,
510                    extracted_sprite.main_entity.into(),
511                ),
512                sort_key,
513                // `batch_range` is calculated in `prepare_sprite_image_bind_groups`
514                batch_range: 0..0,
515                extra_index: PhaseItemExtraIndex::None,
516                extracted_index: index,
517                indexed: true,
518            });
519        }
520    }
521}
522
523pub fn prepare_sprite_view_bind_groups(
524    mut commands: Commands,
525    render_device: Res<RenderDevice>,
526    pipeline_cache: Res<PipelineCache>,
527    sprite_pipeline: Res<SpritePipeline>,
528    view_uniforms: Res<ViewUniforms>,
529    views: Query<(Entity, &Tonemapping), With<ExtractedView>>,
530    tonemapping_luts: Res<TonemappingLuts>,
531    images: Res<RenderAssets<GpuImage>>,
532    fallback_image: Res<FallbackImage>,
533) {
534    let Some(view_binding) = view_uniforms.uniforms.binding() else {
535        return;
536    };
537
538    for (entity, tonemapping) in &views {
539        let lut_bindings =
540            get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
541        let view_bind_group = render_device.create_bind_group(
542            "mesh2d_view_bind_group",
543            &pipeline_cache.get_bind_group_layout(&sprite_pipeline.view_layout),
544            &BindGroupEntries::sequential((view_binding.clone(), lut_bindings.0, lut_bindings.1)),
545        );
546
547        commands.entity(entity).insert(SpriteViewBindGroup {
548            value: view_bind_group,
549        });
550    }
551}
552
553pub fn prepare_sprite_image_bind_groups(
554    render_device: Res<RenderDevice>,
555    render_queue: Res<RenderQueue>,
556    pipeline_cache: Res<PipelineCache>,
557    mut sprite_meta: ResMut<SpriteMeta>,
558    sprite_pipeline: Res<SpritePipeline>,
559    mut image_bind_groups: ResMut<ImageBindGroups>,
560    gpu_images: Res<RenderAssets<GpuImage>>,
561    extracted_sprites: Res<ExtractedSprites>,
562    extracted_slices: Res<ExtractedSlices>,
563    mut phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
564    events: Res<SpriteAssetEvents>,
565    mut batches: ResMut<SpriteBatches>,
566) {
567    // If an image has changed, the GpuImage has (probably) changed
568    for event in &events.images {
569        match event {
570            AssetEvent::Added { .. } |
571            // Images don't have dependencies
572            AssetEvent::LoadedWithDependencies { .. } => {}
573            AssetEvent::Unused { id } | AssetEvent::Modified { id } | AssetEvent::Removed { id } => {
574                image_bind_groups.values.remove(id);
575            }
576        };
577    }
578
579    batches.clear();
580
581    // Clear the sprite instances
582    sprite_meta.sprite_instance_buffer.clear();
583
584    // Index buffer indices
585    let mut index = 0;
586
587    let image_bind_groups = &mut *image_bind_groups;
588
589    for (retained_view, transparent_phase) in phases.iter_mut() {
590        let mut current_batch = None;
591        let mut batch_item_index = 0;
592        let mut batch_image_size = Vec2::ZERO;
593        let mut batch_image_handle = None;
594
595        // Iterate through the phase items and detect when successive sprites that can be batched.
596        // Spawn an entity with a `SpriteBatch` component for each possible batch.
597        // Compatible items share the same entity.
598        for item_index in 0..transparent_phase.items.len() {
599            let item = &transparent_phase.items[item_index];
600
601            let Some(extracted_sprite) = extracted_sprites
602                .sprites
603                .get(item.extracted_index)
604                .filter(|extracted_sprite| extracted_sprite.render_entity == item.entity())
605            else {
606                // If there is a phase item that is not a sprite, then we must start a new
607                // batch to draw the other phase item(s) and to respect draw order. This can be
608                // done by invalidating the batch_image_handle
609                batch_image_handle = None;
610                continue;
611            };
612
613            if batch_image_handle != Some(extracted_sprite.image_handle_id) {
614                let Some(gpu_image) = gpu_images.get(extracted_sprite.image_handle_id) else {
615                    continue;
616                };
617
618                batch_image_size = gpu_image.size_2d().as_vec2();
619                let image_handle = extracted_sprite.image_handle_id;
620                batch_image_handle = Some(image_handle);
621                image_bind_groups
622                    .values
623                    .entry(image_handle)
624                    .or_insert_with(|| {
625                        render_device.create_bind_group(
626                            "sprite_material_bind_group",
627                            &pipeline_cache.get_bind_group_layout(&sprite_pipeline.material_layout),
628                            &BindGroupEntries::sequential((
629                                &gpu_image.texture_view,
630                                &gpu_image.sampler,
631                            )),
632                        )
633                    });
634
635                batch_item_index = item_index;
636                current_batch = Some(batches.entry((*retained_view, item.entity())).insert(
637                    SpriteBatch {
638                        image_handle_id: image_handle,
639                        range: index..index,
640                    },
641                ));
642            }
643            match extracted_sprite.kind {
644                ExtractedSpriteKind::Single { custom_size } => {
645                    let mut uv_offset_scale = Vec4::new(0.0, 1.0, 1.0, -1.0);
646
647                    if extracted_sprite.flip_y {
648                        uv_offset_scale.y += uv_offset_scale.w;
649                        uv_offset_scale.w *= -1.0;
650                    }
651
652                    let transform = extracted_sprite.transform.affine()
653                        * Affine3A::from_scale_rotation_translation(
654                            custom_size.extend(1.0),
655                            Quat::IDENTITY,
656                            (custom_size * -Vec2::splat(0.5)).extend(0.0),
657                        );
658
659                    // Store the vertex data and add the item to the render phase
660                    sprite_meta
661                        .sprite_instance_buffer
662                        .push(SpriteInstance::from(
663                            &transform,
664                            &extracted_sprite.color,
665                            &uv_offset_scale,
666                        ));
667
668                    current_batch.as_mut().unwrap().get_mut().range.end += 1;
669                    index += 1;
670                }
671                ExtractedSpriteKind::Slices { ref indices } => {
672                    for i in indices.clone() {
673                        let slice = &extracted_slices.slices[i];
674                        let rect = slice.rect;
675                        let rect_size = rect.size();
676
677                        // Calculate vertex data for this item
678                        let mut uv_offset_scale: Vec4;
679
680                        // If a rect is specified, adjust UVs and the size of the quad
681                        uv_offset_scale = Vec4::new(
682                            rect.min.x / batch_image_size.x,
683                            rect.max.y / batch_image_size.y,
684                            rect_size.x / batch_image_size.x,
685                            -rect_size.y / batch_image_size.y,
686                        );
687
688                        if extracted_sprite.flip_y {
689                            uv_offset_scale.y += uv_offset_scale.w;
690                            uv_offset_scale.w *= -1.0;
691                        }
692
693                        let transform = extracted_sprite.transform.affine()
694                            * Affine3A::from_scale_rotation_translation(
695                                slice.size.extend(1.0),
696                                Quat::IDENTITY,
697                                (slice.size * -Vec2::splat(0.5) + slice.offset).extend(0.0),
698                            );
699
700                        // Store the vertex data and add the item to the render phase
701                        sprite_meta
702                            .sprite_instance_buffer
703                            .push(SpriteInstance::from(
704                                &transform,
705                                &extracted_sprite.color,
706                                &uv_offset_scale,
707                            ));
708
709                        current_batch.as_mut().unwrap().get_mut().range.end += 1;
710                        index += 1;
711                    }
712                }
713            }
714            transparent_phase.items[batch_item_index]
715                .batch_range_mut()
716                .end += 1;
717        }
718        sprite_meta
719            .sprite_instance_buffer
720            .write_buffer(&render_device, &render_queue);
721
722        if sprite_meta.sprite_index_buffer.len() != 6 {
723            sprite_meta.sprite_index_buffer.clear();
724
725            // NOTE: This code is creating 6 indices pointing to 4 vertices.
726            // The vertices form the corners of a quad based on their two least significant bits.
727            // 10   11
728            //
729            // 00   01
730            // The sprite shader can then use the two least significant bits as the vertex index.
731            // The rest of the properties to transform the vertex positions and UVs (which are
732            // implicit) are baked into the instance transform, and UV offset and scale.
733            // See bevy_sprite_render/src/render/sprite.wesl for the details.
734            sprite_meta.sprite_index_buffer.push(2);
735            sprite_meta.sprite_index_buffer.push(0);
736            sprite_meta.sprite_index_buffer.push(1);
737            sprite_meta.sprite_index_buffer.push(1);
738            sprite_meta.sprite_index_buffer.push(3);
739            sprite_meta.sprite_index_buffer.push(2);
740
741            sprite_meta
742                .sprite_index_buffer
743                .write_buffer(&render_device, &render_queue);
744        }
745    }
746}
747/// [`RenderCommand`] for sprite rendering.
748pub type DrawSprite = (
749    SetItemPipeline,
750    SetSpriteViewBindGroup<0>,
751    SetSpriteTextureBindGroup<1>,
752    DrawSpriteBatch,
753);
754
755pub struct SetSpriteViewBindGroup<const I: usize>;
756impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteViewBindGroup<I> {
757    type Param = ();
758    type ViewQuery = (Read<ViewUniformOffset>, Read<SpriteViewBindGroup>);
759    type ItemQuery = ();
760
761    fn render<'w>(
762        _item: &P,
763        (view_uniform, sprite_view_bind_group): ROQueryItem<'w, '_, Self::ViewQuery>,
764        _entity: Option<()>,
765        _param: SystemParamItem<'w, '_, Self::Param>,
766        pass: &mut TrackedRenderPass<'w>,
767    ) -> RenderCommandResult {
768        pass.set_bind_group(I, &sprite_view_bind_group.value, &[view_uniform.offset]);
769        RenderCommandResult::Success
770    }
771}
772pub struct SetSpriteTextureBindGroup<const I: usize>;
773impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteTextureBindGroup<I> {
774    type Param = (SRes<ImageBindGroups>, SRes<SpriteBatches>);
775    type ViewQuery = Read<ExtractedView>;
776    type ItemQuery = ();
777
778    fn render<'w>(
779        item: &P,
780        view: ROQueryItem<'w, '_, Self::ViewQuery>,
781        _entity: Option<()>,
782        (image_bind_groups, batches): SystemParamItem<'w, '_, Self::Param>,
783        pass: &mut TrackedRenderPass<'w>,
784    ) -> RenderCommandResult {
785        let image_bind_groups = image_bind_groups.into_inner();
786        let Some(batch) = batches.get(&(view.retained_view_entity, item.entity())) else {
787            return RenderCommandResult::Skip;
788        };
789
790        pass.set_bind_group(
791            I,
792            image_bind_groups
793                .values
794                .get(&batch.image_handle_id)
795                .unwrap(),
796            &[],
797        );
798        RenderCommandResult::Success
799    }
800}
801
802pub struct DrawSpriteBatch;
803impl<P: PhaseItem> RenderCommand<P> for DrawSpriteBatch {
804    type Param = (SRes<SpriteMeta>, SRes<SpriteBatches>);
805    type ViewQuery = Read<ExtractedView>;
806    type ItemQuery = ();
807
808    fn render<'w>(
809        item: &P,
810        view: ROQueryItem<'w, '_, Self::ViewQuery>,
811        _entity: Option<()>,
812        (sprite_meta, batches): SystemParamItem<'w, '_, Self::Param>,
813        pass: &mut TrackedRenderPass<'w>,
814    ) -> RenderCommandResult {
815        let sprite_meta = sprite_meta.into_inner();
816        let Some(batch) = batches.get(&(view.retained_view_entity, item.entity())) else {
817            return RenderCommandResult::Skip;
818        };
819
820        pass.set_index_buffer(
821            sprite_meta.sprite_index_buffer.buffer().unwrap().slice(..),
822            IndexFormat::Uint32,
823        );
824        pass.set_vertex_buffer(
825            0,
826            sprite_meta
827                .sprite_instance_buffer
828                .buffer()
829                .unwrap()
830                .slice(..),
831        );
832        pass.draw_indexed(0..6, 0, batch.range.clone());
833        RenderCommandResult::Success
834    }
835}