Skip to content

Shader hot reloading does not work for shader loaded in FromResources #1449

@guimcaballero

Description

@guimcaballero

Bevy version

Master

Operating system & version

MacOs 10.14.6

What you did

Loaded a shader in FromResources instead of in a startup system.

AssetServer::watch_for_changes is being called, both on the FromResources method and on a startup system.

What you expected to happen

Shader should have hot reloading, as when loaded from a startup system.

What actually happened

Shader isn't being reloaded.

Other shaders that are being loaded from a system do have reloading enabled.

Additional information

Here's a minimal example, which is a changed version of the current hot_shader_reloading.rs example:

use bevy::{
    prelude::*,
    render::{
        mesh::shape,
        pipeline::{PipelineDescriptor, RenderPipeline},
        shader::ShaderStages,
    },
};

fn main() {
    App::build()
        .add_plugins(DefaultPlugins)
        .init_resource::<ShaderResource>()
        .add_startup_system(setup.system())
        .run();
}

struct ShaderResource {
    vertex: Handle<Shader>,
    fragment: Handle<Shader>,
}
impl FromResources for ShaderResource {
    fn from_resources(resources: &Resources) -> Self {
        let asset_server = resources.get_mut::<AssetServer>().unwrap();

        // Does nothing
        asset_server.watch_for_changes().unwrap();

        ShaderResource {
            vertex: asset_server.load::<Shader, _>("shaders/hot.vert"),
            fragment: asset_server.load::<Shader, _>("shaders/hot.frag"),
        }
    }
}

fn setup(
    commands: &mut Commands,
    asset_server: ResMut<AssetServer>,
    mut meshes: ResMut<Assets<Mesh>>,
    material: Res<ShaderResource>,
    mut pipelines: ResMut<Assets<PipelineDescriptor>>,
) {
    // Does nothing
    asset_server.watch_for_changes().unwrap();

    let pipeline_handle = pipelines.add(PipelineDescriptor::default_config(ShaderStages {
        vertex: material.vertex.clone(),
        fragment: Some(material.fragment.clone()),
    }));
    commands
        .spawn(MeshBundle {
            mesh: meshes.add(Mesh::from(shape::Cube { size: 2.0 })),
            render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new(
                pipeline_handle,
            )]),
            transform: Transform::from_xyz(0.0, 0.0, 0.0),
            ..Default::default()
        })
        .spawn(PerspectiveCameraBundle {
            transform: Transform::from_xyz(3.0, 5.0, -8.0)
                .looking_at(Vec3::default(), Vec3::unit_y()),
            ..Default::default()
        });
}

As a side note, adding another system that loads the shader but doesn't use the handle, activates hot reloading. For example:

fn asset_server_changes(asset_server: ResMut<AssetServer>) {
    asset_server.watch_for_changes().unwrap();

    // HACK To get hot reloading
    asset_server.load::<Shader, _>("shaders/hot.frag");
    asset_server.load::<Shader, _>("shaders/hot.vert");
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-RenderingDrawing game state to the screenC-BugAn unexpected or incorrect behavior

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions