summaryrefslogtreecommitdiffstats
path: root/gfx/wr/glsl-to-cxx/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/wr/glsl-to-cxx/src
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/wr/glsl-to-cxx/src')
-rw-r--r--gfx/wr/glsl-to-cxx/src/hir.rs4545
-rw-r--r--gfx/wr/glsl-to-cxx/src/lib.rs3681
-rw-r--r--gfx/wr/glsl-to-cxx/src/main.rs8
3 files changed, 8234 insertions, 0 deletions
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, &params[0], &params[1], &params[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()));
+}