Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make glsl and spirv support optional #8491

Merged
merged 3 commits into from
Apr 25, 2023
Merged
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
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ glam_assert = ["bevy_internal/glam_assert"]
# Include a default font, containing only ASCII characters, at the cost of a 20kB binary size increase
default_font = ["bevy_internal/default_font"]

# Enable support for shaders in GLSL
shader_format_glsl = ["bevy_internal/shader_format_glsl"]

# Enable support for shaders in SPIR-V
shader_format_spirv = ["bevy_internal/shader_format_spirv"]

[dependencies]
bevy_dylib = { path = "crates/bevy_dylib", version = "0.11.0-dev", default-features = false, optional = true }
bevy_internal = { path = "crates/bevy_internal", version = "0.11.0-dev", default-features = false }
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ symphonia-isomp4 = ["bevy_audio/symphonia-isomp4"]
symphonia-vorbis = ["bevy_audio/symphonia-vorbis"]
symphonia-wav = ["bevy_audio/symphonia-wav"]

# Shader formats
shader_format_glsl = ["bevy_render/shader_format_glsl"]
shader_format_spirv = ["bevy_render/shader_format_spirv"]

# Enable watching file system for asset hot reload
filesystem_watcher = ["bevy_asset/filesystem_watcher"]

Expand Down
7 changes: 5 additions & 2 deletions crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ bmp = ["image/bmp"]
webp = ["image/webp"]
dds = ["ddsfile"]

shader_format_glsl = ["naga/glsl-in", "naga/wgsl-out"]
shader_format_spirv = ["wgpu/spirv", "naga/spv-in", "naga/spv-out"]

# For ktx2 supercompression
zlib = ["flate2"]
zstd = ["ruzstd"]
Expand Down Expand Up @@ -52,10 +55,10 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
image = { version = "0.24", default-features = false }

# misc
wgpu = { version = "0.15.0", features = ["spirv"] }
wgpu = { version = "0.15.0" }
wgpu-hal = "0.15.1"
codespan-reporting = "0.11.0"
naga = { version = "0.11.0", features = ["glsl-in", "spv-in", "spv-out", "wgsl-in", "wgsl-out"] }
naga = { version = "0.11.0", features = ["wgsl-in"] }
serde = { version = "1", features = ["derive"] }
bitflags = "1.2.1"
smallvec = { version = "1.6", features = ["union", "const_generics"] }
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_render/src/render_resource/pipeline_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,7 @@ fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) {
let msg = error.emit_to_string(source);
error!("failed to process shader:\n{}", msg);
}
#[cfg(feature = "shader_format_glsl")]
ShaderReflectError::GlslParse(errors) => {
let source = source
.get_glsl_source()
Expand All @@ -776,6 +777,7 @@ fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) {

error!("failed to process shader: \n{}", msg);
}
#[cfg(feature = "shader_format_spirv")]
ShaderReflectError::SpirVParse(error) => {
error!("failed to process shader:\n{}", error);
}
Expand Down Expand Up @@ -818,9 +820,11 @@ fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) {
error!("failed to process shader: \n{}", msg);
}
},
#[cfg(feature = "shader_format_glsl")]
AsModuleDescriptorError::WgslConversion(error) => {
error!("failed to convert shader to wgsl: \n{}", error);
}
#[cfg(feature = "shader_format_spirv")]
AsModuleDescriptorError::SpirVConversion(error) => {
error!("failed to convert shader to spirv: \n{}", error);
}
Expand Down
40 changes: 35 additions & 5 deletions crates/bevy_render/src/render_resource/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@ use crate::define_atomic_id;
use bevy_asset::{AssetLoader, AssetPath, Handle, LoadContext, LoadedAsset};
use bevy_reflect::TypeUuid;
use bevy_utils::{tracing::error, BoxedFuture, HashMap};
use naga::{back::wgsl::WriterFlags, valid::Capabilities, valid::ModuleInfo, Module};
#[cfg(feature = "shader_format_glsl")]
use naga::back::wgsl::WriterFlags;
use naga::{valid::Capabilities, valid::ModuleInfo, Module};
use once_cell::sync::Lazy;
use regex::Regex;
use std::{borrow::Cow, marker::Copy, ops::Deref, path::PathBuf, str::FromStr};
use thiserror::Error;
use wgpu::{util::make_spirv, Features, ShaderModuleDescriptor, ShaderSource};
#[cfg(feature = "shader_format_spirv")]
use wgpu::util::make_spirv;
use wgpu::{Features, ShaderModuleDescriptor, ShaderSource};

define_atomic_id!(ShaderId);

#[derive(Error, Debug)]
pub enum ShaderReflectError {
#[error(transparent)]
WgslParse(#[from] naga::front::wgsl::ParseError),
#[cfg(feature = "shader_format_glsl")]
#[error("GLSL Parse Error: {0:?}")]
GlslParse(Vec<naga::front::glsl::Error>),
#[cfg(feature = "shader_format_spirv")]
#[error(transparent)]
SpirVParse(#[from] naga::front::spv::Error),
#[error(transparent)]
Expand Down Expand Up @@ -120,19 +126,29 @@ impl ProcessedShader {
let module = match &self {
// TODO: process macros here
ProcessedShader::Wgsl(source) => naga::front::wgsl::parse_str(source)?,
#[cfg(feature = "shader_format_glsl")]
ProcessedShader::Glsl(source, shader_stage) => {
let mut parser = naga::front::glsl::Parser::default();
parser
.parse(&naga::front::glsl::Options::from(*shader_stage), source)
.map_err(ShaderReflectError::GlslParse)?
}
#[cfg(not(feature = "shader_format_glsl"))]
ProcessedShader::Glsl(_source, _shader_stage) => {
unimplemented!("Enable feature \"shader_format_glsl\" to use GLSL shaders")
}
#[cfg(feature = "shader_format_spirv")]
ProcessedShader::SpirV(source) => naga::front::spv::parse_u8_slice(
source,
&naga::front::spv::Options {
adjust_coordinate_space: false,
..naga::front::spv::Options::default()
},
)?,
#[cfg(not(feature = "shader_format_spirv"))]
ProcessedShader::SpirV(_source) => {
unimplemented!("Enable feature \"shader_format_spirv\" to use SPIR-V shaders")
}
};
const CAPABILITIES: &[(Features, Capabilities)] = &[
(Features::PUSH_CONSTANTS, Capabilities::PUSH_CONSTANT),
Expand Down Expand Up @@ -172,7 +188,7 @@ impl ProcessedShader {

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

ShaderSource::Wgsl(source.clone())
}
#[cfg(feature = "shader_format_glsl")]
ProcessedShader::Glsl(_source, _stage) => {
let reflection = self.reflect(features)?;
let reflection = self.reflect(_features)?;
// TODO: it probably makes more sense to convert this to spirv, but as of writing
// this comment, naga's spirv conversion is broken
let wgsl = reflection.get_wgsl()?;
ShaderSource::Wgsl(wgsl.into())
}
#[cfg(not(feature = "shader_format_glsl"))]
ProcessedShader::Glsl(_source, _stage) => {
unimplemented!("Enable feature \"shader_format_glsl\" to use GLSL shaders")
}
#[cfg(feature = "shader_format_spirv")]
ProcessedShader::SpirV(source) => make_spirv(source),
#[cfg(not(feature = "shader_format_spirv"))]
ProcessedShader::SpirV(_source) => {
unimplemented!()
}
},
})
}
Expand All @@ -203,8 +229,10 @@ impl ProcessedShader {
pub enum AsModuleDescriptorError {
#[error(transparent)]
ShaderReflectError(#[from] ShaderReflectError),
#[cfg(feature = "shader_format_glsl")]
#[error(transparent)]
WgslConversion(#[from] naga::back::wgsl::Error),
#[cfg(feature = "shader_format_spirv")]
#[error(transparent)]
SpirVConversion(#[from] naga::back::spv::Error),
}
Expand All @@ -215,6 +243,7 @@ pub struct ShaderReflection {
}

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

#[cfg(feature = "shader_format_glsl")]
pub fn get_wgsl(&self) -> Result<String, naga::back::wgsl::Error> {
naga::back::wgsl::write_string(&self.module, &self.module_info, WriterFlags::EXPLICIT_TYPES)
}
Expand Down
2 changes: 2 additions & 0 deletions docs/cargo_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ The default feature set enables most of the expected features of a game engine,
|minimp3|MP3 audio format support (through minimp3)|
|mp3|MP3 audio format support|
|serialize|Enable serialization support through serde|
|shader_format_glsl|Enable support for shaders in GLSL|
|shader_format_spirv|Enable support for shaders in SPIR-V|
|subpixel_glyph_atlas|Enable rendering of font glyphs using subpixel accuracy|
|symphonia-aac|AAC audio format support (through symphonia)|
|symphonia-all|AAC, FLAC, MP3, MP4, OGG/VORBIS, and WAV audio formats support (through symphonia)|
Expand Down