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:
João Capucho
2022-02-04 20:06:49 +00:00
committed by GitHub
parent 5f948e9b12
commit e621acc09d
8 changed files with 430 additions and 407 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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();

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)
}
}

View File

@@ -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);
}

View File

@@ -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);
}