From 6844a6f4fde56e76156b62eedf0c4630b198aecd Mon Sep 17 00:00:00 2001 From: Mika Date: Tue, 29 Mar 2022 19:44:45 +0000 Subject: [PATCH] Introduce `SystemLabel`'s for `RenderAssetPlugin`, and change `Image` preparation system to run before others (#3917) # Objective Fixes `StandardMaterial` texture update (see sample code below). Most probably fixes #3674 (did not test) ## Solution Material updates, such as PBR update, reference the underlying `GpuImage`. Like here: https://github.com/bevyengine/bevy/blob/9a7852db0f22eb41f259a1afbb4926eb73863a10/crates/bevy_pbr/src/pbr_material.rs#L177 However, currently the `GpuImage` update may actually happen *after* the material update fetches the gpu image. Resulting in the material actually not being updated for the correct gpu image. In this pull req, I introduce new systemlabels for the renderassetplugin. Also assigned the RenderAssetPlugin:: to the `PreAssetExtract` stage, so that it is executed before any material updates. Code to test. Expected behavior: * should update to red texture Unexpected behavior (before this merge): * texture stays randomly as green one (depending on the execution order of systems) ```rust use bevy::{ prelude::*, render::render_resource::{Extent3d, TextureDimension, TextureFormat}, }; fn main() { App::new() .add_plugins(DefaultPlugins) .add_startup_system(setup) .add_system(changes) .run(); } struct Iteration(usize); #[derive(Component)] struct MyComponent; fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, mut images: ResMut>, ) { commands.spawn_bundle(PointLightBundle { point_light: PointLight { ..Default::default() }, transform: Transform::from_xyz(4.0, 8.0, 4.0), ..Default::default() }); commands.spawn_bundle(PerspectiveCameraBundle { transform: Transform::from_xyz(-2.0, 0.0, 5.0) .looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y), ..Default::default() }); commands.insert_resource(Iteration(0)); commands .spawn_bundle(PbrBundle { mesh: meshes.add(Mesh::from(shape::Quad::new(Vec2::new(3., 2.)))), material: materials.add(StandardMaterial { base_color_texture: Some(images.add(Image::new( Extent3d { width: 600, height: 400, depth_or_array_layers: 1, }, TextureDimension::D2, [0, 255, 0, 128].repeat(600 * 400), // GREEN TextureFormat::Rgba8Unorm, ))), ..Default::default() }), ..Default::default() }) .insert(MyComponent); } fn changes( mut materials: ResMut>, mut images: ResMut>, mut iteration: ResMut, webview_query: Query<&Handle, With>, ) { if iteration.0 == 2 { let material = materials.get_mut(webview_query.single()).unwrap(); let image = images .get_mut(material.base_color_texture.as_ref().unwrap()) .unwrap(); image .data .copy_from_slice(&[255, 0, 0, 255].repeat(600 * 400)); } iteration.0 += 1; } ``` Co-authored-by: Carter Anderson --- crates/bevy_render/src/render_asset.rs | 46 ++++++++++++++++++++++++-- crates/bevy_render/src/texture/mod.rs | 11 ++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index d2fe8ed064..3ee311ffda 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -39,28 +39,68 @@ pub trait RenderAsset: Asset { ) -> Result>; } +#[derive(Clone, Hash, Debug, PartialEq, Eq, SystemLabel)] +pub enum PrepareAssetLabel { + PreAssetPrepare, + AssetPrepare, + PostAssetPrepare, +} + +impl Default for PrepareAssetLabel { + fn default() -> Self { + Self::AssetPrepare + } +} + /// This plugin extracts the changed assets from the "app world" into the "render world" /// and prepares them for the GPU. They can then be accessed from the [`RenderAssets`] resource. /// /// Therefore it sets up the [`RenderStage::Extract`](crate::RenderStage::Extract) and /// [`RenderStage::Prepare`](crate::RenderStage::Prepare) steps for the specified [`RenderAsset`]. -pub struct RenderAssetPlugin(PhantomData A>); +pub struct RenderAssetPlugin { + prepare_asset_label: PrepareAssetLabel, + phantom: PhantomData A>, +} + +impl RenderAssetPlugin { + pub fn with_prepare_asset_label(prepare_asset_label: PrepareAssetLabel) -> Self { + Self { + prepare_asset_label, + phantom: PhantomData, + } + } +} impl Default for RenderAssetPlugin { fn default() -> Self { - Self(PhantomData) + Self { + prepare_asset_label: Default::default(), + phantom: PhantomData, + } } } impl Plugin for RenderAssetPlugin { fn build(&self, app: &mut App) { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + let prepare_asset_system = prepare_assets::.label(self.prepare_asset_label.clone()); + + let prepare_asset_system = match self.prepare_asset_label { + PrepareAssetLabel::PreAssetPrepare => prepare_asset_system, + PrepareAssetLabel::AssetPrepare => { + prepare_asset_system.after(PrepareAssetLabel::PreAssetPrepare) + } + PrepareAssetLabel::PostAssetPrepare => { + prepare_asset_system.after(PrepareAssetLabel::AssetPrepare) + } + }; + render_app .init_resource::>() .init_resource::>() .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_render_asset::) - .add_system_to_stage(RenderStage::Prepare, prepare_assets::); + .add_system_to_stage(RenderStage::Prepare, prepare_asset_system); } } } diff --git a/crates/bevy_render/src/texture/mod.rs b/crates/bevy_render/src/texture/mod.rs index 84bd662747..851eac1371 100644 --- a/crates/bevy_render/src/texture/mod.rs +++ b/crates/bevy_render/src/texture/mod.rs @@ -24,7 +24,10 @@ pub use hdr_texture_loader::*; pub use image_texture_loader::*; pub use texture_cache::*; -use crate::{render_asset::RenderAssetPlugin, RenderApp, RenderStage}; +use crate::{ + render_asset::{PrepareAssetLabel, RenderAssetPlugin}, + RenderApp, RenderStage, +}; use bevy_app::{App, Plugin}; use bevy_asset::{AddAsset, Assets}; @@ -52,8 +55,10 @@ impl Plugin for ImagePlugin { app.init_asset_loader::(); } - app.add_plugin(RenderAssetPlugin::::default()) - .add_asset::(); + app.add_plugin(RenderAssetPlugin::::with_prepare_asset_label( + PrepareAssetLabel::PreAssetPrepare, + )) + .add_asset::(); app.world .resource_mut::>() .set_untracked(DEFAULT_IMAGE_HANDLE, Image::default());