From 6135b21e7af55f2bfe0e488b0fa3369f41de6953 Mon Sep 17 00:00:00 2001 From: Jannik Obermann Date: Fri, 19 Jul 2024 17:53:32 +0200 Subject: [PATCH] Update for bevy 0.14 --- Cargo.lock | 63 +++- crates/compiler/Cargo.toml | 3 + crates/compiler/src/backend_v0_14/mod.rs | 214 +++++++++++++ crates/compiler/src/backend_v0_14/to_wgsl.rs | 307 +++++++++++++++++++ crates/compiler/src/backend_v0_14/util.rs | 108 +++++++ crates/compiler/src/download.rs | 19 ++ crates/compiler/src/lib.rs | 9 + crates/docs/src/lib.rs | 4 + crates/generator/src/lib.rs | 4 + crates/make/Cargo.toml | 1 + crates/make/src/main.rs | 54 ++++ 11 files changed, 779 insertions(+), 7 deletions(-) create mode 100644 crates/compiler/src/backend_v0_14/mod.rs create mode 100644 crates/compiler/src/backend_v0_14/to_wgsl.rs create mode 100644 crates/compiler/src/backend_v0_14/util.rs diff --git a/Cargo.lock b/Cargo.lock index 702b358..ddc1bee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "askama" version = "0.12.1" @@ -129,9 +135,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bumpalo" @@ -213,9 +219,11 @@ dependencies = [ "naga 0.13.0", "naga 0.14.1", "naga 0.19.2", + "naga 0.20.0", "naga_oil 0.10.1", "naga_oil 0.11.0", "naga_oil 0.13.0", + "naga_oil 0.14.0", "naga_oil 0.8.2", "regex", "reqwest", @@ -696,7 +704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ceaaa4eedaece7e4ec08c55c640ba03dbb73fb812a6570a59bcf1930d0f70e" dependencies = [ "bit-set", - "bitflags 2.4.1", + "bitflags 2.6.0", "codespan-reporting", "hexf-parse", "indexmap 1.9.3", @@ -716,7 +724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cd05939c491da968a42986204b7431678be21fdcd4b10cc84997ba130ada5a4" dependencies = [ "bit-set", - "bitflags 2.4.1", + "bitflags 2.6.0", "codespan-reporting", "hexf-parse", "indexmap 2.1.0", @@ -736,7 +744,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" dependencies = [ "bit-set", - "bitflags 2.4.1", + "bitflags 2.6.0", + "codespan-reporting", + "hexf-parse", + "indexmap 2.1.0", + "log", + "num-traits", + "pp-rs", + "rustc-hash", + "termcolor", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "naga" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.6.0", "codespan-reporting", "hexf-parse", "indexmap 2.1.0", @@ -829,6 +858,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "naga_oil" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "275d9720a7338eedac966141089232514c84d76a246a58ef501af88c5edf402f" +dependencies = [ + "bit-set", + "codespan-reporting", + "data-encoding", + "indexmap 2.1.0", + "naga 0.20.0", + "once_cell", + "regex", + "regex-syntax 0.8.2", + "rustc-hash", + "thiserror", + "tracing", + "unicode-ident", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -897,7 +946,7 @@ version = "0.10.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -1101,7 +1150,7 @@ version = "0.38.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", diff --git a/crates/compiler/Cargo.toml b/crates/compiler/Cargo.toml index 2ea7f7f..b23ac37 100644 --- a/crates/compiler/Cargo.toml +++ b/crates/compiler/Cargo.toml @@ -13,11 +13,13 @@ tar = "0.4.40" flate2 = "1.0.28" regex = "1.10.2" +naga_oil_v0_14 = { package = "naga_oil", version = "0.14.0", optional = true } naga_oil_v0_13 = { package = "naga_oil", version = "0.13.0", optional = true } naga_oil_v0_11 = { package = "naga_oil", version = "0.11.0", optional = true } naga_oil_v0_10 = { package = "naga_oil", version = "0.10.1", optional = true } naga_oil_v0_08 = { package = "naga_oil", version = "0.8.2", optional = true } +naga_v0_20 = { package = "naga", version = "0.20.0", optional = true } naga_v0_19 = { package = "naga", version = "0.19.2", optional = true } naga_v0_14 = { package = "naga", version = "0.14.1", optional = true } naga_v0_13 = { package = "naga", version = "0.13.0", optional = true } @@ -26,6 +28,7 @@ naga_v0_12 = { package = "naga", version = "0.12.3", optional = true } [features] default = [] +backend_v0_14 = ["dep:naga_oil_v0_14", "dep:naga_v0_20"] # bevy 0.14.x backend_v0_13 = ["dep:naga_oil_v0_13", "dep:naga_v0_19"] # bevy 0.13.x backend_v0_11 = ["dep:naga_oil_v0_11", "dep:naga_v0_14"] # ... backend_v0_10 = ["dep:naga_oil_v0_10", "dep:naga_v0_13"] # bevy 0.12.x diff --git a/crates/compiler/src/backend_v0_14/mod.rs b/crates/compiler/src/backend_v0_14/mod.rs new file mode 100644 index 0000000..872e371 --- /dev/null +++ b/crates/compiler/src/backend_v0_14/mod.rs @@ -0,0 +1,214 @@ +mod to_wgsl; +mod util; + +use naga_oil_v0_14 as naga_oil; +use naga_v0_20 as naga; + +use crate::{common, download::ShaderSource}; +use docs::*; +use naga::TypeInner; +use naga_oil::compose::{ + self, ComposableModuleDescriptor, Composer, ImportDefinition, NagaModuleDescriptor, + ShaderLanguage, +}; +use std::collections::HashMap; +use to_wgsl::build_ty; +use util::{build_address_space, build_binding, build_expression, build_resource_binding}; + +const NAGA_OIL_DECORATION_PRE: &str = "X_naga_oil_mod_X"; +const NAGA_OIL_DECORATION_POST: &str = "X"; + +pub fn compile( + root_crate_name: &str, + root_crate_version: Version, + shader_def_values: IndexMap, + shader_sources: Vec, +) -> Result> { + let (shaders, mut composer) = compile_shaders(shader_sources)?; + + let mut doc = common::doc_new( + root_crate_name.to_string(), + root_crate_version, + shader_def_values.clone(), + ); + + for (import_path, shader) in &shaders { + let (module_path, module) = common::find_or_create_module(&mut doc, import_path); + + module + .shader_defs + .extend(shader.source.shader_defs.iter().cloned()); + module.source_url = Some(shader.source.docsrs_url.clone()); + + let desc = NagaModuleDescriptor { + source: &shader.source.source, + shader_defs: shader_def_values + .iter() + .map(|(key, value)| { + ( + key.clone(), + match *value { + ShaderDefValue::Bool(value) => compose::ShaderDefValue::Bool(value), + ShaderDefValue::Int(value) => compose::ShaderDefValue::Int(value), + ShaderDefValue::UInt(value) => compose::ShaderDefValue::UInt(value), + }, + ) + }) + .collect(), + ..Default::default() + }; + let naga_module = composer.make_naga_module(desc)?; + let gctx = naga_module.to_ctx(); + + let mut def_paths = HashMap::new(); + for import in &shader.imports { + let module_path = import + .import + .split("::") + .map(str::to_owned) + .collect::>(); + for item in &import.items { + def_paths.insert(item.clone(), module_path.clone()); + } + } + for (_handle, ty) in naga_module.types.iter() { + if !contains_pre(ty.name.as_deref()) { + if let TypeInner::Struct { .. } = &ty.inner { + if let Some(name) = &ty.name { + def_paths.insert(name.clone(), module_path.clone()); + } + } + } + } + + for (_handle, constant) in naga_module.constants.iter() { + if !contains_pre(constant.name.as_deref()) { + module.constants.push(Constant { + name: Ident::from(constant.name.clone()), + ty: build_ty(&naga_module.types[constant.ty], gctx, &def_paths), + init: build_expression(&naga_module.global_expressions[constant.init]), + }); + } + } + + for (_handle, var) in naga_module.global_variables.iter() { + if !contains_pre(var.name.as_deref()) { + module.global_variables.push(GlobalVariable { + name: Ident::from(var.name.clone()), + space: build_address_space(&var.space), + binding: var.binding.as_ref().map(build_resource_binding), + ty: build_ty(&naga_module.types[var.ty], gctx, &def_paths), + init: var + .init + .map(|init| build_expression(&naga_module.global_expressions[init])), + }); + } + } + + for (_handle, ty) in naga_module.types.iter() { + if !contains_pre(ty.name.as_deref()) { + if let TypeInner::Struct { members, .. } = &ty.inner { + module.structs.push(Struct { + name: Ident::from(ty.name.clone()), + members: members + .iter() + .map(|member| StructMember { + name: Ident::from(member.name.clone()), + ty: build_ty(&naga_module.types[member.ty], gctx, &def_paths), + binding: member.binding.as_ref().map(build_binding), + }) + .collect(), + }); + } + } + } + + for (_handle, function) in naga_module.functions.iter() { + if !contains_pre(function.name.as_deref()) { + module.functions.push(Function { + name: Ident::from(function.name.clone()), + arguments: function + .arguments + .iter() + .map(|arg| FunctionArgument { + name: Ident::from(arg.name.clone()), + ty: build_ty(&naga_module.types[arg.ty], gctx, &def_paths), + binding: arg.binding.as_ref().map(build_binding), + }) + .collect(), + ret: function + .result + .as_ref() + .map(|res| build_ty(&naga_module.types[res.ty], gctx, &def_paths)), + }); + } + } + } + + Ok(doc) +} + +fn contains_pre(name: Option<&str>) -> bool { + name.map(|name| name.contains(NAGA_OIL_DECORATION_PRE)) + .unwrap_or(false) +} + +struct Shader { + source: ShaderSource, + imports: Vec, + defines: HashMap, +} + +fn compile_shaders( + shader_sources: Vec, +) -> Result<(HashMap, Composer), Box> { + let mut composer = Composer::default(); + let mut shaders = HashMap::new(); + + for shader_source in shader_sources { + let (import_path, imports, defines) = compose::get_preprocessor_data(&shader_source.source); + if let Some(import_path) = import_path { + shaders.insert( + import_path, + Shader { + source: shader_source, + imports, + defines, + }, + ); + } + } + + fn add_to_composer( + composer: &mut Composer, + name: &str, + shaders: &HashMap, + ) -> Result<(), Box> { + if !composer.contains_module(name) { + let this = match shaders.get(name) { + Some(this) => this, + None => return Err(format!("shader not found: {}", name).into()), + }; + + for import in &this.imports { + add_to_composer(composer, &import.import, shaders)?; + } + + composer.add_composable_module(ComposableModuleDescriptor { + source: &this.source.source, + file_path: &this.source.path.to_string_lossy(), + language: ShaderLanguage::Wgsl, + additional_imports: Default::default(), + shader_defs: this.defines.clone(), + as_name: None, + })?; + } + + Ok(()) + } + for name in shaders.keys() { + add_to_composer(&mut composer, name, &shaders)?; + } + + Ok((shaders, composer)) +} diff --git a/crates/compiler/src/backend_v0_14/to_wgsl.rs b/crates/compiler/src/backend_v0_14/to_wgsl.rs new file mode 100644 index 0000000..5a7e7d1 --- /dev/null +++ b/crates/compiler/src/backend_v0_14/to_wgsl.rs @@ -0,0 +1,307 @@ +use super::naga::{self, proc::GlobalCtx, Scalar, TypeInner}; +use super::{NAGA_OIL_DECORATION_POST, NAGA_OIL_DECORATION_PRE}; +use std::collections::HashMap; + +pub fn build_ty( + ty: &naga::Type, + gctx: GlobalCtx, + def_paths: &HashMap>, +) -> docs::Type { + match &ty.name { + Some(name) => { + let pre_pos = name.find(NAGA_OIL_DECORATION_PRE); + let ends_with_post = name.ends_with(NAGA_OIL_DECORATION_POST); + + let name = match (pre_pos, ends_with_post) { + (Some(pre_pos), true) => name[..pre_pos].to_string(), + _ => name.clone(), + }; + let def_path = def_paths.get(&name).cloned(); + docs::Type::Named { name, def_path } + } + None => build_ty_inner(&ty.inner, gctx, def_paths), + } +} + +// Copy-pasted and adapted from: naga-0.19.2 + +pub fn build_ty_inner( + type_inner: &TypeInner, + gctx: GlobalCtx, + def_paths: &HashMap>, +) -> docs::Type { + let name = match *type_inner { + TypeInner::Vector { size, scalar } => { + format!("vec{}<{}>", vector_size_str(size), scalar_kind_str(scalar),) + } + TypeInner::Sampler { comparison: false } => "sampler".to_string(), + TypeInner::Sampler { comparison: true } => "sampler_comparison".to_string(), + TypeInner::Image { + dim, + arrayed, + class, + } => { + // More about texture types: https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type + use naga::ImageClass as Ic; + + let dim_str = image_dimension_str(dim); + let arrayed_str = if arrayed { "_array" } else { "" }; + let (class_str, multisampled_str, format_str, storage_str) = match class { + Ic::Sampled { kind, multi } => ( + "", + if multi { "multisampled_" } else { "" }, + scalar_kind_str(naga::Scalar { kind, width: 4 }), + "", + ), + Ic::Depth { multi } => ("depth_", if multi { "multisampled_" } else { "" }, "", ""), + Ic::Storage { format, access } => ( + "storage_", + "", + storage_format_str(format), + if access.contains(naga::StorageAccess::LOAD | naga::StorageAccess::STORE) { + ",read_write" + } else if access.contains(naga::StorageAccess::LOAD) { + ",read" + } else { + ",write" + }, + ), + }; + let mut out = format!("texture_{class_str}{multisampled_str}{dim_str}{arrayed_str}"); + + if !format_str.is_empty() { + out += &format!("<{format_str}{storage_str}>"); + } + + out + } + TypeInner::Scalar(scalar) => scalar_kind_str(scalar).to_string(), + TypeInner::Atomic(scalar) => { + format!("atomic<{}>", scalar_kind_str(scalar)) + } + TypeInner::Array { + base, + size, + stride: _, + } => { + // More info https://gpuweb.github.io/gpuweb/wgsl/#array-types + // array -- Constant array + // array -- Dynamic array + let member_type = &gctx.types[base]; + return match size { + naga::ArraySize::Constant(size) => docs::Type::ArrayConstant( + Box::new(build_ty(member_type, gctx, def_paths)), + Some(size.get()), + ), + naga::ArraySize::Dynamic => { + docs::Type::ArrayDynamic(Box::new(build_ty(member_type, gctx, def_paths))) + } + }; + } + TypeInner::BindingArray { base, size } => { + // More info https://github.com/gpuweb/gpuweb/issues/2105 + let member_type = &gctx.types[base]; + return match size { + naga::ArraySize::Constant(size) => docs::Type::BindingArrayConstant( + Box::new(build_ty(member_type, gctx, def_paths)), + Some(size.get()), + ), + naga::ArraySize::Dynamic => docs::Type::BindingArrayDynamic(Box::new(build_ty( + member_type, + gctx, + def_paths, + ))), + }; + } + TypeInner::Matrix { + columns, + rows, + scalar, + } => { + format!( + "mat{}x{}<{}>", + vector_size_str(columns), + vector_size_str(rows), + scalar_kind_str(scalar) + ) + } + TypeInner::Pointer { base, space } => { + let (address, maybe_access) = address_space_str(space); + // Everything but `AddressSpace::Handle` gives us a `address` name, but + // Naga IR never produces pointers to handles, so it doesn't matter much + // how we write such a type. Just write it as the base type alone. + let base = build_ty(&gctx.types[base], gctx, def_paths); + return match address { + Some(address) => docs::Type::PointerWithAddressSpace { + base: Box::new(base), + address_space: address, + maybe_access, + }, + None => base, + }; + } + TypeInner::ValuePointer { + size, + scalar, + space, + } => { + let (address, maybe_access) = address_space_str(space); + let base = docs::Type::Named { + name: match size { + Some(size) => { + format!("vec{}<{}>", vector_size_str(size), scalar_kind_str(scalar)) + } + None => scalar_kind_str(scalar).to_string(), + }, + def_path: None, + }; + return match address { + Some(address) => docs::Type::PointerWithAddressSpace { + base: Box::new(base), + address_space: address, + maybe_access, + }, + None => base, + }; + } + TypeInner::AccelerationStructure => "acceleration_structure".to_string(), + TypeInner::Struct { .. } => { + // TODO: Actually output the struct? + "struct".to_string() + } + TypeInner::RayQuery { .. } => { + // TODO: ??? + "ray_query".to_string() + } + }; + + let def_path = def_paths.get(&name).cloned(); + docs::Type::Named { name, def_path } +} + +const fn vector_size_str(size: naga::VectorSize) -> &'static str { + match size { + naga::VectorSize::Bi => "2", + naga::VectorSize::Tri => "3", + naga::VectorSize::Quad => "4", + } +} + +const fn image_dimension_str(dim: naga::ImageDimension) -> &'static str { + use naga::ImageDimension as IDim; + + match dim { + IDim::D1 => "1d", + IDim::D2 => "2d", + IDim::D3 => "3d", + IDim::Cube => "cube", + } +} + +const fn scalar_kind_str(scalar: naga::Scalar) -> &'static str { + use naga::ScalarKind as Sk; + + match scalar { + Scalar { + kind: Sk::Float, + width: 8, + } => "f64", + Scalar { + kind: Sk::Float, + width: 4, + } => "f32", + Scalar { + kind: Sk::Sint, + width: 4, + } => "i32", + Scalar { + kind: Sk::Uint, + width: 4, + } => "u32", + Scalar { + kind: Sk::Sint, + width: 8, + } => "i64", + Scalar { + kind: Sk::Uint, + width: 8, + } => "u64", + Scalar { + kind: Sk::Bool, + width: 1, + } => "bool", + _ => unreachable!(), + } +} + +const fn storage_format_str(format: naga::StorageFormat) -> &'static str { + use naga::StorageFormat as Sf; + + match format { + Sf::R8Unorm => "r8unorm", + Sf::R8Snorm => "r8snorm", + Sf::R8Uint => "r8uint", + Sf::R8Sint => "r8sint", + Sf::R16Uint => "r16uint", + Sf::R16Sint => "r16sint", + Sf::R16Float => "r16float", + Sf::Rg8Unorm => "rg8unorm", + Sf::Rg8Snorm => "rg8snorm", + Sf::Rg8Uint => "rg8uint", + Sf::Rg8Sint => "rg8sint", + Sf::R32Uint => "r32uint", + Sf::R32Sint => "r32sint", + Sf::R32Float => "r32float", + Sf::Rg16Uint => "rg16uint", + Sf::Rg16Sint => "rg16sint", + Sf::Rg16Float => "rg16float", + Sf::Rgba8Unorm => "rgba8unorm", + Sf::Rgba8Snorm => "rgba8snorm", + Sf::Rgba8Uint => "rgba8uint", + Sf::Rgba8Sint => "rgba8sint", + Sf::Bgra8Unorm => "bgra8unorm", + Sf::Rgb10a2Uint => "rgb10a2uint", + Sf::Rgb10a2Unorm => "rgb10a2unorm", + Sf::Rg11b10Float => "rg11b10float", + Sf::Rg32Uint => "rg32uint", + Sf::Rg32Sint => "rg32sint", + Sf::Rg32Float => "rg32float", + Sf::Rgba16Uint => "rgba16uint", + Sf::Rgba16Sint => "rgba16sint", + Sf::Rgba16Float => "rgba16float", + Sf::Rgba32Uint => "rgba32uint", + Sf::Rgba32Sint => "rgba32sint", + Sf::Rgba32Float => "rgba32float", + Sf::R16Unorm => "r16unorm", + Sf::R16Snorm => "r16snorm", + Sf::Rg16Unorm => "rg16unorm", + Sf::Rg16Snorm => "rg16snorm", + Sf::Rgba16Unorm => "rgba16unorm", + Sf::Rgba16Snorm => "rgba16snorm", + } +} + +const fn address_space_str( + space: naga::AddressSpace, +) -> (Option<&'static str>, Option<&'static str>) { + use naga::AddressSpace as As; + + ( + Some(match space { + As::Private => "private", + As::Uniform => "uniform", + As::Storage { access } => { + if access.contains(naga::StorageAccess::STORE) { + return (Some("storage"), Some("read_write")); + } else { + "storage" + } + } + As::PushConstant => "push_constant", + As::WorkGroup => "workgroup", + As::Handle => return (None, None), + As::Function => "function", + }), + None, + ) +} diff --git a/crates/compiler/src/backend_v0_14/util.rs b/crates/compiler/src/backend_v0_14/util.rs new file mode 100644 index 0000000..0aeeb20 --- /dev/null +++ b/crates/compiler/src/backend_v0_14/util.rs @@ -0,0 +1,108 @@ +use super::naga; +use docs::{ + AddressSpace, Binding, BuiltIn, Expression, Interpolation, Literal, ResourceBinding, Sampling, +}; + +pub fn build_expression(expression: &naga::Expression) -> Expression { + match expression { + naga::Expression::Literal(lit) => Expression::Literal(match *lit { + naga::Literal::F64(v) => Literal::F64(v), + naga::Literal::F32(v) => Literal::F32(v), + naga::Literal::U32(v) => Literal::U32(v), + naga::Literal::I32(v) => Literal::I32(v), + naga::Literal::Bool(v) => Literal::Bool(v), + naga::Literal::I64(v) => Literal::I64(v), + naga::Literal::U64(v) => Literal::U64(v), + naga::Literal::AbstractInt(v) => Literal::AbstractInt(v), + naga::Literal::AbstractFloat(v) => Literal::AbstractFloat(v), + }), + _ => Expression::Unknown, + } +} + +pub fn build_resource_binding(binding: &naga::ResourceBinding) -> ResourceBinding { + ResourceBinding { + group: binding.group, + binding: binding.binding, + } +} + +pub fn build_address_space(address_space: &naga::AddressSpace) -> AddressSpace { + match address_space { + naga::AddressSpace::Function => AddressSpace::Function, + naga::AddressSpace::Private => AddressSpace::Private, + naga::AddressSpace::WorkGroup => AddressSpace::WorkGroup, + naga::AddressSpace::Uniform => AddressSpace::Uniform, + naga::AddressSpace::Storage { access } => AddressSpace::Storage { + load: access.contains(naga::StorageAccess::LOAD), + store: access.contains(naga::StorageAccess::STORE), + }, + naga::AddressSpace::Handle => AddressSpace::Handle, + naga::AddressSpace::PushConstant => AddressSpace::PushConstant, + } +} + +pub fn build_binding(binding: &naga::Binding) -> Binding { + match binding { + naga::Binding::BuiltIn(builtin) => Binding::BuiltIn(build_builtin(builtin)), + naga::Binding::Location { + location, + second_blend_source, + interpolation, + sampling, + } => Binding::Location { + location: *location, + second_blend_source: *second_blend_source, + interpolation: interpolation.as_ref().map(build_interpolation), + sampling: sampling.as_ref().map(build_sampling), + }, + } +} + +pub fn build_builtin(builtin: &naga::BuiltIn) -> BuiltIn { + match builtin { + naga::BuiltIn::Position { invariant } => BuiltIn::Position { + invariant: *invariant, + }, + naga::BuiltIn::ViewIndex => BuiltIn::ViewIndex, + naga::BuiltIn::BaseInstance => BuiltIn::BaseInstance, + naga::BuiltIn::BaseVertex => BuiltIn::BaseVertex, + naga::BuiltIn::ClipDistance => BuiltIn::ClipDistance, + naga::BuiltIn::CullDistance => BuiltIn::CullDistance, + naga::BuiltIn::InstanceIndex => BuiltIn::InstanceIndex, + naga::BuiltIn::PointSize => BuiltIn::PointSize, + naga::BuiltIn::VertexIndex => BuiltIn::VertexIndex, + naga::BuiltIn::FragDepth => BuiltIn::FragDepth, + naga::BuiltIn::PointCoord => BuiltIn::PointCoord, + naga::BuiltIn::FrontFacing => BuiltIn::FrontFacing, + naga::BuiltIn::PrimitiveIndex => BuiltIn::PrimitiveIndex, + naga::BuiltIn::SampleIndex => BuiltIn::SampleIndex, + naga::BuiltIn::SampleMask => BuiltIn::SampleMask, + naga::BuiltIn::GlobalInvocationId => BuiltIn::GlobalInvocationId, + naga::BuiltIn::LocalInvocationId => BuiltIn::LocalInvocationId, + naga::BuiltIn::LocalInvocationIndex => BuiltIn::LocalInvocationIndex, + naga::BuiltIn::WorkGroupId => BuiltIn::WorkGroupId, + naga::BuiltIn::WorkGroupSize => BuiltIn::WorkGroupSize, + naga::BuiltIn::NumWorkGroups => BuiltIn::NumWorkGroups, + naga::BuiltIn::NumSubgroups => BuiltIn::NumSubgroups, + naga::BuiltIn::SubgroupId => BuiltIn::SubgroupId, + naga::BuiltIn::SubgroupSize => BuiltIn::SubgroupSize, + naga::BuiltIn::SubgroupInvocationId => BuiltIn::SubgroupInvocationId, + } +} + +pub fn build_interpolation(interpolation: &naga::Interpolation) -> Interpolation { + match interpolation { + naga::Interpolation::Perspective => Interpolation::Perspective, + naga::Interpolation::Linear => Interpolation::Linear, + naga::Interpolation::Flat => Interpolation::Flat, + } +} + +pub fn build_sampling(sampling: &naga::Sampling) -> Sampling { + match sampling { + naga::Sampling::Center => Sampling::Center, + naga::Sampling::Centroid => Sampling::Centroid, + naga::Sampling::Sample => Sampling::Sample, + } +} diff --git a/crates/compiler/src/download.rs b/crates/compiler/src/download.rs index 5e6fcdd..7b84158 100644 --- a/crates/compiler/src/download.rs +++ b/crates/compiler/src/download.rs @@ -74,6 +74,25 @@ pub fn download_shaders( url }; + // Fixes: https://github.com/bevyengine/bevy/issues/14139 + let code = r#"fn hsv_to_rgb(hsv: vec3) -> vec3 { + let n = vec3(5.0, 3.0, 1.0); + let k = (n + hsv.x / FRAC_PI_3) % 6.0; + return hsv.z - hsv.z * hsv.y * max(vec3(0.0), min(k, min(4.0 - k, vec3(1.0)))); +}"#; + let source = if source.contains(code) { + source.replace( + code, + r#"fn hsv_to_rgb(x: f32, y: f32, z: f32) -> vec3 { + let n = vec3(5.0, 3.0, 1.0); + let k = (n + x / FRAC_PI_3) % 6.0; + return z - z * y * max(vec3(0.0), min(k, min(4.0 - k, vec3(1.0)))); +}"#, + ) + } else { + source + }; + shaders.push(ShaderSource { path, source, diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 9e3870b..0807007 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -2,6 +2,9 @@ mod common; mod download; mod post_process; +#[cfg(feature = "backend_v0_14")] +mod backend_v0_14; + #[cfg(feature = "backend_v0_13")] mod backend_v0_13; @@ -20,6 +23,8 @@ use std::path::Path; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum CompilerBackend { + #[cfg(feature = "backend_v0_14")] + V0_14, #[cfg(feature = "backend_v0_13")] V0_13, #[cfg(feature = "backend_v0_11")] @@ -33,6 +38,8 @@ pub enum CompilerBackend { impl CompilerBackend { fn naga_oil_minor(self) -> u64 { match self { + #[cfg(feature = "backend_v0_14")] + CompilerBackend::V0_14 => 14, #[cfg(feature = "backend_v0_13")] CompilerBackend::V0_13 => 13, #[cfg(feature = "backend_v0_11")] @@ -63,6 +70,8 @@ pub fn compile( // CompileFn type is necessary to avoid compiler error if no backend is enabled let compile: CompileFn = match backend { + #[cfg(feature = "backend_v0_14")] + CompilerBackend::V0_14 => backend_v0_14::compile, #[cfg(feature = "backend_v0_13")] CompilerBackend::V0_13 => backend_v0_13::compile, #[cfg(feature = "backend_v0_11")] diff --git a/crates/docs/src/lib.rs b/crates/docs/src/lib.rs index fe22740..6325dd2 100644 --- a/crates/docs/src/lib.rs +++ b/crates/docs/src/lib.rs @@ -231,6 +231,10 @@ pub enum BuiltIn { WorkGroupId, WorkGroupSize, NumWorkGroups, + NumSubgroups, + SubgroupId, + SubgroupSize, + SubgroupInvocationId, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/crates/generator/src/lib.rs b/crates/generator/src/lib.rs index 55fbc1a..abc797e 100644 --- a/crates/generator/src/lib.rs +++ b/crates/generator/src/lib.rs @@ -432,6 +432,10 @@ fn builtin_str(built_in: &BuiltIn) -> Result<&'static str, Box "local_invocation_index", BuiltIn::WorkGroupId => "workgroup_id", BuiltIn::NumWorkGroups => "num_workgroups", + BuiltIn::NumSubgroups => "num_subgroups", + BuiltIn::SubgroupId => "subgroup_id", + BuiltIn::SubgroupSize => "subgroup_size", + BuiltIn::SubgroupInvocationId => "subgroup_invocation_id", BuiltIn::BaseInstance | BuiltIn::BaseVertex | BuiltIn::ClipDistance diff --git a/crates/make/Cargo.toml b/crates/make/Cargo.toml index f07a7b5..6212bff 100644 --- a/crates/make/Cargo.toml +++ b/crates/make/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] docs = { path = "../docs" } compiler = { path = "../compiler", features = [ + "backend_v0_14", "backend_v0_13", "backend_v0_11", "backend_v0_10", diff --git a/crates/make/src/main.rs b/crates/make/src/main.rs index ff79126..324a377 100644 --- a/crates/make/src/main.rs +++ b/crates/make/src/main.rs @@ -5,6 +5,14 @@ fn main() -> Result<(), Box> { // Compile docs let cache_path = Path::new("target/shader_docs_cache"); let docs = vec![ + compiler::compile( + "bevy", + Version::new(0, 14, 0), + |name| name.starts_with("bevy"), + shader_def_values_0_14(), + cache_path, + compiler::CompilerBackend::V0_14, + )?, compiler::compile( "bevy", Version::new(0, 13, 2), @@ -87,6 +95,52 @@ fn main() -> Result<(), Box> { Ok(()) } +// TODO: More shader defs available on 0.14 +fn shader_def_values_0_14() -> IndexMap { + use ShaderDefValue::*; + + [ + ("AVAILABLE_STORAGE_BUFFER_BINDINGS", UInt(3)), + ("BLEND_MULTIPLY", Bool(true)), + ("BLEND_PREMULTIPLIED_ALPHA", Bool(true)), + ("DEFERRED_PREPASS", Bool(true)), + ("DEPTH_PREPASS", Bool(true)), + ("ENVIRONMENT_MAP", Bool(true)), + ("IRRADIANCE_VOLUME", Bool(true)), + ("IRRADIANCE_VOLUMES_ARE_USABLE", Bool(true)), + ("LIGHTMAP", Bool(true)), + ("MAX_CASCADES_PER_LIGHT", UInt(4)), + ("MAX_DIRECTIONAL_LIGHTS", UInt(10)), + ("MORPH_TARGETS", Bool(true)), + ("MOTION_VECTOR_PREPASS", Bool(true)), + // ("MULTISAMPLED", Bool(true)), causes error + ("NORMAL_PREPASS", Bool(true)), + ("NORMAL_PREPASS_OR_DEFERRED_PREPASS", Bool(true)), + ("PBR_TRANSMISSION_TEXTURES_SUPPORTED", Bool(true)), + ("PREPASS_FRAGMENT", Bool(true)), + ("PREPASS_PIPELINE", Bool(true)), + ("SKINNED", Bool(true)), + ("STANDARD_MATERIAL_CLEARCOAT", Bool(true)), + ("STANDARD_MATERIAL_DIFFUSE_TRANSMISSION", Bool(true)), + ("STANDARD_MATERIAL_NORMAL_MAP", Bool(true)), + ("STANDARD_MATERIAL_SPECULAR_TRANSMISSION", Bool(true)), + ("TONEMAPPING_LUT_SAMPLER_BINDING_INDEX", UInt(20)), + ("TONEMAPPING_LUT_TEXTURE_BINDING_INDEX", UInt(20)), + ("TONEMAP_METHOD_TONY_MC_MAPFACE", Bool(true)), + ("VERTEX_COLORS", Bool(true)), + ("VERTEX_NORMALS", Bool(true)), + ("VERTEX_OUTPUT_INSTANCE_INDEX", Bool(true)), + ("VERTEX_POSITIONS", Bool(true)), + ("VERTEX_TANGENTS", Bool(true)), + ("VERTEX_UVS", Bool(true)), + ("VERTEX_UVS_A", Bool(true)), + ("VERTEX_UVS_B", Bool(true)), + ] + .into_iter() + .map(|(key, value)| (key.to_string(), value)) + .collect() +} + fn shader_def_values_0_13() -> IndexMap { use ShaderDefValue::*;