-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - Introduce SystemLabel
's for RenderAssetPlugin
, and change Image
preparation system to run before others
#3917
Conversation
SystemLabel
's for RenderAssetPlugin
, and change Image
preparation system to run before others
Actually, there is still another (root?) problem left changing the Result: image is not being updated. |
Note that RenderAsset extraction only notes created/modified/removed assets and then immutably borrowing an asset marks none of those things. Then, I think this is because the mutation of the Image causes creation of a completely new GpuImage. But if the material is not mutably borrowed, then it will not be marked as modified and so that new GpuImage will not be used and updated in the material. Separately, I wonder if this setup is leaking textures on the GPU or if there's wonderful RAII cleanup stuff that happens when the old GpuImage (texture, texture view, sampler) is dropped. I would guess so but I'm not sure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this approach. @cart - what do you think?
} | ||
} | ||
|
||
impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> { | ||
fn build(&self, app: &mut App) { | ||
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { | ||
let prepare_asset_system = PrepareAssetSystem::<A>::system(&mut render_app.world); | ||
let prepare_asset_system = PrepareAssetSystem::<A>::system(&mut render_app.world) | ||
.label(self.prepare_asset_label.clone()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any problem with using the same labels for many systems?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope this is fine / a pattern we use in a number of places.
Did some more debugging, and this seems to be cause. With images.get_mut(), the However, the The whole process of creating new GpuImage, TextureView, StandardMaterial etc. seems quite a bit of low-performance approach especially if the texture needs to be updated on each frame. Maybe some alternative approaches (mapping the GPU texture memory and updating directly? could be used), especially in the cases where the |
Yeah, it's not really set up for in-place modification, just uploading the necessary data. rafx has a shadow map atlas which then needs to update sub-rectangles of the full texture, possibly many of them each frame. I imagine we'd want to have some kind of API to allow users to do that - update some sub-rectangle of the texture and have that be used. I don't know if the creation of the new texture, texture view, etc take any significant amount of time, but updating more data than necessary in a texture could easily take too much time (and bandwidth.) |
That would be great, especially if it allowed updating a portion (rectangle) of the texture. Btw, also noticed that bevy/crates/bevy_ui/src/render/mod.rs Line 431 in 9a7852d
Another direction to investigate, could be to run |
I think adding labels to the asset's extract + prepare systems is a nice addition. One issue I see with this specific approach is that there's a fixed amount of "stages" if I understand correctly. This would mean that longer dependency chains can't be handled with this. Would it make sense to automatically assign every extract and prepare system a unique label and then allow specifying dependencies in the asset's |
Perhaps we could add a |
That sounds like a simple enough approach. In that case a default implementation could maybe fallback to a label based on the asset's uuid (not sure how the Though I don't know if this is the right direction. |
Sorry for the criminal levels of neglect here. Now that we have "auto system labeling", we can make the api a little more generic. Something like: pub struct RenderAssetPlugin<A: RenderAsset> {
labels: Vec<fn(ParallelSystemDescriptor) -> ParallelSystemDescriptor>,
marker: PhantomData<fn() -> A>,
}
impl<A: RenderAsset> Default for RenderAssetPlugin<A> {
fn default() -> Self {
Self {
labels: Vec::new(),
marker: PhantomData,
}
}
}
impl<A: RenderAsset> RenderAssetPlugin<A> {
pub fn prepare_after_asset<B: Asset>(mut self) -> Self {
self.prepare_after(prepare_assets::<B>)
}
pub fn prepare_after(mut self, label: impl SystemLabel) -> Self {
self.labels.push(|descriptor| descriptor.after(label));
self
}
}
impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
let mut prepare_system = ParallelSystemDescriptor::new(prepare_assets::<A>);
for labeler in self.labels.iter() {
prepare_system = (labeler)(prepare_system);
}
render_app
.init_resource::<ExtractedAssets<A>>()
.init_resource::<RenderAssets<A>>()
.init_resource::<PrepareNextFrameAssets<A>>()
.add_system_to_stage(RenderStage::Extract, extract_render_asset::<A>)
.add_system_to_stage(RenderStage::Prepare, prepare_system);
}
}
} However!!! While this is more flexible, for the case of "preparing images first", I don't think manually adding a dependency is desirable. People defining their own materials shouldn't need to worry about adding a dependency on Image prep. Images should just "default" to being first. Therefore, I think the impl in this PR is a reasonable way to do this generically. If we have other specific cases that need the more flexible approach, I'm down to add something like the code above. But otherwise, I think we should merge the current PR as-is. |
bors r+ |
… 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::<Image> 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<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>, mut images: ResMut<Assets<Image>>, ) { 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<Assets<StandardMaterial>>, mut images: ResMut<Assets<Image>>, mut iteration: ResMut<Iteration>, webview_query: Query<&Handle<StandardMaterial>, With<MyComponent>>, ) { 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 <mcanders1@gmail.com>
SystemLabel
's for RenderAssetPlugin
, and change Image
preparation system to run before othersSystemLabel
's for RenderAssetPlugin
, and change Image
preparation system to run before others
… preparation system to run before others (bevyengine#3917) # Objective Fixes `StandardMaterial` texture update (see sample code below). Most probably fixes bevyengine#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::<Image> 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<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>, mut images: ResMut<Assets<Image>>, ) { 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<Assets<StandardMaterial>>, mut images: ResMut<Assets<Image>>, mut iteration: ResMut<Iteration>, webview_query: Query<&Handle<StandardMaterial>, With<MyComponent>>, ) { 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 <mcanders1@gmail.com>
… preparation system to run before others (bevyengine#3917) # Objective Fixes `StandardMaterial` texture update (see sample code below). Most probably fixes bevyengine#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::<Image> 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<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>, mut images: ResMut<Assets<Image>>, ) { 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<Assets<StandardMaterial>>, mut images: ResMut<Assets<Image>>, mut iteration: ResMut<Iteration>, webview_query: Query<&Handle<StandardMaterial>, With<MyComponent>>, ) { 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 <mcanders1@gmail.com>
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:bevy/crates/bevy_pbr/src/pbr_material.rs
Line 177 in 9a7852d
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:
Unexpected behavior (before this merge):