diff --git a/src/front/glsl/ast.rs b/src/front/glsl/ast.rs index 4c2054bfe6..759cda38f7 100644 --- a/src/front/glsl/ast.rs +++ b/src/front/glsl/ast.rs @@ -139,6 +139,8 @@ pub enum QualifierKey<'a> { String(Cow<'a, str>), /// Used for `std140` and `std430` layout qualifiers Layout, + /// Used for image formats + Format, } #[derive(Debug)] @@ -146,6 +148,7 @@ pub enum QualifierValue { None, Uint(u32), Layout(StructLayout), + Format(crate::StorageFormat), } #[derive(Debug, Default)] diff --git a/src/front/glsl/parser/types.rs b/src/front/glsl/parser/types.rs index 3fb1f355c4..0772596df4 100644 --- a/src/front/glsl/parser/types.rs +++ b/src/front/glsl/parser/types.rs @@ -285,24 +285,28 @@ impl<'source> ParsingContext<'source> { 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); - - QualifierValue::Uint(value) + word => { + if let Some(format) = map_image_format(word) { + (QualifierKey::Format, QualifierValue::Format(format)) } else { - QualifierValue::None - }; + 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); - (key, value) + QualifierValue::Uint(value) + } else { + QualifierValue::None + }; + + (key, value) + } } }; @@ -326,3 +330,54 @@ impl<'source> ParsingContext<'source> { }) } } + +fn map_image_format(word: &str) -> Option { + let format = match word { + // float-image-format-qualifier: + "rgba32f" => crate::StorageFormat::Rgba32Float, + "rgba16f" => crate::StorageFormat::Rgba16Float, + "rg32f" => crate::StorageFormat::Rg32Float, + "rg16f" => crate::StorageFormat::Rg16Float, + "r11f_g11f_b10f" => crate::StorageFormat::Rg11b10Float, + "r32f" => crate::StorageFormat::R32Float, + "r16f" => crate::StorageFormat::R16Float, + "rgba16" => crate::StorageFormat::Rgba16Float, + "rgb10_a2" => crate::StorageFormat::Rgb10a2Unorm, + "rgba8" => crate::StorageFormat::Rgba8Unorm, + "rg16" => crate::StorageFormat::Rg16Float, + "rg8" => crate::StorageFormat::Rg8Unorm, + "r16" => crate::StorageFormat::R16Float, + "r8" => crate::StorageFormat::R8Unorm, + "rgba8_snorm" => crate::StorageFormat::Rgba8Snorm, + "rg8_snorm" => crate::StorageFormat::Rg8Snorm, + "r8_snorm" => crate::StorageFormat::R8Snorm, + // int-image-format-qualifier: + "rgba32i" => crate::StorageFormat::Rgba32Sint, + "rgba16i" => crate::StorageFormat::Rgba16Sint, + "rgba8i" => crate::StorageFormat::Rgba8Sint, + "rg32i" => crate::StorageFormat::Rg32Sint, + "rg16i" => crate::StorageFormat::Rg16Sint, + "rg8i" => crate::StorageFormat::Rg8Sint, + "r32i" => crate::StorageFormat::R32Sint, + "r16i" => crate::StorageFormat::R16Sint, + "r8i" => crate::StorageFormat::R8Sint, + // uint-image-format-qualifier: + "rgba32ui" => crate::StorageFormat::Rgba32Uint, + "rgba16ui" => crate::StorageFormat::Rgba16Uint, + "rgba8ui" => crate::StorageFormat::Rgba8Uint, + "rg32ui" => crate::StorageFormat::Rg32Uint, + "rg16ui" => crate::StorageFormat::Rg16Uint, + "rg8ui" => crate::StorageFormat::Rg8Uint, + "r32ui" => crate::StorageFormat::R32Uint, + "r16ui" => crate::StorageFormat::R16Uint, + "r8ui" => crate::StorageFormat::R8Uint, + // TODO: These next ones seem incorrect to me + // "rgba16_snorm" => crate::StorageFormat::Rgba16Float, + // "rg16_snorm" => crate::StorageFormat::Rg16Float, + // "r16_snorm" => crate::StorageFormat::R16Float, + // "rgb10_a2ui" => crate::StorageFormat::Rgb10a2Unorm, + _ => return None, + }; + + Some(format) +} diff --git a/src/front/glsl/types.rs b/src/front/glsl/types.rs index becc53ad6c..425844cd6a 100644 --- a/src/front/glsl/types.rs +++ b/src/front/glsl/types.rs @@ -150,9 +150,60 @@ pub fn parse_type(type_name: &str) -> Option { }) }; + let image_parse = |word: &str| { + let mut iter = word.split("image"); + + let texture_kind = |ty| { + Some(match ty { + "" => ScalarKind::Float, + "i" => ScalarKind::Sint, + "u" => ScalarKind::Uint, + _ => return None, + }) + }; + + let kind = iter.next()?; + let size = iter.next()?; + let kind = texture_kind(kind)?; + + let class = ImageClass::Storage { + format: match kind { + ScalarKind::Sint => crate::StorageFormat::Rgba32Sint, + ScalarKind::Uint => crate::StorageFormat::Rgba32Uint, + ScalarKind::Float => crate::StorageFormat::Rgba32Float, + ScalarKind::Bool => unreachable!(), + }, + access: crate::StorageAccess::all(), + }; + + // TODO: glsl support multisampled storage images, naga doesn't + let (dim, arrayed) = match size { + "1D" => (ImageDimension::D1, false), + "1DArray" => (ImageDimension::D1, true), + "2D" => (ImageDimension::D2, false), + "2DArray" => (ImageDimension::D2, true), + "3D" => (ImageDimension::D3, false), + // Naga doesn't support cube images and it's usefulness + // is questionable, so they won't be supported for now + // "Cube" => (ImageDimension::Cube, false), + // "CubeArray" => (ImageDimension::Cube, true), + _ => return None, + }; + + Some(Type { + name: None, + inner: TypeInner::Image { + dim, + arrayed, + class, + }, + }) + }; + vec_parse(word) .or_else(|| mat_parse(word)) .or_else(|| texture_parse(word)) + .or_else(|| image_parse(word)) } } } diff --git a/src/front/glsl/variables.rs b/src/front/glsl/variables.rs index 072b9f4740..182097b74b 100644 --- a/src/front/glsl/variables.rs +++ b/src/front/glsl/variables.rs @@ -393,7 +393,7 @@ impl Parser { body: &mut Block, VarDeclaration { qualifiers, - ty, + mut ty, name, init, meta, @@ -469,17 +469,58 @@ impl Parser { access.remove(restricted_access); } } - AddressSpace::Uniform => { - if let TypeInner::Image { .. } | TypeInner::Sampler { .. } = - self.module.types[ty].inner - { + AddressSpace::Uniform => match self.module.types[ty].inner { + TypeInner::Image { + class, + dim, + arrayed, + } => { + if let crate::ImageClass::Storage { + mut access, + mut format, + } = class + { + if let Some((restricted_access, _)) = + qualifiers.storage_acess.take() + { + access.remove(restricted_access); + } + + match qualifiers.layout_qualifiers.remove(&QualifierKey::Format) { + Some((QualifierValue::Format(f), _)) => format = f, + // TODO: glsl supports images without format qualifier + // if they are `writeonly` + None => self.errors.push(Error { + kind: ErrorKind::SemanticError( + "image types require a format layout qualifier".into(), + ), + meta, + }), + _ => unreachable!(), + } + + ty = self.module.types.insert( + Type { + name: None, + inner: TypeInner::Image { + dim, + arrayed, + class: crate::ImageClass::Storage { format, access }, + }, + }, + meta, + ); + } + space = AddressSpace::Handle - } else if qualifiers - .none_layout_qualifier("push_constant", &mut self.errors) - { - space = AddressSpace::PushConstant } - } + TypeInner::Sampler { .. } => space = AddressSpace::Handle, + _ => { + if qualifiers.none_layout_qualifier("push_constant", &mut self.errors) { + space = AddressSpace::PushConstant + } + } + }, AddressSpace::Function => space = AddressSpace::Private, _ => {} };