Skip to main content

bevy_sprite_render/render/
mod.rs

1use core::ops::Range;
2
3use crate::ComputedTextureSlices;
4use bevy_asset::{load_embedded_asset, AssetEvent, AssetId, AssetServer, Assets, Handle};
5use bevy_camera::visibility::ViewVisibility;
6use bevy_color::{ColorToComponents, LinearRgba};
7use bevy_core_pipeline::{
8    core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT},
9    tonemapping::{
10        get_lut_bind_group_layout_entries, get_lut_bindings, DebandDither, Tonemapping,
11        TonemappingLuts,
12    },
13};
14use bevy_derive::{Deref, DerefMut};
15use bevy_ecs::{
16    prelude::*,
17    query::ROQueryItem,
18    system::{lifetimeless::*, SystemParamItem},
19};
20use bevy_image::{Image, TextureAtlasLayout};
21use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4};
22use bevy_mesh::VertexBufferLayout;
23use bevy_platform::collections::HashMap;
24use bevy_render::{
25    camera::ExtractedCamera,
26    view::{RenderVisibleEntities, RetainedViewEntity},
27};
28use bevy_render::{
29    render_asset::RenderAssets,
30    render_phase::{
31        DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand, RenderCommandResult,
32        SetItemPipeline, TrackedRenderPass, ViewSortedRenderPhases,
33    },
34    render_resource::{
35        binding_types::{sampler, texture_2d, uniform_buffer},
36        *,
37    },
38    renderer::{RenderDevice, RenderQueue},
39    sync_world::RenderEntity,
40    texture::{FallbackImage, GpuImage},
41    view::{
42        texture_format_from_code, texture_format_to_code, ExtractedView, Msaa, ViewUniform,
43        ViewUniformOffset, ViewUniforms,
44    },
45    Extract,
46};
47use bevy_shader::{Shader, ShaderDefVal};
48use bevy_sprite::{Anchor, Sprite, SpriteScalingMode};
49use bevy_transform::components::GlobalTransform;
50use bevy_utils::default;
51use bytemuck::{Pod, Zeroable};
52use fixedbitset::FixedBitSet;
53
54#[derive(impl bevy_ecs::resource::Resource for SpritePipeline where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
55pub struct SpritePipeline {
56    view_layout: BindGroupLayoutDescriptor,
57    material_layout: BindGroupLayoutDescriptor,
58    shader: Handle<Shader>,
59}
60
61pub fn init_sprite_pipeline(mut commands: Commands, asset_server: Res<AssetServer>) {
62    let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
63    let view_layout = BindGroupLayoutDescriptor::new(
64        "sprite_view_layout",
65        &BindGroupLayoutEntries::sequential(
66            ShaderStages::VERTEX_FRAGMENT,
67            (
68                uniform_buffer::<ViewUniform>(true),
69                tonemapping_lut_entries[0].visibility(ShaderStages::FRAGMENT),
70                tonemapping_lut_entries[1].visibility(ShaderStages::FRAGMENT),
71            ),
72        ),
73    );
74
75    let material_layout = BindGroupLayoutDescriptor::new(
76        "sprite_material_layout",
77        &BindGroupLayoutEntries::sequential(
78            ShaderStages::FRAGMENT,
79            (
80                texture_2d(TextureSampleType::Float { filterable: true }),
81                sampler(SamplerBindingType::Filtering),
82            ),
83        ),
84    );
85
86    commands.insert_resource(SpritePipeline {
87        view_layout,
88        material_layout,
89        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.wgsl".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.wgsl"),
90    });
91}
92
93bitflags::bitflags! {
94    #[derive(#[automatically_derived]
impl ::core::clone::Clone for SpritePipelineKey {
    #[inline]
    fn clone(&self) -> SpritePipelineKey {
        let _:
                ::core::clone::AssertParamIsClone<<SpritePipelineKey as
                ::bitflags::__private::PublicFlags>::Internal>;
        *self
    }
}
#[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) -> InternalBitFlags {
                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: &InternalBitFlags) -> 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: &InternalBitFlags)
                -> ::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: &InternalBitFlags) -> ::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 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_NONE: 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);
        }
        impl ::bitflags::Flags for SpritePipelineKey {
            const FLAGS: &'static [::bitflags::Flag<SpritePipelineKey>] =
                {
                    mod __bitflags_flag_names {
                        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_NONE: &'static str =
                            "TONEMAP_METHOD_NONE";
                        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_NONE,
                                        SpritePipelineKey::TONEMAP_METHOD_NONE)
                                },
                                {
                                    ::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)
            }
        }
        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 {
                    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_NONE: &'static str =
                        "TONEMAP_METHOD_NONE";
                    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_NONE {
                            return ::bitflags::__private::core::option::Option::Some(Self(SpritePipelineKey::TONEMAP_METHOD_NONE.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() }
        }
    };Clone, #[automatically_derived]
impl ::core::marker::Copy for SpritePipelineKey { }Copy, #[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)
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for SpritePipelineKey {
    #[inline]
    fn eq(&self, other: &SpritePipelineKey) -> bool { self.0 == other.0 }
}PartialEq, #[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>;
    }
}Eq, #[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)
    }
}Hash)]
95    #[repr(transparent)]
96    // NOTE: Apparently quadro drivers support up to 64x MSAA.
97    // MSAA uses the highest 3 bits for the MSAA log2(sample count) to support up to 128x MSAA.
98    pub struct SpritePipelineKey: u32 {
99        const NONE                              = 0;
100        const TONEMAP_IN_SHADER                 = 1 << 0;
101        const DEBAND_DITHER                     = 1 << 1;
102        const SRGB_COMPOSITING                  = 1 << 2;
103        const OKLAB_COMPOSITING                 = 1 << 3;
104        const COLOR_TARGET_FORMAT_RESERVED_BITS = Self::COLOR_TARGET_FORMAT_MASK_BITS << Self::COLOR_TARGET_FORMAT_SHIFT_BITS;
105        const MSAA_RESERVED_BITS                = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
106        const TONEMAP_METHOD_RESERVED_BITS      = Self::TONEMAP_METHOD_MASK_BITS << Self::TONEMAP_METHOD_SHIFT_BITS;
107        const TONEMAP_METHOD_NONE               = 0 << Self::TONEMAP_METHOD_SHIFT_BITS;
108        const TONEMAP_METHOD_REINHARD           = 1 << Self::TONEMAP_METHOD_SHIFT_BITS;
109        const TONEMAP_METHOD_REINHARD_LUMINANCE = 2 << Self::TONEMAP_METHOD_SHIFT_BITS;
110        const TONEMAP_METHOD_ACES_FITTED        = 3 << Self::TONEMAP_METHOD_SHIFT_BITS;
111        const TONEMAP_METHOD_AGX                = 4 << Self::TONEMAP_METHOD_SHIFT_BITS;
112        const TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM = 5 << Self::TONEMAP_METHOD_SHIFT_BITS;
113        const TONEMAP_METHOD_TONY_MC_MAPFACE    = 6 << Self::TONEMAP_METHOD_SHIFT_BITS;
114        const TONEMAP_METHOD_BLENDER_FILMIC     = 7 << Self::TONEMAP_METHOD_SHIFT_BITS;
115        const TONEMAP_METHOD_PBR_NEUTRAL        = 8 << Self::TONEMAP_METHOD_SHIFT_BITS;
116
117    }
118}
119
120impl SpritePipelineKey {
121    const COLOR_TARGET_FORMAT_MASK_BITS: u32 = bevy_render::view::COLOR_TARGET_FORMAT_MASK_BITS;
122    const COLOR_TARGET_FORMAT_SHIFT_BITS: u32 = 4;
123    const MSAA_MASK_BITS: u32 = 0b111;
124    const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones();
125    const TONEMAP_METHOD_MASK_BITS: u32 = 0b1111;
126    const TONEMAP_METHOD_SHIFT_BITS: u32 =
127        Self::MSAA_SHIFT_BITS - Self::TONEMAP_METHOD_MASK_BITS.count_ones();
128
129    #[inline]
130    pub const fn from_msaa_samples(msaa_samples: u32) -> Self {
131        let msaa_bits =
132            (msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
133        Self::from_bits_retain(msaa_bits)
134    }
135
136    #[inline]
137    pub const fn msaa_samples(&self) -> u32 {
138        1 << ((self.bits() >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
139    }
140
141    /// Create a pipeline key from the view's color target format.
142    #[inline]
143    pub fn from_target_format(format: TextureFormat) -> Self {
144        let code = texture_format_to_code(format)
145            .expect("Texture format is not supported by the pipeline") as u32;
146        Self::from_bits_retain(
147            (code & Self::COLOR_TARGET_FORMAT_MASK_BITS) << Self::COLOR_TARGET_FORMAT_SHIFT_BITS,
148        )
149    }
150
151    /// Color target format of the main pass for this pipeline key.
152    #[inline]
153    pub fn target_format(&self) -> TextureFormat {
154        let code = ((self.bits() >> Self::COLOR_TARGET_FORMAT_SHIFT_BITS)
155            & Self::COLOR_TARGET_FORMAT_MASK_BITS) as u8;
156        texture_format_from_code(code)
157            .expect("Unknown bits in `COLOR_TARGET_FORMAT_MASK_BITS` of the pipeline key")
158    }
159}
160
161impl SpecializedRenderPipeline for SpritePipeline {
162    type Key = SpritePipelineKey;
163
164    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
165        let mut shader_defs = Vec::new();
166        if key.contains(SpritePipelineKey::TONEMAP_IN_SHADER) {
167            shader_defs.push("TONEMAP_IN_SHADER".into());
168            shader_defs.push(ShaderDefVal::UInt(
169                "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
170                1,
171            ));
172            shader_defs.push(ShaderDefVal::UInt(
173                "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
174                2,
175            ));
176
177            let method = key.intersection(SpritePipelineKey::TONEMAP_METHOD_RESERVED_BITS);
178
179            if method == SpritePipelineKey::TONEMAP_METHOD_NONE {
180                shader_defs.push("TONEMAP_METHOD_NONE".into());
181            } else if method == SpritePipelineKey::TONEMAP_METHOD_REINHARD {
182                shader_defs.push("TONEMAP_METHOD_REINHARD".into());
183            } else if method == SpritePipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
184                shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
185            } else if method == SpritePipelineKey::TONEMAP_METHOD_ACES_FITTED {
186                shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
187            } else if method == SpritePipelineKey::TONEMAP_METHOD_AGX {
188                shader_defs.push("TONEMAP_METHOD_AGX".into());
189            } else if method == SpritePipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
190            {
191                shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
192            } else if method == SpritePipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
193                shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
194            } else if method == SpritePipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
195                shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
196            } else if method == SpritePipelineKey::TONEMAP_METHOD_PBR_NEUTRAL {
197                shader_defs.push("TONEMAP_METHOD_PBR_NEUTRAL".into());
198            }
199
200            // Debanding is tied to tonemapping in the shader, cannot run without it.
201            if key.contains(SpritePipelineKey::DEBAND_DITHER) {
202                shader_defs.push("DEBAND_DITHER".into());
203            }
204        }
205
206        if key.contains(SpritePipelineKey::SRGB_COMPOSITING) {
207            shader_defs.push("SRGB_OUTPUT".into());
208        }
209        if key.contains(SpritePipelineKey::OKLAB_COMPOSITING) {
210            shader_defs.push("OKLAB_OUTPUT".into());
211        }
212
213        let format = key.target_format();
214
215        let instance_rate_vertex_buffer_layout = VertexBufferLayout {
216            array_stride: 80,
217            step_mode: VertexStepMode::Instance,
218            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![
219                // @location(0) i_model_transpose_col0: vec4<f32>,
220                VertexAttribute {
221                    format: VertexFormat::Float32x4,
222                    offset: 0,
223                    shader_location: 0,
224                },
225                // @location(1) i_model_transpose_col1: vec4<f32>,
226                VertexAttribute {
227                    format: VertexFormat::Float32x4,
228                    offset: 16,
229                    shader_location: 1,
230                },
231                // @location(2) i_model_transpose_col2: vec4<f32>,
232                VertexAttribute {
233                    format: VertexFormat::Float32x4,
234                    offset: 32,
235                    shader_location: 2,
236                },
237                // @location(3) i_color: vec4<f32>,
238                VertexAttribute {
239                    format: VertexFormat::Float32x4,
240                    offset: 48,
241                    shader_location: 3,
242                },
243                // @location(4) i_uv_offset_scale: vec4<f32>,
244                VertexAttribute {
245                    format: VertexFormat::Float32x4,
246                    offset: 64,
247                    shader_location: 4,
248                },
249            ],
250        };
251
252        RenderPipelineDescriptor {
253            vertex: VertexState {
254                shader: self.shader.clone(),
255                shader_defs: shader_defs.clone(),
256                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],
257                ..default()
258            },
259            fragment: Some(FragmentState {
260                shader: self.shader.clone(),
261                shader_defs,
262                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 {
263                    format,
264                    blend: Some(BlendState::ALPHA_BLENDING),
265                    write_mask: ColorWrites::ALL,
266                })],
267                ..default()
268            }),
269            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()],
270            // Sprites are always alpha blended so they never need to write to depth.
271            // They just need to read it in case an opaque mesh2d
272            // that wrote to depth is present.
273            depth_stencil: Some(DepthStencilState {
274                format: CORE_2D_DEPTH_FORMAT,
275                depth_write_enabled: Some(false),
276                depth_compare: Some(CompareFunction::GreaterEqual),
277                stencil: StencilState {
278                    front: StencilFaceState::IGNORE,
279                    back: StencilFaceState::IGNORE,
280                    read_mask: 0,
281                    write_mask: 0,
282                },
283                bias: DepthBiasState {
284                    constant: 0,
285                    slope_scale: 0.0,
286                    clamp: 0.0,
287                },
288            }),
289            multisample: MultisampleState {
290                count: key.msaa_samples(),
291                mask: !0,
292                alpha_to_coverage_enabled: false,
293            },
294            label: Some("sprite_pipeline".into()),
295            ..default()
296        }
297    }
298}
299
300pub struct ExtractedSlice {
301    pub offset: Vec2,
302    pub rect: Rect,
303    pub size: Vec2,
304}
305
306pub struct ExtractedSprite {
307    pub main_entity: Entity,
308    pub render_entity: Entity,
309    pub transform: GlobalTransform,
310    pub color: LinearRgba,
311    /// Change the on-screen size of the sprite
312    /// Asset ID of the [`Image`] of this sprite
313    /// PERF: storing an `AssetId` instead of `Handle<Image>` enables some optimizations (`ExtractedSprite` becomes `Copy` and doesn't need to be dropped)
314    pub image_handle_id: AssetId<Image>,
315    pub flip_x: bool,
316    pub flip_y: bool,
317    pub kind: ExtractedSpriteKind,
318}
319
320pub enum ExtractedSpriteKind {
321    /// A single sprite with custom sizing and scaling options
322    Single {
323        anchor: Vec2,
324        rect: Option<Rect>,
325        scaling_mode: Option<SpriteScalingMode>,
326        custom_size: Option<Vec2>,
327    },
328    /// Indexes into the list of [`ExtractedSlice`]s stored in the [`ExtractedSlices`] resource
329    /// Used for elements composed from multiple sprites such as text or nine-patched borders
330    Slices { indices: Range<usize> },
331}
332
333#[derive(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() -> ExtractedSprites {
        ExtractedSprites { sprites: ::core::default::Default::default() }
    }
}Default)]
334pub struct ExtractedSprites {
335    pub sprites: Vec<ExtractedSprite>,
336}
337
338#[derive(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() -> ExtractedSlices {
        ExtractedSlices { slices: ::core::default::Default::default() }
    }
}Default)]
339pub struct ExtractedSlices {
340    pub slices: Vec<ExtractedSlice>,
341}
342
343#[derive(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() -> SpriteAssetEvents {
        SpriteAssetEvents { images: ::core::default::Default::default() }
    }
}Default)]
344pub struct SpriteAssetEvents {
345    pub images: Vec<AssetEvent<Image>>,
346}
347
348pub fn extract_sprite_events(
349    mut events: ResMut<SpriteAssetEvents>,
350    mut image_events: Extract<MessageReader<AssetEvent<Image>>>,
351) {
352    let SpriteAssetEvents { ref mut images } = *events;
353    images.clear();
354
355    for event in image_events.read() {
356        images.push(*event);
357    }
358}
359
360pub fn extract_sprites(
361    mut extracted_sprites: ResMut<ExtractedSprites>,
362    mut extracted_slices: ResMut<ExtractedSlices>,
363    texture_atlases: Extract<Res<Assets<TextureAtlasLayout>>>,
364    sprite_query: Extract<
365        Query<(
366            Entity,
367            RenderEntity,
368            &ViewVisibility,
369            &Sprite,
370            &GlobalTransform,
371            &Anchor,
372            Option<&ComputedTextureSlices>,
373        )>,
374    >,
375) {
376    extracted_sprites.sprites.clear();
377    extracted_slices.slices.clear();
378    for (main_entity, render_entity, view_visibility, sprite, transform, anchor, slices) in
379        sprite_query.iter()
380    {
381        if !view_visibility.get() {
382            continue;
383        }
384
385        if let Some(slices) = slices {
386            let start = extracted_slices.slices.len();
387            extracted_slices
388                .slices
389                .extend(slices.extract_slices(sprite, anchor.as_vec()));
390            let end = extracted_slices.slices.len();
391            extracted_sprites.sprites.push(ExtractedSprite {
392                main_entity,
393                render_entity,
394                color: sprite.color.into(),
395                transform: *transform,
396                flip_x: sprite.flip_x,
397                flip_y: sprite.flip_y,
398                image_handle_id: sprite.image.id(),
399                kind: ExtractedSpriteKind::Slices {
400                    indices: start..end,
401                },
402            });
403        } else {
404            let atlas_rect = sprite
405                .texture_atlas
406                .as_ref()
407                .and_then(|s| s.texture_rect(&texture_atlases).map(|r| r.as_rect()));
408            let rect = match (atlas_rect, sprite.rect) {
409                (None, None) => None,
410                (None, Some(sprite_rect)) => Some(sprite_rect),
411                (Some(atlas_rect), None) => Some(atlas_rect),
412                (Some(atlas_rect), Some(mut sprite_rect)) => {
413                    sprite_rect.min += atlas_rect.min;
414                    sprite_rect.max += atlas_rect.min;
415                    Some(sprite_rect)
416                }
417            };
418
419            // PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive
420            extracted_sprites.sprites.push(ExtractedSprite {
421                main_entity,
422                render_entity,
423                color: sprite.color.into(),
424                transform: *transform,
425                flip_x: sprite.flip_x,
426                flip_y: sprite.flip_y,
427                image_handle_id: sprite.image.id(),
428                kind: ExtractedSpriteKind::Single {
429                    anchor: anchor.as_vec(),
430                    rect,
431                    scaling_mode: sprite.image_mode.scale(),
432                    // Pass the custom size
433                    custom_size: sprite.custom_size,
434                },
435            });
436        }
437    }
438}
439
440#[repr(C)]
441#[derive(#[automatically_derived]
impl ::core::marker::Copy for SpriteInstance { }Copy, #[automatically_derived]
impl ::core::clone::Clone for SpriteInstance {
    #[inline]
    fn clone(&self) -> SpriteInstance {
        let _: ::core::clone::AssertParamIsClone<[Vec4; 3]>;
        let _: ::core::clone::AssertParamIsClone<[f32; 4]>;
        let _: ::core::clone::AssertParamIsClone<[f32; 4]>;
        *self
    }
}Clone, unsafe impl ::bytemuck::Pod for SpriteInstance {}Pod, unsafe impl ::bytemuck::Zeroable for SpriteInstance {}Zeroable)]
442struct SpriteInstance {
443    // Affine 4x3 transposed to 3x4
444    pub i_model_transpose: [Vec4; 3],
445    pub i_color: [f32; 4],
446    pub i_uv_offset_scale: [f32; 4],
447}
448
449impl SpriteInstance {
450    #[inline]
451    fn from(transform: &Affine3A, color: &LinearRgba, uv_offset_scale: &Vec4) -> Self {
452        let transpose_model_3x3 = transform.matrix3.transpose();
453        Self {
454            i_model_transpose: [
455                transpose_model_3x3.x_axis.extend(transform.translation.x),
456                transpose_model_3x3.y_axis.extend(transform.translation.y),
457                transpose_model_3x3.z_axis.extend(transform.translation.z),
458            ],
459            i_color: color.to_f32_array(),
460            i_uv_offset_scale: uv_offset_scale.to_array(),
461        }
462    }
463}
464
465#[derive(impl bevy_ecs::resource::Resource for SpriteMeta where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
466pub struct SpriteMeta {
467    sprite_index_buffer: RawBufferVec<u32>,
468    sprite_instance_buffer: RawBufferVec<SpriteInstance>,
469}
470
471impl Default for SpriteMeta {
472    fn default() -> Self {
473        Self {
474            sprite_index_buffer: RawBufferVec::<u32>::new(BufferUsages::INDEX),
475            sprite_instance_buffer: RawBufferVec::<SpriteInstance>::new(BufferUsages::VERTEX),
476        }
477    }
478}
479
480#[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)]
481pub struct SpriteViewBindGroup {
482    pub value: BindGroup,
483}
484
485#[derive(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() -> SpriteBatches {
        SpriteBatches(::core::default::Default::default())
    }
}Default)]
486pub struct SpriteBatches(HashMap<(RetainedViewEntity, Entity), SpriteBatch>);
487
488#[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for SpriteBatch {
    #[inline]
    fn eq(&self, other: &SpriteBatch) -> 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) -> SpriteBatch {
        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)]
489pub struct SpriteBatch {
490    image_handle_id: AssetId<Image>,
491    range: Range<u32>,
492}
493
494#[derive(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() -> ImageBindGroups {
        ImageBindGroups { values: ::core::default::Default::default() }
    }
}Default)]
495pub struct ImageBindGroups {
496    values: HashMap<AssetId<Image>, BindGroup>,
497}
498
499pub fn queue_sprites(
500    mut view_entities: Local<FixedBitSet>,
501    draw_functions: Res<DrawFunctions<Transparent2d>>,
502    sprite_pipeline: Res<SpritePipeline>,
503    mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>,
504    pipeline_cache: Res<PipelineCache>,
505    extracted_sprites: Res<ExtractedSprites>,
506    mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
507    mut cameras: Query<(
508        &RenderVisibleEntities,
509        &ExtractedCamera,
510        &ExtractedView,
511        &Msaa,
512        Option<&Tonemapping>,
513        Option<&DebandDither>,
514    )>,
515) {
516    let draw_sprite_function = draw_functions.read().id::<DrawSprite>();
517
518    for (visible_entities, camera, view, msaa, tonemapping, dither) in &mut cameras {
519        let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)
520        else {
521            continue;
522        };
523
524        let msaa_key = SpritePipelineKey::from_msaa_samples(msaa.samples());
525        let mut view_key = SpritePipelineKey::from_target_format(view.target_format) | msaa_key;
526
527        if camera
528            .compositing_space
529            .is_some_and(|s| s == bevy_camera::CompositingSpace::Srgb)
530        {
531            view_key |= SpritePipelineKey::SRGB_COMPOSITING;
532        }
533        if camera
534            .compositing_space
535            .is_some_and(|s| s == bevy_camera::CompositingSpace::Oklab)
536        {
537            view_key |= SpritePipelineKey::OKLAB_COMPOSITING;
538        }
539
540        if !camera.hdr {
541            if let Some(tonemapping) = tonemapping {
542                view_key |= SpritePipelineKey::TONEMAP_IN_SHADER;
543                view_key |= match tonemapping {
544                    Tonemapping::None => SpritePipelineKey::TONEMAP_METHOD_NONE,
545                    Tonemapping::Reinhard => SpritePipelineKey::TONEMAP_METHOD_REINHARD,
546                    Tonemapping::ReinhardLuminance => {
547                        SpritePipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE
548                    }
549                    Tonemapping::AcesFitted => SpritePipelineKey::TONEMAP_METHOD_ACES_FITTED,
550                    Tonemapping::AgX => SpritePipelineKey::TONEMAP_METHOD_AGX,
551                    Tonemapping::SomewhatBoringDisplayTransform => {
552                        SpritePipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
553                    }
554                    Tonemapping::TonyMcMapface => SpritePipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
555                    Tonemapping::BlenderFilmic => SpritePipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
556                    Tonemapping::KhronosPbrNeutral => SpritePipelineKey::TONEMAP_METHOD_PBR_NEUTRAL,
557                };
558            }
559            if let Some(DebandDither::Enabled) = dither {
560                view_key |= SpritePipelineKey::DEBAND_DITHER;
561            }
562        }
563
564        let pipeline = pipelines.specialize(&pipeline_cache, &sprite_pipeline, view_key);
565
566        view_entities.clear();
567        if let Some(visible_entities) = visible_entities.get::<Sprite>() {
568            view_entities.extend(
569                visible_entities
570                    .iter_visible()
571                    .map(|(_, e)| e.index_u32() as usize),
572            );
573        }
574
575        transparent_phase
576            .items
577            .reserve(extracted_sprites.sprites.len());
578
579        for (index, extracted_sprite) in extracted_sprites.sprites.iter().enumerate() {
580            let view_index = extracted_sprite.main_entity.index_u32();
581
582            if !view_entities.contains(view_index as usize) {
583                continue;
584            }
585
586            // These items will be sorted by depth with other phase items
587            let sort_key = FloatOrd(extracted_sprite.transform.translation().z);
588
589            // Add the item to the render phase
590            transparent_phase.add_transient(Transparent2d {
591                draw_function: draw_sprite_function,
592                pipeline,
593                entity: (
594                    extracted_sprite.render_entity,
595                    extracted_sprite.main_entity.into(),
596                ),
597                sort_key,
598                // `batch_range` is calculated in `prepare_sprite_image_bind_groups`
599                batch_range: 0..0,
600                extra_index: PhaseItemExtraIndex::None,
601                extracted_index: index,
602                indexed: true,
603            });
604        }
605    }
606}
607
608pub fn prepare_sprite_view_bind_groups(
609    mut commands: Commands,
610    render_device: Res<RenderDevice>,
611    pipeline_cache: Res<PipelineCache>,
612    sprite_pipeline: Res<SpritePipeline>,
613    view_uniforms: Res<ViewUniforms>,
614    views: Query<(Entity, &Tonemapping), With<ExtractedView>>,
615    tonemapping_luts: Res<TonemappingLuts>,
616    images: Res<RenderAssets<GpuImage>>,
617    fallback_image: Res<FallbackImage>,
618) {
619    let Some(view_binding) = view_uniforms.uniforms.binding() else {
620        return;
621    };
622
623    for (entity, tonemapping) in &views {
624        let lut_bindings =
625            get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
626        let view_bind_group = render_device.create_bind_group(
627            "mesh2d_view_bind_group",
628            &pipeline_cache.get_bind_group_layout(&sprite_pipeline.view_layout),
629            &BindGroupEntries::sequential((view_binding.clone(), lut_bindings.0, lut_bindings.1)),
630        );
631
632        commands.entity(entity).insert(SpriteViewBindGroup {
633            value: view_bind_group,
634        });
635    }
636}
637
638pub fn prepare_sprite_image_bind_groups(
639    render_device: Res<RenderDevice>,
640    render_queue: Res<RenderQueue>,
641    pipeline_cache: Res<PipelineCache>,
642    mut sprite_meta: ResMut<SpriteMeta>,
643    sprite_pipeline: Res<SpritePipeline>,
644    mut image_bind_groups: ResMut<ImageBindGroups>,
645    gpu_images: Res<RenderAssets<GpuImage>>,
646    extracted_sprites: Res<ExtractedSprites>,
647    extracted_slices: Res<ExtractedSlices>,
648    mut phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
649    events: Res<SpriteAssetEvents>,
650    mut batches: ResMut<SpriteBatches>,
651) {
652    // If an image has changed, the GpuImage has (probably) changed
653    for event in &events.images {
654        match event {
655            AssetEvent::Added { .. } |
656            // Images don't have dependencies
657            AssetEvent::LoadedWithDependencies { .. } => {}
658            AssetEvent::Unused { id } | AssetEvent::Modified { id } | AssetEvent::Removed { id } => {
659                image_bind_groups.values.remove(id);
660            }
661        };
662    }
663
664    batches.clear();
665
666    // Clear the sprite instances
667    sprite_meta.sprite_instance_buffer.clear();
668
669    // Index buffer indices
670    let mut index = 0;
671
672    let image_bind_groups = &mut *image_bind_groups;
673
674    for (retained_view, transparent_phase) in phases.iter_mut() {
675        let mut current_batch = None;
676        let mut batch_item_index = 0;
677        let mut batch_image_size = Vec2::ZERO;
678        let mut batch_image_handle = None;
679
680        // Iterate through the phase items and detect when successive sprites that can be batched.
681        // Spawn an entity with a `SpriteBatch` component for each possible batch.
682        // Compatible items share the same entity.
683        for item_index in 0..transparent_phase.items.len() {
684            let item = &transparent_phase.items[item_index];
685
686            let Some(extracted_sprite) = extracted_sprites
687                .sprites
688                .get(item.extracted_index)
689                .filter(|extracted_sprite| extracted_sprite.render_entity == item.entity())
690            else {
691                // If there is a phase item that is not a sprite, then we must start a new
692                // batch to draw the other phase item(s) and to respect draw order. This can be
693                // done by invalidating the batch_image_handle
694                batch_image_handle = None;
695                continue;
696            };
697
698            if batch_image_handle != Some(extracted_sprite.image_handle_id) {
699                let Some(gpu_image) = gpu_images.get(extracted_sprite.image_handle_id) else {
700                    continue;
701                };
702
703                batch_image_size = gpu_image.size_2d().as_vec2();
704                let image_handle = extracted_sprite.image_handle_id;
705                batch_image_handle = Some(image_handle);
706                image_bind_groups
707                    .values
708                    .entry(image_handle)
709                    .or_insert_with(|| {
710                        render_device.create_bind_group(
711                            "sprite_material_bind_group",
712                            &pipeline_cache.get_bind_group_layout(&sprite_pipeline.material_layout),
713                            &BindGroupEntries::sequential((
714                                &gpu_image.texture_view,
715                                &gpu_image.sampler,
716                            )),
717                        )
718                    });
719
720                batch_item_index = item_index;
721                current_batch = Some(batches.entry((*retained_view, item.entity())).insert(
722                    SpriteBatch {
723                        image_handle_id: image_handle,
724                        range: index..index,
725                    },
726                ));
727            }
728            match extracted_sprite.kind {
729                ExtractedSpriteKind::Single {
730                    anchor,
731                    rect,
732                    scaling_mode,
733                    custom_size,
734                } => {
735                    // By default, the size of the quad is the size of the texture
736                    let mut quad_size = batch_image_size;
737                    let mut texture_size = batch_image_size;
738
739                    // Calculate vertex data for this item
740                    // If a rect is specified, adjust UVs and the size of the quad
741                    let mut uv_offset_scale = if let Some(rect) = rect {
742                        let rect_size = rect.size();
743                        quad_size = rect_size;
744                        // Update texture size to the rect size
745                        // It will help scale properly only portion of the image
746                        texture_size = rect_size;
747                        Vec4::new(
748                            rect.min.x / batch_image_size.x,
749                            rect.max.y / batch_image_size.y,
750                            rect_size.x / batch_image_size.x,
751                            -rect_size.y / batch_image_size.y,
752                        )
753                    } else {
754                        Vec4::new(0.0, 1.0, 1.0, -1.0)
755                    };
756
757                    if extracted_sprite.flip_x {
758                        uv_offset_scale.x += uv_offset_scale.z;
759                        uv_offset_scale.z *= -1.0;
760                    }
761                    if extracted_sprite.flip_y {
762                        uv_offset_scale.y += uv_offset_scale.w;
763                        uv_offset_scale.w *= -1.0;
764                    }
765
766                    // Override the size if a custom one is specified
767                    quad_size = custom_size.unwrap_or(quad_size);
768
769                    // Used for translation of the quad if `TextureScale::Fit...` is specified.
770                    let mut quad_translation = Vec2::ZERO;
771
772                    // Scales the texture based on the `texture_scale` field.
773                    if let Some(scaling_mode) = scaling_mode {
774                        apply_scaling(
775                            scaling_mode,
776                            texture_size,
777                            &mut quad_size,
778                            &mut quad_translation,
779                            &mut uv_offset_scale,
780                        );
781                    }
782
783                    let transform = extracted_sprite.transform.affine()
784                        * Affine3A::from_scale_rotation_translation(
785                            quad_size.extend(1.0),
786                            Quat::IDENTITY,
787                            ((quad_size + quad_translation) * (-anchor - Vec2::splat(0.5)))
788                                .extend(0.0),
789                        );
790
791                    // Store the vertex data and add the item to the render phase
792                    sprite_meta
793                        .sprite_instance_buffer
794                        .push(SpriteInstance::from(
795                            &transform,
796                            &extracted_sprite.color,
797                            &uv_offset_scale,
798                        ));
799
800                    current_batch.as_mut().unwrap().get_mut().range.end += 1;
801                    index += 1;
802                }
803                ExtractedSpriteKind::Slices { ref indices } => {
804                    for i in indices.clone() {
805                        let slice = &extracted_slices.slices[i];
806                        let rect = slice.rect;
807                        let rect_size = rect.size();
808
809                        // Calculate vertex data for this item
810                        let mut uv_offset_scale: Vec4;
811
812                        // If a rect is specified, adjust UVs and the size of the quad
813                        uv_offset_scale = Vec4::new(
814                            rect.min.x / batch_image_size.x,
815                            rect.max.y / batch_image_size.y,
816                            rect_size.x / batch_image_size.x,
817                            -rect_size.y / batch_image_size.y,
818                        );
819
820                        if extracted_sprite.flip_x {
821                            uv_offset_scale.x += uv_offset_scale.z;
822                            uv_offset_scale.z *= -1.0;
823                        }
824                        if extracted_sprite.flip_y {
825                            uv_offset_scale.y += uv_offset_scale.w;
826                            uv_offset_scale.w *= -1.0;
827                        }
828
829                        let transform = extracted_sprite.transform.affine()
830                            * Affine3A::from_scale_rotation_translation(
831                                slice.size.extend(1.0),
832                                Quat::IDENTITY,
833                                (slice.size * -Vec2::splat(0.5) + slice.offset).extend(0.0),
834                            );
835
836                        // Store the vertex data and add the item to the render phase
837                        sprite_meta
838                            .sprite_instance_buffer
839                            .push(SpriteInstance::from(
840                                &transform,
841                                &extracted_sprite.color,
842                                &uv_offset_scale,
843                            ));
844
845                        current_batch.as_mut().unwrap().get_mut().range.end += 1;
846                        index += 1;
847                    }
848                }
849            }
850            transparent_phase.items[batch_item_index]
851                .batch_range_mut()
852                .end += 1;
853        }
854        sprite_meta
855            .sprite_instance_buffer
856            .write_buffer(&render_device, &render_queue);
857
858        if sprite_meta.sprite_index_buffer.len() != 6 {
859            sprite_meta.sprite_index_buffer.clear();
860
861            // NOTE: This code is creating 6 indices pointing to 4 vertices.
862            // The vertices form the corners of a quad based on their two least significant bits.
863            // 10   11
864            //
865            // 00   01
866            // The sprite shader can then use the two least significant bits as the vertex index.
867            // The rest of the properties to transform the vertex positions and UVs (which are
868            // implicit) are baked into the instance transform, and UV offset and scale.
869            // See bevy_sprite_render/src/render/sprite.wgsl for the details.
870            sprite_meta.sprite_index_buffer.push(2);
871            sprite_meta.sprite_index_buffer.push(0);
872            sprite_meta.sprite_index_buffer.push(1);
873            sprite_meta.sprite_index_buffer.push(1);
874            sprite_meta.sprite_index_buffer.push(3);
875            sprite_meta.sprite_index_buffer.push(2);
876
877            sprite_meta
878                .sprite_index_buffer
879                .write_buffer(&render_device, &render_queue);
880        }
881    }
882}
883/// [`RenderCommand`] for sprite rendering.
884pub type DrawSprite = (
885    SetItemPipeline,
886    SetSpriteViewBindGroup<0>,
887    SetSpriteTextureBindGroup<1>,
888    DrawSpriteBatch,
889);
890
891pub struct SetSpriteViewBindGroup<const I: usize>;
892impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteViewBindGroup<I> {
893    type Param = ();
894    type ViewQuery = (Read<ViewUniformOffset>, Read<SpriteViewBindGroup>);
895    type ItemQuery = ();
896
897    fn render<'w>(
898        _item: &P,
899        (view_uniform, sprite_view_bind_group): ROQueryItem<'w, '_, Self::ViewQuery>,
900        _entity: Option<()>,
901        _param: SystemParamItem<'w, '_, Self::Param>,
902        pass: &mut TrackedRenderPass<'w>,
903    ) -> RenderCommandResult {
904        pass.set_bind_group(I, &sprite_view_bind_group.value, &[view_uniform.offset]);
905        RenderCommandResult::Success
906    }
907}
908pub struct SetSpriteTextureBindGroup<const I: usize>;
909impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteTextureBindGroup<I> {
910    type Param = (SRes<ImageBindGroups>, SRes<SpriteBatches>);
911    type ViewQuery = Read<ExtractedView>;
912    type ItemQuery = ();
913
914    fn render<'w>(
915        item: &P,
916        view: ROQueryItem<'w, '_, Self::ViewQuery>,
917        _entity: Option<()>,
918        (image_bind_groups, batches): SystemParamItem<'w, '_, Self::Param>,
919        pass: &mut TrackedRenderPass<'w>,
920    ) -> RenderCommandResult {
921        let image_bind_groups = image_bind_groups.into_inner();
922        let Some(batch) = batches.get(&(view.retained_view_entity, item.entity())) else {
923            return RenderCommandResult::Skip;
924        };
925
926        pass.set_bind_group(
927            I,
928            image_bind_groups
929                .values
930                .get(&batch.image_handle_id)
931                .unwrap(),
932            &[],
933        );
934        RenderCommandResult::Success
935    }
936}
937
938pub struct DrawSpriteBatch;
939impl<P: PhaseItem> RenderCommand<P> for DrawSpriteBatch {
940    type Param = (SRes<SpriteMeta>, SRes<SpriteBatches>);
941    type ViewQuery = Read<ExtractedView>;
942    type ItemQuery = ();
943
944    fn render<'w>(
945        item: &P,
946        view: ROQueryItem<'w, '_, Self::ViewQuery>,
947        _entity: Option<()>,
948        (sprite_meta, batches): SystemParamItem<'w, '_, Self::Param>,
949        pass: &mut TrackedRenderPass<'w>,
950    ) -> RenderCommandResult {
951        let sprite_meta = sprite_meta.into_inner();
952        let Some(batch) = batches.get(&(view.retained_view_entity, item.entity())) else {
953            return RenderCommandResult::Skip;
954        };
955
956        pass.set_index_buffer(
957            sprite_meta.sprite_index_buffer.buffer().unwrap().slice(..),
958            IndexFormat::Uint32,
959        );
960        pass.set_vertex_buffer(
961            0,
962            sprite_meta
963                .sprite_instance_buffer
964                .buffer()
965                .unwrap()
966                .slice(..),
967        );
968        pass.draw_indexed(0..6, 0, batch.range.clone());
969        RenderCommandResult::Success
970    }
971}
972
973/// Scales a texture to fit within a given quad size with keeping the aspect ratio.
974fn apply_scaling(
975    scaling_mode: SpriteScalingMode,
976    texture_size: Vec2,
977    quad_size: &mut Vec2,
978    quad_translation: &mut Vec2,
979    uv_offset_scale: &mut Vec4,
980) {
981    let quad_ratio = quad_size.x / quad_size.y;
982    let texture_ratio = texture_size.x / texture_size.y;
983    let tex_quad_scale = texture_ratio / quad_ratio;
984    let quad_tex_scale = quad_ratio / texture_ratio;
985
986    match scaling_mode {
987        SpriteScalingMode::FillCenter => {
988            if quad_ratio > texture_ratio {
989                // offset texture to center by y coordinate
990                uv_offset_scale.y += (uv_offset_scale.w - uv_offset_scale.w * tex_quad_scale) * 0.5;
991                // sum up scales
992                uv_offset_scale.w *= tex_quad_scale;
993            } else {
994                // offset texture to center by x coordinate
995                uv_offset_scale.x += (uv_offset_scale.z - uv_offset_scale.z * quad_tex_scale) * 0.5;
996                uv_offset_scale.z *= quad_tex_scale;
997            };
998        }
999        SpriteScalingMode::FillStart => {
1000            if quad_ratio > texture_ratio {
1001                uv_offset_scale.y += uv_offset_scale.w - uv_offset_scale.w * tex_quad_scale;
1002                uv_offset_scale.w *= tex_quad_scale;
1003            } else {
1004                uv_offset_scale.z *= quad_tex_scale;
1005            }
1006        }
1007        SpriteScalingMode::FillEnd => {
1008            if quad_ratio > texture_ratio {
1009                uv_offset_scale.w *= tex_quad_scale;
1010            } else {
1011                uv_offset_scale.x += uv_offset_scale.z - uv_offset_scale.z * quad_tex_scale;
1012                uv_offset_scale.z *= quad_tex_scale;
1013            }
1014        }
1015        SpriteScalingMode::FitCenter => {
1016            if texture_ratio > quad_ratio {
1017                // Scale based on width
1018                quad_size.y *= quad_tex_scale;
1019            } else {
1020                // Scale based on height
1021                quad_size.x *= tex_quad_scale;
1022            }
1023        }
1024        SpriteScalingMode::FitStart => {
1025            if texture_ratio > quad_ratio {
1026                // The quad is scaled to match the image ratio, and the quad translation is adjusted
1027                // to start of the quad within the original quad size.
1028                let scale = Vec2::new(1.0, quad_tex_scale);
1029                let new_quad = *quad_size * scale;
1030                let offset = *quad_size - new_quad;
1031                *quad_translation = Vec2::new(0.0, -offset.y);
1032                *quad_size = new_quad;
1033            } else {
1034                let scale = Vec2::new(tex_quad_scale, 1.0);
1035                let new_quad = *quad_size * scale;
1036                let offset = *quad_size - new_quad;
1037                *quad_translation = Vec2::new(offset.x, 0.0);
1038                *quad_size = new_quad;
1039            }
1040        }
1041        SpriteScalingMode::FitEnd => {
1042            if texture_ratio > quad_ratio {
1043                let scale = Vec2::new(1.0, quad_tex_scale);
1044                let new_quad = *quad_size * scale;
1045                let offset = *quad_size - new_quad;
1046                *quad_translation = Vec2::new(0.0, offset.y);
1047                *quad_size = new_quad;
1048            } else {
1049                let scale = Vec2::new(tex_quad_scale, 1.0);
1050                let new_quad = *quad_size * scale;
1051                let offset = *quad_size - new_quad;
1052                *quad_translation = Vec2::new(-offset.x, 0.0);
1053                *quad_size = new_quad;
1054            }
1055        }
1056    }
1057}