diff options
Diffstat (limited to 'gfx/wr/glsl-to-cxx')
-rw-r--r-- | gfx/wr/glsl-to-cxx/Cargo.toml | 11 | ||||
-rw-r--r-- | gfx/wr/glsl-to-cxx/README.md | 21 | ||||
-rw-r--r-- | gfx/wr/glsl-to-cxx/src/hir.rs | 4545 | ||||
-rw-r--r-- | gfx/wr/glsl-to-cxx/src/lib.rs | 3681 | ||||
-rw-r--r-- | gfx/wr/glsl-to-cxx/src/main.rs | 8 |
5 files changed, 8266 insertions, 0 deletions
diff --git a/gfx/wr/glsl-to-cxx/Cargo.toml b/gfx/wr/glsl-to-cxx/Cargo.toml new file mode 100644 index 0000000000..8161060c91 --- /dev/null +++ b/gfx/wr/glsl-to-cxx/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "glsl-to-cxx" +version = "0.1.0" +license = "MPL-2.0" +authors = ["The Mozilla Project Developers", "Dimitri Sabadie"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +glsl = "6.0" diff --git a/gfx/wr/glsl-to-cxx/README.md b/gfx/wr/glsl-to-cxx/README.md new file mode 100644 index 0000000000..54dcbc7d61 --- /dev/null +++ b/gfx/wr/glsl-to-cxx/README.md @@ -0,0 +1,21 @@ +A GLSL to C++ translator. + +Translates GLSL to vectorized C++. Intended for use with WebRender software backend. + +Architecture +------------ +GLSL code is parsed by the glsl crate. In hir.rs we traverse the resulting AST +and build a higher level representation by doing type checking and name +resolution. The resulting hir tree is traversed by lib.rs to output C++ code. + +The generated C++ code is 4x wider then the original glsl. i.e. a glsl 'float' +becomes a C++ 'Float' which is represented by a xmm register (a vector of 4 floats). +Likewise, a vec4 becomes a struct of 4 'Float's for a total of 4 xmm registers and +16 floating point values. + +Vector branching is flattened to non-branching code that unconditionally runs both +sides of the branch and combines the results with a mask based on the condition. + +The compiler also supports scalarization. Values that are known to be the same +across all vector lanes are translated to scalars instead of vectors. Branches on +scalars are translated as actual branches. diff --git a/gfx/wr/glsl-to-cxx/src/hir.rs b/gfx/wr/glsl-to-cxx/src/hir.rs new file mode 100644 index 0000000000..ad48c11df8 --- /dev/null +++ b/gfx/wr/glsl-to-cxx/src/hir.rs @@ -0,0 +1,4545 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Large chunks of this file are derived from the glsl crate which is: + * Copyright (c) 2018, Dimitri Sabadie <dimitri.sabadie@gmail.com> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of Dimitri Sabadie <dimitri.sabadie@gmail.com> nor the names of other + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +use glsl::syntax; +use glsl::syntax::{ArrayedIdentifier, ArraySpecifier, ArraySpecifierDimension, AssignmentOp, BinaryOp, Identifier}; +use glsl::syntax::{NonEmpty, PrecisionQualifier, StructFieldSpecifier, StructSpecifier}; +use glsl::syntax::{TypeSpecifier, TypeSpecifierNonArray, UnaryOp}; +use std::cell::{Cell, Ref, RefCell}; +use std::collections::HashMap; +use std::iter::FromIterator; +use std::mem; +use std::ops::{Deref, DerefMut}; +use std::rc::Rc; + +trait LiftFrom<S> { + fn lift(state: &mut State, s: S) -> Self; +} + +fn lift<S, T: LiftFrom<S>>(state: &mut State, s: S) -> T { + LiftFrom::lift(state, s) +} + +#[derive(Debug)] +pub struct Symbol { + pub name: String, + pub decl: SymDecl, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct FunctionSignature { + ret: Type, + params: Vec<Type>, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct FunctionType { + signatures: NonEmpty<FunctionSignature>, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum SamplerFormat { + Unknown, + RGBA8, + RGBA32F, + RGBA32I, + R8, + RG8, +} + +impl SamplerFormat { + pub fn type_suffix(self) -> Option<&'static str> { + match self { + SamplerFormat::Unknown => None, + SamplerFormat::RGBA8 => Some("RGBA8"), + SamplerFormat::RGBA32F => Some("RGBA32F"), + SamplerFormat::RGBA32I => Some("RGBA32I"), + SamplerFormat::R8 => Some("R8"), + SamplerFormat::RG8 => Some("RG8"), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum StorageClass { + None, + Const, + In, + Out, + Uniform, + Sampler(SamplerFormat), + FragColor(i32), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ArraySizes { + pub sizes: Vec<Expr>, +} + +impl LiftFrom<&ArraySpecifier> for ArraySizes { + fn lift(state: &mut State, a: &ArraySpecifier) -> Self { + ArraySizes { + sizes: a.dimensions.0.iter().map(|a| match a { + ArraySpecifierDimension::Unsized => panic!(), + ArraySpecifierDimension::ExplicitlySized(expr) => translate_expression(state, expr), + }).collect(), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum TypeKind { + Void, + Bool, + Int, + UInt, + Float, + Double, + Vec2, + Vec3, + Vec4, + DVec2, + DVec3, + DVec4, + BVec2, + BVec3, + BVec4, + IVec2, + IVec3, + IVec4, + UVec2, + UVec3, + UVec4, + Mat2, + Mat3, + Mat4, + Mat23, + Mat24, + Mat32, + Mat34, + Mat42, + Mat43, + DMat2, + DMat3, + DMat4, + DMat23, + DMat24, + DMat32, + DMat34, + DMat42, + DMat43, + // floating point opaque types + Sampler1D, + Image1D, + Sampler2D, + Image2D, + Sampler3D, + Image3D, + SamplerCube, + ImageCube, + Sampler2DRect, + Image2DRect, + Sampler1DArray, + Image1DArray, + Sampler2DArray, + Image2DArray, + SamplerBuffer, + ImageBuffer, + Sampler2DMS, + Image2DMS, + Sampler2DMSArray, + Image2DMSArray, + SamplerCubeArray, + ImageCubeArray, + Sampler1DShadow, + Sampler2DShadow, + Sampler2DRectShadow, + Sampler1DArrayShadow, + Sampler2DArrayShadow, + SamplerCubeShadow, + SamplerCubeArrayShadow, + // signed integer opaque types + ISampler1D, + IImage1D, + ISampler2D, + IImage2D, + ISampler3D, + IImage3D, + ISamplerCube, + IImageCube, + ISampler2DRect, + IImage2DRect, + ISampler1DArray, + IImage1DArray, + ISampler2DArray, + IImage2DArray, + ISamplerBuffer, + IImageBuffer, + ISampler2DMS, + IImage2DMS, + ISampler2DMSArray, + IImage2DMSArray, + ISamplerCubeArray, + IImageCubeArray, + // unsigned integer opaque types + AtomicUInt, + USampler1D, + UImage1D, + USampler2D, + UImage2D, + USampler3D, + UImage3D, + USamplerCube, + UImageCube, + USampler2DRect, + UImage2DRect, + USampler1DArray, + UImage1DArray, + USampler2DArray, + UImage2DArray, + USamplerBuffer, + UImageBuffer, + USampler2DMS, + UImage2DMS, + USampler2DMSArray, + UImage2DMSArray, + USamplerCubeArray, + UImageCubeArray, + Struct(SymRef), +} + +impl TypeKind { + pub fn is_sampler(&self) -> bool { + use TypeKind::*; + match self { + Sampler1D + | Image1D + | Sampler2D + | Image2D + | Sampler3D + | Image3D + | SamplerCube + | ImageCube + | Sampler2DRect + | Image2DRect + | Sampler1DArray + | Image1DArray + | Sampler2DArray + | Image2DArray + | SamplerBuffer + | ImageBuffer + | Sampler2DMS + | Image2DMS + | Sampler2DMSArray + | Image2DMSArray + | SamplerCubeArray + | ImageCubeArray + | Sampler1DShadow + | Sampler2DShadow + | Sampler2DRectShadow + | Sampler1DArrayShadow + | Sampler2DArrayShadow + | SamplerCubeShadow + | SamplerCubeArrayShadow + | ISampler1D + | IImage1D + | ISampler2D + | IImage2D + | ISampler3D + | IImage3D + | ISamplerCube + | IImageCube + | ISampler2DRect + | IImage2DRect + | ISampler1DArray + | IImage1DArray + | ISampler2DArray + | IImage2DArray + | ISamplerBuffer + | IImageBuffer + | ISampler2DMS + | IImage2DMS + | ISampler2DMSArray + | IImage2DMSArray + | ISamplerCubeArray + | IImageCubeArray + | USampler1D + | UImage1D + | USampler2D + | UImage2D + | USampler3D + | UImage3D + | USamplerCube + | UImageCube + | USampler2DRect + | UImage2DRect + | USampler1DArray + | UImage1DArray + | USampler2DArray + | UImage2DArray + | USamplerBuffer + | UImageBuffer + | USampler2DMS + | UImage2DMS + | USampler2DMSArray + | UImage2DMSArray + | USamplerCubeArray + | UImageCubeArray => true, + _ => false, + } + } + + pub fn is_bool(&self) -> bool { + use TypeKind::*; + match self { + Bool | BVec2 | BVec3 | BVec4 => true, + _ => false, + } + } + + pub fn to_bool(&self) -> Self { + use TypeKind::*; + match self { + Int | UInt | Float | Double => Bool, + IVec2 | UVec2 | Vec2 | DVec2 => BVec2, + IVec3 | UVec3 | Vec3 | DVec3 => BVec3, + IVec4 | UVec4 | Vec4 | DVec4 => BVec4, + _ => *self, + } + } + + pub fn to_int(&self) -> Self { + use TypeKind::*; + match self { + Bool | UInt | Float | Double => Int, + BVec2 | UVec2 | Vec2 | DVec2 => IVec2, + BVec3 | UVec3 | Vec3 | DVec3 => IVec3, + BVec4 | UVec4 | Vec4 | DVec4 => IVec4, + _ => *self, + } + } + + pub fn to_scalar(&self) -> Self { + use TypeKind::*; + match self { + IVec2 | IVec3 | IVec4 => Int, + UVec2 | UVec3 | UVec4 => UInt, + Vec2 | Vec3 | Vec4 => Float, + DVec2 | DVec3 | DVec4 => Double, + BVec2 | BVec3 | BVec4 => Bool, + _ => *self, + } + } + + pub fn glsl_primitive_type_name(&self) -> Option<&'static str> { + use TypeKind::*; + Some(match self { + Void => "void", + Bool => "bool", + Int => "int", + UInt => "uint", + Float => "float", + Double => "double", + Vec2 => "vec2", + Vec3 => "vec3", + Vec4 => "vec4", + DVec2 => "dvec2", + DVec3 => "dvec3", + DVec4 => "dvec4", + BVec2 => "bvec2", + BVec3 => "bvec3", + BVec4 => "bvec4", + IVec2 => "ivec2", + IVec3 => "ivec3", + IVec4 => "ivec4", + UVec2 => "uvec2", + UVec3 => "uvec3", + UVec4 => "uvec4", + Mat2 => "mat2", + Mat3 => "mat3", + Mat4 => "mat4", + Mat23 => "mat23", + Mat24 => "mat24", + Mat32 => "mat32", + Mat34 => "mat34", + Mat42 => "mat42", + Mat43 => "mat43", + DMat2 => "dmat2", + DMat3 => "dmat3", + DMat4 => "dmat4", + DMat23 => "dmat23", + DMat24 => "dmat24", + DMat32 => "dmat32", + DMat34 => "dmat34", + DMat42 => "dmat42", + DMat43 => "dmat43", + Sampler1D => "sampler1D", + Image1D => "image1D", + Sampler2D => "sampler2D", + Image2D => "image2D", + Sampler3D => "sampler3D", + Image3D => "image3D", + SamplerCube => "samplerCube", + ImageCube => "imageCube", + Sampler2DRect => "sampler2DRect", + Image2DRect => "image2DRect", + Sampler1DArray => "sampler1DArray", + Image1DArray => "image1DArray", + Sampler2DArray => "sampler2DArray", + Image2DArray => "image2DArray", + SamplerBuffer => "samplerBuffer", + ImageBuffer => "imageBuffer", + Sampler2DMS => "sampler2DMS", + Image2DMS => "image2DMS", + Sampler2DMSArray => "sampler2DMSArray", + Image2DMSArray => "image2DMSArray", + SamplerCubeArray => "samplerCubeArray", + ImageCubeArray => "imageCubeArray", + Sampler1DShadow => "sampler1DShadow", + Sampler2DShadow => "sampler2DShadow", + Sampler2DRectShadow => "sampler2DRectShadow", + Sampler1DArrayShadow => "sampler1DArrayShadow", + Sampler2DArrayShadow => "sampler2DArrayShadow", + SamplerCubeShadow => "samplerCubeShadow", + SamplerCubeArrayShadow => "samplerCubeArrayShadow", + ISampler1D => "isampler1D", + IImage1D => "iimage1D", + ISampler2D => "isampler2D", + IImage2D => "iimage2D", + ISampler3D => "isampler3D", + IImage3D => "iimage3D", + ISamplerCube => "isamplerCube", + IImageCube => "iimageCube", + ISampler2DRect => "isampler2DRect", + IImage2DRect => "iimage2DRect", + ISampler1DArray => "isampler1DArray", + IImage1DArray => "iimage1DArray", + ISampler2DArray => "isampler2DArray", + IImage2DArray => "iimage2DArray", + ISamplerBuffer => "isamplerBuffer", + IImageBuffer => "iimageBuffer", + ISampler2DMS => "isampler2MS", + IImage2DMS => "iimage2DMS", + ISampler2DMSArray => "isampler2DMSArray", + IImage2DMSArray => "iimage2DMSArray", + ISamplerCubeArray => "isamplerCubeArray", + IImageCubeArray => "iimageCubeArray", + AtomicUInt => "atomic_uint", + USampler1D => "usampler1D", + UImage1D => "uimage1D", + USampler2D => "usampler2D", + UImage2D => "uimage2D", + USampler3D => "usampler3D", + UImage3D => "uimage3D", + USamplerCube => "usamplerCube", + UImageCube => "uimageCube", + USampler2DRect => "usampler2DRect", + UImage2DRect => "uimage2DRect", + USampler1DArray => "usampler1DArray", + UImage1DArray => "uimage1DArray", + USampler2DArray => "usampler2DArray", + UImage2DArray => "uimage2DArray", + USamplerBuffer => "usamplerBuffer", + UImageBuffer => "uimageBuffer", + USampler2DMS => "usampler2DMS", + UImage2DMS => "uimage2DMS", + USampler2DMSArray => "usamplerDMSArray", + UImage2DMSArray => "uimage2DMSArray", + USamplerCubeArray => "usamplerCubeArray", + UImageCubeArray => "uimageCubeArray", + Struct(..) => return None, + }) + } + + pub fn cxx_primitive_type_name(&self) -> Option<&'static str> { + use TypeKind::*; + match self { + Bool => Some("Bool"), + Int => Some("I32"), + UInt => Some("U32"), + Float => Some("Float"), + Double => Some("Double"), + _ => self.glsl_primitive_type_name(), + } + } + + pub fn cxx_primitive_scalar_type_name(&self) -> Option<&'static str> { + use TypeKind::*; + match self { + Void => Some("void"), + Bool => Some("bool"), + Int => Some("int32_t"), + UInt => Some("uint32_t"), + Float => Some("float"), + Double => Some("double"), + _ => { + if self.is_sampler() { + self.cxx_primitive_type_name() + } else { + None + } + } + } + } + + pub fn from_glsl_primitive_type_name(name: &str) -> Option<TypeKind> { + use TypeKind::*; + Some(match name { + "void" => Void, + "bool" => Bool, + "int" => Int, + "uint" => UInt, + "float" => Float, + "double" => Double, + "vec2" => Vec2, + "vec3" => Vec3, + "vec4" => Vec4, + "dvec2" => DVec2, + "dvec3" => DVec3, + "dvec4" => DVec4, + "bvec2" => BVec2, + "bvec3" => BVec3, + "bvec4" => BVec4, + "ivec2" => IVec2, + "ivec3" => IVec3, + "ivec4" => IVec4, + "uvec2" => UVec2, + "uvec3" => UVec3, + "uvec4" => UVec4, + "mat2" => Mat2, + "mat3" => Mat3, + "mat4" => Mat4, + "mat23" => Mat23, + "mat24" => Mat24, + "mat32" => Mat32, + "mat34" => Mat34, + "mat42" => Mat42, + "mat43" => Mat43, + "dmat2" => DMat2, + "dmat3" => DMat3, + "dmat4" => DMat4, + "dmat23" => DMat23, + "dmat24" => DMat24, + "dmat32" => DMat32, + "dmat34" => DMat34, + "dmat42" => DMat42, + "dmat43" => DMat43, + "sampler1D" => Sampler1D, + "image1D" => Image1D, + "sampler2D" => Sampler2D, + "image2D" => Image2D, + "sampler3D" => Sampler3D, + "image3D" => Image3D, + "samplerCube" => SamplerCube, + "imageCube" => ImageCube, + "sampler2DRect" => Sampler2DRect, + "image2DRect" => Image2DRect, + "sampler1DArray" => Sampler1DArray, + "image1DArray" => Image1DArray, + "sampler2DArray" => Sampler2DArray, + "image2DArray" => Image2DArray, + "samplerBuffer" => SamplerBuffer, + "imageBuffer" => ImageBuffer, + "sampler2DMS" => Sampler2DMS, + "image2DMS" => Image2DMS, + "sampler2DMSArray" => Sampler2DMSArray, + "image2DMSArray" => Image2DMSArray, + "samplerCubeArray" => SamplerCubeArray, + "imageCubeArray" => ImageCubeArray, + "sampler1DShadow" => Sampler1DShadow, + "sampler2DShadow" => Sampler2DShadow, + "sampler2DRectShadow" => Sampler2DRectShadow, + "sampler1DArrayShadow" => Sampler1DArrayShadow, + "sampler2DArrayShadow" => Sampler2DArrayShadow, + "samplerCubeShadow" => SamplerCubeShadow, + "samplerCubeArrayShadow" => SamplerCubeArrayShadow, + "isampler1D" => ISampler1D, + "iimage1D" => IImage1D, + "isampler2D" => ISampler2D, + "iimage2D" => IImage2D, + "isampler3D" => ISampler3D, + "iimage3D" => IImage3D, + "isamplerCube" => ISamplerCube, + "iimageCube" => IImageCube, + "isampler2DRect" => ISampler2DRect, + "iimage2DRect" => IImage2DRect, + "isampler1DArray" => ISampler1DArray, + "iimage1DArray" => IImage1DArray, + "isampler2DArray" => ISampler2DArray, + "iimage2DArray" => IImage2DArray, + "isamplerBuffer" => ISamplerBuffer, + "iimageBuffer" => IImageBuffer, + "isampler2MS" => ISampler2DMS, + "iimage2DMS" => IImage2DMS, + "isampler2DMSArray" => ISampler2DMSArray, + "iimage2DMSArray" => IImage2DMSArray, + "isamplerCubeArray" => ISamplerCubeArray, + "iimageCubeArray" => IImageCubeArray, + "atomic_uint" => AtomicUInt, + "usampler1D" => USampler1D, + "uimage1D" => UImage1D, + "usampler2D" => USampler2D, + "uimage2D" => UImage2D, + "usampler3D" => USampler3D, + "uimage3D" => UImage3D, + "usamplerCube" => USamplerCube, + "uimageCube" => UImageCube, + "usampler2DRect" => USampler2DRect, + "uimage2DRect" => UImage2DRect, + "usampler1DArray" => USampler1DArray, + "uimage1DArray" => UImage1DArray, + "usampler2DArray" => USampler2DArray, + "uimage2DArray" => UImage2DArray, + "usamplerBuffer" => USamplerBuffer, + "uimageBuffer" => UImageBuffer, + "usampler2DMS" => USampler2DMS, + "uimage2DMS" => UImage2DMS, + "usamplerDMSArray" => USampler2DMSArray, + "uimage2DMSArray" => UImage2DMSArray, + "usamplerCubeArray" => USamplerCubeArray, + "uimageCubeArray" => UImageCubeArray, + _ => return None, + }) + } + + pub fn from_primitive_type_specifier(spec: &syntax::TypeSpecifierNonArray) -> Option<TypeKind> { + use TypeKind::*; + Some(match spec { + TypeSpecifierNonArray::Void => Void, + TypeSpecifierNonArray::Bool => Bool, + TypeSpecifierNonArray::Int => Int, + TypeSpecifierNonArray::UInt => UInt, + TypeSpecifierNonArray::Float => Float, + TypeSpecifierNonArray::Double => Double, + TypeSpecifierNonArray::Vec2 => Vec2, + TypeSpecifierNonArray::Vec3 => Vec3, + TypeSpecifierNonArray::Vec4 => Vec4, + TypeSpecifierNonArray::DVec2 => DVec2, + TypeSpecifierNonArray::DVec3 => DVec3, + TypeSpecifierNonArray::DVec4 => DVec4, + TypeSpecifierNonArray::BVec2 => BVec2, + TypeSpecifierNonArray::BVec3 => BVec3, + TypeSpecifierNonArray::BVec4 => BVec4, + TypeSpecifierNonArray::IVec2 => IVec2, + TypeSpecifierNonArray::IVec3 => IVec3, + TypeSpecifierNonArray::IVec4 => IVec4, + TypeSpecifierNonArray::UVec2 => UVec2, + TypeSpecifierNonArray::UVec3 => UVec3, + TypeSpecifierNonArray::UVec4 => UVec4, + TypeSpecifierNonArray::Mat2 => Mat2, + TypeSpecifierNonArray::Mat3 => Mat3, + TypeSpecifierNonArray::Mat4 => Mat4, + TypeSpecifierNonArray::Mat23 => Mat23, + TypeSpecifierNonArray::Mat24 => Mat24, + TypeSpecifierNonArray::Mat32 => Mat32, + TypeSpecifierNonArray::Mat34 => Mat34, + TypeSpecifierNonArray::Mat42 => Mat42, + TypeSpecifierNonArray::Mat43 => Mat43, + TypeSpecifierNonArray::DMat2 => DMat2, + TypeSpecifierNonArray::DMat3 => DMat3, + TypeSpecifierNonArray::DMat4 => DMat4, + TypeSpecifierNonArray::DMat23 => DMat23, + TypeSpecifierNonArray::DMat24 => DMat24, + TypeSpecifierNonArray::DMat32 => DMat32, + TypeSpecifierNonArray::DMat34 => DMat34, + TypeSpecifierNonArray::DMat42 => DMat42, + TypeSpecifierNonArray::DMat43 => DMat43, + TypeSpecifierNonArray::Sampler1D => Sampler1D, + TypeSpecifierNonArray::Image1D => Image1D, + TypeSpecifierNonArray::Sampler2D => Sampler2D, + TypeSpecifierNonArray::Image2D => Image2D, + TypeSpecifierNonArray::Sampler3D => Sampler3D, + TypeSpecifierNonArray::Image3D => Image3D, + TypeSpecifierNonArray::SamplerCube => SamplerCube, + TypeSpecifierNonArray::ImageCube => ImageCube, + TypeSpecifierNonArray::Sampler2DRect => Sampler2DRect, + TypeSpecifierNonArray::Image2DRect => Image2DRect, + TypeSpecifierNonArray::Sampler1DArray => Sampler1DArray, + TypeSpecifierNonArray::Image1DArray => Image1DArray, + TypeSpecifierNonArray::Sampler2DArray => Sampler2DArray, + TypeSpecifierNonArray::Image2DArray => Image2DArray, + TypeSpecifierNonArray::SamplerBuffer => SamplerBuffer, + TypeSpecifierNonArray::ImageBuffer => ImageBuffer, + TypeSpecifierNonArray::Sampler2DMS => Sampler2DMS, + TypeSpecifierNonArray::Image2DMS => Image2DMS, + TypeSpecifierNonArray::Sampler2DMSArray => Sampler2DMSArray, + TypeSpecifierNonArray::Image2DMSArray => Image2DMSArray, + TypeSpecifierNonArray::SamplerCubeArray => SamplerCubeArray, + TypeSpecifierNonArray::ImageCubeArray => ImageCubeArray, + TypeSpecifierNonArray::Sampler1DShadow => Sampler1DShadow, + TypeSpecifierNonArray::Sampler2DShadow => Sampler2DShadow, + TypeSpecifierNonArray::Sampler2DRectShadow => Sampler2DRectShadow, + TypeSpecifierNonArray::Sampler1DArrayShadow => Sampler1DArrayShadow, + TypeSpecifierNonArray::Sampler2DArrayShadow => Sampler2DArrayShadow, + TypeSpecifierNonArray::SamplerCubeShadow => SamplerCubeShadow, + TypeSpecifierNonArray::SamplerCubeArrayShadow => SamplerCubeArrayShadow, + TypeSpecifierNonArray::ISampler1D => ISampler1D, + TypeSpecifierNonArray::IImage1D => IImage1D, + TypeSpecifierNonArray::ISampler2D => ISampler2D, + TypeSpecifierNonArray::IImage2D => IImage2D, + TypeSpecifierNonArray::ISampler3D => ISampler3D, + TypeSpecifierNonArray::IImage3D => IImage3D, + TypeSpecifierNonArray::ISamplerCube => ISamplerCube, + TypeSpecifierNonArray::IImageCube => IImageCube, + TypeSpecifierNonArray::ISampler2DRect => ISampler2DRect, + TypeSpecifierNonArray::IImage2DRect => IImage2DRect, + TypeSpecifierNonArray::ISampler1DArray => ISampler1DArray, + TypeSpecifierNonArray::IImage1DArray => IImage1DArray, + TypeSpecifierNonArray::ISampler2DArray => ISampler2DArray, + TypeSpecifierNonArray::IImage2DArray => IImage2DArray, + TypeSpecifierNonArray::ISamplerBuffer => ISamplerBuffer, + TypeSpecifierNonArray::IImageBuffer => IImageBuffer, + TypeSpecifierNonArray::ISampler2DMS => ISampler2DMS, + TypeSpecifierNonArray::IImage2DMS => IImage2DMS, + TypeSpecifierNonArray::ISampler2DMSArray => ISampler2DMSArray, + TypeSpecifierNonArray::IImage2DMSArray => IImage2DMSArray, + TypeSpecifierNonArray::ISamplerCubeArray => ISamplerCubeArray, + TypeSpecifierNonArray::IImageCubeArray => IImageCubeArray, + TypeSpecifierNonArray::AtomicUInt => AtomicUInt, + TypeSpecifierNonArray::USampler1D => USampler1D, + TypeSpecifierNonArray::UImage1D => UImage1D, + TypeSpecifierNonArray::USampler2D => USampler2D, + TypeSpecifierNonArray::UImage2D => UImage2D, + TypeSpecifierNonArray::USampler3D => USampler3D, + TypeSpecifierNonArray::UImage3D => UImage3D, + TypeSpecifierNonArray::USamplerCube => USamplerCube, + TypeSpecifierNonArray::UImageCube => UImageCube, + TypeSpecifierNonArray::USampler2DRect => USampler2DRect, + TypeSpecifierNonArray::UImage2DRect => UImage2DRect, + TypeSpecifierNonArray::USampler1DArray => USampler1DArray, + TypeSpecifierNonArray::UImage1DArray => UImage1DArray, + TypeSpecifierNonArray::USampler2DArray => USampler2DArray, + TypeSpecifierNonArray::UImage2DArray => UImage2DArray, + TypeSpecifierNonArray::USamplerBuffer => USamplerBuffer, + TypeSpecifierNonArray::UImageBuffer => UImageBuffer, + TypeSpecifierNonArray::USampler2DMS => USampler2DMS, + TypeSpecifierNonArray::UImage2DMS => UImage2DMS, + TypeSpecifierNonArray::USampler2DMSArray => USampler2DMSArray, + TypeSpecifierNonArray::UImage2DMSArray => UImage2DMSArray, + TypeSpecifierNonArray::USamplerCubeArray => USamplerCubeArray, + TypeSpecifierNonArray::UImageCubeArray => UImageCubeArray, + TypeSpecifierNonArray::Struct(..) | TypeSpecifierNonArray::TypeName(..) => return None, + }) + } +} + +impl LiftFrom<&syntax::TypeSpecifierNonArray> for TypeKind { + fn lift(state: &mut State, spec: &syntax::TypeSpecifierNonArray) -> Self { + use TypeKind::*; + if let Some(kind) = TypeKind::from_primitive_type_specifier(spec) { + kind + } else { + match spec { + TypeSpecifierNonArray::Struct(s) => { + Struct(state.lookup(s.name.as_ref().unwrap().as_str()).unwrap()) + } + TypeSpecifierNonArray::TypeName(s) => Struct(state.lookup(&s.0).unwrap()), + _ => unreachable!(), + } + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Type { + pub kind: TypeKind, + pub precision: Option<PrecisionQualifier>, + pub array_sizes: Option<Box<ArraySizes>>, +} + +impl Type { + pub fn new(kind: TypeKind) -> Self { + Type { + kind, + precision: None, + array_sizes: None, + } + } + + pub fn new_array(kind: TypeKind, size: i32) -> Self { + Type { + kind, + precision: None, + array_sizes: Some(Box::new(ArraySizes { sizes: vec![make_const(TypeKind::Int, size)] })), + } + } +} + +impl LiftFrom<&syntax::FullySpecifiedType> for Type { + fn lift(state: &mut State, ty: &syntax::FullySpecifiedType) -> Self { + let kind = lift(state, &ty.ty.ty); + let array_sizes = match ty.ty.array_specifier.as_ref() { + Some(x) => Some(Box::new(lift(state, x))), + None => None, + }; + let precision = get_precision(&ty.qualifier); + Type { + kind, + precision, + array_sizes, + } + } +} + +impl LiftFrom<&syntax::TypeSpecifier> for Type { + fn lift(state: &mut State, ty: &syntax::TypeSpecifier) -> Self { + let kind = lift(state, &ty.ty); + let array_sizes = ty + .array_specifier + .as_ref() + .map(|x| Box::new(lift(state, x))); + Type { + kind, + precision: None, + array_sizes, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct StructField { + pub ty: Type, + pub name: syntax::Identifier, +} + +fn get_precision(qualifiers: &Option<syntax::TypeQualifier>) -> Option<PrecisionQualifier> { + let mut precision = None; + for qual in qualifiers.iter().flat_map(|x| x.qualifiers.0.iter()) { + match qual { + syntax::TypeQualifierSpec::Precision(p) => { + if precision.is_some() { + panic!("Multiple precisions"); + } + precision = Some(p.clone()); + } + _ => {} + } + } + precision +} + +impl LiftFrom<&StructFieldSpecifier> for StructField { + fn lift(state: &mut State, f: &StructFieldSpecifier) -> Self { + let mut ty: Type = lift(state, &f.ty); + match &f.identifiers.0[..] { + [ident] => { + if let Some(a) = &ident.array_spec { + ty.array_sizes = Some(Box::new(lift(state, a))); + } + StructField { + ty, + name: ident.ident.clone(), + } + } + _ => panic!("bad number of identifiers"), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct StructFields { + pub fields: Vec<StructField>, +} + +impl LiftFrom<&StructSpecifier> for StructFields { + fn lift(state: &mut State, s: &StructSpecifier) -> Self { + let fields = s.fields.0.iter().map(|field| lift(state, field)).collect(); + Self { fields } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum RunClass { + Unknown, + Scalar, + Vector, + Dependent(u32), +} + +impl RunClass { + pub fn merge(self, run_class: RunClass) -> RunClass { + match (self, run_class) { + (RunClass::Vector, _) | (_, RunClass::Vector) => RunClass::Vector, + (RunClass::Dependent(x), RunClass::Dependent(y)) => RunClass::Dependent(x | y), + (RunClass::Unknown, _) | (_, RunClass::Dependent(..)) => run_class, + _ => self, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum SymDecl { + NativeFunction(FunctionType, Option<&'static str>, RunClass), + UserFunction(Rc<FunctionDefinition>, RunClass), + Local(StorageClass, Type, RunClass), + Global( + StorageClass, + Option<syntax::InterpolationQualifier>, + Type, + RunClass, + ), + Struct(StructFields), +} + +#[derive(Clone, Debug, PartialEq, Copy, Eq, Hash)] +pub struct SymRef(u32); + +#[derive(Debug)] +struct Scope { + // The field is not actively used but useful for `Debug`. + #[allow(dead_code)] + name: String, + names: HashMap<String, SymRef>, +} +impl Scope { + fn new(name: String) -> Self { + Scope { + name, + names: HashMap::new(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct TexelFetchOffsets { + pub min_x: i32, + pub max_x: i32, + pub min_y: i32, + pub max_y: i32, +} + +impl TexelFetchOffsets { + fn new(x: i32, y: i32) -> Self { + TexelFetchOffsets { + min_x: x, + max_x: x, + min_y: y, + max_y: y, + } + } + + fn add_offset(&mut self, x: i32, y: i32) { + self.min_x = self.min_x.min(x); + self.max_x = self.max_x.max(x); + self.min_y = self.min_y.min(y); + self.max_y = self.max_y.max(y); + } +} + +#[derive(Debug)] +pub struct State { + scopes: Vec<Scope>, + syms: Vec<RefCell<Symbol>>, + in_function: Option<SymRef>, + run_class_changed: Cell<bool>, + last_declaration: SymRef, + branch_run_class: RunClass, + branch_declaration: SymRef, + modified_globals: RefCell<Vec<SymRef>>, + pub used_globals: RefCell<Vec<SymRef>>, + pub texel_fetches: HashMap<(SymRef, SymRef), TexelFetchOffsets>, + clip_dist_sym: SymRef, + pub used_clip_dist: u32, +} + +impl State { + pub fn new() -> Self { + State { + scopes: Vec::new(), + syms: Vec::new(), + in_function: None, + run_class_changed: Cell::new(false), + last_declaration: SymRef(0), + branch_run_class: RunClass::Unknown, + branch_declaration: SymRef(0), + modified_globals: RefCell::new(Vec::new()), + used_globals: RefCell::new(Vec::new()), + texel_fetches: HashMap::new(), + clip_dist_sym: SymRef(0), + used_clip_dist: 0, + } + } + + pub fn lookup(&self, name: &str) -> Option<SymRef> { + for s in self.scopes.iter().rev() { + if let Some(sym) = s.names.get(name) { + return Some(*sym); + } + } + return None; + } + + fn declare(&mut self, name: &str, decl: SymDecl) -> SymRef { + let s = SymRef(self.syms.len() as u32); + self.syms.push(RefCell::new(Symbol { + name: name.into(), + decl, + })); + self.scopes.last_mut().unwrap().names.insert(name.into(), s); + s + } + + pub fn sym(&self, sym: SymRef) -> Ref<Symbol> { + self.syms[sym.0 as usize].borrow() + } + + pub fn sym_mut(&mut self, sym: SymRef) -> &mut Symbol { + self.syms[sym.0 as usize].get_mut() + } + + pub fn lookup_sym_mut(&mut self, name: &str) -> Option<&mut Symbol> { + self.lookup(name) + .map(move |x| self.syms[x.0 as usize].get_mut()) + } + + fn push_scope(&mut self, name: String) { + self.scopes.push(Scope::new(name)); + } + fn pop_scope(&mut self) { + self.scopes.pop(); + } + + fn return_run_class(&self, mut new_run_class: RunClass) { + new_run_class = self.branch_run_class.merge(new_run_class); + if let Some(sym) = self.in_function { + let mut b = self.syms[sym.0 as usize].borrow_mut(); + if let SymDecl::UserFunction(_, ref mut run_class) = b.decl { + *run_class = run_class.merge(new_run_class); + } + } + } + + pub fn function_definition(&self, name: SymRef) -> Option<(Rc<FunctionDefinition>, RunClass)> { + if let SymDecl::UserFunction(ref fd, ref run_class) = &self.sym(name).decl { + Some((fd.clone(), *run_class)) + } else { + None + } + } + + fn merge_run_class(&self, sym: SymRef, mut new_run_class: RunClass) -> RunClass { + if sym.0 <= self.branch_declaration.0 { + new_run_class = self.branch_run_class.merge(new_run_class); + } + let mut b = self.syms[sym.0 as usize].borrow_mut(); + let mut old_run_class = new_run_class; + if let SymDecl::Local(_, _, ref mut run_class) = b.decl { + old_run_class = *run_class; + new_run_class = old_run_class.merge(new_run_class); + *run_class = new_run_class; + } + if old_run_class != RunClass::Unknown && old_run_class != new_run_class { + self.run_class_changed.set(true); + } + new_run_class + } +} + +/// A declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum Declaration { + FunctionPrototype(FunctionPrototype), + StructDefinition(SymRef), + InitDeclaratorList(InitDeclaratorList), + Precision(PrecisionQualifier, TypeSpecifier), + Block(Block), + Global(TypeQualifier, Vec<Identifier>), +} + +/// A general purpose block, containing fields and possibly a list of declared identifiers. Semantic +/// is given with the storage qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct Block { + pub qualifier: TypeQualifier, + pub name: Identifier, + pub fields: Vec<StructFieldSpecifier>, + pub identifier: Option<ArrayedIdentifier>, +} + +/// Function identifier. +#[derive(Clone, Debug, PartialEq)] +pub enum FunIdentifier { + Identifier(SymRef), + Constructor(Type), +} + +/// Function prototype. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionPrototype { + pub ty: Type, + pub name: Identifier, + pub parameters: Vec<FunctionParameterDeclaration>, +} + +impl FunctionPrototype { + pub fn has_parameter(&self, sym: SymRef) -> bool { + for param in &self.parameters { + match param { + FunctionParameterDeclaration::Named(_, ref d) => { + if d.sym == sym { + return true; + } + } + _ => {} + } + } + false + } +} + +/// Function parameter declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum FunctionParameterDeclaration { + Named(Option<ParameterQualifier>, FunctionParameterDeclarator), + Unnamed(Option<ParameterQualifier>, TypeSpecifier), +} + +/// Function parameter declarator. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionParameterDeclarator { + pub ty: Type, + pub name: Identifier, + pub sym: SymRef, +} + +/// Init declarator list. +#[derive(Clone, Debug, PartialEq)] +pub struct InitDeclaratorList { + // XXX it feels like separating out the type and the names is better than + // head and tail + // Also, it might be nice to separate out type definitions from name definitions + pub head: SingleDeclaration, + pub tail: Vec<SingleDeclarationNoType>, +} + +/// Type qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct TypeQualifier { + pub qualifiers: NonEmpty<TypeQualifierSpec>, +} + +fn lift_type_qualifier_for_declaration( + _state: &mut State, + q: &Option<syntax::TypeQualifier>, +) -> Option<TypeQualifier> { + q.as_ref().and_then(|x| { + NonEmpty::from_non_empty_iter(x.qualifiers.0.iter().flat_map(|x| match x { + syntax::TypeQualifierSpec::Precision(_) => None, + syntax::TypeQualifierSpec::Interpolation(_) => None, + syntax::TypeQualifierSpec::Invariant => Some(TypeQualifierSpec::Invariant), + syntax::TypeQualifierSpec::Layout(l) => Some(TypeQualifierSpec::Layout(l.clone())), + syntax::TypeQualifierSpec::Precise => Some(TypeQualifierSpec::Precise), + syntax::TypeQualifierSpec::Storage(_) => None, + })) + .map(|x| TypeQualifier { qualifiers: x }) + }) +} + +fn lift_type_qualifier_for_parameter( + _state: &mut State, + q: &Option<syntax::TypeQualifier>, +) -> Option<ParameterQualifier> { + let mut qp: Option<ParameterQualifier> = None; + if let Some(q) = q { + for x in &q.qualifiers.0 { + match (&qp, x) { + (None, syntax::TypeQualifierSpec::Storage(s)) => match s { + syntax::StorageQualifier::Const => qp = Some(ParameterQualifier::Const), + syntax::StorageQualifier::In => qp = Some(ParameterQualifier::In), + syntax::StorageQualifier::Out => qp = Some(ParameterQualifier::Out), + syntax::StorageQualifier::InOut => qp = Some(ParameterQualifier::InOut), + _ => panic!("Bad storage qualifier for parameter"), + }, + (_, syntax::TypeQualifierSpec::Precision(_)) => {} + _ => panic!("Bad parameter qualifier {:?}", x), + } + } + } + qp +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ParameterQualifier { + Const, + In, + InOut, + Out, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum MemoryQualifier { + Coherent, + Volatile, + Restrict, + ReadOnly, + WriteOnly, +} + +/// Type qualifier spec. +#[derive(Clone, Debug, PartialEq)] +pub enum TypeQualifierSpec { + Layout(syntax::LayoutQualifier), + Invariant, + Parameter(ParameterQualifier), + Memory(MemoryQualifier), + Precise, +} + +/// Single declaration. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleDeclaration { + pub ty: Type, + pub ty_def: Option<SymRef>, + pub qualifier: Option<TypeQualifier>, + pub name: SymRef, + pub initializer: Option<Initializer>, +} + +/// A single declaration with implicit, already-defined type. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleDeclarationNoType { + pub ident: ArrayedIdentifier, + pub initializer: Option<Initializer>, +} + +/// Initializer. +#[derive(Clone, Debug, PartialEq)] +pub enum Initializer { + Simple(Box<Expr>), + List(NonEmpty<Initializer>), +} + +impl From<Expr> for Initializer { + fn from(e: Expr) -> Self { + Initializer::Simple(Box::new(e)) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Expr { + pub kind: ExprKind, + pub ty: Type, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum FieldSet { + Rgba, + Xyzw, + Stpq, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct SwizzleSelector { + pub field_set: FieldSet, + pub components: Vec<i8>, +} + +impl SwizzleSelector { + fn parse(s: &str) -> Self { + let mut components = Vec::new(); + let mut field_set = Vec::new(); + + for c in s.chars() { + match c { + 'r' => { + components.push(0); + field_set.push(FieldSet::Rgba); + } + 'x' => { + components.push(0); + field_set.push(FieldSet::Xyzw); + } + 's' => { + components.push(0); + field_set.push(FieldSet::Stpq); + } + + 'g' => { + components.push(1); + field_set.push(FieldSet::Rgba); + } + 'y' => { + components.push(1); + field_set.push(FieldSet::Xyzw); + } + 't' => { + components.push(1); + field_set.push(FieldSet::Stpq); + } + + 'b' => { + components.push(2); + field_set.push(FieldSet::Rgba); + } + 'z' => { + components.push(2); + field_set.push(FieldSet::Xyzw); + } + 'p' => { + components.push(2); + field_set.push(FieldSet::Stpq); + } + + 'a' => { + components.push(3); + field_set.push(FieldSet::Rgba); + } + 'w' => { + components.push(3); + field_set.push(FieldSet::Xyzw); + } + 'q' => { + components.push(3); + field_set.push(FieldSet::Stpq); + } + _ => panic!("bad selector"), + } + } + + let first = &field_set[0]; + assert!(field_set.iter().all(|item| item == first)); + assert!(components.len() <= 4); + SwizzleSelector { + field_set: first.clone(), + components, + } + } + + pub fn to_field_set(&self, field_set: FieldSet) -> String { + let mut s = String::new(); + let fs = match field_set { + FieldSet::Rgba => ['r', 'g', 'b', 'a'], + FieldSet::Xyzw => ['x', 'y', 'z', 'w'], + FieldSet::Stpq => ['s', 't', 'p', 'q'], + }; + for i in &self.components { + s.push(fs[*i as usize]) + } + s + } + + pub fn to_string(&self) -> String { + self.to_field_set(self.field_set) + } +} + +/// The most general form of an expression. As you can see if you read the variant list, in GLSL, an +/// assignment is an expression. This is a bit silly but think of an assignment as a statement first +/// then an expression which evaluates to what the statement “returns”. +/// +/// An expression is either an assignment or a list (comma) of assignments. +#[derive(Clone, Debug, PartialEq)] +pub enum ExprKind { + /// A variable expression, using an identifier. + Variable(SymRef), + /// Integral constant expression. + IntConst(i32), + /// Unsigned integral constant expression. + UIntConst(u32), + /// Boolean constant expression. + BoolConst(bool), + /// Single precision floating expression. + FloatConst(f32), + /// Double precision floating expression. + DoubleConst(f64), + /// A unary expression, gathering a single expression and a unary operator. + Unary(UnaryOp, Box<Expr>), + /// A binary expression, gathering two expressions and a binary operator. + Binary(BinaryOp, Box<Expr>, Box<Expr>), + /// A ternary conditional expression, gathering three expressions. + Ternary(Box<Expr>, Box<Expr>, Box<Expr>), + /// An assignment is also an expression. Gathers an expression that defines what to assign to, an + /// assignment operator and the value to associate with. + Assignment(Box<Expr>, AssignmentOp, Box<Expr>), + /// Add an array specifier to an expression. + Bracket(Box<Expr>, Vec<Expr>), + /// A functional call. It has a function identifier and a list of expressions (arguments). + FunCall(FunIdentifier, Vec<Expr>), + /// An expression associated with a field selection (struct). + Dot(Box<Expr>, Identifier), + /// An expression associated with a component selection + SwizzleSelector(Box<Expr>, SwizzleSelector), + /// Post-incrementation of an expression. + PostInc(Box<Expr>), + /// Post-decrementation of an expression. + PostDec(Box<Expr>), + /// An expression that contains several, separated with comma. + Comma(Box<Expr>, Box<Expr>), + /// A temporary condition variable + Cond(usize, Box<Expr>), + CondMask, +} + +/* +impl From<i32> for Expr { + fn from(x: i32) -> Expr { + ExprKind::IntConst(x) + } +} + +impl From<u32> for Expr { + fn from(x: u32) -> Expr { + Expr::UIntConst(x) + } +} + +impl From<bool> for Expr { + fn from(x: bool) -> Expr { + Expr::BoolConst(x) + } +} + +impl From<f32> for Expr { + fn from(x: f32) -> Expr { + Expr::FloatConst(x) + } +} + +impl From<f64> for Expr { + fn from(x: f64) -> Expr { + Expr::DoubleConst(x) + } +} +*/ +/// Starting rule. +#[derive(Clone, Debug, PartialEq)] +pub struct TranslationUnit(pub NonEmpty<ExternalDeclaration>); + +impl TranslationUnit { + /// Construct a translation unit from an iterator. + /// + /// # Errors + /// + /// `None` if the iterator yields no value. + pub fn from_iter<I>(iter: I) -> Option<Self> + where + I: IntoIterator<Item = ExternalDeclaration>, + { + NonEmpty::from_non_empty_iter(iter).map(TranslationUnit) + } +} + +impl Deref for TranslationUnit { + type Target = NonEmpty<ExternalDeclaration>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for TranslationUnit { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl IntoIterator for TranslationUnit { + type IntoIter = <NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a TranslationUnit { + type IntoIter = <&'a NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = &'a ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + (&self.0).into_iter() + } +} + +impl<'a> IntoIterator for &'a mut TranslationUnit { + type IntoIter = <&'a mut NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = &'a mut ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + (&mut self.0).into_iter() + } +} + +/// External declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum ExternalDeclaration { + Preprocessor(syntax::Preprocessor), + FunctionDefinition(Rc<FunctionDefinition>), + Declaration(Declaration), +} + +/// Function definition. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionDefinition { + pub prototype: FunctionPrototype, + pub body: CompoundStatement, + pub globals: Vec<SymRef>, + pub texel_fetches: HashMap<(SymRef, SymRef), TexelFetchOffsets>, +} + +/// Compound statement (with no new scope). +#[derive(Clone, Debug, PartialEq)] +pub struct CompoundStatement { + pub statement_list: Vec<Statement>, +} + +impl CompoundStatement { + pub fn new() -> Self { + CompoundStatement { + statement_list: Vec::new(), + } + } +} + +impl FromIterator<Statement> for CompoundStatement { + fn from_iter<T>(iter: T) -> Self + where + T: IntoIterator<Item = Statement>, + { + CompoundStatement { + statement_list: iter.into_iter().collect(), + } + } +} + +/// Statement. +#[derive(Clone, Debug, PartialEq)] +pub enum Statement { + Compound(Box<CompoundStatement>), + Simple(Box<SimpleStatement>), +} + +/// Simple statement. +#[derive(Clone, Debug, PartialEq)] +pub enum SimpleStatement { + Declaration(Declaration), + Expression(ExprStatement), + Selection(SelectionStatement), + Switch(SwitchStatement), + Iteration(IterationStatement), + Jump(JumpStatement), +} + +impl SimpleStatement { + /// Create a new expression statement. + pub fn new_expr<E>(expr: E) -> Self + where + E: Into<Expr>, + { + SimpleStatement::Expression(Some(expr.into())) + } + + /// Create a new selection statement (if / else). + pub fn new_if_else<If, True, False>(ife: If, truee: True, falsee: False) -> Self + where + If: Into<Expr>, + True: Into<Statement>, + False: Into<Statement>, + { + SimpleStatement::Selection(SelectionStatement { + cond: Box::new(ife.into()), + body: Box::new(truee.into()), + else_stmt: Some(Box::new(falsee.into())), + }) + } + + /// Create a new while statement. + pub fn new_while<C, S>(cond: C, body: S) -> Self + where + C: Into<Condition>, + S: Into<Statement>, + { + SimpleStatement::Iteration(IterationStatement::While( + cond.into(), + Box::new(body.into()), + )) + } + + /// Create a new do-while statement. + pub fn new_do_while<C, S>(body: S, cond: C) -> Self + where + S: Into<Statement>, + C: Into<Expr>, + { + SimpleStatement::Iteration(IterationStatement::DoWhile( + Box::new(body.into()), + Box::new(cond.into()), + )) + } +} + +/// Expression statement. +pub type ExprStatement = Option<Expr>; + +/// Selection statement. +#[derive(Clone, Debug, PartialEq)] +pub struct SelectionStatement { + pub cond: Box<Expr>, + pub body: Box<Statement>, + // the else branch + pub else_stmt: Option<Box<Statement>>, +} + +/// Condition. +#[derive(Clone, Debug, PartialEq)] +pub enum Condition { + Expr(Box<Expr>), +} + +impl From<Expr> for Condition { + fn from(expr: Expr) -> Self { + Condition::Expr(Box::new(expr)) + } +} + +/// Switch statement. +#[derive(Clone, Debug, PartialEq)] +pub struct SwitchStatement { + pub head: Box<Expr>, + pub cases: Vec<Case>, +} + +/// Case label statement. +#[derive(Clone, Debug, PartialEq)] +pub enum CaseLabel { + Case(Box<Expr>), + Def, +} + +/// An individual case +#[derive(Clone, Debug, PartialEq)] +pub struct Case { + pub label: CaseLabel, + pub stmts: Vec<Statement>, +} + +/// Iteration statement. +#[derive(Clone, Debug, PartialEq)] +pub enum IterationStatement { + While(Condition, Box<Statement>), + DoWhile(Box<Statement>, Box<Expr>), + For(ForInitStatement, ForRestStatement, Box<Statement>), +} + +/// For init statement. +#[derive(Clone, Debug, PartialEq)] +pub enum ForInitStatement { + Expression(Option<Expr>), + Declaration(Box<Declaration>), +} + +/// For init statement. +#[derive(Clone, Debug, PartialEq)] +pub struct ForRestStatement { + pub condition: Option<Condition>, + pub post_expr: Option<Box<Expr>>, +} + +/// Jump statement. +#[derive(Clone, Debug, PartialEq)] +pub enum JumpStatement { + Continue, + Break, + Return(Option<Box<Expr>>), + Discard, +} + +trait NonEmptyExt<T> { + fn map<U, F: FnMut(&mut State, &T) -> U>(&self, s: &mut State, f: F) -> NonEmpty<U>; + fn new(x: T) -> NonEmpty<T>; +} + +impl<T> NonEmptyExt<T> for NonEmpty<T> { + fn map<U, F: FnMut(&mut State, &T) -> U>(&self, s: &mut State, mut f: F) -> NonEmpty<U> { + NonEmpty::from_non_empty_iter(self.into_iter().map(|x| f(s, &x))).unwrap() + } + fn new(x: T) -> NonEmpty<T> { + NonEmpty::from_non_empty_iter(vec![x].into_iter()).unwrap() + } +} + +fn translate_initializater(state: &mut State, i: &syntax::Initializer) -> Initializer { + match i { + syntax::Initializer::Simple(i) => { + Initializer::Simple(Box::new(translate_expression(state, i))) + } + _ => panic!(), + } +} + +fn translate_struct_declaration(state: &mut State, d: &syntax::SingleDeclaration) -> Declaration { + let ty = d.ty.clone(); + let ty_def = match &ty.ty.ty { + TypeSpecifierNonArray::Struct(s) => { + let decl = SymDecl::Struct(lift(state, s)); + Some(state.declare(s.name.as_ref().unwrap().as_str(), decl)) + } + _ => None, + }; + + let ty_def = ty_def.expect("Must be type definition"); + + Declaration::StructDefinition(ty_def) +} + +fn get_expr_index(e: &syntax::Expr) -> i32 { + match e { + syntax::Expr::IntConst(i) => *i, + syntax::Expr::UIntConst(u) => *u as i32, + syntax::Expr::FloatConst(f) => *f as i32, + syntax::Expr::DoubleConst(f) => *f as i32, + _ => panic!(), + } +} + +fn translate_variable_declaration( + state: &mut State, + d: &syntax::InitDeclaratorList, + default_run_class: RunClass, +) -> Declaration { + let mut ty = d.head.ty.clone(); + ty.ty.array_specifier = d.head.array_specifier.clone(); + let ty_def = match &ty.ty.ty { + TypeSpecifierNonArray::Struct(s) => { + let decl = SymDecl::Struct(lift(state, s)); + Some(state.declare(s.name.as_ref().unwrap().as_str(), decl)) + } + _ => None, + }; + + let mut ty: Type = lift(state, &d.head.ty); + if let Some(array) = &d.head.array_specifier { + ty.array_sizes = Some(Box::new(lift(state, array))) + } + + let (sym, decl) = match d.head.name.as_ref() { + Some(name) => { + let mut storage = StorageClass::None; + let mut interpolation = None; + for qual in d + .head + .ty + .qualifier + .iter() + .flat_map(|x| x.qualifiers.0.iter()) + { + match qual { + syntax::TypeQualifierSpec::Storage(s) => match (&storage, s) { + (StorageClass::FragColor(..), syntax::StorageQualifier::Out) => {} + (StorageClass::Sampler(..), syntax::StorageQualifier::Uniform) => {} + (StorageClass::None, syntax::StorageQualifier::Out) => { + storage = StorageClass::Out; + } + (StorageClass::None, syntax::StorageQualifier::In) => { + storage = StorageClass::In; + } + (StorageClass::None, syntax::StorageQualifier::Uniform) => { + if ty.kind.is_sampler() { + storage = StorageClass::Sampler(SamplerFormat::Unknown); + } else { + storage = StorageClass::Uniform; + } + } + (StorageClass::None, syntax::StorageQualifier::Const) => { + storage = StorageClass::Const; + } + _ => panic!("bad storage {:?}", (storage, s)), + }, + syntax::TypeQualifierSpec::Interpolation(i) => match (&interpolation, i) { + (None, i) => interpolation = Some(i.clone()), + _ => panic!("multiple interpolation"), + }, + syntax::TypeQualifierSpec::Layout(l) => { + let mut loc = -1; + let mut index = -1; + for id in &l.ids { + match id { + syntax::LayoutQualifierSpec::Identifier(ref key, None) => { + match key.as_str() { + "rgba8" => { + storage = StorageClass::Sampler(SamplerFormat::RGBA8); + } + "rgba32f" => { + storage = StorageClass::Sampler(SamplerFormat::RGBA32F); + } + "rgba32i" => { + storage = StorageClass::Sampler(SamplerFormat::RGBA32I); + } + "r8" => { + storage = StorageClass::Sampler(SamplerFormat::R8); + } + "rg8" => { + storage = StorageClass::Sampler(SamplerFormat::RG8); + } + _ => {} + } + } + syntax::LayoutQualifierSpec::Identifier(ref key, Some(ref e)) => { + match key.as_str() { + "location" => { + loc = get_expr_index(e); + } + "index" => { + index = get_expr_index(e); + } + _ => {} + } + } + _ => {} + } + } + if index >= 0 { + assert!(loc == 0); + assert!(index <= 1); + assert!(storage == StorageClass::None); + storage = StorageClass::FragColor(index); + } + } + _ => {} + } + } + let decl = if state.in_function.is_some() { + let run_class = match storage { + StorageClass::Const => RunClass::Scalar, + StorageClass::None => default_run_class, + _ => panic!("bad local storage {:?}", storage), + }; + SymDecl::Local(storage, ty.clone(), run_class) + } else { + let run_class = match storage { + StorageClass::Const | StorageClass::Uniform | StorageClass::Sampler(..) => { + RunClass::Scalar + } + StorageClass::In | StorageClass::Out | StorageClass::FragColor(..) + if interpolation == Some(syntax::InterpolationQualifier::Flat) => + { + RunClass::Scalar + } + _ => RunClass::Vector, + }; + SymDecl::Global(storage, interpolation, ty.clone(), run_class) + }; + (state.declare(name.as_str(), decl.clone()), decl) + } + None => panic!(), + }; + + let head = SingleDeclaration { + qualifier: lift_type_qualifier_for_declaration(state, &d.head.ty.qualifier), + name: sym, + ty, + ty_def, + initializer: d + .head + .initializer + .as_ref() + .map(|x| translate_initializater(state, x)), + }; + + let tail = d + .tail + .iter() + .map(|d| { + if let Some(_array) = &d.ident.array_spec { + panic!("unhandled array") + } + state.declare(d.ident.ident.as_str(), decl.clone()); + SingleDeclarationNoType { + ident: d.ident.clone(), + initializer: d + .initializer + .as_ref() + .map(|x| translate_initializater(state, x)), + } + }) + .collect(); + Declaration::InitDeclaratorList(InitDeclaratorList { head, tail }) +} + +fn translate_init_declarator_list( + state: &mut State, + l: &syntax::InitDeclaratorList, + default_run_class: RunClass, +) -> Declaration { + match &l.head.name { + Some(_name) => translate_variable_declaration(state, l, default_run_class), + None => translate_struct_declaration(state, &l.head), + } +} + +fn translate_declaration( + state: &mut State, + d: &syntax::Declaration, + default_run_class: RunClass, +) -> Declaration { + match d { + syntax::Declaration::Block(_) => panic!(), //Declaration::Block(..), + syntax::Declaration::FunctionPrototype(p) => { + Declaration::FunctionPrototype(translate_function_prototype(state, p)) + } + syntax::Declaration::Global(ty, ids) => { + // glsl non-es supports requalifying variables, but we don't yet. + // However, we still want to allow global layout qualifiers for + // KHR_advanced_blend_equation. + if !ids.is_empty() { + panic!(); + } + let _ = for qual in &ty.qualifiers { + match qual { + syntax::TypeQualifierSpec::Layout(l) => { + for id in &l.ids { + match id { + syntax::LayoutQualifierSpec::Identifier(key, _) => { + match key.as_str() { + "blend_support_all_equations" => (), + _ => panic!(), + } + } + _ => panic!(), + } + } + } + syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Out) => (), + _ => panic!(), + } + }; + Declaration::Global(lift_type_qualifier_for_declaration(state, &Some(ty.clone())).unwrap(), ids.clone()) + } + syntax::Declaration::InitDeclaratorList(dl) => { + translate_init_declarator_list(state, dl, default_run_class) + } + syntax::Declaration::Precision(p, ts) => Declaration::Precision(p.clone(), ts.clone()), + } +} + +fn is_vector(ty: &Type) -> bool { + match ty.kind { + TypeKind::Vec2 + | TypeKind::Vec3 + | TypeKind::Vec4 + | TypeKind::BVec2 + | TypeKind::BVec3 + | TypeKind::BVec4 + | TypeKind::IVec2 + | TypeKind::IVec3 + | TypeKind::IVec4 => ty.array_sizes == None, + _ => false, + } +} + +fn index_vector(ty: &Type) -> Option<TypeKind> { + use TypeKind::*; + if ty.array_sizes != None { + return None; + } + Some(match ty.kind { + Vec2 => Float, + Vec3 => Float, + Vec4 => Float, + DVec2 => Double, + DVec3 => Double, + DVec4 => Double, + BVec2 => Bool, + BVec3 => Bool, + BVec4 => Bool, + IVec2 => Int, + IVec3 => Int, + IVec4 => Int, + UVec2 => UInt, + UVec3 => UInt, + UVec4 => UInt, + _ => return None, + }) + +} + +fn index_matrix(ty: &Type) -> Option<TypeKind> { + use TypeKind::*; + if ty.array_sizes != None { + return None; + } + Some(match ty.kind { + Mat2 => Vec2, + Mat3 => Vec3, + Mat4 => Vec4, + Mat23 => Vec3, + Mat24 => Vec4, + Mat32 => Vec2, + Mat34 => Vec4, + Mat42 => Vec2, + Mat43 => Vec3, + DMat2 => DVec2, + DMat3 => DVec3, + DMat4 => DVec4, + DMat23 => DVec3, + DMat24 => DVec4, + DMat32 => DVec2, + DMat34 => DVec4, + DMat42 => DVec2, + DMat43 => DVec3, + _ => return None, + }) +} + +fn is_ivec(ty: &Type) -> bool { + match ty.kind { + TypeKind::IVec2 | TypeKind::IVec3 | TypeKind::IVec4 => ty.array_sizes == None, + _ => false, + } +} + +fn can_implicitly_convert_to(src: &Type, dst: &Type) -> bool { + // XXX: use an underlying type helper + if src == &Type::new(TypeKind::Double) && dst == &Type::new(TypeKind::Float) { + // We're not supposed to implicitly convert from double to float but glsl 4 has a bug + // where it parses unannotated float constants as double. + true + } else if dst == &Type::new(TypeKind::Double) && src == &Type::new(TypeKind::Float) { + true + } else if (dst == &Type::new(TypeKind::Float) || dst == &Type::new(TypeKind::Double)) && + src == &Type::new(TypeKind::Int) + { + true + } else if (dst == &Type::new(TypeKind::Vec2) || dst == &Type::new(TypeKind::DVec2)) && + src == &Type::new(TypeKind::IVec2) + { + true + } else if dst == &Type::new(TypeKind::IVec2) && + (src == &Type::new(TypeKind::Vec2) || src == &Type::new(TypeKind::DVec2)) + { + true + } else { + src.kind == dst.kind && src.array_sizes == dst.array_sizes + } +} + +fn promoted_type(lhs: &Type, rhs: &Type) -> Type { + if lhs == &Type::new(TypeKind::Double) && rhs == &Type::new(TypeKind::Float) { + Type::new(TypeKind::Double) + } else if lhs == &Type::new(TypeKind::Float) && rhs == &Type::new(TypeKind::Double) { + Type::new(TypeKind::Double) + } else if lhs == &Type::new(TypeKind::Int) && rhs == &Type::new(TypeKind::Double) { + Type::new(TypeKind::Double) + } else if is_vector(&lhs) && + (rhs == &Type::new(TypeKind::Float) || + rhs == &Type::new(TypeKind::Double) || + rhs == &Type::new(TypeKind::Int)) + { + // scalars promote to vectors + lhs.clone() + } else if is_vector(&rhs) && + (lhs == &Type::new(TypeKind::Float) || + lhs == &Type::new(TypeKind::Double) || + lhs == &Type::new(TypeKind::Int)) + { + // scalars promote to vectors + rhs.clone() + } else if lhs == rhs { + lhs.clone() + } else if lhs.kind == rhs.kind { + if lhs.array_sizes == rhs.array_sizes { + // XXX: we need to be able to query the default precision here + match (&lhs.precision, &rhs.precision) { + (Some(PrecisionQualifier::High), _) => lhs.clone(), + (_, Some(PrecisionQualifier::High)) => rhs.clone(), + (None, _) => lhs.clone(), + (_, None) => rhs.clone(), + _ => panic!("precision mismatch {:?} {:?}", lhs.precision, rhs.precision), + } + } else { + panic!("array size mismatch") + } + } else { + assert_eq!(lhs, rhs); + lhs.clone() + } +} + +pub fn is_output(expr: &Expr, state: &State) -> Option<SymRef> { + match &expr.kind { + ExprKind::Variable(i) => match state.sym(*i).decl { + SymDecl::Global(storage, ..) => match storage { + StorageClass::In | StorageClass::Out => return Some(*i), + _ => {} + }, + SymDecl::Local(..) => {} + _ => panic!("should be variable"), + }, + ExprKind::SwizzleSelector(e, ..) => { + return is_output(e, state); + } + ExprKind::Bracket(e, ..) => { + return is_output(e, state); + } + ExprKind::Dot(e, ..) => { + return is_output(e, state); + } + _ => {} + }; + None +} + +pub fn get_texel_fetch_offset( + state: &State, + sampler_expr: &Expr, + uv_expr: &Expr, + offset_expr: &Expr, +) -> Option<(SymRef, SymRef, i32, i32)> { + if let ExprKind::Variable(ref sampler) = &sampler_expr.kind { + //if let ExprKind::Binary(BinaryOp::Add, ref lhs, ref rhs) = &uv_expr.kind { + if let ExprKind::Variable(ref base) = &uv_expr.kind { + if let ExprKind::FunCall(ref fun, ref args) = &offset_expr.kind { + if let FunIdentifier::Identifier(ref offset) = fun { + if state.sym(*offset).name == "ivec2" { + if let ExprKind::IntConst(ref x) = &args[0].kind { + if let ExprKind::IntConst(ref y) = &args[1].kind { + return Some((*sampler, *base, *x, *y)); + } + } + } + } + } + } + //} + } + None +} + +fn make_const(t: TypeKind, v: i32) -> Expr { + Expr { + kind: match t { + TypeKind::Int => ExprKind::IntConst(v as _), + TypeKind::UInt => ExprKind::UIntConst(v as _), + TypeKind::Bool => ExprKind::BoolConst(v != 0), + TypeKind::Float => ExprKind::FloatConst(v as _), + TypeKind::Double => ExprKind::DoubleConst(v as _), + _ => panic!("bad constant type"), + }, + ty: Type::new(t), + } +} + +// Any parameters needing to convert to bool should just compare via != 0. +// This ensures they get the proper all-1s pattern for C++ OpenCL vectors. +fn force_params_to_bool(_state: &mut State, params: &mut Vec<Expr>) { + for e in params { + if !e.ty.kind.is_bool() { + let k = e.ty.kind; + *e = Expr { + kind: ExprKind::Binary( + BinaryOp::NonEqual, + Box::new(e.clone()), + Box::new(make_const(k.to_scalar(), 0)), + ), + ty: Type::new(k.to_bool()), + }; + } + } +} + +// Transform bool params to int, then mask off the low bit so they become 0 or 1. +// C++ OpenCL vectors represent bool as all-1s patterns, which will erroneously +// convert to -1 otherwise. +fn force_params_from_bool(state: &mut State, params: &mut Vec<Expr>) { + for e in params { + if e.ty.kind.is_bool() { + let k = e.ty.kind.to_int(); + let sym = state.lookup(k.glsl_primitive_type_name().unwrap()).unwrap(); + *e = Expr { + kind: ExprKind::Binary( + BinaryOp::BitAnd, + Box::new(Expr { + kind: ExprKind::FunCall( + FunIdentifier::Identifier(sym), + vec![e.clone()], + ), + ty: Type::new(k), + }), + Box::new(make_const(TypeKind::Int, 1)), + ), + ty: Type::new(k), + }; + } + } +} + +fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { + match e { + syntax::Expr::Variable(i) => { + let sym = match state.lookup(i.as_str()) { + Some(sym) => sym, + None => panic!("missing declaration {}", i.as_str()), + }; + let ty = match &state.sym(sym).decl { + SymDecl::Global(_, _, ty, _) => { + let mut globals = state.used_globals.borrow_mut(); + if !globals.contains(&sym) { + globals.push(sym); + } + ty.clone() + } + SymDecl::Local(_, ty, _) => ty.clone(), + _ => panic!("bad variable type"), + }; + Expr { + kind: ExprKind::Variable(sym), + ty, + } + } + syntax::Expr::Assignment(lhs, op, rhs) => { + let lhs = Box::new(translate_expression(state, lhs)); + let rhs = Box::new(translate_expression(state, rhs)); + let ty = if op == &AssignmentOp::Mult { + if lhs.ty.kind == TypeKind::Vec4 && rhs.ty.kind == TypeKind::Float { + lhs.ty.clone() + } else { + promoted_type(&lhs.ty, &rhs.ty) + } + } else { + promoted_type(&lhs.ty, &rhs.ty) + }; + if let Some(global) = is_output(&lhs, state) { + let mut globals = state.modified_globals.borrow_mut(); + if !globals.contains(&global) { + globals.push(global); + } + if global == state.clip_dist_sym { + if let ExprKind::Bracket(_, idx) = &lhs.kind { + // Get the constant array index used for gl_ClipDistance and add it to the used mask. + for dimension in idx { + let idx = match dimension.kind { + ExprKind::IntConst(idx) => idx, + ExprKind::UIntConst(idx) => idx as i32, + _ => panic!("bad index for gl_ClipDistance"), + }; + assert!(idx >= 0 && idx < 4); + state.used_clip_dist |= 1 << idx; + } + } + } + } + Expr { + kind: ExprKind::Assignment(lhs, op.clone(), rhs), + ty, + } + } + syntax::Expr::Binary(op, lhs, rhs) => { + let lhs = Box::new(translate_expression(state, lhs)); + let rhs = Box::new(translate_expression(state, rhs)); + let ty = match op { + BinaryOp::Equal | BinaryOp::NonEqual | BinaryOp::GT | BinaryOp::GTE | BinaryOp::LT | BinaryOp::LTE => { + // comparison operators have a bool result + Type::new(TypeKind::Bool) + } + BinaryOp::Mult => { + match (lhs.ty.kind, rhs.ty.kind) { + (TypeKind::Mat2, TypeKind::Vec2) | + (TypeKind::Mat3, TypeKind::Vec3) | + (TypeKind::Mat3, TypeKind::Mat3) | + (TypeKind::Mat3, TypeKind::Mat43) | + (TypeKind::Mat4, TypeKind::Vec4) => rhs.ty.clone(), + (TypeKind::Mat43, TypeKind::Vec4) => Type::new(TypeKind::Vec3), + (TypeKind::Mat2, TypeKind::Float) | + (TypeKind::Mat3, TypeKind::Float) | + (TypeKind::Mat4, TypeKind::Float) => lhs.ty.clone(), + _ => promoted_type(&lhs.ty, &rhs.ty), + } + } + _ => promoted_type(&lhs.ty, &rhs.ty), + }; + + Expr { + kind: ExprKind::Binary(op.clone(), lhs, rhs), + ty, + } + } + syntax::Expr::Unary(op, e) => { + let e = Box::new(translate_expression(state, e)); + let ty = e.ty.clone(); + Expr { + kind: ExprKind::Unary(op.clone(), e), + ty, + } + } + syntax::Expr::BoolConst(b) => Expr { + kind: ExprKind::BoolConst(*b), + ty: Type::new(TypeKind::Bool), + }, + syntax::Expr::Comma(lhs, rhs) => { + let lhs = Box::new(translate_expression(state, lhs)); + let rhs = Box::new(translate_expression(state, rhs)); + assert_eq!(lhs.ty, rhs.ty); + let ty = lhs.ty.clone(); + Expr { + kind: ExprKind::Comma(lhs, rhs), + ty, + } + } + syntax::Expr::DoubleConst(d) => Expr { + kind: ExprKind::DoubleConst(*d), + ty: Type::new(TypeKind::Double), + }, + syntax::Expr::FloatConst(f) => Expr { + kind: ExprKind::FloatConst(*f), + ty: Type::new(TypeKind::Float), + }, + syntax::Expr::FunCall(fun, params) => { + let ret_ty: Type; + let mut params: Vec<Expr> = params + .iter() + .map(|x| translate_expression(state, x)) + .collect(); + Expr { + kind: ExprKind::FunCall( + match fun { + syntax::FunIdentifier::Identifier(i) => { + let name = i.as_str(); + if name == "texelFetchOffset" && params.len() >= 4 { + if let Some((sampler, base, x, y)) = get_texel_fetch_offset( + state, ¶ms[0], ¶ms[1], ¶ms[3], + ) { + if let Some(offsets) = + state.texel_fetches.get_mut(&(sampler, base)) + { + offsets.add_offset(x, y); + } else { + state + .texel_fetches + .insert((sampler, base), TexelFetchOffsets::new(x, y)); + } + } + } else if name == "swgl_stepInterp" { + let mut globals = state.modified_globals.borrow_mut(); + for (i, sym) in state.syms.iter().enumerate() { + match &sym.borrow().decl { + SymDecl::Global(StorageClass::In, _, _, RunClass::Vector) => { + let symref = SymRef(i as u32); + if !globals.contains(&symref) { + globals.push(symref); + } + } + _ => {} + } + } + } + let sym = match state.lookup(name) { + Some(s) => s, + None => panic!("missing symbol {}", name), + }; + // Force any boolean basic type constructors to generate correct + // bit patterns. + if let Some(t) = TypeKind::from_glsl_primitive_type_name(name) { + if t.is_bool() { + force_params_to_bool(state, &mut params); + } else { + force_params_from_bool(state, &mut params); + } + } + match &state.sym(sym).decl { + SymDecl::NativeFunction(fn_ty, _, _) => { + // Search for a signature where all parameter types are + // compatible. If there are many compatible signatures, + // then choose the one with the most exact matches. + // This is an approximation of the algorith described in + // the "Function Definitions" section of the spec. + let mut ret = None; + let mut best_score = 0; + 'next_sig: for sig in &fn_ty.signatures { + let mut score = 0; + for (e, p) in params.iter().zip(sig.params.iter()) { + if e.ty == *p { + score += 1; + } else if !can_implicitly_convert_to(&e.ty, p) { + continue 'next_sig; + } + } + if score >= best_score { + ret = Some(sig.ret.clone()); + best_score = score; + // If all parameters match exactly, then there + // is no need to search for other matches. + if best_score >= params.len() { + break; + } + } + } + ret_ty = match ret { + Some(t) => t, + None => { + dbg!(&fn_ty.signatures); + dbg!(params.iter().map(|p| p).collect::<Vec<_>>()); + panic!("no matching func {}", i.as_str()) + } + }; + } + SymDecl::UserFunction(fd, _) => { + let mut globals = state.modified_globals.borrow_mut(); + for global in &fd.globals { + if !globals.contains(global) { + globals.push(*global); + } + } + let mut matching = true; + for (e, p) in params.iter().zip(fd.prototype.parameters.iter()) + { + matching &= match p { + FunctionParameterDeclaration::Named(q, d) => { + match q { + Some(ParameterQualifier::InOut) + | Some(ParameterQualifier::Out) => { + if let Some(global) = is_output(e, state) { + if !globals.contains(&global) { + globals.push(global); + } + } + } + _ => {} + } + can_implicitly_convert_to(&e.ty, &d.ty) + } + FunctionParameterDeclaration::Unnamed(..) => panic!(), + }; + } + assert!(matching); + ret_ty = fd.prototype.ty.clone(); + } + SymDecl::Struct(_) => ret_ty = Type::new(TypeKind::Struct(sym)), + _ => panic!("can only call functions"), + }; + FunIdentifier::Identifier(sym) + } + // array constructor + syntax::FunIdentifier::Expr(e) => { + let ty = match &**e { + syntax::Expr::Bracket(i, array) => { + let kind = match &**i { + syntax::Expr::Variable(i) => match i.as_str() { + "vec4" => TypeKind::Vec4, + "vec2" => TypeKind::Vec2, + "int" => TypeKind::Int, + _ => panic!("unexpected type constructor {:?}", i), + }, + _ => panic!(), + }; + + Type { + kind, + precision: None, + array_sizes: Some(Box::new(lift(state, array))), + } + } + _ => panic!(), + }; + ret_ty = ty.clone(); + + FunIdentifier::Constructor(ty) + } + }, + params, + ), + ty: ret_ty, + } + } + syntax::Expr::IntConst(i) => Expr { + kind: ExprKind::IntConst(*i), + ty: Type::new(TypeKind::Int), + }, + syntax::Expr::UIntConst(u) => Expr { + kind: ExprKind::UIntConst(*u), + ty: Type::new(TypeKind::UInt), + }, + syntax::Expr::PostDec(e) => { + let e = Box::new(translate_expression(state, e)); + let ty = e.ty.clone(); + Expr { + kind: ExprKind::PostDec(e), + ty, + } + } + syntax::Expr::PostInc(e) => { + let e = Box::new(translate_expression(state, e)); + let ty = e.ty.clone(); + Expr { + kind: ExprKind::PostInc(e), + ty, + } + } + syntax::Expr::Ternary(cond, lhs, rhs) => { + let cond = Box::new(translate_expression(state, cond)); + let lhs = Box::new(translate_expression(state, lhs)); + let rhs = Box::new(translate_expression(state, rhs)); + let ty = promoted_type(&lhs.ty, &rhs.ty); + Expr { + kind: ExprKind::Ternary(cond, lhs, rhs), + ty, + } + } + syntax::Expr::Dot(e, i) => { + let e = Box::new(translate_expression(state, e)); + let ty = e.ty.clone(); + let ivec = is_ivec(&ty); + if is_vector(&ty) { + let ty = Type::new(match i.as_str().len() { + 1 => { + if ivec { + TypeKind::Int + } else { + TypeKind::Float + } + } + 2 => { + if ivec { + TypeKind::IVec2 + } else { + TypeKind::Vec2 + } + } + 3 => { + if ivec { + TypeKind::IVec3 + } else { + TypeKind::Vec3 + } + } + 4 => { + if ivec { + TypeKind::IVec4 + } else { + TypeKind::Vec4 + } + } + _ => panic!(), + }); + + let sel = SwizzleSelector::parse(i.as_str()); + + Expr { + kind: ExprKind::SwizzleSelector(e, sel), + ty, + } + } else { + match ty.kind { + TypeKind::Struct(s) => { + let sym = state.sym(s); + let fields = match &sym.decl { + SymDecl::Struct(fields) => fields, + _ => panic!("expected struct"), + }; + let field = fields + .fields + .iter() + .find(|x| &x.name == i) + .expect(&format!("missing field `{}` in `{}`", i, sym.name)); + Expr { + kind: ExprKind::Dot(e, i.clone()), + ty: field.ty.clone(), + } + } + _ => panic!("expected struct found {:#?} {:#?}", e, ty), + } + } + } + syntax::Expr::Bracket(e, specifier) => { + let e = Box::new(translate_expression(state, e)); + let ty = if let Some(ty) = index_vector(&e.ty) { + Type::new(ty) + } else if let Some(ty) = index_matrix(&e.ty) { + Type::new(ty) + } else { + let a = match &e.ty.array_sizes { + Some(a) => { + let mut a = *a.clone(); + a.sizes.pop(); + if a.sizes.len() == 0 { + None + } else { + Some(Box::new(a)) + } + } + _ => panic!("{:#?}", e), + }; + Type { + kind: e.ty.kind.clone(), + precision: e.ty.precision.clone(), + array_sizes: a, + } + }; + let indx = specifier.dimensions.0.iter().map(|a| match a { + ArraySpecifierDimension::Unsized => panic!("need expression"), + ArraySpecifierDimension::ExplicitlySized(e) => translate_expression(state, e), + }).collect(); + Expr { + kind: ExprKind::Bracket(e, indx), + ty, + } + } + } +} + +fn translate_switch(state: &mut State, s: &syntax::SwitchStatement) -> SwitchStatement { + let mut cases = Vec::new(); + + let mut case = None; + for stmt in &s.body { + match stmt { + syntax::Statement::Simple(s) => match &**s { + syntax::SimpleStatement::CaseLabel(label) => { + match case.take() { + Some(case) => cases.push(case), + _ => {} + } + case = Some(Case { + label: translate_case(state, &label), + stmts: Vec::new(), + }) + } + _ => match case { + Some(ref mut case) => case.stmts.push(translate_statement(state, stmt)), + _ => panic!("switch must start with case"), + }, + }, + _ => match case { + Some(ref mut case) => case.stmts.push(translate_statement(state, stmt)), + _ => panic!("switch must start with case"), + }, + } + } + match case.take() { + Some(case) => cases.push(case), + _ => {} + } + SwitchStatement { + head: Box::new(translate_expression(state, &s.head)), + cases, + } +} + +fn translate_jump(state: &mut State, s: &syntax::JumpStatement) -> JumpStatement { + match s { + syntax::JumpStatement::Break => JumpStatement::Break, + syntax::JumpStatement::Continue => JumpStatement::Continue, + syntax::JumpStatement::Discard => JumpStatement::Discard, + syntax::JumpStatement::Return(e) => { + JumpStatement::Return(e.as_ref().map(|e| Box::new(translate_expression(state, e)))) + } + } +} + +fn translate_condition(state: &mut State, c: &syntax::Condition) -> Condition { + match c { + syntax::Condition::Expr(e) => Condition::Expr(Box::new(translate_expression(state, e))), + _ => panic!(), + } +} + +fn translate_for_init(state: &mut State, s: &syntax::ForInitStatement) -> ForInitStatement { + match s { + syntax::ForInitStatement::Expression(e) => { + ForInitStatement::Expression(e.as_ref().map(|e| translate_expression(state, e))) + } + syntax::ForInitStatement::Declaration(d) => ForInitStatement::Declaration(Box::new( + translate_declaration(state, d, RunClass::Scalar), + )), + } +} + +fn translate_for_rest(state: &mut State, s: &syntax::ForRestStatement) -> ForRestStatement { + ForRestStatement { + condition: s.condition.as_ref().map(|c| translate_condition(state, c)), + post_expr: s + .post_expr + .as_ref() + .map(|e| Box::new(translate_expression(state, e))), + } +} + +fn translate_iteration(state: &mut State, s: &syntax::IterationStatement) -> IterationStatement { + match s { + syntax::IterationStatement::While(cond, s) => IterationStatement::While( + translate_condition(state, cond), + Box::new(translate_statement(state, s)), + ), + syntax::IterationStatement::For(init, rest, s) => IterationStatement::For( + translate_for_init(state, init), + translate_for_rest(state, rest), + Box::new(translate_statement(state, s)), + ), + syntax::IterationStatement::DoWhile(s, e) => IterationStatement::DoWhile( + Box::new(translate_statement(state, s)), + Box::new(translate_expression(state, e)), + ), + } +} + +fn translate_case(state: &mut State, c: &syntax::CaseLabel) -> CaseLabel { + match c { + syntax::CaseLabel::Def => CaseLabel::Def, + syntax::CaseLabel::Case(e) => CaseLabel::Case(Box::new(translate_expression(state, e))), + } +} + +fn translate_selection_rest( + state: &mut State, + s: &syntax::SelectionRestStatement, +) -> (Box<Statement>, Option<Box<Statement>>) { + match s { + syntax::SelectionRestStatement::Statement(s) => { + (Box::new(translate_statement(state, s)), None) + } + syntax::SelectionRestStatement::Else(if_body, rest) => ( + Box::new(translate_statement(state, if_body)), + Some(Box::new(translate_statement(state, rest))), + ), + } +} + +fn translate_selection(state: &mut State, s: &syntax::SelectionStatement) -> SelectionStatement { + let cond = Box::new(translate_expression(state, &s.cond)); + let (body, else_stmt) = translate_selection_rest(state, &s.rest); + SelectionStatement { + cond, + body, + else_stmt, + } +} + +fn translate_simple_statement(state: &mut State, s: &syntax::SimpleStatement) -> SimpleStatement { + match s { + syntax::SimpleStatement::Declaration(d) => { + SimpleStatement::Declaration(translate_declaration(state, d, RunClass::Unknown)) + } + syntax::SimpleStatement::Expression(e) => { + SimpleStatement::Expression(e.as_ref().map(|e| translate_expression(state, e))) + } + syntax::SimpleStatement::Iteration(i) => { + SimpleStatement::Iteration(translate_iteration(state, i)) + } + syntax::SimpleStatement::Selection(s) => { + SimpleStatement::Selection(translate_selection(state, s)) + } + syntax::SimpleStatement::Jump(j) => SimpleStatement::Jump(translate_jump(state, j)), + syntax::SimpleStatement::Switch(s) => SimpleStatement::Switch(translate_switch(state, s)), + syntax::SimpleStatement::CaseLabel(_) => panic!("should be handled by translate_switch"), + } +} + +fn translate_statement(state: &mut State, s: &syntax::Statement) -> Statement { + match s { + syntax::Statement::Compound(s) => { + Statement::Compound(Box::new(translate_compound_statement(state, s))) + } + syntax::Statement::Simple(s) => { + Statement::Simple(Box::new(translate_simple_statement(state, s))) + } + } +} + +fn translate_compound_statement( + state: &mut State, + cs: &syntax::CompoundStatement, +) -> CompoundStatement { + CompoundStatement { + statement_list: cs + .statement_list + .iter() + .map(|x| translate_statement(state, x)) + .collect(), + } +} + +fn translate_function_parameter_declaration( + state: &mut State, + p: &syntax::FunctionParameterDeclaration, + index: usize, +) -> FunctionParameterDeclaration { + match p { + syntax::FunctionParameterDeclaration::Named(qual, p) => { + let mut ty: Type = lift(state, &p.ty); + if let Some(a) = &p.ident.array_spec { + ty.array_sizes = Some(Box::new(lift(state, a))); + } + + ty.precision = get_precision(qual); + + let decl = SymDecl::Local( + StorageClass::None, + ty.clone(), + RunClass::Dependent(1 << index), + ); + let d = FunctionParameterDeclarator { + ty, + name: p.ident.ident.clone(), + sym: state.declare(p.ident.ident.as_str(), decl), + }; + FunctionParameterDeclaration::Named(lift_type_qualifier_for_parameter(state, qual), d) + } + syntax::FunctionParameterDeclaration::Unnamed(qual, p) => { + FunctionParameterDeclaration::Unnamed( + lift_type_qualifier_for_parameter(state, qual), + p.clone(), + ) + } + } +} + +fn translate_prototype( + state: &mut State, + cs: &syntax::FunctionPrototype, +) -> (FunctionPrototype, SymRef) { + let prototype = FunctionPrototype { + ty: lift(state, &cs.ty), + name: cs.name.clone(), + parameters: cs + .parameters + .iter() + .enumerate() + .map(|(i, x)| translate_function_parameter_declaration(state, x, i)) + .collect(), + }; + let sym = if let Some(sym) = state.lookup(prototype.name.as_str()) { + match &state.sym(sym).decl { + SymDecl::UserFunction(..) => {} + _ => panic!( + "prototype conflicts with existing symbol: {}", + prototype.name.as_str() + ), + } + sym + } else { + let pfd = Rc::new(FunctionDefinition { + prototype: prototype.clone(), + body: CompoundStatement::new(), + globals: Vec::new(), + texel_fetches: HashMap::new(), + }); + state.declare( + prototype.name.as_str(), + SymDecl::UserFunction(pfd, RunClass::Unknown), + ) + }; + (prototype, sym) +} + +fn translate_function_prototype( + state: &mut State, + prototype: &syntax::FunctionPrototype, +) -> FunctionPrototype { + let (prototype, _) = translate_prototype(state, prototype); + prototype +} + +fn translate_function_definition( + state: &mut State, + sfd: &syntax::FunctionDefinition, +) -> Rc<FunctionDefinition> { + let (prototype, sym) = translate_prototype(state, &sfd.prototype); + + state.push_scope(prototype.name.as_str().into()); + state.in_function = Some(sym); + state.modified_globals.get_mut().clear(); + state.texel_fetches.clear(); + let body = translate_compound_statement(state, &sfd.statement); + let mut globals = Vec::new(); + mem::swap(&mut globals, state.modified_globals.get_mut()); + let mut texel_fetches = HashMap::new(); + mem::swap(&mut texel_fetches, &mut state.texel_fetches); + state.in_function = None; + state.pop_scope(); + + let fd = Rc::new(FunctionDefinition { + prototype, + body, + globals, + texel_fetches, + }); + state.sym_mut(sym).decl = SymDecl::UserFunction(fd.clone(), RunClass::Unknown); + fd +} + +fn translate_external_declaration( + state: &mut State, + ed: &syntax::ExternalDeclaration, +) -> ExternalDeclaration { + match ed { + syntax::ExternalDeclaration::Declaration(d) => { + ExternalDeclaration::Declaration(translate_declaration(state, d, RunClass::Unknown)) + } + syntax::ExternalDeclaration::FunctionDefinition(fd) => { + ExternalDeclaration::FunctionDefinition(translate_function_definition(state, fd)) + } + syntax::ExternalDeclaration::Preprocessor(p) => { + ExternalDeclaration::Preprocessor(p.clone()) + } + } +} + +fn declare_function_ext( + state: &mut State, + name: &str, + cxx_name: Option<&'static str>, + ret: Type, + params: Vec<Type>, + run_class: RunClass, +) { + let sig = FunctionSignature { ret, params }; + match state.lookup_sym_mut(name) { + Some(Symbol { + decl: SymDecl::NativeFunction(f, ..), + .. + }) => f.signatures.push(sig), + None => { + state.declare( + name, + SymDecl::NativeFunction( + FunctionType { + signatures: NonEmpty::new(sig), + }, + cxx_name, + run_class, + ), + ); + } + _ => panic!("overloaded function name {}", name), + } + //state.declare(name, Type::Function(FunctionType{ v})) +} + +fn declare_function( + state: &mut State, + name: &str, + cxx_name: Option<&'static str>, + ret: Type, + params: Vec<Type>, +) { + declare_function_ext(state, name, cxx_name, ret, params, RunClass::Unknown) +} + +pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> TranslationUnit { + // global scope + state.push_scope("global".into()); + use TypeKind::*; + declare_function( + state, + "vec2", + Some("make_vec2"), + Type::new(Vec2), + vec![Type::new(Float)], + ); + declare_function( + state, + "vec2", + Some("make_vec2"), + Type::new(Vec2), + vec![Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "vec2", + Some("make_vec2"), + Type::new(Vec2), + vec![Type::new(IVec2)], + ); + declare_function( + state, + "vec2", + Some("make_vec2"), + Type::new(Vec2), + vec![Type::new(IVec3)], + ); + declare_function( + state, + "vec3", + Some("make_vec3"), + Type::new(Vec3), + vec![Type::new(Float), Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "vec3", + Some("make_vec3"), + Type::new(Vec3), + vec![Type::new(Float)], + ); + declare_function( + state, + "vec3", + Some("make_vec3"), + Type::new(Vec3), + vec![Type::new(Vec2), Type::new(Float)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Float)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Vec3), Type::new(Float)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![ + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + ], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Vec2), Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Float), Type::new(Float), Type::new(Vec2)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(Vec4)], + ); + declare_function( + state, + "vec4", + Some("make_vec4"), + Type::new(Vec4), + vec![Type::new(IVec4)], + ); + + declare_function( + state, + "bvec2", + Some("make_bvec2"), + Type::new(BVec2), + vec![Type::new(Bool)], + ); + declare_function( + state, + "bvec3", + Some("make_bvec3"), + Type::new(BVec3), + vec![Type::new(Bool)], + ); + declare_function( + state, + "bvec4", + Some("make_bvec4"), + Type::new(BVec4), + vec![Type::new(Bool)], + ); + declare_function( + state, + "bvec4", + Some("make_bvec4"), + Type::new(BVec4), + vec![Type::new(BVec2), Type::new(BVec2)], + ); + declare_function( + state, + "bvec4", + Some("make_bvec4"), + Type::new(BVec4), + vec![Type::new(Bool), Type::new(Bool), Type::new(Bool), Type::new(Bool)], + ); + declare_function( + state, + "int", + Some("make_int"), + Type::new(Int), + vec![Type::new(Float)], + ); + declare_function( + state, + "float", + Some("make_float"), + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function( + state, + "float", + Some("make_float"), + Type::new(Float), + vec![Type::new(Int)], + ); + declare_function( + state, + "int", + Some("make_int"), + Type::new(Int), + vec![Type::new(UInt)], + ); + declare_function( + state, + "uint", + Some("make_uint"), + Type::new(UInt), + vec![Type::new(Float)], + ); + declare_function( + state, + "uint", + Some("make_uint"), + Type::new(UInt), + vec![Type::new(Int)], + ); + declare_function( + state, + "ivec2", + Some("make_ivec2"), + Type::new(IVec2), + vec![Type::new(UInt), Type::new(UInt)], + ); + declare_function( + state, + "ivec2", + Some("make_ivec2"), + Type::new(IVec2), + vec![Type::new(Int), Type::new(Int)], + ); + declare_function( + state, + "ivec2", + Some("make_ivec2"), + Type::new(IVec2), + vec![Type::new(Vec2)], + ); + declare_function( + state, + "ivec3", + Some("make_ivec3"), + Type::new(IVec3), + vec![Type::new(IVec2), Type::new(Int)], + ); + declare_function( + state, + "ivec4", + Some("make_ivec4"), + Type::new(IVec4), + vec![ + Type::new(Int), + Type::new(Int), + Type::new(Int), + Type::new(Int), + ], + ); + declare_function( + state, + "ivec4", + Some("make_ivec4"), + Type::new(IVec4), + vec![Type::new(Vec4)], + ); + declare_function( + state, + "ivec4", + Some("make_ivec4"), + Type::new(IVec4), + vec![Type::new(IVec2), Type::new(Int), Type::new(Int)], + ); + + declare_function( + state, + "mat2", + Some("make_mat2"), + Type::new(Mat2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "mat2", + Some("make_mat2"), + Type::new(Mat2), + vec![Type::new(Float)], + ); + declare_function( + state, + "mat2", + Some("make_mat2"), + Type::new(Mat2), + vec![Type::new(Mat4)], + ); + declare_function( + state, + "mat3", + Some("make_mat3"), + Type::new(Mat3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "mat3", + Some("make_mat3"), + Type::new(Mat3), + vec![Type::new(Mat4)], + ); + declare_function( + state, + "mat3", + Some("make_mat3"), + Type::new(Mat3), + vec![ + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + ], + ); + declare_function( + state, + "mat3x4", + Some("make_mat3x4"), + Type::new(Mat34), + vec![ + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + ], + ); + declare_function( + state, + "transpose", + None, + Type::new(Mat43), + vec![Type::new(Mat34)], + ); + declare_function( + state, + "mat4", + Some("make_mat4"), + Type::new(Mat4), + vec![ + Type::new(Vec4), + Type::new(Vec4), + Type::new(Vec4), + Type::new(Vec4), + ], + ); + declare_function( + state, + "mat4", + Some("make_mat4"), + Type::new(Mat4), + vec![ + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + Type::new(Float), + ], + ); + declare_function(state, "abs", None, Type::new(Vec2), vec![Type::new(Vec2)]); + declare_function(state, "abs", None, Type::new(Vec3), vec![Type::new(Vec3)]); + declare_function(state, "abs", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "sign", None, Type::new(Vec2), vec![Type::new(Vec2)]); + declare_function(state, "sign", None, Type::new(Vec3), vec![Type::new(Vec3)]); + declare_function(state, "sign", None, Type::new(Float), vec![Type::new(Float)]); + declare_function( + state, + "dot", + None, + Type::new(Float), + vec![Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "dot", + None, + Type::new(Float), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + for t in &[Vec2, Vec3, Vec4] { + declare_function( + state, + "min", + None, + Type::new(*t), + vec![Type::new(*t), Type::new(Float)], + ); + declare_function( + state, + "max", + None, + Type::new(*t), + vec![Type::new(*t), Type::new(Float)], + ); + } + for t in &[Int, Float, Vec2, Vec3, Vec4] { + declare_function( + state, + "min", + None, + Type::new(*t), + vec![Type::new(*t), Type::new(*t)], + ); + declare_function( + state, + "max", + None, + Type::new(*t), + vec![Type::new(*t), Type::new(*t)], + ); + } + + declare_function( + state, + "mix", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2), Type::new(BVec2)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2), Type::new(Float)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec4), + vec![Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec4), + vec![Type::new(Vec4), Type::new(Vec4), Type::new(Float)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(Float)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(BVec3)], + ); + declare_function( + state, + "mix", + None, + Type::new(Float), + vec![Type::new(Float), Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "mix", + None, + Type::new(Vec4), + vec![Type::new(Vec4), Type::new(Vec4), Type::new(BVec4)], + ); + declare_function( + state, + "step", + None, + Type::new(Float), + vec![Type::new(Float), Type::new(Float)], + ); + declare_function( + state, + "step", + None, + Type::new(Vec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "step", + None, + Type::new(Vec2), + vec![Type::new(Float), Type::new(Vec2)], + ); + declare_function( + state, + "step", + None, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "step", + None, + Type::new(Vec4), + vec![Type::new(Float), Type::new(Vec4)], + ); + declare_function( + state, + "notEqual", + None, + Type::new(BVec4), + vec![Type::new(IVec4), Type::new(IVec4)], + ); + + declare_function_ext( + state, + "fwidth", + None, + Type::new(Vec2), + vec![Type::new(Vec2)], + RunClass::Scalar, + ); + declare_function_ext( + state, + "dFdx", + None, + Type::new(Float), + vec![Type::new(Float)], + RunClass::Scalar, + ); + declare_function_ext( + state, + "dFdx", + None, + Type::new(Vec2), + vec![Type::new(Vec2)], + RunClass::Scalar, + ); + + declare_function(state, "cos", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "sin", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "tan", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "atan", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "atan", None, Type::new(Float), vec![Type::new(Float), Type::new(Float)]); + for t in &[Vec2, Vec3, Vec4] { + declare_function( + state, + "clamp", + None, + Type::new(*t), + vec![Type::new(*t), Type::new(Float), Type::new(Float)], + ); + } + for t in &[Float, Vec2, Vec3, Vec4] { + declare_function( + state, + "clamp", + None, + Type::new(*t), + vec![Type::new(*t), Type::new(*t), Type::new(*t)], + ); + } + declare_function( + state, + "length", + None, + Type::new(Float), + vec![Type::new(Vec2)], + ); + declare_function(state, "pow", None, Type::new(Vec3), vec![Type::new(Vec3)]); + declare_function(state, "pow", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "exp", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "exp2", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "log", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "log2", None, Type::new(Float), vec![Type::new(Float)]); + for t in &[Float, Vec2] { + // recip is non-standard + declare_function( + state, + "recip", + None, + Type::new(*t), + vec![Type::new(*t)], + ); + declare_function( + state, + "inversesqrt", + None, + Type::new(*t), + vec![Type::new(*t)], + ); + declare_function( + state, + "sqrt", + None, + Type::new(*t), + vec![Type::new(*t)], + ); + } + declare_function( + state, + "distance", + None, + Type::new(Float), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + + declare_function( + state, + "equal", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "equal", + None, + Type::new(BVec4), + vec![Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "notEqual", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "notEqual", + None, + Type::new(BVec4), + vec![Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "lessThanEqual", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "lessThanEqual", + None, + Type::new(BVec3), + vec![Type::new(Vec3), Type::new(Vec3)], + ); + declare_function( + state, + "lessThanEqual", + None, + Type::new(BVec4), + vec![Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "lessThan", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "lessThan", + None, + Type::new(BVec4), + vec![Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "greaterThan", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "greaterThan", + None, + Type::new(BVec4), + vec![Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "greaterThanEqual", + None, + Type::new(BVec2), + vec![Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "greaterThanEqual", + None, + Type::new(BVec4), + vec![Type::new(Vec4), Type::new(Vec4)], + ); + declare_function(state, "any", None, Type::new(Bool), vec![Type::new(BVec2)]); + declare_function(state, "all", None, Type::new(Bool), vec![Type::new(BVec2)]); + declare_function(state, "all", None, Type::new(Bool), vec![Type::new(BVec4)]); + + declare_function( + state, + "if_then_else", + None, + Type::new(Vec3), + vec![Type::new(BVec3), Type::new(Vec3), Type::new(Vec3)], + ); + declare_function(state, "floor", None, Type::new(Vec4), vec![Type::new(Vec4)]); + declare_function(state, "floor", None, Type::new(Vec2), vec![Type::new(Vec2)]); + declare_function( + state, + "floor", + None, + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function( + state, + "ceil", + None, + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function( + state, + "round", + None, + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function( + state, + "fract", + None, + Type::new(Float), + vec![Type::new(Float)], + ); + declare_function( + state, + "fract", + None, + Type::new(Vec2), + vec![Type::new(Vec2)], + ); + declare_function(state, "mod", None, Type::new(Vec2), vec![Type::new(Vec2)]); + declare_function(state, "mod", None, Type::new(Float), vec![Type::new(Float)]); + + declare_function( + state, + "texelFetch", + None, + Type::new(Vec4), + vec![Type::new(Sampler2D), Type::new(IVec2), Type::new(Int)], + ); + declare_function( + state, + "texelFetch", + None, + Type::new(IVec4), + vec![Type::new(ISampler2D), Type::new(IVec2), Type::new(Int)], + ); + declare_function( + state, + "texelFetchOffset", + None, + Type::new(Vec4), + vec![ + Type::new(Sampler2D), + Type::new(IVec2), + Type::new(Int), + Type::new(IVec2), + ], + ); + declare_function( + state, + "texelFetchOffset", + None, + Type::new(IVec4), + vec![ + Type::new(ISampler2D), + Type::new(IVec2), + Type::new(Int), + Type::new(IVec2), + ], + ); + declare_function( + state, + "texture", + None, + Type::new(Vec4), + vec![Type::new(Sampler2D), Type::new(Vec2)], + ); + declare_function( + state, + "texture", + None, + Type::new(Vec4), + vec![Type::new(Sampler2DRect), Type::new(Vec2)], + ); + declare_function( + state, + "textureSize", + None, + Type::new(IVec2), + vec![Type::new(Sampler2D), Type::new(Int)], + ); + declare_function( + state, + "textureSize", + None, + Type::new(IVec2), + vec![Type::new(Sampler2DRect), Type::new(Int)], + ); + + declare_function( + state, + "inverse", + None, + Type::new(Mat2), + vec![Type::new(Mat2)], + ); + declare_function( + state, + "transpose", + None, + Type::new(Mat3), + vec![Type::new(Mat3)], + ); + declare_function( + state, + "normalize", + None, + Type::new(Vec2), + vec![Type::new(Vec2)], + ); + state.declare( + "gl_FragCoord", + SymDecl::Global(StorageClass::In, None, Type::new(Vec4), RunClass::Vector), + ); + state.declare( + "gl_FragColor", + SymDecl::Global(StorageClass::Out, None, Type::new(Vec4), RunClass::Vector), + ); + state.declare( + "gl_Position", + SymDecl::Global(StorageClass::Out, None, Type::new(Vec4), RunClass::Vector), + ); + state.clip_dist_sym = state.declare( + "gl_ClipDistance", + SymDecl::Global(StorageClass::Out, None, Type::new_array(Float, 4), RunClass::Vector), + ); + + state.declare( + "swgl_SpanLength", + SymDecl::Global(StorageClass::In, None, Type::new(Int), RunClass::Scalar), + ); + state.declare( + "swgl_StepSize", + SymDecl::Global(StorageClass::Const, None, Type::new(Int), RunClass::Scalar), + ); + + for t in &[Float, Vec2, Vec3, Vec4, Int, IVec2, IVec3, IVec4, Mat3, Mat4] { + declare_function_ext( + state, + "swgl_forceScalar", + None, + Type::new(*t), + vec![Type::new(*t)], + RunClass::Scalar, + ); + } + + // GL_ARB_shader_group_vote + for (name, cxx_name) in &[("anyInvocations", "test_any"), + ("allInvocations", "test_all"), + ("allInvocationsEqual", "test_equal")] { + declare_function_ext( + state, + name, + Some(cxx_name), + Type::new(Bool), + vec![Type::new(Bool)], + RunClass::Scalar, + ); + } + + declare_function( + state, + "swgl_stepInterp", + None, + Type::new(Void), + vec![], + ); + + for t in &[Float, Vec2, Vec3, Vec4] { + declare_function_ext( + state, + "swgl_interpStep", + None, + Type::new(*t), + vec![Type::new(*t)], + RunClass::Scalar, + ); + } + + declare_function( + state, + "swgl_commitPartialSolidRGBA8", + None, + Type::new(Void), + vec![Type::new(Int), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitPartialSolidR8", + None, + Type::new(Void), + vec![Type::new(Int), Type::new(Float)], + ); + declare_function( + state, + "swgl_commitSolidRGBA8", + None, + Type::new(Void), + vec![Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitSolidR8", + None, + Type::new(Void), + vec![Type::new(Float)], + ); + declare_function( + state, + "swgl_commitColorRGBA8", + None, + Type::new(Void), + vec![Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitColorR8", + None, + Type::new(Void), + vec![Type::new(Float)], + ); + declare_function( + state, + "swgl_blendDropShadow", + None, + Type::new(Void), + vec![Type::new(Vec4)], + ); + declare_function( + state, + "swgl_blendSubpixelText", + None, + Type::new(Void), + vec![Type::new(Vec4)], + ); + declare_function( + state, + "swgl_clipMask", + None, + Type::new(Void), + vec![Type::new(Sampler2D), Type::new(Vec2), Type::new(Vec2), Type::new(Vec2)], + ); + declare_function( + state, + "swgl_antiAlias", + None, + Type::new(Void), + vec![Type::new(Int)], + ); + declare_function( + state, + "swgl_antiAlias", + None, + Type::new(Void), + vec![Type::new(BVec4)], + ); + declare_function_ext( + state, + "swgl_validateGradient", + None, + Type::new(Int), + vec![Type::new(Sampler2D), Type::new(IVec2), Type::new(Int)], + RunClass::Scalar, + ); + declare_function( + state, + "swgl_commitLinearGradientRGBA8", + None, + Type::new(Void), + vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float), Type::new(Bool), Type::new(Bool), + Type::new(Vec2), Type::new(Vec2), Type::new(Float)], + ); + declare_function( + state, + "swgl_commitRadialGradientRGBA8", + None, + Type::new(Void), + vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float), Type::new(Bool), Type::new(Vec2), + Type::new(Float)], + ); + declare_function( + state, + "swgl_commitGradientRGBA8", + None, + Type::new(Void), + vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float)], + ); + declare_function( + state, + "swgl_commitGradientColorRGBA8", + None, + Type::new(Void), + vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float), Type::new(Float)], + ); + for s in &[Sampler2D, Sampler2DRect] { + declare_function_ext( + state, + "swgl_isTextureLinear", + None, + Type::new(Bool), + vec![Type::new(*s)], + RunClass::Scalar, + ); + declare_function_ext( + state, + "swgl_isTextureRGBA8", + None, + Type::new(Bool), + vec![Type::new(*s)], + RunClass::Scalar, + ); + declare_function_ext( + state, + "swgl_isTextureR8", + None, + Type::new(Bool), + vec![Type::new(*s)], + RunClass::Scalar, + ); + declare_function( + state, + "swgl_commitTextureLinearRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureLinearR8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureLinearR8ToRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitPartialTextureLinearR8", + None, + Type::new(Void), + vec![Type::new(Int), Type::new(*s), Type::new(Vec2), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitPartialTextureLinearInvertR8", + None, + Type::new(Void), + vec![Type::new(Int), Type::new(*s), Type::new(Vec2), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureLinearColorRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureLinearColorRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Float)], + ); + declare_function( + state, + "swgl_commitTextureLinearColorR8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Float)], + ); + declare_function( + state, + "swgl_commitTextureLinearColorR8ToRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Vec4)], + ); + + declare_function( + state, + "swgl_commitTextureLinearRepeatRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), + Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureLinearRepeatColorRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), + Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], + ); + + declare_function( + state, + "swgl_commitTextureNearestRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureNearestColorRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureNearestRepeatRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), + Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureNearestRepeatColorRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), + Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], + ); + + declare_function( + state, + "swgl_commitTextureRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureColorRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureRepeatRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), + Type::new(Vec4), Type::new(Vec4)], + ); + declare_function( + state, + "swgl_commitTextureRepeatColorRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), + Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], + ); + + declare_function( + state, + "swgl_commitGaussianBlurRGBA8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Bool), + Type::new(Int), Type::new(Vec2)], + ); + declare_function( + state, + "swgl_commitGaussianBlurR8", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Bool), + Type::new(Int), Type::new(Vec2)], + ); + declare_function( + state, + "swgl_commitTextureLinearYUV", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(Vec3), Type::new(Mat3), Type::new(Int)], + ); + declare_function( + state, + "swgl_commitTextureLinearYUV", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(Vec3), Type::new(Mat3), Type::new(Int)], + ); + declare_function( + state, + "swgl_commitTextureLinearYUV", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(Vec3), Type::new(Mat3), Type::new(Int)], + ); + declare_function( + state, + "swgl_commitTextureLinearColorYUV", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(Vec3), Type::new(Mat3), Type::new(Int), + Type::new(Float)], + ); + declare_function( + state, + "swgl_commitTextureLinearColorYUV", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(Vec3), Type::new(Mat3), Type::new(Int), + Type::new(Float)], + ); + declare_function( + state, + "swgl_commitTextureLinearColorYUV", + None, + Type::new(Void), + vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(*s), Type::new(Vec2), Type::new(Vec4), + Type::new(Vec3), Type::new(Mat3), Type::new(Int), + Type::new(Float)], + ); + } + + TranslationUnit(tu.0.map(state, translate_external_declaration)) +} + +fn infer_expr_inner(state: &mut State, expr: &Expr, assign: &mut SymRef) -> RunClass { + match expr.kind { + ExprKind::Variable(ref i) => { + *assign = *i; + match &state.sym(*i).decl { + SymDecl::Local(_, _, ref run_class) => *run_class, + SymDecl::Global(_, _, _, ref run_class) => *run_class, + _ => panic!(), + } + } + ExprKind::IntConst(_) + | ExprKind::UIntConst(_) + | ExprKind::BoolConst(_) + | ExprKind::FloatConst(_) + | ExprKind::DoubleConst(_) => RunClass::Scalar, + ExprKind::Unary(_, ref e) => infer_expr(state, e), + ExprKind::Binary(_, ref l, ref r) => infer_expr(state, l).merge(infer_expr(state, r)), + ExprKind::Ternary(ref c, ref s, ref e) => infer_expr(state, c) + .merge(infer_expr(state, s)) + .merge(infer_expr(state, e)), + ExprKind::Assignment(ref v, _, ref e) => { + let mut sym = SymRef(!0); + let run_class = infer_expr_inner(state, v, &mut sym).merge(infer_expr(state, e)); + assert!(sym != SymRef(!0)); + state.merge_run_class(sym, run_class) + } + ExprKind::Bracket(ref e, ref indx) => { + indx.iter().fold( + infer_expr_inner(state, e, assign), + |run_class, indx| run_class.merge(infer_expr(state, indx)), + ) + } + ExprKind::FunCall(ref fun, ref args) => { + let arg_classes: Vec<(RunClass, SymRef)> = args + .iter() + .map(|e| { + let mut assign = SymRef(!0); + let run_class = infer_expr_inner(state, e, &mut assign); + (run_class, assign) + }) + .collect(); + let run_class = if args.is_empty() { + RunClass::Scalar + } else { + arg_classes + .iter() + .fold(RunClass::Unknown, |x, &(y, _)| x.merge(y)) + }; + match fun { + FunIdentifier::Identifier(ref sym) => match &state.sym(*sym).decl { + SymDecl::NativeFunction(_, _, ref ret_class) => { + if *ret_class != RunClass::Unknown { + *ret_class + } else { + run_class + } + } + SymDecl::UserFunction(ref fd, ref run_class) => { + for (&(mut arg_class, assign), param) in + arg_classes.iter().zip(fd.prototype.parameters.iter()) + { + if let FunctionParameterDeclaration::Named(Some(qual), p) = param { + match qual { + ParameterQualifier::InOut | ParameterQualifier::Out => { + if let SymDecl::Local(_, _, param_class) = + &state.sym(p.sym).decl + { + match param_class { + RunClass::Unknown | RunClass::Vector => { + arg_class = RunClass::Vector; + } + RunClass::Dependent(mask) => { + for i in 0 .. 31 { + if (mask & (1 << i)) != 0 { + arg_class = + arg_class.merge(arg_classes[i].0); + } + } + } + RunClass::Scalar => {} + } + } + assert!(assign != SymRef(!0)); + state.merge_run_class(assign, arg_class); + } + _ => {} + } + } + } + if fd.prototype.ty.kind == TypeKind::Void { + RunClass::Scalar + } else { + match *run_class { + RunClass::Unknown | RunClass::Vector => RunClass::Vector, + RunClass::Dependent(mask) => { + let mut ret_class = RunClass::Unknown; + for i in 0 .. 31 { + if (mask & (1 << i)) != 0 { + ret_class = ret_class.merge(arg_classes[i].0); + } + } + ret_class + } + RunClass::Scalar => RunClass::Scalar, + } + } + } + SymDecl::Struct(..) => run_class, + _ => panic!(), + }, + FunIdentifier::Constructor(..) => run_class, + } + } + ExprKind::Dot(ref e, _) => infer_expr_inner(state, e, assign), + ExprKind::SwizzleSelector(ref e, _) => infer_expr_inner(state, e, assign), + ExprKind::PostInc(ref e) => infer_expr_inner(state, e, assign), + ExprKind::PostDec(ref e) => infer_expr_inner(state, e, assign), + ExprKind::Comma(ref a, ref b) => { + infer_expr(state, a); + infer_expr(state, b) + } + ExprKind::Cond(_, ref e) => infer_expr(state, e), + ExprKind::CondMask => RunClass::Vector, + } +} + +fn infer_expr(state: &mut State, expr: &Expr) -> RunClass { + infer_expr_inner(state, expr, &mut SymRef(!0)) +} + +fn infer_condition(state: &mut State, c: &Condition) { + match *c { + Condition::Expr(ref e) => { + infer_expr(state, e); + } + } +} + +fn infer_iteration_statement(state: &mut State, ist: &IterationStatement) { + let changed = state.run_class_changed.replace(true); + match *ist { + IterationStatement::While(ref cond, ref body) => { + while state.run_class_changed.replace(false) { + infer_condition(state, cond); + infer_statement(state, body); + } + } + IterationStatement::DoWhile(ref body, ref cond) => { + while state.run_class_changed.replace(false) { + infer_statement(state, body); + infer_expr(state, cond); + } + } + IterationStatement::For(ref init, ref rest, ref body) => { + match *init { + ForInitStatement::Expression(ref expr) => { + if let Some(ref e) = *expr { + infer_expr(state, e); + } + } + ForInitStatement::Declaration(ref d) => { + infer_declaration(state, d); + } + } + while state.run_class_changed.replace(false) { + if let Some(ref cond) = rest.condition { + infer_condition(state, cond); + } + if let Some(ref e) = rest.post_expr { + infer_expr(state, e); + } + infer_statement(state, body); + } + } + } + state.run_class_changed.set(changed); +} + +fn infer_selection_statement(state: &mut State, sst: &SelectionStatement) { + let mut branch_run_class = state.branch_run_class.merge(infer_expr(state, &sst.cond)); + mem::swap(&mut state.branch_run_class, &mut branch_run_class); + let branch_declaration = state.branch_declaration; + state.branch_declaration = state.last_declaration; + infer_statement(state, &sst.body); + if let Some(ref else_st) = sst.else_stmt { + infer_statement(state, else_st); + } + state.branch_run_class = branch_run_class; + state.branch_declaration = branch_declaration; +} + +fn infer_expression_statement(state: &mut State, est: &ExprStatement) { + if let Some(ref e) = *est { + infer_expr(state, e); + } +} + +fn infer_switch_statement(state: &mut State, sst: &SwitchStatement) { + let mut branch_run_class = state.branch_run_class.merge(infer_expr(state, &sst.head)); + mem::swap(&mut state.branch_run_class, &mut branch_run_class); + let branch_declaration = state.branch_declaration; + state.branch_declaration = state.last_declaration; + for case in &sst.cases { + for st in &case.stmts { + infer_statement(state, st); + } + } + state.branch_run_class = branch_run_class; + state.branch_declaration = branch_declaration; +} + +fn infer_jump_statement(state: &mut State, j: &JumpStatement) { + match *j { + JumpStatement::Continue => {} + JumpStatement::Break => {} + JumpStatement::Discard => {} + JumpStatement::Return(ref e) => { + if let Some(e) = e { + let run_class = infer_expr(state, e); + state.return_run_class(run_class); + } + } + } +} + +fn infer_initializer(state: &mut State, i: &Initializer) -> RunClass { + match *i { + Initializer::Simple(ref e) => infer_expr(state, e), + Initializer::List(ref list) => { + let mut run_class = RunClass::Unknown; + for ini in list.0.iter() { + run_class = run_class.merge(infer_initializer(state, ini)); + } + run_class + } + } +} + +fn infer_declaration(state: &mut State, d: &Declaration) { + match *d { + Declaration::FunctionPrototype(..) => {} + Declaration::InitDeclaratorList(ref list) => { + state.last_declaration = list.head.name; + + let mut run_class = RunClass::Unknown; + for decl in &list.tail { + if let Some(ref initializer) = decl.initializer { + run_class = run_class.merge(infer_initializer(state, initializer)); + } + } + if let Some(ref initializer) = list.head.initializer { + run_class = run_class.merge(infer_initializer(state, initializer)); + state.merge_run_class(list.head.name, run_class); + } + } + Declaration::Precision(..) => {} + Declaration::Block(..) => {} + Declaration::Global(..) => {} + Declaration::StructDefinition(..) => {} + } +} + +fn infer_simple_statement(state: &mut State, sst: &SimpleStatement) { + match *sst { + SimpleStatement::Declaration(ref d) => infer_declaration(state, d), + SimpleStatement::Expression(ref e) => infer_expression_statement(state, e), + SimpleStatement::Selection(ref s) => infer_selection_statement(state, s), + SimpleStatement::Switch(ref s) => infer_switch_statement(state, s), + SimpleStatement::Iteration(ref i) => infer_iteration_statement(state, i), + SimpleStatement::Jump(ref j) => infer_jump_statement(state, j), + } +} + +fn infer_compound_statement(state: &mut State, cst: &CompoundStatement) { + for st in &cst.statement_list { + infer_statement(state, st); + } +} + +fn infer_statement(state: &mut State, st: &Statement) { + match *st { + Statement::Compound(ref cst) => infer_compound_statement(state, cst), + Statement::Simple(ref sst) => infer_simple_statement(state, sst), + } +} + +fn infer_function_definition(state: &mut State, fd: &FunctionDefinition) { + state.in_function = Some(state.lookup(fd.prototype.name.as_str()).unwrap()); + + state.run_class_changed.set(true); + while state.run_class_changed.replace(false) { + for st in &fd.body.statement_list { + infer_statement(state, st); + } + } + + state.in_function = None; +} + +fn infer_external_declaration(state: &mut State, ed: &ExternalDeclaration) { + match *ed { + ExternalDeclaration::Preprocessor(_) => {} + ExternalDeclaration::FunctionDefinition(ref fd) => infer_function_definition(state, fd), + ExternalDeclaration::Declaration(_) => {} + } +} + +pub fn infer_run_class(state: &mut State, tu: &TranslationUnit) { + for ed in &(tu.0).0 { + infer_external_declaration(state, ed); + } +} diff --git a/gfx/wr/glsl-to-cxx/src/lib.rs b/gfx/wr/glsl-to-cxx/src/lib.rs new file mode 100644 index 0000000000..0e8273a987 --- /dev/null +++ b/gfx/wr/glsl-to-cxx/src/lib.rs @@ -0,0 +1,3681 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate glsl; + +mod hir; + +use glsl::parser::Parse; +use glsl::syntax; +use glsl::syntax::{TranslationUnit, UnaryOp}; +use hir::{Statement, Type}; +use std::cell::{Cell, RefCell}; +use std::collections::{BTreeMap, HashMap}; +use std::io::Read; +use std::mem; + +#[derive(PartialEq, Eq)] +enum ShaderKind { + Fragment, + Vertex, +} + +type UniformIndices = BTreeMap<String, (i32, hir::TypeKind, hir::StorageClass)>; + +fn build_uniform_indices(indices: &mut UniformIndices, state: &hir::State) { + for u in state.used_globals.borrow().iter() { + let sym = state.sym(*u); + match &sym.decl { + hir::SymDecl::Global(storage, _, ty, _) => match storage { + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + let next_index = indices.len() as i32 + 1; + indices.entry(sym.name.clone()).or_insert(( + next_index, + ty.kind.clone(), + *storage, + )); + } + _ => {} + }, + _ => {} + } + } +} + +pub fn translate(args: &mut dyn Iterator<Item = String>) -> String { + let _cmd_name = args.next(); + let vertex_file = args.next().unwrap(); + + let vs_name = std::path::Path::new(&vertex_file) + .file_stem() + .unwrap() + .to_string_lossy() + .to_string(); + + let frag_file = args.next().unwrap(); + + let fs_name = std::path::Path::new(&frag_file) + .file_stem() + .unwrap() + .to_string_lossy() + .to_string(); + + let (vs_state, vs_hir, vs_is_frag) = parse_shader(vertex_file); + let (fs_state, fs_hir, fs_is_frag) = parse_shader(frag_file); + + // we use a BTree so that iteration is stable + let mut uniform_indices = BTreeMap::new(); + build_uniform_indices(&mut uniform_indices, &vs_state); + build_uniform_indices(&mut uniform_indices, &fs_state); + + assert_eq!(fs_name, vs_name); + + let mut result = translate_shader( + vs_name, + vs_state, + vs_hir, + vs_is_frag, + &uniform_indices, + ); + result += "\n"; + result += &translate_shader( + fs_name, + fs_state, + fs_hir, + fs_is_frag, + &uniform_indices, + ); + result +} + +fn parse_shader(file: String) -> (hir::State, hir::TranslationUnit, bool) { + let mut contents = String::new(); + let is_frag = file.contains("frag"); + std::fs::File::open(&file) + .unwrap() + .read_to_string(&mut contents) + .unwrap(); + let r = TranslationUnit::parse(contents); + + //println!("{:#?}", r); + let mut ast_glsl = String::new(); + let r = r.unwrap(); + glsl::transpiler::glsl::show_translation_unit(&mut ast_glsl, &r); + //let mut fast = std::fs::File::create("ast").unwrap(); + //fast.write(ast_glsl.as_bytes()); + + let mut state = hir::State::new(); + let hir = hir::ast_to_hir(&mut state, &r); + (state, hir, is_frag) +} + +fn translate_shader( + name: String, + mut state: hir::State, + hir: hir::TranslationUnit, + is_frag: bool, + uniform_indices: &UniformIndices, +) -> String { + //println!("{:#?}", state); + + hir::infer_run_class(&mut state, &hir); + + let mut uniforms = Vec::new(); + let mut inputs = Vec::new(); + let mut outputs = Vec::new(); + + for i in &hir { + match i { + hir::ExternalDeclaration::Declaration(hir::Declaration::InitDeclaratorList(ref d)) => { + match &state.sym(d.head.name).decl { + hir::SymDecl::Global(storage, ..) + if state.used_globals.borrow().contains(&d.head.name) => + { + match storage { + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + uniforms.push(d.head.name); + } + hir::StorageClass::In => { + inputs.push(d.head.name); + } + hir::StorageClass::Out | hir::StorageClass::FragColor(_) => { + outputs.push(d.head.name); + } + _ => {} + } + } + _ => {} + } + } + _ => {} + } + } + + //println!("{:#?}", hir); + + let mut state = OutputState { + hir: state, + output: String::new(), + buffer: RefCell::new(String::new()), + indent: 0, + should_indent: false, + output_cxx: false, + mask: None, + cond_index: 0, + return_type: None, + return_declared: false, + return_vector: false, + is_scalar: Cell::new(false), + is_lval: Cell::new(false), + name: name.clone(), + kind: if is_frag { + ShaderKind::Fragment + } else { + ShaderKind::Vertex + }, + functions: HashMap::new(), + deps: RefCell::new(Vec::new()), + vector_mask: 0, + uses_discard: false, + used_fragcoord: Cell::new(0), + use_perspective: false, + used_globals: RefCell::new(Vec::new()), + texel_fetches: RefCell::new(Vec::new()), + }; + + show_translation_unit(&mut state, &hir); + let _output_glsl = state.finish_output(); + + state.should_indent = true; + state.output_cxx = true; + + if state.output_cxx { + let part_name = name.to_owned() + + match state.kind { + ShaderKind::Vertex => "_vert", + ShaderKind::Fragment => "_frag", + }; + + if state.kind == ShaderKind::Vertex { + write_common_globals(&mut state, &inputs, &outputs, uniform_indices); + write!(state, "struct {0}_vert : VertexShaderImpl, {0}_common {{\nprivate:\n", name); + } else { + write!(state, "struct {0}_frag : FragmentShaderImpl, {0}_vert {{\nprivate:\n", name); + } + + write!(state, "typedef {} Self;\n", part_name); + + show_translation_unit(&mut state, &hir); + + let pruned_inputs: Vec<_> = inputs + .iter() + .filter(|i| state.used_globals.borrow().contains(i)) + .cloned() + .collect(); + + if state.kind == ShaderKind::Vertex { + write_set_uniform_1i(&mut state, uniform_indices); + write_set_uniform_4fv(&mut state, uniform_indices); + write_set_uniform_matrix4fv(&mut state, uniform_indices); + write_load_attribs(&mut state, &pruned_inputs); + write_store_outputs(&mut state, &outputs); + } else { + write_read_inputs(&mut state, &pruned_inputs); + } + + write_abi(&mut state); + write!(state, "}};\n\n"); + + if state.kind == ShaderKind::Fragment { + write!(state, "struct {0}_program : ProgramImpl, {0}_frag {{\n", name); + write_get_uniform_index(&mut state, uniform_indices); + write!(state, "void bind_attrib(const char* name, int index) override {{\n"); + write!(state, " attrib_locations.bind_loc(name, index);\n}}\n"); + write!(state, "int get_attrib(const char* name) const override {{\n"); + write!(state, " return attrib_locations.get_loc(name);\n}}\n"); + write!(state, "size_t interpolants_size() const override {{ return sizeof(InterpOutputs); }}\n"); + write!(state, "VertexShaderImpl* get_vertex_shader() override {{\n"); + write!(state, " return this;\n}}\n"); + write!(state, "FragmentShaderImpl* get_fragment_shader() override {{\n"); + write!(state, " return this;\n}}\n"); + write!(state, "const char* get_name() const override {{ return \"{}\"; }}\n", name); + write!(state, "static ProgramImpl* loader() {{ return new {}_program; }}\n", name); + write!(state, "}};\n\n"); + } + + define_global_consts(&mut state, &hir, &part_name); + } else { + show_translation_unit(&mut state, &hir); + } + let output_cxx = state.finish_output(); + + //let mut hir = std::fs::File::create("hir").unwrap(); + //hir.write(output_glsl.as_bytes()); + + output_cxx +} + +fn write_get_uniform_index(state: &mut OutputState, uniform_indices: &UniformIndices) { + write!( + state, + "int get_uniform(const char *name) const override {{\n" + ); + for (uniform_name, (index, _, _)) in uniform_indices.iter() { + write!( + state, + " if (strcmp(\"{}\", name) == 0) {{ return {}; }}\n", + uniform_name, index + ); + } + write!(state, " return -1;\n"); + write!(state, "}}\n"); +} + +fn float4_compatible(ty: hir::TypeKind) -> bool { + match ty { + hir::TypeKind::Vec4 => true, + _ => false, + } +} + +fn matrix4_compatible(ty: hir::TypeKind) -> bool { + match ty { + hir::TypeKind::Mat4 => true, + _ => false, + } +} + +fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndices) { + write!(state, "struct Samplers {{\n"); + for (name, (_, tk, storage)) in uniform_indices.iter() { + match tk { + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect + | hir::TypeKind::ISampler2D => { + write!(state, " "); + show_type_kind(state, &tk); + let suffix = if let hir::StorageClass::Sampler(format) = storage { + format.type_suffix() + } else { + None + }; + write!(state, "{}_impl {}_impl;\n", suffix.unwrap_or(""), name); + write!(state, " int {}_slot;\n", name); + } + _ => {} + } + } + write!( + state, + " bool set_slot(int index, int value) {{\n" + ); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniform_indices.iter() { + match tk { + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect + | hir::TypeKind::ISampler2D => { + write!(state, " case {}:\n", index); + write!(state, " {}_slot = value;\n", name); + write!(state, " return true;\n"); + } + _ => {} + } + } + write!(state, " }}\n"); + write!(state, " return false;\n"); + write!(state, " }}\n"); + write!(state, "}} samplers;\n"); + +} + +fn write_bind_textures(state: &mut OutputState, uniforms: &UniformIndices) { + write!(state, "void bind_textures() {{\n"); + for (name, (_, tk, storage)) in uniforms { + match storage { + hir::StorageClass::Sampler(_format) => { + match tk { + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect => write!(state, + " {0} = lookup_sampler(&samplers.{0}_impl, samplers.{0}_slot);\n", + name), + hir::TypeKind::ISampler2D => write!(state, + " {0} = lookup_isampler(&samplers.{0}_impl, samplers.{0}_slot);\n", + name), + _ => {} + }; + } + _ => {} + } + } + write!(state, "}}\n"); +} + +fn write_set_uniform_1i( + state: &mut OutputState, + uniforms: &UniformIndices, +) { + write!( + state, + "static void set_uniform_1i(VertexShaderImpl* impl, int index, int value) {{\n" + ); + write!(state, " Self* self = (Self*)impl;\n"); + write!(state, " if (self->samplers.set_slot(index, value)) return;\n"); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniforms { + write!(state, " case {}:\n", index); + match tk { + hir::TypeKind::Int => write!( + state, + " self->{} = {}(value);\n", + name, + tk.cxx_primitive_scalar_type_name().unwrap(), + ), + _ => write!(state, " assert(0); // {}\n", name), + }; + write!(state, " break;\n"); + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_set_uniform_4fv( + state: &mut OutputState, + uniforms: &UniformIndices, +) { + write!( + state, + "static void set_uniform_4fv(VertexShaderImpl* impl, int index, const float *value) {{\n" + ); + write!(state, " Self* self = (Self*)impl;\n"); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniforms { + write!(state, " case {}:\n", index); + if float4_compatible(tk.clone()) { + write!( + state, + " self->{} = vec4_scalar::load_from_ptr(value);\n", + name + ); + } else { + write!(state, " assert(0); // {}\n", name); + } + write!(state, " break;\n"); + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_set_uniform_matrix4fv( + state: &mut OutputState, + uniforms: &UniformIndices, +) { + write!( + state, + "static void set_uniform_matrix4fv(VertexShaderImpl* impl, int index, const float *value) {{\n" + ); + write!(state, " Self* self = (Self*)impl;\n"); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniforms { + write!(state, " case {}:\n", index); + if matrix4_compatible(tk.clone()) { + write!( + state, + " self->{} = mat4_scalar::load_from_ptr(value);\n", + name + ); + } else { + write!(state, " assert(0); // {}\n", name); + } + write!(state, " break;\n"); + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_bind_attrib_location(state: &mut OutputState, attribs: &[hir::SymRef]) { + write!(state, "struct AttribLocations {{\n"); + for i in attribs { + let sym = state.hir.sym(*i); + write!(state, " int {} = NULL_ATTRIB;\n", sym.name.as_str()); + } + write!(state, " void bind_loc(const char* name, int index) {{\n"); + for i in attribs { + let sym = state.hir.sym(*i); + write!( + state, + " if (strcmp(\"{0}\", name) == 0) {{ {0} = index; return; }}\n", + sym.name.as_str() + ); + } + write!(state, " }}\n"); + write!(state, " int get_loc(const char* name) const {{\n"); + for i in attribs { + let sym = state.hir.sym(*i); + write!(state, + " if (strcmp(\"{0}\", name) == 0) {{ \ + return {0} != NULL_ATTRIB ? {0} : -1; \ + }}\n", + sym.name.as_str()); + } + write!(state, " return -1;\n"); + write!(state, " }}\n"); + write!(state, "}} attrib_locations;\n"); +} + +fn write_common_globals(state: &mut OutputState, attribs: &[hir::SymRef], + outputs: &[hir::SymRef], uniforms: &UniformIndices) { + write!(state, "struct {}_common {{\n", state.name); + + write_program_samplers(state, uniforms); + write_bind_attrib_location(state, attribs); + + let is_scalar = state.is_scalar.replace(true); + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Out, _, ty, hir::RunClass::Scalar) => { + show_type(state, ty); + write!(state, " {};\n", sym.name.as_str()); + } + _ => {} + } + } + for (name, (_, tk, storage)) in uniforms { + match storage { + hir::StorageClass::Sampler(format) => { + write!(state, + "{}{} {};\n", + tk.cxx_primitive_type_name().unwrap(), + format.type_suffix().unwrap_or(""), + name, + ); + } + _ => { + show_type_kind(state, tk); + write!(state, " {};\n", name); + } + } + } + state.is_scalar.set(is_scalar); + + write_bind_textures(state, uniforms); + + write!(state, "}};\n"); +} + +//fn type_name(state: &OutputState, ty: &Type) -> String { +// let buffer = state.push_buffer(); +// show_type(state, ty); +// state.pop_buffer(buffer) +//} + +fn write_load_attribs(state: &mut OutputState, attribs: &[hir::SymRef]) { + write!(state, "static void load_attribs(\ + VertexShaderImpl* impl, VertexAttrib *attribs, \ + uint32_t start, int instance, int count) {{\ + Self* self = (Self*)impl;\n"); + for i in attribs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _interpolation, _ty, run_class) => { + let name = sym.name.as_str(); + let func = if *run_class == hir::RunClass::Scalar { + "load_flat_attrib" + } else { + "load_attrib" + }; + write!(state, + " {0}(self->{1}, attribs[self->attrib_locations.{1}], start, instance, count);\n", + func, name); + } + _ => panic!(), + } + } + write!(state, "}}\n"); +} + +fn write_store_outputs(state: &mut OutputState, outputs: &[hir::SymRef]) { + let is_scalar = state.is_scalar.replace(true); + write!(state, "public:\nstruct InterpOutputs {{\n"); + if state.hir.used_clip_dist != 0 { + state.write(" Float swgl_ClipDistance;\n"); + } + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, run_class) => { + if *run_class != hir::RunClass::Scalar { + show_type(state, ty); + write!(state, " {};\n", sym.name.as_str()); + } + } + _ => panic!(), + } + } + + write!(state, "}};\nprivate:\n"); + state.is_scalar.set(is_scalar); + + write!( + state, + "ALWAYS_INLINE void store_interp_outputs(char* dest_ptr, size_t stride) {{\n" + ); + write!(state, " for(int n = 0; n < 4; n++) {{\n"); + write!( + state, + " auto* dest = reinterpret_cast<InterpOutputs*>(dest_ptr);\n" + ); + if state.hir.used_clip_dist != 0 { + for (i, comp) in "xyzw".chars().enumerate() { + if (state.hir.used_clip_dist & (1 << i)) != 0 { + write!(state, " dest->swgl_ClipDistance.{} = get_nth(gl_ClipDistance[{}], n);\n", comp, i); + } else { + write!(state, " dest->swgl_ClipDistance.{} = 0.0f;\n", comp); + } + } + } + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " dest->{} = get_nth({}, n);\n", name, name); + } + } + _ => panic!(), + } + } + write!(state, " dest_ptr += stride;\n"); + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { + write!( + state, + "typedef {}_vert::InterpOutputs InterpInputs;\n", + state.name + ); + + write!(state, "InterpInputs interp_step;\n"); + + let mut has_varying = false; + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, run_class) => { + if *run_class != hir::RunClass::Scalar { + if !has_varying { + has_varying = true; + write!(state, "struct InterpPerspective {{\n"); + } + show_type(state, ty); + write!(state, " {};\n", sym.name.as_str()); + } + } + _ => panic!(), + } + } + if has_varying { + write!(state, "}};\n"); + write!(state, "InterpPerspective interp_perspective;\n"); + } + + write!(state, + "static void read_interp_inputs(\ + FragmentShaderImpl* impl, const void* init_, const void* step_) {{\ + Self* self = (Self*)impl;\ + const InterpInputs* init = (const InterpInputs*)init_;\ + const InterpInputs* step = (const InterpInputs*)step_;\n"); + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!( + state, + " self->{0} = init_interp(init->{0}, step->{0});\n", + name + ); + write!( + state, + " self->interp_step.{0} = step->{0} * 4.0f;\n", + name + ); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + + let used_fragcoord = state.used_fragcoord.get(); + if has_varying || (used_fragcoord & (4 | 8)) != 0 { + state.use_perspective = true; + } + if state.use_perspective { + write!(state, + "static void read_perspective_inputs(\ + FragmentShaderImpl* impl, const void* init_, const void* step_) {{\ + Self* self = (Self*)impl;\ + const InterpInputs* init = (const InterpInputs*)init_;\ + const InterpInputs* step = (const InterpInputs*)step_;\n"); + if has_varying { + write!(state, " Float w = 1.0f / self->gl_FragCoord.w;\n"); + } + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!( + state, + " self->interp_perspective.{0} = init_interp(init->{0}, step->{0});\n", + name + ); + write!(state, " self->{0} = self->interp_perspective.{0} * w;\n", name); + write!( + state, + " self->interp_step.{0} = step->{0} * 4.0f;\n", + name + ); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + } + + write!(state, "ALWAYS_INLINE void step_interp_inputs(int steps = 4) {{\n"); + if (used_fragcoord & 1) != 0 { + write!(state, " step_fragcoord(steps);\n"); + } + if !inputs.is_empty() { + write!(state, " float chunks = steps * 0.25f;\n"); + } + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " {0} += interp_step.{0} * chunks;\n", name); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + + if state.use_perspective { + write!(state, "ALWAYS_INLINE void step_perspective_inputs(int steps = 4) {{\n"); + if (used_fragcoord & 1) != 0 { + write!(state, " step_fragcoord(steps);\n"); + } + write!(state, " step_perspective(steps);\n"); + if !inputs.is_empty() { + write!(state, " float chunks = steps * 0.25f;\n"); + } + if has_varying { + write!(state, " Float w = 1.0f / gl_FragCoord.w;\n"); + } + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " interp_perspective.{0} += interp_step.{0} * chunks;\n", name); + write!(state, " {0} = w * interp_perspective.{0};\n", name); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + } +} + +pub struct OutputState { + hir: hir::State, + output: String, + buffer: RefCell<String>, + should_indent: bool, + output_cxx: bool, + indent: i32, + mask: Option<Box<hir::Expr>>, + cond_index: usize, + return_type: Option<Box<hir::Type>>, + return_declared: bool, + return_vector: bool, + is_scalar: Cell<bool>, + is_lval: Cell<bool>, + name: String, + kind: ShaderKind, + functions: HashMap<(hir::SymRef, u32), bool>, + deps: RefCell<Vec<(hir::SymRef, u32)>>, + vector_mask: u32, + uses_discard: bool, + used_fragcoord: Cell<i32>, + use_perspective: bool, + used_globals: RefCell<Vec<hir::SymRef>>, + texel_fetches: RefCell<Vec<(hir::SymRef, hir::SymRef, hir::TexelFetchOffsets)>>, +} + +use std::fmt::{Arguments, Write}; + +impl OutputState { + fn indent(&mut self) { + if self.should_indent { + self.indent += 1 + } + } + fn outdent(&mut self) { + if self.should_indent { + self.indent -= 1 + } + } + + fn write(&self, s: &str) { + self.buffer.borrow_mut().push_str(s); + } + + fn flush_buffer(&mut self) { + self.output.push_str(&self.buffer.borrow()); + self.buffer.borrow_mut().clear(); + } + + fn finish_output(&mut self) -> String { + self.flush_buffer(); + + let mut s = String::new(); + mem::swap(&mut self.output, &mut s); + s + } + + fn push_buffer(&self) -> String { + self.buffer.replace(String::new()) + } + + fn pop_buffer(&self, s: String) -> String { + self.buffer.replace(s) + } + + fn write_fmt(&self, args: Arguments) { + let _ = self.buffer.borrow_mut().write_fmt(args); + } +} + +pub fn show_identifier(state: &OutputState, i: &syntax::Identifier) { + state.write(&i.0); +} + +fn glsl_primitive_type_name_to_cxx(glsl_name: &str) -> &str { + hir::TypeKind::from_glsl_primitive_type_name(glsl_name) + .and_then(|kind| kind.cxx_primitive_type_name()) + .unwrap_or(glsl_name) +} + +fn add_used_global(state: &OutputState, i: &hir::SymRef) { + let mut globals = state.used_globals.borrow_mut(); + if !globals.contains(i) { + globals.push(*i); + } +} + +pub fn show_sym(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::NativeFunction(_, ref cxx_name, _) => { + let mut name = sym.name.as_str(); + if state.output_cxx { + name = cxx_name.unwrap_or(name); + } + state.write(name); + } + hir::SymDecl::Global(..) => { + if state.output_cxx { + add_used_global(state, i); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::UserFunction(..) | hir::SymDecl::Local(..) | hir::SymDecl::Struct(..) => { + let mut name = sym.name.as_str(); + // we want to replace constructor names + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + } +} + +pub fn show_variable(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, _) => { + show_type(state, ty); + state.write(" "); + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + _ => panic!(), + } +} + +pub fn write_default_constructor(state: &OutputState, name: &str) { + // write default constructor + let _ = write!(state, "{}() = default;\n", name); +} + +pub fn write_constructor(state: &OutputState, name: &str, s: &hir::StructFields) { + if s.fields.len() == 1 { + state.write("explicit "); + } + let _ = write!(state, "{}(", name); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + show_type(state, &field.ty); + state.write(" "); + show_identifier_and_type(state, &field.name, &field.ty); + first_field = false; + } + state.write(") : "); + + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + let _ = write!(state, "{}({})", field.name, field.name); + first_field = false; + } + state.write("{}\n"); +} + +pub fn write_convert_constructor(state: &OutputState, name: &str, s: &hir::StructFields) { + if s.fields.len() == 1 { + state.write("explicit "); + } + let _ = write!(state, "{}(", name); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + + let is_scalar = state.is_scalar.replace(true); + show_type(state, &field.ty); + state.is_scalar.set(is_scalar); + + state.write(" "); + + show_identifier_and_type(state, &field.name, &field.ty); + first_field = false; + } + state.write(")"); + + let mut first_field = true; + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_none() { + if first_field { + state.write(":"); + } else { + state.write(","); + } + let _ = write!(state, "{}({})", name, name); + first_field = false; + } + } + state.write("{\n"); + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_some() { + let _ = write!(state, "this->{}.convert({});\n", name, name); + } + } + state.write("}\n"); + + let _ = write!(state, "IMPLICIT {}({}_scalar s)", name, name); + let mut first_field = true; + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_none() { + if first_field { + state.write(":"); + } else { + state.write(","); + } + let _ = write!(state, "{}(s.{})", name, name); + first_field = false; + } + } + state.write("{\n"); + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_some() { + let _ = write!(state, "{}.convert(s.{});\n", name, name); + } + } + state.write("}\n"); +} + +pub fn write_if_then_else(state: &OutputState, name: &str, s: &hir::StructFields) { + let _ = write!( + state, + "friend {} if_then_else(I32 c, {} t, {} e) {{ return {}(\n", + name, name, name, name + ); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + let _ = write!(state, "if_then_else(c, t.{}, e.{})", field.name, field.name); + first_field = false; + } + state.write(");\n}"); +} + +pub fn show_storage_class(state: &OutputState, q: &hir::StorageClass) { + match *q { + hir::StorageClass::None => {} + hir::StorageClass::Const => { + state.write("const "); + } + hir::StorageClass::In => { + state.write("in "); + } + hir::StorageClass::Out => { + state.write("out "); + } + hir::StorageClass::FragColor(index) => { + write!(state, "layout(location = 0, index = {}) out ", index); + } + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + state.write("uniform "); + } + } +} + +pub fn show_sym_decl(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(storage, ..) => { + if !state.output_cxx { + show_storage_class(state, storage) + } + if storage == &hir::StorageClass::Const { + state.write("static constexpr "); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::Local(storage, ..) => { + if !state.output_cxx { + show_storage_class(state, storage) + } + if storage == &hir::StorageClass::Const { + state.write("const "); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::Struct(s) => { + let name = sym.name.as_str(); + + if state.output_cxx { + let name_scalar = format!("{}_scalar", name); + write!(state, "struct {} {{\n", name_scalar); + let is_scalar = state.is_scalar.replace(true); + for field in &s.fields { + show_struct_field(state, field); + } + write_default_constructor(state, &name_scalar); + write_constructor(state, &name_scalar, s); + state.is_scalar.set(is_scalar); + state.write("};\n"); + } + + write!(state, "struct {} {{\n", name); + for field in &s.fields { + show_struct_field(state, field); + } + + // write if_then_else + if state.output_cxx { + write_default_constructor(state, name); + write_constructor(state, name, s); + write_convert_constructor(state, name, s); + write_if_then_else(state, name, s); + } + state.write("}"); + } + _ => panic!(), + } +} + +pub fn show_type_name(state: &OutputState, t: &syntax::TypeName) { + state.write(&t.0); +} + +pub fn show_type_specifier_non_array(state: &mut OutputState, t: &syntax::TypeSpecifierNonArray) { + if let Some(kind) = hir::TypeKind::from_primitive_type_specifier(t) { + show_type_kind(state, &kind); + } else { + match t { + syntax::TypeSpecifierNonArray::Struct(ref _s) => panic!(), //show_struct_non_declaration(state, s), + syntax::TypeSpecifierNonArray::TypeName(ref tn) => show_type_name(state, tn), + _ => unreachable!(), + } + } +} + +pub fn show_type_kind(state: &OutputState, t: &hir::TypeKind) { + if state.output_cxx { + if state.is_scalar.get() { + if let Some(name) = t.cxx_primitive_scalar_type_name() { + state.write(name); + } else if let Some(name) = t.cxx_primitive_type_name() { + let mut scalar_name = String::from(name); + scalar_name.push_str("_scalar"); + state.write(scalar_name.as_str()); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + let mut scalar_name = String::from(state.hir.sym(*s).name.as_str()); + scalar_name.push_str("_scalar"); + state.write(scalar_name.as_str()); + } + _ => unreachable!(), + } + } + } else if let Some(name) = t.cxx_primitive_type_name() { + state.write(name); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + state.write(state.hir.sym(*s).name.as_str()); + } + _ => unreachable!(), + } + } + } else if let Some(name) = t.glsl_primitive_type_name() { + state.write(name); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + state.write(state.hir.sym(*s).name.as_str()); + } + _ => unreachable!(), + } + } +} + +pub fn show_type_specifier(state: &mut OutputState, t: &syntax::TypeSpecifier) { + show_type_specifier_non_array(state, &t.ty); + + if let Some(ref arr_spec) = t.array_specifier { + show_array_spec(state, arr_spec); + } +} + +pub fn show_type(state: &OutputState, t: &Type) { + if !state.output_cxx { + if let Some(ref precision) = t.precision { + show_precision_qualifier(state, precision); + state.write(" "); + } + } + + if state.output_cxx { + if let Some(ref array) = t.array_sizes { + state.write("Array<"); + show_type_kind(state, &t.kind); + let size = match &array.sizes[..] { + [size] => size, + _ => panic!(), + }; + state.write(","); + show_hir_expr(state, size); + state.write(">"); + } else { + show_type_kind(state, &t.kind); + } + } else { + show_type_kind(state, &t.kind); + } + + /*if let Some(ref arr_spec) = t.array_sizes { + panic!(); + }*/ +} + +/*pub fn show_fully_specified_type(state: &mut OutputState, t: &FullySpecifiedType) { + state.flat = false; + if let Some(ref qual) = t.qualifier { + if !state.output_cxx { + show_type_qualifier(state, &qual); + } else { + state.flat = + qual.qualifiers.0.iter() + .flat_map(|q| match q { syntax::TypeQualifierSpec::Interpolation(Flat) => Some(()), _ => None}) + .next().is_some(); + } + state.write(" "); + } + + show_type_specifier(state, &t.ty); +}*/ + +/*pub fn show_struct_non_declaration(state: &mut OutputState, s: &syntax::StructSpecifier) { + state.write("struct "); + + if let Some(ref name) = s.name { + let _ = write!(state, "{} ", name); + } + + state.write("{\n"); + + for field in &s.fields.0 { + show_struct_field(state, field); + } + + state.write("}"); +}*/ + +pub fn show_struct(_state: &OutputState, _s: &syntax::StructSpecifier) { + panic!(); + //show_struct_non_declaration(state, s); + //state.write(";\n"); +} + +pub fn show_struct_field(state: &OutputState, field: &hir::StructField) { + show_type(state, &field.ty); + state.write(" "); + + show_identifier_and_type(state, &field.name, &field.ty); + + state.write(";\n"); +} + +pub fn show_array_spec(state: &OutputState, a: &syntax::ArraySpecifier) { + for dimension in &a.dimensions { + match dimension { + syntax::ArraySpecifierDimension::Unsized => { + state.write("[]"); + } + syntax::ArraySpecifierDimension::ExplicitlySized(ref e) => { + state.write("["); + show_expr(state, &e); + state.write("]"); + } + } + } +} + +pub fn show_identifier_and_type(state: &OutputState, ident: &syntax::Identifier, ty: &hir::Type) { + let _ = write!(state, "{}", ident); + + if !state.output_cxx { + if let Some(ref arr_spec) = ty.array_sizes { + show_array_sizes(state, &arr_spec); + } + } +} + +pub fn show_arrayed_identifier(state: &OutputState, ident: &syntax::ArrayedIdentifier) { + let _ = write!(state, "{}", ident.ident); + + if let Some(ref arr_spec) = ident.array_spec { + show_array_spec(state, &arr_spec); + } +} + +pub fn show_array_sizes(state: &OutputState, a: &hir::ArraySizes) { + state.write("["); + match &a.sizes[..] { + [a] => show_hir_expr(state, a), + _ => panic!(), + } + + state.write("]"); + /* + match *a { + syntax::ArraySpecifier::Unsized => { state.write("[]"); } + syntax::ArraySpecifier::ExplicitlySized(ref e) => { + state.write("["); + show_expr(state, &e); + state.write("]"); + } + }*/ +} + +pub fn show_type_qualifier(state: &OutputState, q: &hir::TypeQualifier) { + let mut qualifiers = q.qualifiers.0.iter(); + let first = qualifiers.next().unwrap(); + + show_type_qualifier_spec(state, first); + + for qual_spec in qualifiers { + state.write(" "); + show_type_qualifier_spec(state, qual_spec) + } +} + +pub fn show_type_qualifier_spec(state: &OutputState, q: &hir::TypeQualifierSpec) { + match *q { + hir::TypeQualifierSpec::Layout(ref l) => show_layout_qualifier(state, &l), + hir::TypeQualifierSpec::Parameter(ref _p) => panic!(), + hir::TypeQualifierSpec::Memory(ref _m) => panic!(), + hir::TypeQualifierSpec::Invariant => { + state.write("invariant"); + } + hir::TypeQualifierSpec::Precise => { + state.write("precise"); + } + } +} + +pub fn show_syntax_storage_qualifier(state: &OutputState, q: &syntax::StorageQualifier) { + match *q { + syntax::StorageQualifier::Const => { + state.write("const"); + } + syntax::StorageQualifier::InOut => { + state.write("inout"); + } + syntax::StorageQualifier::In => { + state.write("in"); + } + syntax::StorageQualifier::Out => { + state.write("out"); + } + syntax::StorageQualifier::Centroid => { + state.write("centroid"); + } + syntax::StorageQualifier::Patch => { + state.write("patch"); + } + syntax::StorageQualifier::Sample => { + state.write("sample"); + } + syntax::StorageQualifier::Uniform => { + state.write("uniform"); + } + syntax::StorageQualifier::Attribute => { + state.write("attribute"); + } + syntax::StorageQualifier::Varying => { + state.write("varying"); + } + syntax::StorageQualifier::Buffer => { + state.write("buffer"); + } + syntax::StorageQualifier::Shared => { + state.write("shared"); + } + syntax::StorageQualifier::Coherent => { + state.write("coherent"); + } + syntax::StorageQualifier::Volatile => { + state.write("volatile"); + } + syntax::StorageQualifier::Restrict => { + state.write("restrict"); + } + syntax::StorageQualifier::ReadOnly => { + state.write("readonly"); + } + syntax::StorageQualifier::WriteOnly => { + state.write("writeonly"); + } + syntax::StorageQualifier::Subroutine(ref n) => show_subroutine(state, &n), + } +} + +pub fn show_subroutine(state: &OutputState, types: &[syntax::TypeName]) { + state.write("subroutine"); + + if !types.is_empty() { + state.write("("); + + let mut types_iter = types.iter(); + let first = types_iter.next().unwrap(); + + show_type_name(state, first); + + for type_name in types_iter { + state.write(", "); + show_type_name(state, type_name); + } + + state.write(")"); + } +} + +pub fn show_layout_qualifier(state: &OutputState, l: &syntax::LayoutQualifier) { + let mut qualifiers = l.ids.0.iter(); + let first = qualifiers.next().unwrap(); + + state.write("layout ("); + show_layout_qualifier_spec(state, first); + + for qual_spec in qualifiers { + state.write(", "); + show_layout_qualifier_spec(state, qual_spec); + } + + state.write(")"); +} + +pub fn show_layout_qualifier_spec(state: &OutputState, l: &syntax::LayoutQualifierSpec) { + match *l { + syntax::LayoutQualifierSpec::Identifier(ref i, Some(ref e)) => { + let _ = write!(state, "{} = ", i); + show_expr(state, &e); + } + syntax::LayoutQualifierSpec::Identifier(ref i, None) => show_identifier(state, &i), + syntax::LayoutQualifierSpec::Shared => { + state.write("shared"); + } + } +} + +pub fn show_precision_qualifier(state: &OutputState, p: &syntax::PrecisionQualifier) { + match *p { + syntax::PrecisionQualifier::High => { + state.write("highp"); + } + syntax::PrecisionQualifier::Medium => { + state.write("mediump"); + } + syntax::PrecisionQualifier::Low => { + state.write("low"); + } + } +} + +pub fn show_interpolation_qualifier(state: &OutputState, i: &syntax::InterpolationQualifier) { + match *i { + syntax::InterpolationQualifier::Smooth => { + state.write("smooth"); + } + syntax::InterpolationQualifier::Flat => { + state.write("flat"); + } + syntax::InterpolationQualifier::NoPerspective => { + state.write("noperspective"); + } + } +} + +pub fn show_parameter_qualifier(state: &mut OutputState, i: &Option<hir::ParameterQualifier>) { + if let Some(i) = i { + if state.output_cxx { + match *i { + hir::ParameterQualifier::Out => { + state.write("&"); + } + hir::ParameterQualifier::InOut => { + state.write("&"); + } + _ => {} + } + } else { + match *i { + hir::ParameterQualifier::Const => { + state.write("const"); + } + hir::ParameterQualifier::In => { + state.write("in"); + } + hir::ParameterQualifier::Out => { + state.write("out"); + } + hir::ParameterQualifier::InOut => { + state.write("inout"); + } + } + } + } +} + +pub fn show_float(state: &OutputState, x: f32) { + if x.fract() == 0. { + write!(state, "{}.f", x); + } else { + write!(state, "{}f", x); + } +} + +pub fn show_double(state: &OutputState, x: f64) { + // force doubles to print as floats + if x.fract() == 0. { + write!(state, "{}.f", x); + } else { + write!(state, "{}f", x); + } +} + +fn expr_run_class(state: &OutputState, expr: &hir::Expr) -> hir::RunClass { + match &expr.kind { + hir::ExprKind::Variable(i) => symbol_run_class(&state.hir.sym(*i).decl, state.vector_mask), + hir::ExprKind::IntConst(_) + | hir::ExprKind::UIntConst(_) + | hir::ExprKind::BoolConst(_) + | hir::ExprKind::FloatConst(_) + | hir::ExprKind::DoubleConst(_) => hir::RunClass::Scalar, + hir::ExprKind::Unary(_, ref e) => expr_run_class(state, e), + hir::ExprKind::Binary(_, ref l, ref r) => { + expr_run_class(state, l).merge(expr_run_class(state, r)) + } + hir::ExprKind::Ternary(ref c, ref s, ref e) => expr_run_class(state, c) + .merge(expr_run_class(state, s)) + .merge(expr_run_class(state, e)), + hir::ExprKind::Assignment(ref v, _, ref e) => { + expr_run_class(state, v).merge(expr_run_class(state, e)) + } + hir::ExprKind::Bracket(ref e, ref indx) => { + indx.iter().fold( + expr_run_class(state, e), + |run_class, indx| run_class.merge(expr_run_class(state, indx)), + ) + } + hir::ExprKind::FunCall(ref fun, ref args) => { + let arg_mask: u32 = args.iter().enumerate().fold(0, |mask, (idx, e)| { + if expr_run_class(state, e) == hir::RunClass::Vector { + mask | (1 << idx) + } else { + mask + } + }); + match fun { + hir::FunIdentifier::Identifier(ref sym) => match &state.hir.sym(*sym).decl { + hir::SymDecl::NativeFunction(_, _, ref ret_class) => { + if *ret_class != hir::RunClass::Unknown { + *ret_class + } else if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + hir::SymDecl::UserFunction(ref fd, ref run_class) => { + let param_mask: u32 = fd.prototype.parameters.iter().enumerate().fold( + arg_mask, + |mask, (idx, param)| { + if let hir::FunctionParameterDeclaration::Named(Some(qual), p) = + param + { + match qual { + hir::ParameterQualifier::InOut + | hir::ParameterQualifier::Out => { + if symbol_run_class( + &state.hir.sym(p.sym).decl, + arg_mask, + ) == hir::RunClass::Vector + { + mask | (1 << idx) + } else { + mask + } + } + _ => mask, + } + } else { + mask + } + }, + ); + match *run_class { + hir::RunClass::Scalar => hir::RunClass::Scalar, + hir::RunClass::Dependent(mask) => { + if (mask & param_mask) != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => hir::RunClass::Vector, + } + } + hir::SymDecl::Struct(..) => { + if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => panic!(), + }, + hir::FunIdentifier::Constructor(..) => { + if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + } + } + hir::ExprKind::Dot(ref e, _) => expr_run_class(state, e), + hir::ExprKind::SwizzleSelector(ref e, _) => expr_run_class(state, e), + hir::ExprKind::PostInc(ref e) => expr_run_class(state, e), + hir::ExprKind::PostDec(ref e) => expr_run_class(state, e), + hir::ExprKind::Comma(_, ref e) => expr_run_class(state, e), + hir::ExprKind::Cond(_, ref e) => expr_run_class(state, e), + hir::ExprKind::CondMask => hir::RunClass::Vector, + } +} + +pub fn show_hir_expr(state: &OutputState, expr: &hir::Expr) { + show_hir_expr_inner(state, expr, false); +} + +pub fn show_hir_expr_inner(state: &OutputState, expr: &hir::Expr, top_level: bool) { + match expr.kind { + hir::ExprKind::Variable(ref i) => show_sym(state, i), + hir::ExprKind::IntConst(ref x) => { + let _ = write!(state, "{}", x); + } + hir::ExprKind::UIntConst(ref x) => { + let _ = write!(state, "{}u", x); + } + hir::ExprKind::BoolConst(ref x) => { + let _ = write!(state, "{}", x); + } + hir::ExprKind::FloatConst(ref x) => show_float(state, *x), + hir::ExprKind::DoubleConst(ref x) => show_double(state, *x), + hir::ExprKind::Unary(ref op, ref e) => { + show_unary_op(state, &op); + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + } + hir::ExprKind::Binary(ref op, ref l, ref r) => { + state.write("("); + show_hir_expr(state, &l); + state.write(")"); + show_binary_op(state, &op); + state.write("("); + show_hir_expr(state, &r); + state.write(")"); + } + hir::ExprKind::Ternary(ref c, ref s, ref e) => { + if state.output_cxx && expr_run_class(state, c) != hir::RunClass::Scalar { + state.write("if_then_else("); + show_hir_expr(state, &c); + state.write(", "); + show_hir_expr(state, &s); + state.write(", "); + show_hir_expr(state, &e); + state.write(")"); + } else { + show_hir_expr(state, &c); + state.write(" ? "); + show_hir_expr(state, &s); + state.write(" : "); + show_hir_expr(state, &e); + } + } + hir::ExprKind::Assignment(ref v, ref op, ref e) => { + let is_output = hir::is_output(v, &state.hir).is_some(); + let is_scalar_var = expr_run_class(state, v) == hir::RunClass::Scalar; + let is_scalar_expr = expr_run_class(state, e) == hir::RunClass::Scalar; + let force_scalar = is_scalar_var && !is_scalar_expr; + + if let Some(mask) = &state.mask { + let is_scalar_mask = expr_run_class(state, mask) == hir::RunClass::Scalar; + let force_scalar_mask = is_scalar_var && is_scalar_expr && !is_scalar_mask; + + if force_scalar || force_scalar_mask { + if top_level { + state.write("if ("); + } else { + state.write("("); + } + } else { + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" = if_then_else("); + } + + if is_output && state.return_declared { + state.write("(("); + show_hir_expr(state, mask); + state.write(")&ret_mask)"); + } else { + show_hir_expr(state, mask); + } + if force_scalar || force_scalar_mask { + if top_level { + state.write("[0]) { "); + } else { + state.write("[0] ? "); + } + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" = "); + } else { + state.write(","); + } + + if op != &syntax::AssignmentOp::Equal { + show_hir_expr(state, &v); + } + + match *op { + syntax::AssignmentOp::Equal => {} + syntax::AssignmentOp::Mult => { + state.write("*"); + } + syntax::AssignmentOp::Div => { + state.write("/"); + } + syntax::AssignmentOp::Mod => { + state.write("%"); + } + syntax::AssignmentOp::Add => { + state.write("+"); + } + syntax::AssignmentOp::Sub => { + state.write("-"); + } + syntax::AssignmentOp::LShift => { + state.write("<<"); + } + syntax::AssignmentOp::RShift => { + state.write(">>"); + } + syntax::AssignmentOp::And => { + state.write("&"); + } + syntax::AssignmentOp::Xor => { + state.write("^"); + } + syntax::AssignmentOp::Or => { + state.write("|"); + } + } + if force_scalar { + state.write("force_scalar("); + } + show_hir_expr(state, &e); + if force_scalar { + state.write(")"); + } + if force_scalar || force_scalar_mask { + if top_level { + state.write("; }"); + } else { + state.write(" : "); + show_hir_expr(state, &v); + state.write(")"); + } + } else { + state.write(","); + show_hir_expr(state, &v); + state.write(")"); + } + } else { + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" "); + + if is_output && state.return_declared { + state.write("= "); + if force_scalar { + state.write("force_scalar("); + } + state.write("if_then_else(ret_mask,"); + + if op != &syntax::AssignmentOp::Equal { + show_hir_expr(state, &v); + } + + match *op { + syntax::AssignmentOp::Equal => {} + syntax::AssignmentOp::Mult => { + state.write("*"); + } + syntax::AssignmentOp::Div => { + state.write("/"); + } + syntax::AssignmentOp::Mod => { + state.write("%"); + } + syntax::AssignmentOp::Add => { + state.write("+"); + } + syntax::AssignmentOp::Sub => { + state.write("-"); + } + syntax::AssignmentOp::LShift => { + state.write("<<"); + } + syntax::AssignmentOp::RShift => { + state.write(">>"); + } + syntax::AssignmentOp::And => { + state.write("&"); + } + syntax::AssignmentOp::Xor => { + state.write("^"); + } + syntax::AssignmentOp::Or => { + state.write("|"); + } + } + show_hir_expr(state, &e); + state.write(","); + show_hir_expr(state, &v); + state.write(")"); + } else { + show_assignment_op(state, &op); + state.write(" "); + if force_scalar { + state.write("force_scalar("); + } + show_hir_expr(state, &e); + } + + if force_scalar { + state.write(")"); + } + } + } + hir::ExprKind::Bracket(ref e, ref indx) => { + show_hir_expr(state, &e); + state.write("["); + for dimension in indx { + show_hir_expr(state, dimension); + } + state.write("]"); + } + hir::ExprKind::FunCall(ref fun, ref args) => { + let mut cond_mask: u32 = 0; + let mut adapt_mask: u32 = 0; + let mut has_ret = false; + let mut array_constructor = false; + + let mut arg_mask: u32 = 0; + for (idx, e) in args.iter().enumerate() { + if expr_run_class(state, e) == hir::RunClass::Vector { + arg_mask |= 1 << idx; + } + } + + match fun { + hir::FunIdentifier::Constructor(t) => { + let is_scalar = state.is_scalar.replace(arg_mask == 0); + show_type(state, t); + state.is_scalar.set(is_scalar); + array_constructor = t.array_sizes.is_some(); + } + hir::FunIdentifier::Identifier(name) => { + if state.output_cxx { + let sym = state.hir.sym(*name); + match &sym.decl { + hir::SymDecl::NativeFunction(..) => { + if sym.name == "texelFetchOffset" && args.len() >= 4 { + if let Some((sampler, base, x, y)) = hir::get_texel_fetch_offset( + &state.hir, &args[0], &args[1], &args[3], + ) { + let base_sym = state.hir.sym(base); + let sampler_sym = state.hir.sym(sampler); + add_used_global(state, &sampler); + if let hir::SymDecl::Global(..) = &base_sym.decl { + add_used_global(state, &base); + } + write!( + state, + "texelFetchUnchecked({}, {}_{}_fetch, {}, {})", + sampler_sym.name, + sampler_sym.name, + base_sym.name, + x, + y, + ); + return; + } + } + show_sym(state, name) + } + hir::SymDecl::UserFunction(ref fd, ref _run_class) => { + if (state.mask.is_some() || state.return_declared) && + !fd.globals.is_empty() + { + cond_mask |= 1 << 31; + } + let mut param_mask: u32 = 0; + for (idx, (param, e)) in + fd.prototype.parameters.iter().zip(args.iter()).enumerate() + { + if let hir::FunctionParameterDeclaration::Named(qual, p) = param + { + if symbol_run_class(&state.hir.sym(p.sym).decl, arg_mask) + == hir::RunClass::Vector + { + param_mask |= 1 << idx; + } + match qual { + Some(hir::ParameterQualifier::InOut) + | Some(hir::ParameterQualifier::Out) => { + if state.mask.is_some() || state.return_declared { + cond_mask |= 1 << idx; + } + if (!arg_mask & param_mask & (1 << idx)) != 0 { + if adapt_mask == 0 { + state.write(if top_level { + "{ " + } else { + "({ " + }); + } + show_type(state, &p.ty); + write!(state, " _arg{}_ = ", idx); + show_hir_expr(state, e); + state.write("; "); + adapt_mask |= 1 << idx; + } + } + _ => {} + } + } + } + if adapt_mask != 0 && + fd.prototype.ty.kind != hir::TypeKind::Void && + !top_level + { + state.write("auto _ret_ = "); + has_ret = true; + } + show_sym(state, name); + let mut deps = state.deps.borrow_mut(); + let dep_key = ( + *name, + if cond_mask != 0 { + param_mask | (1 << 31) + } else { + param_mask + }, + ); + if !deps.contains(&dep_key) { + deps.push(dep_key); + } + } + hir::SymDecl::Struct(..) => { + show_sym(state, name); + if arg_mask == 0 { + state.write("_scalar"); + } + } + _ => panic!("bad identifier to function call"), + } + } + } + } + + if array_constructor { + state.write("{{"); + } else { + state.write("("); + } + + for (idx, e) in args.iter().enumerate() { + if idx != 0 { + state.write(", "); + } + if (adapt_mask & (1 << idx)) != 0 { + write!(state, "_arg{}_", idx); + } else { + show_hir_expr(state, e); + } + } + + if cond_mask != 0 { + if !args.is_empty() { + state.write(", "); + } + if let Some(mask) = &state.mask { + if state.return_declared { + state.write("("); + show_hir_expr(state, mask); + state.write(")&ret_mask"); + } else { + show_hir_expr(state, mask); + } + } else if state.return_declared { + state.write("ret_mask"); + } else { + state.write("~0"); + } + } + + if array_constructor { + state.write("}}"); + } else { + state.write(")"); + } + + if adapt_mask != 0 { + state.write("; "); + for (idx, e) in args.iter().enumerate() { + if (adapt_mask & (1 << idx)) != 0 { + state.is_lval.set(true); + show_hir_expr(state, e); + state.is_lval.set(false); + write!(state, " = force_scalar(_arg{}_); ", idx); + } + } + if has_ret { + state.write("_ret_; })"); + } else { + state.write(if top_level { "}" } else { "})" }); + } + } + } + hir::ExprKind::Dot(ref e, ref i) => { + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + state.write("."); + show_identifier(state, i); + } + hir::ExprKind::SwizzleSelector(ref e, ref s) => { + if state.output_cxx { + if let hir::ExprKind::Variable(ref sym) = &e.kind { + if state.hir.sym(*sym).name == "gl_FragCoord" { + state.used_fragcoord.set( + s.components.iter().fold( + state.used_fragcoord.get(), + |used, c| used | (1 << c))); + } + } + state.write("("); + show_hir_expr(state, &e); + state.write(")."); + if s.components.len() == 1 { + // For single component swizzles, output a field access to + // avoid stressing inlining of sel(). + state.write(&s.to_field_set(hir::FieldSet::Xyzw)); + } else { + if state.is_lval.get() && s.components.len() > 1 { + state.write("lsel("); + } else { + state.write("sel("); + } + for (i, c) in s.to_string().chars().enumerate() { + if i > 0 { + state.write(","); + } + write!(state, "{}", c.to_uppercase()); + } + state.write(")"); + } + } else { + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + state.write("."); + state.write(&s.to_string()); + } + } + hir::ExprKind::PostInc(ref e) => { + show_hir_expr(state, &e); + state.write("++"); + } + hir::ExprKind::PostDec(ref e) => { + show_hir_expr(state, &e); + state.write("--"); + } + hir::ExprKind::Comma(ref a, ref b) => { + show_hir_expr(state, &a); + state.write(", "); + show_hir_expr(state, &b); + } + hir::ExprKind::Cond(index, _) => { + write!(state, "_c{}_", index); + } + hir::ExprKind::CondMask => { + state.write("_cond_mask_"); + } + } +} + +pub fn show_expr(state: &OutputState, expr: &syntax::Expr) { + match *expr { + syntax::Expr::Variable(ref i) => show_identifier(state, &i), + syntax::Expr::IntConst(ref x) => { + let _ = write!(state, "{}", x); + } + syntax::Expr::UIntConst(ref x) => { + let _ = write!(state, "{}u", x); + } + syntax::Expr::BoolConst(ref x) => { + let _ = write!(state, "{}", x); + } + syntax::Expr::FloatConst(ref x) => show_float(state, *x), + syntax::Expr::DoubleConst(ref x) => show_double(state, *x), + syntax::Expr::Unary(ref op, ref e) => { + show_unary_op(state, &op); + state.write("("); + show_expr(state, &e); + state.write(")"); + } + syntax::Expr::Binary(ref op, ref l, ref r) => { + state.write("("); + show_expr(state, &l); + state.write(")"); + show_binary_op(state, &op); + state.write("("); + show_expr(state, &r); + state.write(")"); + } + syntax::Expr::Ternary(ref c, ref s, ref e) => { + show_expr(state, &c); + state.write(" ? "); + show_expr(state, &s); + state.write(" : "); + show_expr(state, &e); + } + syntax::Expr::Assignment(ref v, ref op, ref e) => { + show_expr(state, &v); + state.write(" "); + show_assignment_op(state, &op); + state.write(" "); + show_expr(state, &e); + } + syntax::Expr::Bracket(ref e, ref a) => { + show_expr(state, &e); + show_array_spec(state, &a); + } + syntax::Expr::FunCall(ref fun, ref args) => { + show_function_identifier(state, &fun); + state.write("("); + + if !args.is_empty() { + let mut args_iter = args.iter(); + let first = args_iter.next().unwrap(); + show_expr(state, first); + + for e in args_iter { + state.write(", "); + show_expr(state, e); + } + } + + state.write(")"); + } + syntax::Expr::Dot(ref e, ref i) => { + state.write("("); + show_expr(state, &e); + state.write(")"); + state.write("."); + show_identifier(state, &i); + } + syntax::Expr::PostInc(ref e) => { + show_expr(state, &e); + state.write("++"); + } + syntax::Expr::PostDec(ref e) => { + show_expr(state, &e); + state.write("--"); + } + syntax::Expr::Comma(ref a, ref b) => { + show_expr(state, &a); + state.write(", "); + show_expr(state, &b); + } + } +} + +pub fn show_unary_op(state: &OutputState, op: &syntax::UnaryOp) { + match *op { + syntax::UnaryOp::Inc => { + state.write("++"); + } + syntax::UnaryOp::Dec => { + state.write("--"); + } + syntax::UnaryOp::Add => { + state.write("+"); + } + syntax::UnaryOp::Minus => { + state.write("-"); + } + syntax::UnaryOp::Not => { + state.write("!"); + } + syntax::UnaryOp::Complement => { + state.write("~"); + } + } +} + +pub fn show_binary_op(state: &OutputState, op: &syntax::BinaryOp) { + match *op { + syntax::BinaryOp::Or => { + state.write("||"); + } + syntax::BinaryOp::Xor => { + state.write("^^"); + } + syntax::BinaryOp::And => { + state.write("&&"); + } + syntax::BinaryOp::BitOr => { + state.write("|"); + } + syntax::BinaryOp::BitXor => { + state.write("^"); + } + syntax::BinaryOp::BitAnd => { + state.write("&"); + } + syntax::BinaryOp::Equal => { + state.write("=="); + } + syntax::BinaryOp::NonEqual => { + state.write("!="); + } + syntax::BinaryOp::LT => { + state.write("<"); + } + syntax::BinaryOp::GT => { + state.write(">"); + } + syntax::BinaryOp::LTE => { + state.write("<="); + } + syntax::BinaryOp::GTE => { + state.write(">="); + } + syntax::BinaryOp::LShift => { + state.write("<<"); + } + syntax::BinaryOp::RShift => { + state.write(">>"); + } + syntax::BinaryOp::Add => { + state.write("+"); + } + syntax::BinaryOp::Sub => { + state.write("-"); + } + syntax::BinaryOp::Mult => { + state.write("*"); + } + syntax::BinaryOp::Div => { + state.write("/"); + } + syntax::BinaryOp::Mod => { + state.write("%"); + } + } +} + +pub fn show_assignment_op(state: &OutputState, op: &syntax::AssignmentOp) { + match *op { + syntax::AssignmentOp::Equal => { + state.write("="); + } + syntax::AssignmentOp::Mult => { + state.write("*="); + } + syntax::AssignmentOp::Div => { + state.write("/="); + } + syntax::AssignmentOp::Mod => { + state.write("%="); + } + syntax::AssignmentOp::Add => { + state.write("+="); + } + syntax::AssignmentOp::Sub => { + state.write("-="); + } + syntax::AssignmentOp::LShift => { + state.write("<<="); + } + syntax::AssignmentOp::RShift => { + state.write(">>="); + } + syntax::AssignmentOp::And => { + state.write("&="); + } + syntax::AssignmentOp::Xor => { + state.write("^="); + } + syntax::AssignmentOp::Or => { + state.write("|="); + } + } +} + +pub fn show_function_identifier(state: &OutputState, i: &syntax::FunIdentifier) { + match *i { + syntax::FunIdentifier::Identifier(ref n) => show_identifier(state, &n), + syntax::FunIdentifier::Expr(ref e) => show_expr(state, &*e), + } +} + +pub fn show_hir_function_identifier(state: &OutputState, i: &hir::FunIdentifier) { + match *i { + hir::FunIdentifier::Identifier(ref n) => show_sym(state, n), + hir::FunIdentifier::Constructor(ref t) => show_type(state, &*t), + } +} + +pub fn show_declaration(state: &mut OutputState, d: &hir::Declaration) { + show_indent(state); + match *d { + hir::Declaration::FunctionPrototype(ref proto) => { + if !state.output_cxx { + show_function_prototype(state, &proto); + state.write(";\n"); + } + } + hir::Declaration::InitDeclaratorList(ref list) => { + show_init_declarator_list(state, &list); + state.write(";\n"); + + if state.output_cxx { + let base = list.head.name; + let base_sym = state.hir.sym(base); + if let hir::SymDecl::Local(..) = &base_sym.decl { + let mut texel_fetches = state.texel_fetches.borrow_mut(); + while let Some(idx) = texel_fetches.iter().position(|&(_, b, _)| b == base) + { + let (sampler, _, offsets) = texel_fetches.remove(idx); + let sampler_sym = state.hir.sym(sampler); + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } + } + } + } + hir::Declaration::Precision(ref qual, ref ty) => { + if !state.output_cxx { + show_precision_qualifier(state, &qual); + show_type_specifier(state, &ty); + state.write(";\n"); + } + } + hir::Declaration::Block(ref _block) => { + panic!(); + //show_block(state, &block); + //state.write(";\n"); + } + hir::Declaration::Global(ref qual, ref identifiers) => { + // We only want to output GLSL layout qualifiers if not C++ + if !state.output_cxx { + show_type_qualifier(state, &qual); + + if !identifiers.is_empty() { + let mut iter = identifiers.iter(); + let first = iter.next().unwrap(); + show_identifier(state, first); + + for identifier in iter { + let _ = write!(state, ", {}", identifier); + } + } + + state.write(";\n"); + } + } + hir::Declaration::StructDefinition(ref sym) => { + show_sym_decl(state, sym); + + state.write(";\n"); + } + } +} + +pub fn show_function_prototype(state: &mut OutputState, fp: &hir::FunctionPrototype) { + let is_scalar = state.is_scalar.replace(!state.return_vector); + show_type(state, &fp.ty); + state.is_scalar.set(is_scalar); + + state.write(" "); + show_identifier(state, &fp.name); + + state.write("("); + + if !fp.parameters.is_empty() { + let mut iter = fp.parameters.iter(); + let first = iter.next().unwrap(); + show_function_parameter_declaration(state, first); + + for param in iter { + state.write(", "); + show_function_parameter_declaration(state, param); + } + } + + if state.output_cxx && (state.vector_mask & (1 << 31)) != 0 { + if !fp.parameters.is_empty() { + state.write(", "); + } + state.write("I32 _cond_mask_"); + } + + state.write(")"); +} + +pub fn show_function_parameter_declaration( + state: &mut OutputState, + p: &hir::FunctionParameterDeclaration, +) { + match *p { + hir::FunctionParameterDeclaration::Named(ref qual, ref fpd) => { + if state.output_cxx { + let is_scalar = state.is_scalar.replace( + symbol_run_class(&state.hir.sym(fpd.sym).decl, state.vector_mask) + == hir::RunClass::Scalar, + ); + show_type(state, &fpd.ty); + state.is_scalar.set(is_scalar); + show_parameter_qualifier(state, qual); + } else { + show_parameter_qualifier(state, qual); + state.write(" "); + show_type(state, &fpd.ty); + } + state.write(" "); + show_identifier_and_type(state, &fpd.name, &fpd.ty); + } + hir::FunctionParameterDeclaration::Unnamed(ref qual, ref ty) => { + if state.output_cxx { + show_type_specifier(state, ty); + show_parameter_qualifier(state, qual); + } else { + show_parameter_qualifier(state, qual); + state.write(" "); + show_type_specifier(state, ty); + } + } + } +} + +pub fn show_init_declarator_list(state: &mut OutputState, i: &hir::InitDeclaratorList) { + show_single_declaration(state, &i.head); + + for decl in &i.tail { + state.write(", "); + show_single_declaration_no_type(state, decl); + } +} + +pub fn show_single_declaration(state: &mut OutputState, d: &hir::SingleDeclaration) { + if state.output_cxx { + show_single_declaration_cxx(state, d) + } else { + show_single_declaration_glsl(state, d) + } +} + +pub fn show_single_declaration_glsl(state: &mut OutputState, d: &hir::SingleDeclaration) { + if let Some(ref qual) = d.qualifier { + show_type_qualifier(state, &qual); + state.write(" "); + } + + let sym = state.hir.sym(d.name); + match &sym.decl { + hir::SymDecl::Global(storage, interpolation, ..) => { + show_storage_class(state, storage); + if let Some(i) = interpolation { + show_interpolation_qualifier(state, i); + } + } + hir::SymDecl::Local(storage, ..) => show_storage_class(state, storage), + _ => panic!("should be variable"), + } + + if let Some(ty_def) = d.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.ty); + } + + state.write(" "); + state.write(sym.name.as_str()); + + if let Some(ref arr_spec) = d.ty.array_sizes { + show_array_sizes(state, &arr_spec); + } + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +fn symbol_run_class(decl: &hir::SymDecl, vector_mask: u32) -> hir::RunClass { + let run_class = match decl { + hir::SymDecl::Global(_, _, _, run_class) => *run_class, + hir::SymDecl::Local(_, _, run_class) => *run_class, + _ => hir::RunClass::Vector, + }; + match run_class { + hir::RunClass::Scalar => hir::RunClass::Scalar, + hir::RunClass::Dependent(mask) => { + if (mask & vector_mask) != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => hir::RunClass::Vector, + } +} + +pub fn show_single_declaration_cxx(state: &mut OutputState, d: &hir::SingleDeclaration) { + let sym = state.hir.sym(d.name); + if state.kind == ShaderKind::Vertex { + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Uniform, ..) | + hir::SymDecl::Global(hir::StorageClass::Sampler(_), ..) | + hir::SymDecl::Global(hir::StorageClass::Out, _, _, hir::RunClass::Scalar) => { + state.write("// "); + } + _ => {} + } + } else { + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::FragColor(index), ..) => { + let fragcolor = match index { + 0 => "gl_FragColor", + 1 => "gl_SecondaryFragColor", + _ => panic!(), + }; + write!(state, "#define {} {}\n", sym.name, fragcolor); + show_indent(state); + state.write("// "); + } + hir::SymDecl::Global(hir::StorageClass::Out, ..) => { + write!(state, "#define {} gl_FragColor\n", sym.name); + show_indent(state); + state.write("// "); + } + hir::SymDecl::Global(hir::StorageClass::Uniform, ..) | + hir::SymDecl::Global(hir::StorageClass::Sampler(_), ..) | + hir::SymDecl::Global(hir::StorageClass::In, _, _, hir::RunClass::Scalar) => { + state.write("// "); + } + _ => {} + } + } + let is_scalar = state + .is_scalar + .replace(symbol_run_class(&sym.decl, state.vector_mask) == hir::RunClass::Scalar); + + if let Some(ref _array) = d.ty.array_sizes { + show_type(state, &d.ty); + } else { + if let Some(ty_def) = d.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.ty); + } + } + + // XXX: this is pretty grotty + state.write(" "); + show_sym_decl(state, &d.name); + + state.is_scalar.set(is_scalar); + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +pub fn show_single_declaration_no_type(state: &OutputState, d: &hir::SingleDeclarationNoType) { + show_arrayed_identifier(state, &d.ident); + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +pub fn show_initializer(state: &OutputState, i: &hir::Initializer) { + match *i { + hir::Initializer::Simple(ref e) => show_hir_expr(state, e), + hir::Initializer::List(ref list) => { + let mut iter = list.0.iter(); + let first = iter.next().unwrap(); + + state.write("{ "); + show_initializer(state, first); + + for ini in iter { + state.write(", "); + show_initializer(state, ini); + } + + state.write(" }"); + } + } +} + +/* +pub fn show_block(state: &mut OutputState, b: &hir::Block) { + show_type_qualifier(state, &b.qualifier); + state.write(" "); + show_identifier(state, &b.name); + state.write(" {"); + + for field in &b.fields { + show_struct_field(state, field); + state.write("\n"); + } + state.write("}"); + + if let Some(ref ident) = b.identifier { + show_arrayed_identifier(state, ident); + } +} +*/ + +// This is a hack to run through the first time with an empty writter to find if 'return' is declared. +pub fn has_conditional_return(state: &mut OutputState, cst: &hir::CompoundStatement) -> bool { + let buffer = state.push_buffer(); + show_compound_statement(state, cst); + state.pop_buffer(buffer); + let result = state.return_declared; + state.return_declared = false; + result +} + +fn define_texel_fetch_ptr( + state: &OutputState, + base_sym: &hir::Symbol, + sampler_sym: &hir::Symbol, + offsets: &hir::TexelFetchOffsets, +) { + show_indent(state); + write!( + state, + "auto {}_{}_fetch = texelFetchPtr({}, {}, {}, {}, {}, {});\n", + sampler_sym.name, + base_sym.name, + sampler_sym.name, + base_sym.name, + offsets.min_x, + offsets.max_x, + offsets.min_y, + offsets.max_y, + ); +} + +pub fn show_function_definition( + state: &mut OutputState, + fd: &hir::FunctionDefinition, + vector_mask: u32, +) { + // println!("start {:?} {:?}", fd.prototype.name, vector_mask); + if state.output_cxx && fd.prototype.name.as_str() == "main" { + state.write("ALWAYS_INLINE "); + } + show_function_prototype(state, &fd.prototype); + state.write(" "); + state.return_type = Some(Box::new(fd.prototype.ty.clone())); + + if state.output_cxx && (vector_mask & (1 << 31)) != 0 { + state.mask = Some(Box::new(hir::Expr { + kind: hir::ExprKind::CondMask, + ty: hir::Type::new(hir::TypeKind::Bool), + })); + } + + show_indent(state); + state.write("{\n"); + + state.indent(); + if has_conditional_return(state, &fd.body) { + show_indent(state); + state.write(if state.return_vector { + "I32" + } else { + "int32_t" + }); + state.write(" ret_mask = "); + if let Some(mask) = &state.mask { + show_hir_expr(state, mask); + } else { + state.write("~0"); + } + state.write(";\n"); + // XXX: the cloning here is bad + show_indent(state); + if fd.prototype.ty != Type::new(hir::TypeKind::Void) { + let is_scalar = state.is_scalar.replace(!state.return_vector); + show_type(state, &state.return_type.clone().unwrap()); + state.write(" ret;\n"); + state.is_scalar.set(is_scalar); + } + } + + if state.output_cxx { + match fd.prototype.name.as_str() { + "swgl_drawSpanRGBA8" | + "swgl_drawSpanR8" => { + // Partial spans are not drawn using span shaders, but rather drawn with a fragment shader + // where the span shader left off. We need to undo any changes to the interpolants made by + // the span shaders so that we can reset the interpolants to where the fragment shader + // expects them. We do this by saving them in an _Undo_ struct on entry to the span shader, + // and then restore them in the _Undo_ struct destructor. + let mut needs_undo = vec![]; + for global in &fd.globals { + let sym = state.hir.sym(*global); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::In, _, ty, hir::RunClass::Vector) => { + if needs_undo.is_empty() { + state.write("struct _Undo_ {\nSelf* self;\n"); + } + show_type(state, ty); + write!(state, " {};\n", sym.name); + needs_undo.push(sym.name.clone()); + } + _ => {} + } + } + if !needs_undo.is_empty() { + state.write("explicit _Undo_(Self* self) : self(self)"); + for name in &needs_undo { + write!(state, ", {0}(self->{0})", name); + } + state.write(" {}\n"); + state.write("~_Undo_() {\n"); + for name in &needs_undo { + write!(state, "self->{0} = {0};\n", name); + } + state.write("}} _undo_(this);\n"); + } + } + _ => {} + } + + let mut texel_fetches = state.texel_fetches.borrow_mut(); + texel_fetches.clear(); + for ((sampler, base), offsets) in fd.texel_fetches.iter() { + add_used_global(state, sampler); + let sampler_sym = state.hir.sym(*sampler); + let base_sym = state.hir.sym(*base); + match &base_sym.decl { + hir::SymDecl::Global(..) => { + add_used_global(state, base); + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } + hir::SymDecl::Local(..) => { + if fd.prototype.has_parameter(*base) { + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } else { + texel_fetches.push((*sampler, *base, offsets.clone())); + } + } + _ => panic!(), + } + } + } + + for st in &fd.body.statement_list { + show_statement(state, st); + } + + if state.return_declared { + show_indent(state); + if fd.prototype.ty == Type::new(hir::TypeKind::Void) { + state.write("return;\n"); + } else { + state.write("return ret;\n"); + } + } + state.outdent(); + + show_indent(state); + state.write("}\n"); + // println!("end {:?}", fd.prototype.name); + + state.return_type = None; + state.return_declared = false; + state.mask = None; +} + +pub fn show_compound_statement(state: &mut OutputState, cst: &hir::CompoundStatement) { + show_indent(state); + state.write("{\n"); + + state.indent(); + for st in &cst.statement_list { + show_statement(state, st); + } + state.outdent(); + + show_indent(state); + state.write("}\n"); +} + +pub fn show_statement(state: &mut OutputState, st: &hir::Statement) { + match *st { + hir::Statement::Compound(ref cst) => show_compound_statement(state, cst), + hir::Statement::Simple(ref sst) => show_simple_statement(state, sst), + } +} + +pub fn show_simple_statement(state: &mut OutputState, sst: &hir::SimpleStatement) { + match *sst { + hir::SimpleStatement::Declaration(ref d) => show_declaration(state, d), + hir::SimpleStatement::Expression(ref e) => show_expression_statement(state, e), + hir::SimpleStatement::Selection(ref s) => show_selection_statement(state, s), + hir::SimpleStatement::Switch(ref s) => show_switch_statement(state, s), + hir::SimpleStatement::Iteration(ref i) => show_iteration_statement(state, i), + hir::SimpleStatement::Jump(ref j) => show_jump_statement(state, j), + } +} + +pub fn show_indent(state: &OutputState) { + for _ in 0 .. state.indent { + state.write(" "); + } +} + +pub fn show_expression_statement(state: &mut OutputState, est: &hir::ExprStatement) { + show_indent(state); + + if let Some(ref e) = *est { + show_hir_expr_inner(state, e, true); + } + + state.write(";\n"); +} + +pub fn show_selection_statement(state: &mut OutputState, sst: &hir::SelectionStatement) { + show_indent(state); + + if state.output_cxx && + (state.return_declared || expr_run_class(state, &sst.cond) != hir::RunClass::Scalar) + { + let (cond_index, mask) = if state.mask.is_none() || sst.else_stmt.is_some() { + let cond = sst.cond.clone(); + state.cond_index += 1; + let cond_index = state.cond_index; + write!(state, "auto _c{}_ = ", cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + ( + cond_index, + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }), + ) + } else { + (0, sst.cond.clone()) + }; + + let previous = mem::replace(&mut state.mask, None); + state.mask = Some(match previous.clone() { + Some(e) => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::BitAnd, e, mask.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + state.cond_index += 1; + let nested_cond_index = state.cond_index; + show_indent(state); + write!(state, "auto _c{}_ = ", nested_cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(nested_cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }) + } + None => mask.clone(), + }); + + show_statement(state, &sst.body); + state.mask = previous; + + if let Some(rest) = &sst.else_stmt { + // invert the condition + let inverted_cond = Box::new(hir::Expr { + kind: hir::ExprKind::Unary(UnaryOp::Complement, mask), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + let previous = mem::replace(&mut state.mask, None); + state.mask = Some(match previous.clone() { + Some(e) => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::BitAnd, e, inverted_cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + show_indent(state); + write!(state, "_c{}_ = ", cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }) + } + None => inverted_cond, + }); + + show_statement(state, rest); + state.mask = previous; + } + } else { + state.write("if ("); + show_hir_expr(state, &sst.cond); + state.write(") {\n"); + + state.indent(); + show_statement(state, &sst.body); + state.outdent(); + + show_indent(state); + if let Some(rest) = &sst.else_stmt { + state.write("} else "); + show_statement(state, rest); + } else { + state.write("}\n"); + } + } +} + +fn case_stmts_to_if_stmts(stmts: &[Statement], last: bool) -> (Option<Box<Statement>>, bool) { + // Look for jump statements and remove them + // We currently are pretty strict on the form that the statement + // list needs to be in. This can be loosened as needed. + let mut fallthrough = false; + let cstmt = match &stmts[..] { + [hir::Statement::Compound(c)] => match c.statement_list.split_last() { + Some((hir::Statement::Simple(s), rest)) => match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: rest.to_owned(), + }, + _ => panic!("fall through not supported"), + }, + _ => panic!("empty compound"), + }, + [hir::Statement::Simple(s)] => { + match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: Vec::new(), + }, + _ => { + if last { + // we don't need a break at the end + hir::CompoundStatement { + statement_list: vec![hir::Statement::Simple(s.clone())], + } + } else { + panic!("fall through not supported {:?}", s) + } + } + } + } + [] => return (None, true), + stmts => match stmts.split_last() { + Some((hir::Statement::Simple(s), rest)) => match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: rest.to_owned(), + }, + _ => { + if !last { + fallthrough = true; + } + hir::CompoundStatement { + statement_list: stmts.to_owned(), + } + } + }, + _ => panic!("unexpected empty"), + }, + }; + let stmts = Box::new(hir::Statement::Compound(Box::new(cstmt))); + (Some(stmts), fallthrough) +} + +fn build_selection<'a, I: Iterator<Item = &'a hir::Case>>( + head: &Box<hir::Expr>, + case: &hir::Case, + mut cases: I, + default: Option<&hir::Case>, + previous_condition: Option<Box<hir::Expr>>, + previous_stmts: Option<Box<hir::Statement>>, +) -> hir::SelectionStatement { + let cond = match &case.label { + hir::CaseLabel::Case(e) => Some(Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::Equal, head.clone(), e.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + })), + hir::CaseLabel::Def => None, + }; + + // if we have two conditions join them + let cond = match (&previous_condition, &cond) { + (Some(prev), Some(cond)) => Some(Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::Or, prev.clone(), cond.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + })), + (_, cond) => cond.clone(), + }; + + /* + + // find the next case that's not a default + let next_case = loop { + match cases.next() { + Some(hir::Case { label: hir::CaseLabel::Def, ..}) => { }, + case => break case, + } + };*/ + + let (cond, body, else_stmt) = match (cond, cases.next()) { + (None, Some(next_case)) => { + assert!(previous_stmts.is_none()); + // default so just move on to the next + return build_selection(head, next_case, cases, default, None, None); + } + (Some(cond), Some(next_case)) => { + assert!(previous_stmts.is_none()); + let (stmts, fallthrough) = case_stmts_to_if_stmts(&case.stmts, false); + if !fallthrough && stmts.is_some() { + ( + cond, + stmts.unwrap(), + Some(Box::new(hir::Statement::Simple(Box::new( + hir::SimpleStatement::Selection(build_selection( + head, next_case, cases, default, None, None, + )), + )))), + ) + } else { + // empty so fall through to the next + return build_selection(head, next_case, cases, default, Some(cond), stmts); + } + } + (Some(cond), None) => { + // non-default last + assert!(previous_stmts.is_none()); + let (stmts, _) = case_stmts_to_if_stmts(&case.stmts, default.is_none()); + let stmts = stmts.expect("empty case labels unsupported at the end"); + // add the default case at the end if we have one + ( + cond, + stmts, + match default { + Some(default) => { + let (default_stmts, fallthrough) = + case_stmts_to_if_stmts(&default.stmts, true); + assert!(!fallthrough); + Some(default_stmts.expect("empty default unsupported")) + } + None => None, + }, + ) + } + (None, None) => { + // default, last + + assert!(default.is_some()); + + let (stmts, fallthrough) = case_stmts_to_if_stmts(&case.stmts, true); + let stmts = stmts.expect("empty default unsupported"); + assert!(!fallthrough); + + match previous_stmts { + Some(previous_stmts) => { + let cond = previous_condition.expect("must have previous condition"); + (cond, previous_stmts, Some(stmts)) + } + None => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::BoolConst(true), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + (cond, stmts, None) + } + } + } + }; + + hir::SelectionStatement { + cond, + body, + else_stmt, + } +} + +pub fn lower_switch_to_ifs(sst: &hir::SwitchStatement) -> hir::SelectionStatement { + let default = sst.cases.iter().find(|x| x.label == hir::CaseLabel::Def); + let mut cases = sst.cases.iter(); + let r = build_selection(&sst.head, cases.next().unwrap(), cases, default, None, None); + r +} + +fn is_declaration(stmt: &hir::Statement) -> bool { + if let hir::Statement::Simple(s) = stmt { + if let hir::SimpleStatement::Declaration(..) = **s { + return true; + } + } + return false; +} + +pub fn show_switch_statement(state: &mut OutputState, sst: &hir::SwitchStatement) { + if state.output_cxx && expr_run_class(state, &sst.head) != hir::RunClass::Scalar { + // XXX: when lowering switches we end up with a mask that has + // a bunch of mutually exclusive conditions. + // It would be nice if we could fold them together. + let ifs = lower_switch_to_ifs(sst); + return show_selection_statement(state, &ifs); + } + + show_indent(state); + state.write("switch ("); + show_hir_expr(state, &sst.head); + state.write(") {\n"); + state.indent(); + + for case in &sst.cases { + show_case_label(state, &case.label); + state.indent(); + + let has_declaration = case.stmts.iter().any(|x| is_declaration(x)); + // glsl allows declarations in switch statements while C requires them to be + // in a compound statement. If we have a declaration wrap the statements in an block. + // This will break some glsl shaders but keeps the saner ones working + if has_declaration { + show_indent(state); + state.write("{\n"); + state.indent(); + } + for st in &case.stmts { + show_statement(state, st); + } + + if has_declaration { + show_indent(state); + state.write("}\n"); + state.outdent(); + } + + state.outdent(); + } + state.outdent(); + show_indent(state); + state.write("}\n"); +} + +pub fn show_case_label(state: &mut OutputState, cl: &hir::CaseLabel) { + show_indent(state); + match *cl { + hir::CaseLabel::Case(ref e) => { + state.write("case "); + show_hir_expr(state, e); + state.write(":\n"); + } + hir::CaseLabel::Def => { + state.write("default:\n"); + } + } +} + +pub fn show_iteration_statement(state: &mut OutputState, ist: &hir::IterationStatement) { + show_indent(state); + match *ist { + hir::IterationStatement::While(ref cond, ref body) => { + state.write("while ("); + show_condition(state, cond); + state.write(") "); + show_statement(state, body); + } + hir::IterationStatement::DoWhile(ref body, ref cond) => { + state.write("do "); + show_statement(state, body); + state.write(" while ("); + show_hir_expr(state, cond); + state.write(");\n"); + } + hir::IterationStatement::For(ref init, ref rest, ref body) => { + state.write("for ("); + show_for_init_statement(state, init); + show_for_rest_statement(state, rest); + state.write(") "); + show_statement(state, body); + } + } +} + +pub fn show_condition(state: &mut OutputState, c: &hir::Condition) { + match *c { + hir::Condition::Expr(ref e) => show_hir_expr(state, e), + /*hir::Condition::Assignment(ref ty, ref name, ref initializer) => { + show_type(state, ty); + state.write(" "); + show_identifier(f, name); + state.write(" = "); + show_initializer(state, initializer); + }*/ + } +} + +pub fn show_for_init_statement(state: &mut OutputState, i: &hir::ForInitStatement) { + match *i { + hir::ForInitStatement::Expression(ref expr) => { + if let Some(ref e) = *expr { + show_hir_expr(state, e); + } + } + hir::ForInitStatement::Declaration(ref d) => { + show_declaration(state, d); + } + } +} + +pub fn show_for_rest_statement(state: &mut OutputState, r: &hir::ForRestStatement) { + if let Some(ref cond) = r.condition { + show_condition(state, cond); + } + + state.write("; "); + + if let Some(ref e) = r.post_expr { + show_hir_expr(state, e); + } +} + +fn use_return_mask(state: &OutputState) -> bool { + if let Some(mask) = &state.mask { + mask.kind != hir::ExprKind::CondMask + } else { + false + } +} + +pub fn show_jump_statement(state: &mut OutputState, j: &hir::JumpStatement) { + show_indent(state); + match *j { + hir::JumpStatement::Continue => { + state.write("continue;\n"); + } + hir::JumpStatement::Break => { + state.write("break;\n"); + } + hir::JumpStatement::Discard => { + if state.output_cxx { + state.uses_discard = true; + if let Some(mask) = &state.mask { + state.write("swgl_IsPixelDiscarded |= ("); + show_hir_expr(state, mask); + state.write(")"); + if state.return_declared { + state.write("&ret_mask"); + } + state.write(";\n"); + } else { + state.write("swgl_IsPixelDiscarded = true;\n"); + } + } else { + state.write("discard;\n"); + } + } + hir::JumpStatement::Return(ref e) => { + if let Some(e) = e { + if state.output_cxx { + if use_return_mask(state) { + // We cast any conditions by `ret_mask_type` so that scalars nicely + // convert to -1. i.e. I32 &= bool will give the wrong result. while I32 &= I32(bool) works + let ret_mask_type = if state.return_vector { + "I32" + } else { + "int32_t" + }; + if state.return_declared { + // XXX: the cloning here is bad + write!(state, "ret = if_then_else(ret_mask & {}(", ret_mask_type); + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write("), "); + show_hir_expr(state, e); + state.write(", ret);\n"); + } else { + state.write("ret = "); + show_hir_expr(state, e); + state.write(";\n"); + } + + show_indent(state); + + if state.return_declared { + write!(state, "ret_mask &= ~{}(", ret_mask_type); + } else { + write!(state, "ret_mask = ~{}(", ret_mask_type); + } + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write(");\n"); + state.return_declared = true; + } else { + if state.return_declared { + state.write("ret = if_then_else(ret_mask, "); + show_hir_expr(state, e); + state.write(", ret);\n"); + } else { + state.write("return "); + show_hir_expr(state, e); + state.write(";\n"); + } + } + } else { + state.write("return "); + show_hir_expr(state, e); + state.write(";\n"); + } + } else { + if state.output_cxx { + if use_return_mask(state) { + show_indent(state); + let ret_mask_type = if state.return_vector { + "I32" + } else { + "int32_t" + }; + if state.return_declared { + write!(state, "ret_mask &= ~{}(", ret_mask_type); + } else { + write!(state, "ret_mask = ~{}(", ret_mask_type); + } + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write(");\n"); + state.return_declared = true; + } else { + state.write("return;\n"); + } + } else { + state.write("return;\n"); + } + } + } + } +} + +pub fn show_path(state: &OutputState, path: &syntax::Path) { + match path { + syntax::Path::Absolute(s) => { + let _ = write!(state, "<{}>", s); + } + syntax::Path::Relative(s) => { + let _ = write!(state, "\"{}\"", s); + } + } +} + +pub fn show_preprocessor(state: &OutputState, pp: &syntax::Preprocessor) { + match *pp { + syntax::Preprocessor::Define(ref pd) => show_preprocessor_define(state, pd), + syntax::Preprocessor::Else => show_preprocessor_else(state), + syntax::Preprocessor::ElseIf(ref pei) => show_preprocessor_elseif(state, pei), + syntax::Preprocessor::EndIf => show_preprocessor_endif(state), + syntax::Preprocessor::Error(ref pe) => show_preprocessor_error(state, pe), + syntax::Preprocessor::If(ref pi) => show_preprocessor_if(state, pi), + syntax::Preprocessor::IfDef(ref pid) => show_preprocessor_ifdef(state, pid), + syntax::Preprocessor::IfNDef(ref pind) => show_preprocessor_ifndef(state, pind), + syntax::Preprocessor::Include(ref pi) => show_preprocessor_include(state, pi), + syntax::Preprocessor::Line(ref pl) => show_preprocessor_line(state, pl), + syntax::Preprocessor::Pragma(ref pp) => show_preprocessor_pragma(state, pp), + syntax::Preprocessor::Undef(ref pu) => show_preprocessor_undef(state, pu), + syntax::Preprocessor::Version(ref pv) => show_preprocessor_version(state, pv), + syntax::Preprocessor::Extension(ref pe) => show_preprocessor_extension(state, pe), + } +} + +pub fn show_preprocessor_define(state: &OutputState, pd: &syntax::PreprocessorDefine) { + match *pd { + syntax::PreprocessorDefine::ObjectLike { + ref ident, + ref value, + } => { + let _ = write!(state, "#define {} {}\n", ident, value); + } + + syntax::PreprocessorDefine::FunctionLike { + ref ident, + ref args, + ref value, + } => { + let _ = write!(state, "#define {}(", ident); + + if !args.is_empty() { + let _ = write!(state, "{}", &args[0]); + + for arg in &args[1 .. args.len()] { + let _ = write!(state, ", {}", arg); + } + } + + let _ = write!(state, ") {}\n", value); + } + } +} + +pub fn show_preprocessor_else(state: &OutputState) { + state.write("#else\n"); +} + +pub fn show_preprocessor_elseif(state: &OutputState, pei: &syntax::PreprocessorElseIf) { + let _ = write!(state, "#elseif {}\n", pei.condition); +} + +pub fn show_preprocessor_error(state: &OutputState, pe: &syntax::PreprocessorError) { + let _ = writeln!(state, "#error {}", pe.message); +} + +pub fn show_preprocessor_endif(state: &OutputState) { + state.write("#endif\n"); +} + +pub fn show_preprocessor_if(state: &OutputState, pi: &syntax::PreprocessorIf) { + let _ = write!(state, "#if {}\n", pi.condition); +} + +pub fn show_preprocessor_ifdef(state: &OutputState, pid: &syntax::PreprocessorIfDef) { + state.write("#ifdef "); + show_identifier(state, &pid.ident); + state.write("\n"); +} + +pub fn show_preprocessor_ifndef(state: &OutputState, pind: &syntax::PreprocessorIfNDef) { + state.write("#ifndef "); + show_identifier(state, &pind.ident); + state.write("\n"); +} + +pub fn show_preprocessor_include(state: &OutputState, pi: &syntax::PreprocessorInclude) { + state.write("#include "); + show_path(state, &pi.path); + state.write("\n"); +} + +pub fn show_preprocessor_line(state: &OutputState, pl: &syntax::PreprocessorLine) { + let _ = write!(state, "#line {}", pl.line); + if let Some(source_string_number) = pl.source_string_number { + let _ = write!(state, " {}", source_string_number); + } + state.write("\n"); +} + +pub fn show_preprocessor_pragma(state: &OutputState, pp: &syntax::PreprocessorPragma) { + let _ = writeln!(state, "#pragma {}", pp.command); +} + +pub fn show_preprocessor_undef(state: &OutputState, pud: &syntax::PreprocessorUndef) { + state.write("#undef "); + show_identifier(state, &pud.name); + state.write("\n"); +} + +pub fn show_preprocessor_version(state: &OutputState, pv: &syntax::PreprocessorVersion) { + let _ = write!(state, "#version {}", pv.version); + + if let Some(ref profile) = pv.profile { + match *profile { + syntax::PreprocessorVersionProfile::Core => { + state.write(" core"); + } + syntax::PreprocessorVersionProfile::Compatibility => { + state.write(" compatibility"); + } + syntax::PreprocessorVersionProfile::ES => { + state.write(" es"); + } + } + } + + state.write("\n"); +} + +pub fn show_preprocessor_extension(state: &OutputState, pe: &syntax::PreprocessorExtension) { + state.write("#extension "); + + match pe.name { + syntax::PreprocessorExtensionName::All => { + state.write("all"); + } + syntax::PreprocessorExtensionName::Specific(ref n) => { + state.write(n); + } + } + + if let Some(ref behavior) = pe.behavior { + match *behavior { + syntax::PreprocessorExtensionBehavior::Require => { + state.write(" : require"); + } + syntax::PreprocessorExtensionBehavior::Enable => { + state.write(" : enable"); + } + syntax::PreprocessorExtensionBehavior::Warn => { + state.write(" : warn"); + } + syntax::PreprocessorExtensionBehavior::Disable => { + state.write(" : disable"); + } + } + } + + state.write("\n"); +} + +pub fn show_external_declaration(state: &mut OutputState, ed: &hir::ExternalDeclaration) { + match *ed { + hir::ExternalDeclaration::Preprocessor(ref pp) => { + if !state.output_cxx { + show_preprocessor(state, pp) + } + } + hir::ExternalDeclaration::FunctionDefinition(ref fd) => { + if !state.output_cxx { + show_function_definition(state, fd, !0) + } + } + hir::ExternalDeclaration::Declaration(ref d) => show_declaration(state, d), + } +} + +pub fn show_cxx_function_definition(state: &mut OutputState, name: hir::SymRef, vector_mask: u32) { + if let Some((ref fd, run_class)) = state.hir.function_definition(name) { + state.vector_mask = vector_mask; + state.return_vector = (vector_mask & (1 << 31)) != 0 + || match run_class { + hir::RunClass::Scalar => false, + hir::RunClass::Dependent(mask) => (mask & vector_mask) != 0, + _ => true, + }; + match state.functions.get(&(name, vector_mask)) { + Some(true) => {} + Some(false) => { + show_function_prototype(state, &fd.prototype); + state.functions.insert((name, vector_mask), true); + } + None => { + state.functions.insert((name, vector_mask), false); + let buffer = state.push_buffer(); + show_function_definition(state, fd, vector_mask); + for (name, vector_mask) in state.deps.replace(Vec::new()) { + show_cxx_function_definition(state, name, vector_mask); + } + state.flush_buffer(); + state.pop_buffer(buffer); + state.functions.insert((name, vector_mask), true); + } + } + } +} + +pub fn show_translation_unit(state: &mut OutputState, tu: &hir::TranslationUnit) { + state.flush_buffer(); + + for ed in &(tu.0).0 { + show_external_declaration(state, ed); + state.flush_buffer(); + } + if state.output_cxx { + for name in &["main", "swgl_drawSpanRGBA8", "swgl_drawSpanR8"] { + if let Some(sym) = state.hir.lookup(name) { + show_cxx_function_definition(state, sym, 0); + state.flush_buffer(); + } + } + } +} + +fn write_abi(state: &mut OutputState) { + match state.kind { + ShaderKind::Fragment => { + state.write("static void run(FragmentShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + if state.uses_discard { + state.write(" self->swgl_IsPixelDiscarded = false;\n"); + } + state.write(" self->main();\n"); + state.write(" self->step_interp_inputs();\n"); + state.write("}\n"); + state.write("static void skip(FragmentShaderImpl* impl, int steps) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + state.write(" self->step_interp_inputs(steps);\n"); + state.write("}\n"); + if state.use_perspective { + state.write("static void run_perspective(FragmentShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + if state.uses_discard { + state.write(" self->swgl_IsPixelDiscarded = false;\n"); + } + state.write(" self->main();\n"); + state.write(" self->step_perspective_inputs();\n"); + state.write("}\n"); + state.write("static void skip_perspective(FragmentShaderImpl* impl, int steps) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + state.write(" self->step_perspective_inputs(steps);\n"); + state.write("}\n"); + } + if state.hir.lookup("swgl_drawSpanRGBA8").is_some() { + state.write("static int draw_span_RGBA8(FragmentShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl; DISPATCH_DRAW_SPAN(self, RGBA8); }\n"); + } + if state.hir.lookup("swgl_drawSpanR8").is_some() { + state.write("static int draw_span_R8(FragmentShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl; DISPATCH_DRAW_SPAN(self, R8); }\n"); + } + + write!(state, "public:\n{}_frag() {{\n", state.name); + } + ShaderKind::Vertex => { + state.write("static void run(VertexShaderImpl* impl, char* interps, size_t interp_stride) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + state.write(" self->main();\n"); + state.write(" self->store_interp_outputs(interps, interp_stride);\n"); + state.write("}\n"); + state.write("static void init_batch(VertexShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl; self->bind_textures(); }\n"); + + write!(state, "public:\n{}_vert() {{\n", state.name); + } + } + match state.kind { + ShaderKind::Fragment => { + state.write(" init_span_func = &read_interp_inputs;\n"); + state.write(" run_func = &run;\n"); + state.write(" skip_func = &skip;\n"); + if state.hir.lookup("swgl_drawSpanRGBA8").is_some() { + state.write(" draw_span_RGBA8_func = &draw_span_RGBA8;\n"); + } + if state.hir.lookup("swgl_drawSpanR8").is_some() { + state.write(" draw_span_R8_func = &draw_span_R8;\n"); + } + if state.uses_discard { + state.write(" enable_discard();\n"); + } + if state.use_perspective { + state.write(" enable_perspective();\n"); + state.write(" init_span_w_func = &read_perspective_inputs;\n"); + state.write(" run_w_func = &run_perspective;\n"); + state.write(" skip_w_func = &skip_perspective;\n"); + } else { + state.write(" init_span_w_func = &read_interp_inputs;\n"); + state.write(" run_w_func = &run;\n"); + state.write(" skip_w_func = &skip;\n"); + } + } + ShaderKind::Vertex => { + state.write(" set_uniform_1i_func = &set_uniform_1i;\n"); + state.write(" set_uniform_4fv_func = &set_uniform_4fv;\n"); + state.write(" set_uniform_matrix4fv_func = &set_uniform_matrix4fv;\n"); + state.write(" init_batch_func = &init_batch;\n"); + state.write(" load_attribs_func = &load_attribs;\n"); + state.write(" run_primitive_func = &run;\n"); + if state.hir.used_clip_dist != 0 { + state.write(" enable_clip_distance();\n"); + } + } + } + state.write("}\n"); +} + +pub fn define_global_consts(state: &mut OutputState, tu: &hir::TranslationUnit, part_name: &str) { + for i in tu { + match i { + hir::ExternalDeclaration::Declaration(hir::Declaration::InitDeclaratorList(ref d)) => { + let sym = state.hir.sym(d.head.name); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Const, ..) => { + let is_scalar = state.is_scalar.replace( + symbol_run_class(&sym.decl, state.vector_mask) == hir::RunClass::Scalar, + ); + if let Some(ref _array) = d.head.ty.array_sizes { + show_type(state, &d.head.ty); + } else { + if let Some(ty_def) = d.head.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.head.ty); + } + } + write!(state, " constexpr {}::{};\n", part_name, sym.name); + state.is_scalar.set(is_scalar); + } + _ => {} + } + } + _ => {} + } + } +} diff --git a/gfx/wr/glsl-to-cxx/src/main.rs b/gfx/wr/glsl-to-cxx/src/main.rs new file mode 100644 index 0000000000..e40262c84e --- /dev/null +++ b/gfx/wr/glsl-to-cxx/src/main.rs @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use glsl_to_cxx::translate; +fn main() { + println!("{}", translate(&mut std::env::args())); +} |