Skip to content

CPU drawing into 2D texture is not updated to the GPU when using any 2D materials #20269

@AlexGrek

Description

@AlexGrek

Bevy version

The release number or commit hash of the version you're using.
bevy = "0.16.1"

[Optional] Relevant system information

AdapterInfo { name: "Apple M3", vendor: 0, device: 0, device_type: IntegratedGpu, driver: "", driver_info: "", backend: Metal }

It should reproduce on any other GPU

What you did

Create a sprite using any 2D material with custom texture, like ColorMaterial or something custom

let image = Image::new_fill(
        // 2D image of size
        Extent3d {
            width: s as u32,
            height: s as u32,
            depth_or_array_layers: 1,
        },
        TextureDimension::D2,
        // Initialize it with a beige color
        &(color),
        // Use the same encoding as the color we set
        TextureFormat::Rgba8UnormSrgb,
        RenderAssetUsages::RENDER_WORLD | RenderAssetUsages::MAIN_WORLD,
    );
    let handle = images.add(image);

    let mat = ColorMaterial {
        texture: handle.clone(),
    };

    let material_handle = materials.add(mat);

    let mesh = meshes.add(Rectangle::new(
        s as f32,
        s as f32,
    ));

   commands.spawn((
        MeshMaterial2d(material_handle.clone()),
        Mesh2d(mesh),
        Transform::from_xyz(0.0, 0.0, 100000.0),
    ));
    commands.insert_resource(LightOverlayTextureHandle(handle)); // my custom handle res
    commands.insert_resource(LightOverlayMaterialHandle(material_handle));

Draw to that texture in a system

pub fn draw_random_color_pixel(
    light_texture_handle: Res<LightOverlayTextureHandle>,
    light_material_handle: Res<LightOverlayMaterialHandle>,
    mut images: ResMut<Assets<Image>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    // Get mutable reference to the image
    if let Some(image) = images.get_mut(&light_texture_handle.0) {
        let width = image.size().x;
        let height = image.size().y;

        // Generate random coordinates and color
        let mut rng = rand::rng();
        let x = rng.gen_range(0..width);
        let y = rng.gen_range(0..height);

        let color = Color::srgb(
            rng.gen_range(0.0..=1.0),
            rng.gen_range(0.0..=1.0),
            rng.gen_range(0.0..=1.0),
        );

        // Set the pixel
        image.set_color_at(x, y, color).unwrap();     
    }
    // placeholder for a hotfix
}

What went wrong

If using FixedUpdate sheduler - no draws happen on the screen, texture is not updated
If using Update scheduler - only first update is visible on the screen, then texture is not updated

Hot fix:

Add materials.get_mut(&light_material_handle.0) to the end of your system, that does literally nothing and does not actually mutate material, but it assumes that material has changed and updates the texture, this way it works perfectly.

I believe this is not how it is supposed to work.

Additional information

When using example with sprite::from_image(), then it tracks texture changes correctly. It should work the same when Material2D is used.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-BugAn unexpected or incorrect behaviorS-Needs-TriageThis issue needs to be labelled

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions