feat(wgsl-in): create skeleton for enable directives

Co-Authored-By: FL33TW00D <chris@fleetwood.dev>
This commit is contained in:
Erich Gubler 2024-10-12 19:34:43 -04:00
parent 54861b712c
commit bf4cd9cd31
7 changed files with 185 additions and 21 deletions

View File

@ -85,7 +85,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
- Support local `const` declarations in WGSL. By @sagudev in [#6156](https://github.com/gfx-rs/wgpu/pull/6156). - Support local `const` declarations in WGSL. By @sagudev in [#6156](https://github.com/gfx-rs/wgpu/pull/6156).
- Implemented `const_assert` in WGSL. By @sagudev in [#6198](https://github.com/gfx-rs/wgpu/pull/6198). - Implemented `const_assert` in WGSL. By @sagudev in [#6198](https://github.com/gfx-rs/wgpu/pull/6198).
- Support polyfilling `inverse` in WGSL. By @chyyran in [#6385](https://github.com/gfx-rs/wgpu/pull/6385). - Support polyfilling `inverse` in WGSL. By @chyyran in [#6385](https://github.com/gfx-rs/wgpu/pull/6385).
- Add an internal skeleton for parsing `requires`, `enable`, and `diagnostic` directives that don't yet do anything besides emit nicer errors. By @ErichDonGubler in [#6352](https://github.com/gfx-rs/wgpu/pull/6352). - Add an internal skeleton for parsing `requires`, `enable`, and `diagnostic` directives that don't yet do anything besides emit nicer errors. By @ErichDonGubler in [#6352](https://github.com/gfx-rs/wgpu/pull/6352), [#6424](https://github.com/gfx-rs/wgpu/pull/6424).
- Include error chain information as a message and notes in shader compilation messages. By @ErichDonGubler in [#6436](https://github.com/gfx-rs/wgpu/pull/6436). - Include error chain information as a message and notes in shader compilation messages. By @ErichDonGubler in [#6436](https://github.com/gfx-rs/wgpu/pull/6436).
- Unify Naga CLI error output with the format of shader compilation messages. By @ErichDonGubler in [#6436](https://github.com/gfx-rs/wgpu/pull/6436). - Unify Naga CLI error output with the format of shader compilation messages. By @ErichDonGubler in [#6436](https://github.com/gfx-rs/wgpu/pull/6436).

View File

@ -1,3 +1,6 @@
use crate::front::wgsl::parse::directive::enable_extension::{
EnableExtension, UnimplementedEnableExtension,
};
use crate::front::wgsl::parse::directive::{DirectiveKind, UnimplementedDirectiveKind}; use crate::front::wgsl::parse::directive::{DirectiveKind, UnimplementedDirectiveKind};
use crate::front::wgsl::parse::lexer::Token; use crate::front::wgsl::parse::lexer::Token;
use crate::front::wgsl::Scalar; use crate::front::wgsl::Scalar;
@ -186,6 +189,7 @@ pub(crate) enum Error<'a> {
UnknownType(Span), UnknownType(Span),
UnknownStorageFormat(Span), UnknownStorageFormat(Span),
UnknownConservativeDepth(Span), UnknownConservativeDepth(Span),
UnknownEnableExtension(Span, &'a str),
SizeAttributeTooLow(Span, u32), SizeAttributeTooLow(Span, u32),
AlignAttributeTooLow(Span, Alignment), AlignAttributeTooLow(Span, Alignment),
NonPowerOfTwoAlignAttribute(Span), NonPowerOfTwoAlignAttribute(Span),
@ -275,6 +279,10 @@ pub(crate) enum Error<'a> {
DirectiveAfterFirstGlobalDecl { DirectiveAfterFirstGlobalDecl {
directive_span: Span, directive_span: Span,
}, },
EnableExtensionNotYetImplemented {
kind: UnimplementedEnableExtension,
span: Span,
},
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -525,6 +533,14 @@ impl<'a> Error<'a> {
labels: vec![(bad_span, "unknown type".into())], labels: vec![(bad_span, "unknown type".into())],
notes: vec![], notes: vec![],
}, },
Error::UnknownEnableExtension(span, word) => ParseError {
message: format!("unknown enable-extension `{}`", word),
labels: vec![(span, "".into())],
notes: vec![
"See available extensions at <https://www.w3.org/TR/WGSL/#enable-extension>."
.into(),
],
},
Error::SizeAttributeTooLow(bad_span, min_size) => ParseError { Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
message: format!("struct member size must be at least {min_size}"), message: format!("struct member size must be at least {min_size}"),
labels: vec![(bad_span, format!("must be at least {min_size}").into())], labels: vec![(bad_span, format!("must be at least {min_size}").into())],
@ -907,6 +923,28 @@ impl<'a> Error<'a> {
) )
.into()], .into()],
}, },
Error::EnableExtensionNotYetImplemented { kind, span } => ParseError {
message: format!(
"the `{}` extension is not yet supported",
EnableExtension::Unimplemented(kind).to_ident()
),
labels: vec![(
span,
concat!(
"this extension specifies standard functionality ",
"which is not yet implemented in Naga"
)
.into(),
)],
notes: vec![format!(
concat!(
"Let Naga maintainers know that you ran into this at ",
"<https://github.com/gfx-rs/wgpu/issues/{}>, ",
"so they can prioritize it!"
),
kind.tracking_issue_num()
)],
},
} }
} }
} }

View File

@ -1,3 +1,4 @@
use crate::front::wgsl::parse::directive::enable_extension::EnableExtensions;
use crate::front::wgsl::parse::number::Number; use crate::front::wgsl::parse::number::Number;
use crate::front::wgsl::Scalar; use crate::front::wgsl::Scalar;
use crate::{Arena, FastIndexSet, Handle, Span}; use crate::{Arena, FastIndexSet, Handle, Span};
@ -5,6 +6,7 @@ use std::hash::Hash;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TranslationUnit<'a> { pub struct TranslationUnit<'a> {
pub enable_extensions: EnableExtensions,
pub decls: Arena<GlobalDecl<'a>>, pub decls: Arena<GlobalDecl<'a>>,
/// The common expressions arena for the entire translation unit. /// The common expressions arena for the entire translation unit.
/// ///

View File

@ -2,9 +2,13 @@
//! //!
//! See also <https://www.w3.org/TR/WGSL/#directives>. //! See also <https://www.w3.org/TR/WGSL/#directives>.
pub mod enable_extension;
/// A parsed sentinel word indicating the type of directive to be parsed next. /// A parsed sentinel word indicating the type of directive to be parsed next.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum DirectiveKind { pub enum DirectiveKind {
/// An [`enable_extension`].
Enable,
Unimplemented(UnimplementedDirectiveKind), Unimplemented(UnimplementedDirectiveKind),
} }
@ -17,7 +21,7 @@ impl DirectiveKind {
pub fn from_ident(s: &str) -> Option<Self> { pub fn from_ident(s: &str) -> Option<Self> {
Some(match s { Some(match s {
Self::DIAGNOSTIC => Self::Unimplemented(UnimplementedDirectiveKind::Diagnostic), Self::DIAGNOSTIC => Self::Unimplemented(UnimplementedDirectiveKind::Diagnostic),
Self::ENABLE => Self::Unimplemented(UnimplementedDirectiveKind::Enable), Self::ENABLE => Self::Enable,
Self::REQUIRES => Self::Unimplemented(UnimplementedDirectiveKind::Requires), Self::REQUIRES => Self::Unimplemented(UnimplementedDirectiveKind::Requires),
_ => return None, _ => return None,
}) })
@ -26,9 +30,9 @@ impl DirectiveKind {
/// Maps this [`DirectiveKind`] into the sentinel word associated with it in WGSL. /// Maps this [`DirectiveKind`] into the sentinel word associated with it in WGSL.
pub const fn to_ident(self) -> &'static str { pub const fn to_ident(self) -> &'static str {
match self { match self {
Self::Enable => Self::ENABLE,
Self::Unimplemented(kind) => match kind { Self::Unimplemented(kind) => match kind {
UnimplementedDirectiveKind::Diagnostic => Self::DIAGNOSTIC, UnimplementedDirectiveKind::Diagnostic => Self::DIAGNOSTIC,
UnimplementedDirectiveKind::Enable => Self::ENABLE,
UnimplementedDirectiveKind::Requires => Self::REQUIRES, UnimplementedDirectiveKind::Requires => Self::REQUIRES,
}, },
} }
@ -47,7 +51,6 @@ impl DirectiveKind {
#[cfg_attr(test, derive(strum::EnumIter))] #[cfg_attr(test, derive(strum::EnumIter))]
pub enum UnimplementedDirectiveKind { pub enum UnimplementedDirectiveKind {
Diagnostic, Diagnostic,
Enable,
Requires, Requires,
} }
@ -56,7 +59,6 @@ impl UnimplementedDirectiveKind {
match self { match self {
Self::Diagnostic => 5320, Self::Diagnostic => 5320,
Self::Requires => 6350, Self::Requires => 6350,
Self::Enable => 5476,
} }
} }
} }
@ -86,19 +88,6 @@ error: `diagnostic` is not yet implemented
= note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5320>, so they can prioritize it! = note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5320>, so they can prioritize it!
";
}
UnimplementedDirectiveKind::Enable => {
shader = "enable f16;";
expected_msg = "\
error: `enable` is not yet implemented
wgsl:1:1
1 enable f16;
^^^^^^ this global directive is standard, but not yet implemented
= note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5476>, so they can prioritize it!
"; ";
} }
UnimplementedDirectiveKind::Requires => { UnimplementedDirectiveKind::Requires => {
@ -139,7 +128,7 @@ error: expected global declaration, but found a global directive
"; ";
} }
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Enable) => { DirectiveKind::Enable => {
directive = "enable f16"; directive = "enable f16";
expected_msg = "\ expected_msg = "\
error: expected global declaration, but found a global directive error: expected global declaration, but found a global directive

View File

@ -0,0 +1,112 @@
//! `enable …;` extensions in WGSL.
//!
//! The focal point of this module is the [`EnableExtension`] API.
use crate::{front::wgsl::error::Error, Span};
/// Tracks the status of every enable extension known to Naga.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EnableExtensions {}
impl EnableExtensions {
pub(crate) const fn empty() -> Self {
Self {}
}
/// Add an enable extension to the set requested by a module.
#[allow(unreachable_code)]
pub(crate) fn add(&mut self, ext: ImplementedEnableExtension) {
let _field: &mut bool = match ext {};
*_field = true;
}
/// Query whether an enable extension tracked here has been requested.
#[allow(unused)]
pub(crate) const fn contains(&self, ext: ImplementedEnableExtension) -> bool {
match ext {}
}
}
impl Default for EnableExtensions {
fn default() -> Self {
Self::empty()
}
}
/// A shader language extension not guaranteed to be present in all environments.
///
/// WGSL spec.: <https://www.w3.org/TR/WGSL/#enable-extensions-sec>
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum EnableExtension {
#[allow(unused)]
Implemented(ImplementedEnableExtension),
Unimplemented(UnimplementedEnableExtension),
}
impl EnableExtension {
const F16: &'static str = "f16";
const CLIP_DISTANCES: &'static str = "clip_distances";
const DUAL_SOURCE_BLENDING: &'static str = "dual_source_blending";
/// Convert from a sentinel word in WGSL into its associated [`EnableExtension`], if possible.
pub(crate) fn from_ident(word: &str, span: Span) -> Result<Self, Error<'_>> {
Ok(match word {
Self::F16 => Self::Unimplemented(UnimplementedEnableExtension::F16),
Self::CLIP_DISTANCES => {
Self::Unimplemented(UnimplementedEnableExtension::ClipDistances)
}
Self::DUAL_SOURCE_BLENDING => {
Self::Unimplemented(UnimplementedEnableExtension::DualSourceBlending)
}
_ => return Err(Error::UnknownEnableExtension(span, word)),
})
}
/// Maps this [`EnableExtension`] into the sentinel word associated with it in WGSL.
pub const fn to_ident(self) -> &'static str {
match self {
Self::Implemented(kind) => match kind {},
Self::Unimplemented(kind) => match kind {
UnimplementedEnableExtension::F16 => Self::F16,
UnimplementedEnableExtension::ClipDistances => Self::CLIP_DISTANCES,
UnimplementedEnableExtension::DualSourceBlending => Self::DUAL_SOURCE_BLENDING,
},
}
}
}
/// A variant of [`EnableExtension::Implemented`].
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum ImplementedEnableExtension {}
/// A variant of [`EnableExtension::Unimplemented`].
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum UnimplementedEnableExtension {
/// Enables `f16`/`half` primitive support in all shader languages.
///
/// In the WGSL standard, this corresponds to [`enable f16;`].
///
/// [`enable f16;`]: https://www.w3.org/TR/WGSL/#extension-f16
F16,
/// Enables the `clip_distances` variable in WGSL.
///
/// In the WGSL standard, this corresponds to [`enable clip_distances;`].
///
/// [`enable clip_distances;`]: https://www.w3.org/TR/WGSL/#extension-f16
ClipDistances,
/// Enables the `blend_src` attribute in WGSL.
///
/// In the WGSL standard, this corresponds to [`enable dual_source_blending;`].
///
/// [`enable dual_source_blending;`]: https://www.w3.org/TR/WGSL/#extension-f16
DualSourceBlending,
}
impl UnimplementedEnableExtension {
pub(crate) const fn tracking_issue_num(self) -> u16 {
match self {
Self::F16 => 4384,
Self::ClipDistances => 6236,
Self::DualSourceBlending => 6402,
}
}
}

View File

@ -1,5 +1,6 @@
use super::{number::consume_number, Error, ExpectedToken}; use super::{number::consume_number, Error, ExpectedToken};
use crate::front::wgsl::error::NumberError; use crate::front::wgsl::error::NumberError;
use crate::front::wgsl::parse::directive::enable_extension::EnableExtensions;
use crate::front::wgsl::parse::{conv, Number}; use crate::front::wgsl::parse::{conv, Number};
use crate::front::wgsl::Scalar; use crate::front::wgsl::Scalar;
use crate::Span; use crate::Span;
@ -204,6 +205,8 @@ pub(in crate::front::wgsl) struct Lexer<'a> {
pub(in crate::front::wgsl) source: &'a str, pub(in crate::front::wgsl) source: &'a str,
// The byte offset of the end of the last non-trivia token. // The byte offset of the end of the last non-trivia token.
last_end_offset: usize, last_end_offset: usize,
#[allow(dead_code)]
pub(in crate::front::wgsl) enable_extensions: EnableExtensions,
} }
impl<'a> Lexer<'a> { impl<'a> Lexer<'a> {
@ -212,6 +215,7 @@ impl<'a> Lexer<'a> {
input, input,
source: input, source: input,
last_end_offset: 0, last_end_offset: 0,
enable_extensions: EnableExtensions::empty(),
} }
} }

View File

@ -1,4 +1,5 @@
use crate::front::wgsl::error::{Error, ExpectedToken}; use crate::front::wgsl::error::{Error, ExpectedToken};
use crate::front::wgsl::parse::directive::enable_extension::{EnableExtension, EnableExtensions};
use crate::front::wgsl::parse::directive::DirectiveKind; use crate::front::wgsl::parse::directive::DirectiveKind;
use crate::front::wgsl::parse::lexer::{Lexer, Token}; use crate::front::wgsl::parse::lexer::{Lexer, Token};
use crate::front::wgsl::parse::number::Number; use crate::front::wgsl::parse::number::Number;
@ -2258,7 +2259,6 @@ impl Parser {
Ok(fun) Ok(fun)
} }
#[allow(unused)]
fn directive_ident_list<'a>( fn directive_ident_list<'a>(
&self, &self,
lexer: &mut Lexer<'a>, lexer: &mut Lexer<'a>,
@ -2510,14 +2510,30 @@ impl Parser {
let mut lexer = Lexer::new(source); let mut lexer = Lexer::new(source);
let mut tu = ast::TranslationUnit::default(); let mut tu = ast::TranslationUnit::default();
let mut enable_extensions = EnableExtensions::empty();
// Parse directives. // Parse directives.
#[allow(clippy::never_loop, unreachable_code)]
while let Ok((ident, span)) = lexer.peek_ident_with_span() { while let Ok((ident, span)) = lexer.peek_ident_with_span() {
if let Some(kind) = DirectiveKind::from_ident(ident) { if let Some(kind) = DirectiveKind::from_ident(ident) {
self.push_rule_span(Rule::Directive, &mut lexer); self.push_rule_span(Rule::Directive, &mut lexer);
let _ = lexer.next_ident_with_span().unwrap(); let _ = lexer.next_ident_with_span().unwrap();
match kind { match kind {
DirectiveKind::Enable => {
self.directive_ident_list(&mut lexer, |ident, span| {
let kind = EnableExtension::from_ident(ident, span)?;
let extension = match kind {
EnableExtension::Implemented(kind) => kind,
EnableExtension::Unimplemented(kind) => {
return Err(Error::EnableExtensionNotYetImplemented {
kind,
span,
})
}
};
enable_extensions.add(extension);
Ok(())
})?;
}
DirectiveKind::Unimplemented(kind) => { DirectiveKind::Unimplemented(kind) => {
return Err(Error::DirectiveNotYetImplemented { kind, span }) return Err(Error::DirectiveNotYetImplemented { kind, span })
} }
@ -2528,6 +2544,9 @@ impl Parser {
} }
} }
lexer.enable_extensions = enable_extensions.clone();
tu.enable_extensions = enable_extensions;
loop { loop {
match self.global_decl(&mut lexer, &mut tu) { match self.global_decl(&mut lexer, &mut tu) {
Err(error) => return Err(error), Err(error) => return Err(error),