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 {
#[inline]
pub const fn empty() -> Self {
Self(<u32 as ::bitflags::Bits>::EMPTY)
}
#[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
}
#[inline]
pub const fn bits(&self) -> u32 { self.0 }
#[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 }
}
#[inline]
pub const fn from_bits_truncate(bits: u32) -> Self {
Self(bits & Self::all().0)
}
#[inline]
pub const fn from_bits_retain(bits: u32) -> Self { Self(bits) }
#[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
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.0 == <u32 as ::bitflags::Bits>::EMPTY
}
#[inline]
pub const fn is_all(&self) -> bool {
Self::all().0 | self.0 == self.0
}
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
self.0 & other.0 != <u32 as ::bitflags::Bits>::EMPTY
}
#[inline]
pub const fn contains(&self, other: Self) -> bool {
self.0 & other.0 == other.0
}
#[inline]
pub fn insert(&mut self, other: Self) {
*self = Self(self.0).union(other);
}
#[inline]
pub fn remove(&mut self, other: Self) {
*self = Self(self.0).difference(other);
}
#[inline]
pub fn toggle(&mut self, other: Self) {
*self = Self(self.0).symmetric_difference(other);
}
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
if value { self.insert(other); } else { self.remove(other); }
}
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0 & other.0)
}
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0 ^ other.0)
}
#[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;
#[inline]
fn bitor(self, other: InternalBitFlags) -> Self {
self.union(other)
}
}
impl ::bitflags::__private::core::ops::BitOrAssign for
InternalBitFlags {
#[inline]
fn bitor_assign(&mut self, other: Self) { self.insert(other); }
}
impl ::bitflags::__private::core::ops::BitXor for InternalBitFlags {
type Output = Self;
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
}
}
impl ::bitflags::__private::core::ops::BitXorAssign for
InternalBitFlags {
#[inline]
fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
}
impl ::bitflags::__private::core::ops::BitAnd for InternalBitFlags {
type Output = Self;
#[inline]
fn bitand(self, other: Self) -> Self { self.intersection(other) }
}
impl ::bitflags::__private::core::ops::BitAndAssign for
InternalBitFlags {
#[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;
#[inline]
fn sub(self, other: Self) -> Self { self.difference(other) }
}
impl ::bitflags::__private::core::ops::SubAssign for InternalBitFlags
{
#[inline]
fn sub_assign(&mut self, other: Self) { self.remove(other); }
}
impl ::bitflags::__private::core::ops::Not for InternalBitFlags {
type Output = Self;
#[inline]
fn not(self) -> Self { self.complement() }
}
impl ::bitflags::__private::core::iter::Extend<InternalBitFlags> for
InternalBitFlags {
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 {
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 {
#[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()))
}
#[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 {
#[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 {
#[inline]
pub const fn empty() -> Self { Self(InternalBitFlags::empty()) }
#[inline]
pub const fn all() -> Self { Self(InternalBitFlags::all()) }
#[inline]
pub const fn bits(&self) -> u32 { self.0.bits() }
#[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,
}
}
#[inline]
pub const fn from_bits_truncate(bits: u32) -> Self {
Self(InternalBitFlags::from_bits_truncate(bits))
}
#[inline]
pub const fn from_bits_retain(bits: u32) -> Self {
Self(InternalBitFlags::from_bits_retain(bits))
}
#[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,
}
}
#[inline]
pub const fn is_empty(&self) -> bool { self.0.is_empty() }
#[inline]
pub const fn is_all(&self) -> bool { self.0.is_all() }
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
self.0.intersects(other.0)
}
#[inline]
pub const fn contains(&self, other: Self) -> bool {
self.0.contains(other.0)
}
#[inline]
pub fn insert(&mut self, other: Self) { self.0.insert(other.0) }
#[inline]
pub fn remove(&mut self, other: Self) { self.0.remove(other.0) }
#[inline]
pub fn toggle(&mut self, other: Self) { self.0.toggle(other.0) }
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
self.0.set(other.0, value)
}
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0.intersection(other.0))
}
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0.union(other.0))
}
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0.difference(other.0))
}
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0.symmetric_difference(other.0))
}
#[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;
#[inline]
fn bitor(self, other: SpritePipelineKey) -> Self {
self.union(other)
}
}
impl ::bitflags::__private::core::ops::BitOrAssign for
SpritePipelineKey {
#[inline]
fn bitor_assign(&mut self, other: Self) { self.insert(other); }
}
impl ::bitflags::__private::core::ops::BitXor for SpritePipelineKey {
type Output = Self;
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
}
}
impl ::bitflags::__private::core::ops::BitXorAssign for
SpritePipelineKey {
#[inline]
fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
}
impl ::bitflags::__private::core::ops::BitAnd for SpritePipelineKey {
type Output = Self;
#[inline]
fn bitand(self, other: Self) -> Self { self.intersection(other) }
}
impl ::bitflags::__private::core::ops::BitAndAssign for
SpritePipelineKey {
#[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;
#[inline]
fn sub(self, other: Self) -> Self { self.difference(other) }
}
impl ::bitflags::__private::core::ops::SubAssign for SpritePipelineKey
{
#[inline]
fn sub_assign(&mut self, other: Self) { self.remove(other); }
}
impl ::bitflags::__private::core::ops::Not for SpritePipelineKey {
type Output = Self;
#[inline]
fn not(self) -> Self { self.complement() }
}
impl ::bitflags::__private::core::iter::Extend<SpritePipelineKey> for
SpritePipelineKey {
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 {
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 {
#[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()))
}
#[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 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 #[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 #[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 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 VertexAttribute {
221 format: VertexFormat::Float32x4,
222 offset: 0,
223 shader_location: 0,
224 },
225 VertexAttribute {
227 format: VertexFormat::Float32x4,
228 offset: 16,
229 shader_location: 1,
230 },
231 VertexAttribute {
233 format: VertexFormat::Float32x4,
234 offset: 32,
235 shader_location: 2,
236 },
237 VertexAttribute {
239 format: VertexFormat::Float32x4,
240 offset: 48,
241 shader_location: 3,
242 },
243 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 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 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 Single {
323 anchor: Vec2,
324 rect: Option<Rect>,
325 scaling_mode: Option<SpriteScalingMode>,
326 custom_size: Option<Vec2>,
327 },
328 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 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 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 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 let sort_key = FloatOrd(extracted_sprite.transform.translation().z);
588
589 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: 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 for event in &events.images {
654 match event {
655 AssetEvent::Added { .. } |
656 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 sprite_meta.sprite_instance_buffer.clear();
668
669 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 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 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 let mut quad_size = batch_image_size;
737 let mut texture_size = batch_image_size;
738
739 let mut uv_offset_scale = if let Some(rect) = rect {
742 let rect_size = rect.size();
743 quad_size = rect_size;
744 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 quad_size = custom_size.unwrap_or(quad_size);
768
769 let mut quad_translation = Vec2::ZERO;
771
772 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 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 let mut uv_offset_scale: Vec4;
811
812 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 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 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}
883pub 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
973fn 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 uv_offset_scale.y += (uv_offset_scale.w - uv_offset_scale.w * tex_quad_scale) * 0.5;
991 uv_offset_scale.w *= tex_quad_scale;
993 } else {
994 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 quad_size.y *= quad_tex_scale;
1019 } else {
1020 quad_size.x *= tex_quad_scale;
1022 }
1023 }
1024 SpriteScalingMode::FitStart => {
1025 if texture_ratio > quad_ratio {
1026 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}