diff --git a/src/front/glsl/ast.rs b/src/front/glsl/ast.rs index 91c62f7a6d..d1ba273d3f 100644 --- a/src/front/glsl/ast.rs +++ b/src/front/glsl/ast.rs @@ -955,6 +955,7 @@ pub enum TypeQualifier { WorkGroupSize(usize, u32), Sampling(Sampling), Layout(StructLayout), + Precision(Precision), EarlyFragmentTests, } @@ -984,6 +985,14 @@ pub enum StructLayout { Std430, } +// TODO: Encode precision hints in the IR +#[derive(Debug, Clone, PartialEq, Copy)] +pub enum Precision { + Low, + Medium, + High, +} + #[derive(Debug, Clone, PartialEq, Copy)] pub enum ParameterQualifier { In, diff --git a/src/front/glsl/lex.rs b/src/front/glsl/lex.rs index fb203dd6cb..e12cb5e3ef 100644 --- a/src/front/glsl/lex.rs +++ b/src/front/glsl/lex.rs @@ -1,4 +1,5 @@ use super::{ + ast::Precision, token::{SourceMetadata, Token, TokenValue}, types::parse_type, }; @@ -58,6 +59,7 @@ impl<'a> Iterator for Lexer<'a> { PPTokenValue::Float(float) => TokenValue::FloatConstant(float), PPTokenValue::Ident(ident) => { match ident.as_str() { + // Qualifiers "layout" => TokenValue::Layout, "in" => TokenValue::In, "out" => TokenValue::Out, @@ -70,6 +72,10 @@ impl<'a> Iterator for Lexer<'a> { "sample" => TokenValue::Sampling(crate::Sampling::Sample), "const" => TokenValue::Const, "inout" => TokenValue::InOut, + "precision" => TokenValue::Precision, + "highp" => TokenValue::PrecisionQualifier(Precision::High), + "mediump" => TokenValue::PrecisionQualifier(Precision::Medium), + "lowp" => TokenValue::PrecisionQualifier(Precision::Low), // values "true" => TokenValue::BoolConstant(true), "false" => TokenValue::BoolConstant(false), diff --git a/src/front/glsl/parser.rs b/src/front/glsl/parser.rs index b1f933e100..9d07007804 100644 --- a/src/front/glsl/parser.rs +++ b/src/front/glsl/parser.rs @@ -10,9 +10,11 @@ use super::{ Program, }; use crate::{ - arena::Handle, front::glsl::error::ExpectedToken, Arena, ArraySize, BinaryOperator, Block, - Constant, ConstantInner, Expression, Function, FunctionResult, ResourceBinding, ScalarValue, - Statement, StorageClass, StructMember, SwitchCase, Type, TypeInner, UnaryOperator, + arena::Handle, + front::glsl::{ast::Precision, error::ExpectedToken}, + Arena, ArraySize, BinaryOperator, Block, Constant, ConstantInner, Expression, Function, + FunctionResult, ResourceBinding, ScalarKind, ScalarValue, Statement, StorageClass, + StructMember, SwitchCase, Type, TypeInner, UnaryOperator, }; use core::convert::TryFrom; use std::{iter::Peekable, mem}; @@ -206,6 +208,7 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> { self.lexer.peek().map_or(false, |t| match t.value { TokenValue::Interpolation(_) | TokenValue::Sampling(_) + | TokenValue::PrecisionQualifier(_) | TokenValue::Const | TokenValue::In | TokenValue::Out @@ -241,7 +244,7 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> { StorageQualifier::StorageClass(StorageClass::Storage), ), TokenValue::Sampling(s) => TypeQualifier::Sampling(s), - + TokenValue::PrecisionQualifier(p) => TypeQualifier::Precision(p), _ => unreachable!(), }, token.meta, @@ -829,7 +832,51 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> { } } } else { - Ok(false) + match self.lexer.peek().map(|t| &t.value) { + Some(&TokenValue::Precision) => { + // PRECISION precision_qualifier type_specifier SEMICOLON + self.bump()?; + + let token = self.bump()?; + let _ = match token.value { + TokenValue::PrecisionQualifier(p) => p, + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![ + TokenValue::PrecisionQualifier(Precision::High).into(), + TokenValue::PrecisionQualifier(Precision::Medium).into(), + TokenValue::PrecisionQualifier(Precision::Low).into(), + ], + )) + } + }; + + let (ty, meta) = self.parse_type_non_void()?; + + match self.program.module.types[ty].inner { + TypeInner::Scalar { + kind: ScalarKind::Float, + .. + } + | TypeInner::Scalar { + kind: ScalarKind::Sint, + .. + } => {} + _ => { + return Err(ErrorKind::SemanticError( + meta, + "Precision statement can only work on floats and ints".into(), + )) + } + } + + self.expect(TokenValue::Semicolon)?; + + Ok(true) + } + _ => Ok(false), + } } } diff --git a/src/front/glsl/parser_tests.rs b/src/front/glsl/parser_tests.rs index 8ff6aa1fe3..4b1b9ae32a 100644 --- a/src/front/glsl/parser_tests.rs +++ b/src/front/glsl/parser_tests.rs @@ -246,6 +246,15 @@ fn declarations() { &entry_points, ) .unwrap(); + + let _program = parse_program( + r#" + #version 450 + precision highp float; + "#, + &entry_points, + ) + .unwrap(); } #[test] diff --git a/src/front/glsl/token.rs b/src/front/glsl/token.rs index 7d01ffb77c..b1f147ab7d 100644 --- a/src/front/glsl/token.rs +++ b/src/front/glsl/token.rs @@ -1,5 +1,6 @@ pub use pp_rs::token::{Float, Integer, PreprocessorError}; +use super::ast::Precision; use crate::{Interpolation, Sampling, Type}; use std::{fmt, ops::Range}; @@ -57,6 +58,8 @@ pub enum TokenValue { Const, Interpolation(Interpolation), Sampling(Sampling), + Precision, + PrecisionQualifier(Precision), Continue, Break, diff --git a/src/front/glsl/variables.rs b/src/front/glsl/variables.rs index effad7b18f..529036caa8 100644 --- a/src/front/glsl/variables.rs +++ b/src/front/glsl/variables.rs @@ -302,6 +302,7 @@ impl Program<'_> { let mut location = None; let mut sampling = None; let mut layout = None; + let mut precision = None; for &(ref qualifier, meta) in qualifiers { match *qualifier { @@ -350,7 +351,12 @@ impl Program<'_> { meta, "Cannot use more than one layout qualifier per declaration" ), - + TypeQualifier::Precision(ref p) => qualifier_arm!( + p, + precision, + meta, + "Cannot use more than one precision qualifier per declaration" + ), _ => { return Err(ErrorKind::SemanticError( meta,