Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions assets/shaders/custom_material.wesl

This file was deleted.

4 changes: 4 additions & 0 deletions assets/shaders/custom_material_import.wesl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
struct CustomMaterial {
// Needed for 16-bit alignment on WebGL2
time: vec4<f32>,
}
43 changes: 36 additions & 7 deletions crates/bevy_shader/src/shader_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub struct ShaderCache<ShaderModule, RenderDevice> {
&ValidateShader,
) -> Result<ShaderModule, PipelineCacheError>,
#[cfg(feature = "shader_format_wesl")]
asset_paths: HashMap<wesl::syntax::ModulePath, AssetId<Shader>>,
asset_paths: HashMap<wesl::ModulePath, AssetId<Shader>>,
shaders: HashMap<AssetId<Shader>, Shader>,
import_path_shaders: HashMap<ShaderImport, AssetId<Shader>>,
waiting_on_import: HashMap<ShaderImport, Vec<AssetId<Shader>>>,
Expand Down Expand Up @@ -210,7 +210,7 @@ impl<ShaderModule, RenderDevice> ShaderCache<ShaderModule, RenderDevice> {
if let ShaderImport::AssetPath(path) = shader.import_path() {
let shader_resolver =
ShaderResolver::new(&self.asset_paths, &self.shaders);
let module_path = wesl::syntax::ModulePath::from_path(path);
let module_path = wesl_module_path_from_asset_path(path);
let mut compiler_options = wesl::CompileOptions {
imports: true,
condcomp: true,
Expand Down Expand Up @@ -361,7 +361,7 @@ impl<ShaderModule, RenderDevice> ShaderCache<ShaderModule, RenderDevice> {
&& let ShaderImport::AssetPath(path) = shader.import_path()
{
self.asset_paths
.insert(wesl::syntax::ModulePath::from_path(path), id);
.insert(wesl_module_path_from_asset_path(path), id);
}
self.shaders.insert(id, shader);
pipelines_to_queue
Expand All @@ -371,22 +371,30 @@ impl<ShaderModule, RenderDevice> ShaderCache<ShaderModule, RenderDevice> {
let pipelines_to_queue = self.clear(id);
if let Some(shader) = self.shaders.remove(&id) {
self.import_path_shaders.remove(shader.import_path());

#[cfg(feature = "shader_format_wesl")]
if let Source::Wesl(_) = shader.source
&& let ShaderImport::AssetPath(path) = shader.import_path()
{
self.asset_paths
.remove(&wesl_module_path_from_asset_path(path));
}
}

pipelines_to_queue
}
}

#[cfg(feature = "shader_format_wesl")]
pub struct ShaderResolver<'a> {
asset_paths: &'a HashMap<wesl::syntax::ModulePath, AssetId<Shader>>,
struct ShaderResolver<'a> {
asset_paths: &'a HashMap<wesl::ModulePath, AssetId<Shader>>,
shaders: &'a HashMap<AssetId<Shader>, Shader>,
}

#[cfg(feature = "shader_format_wesl")]
impl<'a> ShaderResolver<'a> {
pub fn new(
asset_paths: &'a HashMap<wesl::syntax::ModulePath, AssetId<Shader>>,
asset_paths: &'a HashMap<wesl::ModulePath, AssetId<Shader>>,
shaders: &'a HashMap<AssetId<Shader>, Shader>,
) -> Self {
Self {
Expand All @@ -400,7 +408,7 @@ impl<'a> ShaderResolver<'a> {
impl<'a> wesl::Resolver for ShaderResolver<'a> {
fn resolve_source(
&self,
module_path: &wesl::syntax::ModulePath,
module_path: &wesl::ModulePath,
) -> Result<alloc::borrow::Cow<'_, str>, wesl::ResolveError> {
let asset_id = self.asset_paths.get(module_path).ok_or_else(|| {
wesl::ResolveError::ModuleNotFound(module_path.clone(), "Invalid asset id".to_string())
Expand All @@ -411,6 +419,27 @@ impl<'a> wesl::Resolver for ShaderResolver<'a> {
}
}

#[cfg(feature = "shader_format_wesl")]
fn wesl_module_path_from_asset_path(path: &String) -> wesl::ModulePath {
use bevy_asset::{io::AssetSourceId, AssetPath};
use std::path::PathBuf;

let asset_path = AssetPath::from(path);
if let AssetSourceId::Name(source) = asset_path.source() {
let mut comp = vec!["bevy_asset".to_string()];
let mut path = PathBuf::new();
path.push(source.as_ref());
path.push(asset_path.path());
comp.extend(wesl::ModulePath::from_path(path).components);
wesl::ModulePath {
origin: wesl::syntax::PathOrigin::Package,
components: comp,
}
} else {
wesl::ModulePath::from_path(asset_path.path())
}
}

/// Type of error returned by a `PipelineCache` when the creation of a GPU pipeline object failed.
#[cfg_attr(
not(target_arch = "wasm32"),
Expand Down
12 changes: 12 additions & 0 deletions examples/shader/files/custom_material.wesl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import super::util::make_polka_dots;
import bevy_asset::embedded::shader_material_wesl::files::vertex_attr;
import package::shaders::custom_material_import::CustomMaterial;

@group(3) @binding(0) var<uniform> material: CustomMaterial;

@fragment
fn fragment(
mesh: vertex_attr::VertexOutput,
) -> @location(0) vec4<f32> {
return make_polka_dots(mesh.uv, material.time.x);
}
File renamed without changes.
4 changes: 4 additions & 0 deletions examples/shader/files/vertex_attr.wesl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(2) uv: vec2<f32>,
}
37 changes: 31 additions & 6 deletions examples/shader/shader_material_wesl.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! A shader that uses the WESL shading language.

use bevy::asset::embedded_asset;
use bevy::{
mesh::MeshVertexBufferLayoutRef,
pbr::{MaterialPipeline, MaterialPipelineKey},
Expand All @@ -12,7 +13,8 @@ use bevy::{
};

/// This example uses shader source files from the assets subdirectory
const FRAGMENT_SHADER_ASSET_PATH: &str = "shaders/custom_material.wesl";
const FRAGMENT_SHADER_ASSET_PATH: &str =
"embedded://shader_material_wesl/files/custom_material.wesl";

fn main() {
App::new()
Expand All @@ -37,13 +39,36 @@ pub struct CustomMaterialPlugin;
#[derive(Resource)]
struct UtilityShader(Handle<Shader>);

#[expect(
dead_code,
reason = "used to kept a strong handle, shader is referenced by the material"
)]
#[derive(Resource)]
struct VertexAttrShader(Handle<Shader>);

#[expect(
dead_code,
reason = "used to kept a strong handle, shader is referenced by the material"
)]
#[derive(Resource)]
struct CustomMaterialImportShader(Handle<Shader>);

impl Plugin for CustomMaterialPlugin {
fn build(&self, app: &mut App) {
let handle = app
.world_mut()
.resource_mut::<AssetServer>()
.load::<Shader>("shaders/util.wesl");
app.insert_resource(UtilityShader(handle));
embedded_asset!(app, "examples/shader", "files/custom_material.wesl");
embedded_asset!(app, "examples/shader", "files/vertex_attr.wesl");
embedded_asset!(app, "examples/shader", "files/util.wesl");

let asset_server = app.world_mut().resource_mut::<AssetServer>();
let utils_handle =
asset_server.load::<Shader>("embedded://shader_material_wesl/files/util.wesl");
let vertex_attr_handle =
asset_server.load::<Shader>("embedded://shader_material_wesl/files/vertex_attr.wesl");
let custom_material_import_handle =
asset_server.load::<Shader>("shaders/custom_material_import.wesl");
app.insert_resource(UtilityShader(utils_handle))
.insert_resource(VertexAttrShader(vertex_attr_handle))
.insert_resource(CustomMaterialImportShader(custom_material_import_handle));
Comment on lines +62 to +71
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's bad to manually import every dependency. But Shader::from_wesl requires parsing dependencies in advance when loading (such as naga_oil::compose::get_preprocessor_data), which wesl-rs currently doesn't provide.

}
}

Expand Down