Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quellcode-Bibliothek lib.rs   Sprache: Rust

 

/* 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/. */


 #![allow(unknown_lints, mismatched_lifetime_syntaxes)]

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)&nbsp;{
    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 = match r {
        Ok(ok) => ok,
        Err(e) => panic!("failed to parse {:?}: {:?}", file, e),
    };
    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;
                                    }
                                } else if sym.name.starts_with("swgl_commitDithered") {
                                    state.used_fragcoord.set(
                                        state.used_fragcoord.get() | 1 | 2);
                                }
                                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)&nbsp;{
    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)&nbsp;{
    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)&nbsp;{
    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)&nbsp;{
    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)&nbsp;{
    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)&nbsp;{
    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)&nbsp;{
    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)&nbsp;{
    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);
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

Messung V0.5 in Prozent
C=99 H=99 G=98

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.143Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators


Jenseits des Üblichen ....
    

Besucher

Besucher

Statistik
#Sources=1126864
#Domains=1897691
 




Quellcode-Bibliothek  | Datei:   | Haftungsausschluß  | Download des  |   | © 2026 JDD |