Skip to content

Commit 949487d

Browse files
authored
make glsl and spirv support optional (#8491)
# Objective - Reduce compilation time ## Solution - Make `spirv` and `glsl` shader format support optional. They are not needed for Bevy shaders. - on my mac (where shaders are compiled to `msl`), this reduces the total build time by 2 to 5 seconds, improvement should be even better with less cores There is a big reduction in compile time for `naga`, and small improvements on `wgpu` and `bevy_render` This PR with optional shader formats enabled timings: <img width="1478" alt="current main" src="https://user-images.githubusercontent.com/8672791/234347032-cbd5c276-a9b0-49c3-b793-481677391c18.png"> This PR: <img width="1479" alt="this pr" src="https://user-images.githubusercontent.com/8672791/234347059-a67412a9-da8d-4356-91d8-7b0ae84ca100.png"> --- ## Migration Guide - If you want to use shaders in `spirv`, enable the `shader_format_spirv` feature - If you want to use shaders in `glsl`, enable the `shader_format_glsl` feature
1 parent dea91e9 commit 949487d

File tree

6 files changed

+56
-7
lines changed

6 files changed

+56
-7
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ glam_assert = ["bevy_internal/glam_assert"]
229229
# Include a default font, containing only ASCII characters, at the cost of a 20kB binary size increase
230230
default_font = ["bevy_internal/default_font"]
231231

232+
# Enable support for shaders in GLSL
233+
shader_format_glsl = ["bevy_internal/shader_format_glsl"]
234+
235+
# Enable support for shaders in SPIR-V
236+
shader_format_spirv = ["bevy_internal/shader_format_spirv"]
237+
232238
[dependencies]
233239
bevy_dylib = { path = "crates/bevy_dylib", version = "0.11.0-dev", default-features = false, optional = true }
234240
bevy_internal = { path = "crates/bevy_internal", version = "0.11.0-dev", default-features = false }

crates/bevy_internal/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ symphonia-isomp4 = ["bevy_audio/symphonia-isomp4"]
5757
symphonia-vorbis = ["bevy_audio/symphonia-vorbis"]
5858
symphonia-wav = ["bevy_audio/symphonia-wav"]
5959

60+
# Shader formats
61+
shader_format_glsl = ["bevy_render/shader_format_glsl"]
62+
shader_format_spirv = ["bevy_render/shader_format_spirv"]
63+
6064
# Enable watching file system for asset hot reload
6165
filesystem_watcher = ["bevy_asset/filesystem_watcher"]
6266

crates/bevy_render/Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ bmp = ["image/bmp"]
1818
webp = ["image/webp"]
1919
dds = ["ddsfile"]
2020

21+
shader_format_glsl = ["naga/glsl-in", "naga/wgsl-out"]
22+
shader_format_spirv = ["wgpu/spirv", "naga/spv-in", "naga/spv-out"]
23+
2124
# For ktx2 supercompression
2225
zlib = ["flate2"]
2326
zstd = ["ruzstd"]
@@ -52,10 +55,10 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
5255
image = { version = "0.24", default-features = false }
5356

5457
# misc
55-
wgpu = { version = "0.15.0", features = ["spirv"] }
58+
wgpu = { version = "0.15.0" }
5659
wgpu-hal = "0.15.1"
5760
codespan-reporting = "0.11.0"
58-
naga = { version = "0.11.0", features = ["glsl-in", "spv-in", "spv-out", "wgsl-in", "wgsl-out"] }
61+
naga = { version = "0.11.0", features = ["wgsl-in"] }
5962
serde = { version = "1", features = ["derive"] }
6063
bitflags = "1.2.1"
6164
smallvec = { version = "1.6", features = ["union", "const_generics"] }

crates/bevy_render/src/render_resource/pipeline_cache.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) {
752752
let msg = error.emit_to_string(source);
753753
error!("failed to process shader:\n{}", msg);
754754
}
755+
#[cfg(feature = "shader_format_glsl")]
755756
ShaderReflectError::GlslParse(errors) => {
756757
let source = source
757758
.get_glsl_source()
@@ -776,6 +777,7 @@ fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) {
776777

777778
error!("failed to process shader: \n{}", msg);
778779
}
780+
#[cfg(feature = "shader_format_spirv")]
779781
ShaderReflectError::SpirVParse(error) => {
780782
error!("failed to process shader:\n{}", error);
781783
}
@@ -818,9 +820,11 @@ fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) {
818820
error!("failed to process shader: \n{}", msg);
819821
}
820822
},
823+
#[cfg(feature = "shader_format_glsl")]
821824
AsModuleDescriptorError::WgslConversion(error) => {
822825
error!("failed to convert shader to wgsl: \n{}", error);
823826
}
827+
#[cfg(feature = "shader_format_spirv")]
824828
AsModuleDescriptorError::SpirVConversion(error) => {
825829
error!("failed to convert shader to spirv: \n{}", error);
826830
}

crates/bevy_render/src/render_resource/shader.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,27 @@ use crate::define_atomic_id;
33
use bevy_asset::{AssetLoader, AssetPath, Handle, LoadContext, LoadedAsset};
44
use bevy_reflect::TypeUuid;
55
use bevy_utils::{tracing::error, BoxedFuture, HashMap};
6-
use naga::{back::wgsl::WriterFlags, valid::Capabilities, valid::ModuleInfo, Module};
6+
#[cfg(feature = "shader_format_glsl")]
7+
use naga::back::wgsl::WriterFlags;
8+
use naga::{valid::Capabilities, valid::ModuleInfo, Module};
79
use once_cell::sync::Lazy;
810
use regex::Regex;
911
use std::{borrow::Cow, marker::Copy, ops::Deref, path::PathBuf, str::FromStr};
1012
use thiserror::Error;
11-
use wgpu::{util::make_spirv, Features, ShaderModuleDescriptor, ShaderSource};
13+
#[cfg(feature = "shader_format_spirv")]
14+
use wgpu::util::make_spirv;
15+
use wgpu::{Features, ShaderModuleDescriptor, ShaderSource};
1216

1317
define_atomic_id!(ShaderId);
1418

1519
#[derive(Error, Debug)]
1620
pub enum ShaderReflectError {
1721
#[error(transparent)]
1822
WgslParse(#[from] naga::front::wgsl::ParseError),
23+
#[cfg(feature = "shader_format_glsl")]
1924
#[error("GLSL Parse Error: {0:?}")]
2025
GlslParse(Vec<naga::front::glsl::Error>),
26+
#[cfg(feature = "shader_format_spirv")]
2127
#[error(transparent)]
2228
SpirVParse(#[from] naga::front::spv::Error),
2329
#[error(transparent)]
@@ -120,19 +126,29 @@ impl ProcessedShader {
120126
let module = match &self {
121127
// TODO: process macros here
122128
ProcessedShader::Wgsl(source) => naga::front::wgsl::parse_str(source)?,
129+
#[cfg(feature = "shader_format_glsl")]
123130
ProcessedShader::Glsl(source, shader_stage) => {
124131
let mut parser = naga::front::glsl::Parser::default();
125132
parser
126133
.parse(&naga::front::glsl::Options::from(*shader_stage), source)
127134
.map_err(ShaderReflectError::GlslParse)?
128135
}
136+
#[cfg(not(feature = "shader_format_glsl"))]
137+
ProcessedShader::Glsl(_source, _shader_stage) => {
138+
unimplemented!("Enable feature \"shader_format_glsl\" to use GLSL shaders")
139+
}
140+
#[cfg(feature = "shader_format_spirv")]
129141
ProcessedShader::SpirV(source) => naga::front::spv::parse_u8_slice(
130142
source,
131143
&naga::front::spv::Options {
132144
adjust_coordinate_space: false,
133145
..naga::front::spv::Options::default()
134146
},
135147
)?,
148+
#[cfg(not(feature = "shader_format_spirv"))]
149+
ProcessedShader::SpirV(_source) => {
150+
unimplemented!("Enable feature \"shader_format_spirv\" to use SPIR-V shaders")
151+
}
136152
};
137153
const CAPABILITIES: &[(Features, Capabilities)] = &[
138154
(Features::PUSH_CONSTANTS, Capabilities::PUSH_CONSTANT),
@@ -172,7 +188,7 @@ impl ProcessedShader {
172188

173189
pub fn get_module_descriptor(
174190
&self,
175-
features: Features,
191+
_features: Features,
176192
) -> Result<ShaderModuleDescriptor, AsModuleDescriptorError> {
177193
Ok(ShaderModuleDescriptor {
178194
label: None,
@@ -182,18 +198,28 @@ impl ProcessedShader {
182198
// Parse and validate the shader early, so that (e.g. while hot reloading) we can
183199
// display nicely formatted error messages instead of relying on just displaying the error string
184200
// returned by wgpu upon creating the shader module.
185-
let _ = self.reflect(features)?;
201+
let _ = self.reflect(_features)?;
186202

187203
ShaderSource::Wgsl(source.clone())
188204
}
205+
#[cfg(feature = "shader_format_glsl")]
189206
ProcessedShader::Glsl(_source, _stage) => {
190-
let reflection = self.reflect(features)?;
207+
let reflection = self.reflect(_features)?;
191208
// TODO: it probably makes more sense to convert this to spirv, but as of writing
192209
// this comment, naga's spirv conversion is broken
193210
let wgsl = reflection.get_wgsl()?;
194211
ShaderSource::Wgsl(wgsl.into())
195212
}
213+
#[cfg(not(feature = "shader_format_glsl"))]
214+
ProcessedShader::Glsl(_source, _stage) => {
215+
unimplemented!("Enable feature \"shader_format_glsl\" to use GLSL shaders")
216+
}
217+
#[cfg(feature = "shader_format_spirv")]
196218
ProcessedShader::SpirV(source) => make_spirv(source),
219+
#[cfg(not(feature = "shader_format_spirv"))]
220+
ProcessedShader::SpirV(_source) => {
221+
unimplemented!()
222+
}
197223
},
198224
})
199225
}
@@ -203,8 +229,10 @@ impl ProcessedShader {
203229
pub enum AsModuleDescriptorError {
204230
#[error(transparent)]
205231
ShaderReflectError(#[from] ShaderReflectError),
232+
#[cfg(feature = "shader_format_glsl")]
206233
#[error(transparent)]
207234
WgslConversion(#[from] naga::back::wgsl::Error),
235+
#[cfg(feature = "shader_format_spirv")]
208236
#[error(transparent)]
209237
SpirVConversion(#[from] naga::back::spv::Error),
210238
}
@@ -215,6 +243,7 @@ pub struct ShaderReflection {
215243
}
216244

217245
impl ShaderReflection {
246+
#[cfg(feature = "shader_format_spirv")]
218247
pub fn get_spirv(&self) -> Result<Vec<u32>, naga::back::spv::Error> {
219248
naga::back::spv::write_vec(
220249
&self.module,
@@ -227,6 +256,7 @@ impl ShaderReflection {
227256
)
228257
}
229258

259+
#[cfg(feature = "shader_format_glsl")]
230260
pub fn get_wgsl(&self) -> Result<String, naga::back::wgsl::Error> {
231261
naga::back::wgsl::write_string(&self.module, &self.module_info, WriterFlags::EXPLICIT_TYPES)
232262
}

docs/cargo_features.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ The default feature set enables most of the expected features of a game engine,
5757
|minimp3|MP3 audio format support (through minimp3)|
5858
|mp3|MP3 audio format support|
5959
|serialize|Enable serialization support through serde|
60+
|shader_format_glsl|Enable support for shaders in GLSL|
61+
|shader_format_spirv|Enable support for shaders in SPIR-V|
6062
|subpixel_glyph_atlas|Enable rendering of font glyphs using subpixel accuracy|
6163
|symphonia-aac|AAC audio format support (through symphonia)|
6264
|symphonia-all|AAC, FLAC, MP3, MP4, OGG/VORBIS, and WAV audio formats support (through symphonia)|

0 commit comments

Comments
 (0)