Sprite change image (#3207)

# Objective

- Changing the underlying image would not update a sprite

## Solution

- 'Detect' if the underlying image changes to update the sprite

Currently, we don't support change detection on `RenderAssets`, so we have to manually check it. 
This method at least maintains the bind groups when the image isn't changing. They were cached, so I assume that's important.

This gives us correct behaviour here.

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Daniel McNab 2021-12-01 23:09:31 +00:00
parent 2def9027d7
commit 5fe73be2b5
3 changed files with 46 additions and 4 deletions

View file

@ -102,9 +102,8 @@ fn extract_render_asset<A: RenderAsset>(
changed_assets.insert(handle); changed_assets.insert(handle);
} }
AssetEvent::Removed { handle } => { AssetEvent::Removed { handle } => {
if !changed_assets.remove(handle) { changed_assets.remove(handle);
removed.push(handle.clone_weak()); removed.push(handle.clone_weak());
}
} }
} }
} }

View file

@ -43,7 +43,9 @@ impl Plugin for SpritePlugin {
.init_resource::<SpecializedPipelines<SpritePipeline>>() .init_resource::<SpecializedPipelines<SpritePipeline>>()
.init_resource::<SpriteMeta>() .init_resource::<SpriteMeta>()
.init_resource::<ExtractedSprites>() .init_resource::<ExtractedSprites>()
.init_resource::<SpriteAssetEvents>()
.add_system_to_stage(RenderStage::Extract, render::extract_sprites) .add_system_to_stage(RenderStage::Extract, render::extract_sprites)
.add_system_to_stage(RenderStage::Extract, render::extract_sprite_events)
.add_system_to_stage(RenderStage::Prepare, render::prepare_sprites) .add_system_to_stage(RenderStage::Prepare, render::prepare_sprites)
.add_system_to_stage(RenderStage::Queue, queue_sprites); .add_system_to_stage(RenderStage::Queue, queue_sprites);

View file

@ -4,7 +4,7 @@ use crate::{
texture_atlas::{TextureAtlas, TextureAtlasSprite}, texture_atlas::{TextureAtlas, TextureAtlasSprite},
Rect, Sprite, SPRITE_SHADER_HANDLE, Rect, Sprite, SPRITE_SHADER_HANDLE,
}; };
use bevy_asset::{Assets, Handle}; use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_core::FloatOrd; use bevy_core::FloatOrd;
use bevy_core_pipeline::Transparent2d; use bevy_core_pipeline::Transparent2d;
use bevy_ecs::{ use bevy_ecs::{
@ -172,6 +172,37 @@ pub struct ExtractedSprites {
sprites: Vec<ExtractedSprite>, sprites: Vec<ExtractedSprite>,
} }
#[derive(Default)]
pub struct SpriteAssetEvents {
images: Vec<AssetEvent<Image>>,
}
pub fn extract_sprite_events(
mut render_world: ResMut<RenderWorld>,
mut image_events: EventReader<AssetEvent<Image>>,
) {
let mut events = render_world
.get_resource_mut::<SpriteAssetEvents>()
.unwrap();
let SpriteAssetEvents { ref mut images } = *events;
images.clear();
for image in image_events.iter() {
// AssetEvent: !Clone
images.push(match image {
AssetEvent::Created { handle } => AssetEvent::Created {
handle: handle.clone_weak(),
},
AssetEvent::Modified { handle } => AssetEvent::Modified {
handle: handle.clone_weak(),
},
AssetEvent::Removed { handle } => AssetEvent::Removed {
handle: handle.clone_weak(),
},
});
}
}
pub fn extract_sprites( pub fn extract_sprites(
mut render_world: ResMut<RenderWorld>, mut render_world: ResMut<RenderWorld>,
images: Res<Assets<Image>>, images: Res<Assets<Image>>,
@ -453,7 +484,17 @@ pub fn queue_sprites(
gpu_images: Res<RenderAssets<Image>>, gpu_images: Res<RenderAssets<Image>>,
mut sprite_batches: Query<(Entity, &SpriteBatch)>, mut sprite_batches: Query<(Entity, &SpriteBatch)>,
mut views: Query<&mut RenderPhase<Transparent2d>>, mut views: Query<&mut RenderPhase<Transparent2d>>,
events: Res<SpriteAssetEvents>,
) { ) {
// If an image has changed, the GpuImage has (probably) changed
for event in &events.images {
match event {
AssetEvent::Created { .. } => None,
AssetEvent::Modified { handle } => image_bind_groups.values.remove(handle),
AssetEvent::Removed { handle } => image_bind_groups.values.remove(handle),
};
}
if let Some(view_binding) = view_uniforms.uniforms.binding() { if let Some(view_binding) = view_uniforms.uniforms.binding() {
sprite_meta.view_bind_group = Some(render_device.create_bind_group(&BindGroupDescriptor { sprite_meta.view_bind_group = Some(render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry { entries: &[BindGroupEntry {