mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
glsl-in: Type qualifiers rework (#1713)
The previous implementation had many issues, most importantly it didn't allow to implement (at least in a sane way) some future features like default qualifiers and format qualifiers. This implementations centralizes the qualifiers into a struct with a hashmap for layout qualifiers, error reporting is also centralized which means that `add_global_var` and `add_local_var` no longer need to duplicate effort and the ugly loop + match combo is gone. Finally some minor fixes are also done as part of this rework, like a default location being provided for input/outputs variables and layout qualifiers being allowed to be repeated (overwriting the previous one) like the spec defines.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::fmt;
|
||||
use std::{borrow::Cow, fmt};
|
||||
|
||||
use super::{builtins::MacroCall, context::ExprPos, Span};
|
||||
use crate::{
|
||||
@@ -134,19 +134,118 @@ pub enum HirExprKind {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
pub enum QualifierKey<'a> {
|
||||
String(Cow<'a, str>),
|
||||
/// Used for `std140` and `std430` layout qualifiers
|
||||
Layout,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TypeQualifier {
|
||||
StorageQualifier(StorageQualifier),
|
||||
Interpolation(Interpolation),
|
||||
Set(u32),
|
||||
Binding(u32),
|
||||
Location(u32),
|
||||
WorkGroupSize(usize, u32),
|
||||
Sampling(Sampling),
|
||||
pub enum QualifierValue {
|
||||
None,
|
||||
Uint(u32),
|
||||
Layout(StructLayout),
|
||||
Precision(Precision),
|
||||
EarlyFragmentTests,
|
||||
StorageAccess(StorageAccess),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TypeQualifiers<'a> {
|
||||
pub span: Span,
|
||||
pub storage: (StorageQualifier, Span),
|
||||
pub interpolation: Option<(Interpolation, Span)>,
|
||||
pub precision: Option<(Precision, Span)>,
|
||||
pub sampling: Option<(Sampling, Span)>,
|
||||
pub storage_acess: Option<(StorageAccess, Span)>,
|
||||
pub layout_qualifiers: crate::FastHashMap<QualifierKey<'a>, (QualifierValue, Span)>,
|
||||
}
|
||||
|
||||
impl<'a> TypeQualifiers<'a> {
|
||||
/// Appends `errors` with errors for all unused qualifiers
|
||||
pub fn unused_errors(&self, errors: &mut Vec<super::Error>) {
|
||||
if let Some((_, meta)) = self.interpolation {
|
||||
errors.push(super::Error {
|
||||
kind: super::ErrorKind::SemanticError(
|
||||
"Interpolation qualifiers can only be used in in/out variables".into(),
|
||||
),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some((_, meta)) = self.sampling {
|
||||
errors.push(super::Error {
|
||||
kind: super::ErrorKind::SemanticError(
|
||||
"Sampling qualifiers can only be used in in/out variables".into(),
|
||||
),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some((_, meta)) = self.storage_acess {
|
||||
errors.push(super::Error {
|
||||
kind: super::ErrorKind::SemanticError(
|
||||
"Memory qualifiers can only be used in storage variables".into(),
|
||||
),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
|
||||
for &(_, meta) in self.layout_qualifiers.values() {
|
||||
errors.push(super::Error {
|
||||
kind: super::ErrorKind::SemanticError("Unexpected qualifier".into()),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't
|
||||
/// a [`QualifierValue::Uint`]
|
||||
pub fn uint_layout_qualifier(
|
||||
&mut self,
|
||||
name: &'a str,
|
||||
errors: &mut Vec<super::Error>,
|
||||
) -> Option<u32> {
|
||||
match self
|
||||
.layout_qualifiers
|
||||
.remove(&QualifierKey::String(name.into()))
|
||||
{
|
||||
Some((QualifierValue::Uint(v), _)) => Some(v),
|
||||
Some((_, meta)) => {
|
||||
errors.push(super::Error {
|
||||
kind: super::ErrorKind::SemanticError("Qualifier expects a uint value".into()),
|
||||
meta,
|
||||
});
|
||||
// Return a dummy value instead of `None` to differentiate from
|
||||
// the qualifier not existing, since some parts might require the
|
||||
// qualifier to exist and throwing another error that it doesn't
|
||||
// exist would be unhelpful
|
||||
Some(0)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't
|
||||
/// a [`QualifierValue::None`]
|
||||
pub fn none_layout_qualifier(&mut self, name: &'a str, errors: &mut Vec<super::Error>) -> bool {
|
||||
match self
|
||||
.layout_qualifiers
|
||||
.remove(&QualifierKey::String(name.into()))
|
||||
{
|
||||
Some((QualifierValue::None, _)) => true,
|
||||
Some((_, meta)) => {
|
||||
errors.push(super::Error {
|
||||
kind: super::ErrorKind::SemanticError(
|
||||
"Qualifier doesn't expect a value".into(),
|
||||
),
|
||||
meta,
|
||||
});
|
||||
// Return a `true` to since the qualifier is defined and adding
|
||||
// another error for it not being defined would be unhelpful
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -169,6 +268,12 @@ pub enum StorageQualifier {
|
||||
Const,
|
||||
}
|
||||
|
||||
impl Default for StorageQualifier {
|
||||
fn default() -> Self {
|
||||
StorageQualifier::AddressSpace(AddressSpace::Function)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StructLayout {
|
||||
Std140,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::{
|
||||
ast::{FunctionKind, Profile, TypeQualifier},
|
||||
ast::{FunctionKind, Profile, TypeQualifiers},
|
||||
context::{Context, ExprPos},
|
||||
error::ExpectedToken,
|
||||
error::{Error, ErrorKind},
|
||||
@@ -366,15 +366,15 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeclarationContext<'ctx> {
|
||||
qualifiers: Vec<(TypeQualifier, Span)>,
|
||||
pub struct DeclarationContext<'ctx, 'qualifiers> {
|
||||
qualifiers: TypeQualifiers<'qualifiers>,
|
||||
external: bool,
|
||||
|
||||
ctx: &'ctx mut Context,
|
||||
body: &'ctx mut Block,
|
||||
}
|
||||
|
||||
impl<'ctx> DeclarationContext<'ctx> {
|
||||
impl<'ctx, 'qualifiers> DeclarationContext<'ctx, 'qualifiers> {
|
||||
fn add_var(
|
||||
&mut self,
|
||||
parser: &mut Parser,
|
||||
@@ -384,7 +384,7 @@ impl<'ctx> DeclarationContext<'ctx> {
|
||||
meta: Span,
|
||||
) -> Result<Handle<Expression>> {
|
||||
let decl = VarDeclaration {
|
||||
qualifiers: &self.qualifiers,
|
||||
qualifiers: &mut self.qualifiers,
|
||||
ty,
|
||||
name: Some(name),
|
||||
init,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
front::glsl::{
|
||||
ast::{
|
||||
GlobalLookup, GlobalLookupKind, Precision, StorageQualifier, StructLayout,
|
||||
TypeQualifier,
|
||||
GlobalLookup, GlobalLookupKind, Precision, QualifierKey, QualifierValue,
|
||||
StorageQualifier, StructLayout, TypeQualifiers,
|
||||
},
|
||||
context::{Context, ExprPos},
|
||||
error::ExpectedToken,
|
||||
@@ -249,7 +249,7 @@ impl<'source> ParsingContext<'source> {
|
||||
// type_qualifier IDENTIFIER identifier_list SEMICOLON
|
||||
|
||||
if self.peek_type_qualifier(parser) || self.peek_type_name(parser) {
|
||||
let qualifiers = self.parse_type_qualifiers(parser)?;
|
||||
let mut qualifiers = self.parse_type_qualifiers(parser)?;
|
||||
|
||||
if self.peek_type_name(parser) {
|
||||
// This branch handles variables and function prototypes and if
|
||||
@@ -361,7 +361,7 @@ impl<'source> ParsingContext<'source> {
|
||||
parser,
|
||||
ctx,
|
||||
body,
|
||||
&qualifiers,
|
||||
&mut qualifiers,
|
||||
ty_name,
|
||||
token.meta,
|
||||
)
|
||||
@@ -377,33 +377,28 @@ impl<'source> ParsingContext<'source> {
|
||||
}
|
||||
}
|
||||
TokenValue::Semicolon => {
|
||||
let mut meta_all = token.meta;
|
||||
for &(ref qualifier, meta) in qualifiers.iter() {
|
||||
meta_all.subsume(meta);
|
||||
match *qualifier {
|
||||
TypeQualifier::WorkGroupSize(i, value) => {
|
||||
parser.meta.workgroup_size[i] = value
|
||||
}
|
||||
TypeQualifier::EarlyFragmentTests => {
|
||||
parser.meta.early_fragment_tests = true;
|
||||
}
|
||||
TypeQualifier::StorageQualifier(_) => {
|
||||
// TODO: Maybe add some checks here
|
||||
// This is needed because of cases like
|
||||
// layout(early_fragment_tests) in;
|
||||
}
|
||||
_ => {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"Qualifier not supported as standalone".into(),
|
||||
),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(value) =
|
||||
qualifiers.uint_layout_qualifier("local_size_x", &mut parser.errors)
|
||||
{
|
||||
parser.meta.workgroup_size[0] = value;
|
||||
}
|
||||
if let Some(value) =
|
||||
qualifiers.uint_layout_qualifier("local_size_y", &mut parser.errors)
|
||||
{
|
||||
parser.meta.workgroup_size[1] = value;
|
||||
}
|
||||
if let Some(value) =
|
||||
qualifiers.uint_layout_qualifier("local_size_z", &mut parser.errors)
|
||||
{
|
||||
parser.meta.workgroup_size[2] = value;
|
||||
}
|
||||
|
||||
Ok(Some(meta_all))
|
||||
parser.meta.early_fragment_tests |= qualifiers
|
||||
.none_layout_qualifier("early_fragment_tests", &mut parser.errors);
|
||||
|
||||
qualifiers.unused_errors(&mut parser.errors);
|
||||
|
||||
Ok(Some(qualifiers.span))
|
||||
}
|
||||
_ => Err(Error {
|
||||
kind: ErrorKind::InvalidToken(
|
||||
@@ -471,27 +466,22 @@ impl<'source> ParsingContext<'source> {
|
||||
parser: &mut Parser,
|
||||
ctx: &mut Context,
|
||||
body: &mut Block,
|
||||
qualifiers: &[(TypeQualifier, Span)],
|
||||
qualifiers: &mut TypeQualifiers,
|
||||
ty_name: String,
|
||||
meta: Span,
|
||||
) -> Result<Span> {
|
||||
let mut storage = None;
|
||||
let mut layout = None;
|
||||
|
||||
for &(ref qualifier, _) in qualifiers {
|
||||
match *qualifier {
|
||||
TypeQualifier::StorageQualifier(StorageQualifier::AddressSpace(c)) => {
|
||||
storage = Some(c)
|
||||
let layout = match qualifiers.layout_qualifiers.remove(&QualifierKey::Layout) {
|
||||
Some((QualifierValue::Layout(l), _)) => l,
|
||||
None => {
|
||||
if let StorageQualifier::AddressSpace(AddressSpace::Storage { .. }) =
|
||||
qualifiers.storage.0
|
||||
{
|
||||
StructLayout::Std430
|
||||
} else {
|
||||
StructLayout::Std140
|
||||
}
|
||||
TypeQualifier::Layout(l) => layout = Some(l),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
let layout = match (layout, storage) {
|
||||
(Some(layout), _) => layout,
|
||||
(None, Some(AddressSpace::Storage { .. })) => StructLayout::Std430,
|
||||
_ => StructLayout::Std140,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut members = Vec::new();
|
||||
|
||||
@@ -396,7 +396,7 @@ impl<'source> ParsingContext<'source> {
|
||||
if self.bump_if(parser, TokenValue::Semicolon).is_none() {
|
||||
let (expr, expr_meta) =
|
||||
if self.peek_type_name(parser) || self.peek_type_qualifier(parser) {
|
||||
let qualifiers = self.parse_type_qualifiers(parser)?;
|
||||
let mut qualifiers = self.parse_type_qualifiers(parser)?;
|
||||
let (ty, mut meta) = self.parse_type_non_void(parser)?;
|
||||
let name = self.expect_ident(parser)?.0;
|
||||
|
||||
@@ -407,7 +407,7 @@ impl<'source> ParsingContext<'source> {
|
||||
meta.subsume(end_meta);
|
||||
|
||||
let decl = VarDeclaration {
|
||||
qualifiers: &qualifiers,
|
||||
qualifiers: &mut qualifiers,
|
||||
ty,
|
||||
name: Some(name),
|
||||
init: None,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
front::glsl::{
|
||||
ast::{StorageQualifier, StructLayout, TypeQualifier},
|
||||
ast::{QualifierKey, QualifierValue, StorageQualifier, StructLayout, TypeQualifiers},
|
||||
error::ExpectedToken,
|
||||
parser::ParsingContext,
|
||||
token::{Token, TokenValue},
|
||||
@@ -128,11 +128,8 @@ impl<'source> ParsingContext<'source> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_type_qualifiers(
|
||||
&mut self,
|
||||
parser: &mut Parser,
|
||||
) -> Result<Vec<(TypeQualifier, Span)>> {
|
||||
let mut qualifiers = Vec::new();
|
||||
pub fn parse_type_qualifiers<'a>(&mut self, parser: &mut Parser) -> Result<TypeQualifiers<'a>> {
|
||||
let mut qualifiers = TypeQualifiers::default();
|
||||
|
||||
while self.peek_type_qualifier(parser) {
|
||||
let token = self.bump(parser)?;
|
||||
@@ -143,31 +140,104 @@ impl<'source> ParsingContext<'source> {
|
||||
continue;
|
||||
}
|
||||
|
||||
qualifiers.push((
|
||||
match token.value {
|
||||
TokenValue::Interpolation(i) => TypeQualifier::Interpolation(i),
|
||||
TokenValue::Const => TypeQualifier::StorageQualifier(StorageQualifier::Const),
|
||||
TokenValue::In => TypeQualifier::StorageQualifier(StorageQualifier::Input),
|
||||
TokenValue::Out => TypeQualifier::StorageQualifier(StorageQualifier::Output),
|
||||
TokenValue::Uniform => TypeQualifier::StorageQualifier(
|
||||
StorageQualifier::AddressSpace(AddressSpace::Uniform),
|
||||
),
|
||||
TokenValue::Shared => TypeQualifier::StorageQualifier(
|
||||
StorageQualifier::AddressSpace(AddressSpace::WorkGroup),
|
||||
),
|
||||
TokenValue::Buffer => TypeQualifier::StorageQualifier(
|
||||
StorageQualifier::AddressSpace(AddressSpace::Storage {
|
||||
access: crate::StorageAccess::default(),
|
||||
}),
|
||||
),
|
||||
TokenValue::Sampling(s) => TypeQualifier::Sampling(s),
|
||||
TokenValue::PrecisionQualifier(p) => TypeQualifier::Precision(p),
|
||||
TokenValue::StorageAccess(access) => TypeQualifier::StorageAccess(access),
|
||||
TokenValue::Restrict => continue,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
token.meta,
|
||||
))
|
||||
qualifiers.span.subsume(token.meta);
|
||||
|
||||
match token.value {
|
||||
TokenValue::Interpolation(i) => {
|
||||
if qualifiers.interpolation.is_some() {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"Cannot use more than one interpolation qualifier per declaration"
|
||||
.into(),
|
||||
),
|
||||
meta: token.meta,
|
||||
})
|
||||
}
|
||||
|
||||
qualifiers.interpolation = Some((i, token.meta));
|
||||
}
|
||||
TokenValue::Const
|
||||
| TokenValue::In
|
||||
| TokenValue::Out
|
||||
| TokenValue::Uniform
|
||||
| TokenValue::Shared
|
||||
| TokenValue::Buffer => {
|
||||
let storage = match token.value {
|
||||
TokenValue::Const => StorageQualifier::Const,
|
||||
TokenValue::In => StorageQualifier::Input,
|
||||
TokenValue::Out => StorageQualifier::Output,
|
||||
TokenValue::Uniform => {
|
||||
StorageQualifier::AddressSpace(AddressSpace::Uniform)
|
||||
}
|
||||
TokenValue::Shared => {
|
||||
StorageQualifier::AddressSpace(AddressSpace::WorkGroup)
|
||||
}
|
||||
TokenValue::Buffer => {
|
||||
StorageQualifier::AddressSpace(AddressSpace::Storage {
|
||||
access: crate::StorageAccess::all(),
|
||||
})
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if StorageQualifier::AddressSpace(AddressSpace::Function)
|
||||
!= qualifiers.storage.0
|
||||
{
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"Cannot use more than one storage qualifier per declaration".into(),
|
||||
),
|
||||
meta: token.meta,
|
||||
});
|
||||
}
|
||||
|
||||
qualifiers.storage = (storage, token.meta);
|
||||
}
|
||||
TokenValue::Sampling(s) => {
|
||||
if qualifiers.sampling.is_some() {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"Cannot use more than one sampling qualifier per declaration"
|
||||
.into(),
|
||||
),
|
||||
meta: token.meta,
|
||||
})
|
||||
}
|
||||
|
||||
qualifiers.sampling = Some((s, token.meta));
|
||||
}
|
||||
TokenValue::PrecisionQualifier(p) => {
|
||||
if qualifiers.interpolation.is_some() {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"Cannot use more than one precision qualifier per declaration"
|
||||
.into(),
|
||||
),
|
||||
meta: token.meta,
|
||||
})
|
||||
}
|
||||
|
||||
qualifiers.precision = Some((p, token.meta));
|
||||
}
|
||||
TokenValue::StorageAccess(access) => {
|
||||
let storage_access = qualifiers
|
||||
.storage_acess
|
||||
.get_or_insert((crate::StorageAccess::empty(), Span::default()));
|
||||
if storage_access.0.contains(access) {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"The same memory qualifier can only be used once".into(),
|
||||
),
|
||||
meta: token.meta,
|
||||
})
|
||||
}
|
||||
|
||||
storage_access.0 |= access;
|
||||
storage_access.1.subsume(token.meta);
|
||||
}
|
||||
TokenValue::Restrict => continue,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(qualifiers)
|
||||
@@ -176,11 +246,11 @@ impl<'source> ParsingContext<'source> {
|
||||
pub fn parse_layout_qualifier_id_list(
|
||||
&mut self,
|
||||
parser: &mut Parser,
|
||||
qualifiers: &mut Vec<(TypeQualifier, Span)>,
|
||||
qualifiers: &mut TypeQualifiers,
|
||||
) -> Result<()> {
|
||||
self.expect(parser, TokenValue::LeftParen)?;
|
||||
loop {
|
||||
self.parse_layout_qualifier_id(parser, qualifiers)?;
|
||||
self.parse_layout_qualifier_id(parser, &mut qualifiers.layout_qualifiers)?;
|
||||
|
||||
if self.bump_if(parser, TokenValue::Comma).is_some() {
|
||||
continue;
|
||||
@@ -188,7 +258,8 @@ impl<'source> ParsingContext<'source> {
|
||||
|
||||
break;
|
||||
}
|
||||
self.expect(parser, TokenValue::RightParen)?;
|
||||
let token = self.expect(parser, TokenValue::RightParen)?;
|
||||
qualifiers.span.subsume(token.meta);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -196,7 +267,7 @@ impl<'source> ParsingContext<'source> {
|
||||
pub fn parse_layout_qualifier_id(
|
||||
&mut self,
|
||||
parser: &mut Parser,
|
||||
qualifiers: &mut Vec<(TypeQualifier, Span)>,
|
||||
qualifiers: &mut crate::FastHashMap<QualifierKey, (QualifierValue, Span)>,
|
||||
) -> Result<()> {
|
||||
// layout_qualifier_id:
|
||||
// IDENTIFIER
|
||||
@@ -205,60 +276,38 @@ impl<'source> ParsingContext<'source> {
|
||||
let mut token = self.bump(parser)?;
|
||||
match token.value {
|
||||
TokenValue::Identifier(name) => {
|
||||
if self.bump_if(parser, TokenValue::Assign).is_some() {
|
||||
let (value, end_meta) = self.parse_uint_constant(parser)?;
|
||||
token.meta.subsume(end_meta);
|
||||
let (key, value) = match name.as_str() {
|
||||
"std140" => (
|
||||
QualifierKey::Layout,
|
||||
QualifierValue::Layout(StructLayout::Std140),
|
||||
),
|
||||
"std430" => (
|
||||
QualifierKey::Layout,
|
||||
QualifierValue::Layout(StructLayout::Std430),
|
||||
),
|
||||
_ => {
|
||||
let key = QualifierKey::String(name.into());
|
||||
let value = if self.bump_if(parser, TokenValue::Assign).is_some() {
|
||||
let (value, end_meta) = match self.parse_uint_constant(parser) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
parser.errors.push(e);
|
||||
(0, Span::default())
|
||||
}
|
||||
};
|
||||
token.meta.subsume(end_meta);
|
||||
|
||||
qualifiers.push((
|
||||
match name.as_str() {
|
||||
"location" => TypeQualifier::Location(value),
|
||||
"set" => TypeQualifier::Set(value),
|
||||
"binding" => TypeQualifier::Binding(value),
|
||||
"local_size_x" => TypeQualifier::WorkGroupSize(0, value),
|
||||
"local_size_y" => TypeQualifier::WorkGroupSize(1, value),
|
||||
"local_size_z" => TypeQualifier::WorkGroupSize(2, value),
|
||||
_ => {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::UnknownLayoutQualifier(name),
|
||||
meta: token.meta,
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
token.meta,
|
||||
))
|
||||
} else {
|
||||
qualifiers.push((
|
||||
match name.as_str() {
|
||||
"push_constant" => {
|
||||
qualifiers.push((
|
||||
TypeQualifier::Layout(StructLayout::Std430),
|
||||
token.meta,
|
||||
));
|
||||
qualifiers.push((
|
||||
TypeQualifier::StorageQualifier(
|
||||
StorageQualifier::AddressSpace(AddressSpace::PushConstant),
|
||||
),
|
||||
token.meta,
|
||||
));
|
||||
return Ok(());
|
||||
}
|
||||
"std140" => TypeQualifier::Layout(StructLayout::Std140),
|
||||
"std430" => TypeQualifier::Layout(StructLayout::Std430),
|
||||
"early_fragment_tests" => TypeQualifier::EarlyFragmentTests,
|
||||
_ => {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::UnknownLayoutQualifier(name),
|
||||
meta: token.meta,
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
token.meta,
|
||||
));
|
||||
QualifierValue::Uint(value)
|
||||
} else {
|
||||
QualifierValue::None
|
||||
};
|
||||
|
||||
(key, value)
|
||||
}
|
||||
};
|
||||
|
||||
qualifiers.insert(key, (value, token.meta));
|
||||
}
|
||||
// TODO: handle Shared?
|
||||
_ => parser.errors.push(Error {
|
||||
kind: ErrorKind::InvalidToken(token.value, vec![ExpectedToken::Identifier]),
|
||||
meta: token.meta,
|
||||
|
||||
@@ -6,25 +6,12 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
AddressSpace, Binding, Block, BuiltIn, Constant, Expression, GlobalVariable, Handle,
|
||||
Interpolation, LocalVariable, ResourceBinding, ScalarKind, ShaderStage, StorageAccess,
|
||||
SwizzleComponent, Type, TypeInner, VectorSize,
|
||||
Interpolation, LocalVariable, ResourceBinding, ScalarKind, ShaderStage, SwizzleComponent, Type,
|
||||
TypeInner, VectorSize,
|
||||
};
|
||||
|
||||
macro_rules! qualifier_arm {
|
||||
($src:expr, $tgt:expr, $meta:expr, $msg:literal, $errors:expr $(,)?) => {{
|
||||
if $tgt.is_some() {
|
||||
$errors.push(Error {
|
||||
kind: ErrorKind::SemanticError($msg.into()),
|
||||
meta: $meta,
|
||||
})
|
||||
}
|
||||
|
||||
$tgt = Some($src);
|
||||
}};
|
||||
}
|
||||
|
||||
pub struct VarDeclaration<'a> {
|
||||
pub qualifiers: &'a [(TypeQualifier, Span)],
|
||||
pub struct VarDeclaration<'a, 'key> {
|
||||
pub qualifiers: &'a mut TypeQualifiers<'key>,
|
||||
pub ty: Handle<Type>,
|
||||
pub name: Option<String>,
|
||||
pub init: Option<Handle<Constant>>,
|
||||
@@ -412,242 +399,141 @@ impl Parser {
|
||||
meta,
|
||||
}: VarDeclaration,
|
||||
) -> Result<GlobalOrConstant> {
|
||||
let mut storage = StorageQualifier::AddressSpace(AddressSpace::Private);
|
||||
let mut interpolation = None;
|
||||
let mut set = None;
|
||||
let mut binding = None;
|
||||
let mut location = None;
|
||||
let mut sampling = None;
|
||||
let mut layout = None;
|
||||
let mut precision = None;
|
||||
let mut access = StorageAccess::all();
|
||||
|
||||
for &(ref qualifier, meta) in qualifiers {
|
||||
match *qualifier {
|
||||
TypeQualifier::StorageQualifier(s) => {
|
||||
if StorageQualifier::AddressSpace(AddressSpace::PushConstant) == storage
|
||||
&& s == StorageQualifier::AddressSpace(AddressSpace::Uniform)
|
||||
{
|
||||
// Ignore the Uniform qualifier if the space was already set to PushConstant
|
||||
continue;
|
||||
} else if StorageQualifier::AddressSpace(AddressSpace::Private) != storage {
|
||||
self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"Cannot use more than one storage qualifier per declaration".into(),
|
||||
),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
|
||||
storage = s;
|
||||
}
|
||||
TypeQualifier::Interpolation(i) => qualifier_arm!(
|
||||
i,
|
||||
interpolation,
|
||||
meta,
|
||||
"Cannot use more than one interpolation qualifier per declaration",
|
||||
self.errors
|
||||
),
|
||||
TypeQualifier::Binding(r) => qualifier_arm!(
|
||||
r,
|
||||
binding,
|
||||
meta,
|
||||
"Cannot use more than one binding per declaration",
|
||||
self.errors
|
||||
),
|
||||
TypeQualifier::Set(s) => qualifier_arm!(
|
||||
s,
|
||||
set,
|
||||
meta,
|
||||
"Cannot use more than one binding per declaration",
|
||||
self.errors
|
||||
),
|
||||
TypeQualifier::Location(l) => qualifier_arm!(
|
||||
l,
|
||||
location,
|
||||
meta,
|
||||
"Cannot use more than one binding per declaration",
|
||||
self.errors
|
||||
),
|
||||
TypeQualifier::Sampling(s) => qualifier_arm!(
|
||||
s,
|
||||
sampling,
|
||||
meta,
|
||||
"Cannot use more than one sampling qualifier per declaration",
|
||||
self.errors
|
||||
),
|
||||
TypeQualifier::Layout(ref l) => qualifier_arm!(
|
||||
l,
|
||||
layout,
|
||||
meta,
|
||||
"Cannot use more than one layout qualifier per declaration",
|
||||
self.errors
|
||||
),
|
||||
TypeQualifier::Precision(ref p) => qualifier_arm!(
|
||||
p,
|
||||
precision,
|
||||
meta,
|
||||
"Cannot use more than one precision qualifier per declaration",
|
||||
self.errors
|
||||
),
|
||||
TypeQualifier::StorageAccess(a) => access &= a,
|
||||
_ => {
|
||||
self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError("Qualifier not supported in globals".into()),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match storage {
|
||||
StorageQualifier::AddressSpace(AddressSpace::PushConstant) => {
|
||||
if set.is_some() {
|
||||
self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"set cannot be used to decorate push constant".into(),
|
||||
),
|
||||
meta,
|
||||
let storage = qualifiers.storage.0;
|
||||
let (ret, lookup) = match storage {
|
||||
StorageQualifier::Input | StorageQualifier::Output => {
|
||||
let input = storage == StorageQualifier::Input;
|
||||
// TODO: glslang seems to use a counter for variables without
|
||||
// explicit location (even if that causes collisions)
|
||||
let location = qualifiers
|
||||
.uint_layout_qualifier("location", &mut self.errors)
|
||||
.unwrap_or(0);
|
||||
let interpolation = qualifiers.interpolation.take().map(|(i, _)| i).or_else(|| {
|
||||
let kind = self.module.types[ty].inner.scalar_kind()?;
|
||||
Some(match kind {
|
||||
ScalarKind::Float => Interpolation::Perspective,
|
||||
_ => Interpolation::Flat,
|
||||
})
|
||||
}
|
||||
}
|
||||
StorageQualifier::AddressSpace(AddressSpace::Uniform)
|
||||
| StorageQualifier::AddressSpace(AddressSpace::Storage { .. }) => {
|
||||
if binding.is_none() {
|
||||
self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"uniform/buffer blocks require layout(binding=X)".into(),
|
||||
),
|
||||
meta,
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if set.is_some() || binding.is_some() {
|
||||
self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"set/binding can only be applied to uniform/buffer blocks".into(),
|
||||
),
|
||||
meta,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let sampling = qualifiers.sampling.take().map(|(s, _)| s);
|
||||
|
||||
if (sampling.is_some() || interpolation.is_some()) && location.is_none() {
|
||||
return Err(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"Sampling and interpolation qualifiers can only be used in in/out variables"
|
||||
.into(),
|
||||
),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
let handle = self.module.global_variables.append(
|
||||
GlobalVariable {
|
||||
name: name.clone(),
|
||||
space: AddressSpace::Private,
|
||||
binding: None,
|
||||
ty,
|
||||
init,
|
||||
},
|
||||
meta,
|
||||
);
|
||||
|
||||
if let Some(location) = location {
|
||||
let input = storage == StorageQualifier::Input;
|
||||
let interpolation = interpolation.or_else(|| {
|
||||
let kind = self.module.types[ty].inner.scalar_kind()?;
|
||||
Some(match kind {
|
||||
ScalarKind::Float => Interpolation::Perspective,
|
||||
_ => Interpolation::Flat,
|
||||
})
|
||||
});
|
||||
|
||||
let handle = self.module.global_variables.append(
|
||||
GlobalVariable {
|
||||
let idx = self.entry_args.len();
|
||||
self.entry_args.push(EntryArg {
|
||||
name: name.clone(),
|
||||
space: AddressSpace::Private,
|
||||
binding: None,
|
||||
ty,
|
||||
init,
|
||||
},
|
||||
meta,
|
||||
);
|
||||
binding: Binding::Location {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
},
|
||||
handle,
|
||||
storage,
|
||||
});
|
||||
|
||||
let idx = self.entry_args.len();
|
||||
self.entry_args.push(EntryArg {
|
||||
name: name.clone(),
|
||||
binding: Binding::Location {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
},
|
||||
handle,
|
||||
storage,
|
||||
});
|
||||
|
||||
if let Some(name) = name {
|
||||
let lookup = GlobalLookup {
|
||||
kind: GlobalLookupKind::Variable(handle),
|
||||
entry_arg: Some(idx),
|
||||
mutable: !input,
|
||||
};
|
||||
ctx.add_global(self, &name, lookup, body);
|
||||
|
||||
self.global_variables.push((name, lookup));
|
||||
(GlobalOrConstant::Global(handle), lookup)
|
||||
}
|
||||
StorageQualifier::Const => {
|
||||
let init = init.ok_or_else(|| Error {
|
||||
kind: ErrorKind::SemanticError("const values must have an initializer".into()),
|
||||
meta,
|
||||
})?;
|
||||
|
||||
return Ok(GlobalOrConstant::Global(handle));
|
||||
} else if let StorageQualifier::Const = storage {
|
||||
let init = init.ok_or_else(|| Error {
|
||||
kind: ErrorKind::SemanticError("const values must have an initializer".into()),
|
||||
meta,
|
||||
})?;
|
||||
if let Some(name) = name {
|
||||
let lookup = GlobalLookup {
|
||||
kind: GlobalLookupKind::Constant(init, ty),
|
||||
entry_arg: None,
|
||||
mutable: false,
|
||||
};
|
||||
ctx.add_global(self, &name, lookup, body);
|
||||
|
||||
self.global_variables.push((name, lookup));
|
||||
(GlobalOrConstant::Constant(init), lookup)
|
||||
}
|
||||
return Ok(GlobalOrConstant::Constant(init));
|
||||
}
|
||||
|
||||
let space = match self.module.types[ty].inner {
|
||||
TypeInner::Image { .. } => AddressSpace::Handle,
|
||||
TypeInner::Sampler { .. } => AddressSpace::Handle,
|
||||
_ => {
|
||||
if let StorageQualifier::AddressSpace(AddressSpace::Storage { .. }) = storage {
|
||||
AddressSpace::Storage { access }
|
||||
} else {
|
||||
match storage {
|
||||
StorageQualifier::AddressSpace(space) => space,
|
||||
_ => AddressSpace::Private,
|
||||
StorageQualifier::AddressSpace(mut space) => {
|
||||
match space {
|
||||
AddressSpace::Storage { ref mut access } => {
|
||||
if let Some((restricted_access, _)) = qualifiers.storage_acess.take() {
|
||||
access.remove(restricted_access);
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressSpace::Uniform => {
|
||||
if let TypeInner::Image { .. } | TypeInner::Sampler { .. } =
|
||||
self.module.types[ty].inner
|
||||
{
|
||||
space = AddressSpace::Handle
|
||||
} else if qualifiers
|
||||
.none_layout_qualifier("push_constant", &mut self.errors)
|
||||
{
|
||||
space = AddressSpace::PushConstant
|
||||
}
|
||||
}
|
||||
AddressSpace::Function => space = AddressSpace::Private,
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let binding = match space {
|
||||
AddressSpace::Uniform | AddressSpace::Storage { .. } | AddressSpace::Handle => {
|
||||
let binding = qualifiers.uint_layout_qualifier("binding", &mut self.errors);
|
||||
if binding.is_none() {
|
||||
self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"uniform/buffer blocks require layout(binding=X)".into(),
|
||||
),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
let set = qualifiers.uint_layout_qualifier("set", &mut self.errors);
|
||||
binding.map(|binding| ResourceBinding {
|
||||
group: set.unwrap_or(0),
|
||||
binding,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let handle = self.module.global_variables.append(
|
||||
GlobalVariable {
|
||||
name: name.clone(),
|
||||
space,
|
||||
binding,
|
||||
ty,
|
||||
init,
|
||||
},
|
||||
meta,
|
||||
);
|
||||
|
||||
let lookup = GlobalLookup {
|
||||
kind: GlobalLookupKind::Variable(handle),
|
||||
entry_arg: None,
|
||||
mutable: true,
|
||||
};
|
||||
|
||||
(GlobalOrConstant::Global(handle), lookup)
|
||||
}
|
||||
};
|
||||
|
||||
let handle = self.module.global_variables.append(
|
||||
GlobalVariable {
|
||||
name: name.clone(),
|
||||
space,
|
||||
binding: binding.map(|binding| ResourceBinding {
|
||||
group: set.unwrap_or(0),
|
||||
binding,
|
||||
}),
|
||||
ty,
|
||||
init,
|
||||
},
|
||||
meta,
|
||||
);
|
||||
|
||||
if let Some(name) = name {
|
||||
let lookup = GlobalLookup {
|
||||
kind: GlobalLookupKind::Variable(handle),
|
||||
entry_arg: None,
|
||||
mutable: true,
|
||||
};
|
||||
ctx.add_global(self, &name, lookup, body);
|
||||
|
||||
self.global_variables.push((name, lookup));
|
||||
}
|
||||
|
||||
Ok(GlobalOrConstant::Global(handle))
|
||||
qualifiers.unused_errors(&mut self.errors);
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub(crate) fn add_local_var(
|
||||
@@ -666,37 +552,18 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
|
||||
let mut mutable = true;
|
||||
let mut precision = None;
|
||||
|
||||
for &(ref qualifier, meta) in decl.qualifiers {
|
||||
match *qualifier {
|
||||
TypeQualifier::StorageQualifier(StorageQualifier::Const) => {
|
||||
if !mutable {
|
||||
self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"Cannot use more than one constant qualifier per declaration"
|
||||
.into(),
|
||||
),
|
||||
meta,
|
||||
})
|
||||
}
|
||||
|
||||
mutable = false;
|
||||
}
|
||||
TypeQualifier::Precision(ref p) => qualifier_arm!(
|
||||
p,
|
||||
precision,
|
||||
meta,
|
||||
"Cannot use more than one precision qualifier per declaration",
|
||||
self.errors
|
||||
),
|
||||
_ => self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError("Qualifier not supported in locals".into()),
|
||||
meta,
|
||||
}),
|
||||
let storage = decl.qualifiers.storage;
|
||||
let mutable = match storage.0 {
|
||||
StorageQualifier::AddressSpace(AddressSpace::Function) => true,
|
||||
StorageQualifier::Const => false,
|
||||
_ => {
|
||||
self.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError("Locals cannot have a storage qualifier".into()),
|
||||
meta: storage.1,
|
||||
});
|
||||
true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let handle = ctx.locals.append(
|
||||
LocalVariable {
|
||||
@@ -712,6 +579,8 @@ impl Parser {
|
||||
ctx.add_local_var(name, expr, mutable);
|
||||
}
|
||||
|
||||
decl.qualifiers.unused_errors(&mut self.errors);
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@ struct BST {
|
||||
data: i32;
|
||||
};
|
||||
|
||||
struct FragmentOutput {
|
||||
@location(0) o_color: vec4<f32>;
|
||||
};
|
||||
|
||||
var<private> global: f32;
|
||||
var<private> o_color: vec4<f32>;
|
||||
|
||||
@@ -212,7 +216,8 @@ fn main_1() {
|
||||
}
|
||||
|
||||
@stage(fragment)
|
||||
fn main() {
|
||||
fn main() -> FragmentOutput {
|
||||
main_1();
|
||||
return;
|
||||
let _e5 = o_color;
|
||||
return FragmentOutput(_e5);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ struct Mat4x3_ {
|
||||
mz: vec4<f32>;
|
||||
};
|
||||
|
||||
struct FragmentOutput {
|
||||
@location(0) o_color: vec4<f32>;
|
||||
};
|
||||
|
||||
var<private> o_color: vec4<f32>;
|
||||
|
||||
fn Fma(d: ptr<function, Mat4x3_>, m: Mat4x3_, s: f32) {
|
||||
@@ -38,7 +42,8 @@ fn main_1() {
|
||||
}
|
||||
|
||||
@stage(fragment)
|
||||
fn main() {
|
||||
fn main() -> FragmentOutput {
|
||||
main_1();
|
||||
return;
|
||||
let _e3 = o_color;
|
||||
return FragmentOutput(_e3);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user