Skip to content

Commit 1395152

Browse files
authored
Use embedded_asset to load PBR shaders (#19137)
# Objective - Get in-engine shader hot reloading working ## Solution - Adopt #12009 - Cut back on everything possible to land an MVP: we only hot-reload PBR in deferred shading mode. This is to minimize the diff and avoid merge hell. The rest shall come in followups. ## Testing - `cargo run --example pbr --features="embedded_watcher"` and edit some pbr shader code
1 parent 415ffa5 commit 1395152

File tree

9 files changed

+177
-188
lines changed

9 files changed

+177
-188
lines changed

crates/bevy_asset/src/io/embedded/mod.rs

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ use crate::io::{
88
memory::{Dir, MemoryAssetReader, Value},
99
AssetSource, AssetSourceBuilders,
1010
};
11+
use crate::AssetServer;
1112
use alloc::boxed::Box;
12-
use bevy_ecs::resource::Resource;
13+
use bevy_app::App;
14+
use bevy_ecs::{resource::Resource, world::World};
1315
use std::path::{Path, PathBuf};
1416

1517
#[cfg(feature = "embedded_watcher")]
@@ -132,6 +134,71 @@ impl EmbeddedAssetRegistry {
132134
}
133135
}
134136

137+
/// Trait for the [`load_embedded_asset!`] macro, to access [`AssetServer`]
138+
/// from arbitrary things.
139+
///
140+
/// [`load_embedded_asset!`]: crate::load_embedded_asset
141+
pub trait GetAssetServer {
142+
fn get_asset_server(&self) -> &AssetServer;
143+
}
144+
impl GetAssetServer for App {
145+
fn get_asset_server(&self) -> &AssetServer {
146+
self.world().get_asset_server()
147+
}
148+
}
149+
impl GetAssetServer for World {
150+
fn get_asset_server(&self) -> &AssetServer {
151+
self.resource()
152+
}
153+
}
154+
impl GetAssetServer for AssetServer {
155+
fn get_asset_server(&self) -> &AssetServer {
156+
self
157+
}
158+
}
159+
160+
/// Load an [embedded asset](crate::embedded_asset).
161+
///
162+
/// This is useful if the embedded asset in question is not publicly exposed, but
163+
/// you need to use it internally.
164+
///
165+
/// # Syntax
166+
///
167+
/// This macro takes two arguments and an optional third one:
168+
/// 1. The asset source. It may be `AssetServer`, `World` or `App`.
169+
/// 2. The path to the asset to embed, as a string literal.
170+
/// 3. Optionally, a closure of the same type as in [`AssetServer::load_with_settings`].
171+
/// Consider explicitly typing the closure argument in case of type error.
172+
///
173+
/// # Usage
174+
///
175+
/// The advantage compared to using directly [`AssetServer::load`] is:
176+
/// - This also accepts [`World`] and [`App`] arguments.
177+
/// - This uses the exact same path as `embedded_asset!`, so you can keep it
178+
/// consistent.
179+
///
180+
/// As a rule of thumb:
181+
/// - If the asset in used in the same module as it is declared using `embedded_asset!`,
182+
/// use this macro.
183+
/// - Otherwise, use `AssetServer::load`.
184+
#[macro_export]
185+
macro_rules! load_embedded_asset {
186+
(@get: $path: literal, $provider: expr) => {{
187+
let path = $crate::embedded_path!($path);
188+
let path = $crate::AssetPath::from_path_buf(path).with_source("embedded");
189+
let asset_server = $crate::io::embedded::GetAssetServer::get_asset_server($provider);
190+
(path, asset_server)
191+
}};
192+
($provider: expr, $path: literal, $settings: expr) => {{
193+
let (path, asset_server) = $crate::load_embedded_asset!(@get: $path, $provider);
194+
asset_server.load_with_settings(path, $settings)
195+
}};
196+
($provider: expr, $path: literal) => {{
197+
let (path, asset_server) = $crate::load_embedded_asset!(@get: $path, $provider);
198+
asset_server.load(path)
199+
}};
200+
}
201+
135202
/// Returns the [`Path`] for a given `embedded` asset.
136203
/// This is used internally by [`embedded_asset`] and can be used to get a [`Path`]
137204
/// that matches the [`AssetPath`](crate::AssetPath) used by that asset.
@@ -140,7 +207,7 @@ impl EmbeddedAssetRegistry {
140207
#[macro_export]
141208
macro_rules! embedded_path {
142209
($path_str: expr) => {{
143-
embedded_path!("src", $path_str)
210+
$crate::embedded_path!("src", $path_str)
144211
}};
145212

146213
($source_path: expr, $path_str: expr) => {{
@@ -192,7 +259,7 @@ pub fn _embedded_asset_path(
192259
/// Creates a new `embedded` asset by embedding the bytes of the given path into the current binary
193260
/// and registering those bytes with the `embedded` [`AssetSource`].
194261
///
195-
/// This accepts the current [`App`](bevy_app::App) as the first parameter and a path `&str` (relative to the current file) as the second.
262+
/// This accepts the current [`App`] as the first parameter and a path `&str` (relative to the current file) as the second.
196263
///
197264
/// By default this will generate an [`AssetPath`] using the following rules:
198265
///
@@ -217,14 +284,19 @@ pub fn _embedded_asset_path(
217284
///
218285
/// `embedded_asset!(app, "rock.wgsl")`
219286
///
220-
/// `rock.wgsl` can now be loaded by the [`AssetServer`](crate::AssetServer) with the following path:
287+
/// `rock.wgsl` can now be loaded by the [`AssetServer`] as follows:
221288
///
222289
/// ```no_run
223-
/// # use bevy_asset::{Asset, AssetServer};
290+
/// # use bevy_asset::{Asset, AssetServer, load_embedded_asset};
224291
/// # use bevy_reflect::TypePath;
225292
/// # let asset_server: AssetServer = panic!();
226293
/// # #[derive(Asset, TypePath)]
227294
/// # struct Shader;
295+
/// // If we are loading the shader in the same module we used `embedded_asset!`:
296+
/// let shader = load_embedded_asset!(&asset_server, "rock.wgsl");
297+
/// # let _: bevy_asset::Handle<Shader> = shader;
298+
///
299+
/// // If the goal is to expose the asset **to the end user**:
228300
/// let shader = asset_server.load::<Shader>("embedded://bevy_rock/render/rock.wgsl");
229301
/// ```
230302
///
@@ -258,11 +330,11 @@ pub fn _embedded_asset_path(
258330
/// [`embedded_path`]: crate::embedded_path
259331
#[macro_export]
260332
macro_rules! embedded_asset {
261-
($app: ident, $path: expr) => {{
333+
($app: expr, $path: expr) => {{
262334
$crate::embedded_asset!($app, "src", $path)
263335
}};
264336

265-
($app: ident, $source_path: expr, $path: expr) => {{
337+
($app: expr, $source_path: expr, $path: expr) => {{
266338
let mut embedded = $app
267339
.world_mut()
268340
.resource_mut::<$crate::io::embedded::EmbeddedAssetRegistry>();

crates/bevy_asset/src/path.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,16 @@ impl<'a> AssetPath<'a> {
223223
Ok((source, path, label))
224224
}
225225

226+
/// Creates a new [`AssetPath`] from a [`PathBuf`].
227+
#[inline]
228+
pub fn from_path_buf(path_buf: PathBuf) -> AssetPath<'a> {
229+
AssetPath {
230+
path: CowArc::Owned(path_buf.into()),
231+
source: AssetSourceId::Default,
232+
label: None,
233+
}
234+
}
235+
226236
/// Creates a new [`AssetPath`] from a [`Path`].
227237
#[inline]
228238
pub fn from_path(path: &'a Path) -> AssetPath<'a> {

crates/bevy_pbr/src/deferred/mod.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
ViewLightsUniformOffset,
1111
};
1212
use bevy_app::prelude::*;
13-
use bevy_asset::{load_internal_asset, weak_handle, Handle};
13+
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
1414
use bevy_core_pipeline::{
1515
core_3d::graph::{Core3d, Node3d},
1616
deferred::{
@@ -34,9 +34,6 @@ use bevy_render::{
3434

3535
pub struct DeferredPbrLightingPlugin;
3636

37-
pub const DEFERRED_LIGHTING_SHADER_HANDLE: Handle<Shader> =
38-
weak_handle!("f4295279-8890-4748-b654-ca4d2183df1c");
39-
4037
pub const DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID: u8 = 1;
4138

4239
/// Component with a `depth_id` for specifying which corresponding materials should be rendered by this specific PBR deferred lighting pass.
@@ -100,12 +97,7 @@ impl Plugin for DeferredPbrLightingPlugin {
10097
))
10198
.add_systems(PostUpdate, insert_deferred_lighting_pass_id_component);
10299

103-
load_internal_asset!(
104-
app,
105-
DEFERRED_LIGHTING_SHADER_HANDLE,
106-
"deferred_lighting.wgsl",
107-
Shader::from_wgsl
108-
);
100+
embedded_asset!(app, "deferred_lighting.wgsl");
109101

110102
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
111103
return;
@@ -237,6 +229,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
237229
pub struct DeferredLightingLayout {
238230
mesh_pipeline: MeshPipeline,
239231
bind_group_layout_1: BindGroupLayout,
232+
deferred_lighting_shader: Handle<Shader>,
240233
}
241234

242235
#[derive(Component)]
@@ -360,13 +353,13 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
360353
self.bind_group_layout_1.clone(),
361354
],
362355
vertex: VertexState {
363-
shader: DEFERRED_LIGHTING_SHADER_HANDLE,
356+
shader: self.deferred_lighting_shader.clone(),
364357
shader_defs: shader_defs.clone(),
365358
entry_point: "vertex".into(),
366359
buffers: Vec::new(),
367360
},
368361
fragment: Some(FragmentState {
369-
shader: DEFERRED_LIGHTING_SHADER_HANDLE,
362+
shader: self.deferred_lighting_shader.clone(),
370363
shader_defs,
371364
entry_point: "fragment".into(),
372365
targets: vec![Some(ColorTargetState {
@@ -416,6 +409,7 @@ impl FromWorld for DeferredLightingLayout {
416409
Self {
417410
mesh_pipeline: world.resource::<MeshPipeline>().clone(),
418411
bind_group_layout_1: layout,
412+
deferred_lighting_shader: load_embedded_asset!(world, "deferred_lighting.wgsl"),
419413
}
420414
}
421415
}

0 commit comments

Comments
 (0)