Skip to content

Commit

Permalink
Add ShaderLoader, rebuild pipelines for modified shader assets
Browse files Browse the repository at this point in the history
  • Loading branch information
yrns committed Nov 28, 2020
1 parent f69cc6f commit 57b0866
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 16 deletions.
11 changes: 11 additions & 0 deletions assets/shaders/custom.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#version 450

layout(location = 0) out vec4 o_Target;

layout(set = 1, binding = 1) uniform MyMaterial_color {
vec4 color;
};

void main() {
o_Target = color * 0.5;
}
7 changes: 5 additions & 2 deletions crates/bevy_render/src/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use crate::{
},
shader::Shader,
};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{Query, Res, ResMut, SystemParam};
use bevy_app::{EventReader, Events};
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_ecs::{Local, Query, Res, ResMut, SystemParam};
use bevy_property::Properties;
use std::{ops::Range, sync::Arc};
use thiserror::Error;
Expand Down Expand Up @@ -123,6 +124,8 @@ pub enum DrawError {
pub struct DrawContext<'a> {
pub pipelines: ResMut<'a, Assets<PipelineDescriptor>>,
pub shaders: ResMut<'a, Assets<Shader>>,
pub shader_events: ResMut<'a, Events<AssetEvent<Shader>>>,
pub shader_event_reader: Local<'a, EventReader<AssetEvent<Shader>>>,
pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
pub shared_buffers: Res<'a, SharedBuffers>,
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ use render_graph::{
RenderGraph,
};
use renderer::{AssetRenderResourceBindings, RenderResourceBindings};
use shader::ShaderLoader;
use std::ops::Range;
#[cfg(feature = "hdr")]
use texture::HdrTextureLoader;
Expand Down Expand Up @@ -88,6 +89,8 @@ impl Plugin for RenderPlugin {
app.init_asset_loader::<HdrTextureLoader>();
}

app.init_asset_loader::<ShaderLoader>();

if app.resources().get::<ClearColor>().is_none() {
app.resources_mut().insert(ClearColor::default());
}
Expand Down
18 changes: 18 additions & 0 deletions crates/bevy_render/src/pipeline/pipeline_compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,22 @@ impl PipelineCompiler {
})
.flatten()
}

/// Remove any specialized shaders or pipelines that match the
/// changed shader set.
pub fn remove_shaders(
&mut self,
shaders: HashSet<&Handle<Shader>>,
pipelines: &Assets<PipelineDescriptor>,
) {
self.specialized_shaders.retain(|k, _| !shaders.contains(k));
self.specialized_pipelines.retain(|k, _| {
pipelines
.get(k)
.unwrap()
.shader_stages
.iter()
.all(|s| !shaders.contains(&s))
});
}
}
20 changes: 19 additions & 1 deletion crates/bevy_render/src/pipeline/render_pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use crate::{
prelude::Msaa,
renderer::RenderResourceBindings,
};
use bevy_asset::{Assets, Handle};
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_ecs::{Query, Res, ResMut};
use bevy_property::Properties;
use bevy_utils::HashSet;

#[derive(Debug, Properties, Default, Clone)]
#[non_exhaustive]
Expand Down Expand Up @@ -83,6 +84,23 @@ pub fn draw_render_pipelines_system(
meshes: Res<Assets<Mesh>>,
mut query: Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>)>,
) {
// Pipelines will be rebuilt for shaders that have been modified.
let mut changed_shaders = HashSet::default();
for event in draw_context
.shader_event_reader
.iter(&draw_context.shader_events)
{
match event {
AssetEvent::Modified { handle } => {
changed_shaders.insert(handle);
}
_ => (),
}
}
draw_context
.pipeline_compiler
.remove_shaders(changed_shaders, &draw_context.pipelines);

for (mut draw, mut render_pipelines, mesh_handle) in query.iter_mut() {
if !draw.is_visible {
continue;
Expand Down
31 changes: 30 additions & 1 deletion crates/bevy_render/src/shader/shader.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::ShaderLayout;
use bevy_asset::Handle;
use bevy_asset::{AssetLoader, Handle, LoadContext, LoadedAsset};
use bevy_type_registry::TypeUuid;
use bevy_utils::BoxedFuture;
use std::marker::Copy;

/// The stage of a shader
Expand Down Expand Up @@ -188,3 +189,31 @@ impl ShaderStages {
}
}
}

#[derive(Default)]
pub struct ShaderLoader;

impl AssetLoader for ShaderLoader {
fn load<'a>(
&'a self,
bytes: &'a [u8],
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
Box::pin(async move {
let ext = load_context.path().extension().unwrap().to_str().unwrap();

let shader = match ext {
"vert" => Shader::from_glsl(ShaderStage::Vertex, std::str::from_utf8(bytes)?),
"frag" => Shader::from_glsl(ShaderStage::Fragment, std::str::from_utf8(bytes)?),
_ => panic!("unhandled extension: {}", ext),
};

load_context.set_default_asset(LoadedAsset::new(shader));
Ok(())
})
}

fn extensions(&self) -> &[&str] {
&["vert", "frag"]
}
}
16 changes: 4 additions & 12 deletions examples/shader/shader_custom_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,29 +39,21 @@ void main() {
}
"#;

const FRAGMENT_SHADER: &str = r#"
#version 450
layout(location = 0) out vec4 o_Target;
layout(set = 1, binding = 1) uniform MyMaterial_color {
vec4 color;
};
void main() {
o_Target = color;
}
"#;

fn setup(
commands: &mut Commands,
asset_server: ResMut<AssetServer>,
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
mut shaders: ResMut<Assets<Shader>>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<MyMaterial>>,
mut render_graph: ResMut<RenderGraph>,
) {
asset_server.watch_for_changes().unwrap();

// Create a new shader pipeline
let pipeline_handle = pipelines.add(PipelineDescriptor::default_config(ShaderStages {
vertex: shaders.add(Shader::from_glsl(ShaderStage::Vertex, VERTEX_SHADER)),
fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))),
fragment: Some(asset_server.load::<Shader, _>("shaders/custom.frag")),
}));

// Add an AssetRenderResourcesNode to our Render Graph. This will bind MyMaterial resources to our shader
Expand Down

0 comments on commit 57b0866

Please sign in to comment.