add tonemapping LUT bindings for sprite and mesh2d pipelines (#13262)
Fixes #13118 If you use `Sprite` or `Mesh2d` and create `Camera` with * hdr=false * any tonemapper You would get ``` wgpu error: Validation Error Caused by: In Device::create_render_pipeline note: label = `sprite_pipeline` Error matching ShaderStages(FRAGMENT) shader requirements against the pipeline Shader global ResourceBinding { group: 0, binding: 19 } is not available in the pipeline layout Binding is missing from the pipeline layout ``` Because of missing tonemapping LUT bindings ## Solution Add missing bindings for tonemapping LUT's to `SpritePipeline` & `Mesh2dPipeline` ## Testing I checked that * `tonemapping` * `color_grading` * `sprite_animations` * `2d_shapes` * `meshlet` * `deferred_rendering` examples are still working 2d cases I checked with this code: ``` use bevy::{ color::palettes::css::PURPLE, core_pipeline::tonemapping::Tonemapping, prelude::*, sprite::MaterialMesh2dBundle, }; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems(Update, toggle_tonemapping_method) .run(); } fn setup( mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<ColorMaterial>>, asset_server: Res<AssetServer>, ) { commands.spawn(Camera2dBundle { camera: Camera { hdr: false, ..default() }, tonemapping: Tonemapping::BlenderFilmic, ..default() }); commands.spawn(MaterialMesh2dBundle { mesh: meshes.add(Rectangle::default()).into(), transform: Transform::default().with_scale(Vec3::splat(128.)), material: materials.add(Color::from(PURPLE)), ..default() }); commands.spawn(SpriteBundle { texture: asset_server.load("asd.png"), ..default() }); } fn toggle_tonemapping_method( keys: Res<ButtonInput<KeyCode>>, mut tonemapping: Query<&mut Tonemapping>, ) { let mut method = tonemapping.single_mut(); if keys.just_pressed(KeyCode::Digit1) { *method = Tonemapping::None; } else if keys.just_pressed(KeyCode::Digit2) { *method = Tonemapping::Reinhard; } else if keys.just_pressed(KeyCode::Digit3) { *method = Tonemapping::ReinhardLuminance; } else if keys.just_pressed(KeyCode::Digit4) { *method = Tonemapping::AcesFitted; } else if keys.just_pressed(KeyCode::Digit5) { *method = Tonemapping::AgX; } else if keys.just_pressed(KeyCode::Digit6) { *method = Tonemapping::SomewhatBoringDisplayTransform; } else if keys.just_pressed(KeyCode::Digit7) { *method = Tonemapping::TonyMcMapface; } else if keys.just_pressed(KeyCode::Digit8) { *method = Tonemapping::BlenderFilmic; } } ``` --- ## Changelog Fix the bug which led to the crash when user uses any tonemapper without hdr for rendering sprites and 2d meshes.
This commit is contained in:
		
							parent
							
								
									f45eddfe82
								
							
						
					
					
						commit
						cdc605cc48
					
				@ -0,0 +1,5 @@
 | 
			
		||||
#define_import_path bevy_core_pipeline::tonemapping_lut_bindings
 | 
			
		||||
 | 
			
		||||
@group(0) @binding(#TONEMAPPING_LUT_TEXTURE_BINDING_INDEX) var dt_lut_texture: texture_3d<f32>;
 | 
			
		||||
@group(0) @binding(#TONEMAPPING_LUT_SAMPLER_BINDING_INDEX) var dt_lut_sampler: sampler;
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,9 @@ const TONEMAPPING_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(1701536
 | 
			
		||||
const TONEMAPPING_SHARED_SHADER_HANDLE: Handle<Shader> =
 | 
			
		||||
    Handle::weak_from_u128(2499430578245347910);
 | 
			
		||||
 | 
			
		||||
const TONEMAPPING_LUT_BINDINGS_SHADER_HANDLE: Handle<Shader> =
 | 
			
		||||
    Handle::weak_from_u128(8392056472189465073);
 | 
			
		||||
 | 
			
		||||
/// 3D LUT (look up table) textures used for tonemapping
 | 
			
		||||
#[derive(Resource, Clone, ExtractResource)]
 | 
			
		||||
pub struct TonemappingLuts {
 | 
			
		||||
@ -52,6 +55,12 @@ impl Plugin for TonemappingPlugin {
 | 
			
		||||
            "tonemapping_shared.wgsl",
 | 
			
		||||
            Shader::from_wgsl
 | 
			
		||||
        );
 | 
			
		||||
        load_internal_asset!(
 | 
			
		||||
            app,
 | 
			
		||||
            TONEMAPPING_LUT_BINDINGS_SHADER_HANDLE,
 | 
			
		||||
            "lut_bindings.wgsl",
 | 
			
		||||
            Shader::from_wgsl
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if !app.world().is_resource_added::<TonemappingLuts>() {
 | 
			
		||||
            let mut images = app.world_mut().resource_mut::<Assets<Image>>();
 | 
			
		||||
@ -208,6 +217,16 @@ impl SpecializedRenderPipeline for TonemappingPipeline {
 | 
			
		||||
 | 
			
		||||
    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
 | 
			
		||||
        let mut shader_defs = Vec::new();
 | 
			
		||||
 | 
			
		||||
        shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
            "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
 | 
			
		||||
            3,
 | 
			
		||||
        ));
 | 
			
		||||
        shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
            "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
 | 
			
		||||
            4,
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        if let DebandDither::Enabled = key.deband_dither {
 | 
			
		||||
            shader_defs.push("DEBAND_DITHER".into());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,12 @@
 | 
			
		||||
#define TONEMAPPING_PASS
 | 
			
		||||
 | 
			
		||||
#import bevy_render::view::View
 | 
			
		||||
#import bevy_render::{
 | 
			
		||||
    view::View,
 | 
			
		||||
    maths::powsafe,
 | 
			
		||||
}
 | 
			
		||||
#import bevy_core_pipeline::{
 | 
			
		||||
    fullscreen_vertex_shader::FullscreenVertexOutput,
 | 
			
		||||
    tonemapping::{tone_mapping, powsafe, screen_space_dither},
 | 
			
		||||
    tonemapping::{tone_mapping, screen_space_dither},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@group(0) @binding(0) var<uniform> view: View;
 | 
			
		||||
 | 
			
		||||
@ -3,17 +3,13 @@
 | 
			
		||||
#import bevy_render::{
 | 
			
		||||
    view::ColorGrading,
 | 
			
		||||
    color_operations::{hsv_to_rgb, rgb_to_hsv},
 | 
			
		||||
    maths::PI_2
 | 
			
		||||
    maths::{PI_2, powsafe},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hack !! not sure what to do with this
 | 
			
		||||
#ifdef TONEMAPPING_PASS
 | 
			
		||||
    @group(0) @binding(3) var dt_lut_texture: texture_3d<f32>;
 | 
			
		||||
    @group(0) @binding(4) var dt_lut_sampler: sampler;
 | 
			
		||||
#else
 | 
			
		||||
    @group(0) @binding(20) var dt_lut_texture: texture_3d<f32>;
 | 
			
		||||
    @group(0) @binding(21) var dt_lut_sampler: sampler;
 | 
			
		||||
#endif
 | 
			
		||||
#import bevy_core_pipeline::tonemapping_lut_bindings::{
 | 
			
		||||
    dt_lut_texture,
 | 
			
		||||
    dt_lut_sampler,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Half the size of the crossfade region between shadows and midtones and
 | 
			
		||||
// between midtones and highlights. This value, 0.1, corresponds to 10% of the
 | 
			
		||||
@ -162,11 +158,6 @@ fn ACESFitted(color: vec3<f32>) -> vec3<f32> {
 | 
			
		||||
// https://github.com/MrLixm/AgXc
 | 
			
		||||
// https://github.com/sobotka/AgX
 | 
			
		||||
 | 
			
		||||
// pow() but safe for NaNs/negatives
 | 
			
		||||
fn powsafe(color: vec3<f32>, power: f32) -> vec3<f32> {
 | 
			
		||||
    return pow(abs(color), vec3(power)) * sign(color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    Increase color saturation of the given color data.
 | 
			
		||||
    :param color: expected sRGB primaries input
 | 
			
		||||
 | 
			
		||||
@ -254,6 +254,14 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
 | 
			
		||||
 | 
			
		||||
        if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
 | 
			
		||||
            shader_defs.push("TONEMAP_IN_SHADER".into());
 | 
			
		||||
            shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
                "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
 | 
			
		||||
                20,
 | 
			
		||||
            ));
 | 
			
		||||
            shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
                "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
 | 
			
		||||
                21,
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
            let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1667,6 +1667,14 @@ impl SpecializedMeshPipeline for MeshPipeline {
 | 
			
		||||
 | 
			
		||||
        if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
 | 
			
		||||
            shader_defs.push("TONEMAP_IN_SHADER".into());
 | 
			
		||||
            shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
                "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
 | 
			
		||||
                20,
 | 
			
		||||
            ));
 | 
			
		||||
            shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
                "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
 | 
			
		||||
                21,
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
            let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,6 @@ const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: u32 = 64u;
 | 
			
		||||
@group(0) @binding(19) var irradiance_volume_sampler: sampler;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// NB: If you change these, make sure to update `tonemapping_shared.wgsl` too.
 | 
			
		||||
@group(0) @binding(20) var dt_lut_texture: texture_3d<f32>;
 | 
			
		||||
@group(0) @binding(21) var dt_lut_sampler: sampler;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
    irradiance_volume,
 | 
			
		||||
    mesh_types::{MESH_FLAGS_SHADOW_RECEIVER_BIT, MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT},
 | 
			
		||||
}
 | 
			
		||||
#import bevy_render::maths::E
 | 
			
		||||
#import bevy_render::maths::{E, powsafe}
 | 
			
		||||
 | 
			
		||||
#ifdef MESHLET_MESH_MATERIAL_PASS
 | 
			
		||||
#import bevy_pbr::meshlet_visibility_buffer_resolve::VertexOutput
 | 
			
		||||
@ -28,7 +28,10 @@
 | 
			
		||||
#import bevy_pbr::environment_map
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#import bevy_core_pipeline::tonemapping::{screen_space_dither, powsafe, tone_mapping}
 | 
			
		||||
#ifdef TONEMAP_IN_SHADER
 | 
			
		||||
#import bevy_core_pipeline::tonemapping::{tone_mapping, screen_space_dither}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Biasing info needed to sample from a texture when calling `sample_texture`.
 | 
			
		||||
// How this is done depends on whether we're rendering meshlets or regular
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,9 @@
 | 
			
		||||
 | 
			
		||||
#import bevy_render::maths::PI
 | 
			
		||||
 | 
			
		||||
#import bevy_core_pipeline::tonemapping::{
 | 
			
		||||
    approximate_inverse_tone_mapping
 | 
			
		||||
};
 | 
			
		||||
#ifdef TONEMAP_IN_SHADER
 | 
			
		||||
#import bevy_core_pipeline::tonemapping::approximate_inverse_tone_mapping
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
fn specular_transmissive_light(world_position: vec4<f32>, frag_coord: vec3<f32>, view_z: f32, N: vec3<f32>, V: vec3<f32>, F0: vec3<f32>, ior: f32, thickness: f32, perceptual_roughness: f32, specular_transmissive_color: vec3<f32>, transmitted_environment_light_specular: vec3<f32>) -> vec3<f32> {
 | 
			
		||||
    // Calculate the ratio between refaction indexes. Assume air/vacuum for the space outside the mesh
 | 
			
		||||
 | 
			
		||||
@ -88,3 +88,8 @@ fn sphere_intersects_plane_half_space(
 | 
			
		||||
) -> bool {
 | 
			
		||||
    return dot(plane, sphere_center) + sphere_radius > 0.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pow() but safe for NaNs/negatives
 | 
			
		||||
fn powsafe(color: vec3<f32>, power: f32) -> vec3<f32> {
 | 
			
		||||
    return pow(abs(color), vec3(power)) * sign(color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,8 @@ use bevy_render::{
 | 
			
		||||
pub struct SpritePlugin;
 | 
			
		||||
 | 
			
		||||
pub const SPRITE_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(2763343953151597127);
 | 
			
		||||
pub const SPRITE_VIEW_BINDINGS_SHADER_HANDLE: Handle<Shader> =
 | 
			
		||||
    Handle::weak_from_u128(8846920112458963210);
 | 
			
		||||
 | 
			
		||||
/// System set for sprite rendering.
 | 
			
		||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
 | 
			
		||||
@ -94,6 +96,12 @@ impl Plugin for SpritePlugin {
 | 
			
		||||
            "render/sprite.wgsl",
 | 
			
		||||
            Shader::from_wgsl
 | 
			
		||||
        );
 | 
			
		||||
        load_internal_asset!(
 | 
			
		||||
            app,
 | 
			
		||||
            SPRITE_VIEW_BINDINGS_SHADER_HANDLE,
 | 
			
		||||
            "render/sprite_view_bindings.wgsl",
 | 
			
		||||
            Shader::from_wgsl
 | 
			
		||||
        );
 | 
			
		||||
        app.init_asset::<TextureAtlasLayout>()
 | 
			
		||||
            .register_asset_reflect::<TextureAtlasLayout>()
 | 
			
		||||
            .register_type::<Sprite>()
 | 
			
		||||
@ -146,7 +154,8 @@ impl Plugin for SpritePlugin {
 | 
			
		||||
                        queue_sprites
 | 
			
		||||
                            .in_set(RenderSet::Queue)
 | 
			
		||||
                            .ambiguous_with(queue_material2d_meshes::<ColorMaterial>),
 | 
			
		||||
                        prepare_sprites.in_set(RenderSet::PrepareBindGroups),
 | 
			
		||||
                        prepare_sprite_image_bind_groups.in_set(RenderSet::PrepareBindGroups),
 | 
			
		||||
                        prepare_sprite_view_bind_groups.in_set(RenderSet::PrepareBindGroups),
 | 
			
		||||
                    ),
 | 
			
		||||
                );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,9 @@ use bevy_app::Plugin;
 | 
			
		||||
use bevy_asset::{load_internal_asset, AssetId, Handle};
 | 
			
		||||
 | 
			
		||||
use bevy_core_pipeline::core_2d::Transparent2d;
 | 
			
		||||
use bevy_core_pipeline::tonemapping::{
 | 
			
		||||
    get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts,
 | 
			
		||||
};
 | 
			
		||||
use bevy_derive::{Deref, DerefMut};
 | 
			
		||||
use bevy_ecs::entity::EntityHashMap;
 | 
			
		||||
use bevy_ecs::{
 | 
			
		||||
@ -16,6 +19,7 @@ use bevy_render::batching::no_gpu_preprocessing::{
 | 
			
		||||
    BatchedInstanceBuffer,
 | 
			
		||||
};
 | 
			
		||||
use bevy_render::mesh::{GpuMesh, MeshVertexBufferLayoutRef};
 | 
			
		||||
use bevy_render::texture::FallbackImage;
 | 
			
		||||
use bevy_render::{
 | 
			
		||||
    batching::{GetBatchData, NoAutomaticBatching},
 | 
			
		||||
    globals::{GlobalsBuffer, GlobalsUniform},
 | 
			
		||||
@ -263,14 +267,22 @@ impl FromWorld for Mesh2dPipeline {
 | 
			
		||||
        )> = SystemState::new(world);
 | 
			
		||||
        let (render_device, render_queue, default_sampler) = system_state.get_mut(world);
 | 
			
		||||
        let render_device = render_device.into_inner();
 | 
			
		||||
        let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
 | 
			
		||||
        let view_layout = render_device.create_bind_group_layout(
 | 
			
		||||
            "mesh2d_view_layout",
 | 
			
		||||
            &BindGroupLayoutEntries::sequential(
 | 
			
		||||
            &BindGroupLayoutEntries::with_indices(
 | 
			
		||||
                ShaderStages::VERTEX_FRAGMENT,
 | 
			
		||||
                (
 | 
			
		||||
                    // View
 | 
			
		||||
                    uniform_buffer::<ViewUniform>(true),
 | 
			
		||||
                    uniform_buffer::<GlobalsUniform>(false),
 | 
			
		||||
                    (0, uniform_buffer::<ViewUniform>(true)),
 | 
			
		||||
                    (1, uniform_buffer::<GlobalsUniform>(false)),
 | 
			
		||||
                    (
 | 
			
		||||
                        2,
 | 
			
		||||
                        tonemapping_lut_entries[0].visibility(ShaderStages::FRAGMENT),
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        3,
 | 
			
		||||
                        tonemapping_lut_entries[1].visibility(ShaderStages::FRAGMENT),
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ),
 | 
			
		||||
        );
 | 
			
		||||
@ -475,6 +487,14 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
 | 
			
		||||
 | 
			
		||||
        if key.contains(Mesh2dPipelineKey::TONEMAP_IN_SHADER) {
 | 
			
		||||
            shader_defs.push("TONEMAP_IN_SHADER".into());
 | 
			
		||||
            shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
                "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
 | 
			
		||||
                2,
 | 
			
		||||
            ));
 | 
			
		||||
            shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
                "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
 | 
			
		||||
                3,
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
            let method = key.intersection(Mesh2dPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
 | 
			
		||||
 | 
			
		||||
@ -584,29 +604,42 @@ pub struct Mesh2dViewBindGroup {
 | 
			
		||||
    pub value: BindGroup,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::too_many_arguments)]
 | 
			
		||||
pub fn prepare_mesh2d_view_bind_groups(
 | 
			
		||||
    mut commands: Commands,
 | 
			
		||||
    render_device: Res<RenderDevice>,
 | 
			
		||||
    mesh2d_pipeline: Res<Mesh2dPipeline>,
 | 
			
		||||
    view_uniforms: Res<ViewUniforms>,
 | 
			
		||||
    views: Query<Entity, With<ExtractedView>>,
 | 
			
		||||
    views: Query<(Entity, &Tonemapping), With<ExtractedView>>,
 | 
			
		||||
    globals_buffer: Res<GlobalsBuffer>,
 | 
			
		||||
    tonemapping_luts: Res<TonemappingLuts>,
 | 
			
		||||
    images: Res<RenderAssets<GpuImage>>,
 | 
			
		||||
    fallback_image: Res<FallbackImage>,
 | 
			
		||||
) {
 | 
			
		||||
    if let (Some(view_binding), Some(globals)) = (
 | 
			
		||||
    let (Some(view_binding), Some(globals)) = (
 | 
			
		||||
        view_uniforms.uniforms.binding(),
 | 
			
		||||
        globals_buffer.buffer.binding(),
 | 
			
		||||
    ) {
 | 
			
		||||
        for entity in &views {
 | 
			
		||||
            let view_bind_group = render_device.create_bind_group(
 | 
			
		||||
                "mesh2d_view_bind_group",
 | 
			
		||||
                &mesh2d_pipeline.view_layout,
 | 
			
		||||
                &BindGroupEntries::sequential((view_binding.clone(), globals.clone())),
 | 
			
		||||
            );
 | 
			
		||||
    ) else {
 | 
			
		||||
        return;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
            commands.entity(entity).insert(Mesh2dViewBindGroup {
 | 
			
		||||
                value: view_bind_group,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    for (entity, tonemapping) in &views {
 | 
			
		||||
        let lut_bindings =
 | 
			
		||||
            get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
 | 
			
		||||
        let view_bind_group = render_device.create_bind_group(
 | 
			
		||||
            "mesh2d_view_bind_group",
 | 
			
		||||
            &mesh2d_pipeline.view_layout,
 | 
			
		||||
            &BindGroupEntries::with_indices((
 | 
			
		||||
                (0, view_binding.clone()),
 | 
			
		||||
                (1, globals.clone()),
 | 
			
		||||
                (2, lut_bindings.0),
 | 
			
		||||
                (3, lut_bindings.1),
 | 
			
		||||
            )),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        commands.entity(entity).insert(Mesh2dViewBindGroup {
 | 
			
		||||
            value: view_bind_group,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,3 +6,6 @@
 | 
			
		||||
@group(0) @binding(0) var<uniform> view: View;
 | 
			
		||||
 | 
			
		||||
@group(0) @binding(1) var<uniform> globals: Globals;
 | 
			
		||||
 | 
			
		||||
@group(0) @binding(2) var dt_lut_texture: texture_3d<f32>;
 | 
			
		||||
@group(0) @binding(3) var dt_lut_sampler: sampler;
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,12 @@ use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
 | 
			
		||||
use bevy_color::LinearRgba;
 | 
			
		||||
use bevy_core_pipeline::{
 | 
			
		||||
    core_2d::Transparent2d,
 | 
			
		||||
    tonemapping::{DebandDither, Tonemapping},
 | 
			
		||||
    tonemapping::{
 | 
			
		||||
        get_lut_bind_group_layout_entries, get_lut_bindings, DebandDither, Tonemapping,
 | 
			
		||||
        TonemappingLuts,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
use bevy_ecs::entity::EntityHashMap;
 | 
			
		||||
use bevy_ecs::{entity::EntityHashMap, query::ROQueryItem};
 | 
			
		||||
use bevy_ecs::{
 | 
			
		||||
    prelude::*,
 | 
			
		||||
    system::{lifetimeless::*, SystemParamItem, SystemState},
 | 
			
		||||
@ -28,7 +31,8 @@ use bevy_render::{
 | 
			
		||||
    },
 | 
			
		||||
    renderer::{RenderDevice, RenderQueue},
 | 
			
		||||
    texture::{
 | 
			
		||||
        BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
 | 
			
		||||
        BevyDefault, DefaultImageSampler, FallbackImage, GpuImage, Image, ImageSampler,
 | 
			
		||||
        TextureFormatPixelInfo,
 | 
			
		||||
    },
 | 
			
		||||
    view::{
 | 
			
		||||
        ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms,
 | 
			
		||||
@ -57,11 +61,22 @@ impl FromWorld for SpritePipeline {
 | 
			
		||||
        )> = SystemState::new(world);
 | 
			
		||||
        let (render_device, default_sampler, render_queue) = system_state.get_mut(world);
 | 
			
		||||
 | 
			
		||||
        let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
 | 
			
		||||
        let view_layout = render_device.create_bind_group_layout(
 | 
			
		||||
            "sprite_view_layout",
 | 
			
		||||
            &BindGroupLayoutEntries::single(
 | 
			
		||||
            &BindGroupLayoutEntries::with_indices(
 | 
			
		||||
                ShaderStages::VERTEX_FRAGMENT,
 | 
			
		||||
                uniform_buffer::<ViewUniform>(true),
 | 
			
		||||
                (
 | 
			
		||||
                    (0, uniform_buffer::<ViewUniform>(true)),
 | 
			
		||||
                    (
 | 
			
		||||
                        1,
 | 
			
		||||
                        tonemapping_lut_entries[0].visibility(ShaderStages::FRAGMENT),
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        2,
 | 
			
		||||
                        tonemapping_lut_entries[1].visibility(ShaderStages::FRAGMENT),
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -174,6 +189,14 @@ impl SpecializedRenderPipeline for SpritePipeline {
 | 
			
		||||
        let mut shader_defs = Vec::new();
 | 
			
		||||
        if key.contains(SpritePipelineKey::TONEMAP_IN_SHADER) {
 | 
			
		||||
            shader_defs.push("TONEMAP_IN_SHADER".into());
 | 
			
		||||
            shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
                "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
 | 
			
		||||
                1,
 | 
			
		||||
            ));
 | 
			
		||||
            shader_defs.push(ShaderDefVal::UInt(
 | 
			
		||||
                "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
 | 
			
		||||
                2,
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
            let method = key.intersection(SpritePipelineKey::TONEMAP_METHOD_RESERVED_BITS);
 | 
			
		||||
 | 
			
		||||
@ -412,7 +435,6 @@ impl SpriteInstance {
 | 
			
		||||
 | 
			
		||||
#[derive(Resource)]
 | 
			
		||||
pub struct SpriteMeta {
 | 
			
		||||
    view_bind_group: Option<BindGroup>,
 | 
			
		||||
    sprite_index_buffer: RawBufferVec<u32>,
 | 
			
		||||
    sprite_instance_buffer: RawBufferVec<SpriteInstance>,
 | 
			
		||||
}
 | 
			
		||||
@ -420,13 +442,17 @@ pub struct SpriteMeta {
 | 
			
		||||
impl Default for SpriteMeta {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            view_bind_group: None,
 | 
			
		||||
            sprite_index_buffer: RawBufferVec::<u32>::new(BufferUsages::INDEX),
 | 
			
		||||
            sprite_instance_buffer: RawBufferVec::<SpriteInstance>::new(BufferUsages::VERTEX),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Component)]
 | 
			
		||||
pub struct SpriteViewBindGroup {
 | 
			
		||||
    pub value: BindGroup,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Component, PartialEq, Eq, Clone)]
 | 
			
		||||
pub struct SpriteBatch {
 | 
			
		||||
    image_handle_id: AssetId<Image>,
 | 
			
		||||
@ -528,13 +554,46 @@ pub fn queue_sprites(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::too_many_arguments)]
 | 
			
		||||
pub fn prepare_sprites(
 | 
			
		||||
pub fn prepare_sprite_view_bind_groups(
 | 
			
		||||
    mut commands: Commands,
 | 
			
		||||
    render_device: Res<RenderDevice>,
 | 
			
		||||
    sprite_pipeline: Res<SpritePipeline>,
 | 
			
		||||
    view_uniforms: Res<ViewUniforms>,
 | 
			
		||||
    views: Query<(Entity, &Tonemapping), With<ExtractedView>>,
 | 
			
		||||
    tonemapping_luts: Res<TonemappingLuts>,
 | 
			
		||||
    images: Res<RenderAssets<GpuImage>>,
 | 
			
		||||
    fallback_image: Res<FallbackImage>,
 | 
			
		||||
) {
 | 
			
		||||
    let Some(view_binding) = view_uniforms.uniforms.binding() else {
 | 
			
		||||
        return;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (entity, tonemapping) in &views {
 | 
			
		||||
        let lut_bindings =
 | 
			
		||||
            get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
 | 
			
		||||
        let view_bind_group = render_device.create_bind_group(
 | 
			
		||||
            "mesh2d_view_bind_group",
 | 
			
		||||
            &sprite_pipeline.view_layout,
 | 
			
		||||
            &BindGroupEntries::with_indices((
 | 
			
		||||
                (0, view_binding.clone()),
 | 
			
		||||
                (1, lut_bindings.0),
 | 
			
		||||
                (2, lut_bindings.1),
 | 
			
		||||
            )),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        commands.entity(entity).insert(SpriteViewBindGroup {
 | 
			
		||||
            value: view_bind_group,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::too_many_arguments)]
 | 
			
		||||
pub fn prepare_sprite_image_bind_groups(
 | 
			
		||||
    mut commands: Commands,
 | 
			
		||||
    mut previous_len: Local<usize>,
 | 
			
		||||
    render_device: Res<RenderDevice>,
 | 
			
		||||
    render_queue: Res<RenderQueue>,
 | 
			
		||||
    mut sprite_meta: ResMut<SpriteMeta>,
 | 
			
		||||
    view_uniforms: Res<ViewUniforms>,
 | 
			
		||||
    sprite_pipeline: Res<SpritePipeline>,
 | 
			
		||||
    mut image_bind_groups: ResMut<ImageBindGroups>,
 | 
			
		||||
    gpu_images: Res<RenderAssets<GpuImage>>,
 | 
			
		||||
@ -555,163 +614,155 @@ pub fn prepare_sprites(
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if let Some(view_binding) = view_uniforms.uniforms.binding() {
 | 
			
		||||
        let mut batches: Vec<(Entity, SpriteBatch)> = Vec::with_capacity(*previous_len);
 | 
			
		||||
    let mut batches: Vec<(Entity, SpriteBatch)> = Vec::with_capacity(*previous_len);
 | 
			
		||||
 | 
			
		||||
        // Clear the sprite instances
 | 
			
		||||
        sprite_meta.sprite_instance_buffer.clear();
 | 
			
		||||
    // Clear the sprite instances
 | 
			
		||||
    sprite_meta.sprite_instance_buffer.clear();
 | 
			
		||||
 | 
			
		||||
        sprite_meta.view_bind_group = Some(render_device.create_bind_group(
 | 
			
		||||
            "sprite_view_bind_group",
 | 
			
		||||
            &sprite_pipeline.view_layout,
 | 
			
		||||
            &BindGroupEntries::single(view_binding),
 | 
			
		||||
        ));
 | 
			
		||||
    // Index buffer indices
 | 
			
		||||
    let mut index = 0;
 | 
			
		||||
 | 
			
		||||
        // Index buffer indices
 | 
			
		||||
        let mut index = 0;
 | 
			
		||||
    let image_bind_groups = &mut *image_bind_groups;
 | 
			
		||||
 | 
			
		||||
        let image_bind_groups = &mut *image_bind_groups;
 | 
			
		||||
    for transparent_phase in phases.values_mut() {
 | 
			
		||||
        let mut batch_item_index = 0;
 | 
			
		||||
        let mut batch_image_size = Vec2::ZERO;
 | 
			
		||||
        let mut batch_image_handle = AssetId::invalid();
 | 
			
		||||
 | 
			
		||||
        for transparent_phase in phases.values_mut() {
 | 
			
		||||
            let mut batch_item_index = 0;
 | 
			
		||||
            let mut batch_image_size = Vec2::ZERO;
 | 
			
		||||
            let mut batch_image_handle = AssetId::invalid();
 | 
			
		||||
        // Iterate through the phase items and detect when successive sprites that can be batched.
 | 
			
		||||
        // Spawn an entity with a `SpriteBatch` component for each possible batch.
 | 
			
		||||
        // Compatible items share the same entity.
 | 
			
		||||
        for item_index in 0..transparent_phase.items.len() {
 | 
			
		||||
            let item = &transparent_phase.items[item_index];
 | 
			
		||||
            let Some(extracted_sprite) = extracted_sprites.sprites.get(&item.entity) else {
 | 
			
		||||
                // If there is a phase item that is not a sprite, then we must start a new
 | 
			
		||||
                // batch to draw the other phase item(s) and to respect draw order. This can be
 | 
			
		||||
                // done by invalidating the batch_image_handle
 | 
			
		||||
                batch_image_handle = AssetId::invalid();
 | 
			
		||||
                continue;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Iterate through the phase items and detect when successive sprites that can be batched.
 | 
			
		||||
            // Spawn an entity with a `SpriteBatch` component for each possible batch.
 | 
			
		||||
            // Compatible items share the same entity.
 | 
			
		||||
            for item_index in 0..transparent_phase.items.len() {
 | 
			
		||||
                let item = &transparent_phase.items[item_index];
 | 
			
		||||
                let Some(extracted_sprite) = extracted_sprites.sprites.get(&item.entity) else {
 | 
			
		||||
                    // If there is a phase item that is not a sprite, then we must start a new
 | 
			
		||||
                    // batch to draw the other phase item(s) and to respect draw order. This can be
 | 
			
		||||
                    // done by invalidating the batch_image_handle
 | 
			
		||||
                    batch_image_handle = AssetId::invalid();
 | 
			
		||||
            let batch_image_changed = batch_image_handle != extracted_sprite.image_handle_id;
 | 
			
		||||
            if batch_image_changed {
 | 
			
		||||
                let Some(gpu_image) = gpu_images.get(extracted_sprite.image_handle_id) else {
 | 
			
		||||
                    continue;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let batch_image_changed = batch_image_handle != extracted_sprite.image_handle_id;
 | 
			
		||||
                if batch_image_changed {
 | 
			
		||||
                    let Some(gpu_image) = gpu_images.get(extracted_sprite.image_handle_id) else {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    batch_image_size = gpu_image.size.as_vec2();
 | 
			
		||||
                    batch_image_handle = extracted_sprite.image_handle_id;
 | 
			
		||||
                    image_bind_groups
 | 
			
		||||
                        .values
 | 
			
		||||
                        .entry(batch_image_handle)
 | 
			
		||||
                        .or_insert_with(|| {
 | 
			
		||||
                            render_device.create_bind_group(
 | 
			
		||||
                                "sprite_material_bind_group",
 | 
			
		||||
                                &sprite_pipeline.material_layout,
 | 
			
		||||
                                &BindGroupEntries::sequential((
 | 
			
		||||
                                    &gpu_image.texture_view,
 | 
			
		||||
                                    &gpu_image.sampler,
 | 
			
		||||
                                )),
 | 
			
		||||
                            )
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // By default, the size of the quad is the size of the texture
 | 
			
		||||
                let mut quad_size = batch_image_size;
 | 
			
		||||
 | 
			
		||||
                // Calculate vertex data for this item
 | 
			
		||||
                let mut uv_offset_scale: Vec4;
 | 
			
		||||
 | 
			
		||||
                // If a rect is specified, adjust UVs and the size of the quad
 | 
			
		||||
                if let Some(rect) = extracted_sprite.rect {
 | 
			
		||||
                    let rect_size = rect.size();
 | 
			
		||||
                    uv_offset_scale = Vec4::new(
 | 
			
		||||
                        rect.min.x / batch_image_size.x,
 | 
			
		||||
                        rect.max.y / batch_image_size.y,
 | 
			
		||||
                        rect_size.x / batch_image_size.x,
 | 
			
		||||
                        -rect_size.y / batch_image_size.y,
 | 
			
		||||
                    );
 | 
			
		||||
                    quad_size = rect_size;
 | 
			
		||||
                } else {
 | 
			
		||||
                    uv_offset_scale = Vec4::new(0.0, 1.0, 1.0, -1.0);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if extracted_sprite.flip_x {
 | 
			
		||||
                    uv_offset_scale.x += uv_offset_scale.z;
 | 
			
		||||
                    uv_offset_scale.z *= -1.0;
 | 
			
		||||
                }
 | 
			
		||||
                if extracted_sprite.flip_y {
 | 
			
		||||
                    uv_offset_scale.y += uv_offset_scale.w;
 | 
			
		||||
                    uv_offset_scale.w *= -1.0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Override the size if a custom one is specified
 | 
			
		||||
                if let Some(custom_size) = extracted_sprite.custom_size {
 | 
			
		||||
                    quad_size = custom_size;
 | 
			
		||||
                }
 | 
			
		||||
                let transform = extracted_sprite.transform.affine()
 | 
			
		||||
                    * Affine3A::from_scale_rotation_translation(
 | 
			
		||||
                        quad_size.extend(1.0),
 | 
			
		||||
                        Quat::IDENTITY,
 | 
			
		||||
                        (quad_size * (-extracted_sprite.anchor - Vec2::splat(0.5))).extend(0.0),
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                // Store the vertex data and add the item to the render phase
 | 
			
		||||
                sprite_meta
 | 
			
		||||
                    .sprite_instance_buffer
 | 
			
		||||
                    .push(SpriteInstance::from(
 | 
			
		||||
                        &transform,
 | 
			
		||||
                        &extracted_sprite.color,
 | 
			
		||||
                        &uv_offset_scale,
 | 
			
		||||
                    ));
 | 
			
		||||
 | 
			
		||||
                if batch_image_changed {
 | 
			
		||||
                    batch_item_index = item_index;
 | 
			
		||||
 | 
			
		||||
                    batches.push((
 | 
			
		||||
                        item.entity,
 | 
			
		||||
                        SpriteBatch {
 | 
			
		||||
                            image_handle_id: batch_image_handle,
 | 
			
		||||
                            range: index..index,
 | 
			
		||||
                        },
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                transparent_phase.items[batch_item_index]
 | 
			
		||||
                    .batch_range_mut()
 | 
			
		||||
                    .end += 1;
 | 
			
		||||
                batches.last_mut().unwrap().1.range.end += 1;
 | 
			
		||||
                index += 1;
 | 
			
		||||
                batch_image_size = gpu_image.size.as_vec2();
 | 
			
		||||
                batch_image_handle = extracted_sprite.image_handle_id;
 | 
			
		||||
                image_bind_groups
 | 
			
		||||
                    .values
 | 
			
		||||
                    .entry(batch_image_handle)
 | 
			
		||||
                    .or_insert_with(|| {
 | 
			
		||||
                        render_device.create_bind_group(
 | 
			
		||||
                            "sprite_material_bind_group",
 | 
			
		||||
                            &sprite_pipeline.material_layout,
 | 
			
		||||
                            &BindGroupEntries::sequential((
 | 
			
		||||
                                &gpu_image.texture_view,
 | 
			
		||||
                                &gpu_image.sampler,
 | 
			
		||||
                            )),
 | 
			
		||||
                        )
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sprite_meta
 | 
			
		||||
            .sprite_instance_buffer
 | 
			
		||||
            .write_buffer(&render_device, &render_queue);
 | 
			
		||||
 | 
			
		||||
        if sprite_meta.sprite_index_buffer.len() != 6 {
 | 
			
		||||
            sprite_meta.sprite_index_buffer.clear();
 | 
			
		||||
            // By default, the size of the quad is the size of the texture
 | 
			
		||||
            let mut quad_size = batch_image_size;
 | 
			
		||||
 | 
			
		||||
            // NOTE: This code is creating 6 indices pointing to 4 vertices.
 | 
			
		||||
            // The vertices form the corners of a quad based on their two least significant bits.
 | 
			
		||||
            // 10   11
 | 
			
		||||
            //
 | 
			
		||||
            // 00   01
 | 
			
		||||
            // The sprite shader can then use the two least significant bits as the vertex index.
 | 
			
		||||
            // The rest of the properties to transform the vertex positions and UVs (which are
 | 
			
		||||
            // implicit) are baked into the instance transform, and UV offset and scale.
 | 
			
		||||
            // See bevy_sprite/src/render/sprite.wgsl for the details.
 | 
			
		||||
            sprite_meta.sprite_index_buffer.push(2);
 | 
			
		||||
            sprite_meta.sprite_index_buffer.push(0);
 | 
			
		||||
            sprite_meta.sprite_index_buffer.push(1);
 | 
			
		||||
            sprite_meta.sprite_index_buffer.push(1);
 | 
			
		||||
            sprite_meta.sprite_index_buffer.push(3);
 | 
			
		||||
            sprite_meta.sprite_index_buffer.push(2);
 | 
			
		||||
            // Calculate vertex data for this item
 | 
			
		||||
            let mut uv_offset_scale: Vec4;
 | 
			
		||||
 | 
			
		||||
            // If a rect is specified, adjust UVs and the size of the quad
 | 
			
		||||
            if let Some(rect) = extracted_sprite.rect {
 | 
			
		||||
                let rect_size = rect.size();
 | 
			
		||||
                uv_offset_scale = Vec4::new(
 | 
			
		||||
                    rect.min.x / batch_image_size.x,
 | 
			
		||||
                    rect.max.y / batch_image_size.y,
 | 
			
		||||
                    rect_size.x / batch_image_size.x,
 | 
			
		||||
                    -rect_size.y / batch_image_size.y,
 | 
			
		||||
                );
 | 
			
		||||
                quad_size = rect_size;
 | 
			
		||||
            } else {
 | 
			
		||||
                uv_offset_scale = Vec4::new(0.0, 1.0, 1.0, -1.0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if extracted_sprite.flip_x {
 | 
			
		||||
                uv_offset_scale.x += uv_offset_scale.z;
 | 
			
		||||
                uv_offset_scale.z *= -1.0;
 | 
			
		||||
            }
 | 
			
		||||
            if extracted_sprite.flip_y {
 | 
			
		||||
                uv_offset_scale.y += uv_offset_scale.w;
 | 
			
		||||
                uv_offset_scale.w *= -1.0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Override the size if a custom one is specified
 | 
			
		||||
            if let Some(custom_size) = extracted_sprite.custom_size {
 | 
			
		||||
                quad_size = custom_size;
 | 
			
		||||
            }
 | 
			
		||||
            let transform = extracted_sprite.transform.affine()
 | 
			
		||||
                * Affine3A::from_scale_rotation_translation(
 | 
			
		||||
                    quad_size.extend(1.0),
 | 
			
		||||
                    Quat::IDENTITY,
 | 
			
		||||
                    (quad_size * (-extracted_sprite.anchor - Vec2::splat(0.5))).extend(0.0),
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
            // Store the vertex data and add the item to the render phase
 | 
			
		||||
            sprite_meta
 | 
			
		||||
                .sprite_index_buffer
 | 
			
		||||
                .write_buffer(&render_device, &render_queue);
 | 
			
		||||
        }
 | 
			
		||||
                .sprite_instance_buffer
 | 
			
		||||
                .push(SpriteInstance::from(
 | 
			
		||||
                    &transform,
 | 
			
		||||
                    &extracted_sprite.color,
 | 
			
		||||
                    &uv_offset_scale,
 | 
			
		||||
                ));
 | 
			
		||||
 | 
			
		||||
        *previous_len = batches.len();
 | 
			
		||||
        commands.insert_or_spawn_batch(batches);
 | 
			
		||||
            if batch_image_changed {
 | 
			
		||||
                batch_item_index = item_index;
 | 
			
		||||
 | 
			
		||||
                batches.push((
 | 
			
		||||
                    item.entity,
 | 
			
		||||
                    SpriteBatch {
 | 
			
		||||
                        image_handle_id: batch_image_handle,
 | 
			
		||||
                        range: index..index,
 | 
			
		||||
                    },
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            transparent_phase.items[batch_item_index]
 | 
			
		||||
                .batch_range_mut()
 | 
			
		||||
                .end += 1;
 | 
			
		||||
            batches.last_mut().unwrap().1.range.end += 1;
 | 
			
		||||
            index += 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    sprite_meta
 | 
			
		||||
        .sprite_instance_buffer
 | 
			
		||||
        .write_buffer(&render_device, &render_queue);
 | 
			
		||||
 | 
			
		||||
    if sprite_meta.sprite_index_buffer.len() != 6 {
 | 
			
		||||
        sprite_meta.sprite_index_buffer.clear();
 | 
			
		||||
 | 
			
		||||
        // NOTE: This code is creating 6 indices pointing to 4 vertices.
 | 
			
		||||
        // The vertices form the corners of a quad based on their two least significant bits.
 | 
			
		||||
        // 10   11
 | 
			
		||||
        //
 | 
			
		||||
        // 00   01
 | 
			
		||||
        // The sprite shader can then use the two least significant bits as the vertex index.
 | 
			
		||||
        // The rest of the properties to transform the vertex positions and UVs (which are
 | 
			
		||||
        // implicit) are baked into the instance transform, and UV offset and scale.
 | 
			
		||||
        // See bevy_sprite/src/render/sprite.wgsl for the details.
 | 
			
		||||
        sprite_meta.sprite_index_buffer.push(2);
 | 
			
		||||
        sprite_meta.sprite_index_buffer.push(0);
 | 
			
		||||
        sprite_meta.sprite_index_buffer.push(1);
 | 
			
		||||
        sprite_meta.sprite_index_buffer.push(1);
 | 
			
		||||
        sprite_meta.sprite_index_buffer.push(3);
 | 
			
		||||
        sprite_meta.sprite_index_buffer.push(2);
 | 
			
		||||
 | 
			
		||||
        sprite_meta
 | 
			
		||||
            .sprite_index_buffer
 | 
			
		||||
            .write_buffer(&render_device, &render_queue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *previous_len = batches.len();
 | 
			
		||||
    commands.insert_or_spawn_batch(batches);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// [`RenderCommand`] for sprite rendering.
 | 
			
		||||
@ -724,22 +775,18 @@ pub type DrawSprite = (
 | 
			
		||||
 | 
			
		||||
pub struct SetSpriteViewBindGroup<const I: usize>;
 | 
			
		||||
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteViewBindGroup<I> {
 | 
			
		||||
    type Param = SRes<SpriteMeta>;
 | 
			
		||||
    type ViewQuery = Read<ViewUniformOffset>;
 | 
			
		||||
    type Param = ();
 | 
			
		||||
    type ViewQuery = (Read<ViewUniformOffset>, Read<SpriteViewBindGroup>);
 | 
			
		||||
    type ItemQuery = ();
 | 
			
		||||
 | 
			
		||||
    fn render<'w>(
 | 
			
		||||
        _item: &P,
 | 
			
		||||
        view_uniform: &'_ ViewUniformOffset,
 | 
			
		||||
        (view_uniform, sprite_view_bind_group): ROQueryItem<'w, Self::ViewQuery>,
 | 
			
		||||
        _entity: Option<()>,
 | 
			
		||||
        sprite_meta: SystemParamItem<'w, '_, Self::Param>,
 | 
			
		||||
        _param: SystemParamItem<'w, '_, Self::Param>,
 | 
			
		||||
        pass: &mut TrackedRenderPass<'w>,
 | 
			
		||||
    ) -> RenderCommandResult {
 | 
			
		||||
        pass.set_bind_group(
 | 
			
		||||
            I,
 | 
			
		||||
            sprite_meta.into_inner().view_bind_group.as_ref().unwrap(),
 | 
			
		||||
            &[view_uniform.offset],
 | 
			
		||||
        );
 | 
			
		||||
        pass.set_bind_group(I, &sprite_view_bind_group.value, &[view_uniform.offset]);
 | 
			
		||||
        RenderCommandResult::Success
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
    view::View,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@group(0) @binding(0) var<uniform> view: View;
 | 
			
		||||
#import bevy_sprite::sprite_view_bindings::view
 | 
			
		||||
 | 
			
		||||
struct VertexInput {
 | 
			
		||||
    @builtin(vertex_index) index: u32,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								crates/bevy_sprite/src/render/sprite_view_bindings.wgsl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								crates/bevy_sprite/src/render/sprite_view_bindings.wgsl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
#define_import_path bevy_sprite::sprite_view_bindings
 | 
			
		||||
 | 
			
		||||
#import bevy_render::view::View
 | 
			
		||||
 | 
			
		||||
@group(0) @binding(0) var<uniform> view: View;
 | 
			
		||||
 | 
			
		||||
@group(0) @binding(1) var dt_lut_texture: texture_3d<f32>;
 | 
			
		||||
@group(0) @binding(2) var dt_lut_sampler: sampler;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user