bevyengine / bevyengine/bevy

Calling get_mut on an image being used as a render target causes it to not get drawn on that frame

Open
#20,445 1 comment 1 reaction 0 assignees View on GitHub
A-Rendering C-Bug S-Needs-Investigation
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

## Bevy version

0.17.2 - been a problem since 1.15 at least

## Relevant system information

```ignore
`AdapterInfo { name: "Intel(R) Arc(tm) Graphics (MTL)", device_type: IntegratedGpu, driver: "Intel open-source Mesa driver", backend: Vulkan }`
```

## What you did

I started from the render to texture example: https://bevy.org/examples/3d-rendering/render-to-texture/
I replaced the 3D main pass with a 2D sprite and camera. When i call get_mut on the image of the sprite (i want to resize the image), the image doesn't get rendered on that frame, normal non-mut get does work.

I tried to schedule the system that calls get_mut as First, PreUpdate, Update and Last, but the problem persists.

## What went wrong

- what were you expecting?
- Essentially the same behavior as resizing the main window at runtime
- The image does not flash back
- The image gets rendered, even though the image might have been changed or resized, one frame of delay for the camera to render to the image again is acceptable, but the image shouldn't dissapear but just stay the same for that frame.
- what actually happened?
- The image doesn't get drawn for the frame get_mut is called on or turns transparent
## Additional information

I have written a minimal reproduction sample:
The bug gets triggered every second for 1 frame or constantly when the left mouse button is pressed.

```rust
use std::time::Duration;

use bevy::input::common_conditions::*;
use bevy::time::common_conditions::*;

use bevy::{
prelude::*,
render::{
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages},
view::RenderLayers,
},
};

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, (rotator_system))
.add_systems(Update, (
handle_click
.run_if(on_timer(Duration::from_secs(1)).or(input_pressed(MouseButton::Left))),
))
.run();
}

// Marks the first pass cube (rendered to a texture.)
#[derive(Component)]
struct FirstPassCube;

// Marks the main pass cube, to which the texture is applied.
#[derive(Component)]
struct MainPassCube;

fn setup(
mut commands: Commands,
mut meshes: ResMut>,
mut materials: ResMut>,
mut images: ResMut>,
) {
let size = Extent3d {
width: 512,
height: 512,
..default()
};

// This is the texture that will be rendered to.
let mut image = Image::new_fill(
size,
TextureDimension::D2,
&[0, 0, 0, 0],
TextureFormat::Bgra8UnormSrgb,
RenderAssetUsages::default(),
);
// You need to set these texture usage flags in order to use the image as a render target
image.texture_descriptor.usage =
TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT;

let image_handle = images.add(image);

let cube_handle = meshes.add(Cuboid::new(4.0, 4.0, 4.0));
let cube_material_handle = materials.add(StandardMaterial {
base_color: Color::srgb(0.8, 0.7, 0.6),
reflectance: 0.02,
unlit: false,
..default()
});

// This specifies the layer used for the first pass, which will be attached to the first pass camera and cube.
let first_pass_layer = RenderLayers::layer(1);

// The cube that will be rendered to the texture.
commands.spawn((
Mesh3d(cube_handle),
MeshMaterial3d(cube_material_handle),
Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)),
FirstPassCube,
first_pass_layer.clone(),
));

// Light
// NOTE: we add the light to both layers so it affects both the rendered-to-texture cube, and the cube on which we display the texture
// Setting the layer to RenderLayers::layer(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit.
// Setting the layer to RenderLayers::layer(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit.
commands.spawn((
PointLight::default(),
Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)),
RenderLayers::layer(0).with(1),
));

commands.spawn((
Camera3d::default(),
Camera {
target: image_handle.clone().into(),
clear_color: Color::WHITE.into(),
..default()
},
Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)).looking_at(Vec3::ZERO, Vec3::Y),
first_pass_layer,
));

commands.spawn(Camera2d);

commands.spawn(Sprite::from_image(image_handle.clone()));

}

// call get_mut on the sprite's image every second or when left mouse button is pressed
fn handle_click(sprites: Query<&Sprite>, mut assets: ResMut>){
assets.get_mut(sprites.single().unwrap().image.id()).unwrap();
}

/// Rotates the inner cube (first pass)
fn rotator_system(time: Res

related: #17854

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.