diff options
Diffstat (limited to 'gfx/wr/glsl-to-cxx/src/lib.rs')
-rw-r--r-- | gfx/wr/glsl-to-cxx/src/lib.rs | 3681 |
1 files changed, 3681 insertions, 0 deletions
diff --git a/gfx/wr/glsl-to-cxx/src/lib.rs b/gfx/wr/glsl-to-cxx/src/lib.rs new file mode 100644 index 0000000000..0e8273a987 --- /dev/null +++ b/gfx/wr/glsl-to-cxx/src/lib.rs @@ -0,0 +1,3681 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate glsl; + +mod hir; + +use glsl::parser::Parse; +use glsl::syntax; +use glsl::syntax::{TranslationUnit, UnaryOp}; +use hir::{Statement, Type}; +use std::cell::{Cell, RefCell}; +use std::collections::{BTreeMap, HashMap}; +use std::io::Read; +use std::mem; + +#[derive(PartialEq, Eq)] +enum ShaderKind { + Fragment, + Vertex, +} + +type UniformIndices = BTreeMap<String, (i32, hir::TypeKind, hir::StorageClass)>; + +fn build_uniform_indices(indices: &mut UniformIndices, state: &hir::State) { + for u in state.used_globals.borrow().iter() { + let sym = state.sym(*u); + match &sym.decl { + hir::SymDecl::Global(storage, _, ty, _) => match storage { + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + let next_index = indices.len() as i32 + 1; + indices.entry(sym.name.clone()).or_insert(( + next_index, + ty.kind.clone(), + *storage, + )); + } + _ => {} + }, + _ => {} + } + } +} + +pub fn translate(args: &mut dyn Iterator<Item = String>) -> String { + let _cmd_name = args.next(); + let vertex_file = args.next().unwrap(); + + let vs_name = std::path::Path::new(&vertex_file) + .file_stem() + .unwrap() + .to_string_lossy() + .to_string(); + + let frag_file = args.next().unwrap(); + + let fs_name = std::path::Path::new(&frag_file) + .file_stem() + .unwrap() + .to_string_lossy() + .to_string(); + + let (vs_state, vs_hir, vs_is_frag) = parse_shader(vertex_file); + let (fs_state, fs_hir, fs_is_frag) = parse_shader(frag_file); + + // we use a BTree so that iteration is stable + let mut uniform_indices = BTreeMap::new(); + build_uniform_indices(&mut uniform_indices, &vs_state); + build_uniform_indices(&mut uniform_indices, &fs_state); + + assert_eq!(fs_name, vs_name); + + let mut result = translate_shader( + vs_name, + vs_state, + vs_hir, + vs_is_frag, + &uniform_indices, + ); + result += "\n"; + result += &translate_shader( + fs_name, + fs_state, + fs_hir, + fs_is_frag, + &uniform_indices, + ); + result +} + +fn parse_shader(file: String) -> (hir::State, hir::TranslationUnit, bool) { + let mut contents = String::new(); + let is_frag = file.contains("frag"); + std::fs::File::open(&file) + .unwrap() + .read_to_string(&mut contents) + .unwrap(); + let r = TranslationUnit::parse(contents); + + //println!("{:#?}", r); + let mut ast_glsl = String::new(); + let r = r.unwrap(); + glsl::transpiler::glsl::show_translation_unit(&mut ast_glsl, &r); + //let mut fast = std::fs::File::create("ast").unwrap(); + //fast.write(ast_glsl.as_bytes()); + + let mut state = hir::State::new(); + let hir = hir::ast_to_hir(&mut state, &r); + (state, hir, is_frag) +} + +fn translate_shader( + name: String, + mut state: hir::State, + hir: hir::TranslationUnit, + is_frag: bool, + uniform_indices: &UniformIndices, +) -> String { + //println!("{:#?}", state); + + hir::infer_run_class(&mut state, &hir); + + let mut uniforms = Vec::new(); + let mut inputs = Vec::new(); + let mut outputs = Vec::new(); + + for i in &hir { + match i { + hir::ExternalDeclaration::Declaration(hir::Declaration::InitDeclaratorList(ref d)) => { + match &state.sym(d.head.name).decl { + hir::SymDecl::Global(storage, ..) + if state.used_globals.borrow().contains(&d.head.name) => + { + match storage { + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + uniforms.push(d.head.name); + } + hir::StorageClass::In => { + inputs.push(d.head.name); + } + hir::StorageClass::Out | hir::StorageClass::FragColor(_) => { + outputs.push(d.head.name); + } + _ => {} + } + } + _ => {} + } + } + _ => {} + } + } + + //println!("{:#?}", hir); + + let mut state = OutputState { + hir: state, + output: String::new(), + buffer: RefCell::new(String::new()), + indent: 0, + should_indent: false, + output_cxx: false, + mask: None, + cond_index: 0, + return_type: None, + return_declared: false, + return_vector: false, + is_scalar: Cell::new(false), + is_lval: Cell::new(false), + name: name.clone(), + kind: if is_frag { + ShaderKind::Fragment + } else { + ShaderKind::Vertex + }, + functions: HashMap::new(), + deps: RefCell::new(Vec::new()), + vector_mask: 0, + uses_discard: false, + used_fragcoord: Cell::new(0), + use_perspective: false, + used_globals: RefCell::new(Vec::new()), + texel_fetches: RefCell::new(Vec::new()), + }; + + show_translation_unit(&mut state, &hir); + let _output_glsl = state.finish_output(); + + state.should_indent = true; + state.output_cxx = true; + + if state.output_cxx { + let part_name = name.to_owned() + + match state.kind { + ShaderKind::Vertex => "_vert", + ShaderKind::Fragment => "_frag", + }; + + if state.kind == ShaderKind::Vertex { + write_common_globals(&mut state, &inputs, &outputs, uniform_indices); + write!(state, "struct {0}_vert : VertexShaderImpl, {0}_common {{\nprivate:\n", name); + } else { + write!(state, "struct {0}_frag : FragmentShaderImpl, {0}_vert {{\nprivate:\n", name); + } + + write!(state, "typedef {} Self;\n", part_name); + + show_translation_unit(&mut state, &hir); + + let pruned_inputs: Vec<_> = inputs + .iter() + .filter(|i| state.used_globals.borrow().contains(i)) + .cloned() + .collect(); + + if state.kind == ShaderKind::Vertex { + write_set_uniform_1i(&mut state, uniform_indices); + write_set_uniform_4fv(&mut state, uniform_indices); + write_set_uniform_matrix4fv(&mut state, uniform_indices); + write_load_attribs(&mut state, &pruned_inputs); + write_store_outputs(&mut state, &outputs); + } else { + write_read_inputs(&mut state, &pruned_inputs); + } + + write_abi(&mut state); + write!(state, "}};\n\n"); + + if state.kind == ShaderKind::Fragment { + write!(state, "struct {0}_program : ProgramImpl, {0}_frag {{\n", name); + write_get_uniform_index(&mut state, uniform_indices); + write!(state, "void bind_attrib(const char* name, int index) override {{\n"); + write!(state, " attrib_locations.bind_loc(name, index);\n}}\n"); + write!(state, "int get_attrib(const char* name) const override {{\n"); + write!(state, " return attrib_locations.get_loc(name);\n}}\n"); + write!(state, "size_t interpolants_size() const override {{ return sizeof(InterpOutputs); }}\n"); + write!(state, "VertexShaderImpl* get_vertex_shader() override {{\n"); + write!(state, " return this;\n}}\n"); + write!(state, "FragmentShaderImpl* get_fragment_shader() override {{\n"); + write!(state, " return this;\n}}\n"); + write!(state, "const char* get_name() const override {{ return \"{}\"; }}\n", name); + write!(state, "static ProgramImpl* loader() {{ return new {}_program; }}\n", name); + write!(state, "}};\n\n"); + } + + define_global_consts(&mut state, &hir, &part_name); + } else { + show_translation_unit(&mut state, &hir); + } + let output_cxx = state.finish_output(); + + //let mut hir = std::fs::File::create("hir").unwrap(); + //hir.write(output_glsl.as_bytes()); + + output_cxx +} + +fn write_get_uniform_index(state: &mut OutputState, uniform_indices: &UniformIndices) { + write!( + state, + "int get_uniform(const char *name) const override {{\n" + ); + for (uniform_name, (index, _, _)) in uniform_indices.iter() { + write!( + state, + " if (strcmp(\"{}\", name) == 0) {{ return {}; }}\n", + uniform_name, index + ); + } + write!(state, " return -1;\n"); + write!(state, "}}\n"); +} + +fn float4_compatible(ty: hir::TypeKind) -> bool { + match ty { + hir::TypeKind::Vec4 => true, + _ => false, + } +} + +fn matrix4_compatible(ty: hir::TypeKind) -> bool { + match ty { + hir::TypeKind::Mat4 => true, + _ => false, + } +} + +fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndices) { + write!(state, "struct Samplers {{\n"); + for (name, (_, tk, storage)) in uniform_indices.iter() { + match tk { + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect + | hir::TypeKind::ISampler2D => { + write!(state, " "); + show_type_kind(state, &tk); + let suffix = if let hir::StorageClass::Sampler(format) = storage { + format.type_suffix() + } else { + None + }; + write!(state, "{}_impl {}_impl;\n", suffix.unwrap_or(""), name); + write!(state, " int {}_slot;\n", name); + } + _ => {} + } + } + write!( + state, + " bool set_slot(int index, int value) {{\n" + ); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniform_indices.iter() { + match tk { + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect + | hir::TypeKind::ISampler2D => { + write!(state, " case {}:\n", index); + write!(state, " {}_slot = value;\n", name); + write!(state, " return true;\n"); + } + _ => {} + } + } + write!(state, " }}\n"); + write!(state, " return false;\n"); + write!(state, " }}\n"); + write!(state, "}} samplers;\n"); + +} + +fn write_bind_textures(state: &mut OutputState, uniforms: &UniformIndices) { + write!(state, "void bind_textures() {{\n"); + for (name, (_, tk, storage)) in uniforms { + match storage { + hir::StorageClass::Sampler(_format) => { + match tk { + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect => write!(state, + " {0} = lookup_sampler(&samplers.{0}_impl, samplers.{0}_slot);\n", + name), + hir::TypeKind::ISampler2D => write!(state, + " {0} = lookup_isampler(&samplers.{0}_impl, samplers.{0}_slot);\n", + name), + _ => {} + }; + } + _ => {} + } + } + write!(state, "}}\n"); +} + +fn write_set_uniform_1i( + state: &mut OutputState, + uniforms: &UniformIndices, +) { + write!( + state, + "static void set_uniform_1i(VertexShaderImpl* impl, int index, int value) {{\n" + ); + write!(state, " Self* self = (Self*)impl;\n"); + write!(state, " if (self->samplers.set_slot(index, value)) return;\n"); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniforms { + write!(state, " case {}:\n", index); + match tk { + hir::TypeKind::Int => write!( + state, + " self->{} = {}(value);\n", + name, + tk.cxx_primitive_scalar_type_name().unwrap(), + ), + _ => write!(state, " assert(0); // {}\n", name), + }; + write!(state, " break;\n"); + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_set_uniform_4fv( + state: &mut OutputState, + uniforms: &UniformIndices, +) { + write!( + state, + "static void set_uniform_4fv(VertexShaderImpl* impl, int index, const float *value) {{\n" + ); + write!(state, " Self* self = (Self*)impl;\n"); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniforms { + write!(state, " case {}:\n", index); + if float4_compatible(tk.clone()) { + write!( + state, + " self->{} = vec4_scalar::load_from_ptr(value);\n", + name + ); + } else { + write!(state, " assert(0); // {}\n", name); + } + write!(state, " break;\n"); + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_set_uniform_matrix4fv( + state: &mut OutputState, + uniforms: &UniformIndices, +) { + write!( + state, + "static void set_uniform_matrix4fv(VertexShaderImpl* impl, int index, const float *value) {{\n" + ); + write!(state, " Self* self = (Self*)impl;\n"); + write!(state, " switch (index) {{\n"); + for (name, (index, tk, _)) in uniforms { + write!(state, " case {}:\n", index); + if matrix4_compatible(tk.clone()) { + write!( + state, + " self->{} = mat4_scalar::load_from_ptr(value);\n", + name + ); + } else { + write!(state, " assert(0); // {}\n", name); + } + write!(state, " break;\n"); + } + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_bind_attrib_location(state: &mut OutputState, attribs: &[hir::SymRef]) { + write!(state, "struct AttribLocations {{\n"); + for i in attribs { + let sym = state.hir.sym(*i); + write!(state, " int {} = NULL_ATTRIB;\n", sym.name.as_str()); + } + write!(state, " void bind_loc(const char* name, int index) {{\n"); + for i in attribs { + let sym = state.hir.sym(*i); + write!( + state, + " if (strcmp(\"{0}\", name) == 0) {{ {0} = index; return; }}\n", + sym.name.as_str() + ); + } + write!(state, " }}\n"); + write!(state, " int get_loc(const char* name) const {{\n"); + for i in attribs { + let sym = state.hir.sym(*i); + write!(state, + " if (strcmp(\"{0}\", name) == 0) {{ \ + return {0} != NULL_ATTRIB ? {0} : -1; \ + }}\n", + sym.name.as_str()); + } + write!(state, " return -1;\n"); + write!(state, " }}\n"); + write!(state, "}} attrib_locations;\n"); +} + +fn write_common_globals(state: &mut OutputState, attribs: &[hir::SymRef], + outputs: &[hir::SymRef], uniforms: &UniformIndices) { + write!(state, "struct {}_common {{\n", state.name); + + write_program_samplers(state, uniforms); + write_bind_attrib_location(state, attribs); + + let is_scalar = state.is_scalar.replace(true); + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Out, _, ty, hir::RunClass::Scalar) => { + show_type(state, ty); + write!(state, " {};\n", sym.name.as_str()); + } + _ => {} + } + } + for (name, (_, tk, storage)) in uniforms { + match storage { + hir::StorageClass::Sampler(format) => { + write!(state, + "{}{} {};\n", + tk.cxx_primitive_type_name().unwrap(), + format.type_suffix().unwrap_or(""), + name, + ); + } + _ => { + show_type_kind(state, tk); + write!(state, " {};\n", name); + } + } + } + state.is_scalar.set(is_scalar); + + write_bind_textures(state, uniforms); + + write!(state, "}};\n"); +} + +//fn type_name(state: &OutputState, ty: &Type) -> String { +// let buffer = state.push_buffer(); +// show_type(state, ty); +// state.pop_buffer(buffer) +//} + +fn write_load_attribs(state: &mut OutputState, attribs: &[hir::SymRef]) { + write!(state, "static void load_attribs(\ + VertexShaderImpl* impl, VertexAttrib *attribs, \ + uint32_t start, int instance, int count) {{\ + Self* self = (Self*)impl;\n"); + for i in attribs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _interpolation, _ty, run_class) => { + let name = sym.name.as_str(); + let func = if *run_class == hir::RunClass::Scalar { + "load_flat_attrib" + } else { + "load_attrib" + }; + write!(state, + " {0}(self->{1}, attribs[self->attrib_locations.{1}], start, instance, count);\n", + func, name); + } + _ => panic!(), + } + } + write!(state, "}}\n"); +} + +fn write_store_outputs(state: &mut OutputState, outputs: &[hir::SymRef]) { + let is_scalar = state.is_scalar.replace(true); + write!(state, "public:\nstruct InterpOutputs {{\n"); + if state.hir.used_clip_dist != 0 { + state.write(" Float swgl_ClipDistance;\n"); + } + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, run_class) => { + if *run_class != hir::RunClass::Scalar { + show_type(state, ty); + write!(state, " {};\n", sym.name.as_str()); + } + } + _ => panic!(), + } + } + + write!(state, "}};\nprivate:\n"); + state.is_scalar.set(is_scalar); + + write!( + state, + "ALWAYS_INLINE void store_interp_outputs(char* dest_ptr, size_t stride) {{\n" + ); + write!(state, " for(int n = 0; n < 4; n++) {{\n"); + write!( + state, + " auto* dest = reinterpret_cast<InterpOutputs*>(dest_ptr);\n" + ); + if state.hir.used_clip_dist != 0 { + for (i, comp) in "xyzw".chars().enumerate() { + if (state.hir.used_clip_dist & (1 << i)) != 0 { + write!(state, " dest->swgl_ClipDistance.{} = get_nth(gl_ClipDistance[{}], n);\n", comp, i); + } else { + write!(state, " dest->swgl_ClipDistance.{} = 0.0f;\n", comp); + } + } + } + for i in outputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " dest->{} = get_nth({}, n);\n", name, name); + } + } + _ => panic!(), + } + } + write!(state, " dest_ptr += stride;\n"); + write!(state, " }}\n"); + write!(state, "}}\n"); +} + +fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { + write!( + state, + "typedef {}_vert::InterpOutputs InterpInputs;\n", + state.name + ); + + write!(state, "InterpInputs interp_step;\n"); + + let mut has_varying = false; + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, run_class) => { + if *run_class != hir::RunClass::Scalar { + if !has_varying { + has_varying = true; + write!(state, "struct InterpPerspective {{\n"); + } + show_type(state, ty); + write!(state, " {};\n", sym.name.as_str()); + } + } + _ => panic!(), + } + } + if has_varying { + write!(state, "}};\n"); + write!(state, "InterpPerspective interp_perspective;\n"); + } + + write!(state, + "static void read_interp_inputs(\ + FragmentShaderImpl* impl, const void* init_, const void* step_) {{\ + Self* self = (Self*)impl;\ + const InterpInputs* init = (const InterpInputs*)init_;\ + const InterpInputs* step = (const InterpInputs*)step_;\n"); + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!( + state, + " self->{0} = init_interp(init->{0}, step->{0});\n", + name + ); + write!( + state, + " self->interp_step.{0} = step->{0} * 4.0f;\n", + name + ); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + + let used_fragcoord = state.used_fragcoord.get(); + if has_varying || (used_fragcoord & (4 | 8)) != 0 { + state.use_perspective = true; + } + if state.use_perspective { + write!(state, + "static void read_perspective_inputs(\ + FragmentShaderImpl* impl, const void* init_, const void* step_) {{\ + Self* self = (Self*)impl;\ + const InterpInputs* init = (const InterpInputs*)init_;\ + const InterpInputs* step = (const InterpInputs*)step_;\n"); + if has_varying { + write!(state, " Float w = 1.0f / self->gl_FragCoord.w;\n"); + } + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!( + state, + " self->interp_perspective.{0} = init_interp(init->{0}, step->{0});\n", + name + ); + write!(state, " self->{0} = self->interp_perspective.{0} * w;\n", name); + write!( + state, + " self->interp_step.{0} = step->{0} * 4.0f;\n", + name + ); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + } + + write!(state, "ALWAYS_INLINE void step_interp_inputs(int steps = 4) {{\n"); + if (used_fragcoord & 1) != 0 { + write!(state, " step_fragcoord(steps);\n"); + } + if !inputs.is_empty() { + write!(state, " float chunks = steps * 0.25f;\n"); + } + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " {0} += interp_step.{0} * chunks;\n", name); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + + if state.use_perspective { + write!(state, "ALWAYS_INLINE void step_perspective_inputs(int steps = 4) {{\n"); + if (used_fragcoord & 1) != 0 { + write!(state, " step_fragcoord(steps);\n"); + } + write!(state, " step_perspective(steps);\n"); + if !inputs.is_empty() { + write!(state, " float chunks = steps * 0.25f;\n"); + } + if has_varying { + write!(state, " Float w = 1.0f / gl_FragCoord.w;\n"); + } + for i in inputs { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, _, run_class) => { + if *run_class != hir::RunClass::Scalar { + let name = sym.name.as_str(); + write!(state, " interp_perspective.{0} += interp_step.{0} * chunks;\n", name); + write!(state, " {0} = w * interp_perspective.{0};\n", name); + } + } + _ => panic!(), + } + } + write!(state, "}}\n"); + } +} + +pub struct OutputState { + hir: hir::State, + output: String, + buffer: RefCell<String>, + should_indent: bool, + output_cxx: bool, + indent: i32, + mask: Option<Box<hir::Expr>>, + cond_index: usize, + return_type: Option<Box<hir::Type>>, + return_declared: bool, + return_vector: bool, + is_scalar: Cell<bool>, + is_lval: Cell<bool>, + name: String, + kind: ShaderKind, + functions: HashMap<(hir::SymRef, u32), bool>, + deps: RefCell<Vec<(hir::SymRef, u32)>>, + vector_mask: u32, + uses_discard: bool, + used_fragcoord: Cell<i32>, + use_perspective: bool, + used_globals: RefCell<Vec<hir::SymRef>>, + texel_fetches: RefCell<Vec<(hir::SymRef, hir::SymRef, hir::TexelFetchOffsets)>>, +} + +use std::fmt::{Arguments, Write}; + +impl OutputState { + fn indent(&mut self) { + if self.should_indent { + self.indent += 1 + } + } + fn outdent(&mut self) { + if self.should_indent { + self.indent -= 1 + } + } + + fn write(&self, s: &str) { + self.buffer.borrow_mut().push_str(s); + } + + fn flush_buffer(&mut self) { + self.output.push_str(&self.buffer.borrow()); + self.buffer.borrow_mut().clear(); + } + + fn finish_output(&mut self) -> String { + self.flush_buffer(); + + let mut s = String::new(); + mem::swap(&mut self.output, &mut s); + s + } + + fn push_buffer(&self) -> String { + self.buffer.replace(String::new()) + } + + fn pop_buffer(&self, s: String) -> String { + self.buffer.replace(s) + } + + fn write_fmt(&self, args: Arguments) { + let _ = self.buffer.borrow_mut().write_fmt(args); + } +} + +pub fn show_identifier(state: &OutputState, i: &syntax::Identifier) { + state.write(&i.0); +} + +fn glsl_primitive_type_name_to_cxx(glsl_name: &str) -> &str { + hir::TypeKind::from_glsl_primitive_type_name(glsl_name) + .and_then(|kind| kind.cxx_primitive_type_name()) + .unwrap_or(glsl_name) +} + +fn add_used_global(state: &OutputState, i: &hir::SymRef) { + let mut globals = state.used_globals.borrow_mut(); + if !globals.contains(i) { + globals.push(*i); + } +} + +pub fn show_sym(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::NativeFunction(_, ref cxx_name, _) => { + let mut name = sym.name.as_str(); + if state.output_cxx { + name = cxx_name.unwrap_or(name); + } + state.write(name); + } + hir::SymDecl::Global(..) => { + if state.output_cxx { + add_used_global(state, i); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::UserFunction(..) | hir::SymDecl::Local(..) | hir::SymDecl::Struct(..) => { + let mut name = sym.name.as_str(); + // we want to replace constructor names + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + } +} + +pub fn show_variable(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(_, _, ty, _) => { + show_type(state, ty); + state.write(" "); + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + _ => panic!(), + } +} + +pub fn write_default_constructor(state: &OutputState, name: &str) { + // write default constructor + let _ = write!(state, "{}() = default;\n", name); +} + +pub fn write_constructor(state: &OutputState, name: &str, s: &hir::StructFields) { + if s.fields.len() == 1 { + state.write("explicit "); + } + let _ = write!(state, "{}(", name); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + show_type(state, &field.ty); + state.write(" "); + show_identifier_and_type(state, &field.name, &field.ty); + first_field = false; + } + state.write(") : "); + + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + let _ = write!(state, "{}({})", field.name, field.name); + first_field = false; + } + state.write("{}\n"); +} + +pub fn write_convert_constructor(state: &OutputState, name: &str, s: &hir::StructFields) { + if s.fields.len() == 1 { + state.write("explicit "); + } + let _ = write!(state, "{}(", name); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + + let is_scalar = state.is_scalar.replace(true); + show_type(state, &field.ty); + state.is_scalar.set(is_scalar); + + state.write(" "); + + show_identifier_and_type(state, &field.name, &field.ty); + first_field = false; + } + state.write(")"); + + let mut first_field = true; + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_none() { + if first_field { + state.write(":"); + } else { + state.write(","); + } + let _ = write!(state, "{}({})", name, name); + first_field = false; + } + } + state.write("{\n"); + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_some() { + let _ = write!(state, "this->{}.convert({});\n", name, name); + } + } + state.write("}\n"); + + let _ = write!(state, "IMPLICIT {}({}_scalar s)", name, name); + let mut first_field = true; + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_none() { + if first_field { + state.write(":"); + } else { + state.write(","); + } + let _ = write!(state, "{}(s.{})", name, name); + first_field = false; + } + } + state.write("{\n"); + for hir::StructField { ty, name } in &s.fields { + if ty.array_sizes.is_some() { + let _ = write!(state, "{}.convert(s.{});\n", name, name); + } + } + state.write("}\n"); +} + +pub fn write_if_then_else(state: &OutputState, name: &str, s: &hir::StructFields) { + let _ = write!( + state, + "friend {} if_then_else(I32 c, {} t, {} e) {{ return {}(\n", + name, name, name, name + ); + let mut first_field = true; + for field in &s.fields { + if !first_field { + state.write(", "); + } + let _ = write!(state, "if_then_else(c, t.{}, e.{})", field.name, field.name); + first_field = false; + } + state.write(");\n}"); +} + +pub fn show_storage_class(state: &OutputState, q: &hir::StorageClass) { + match *q { + hir::StorageClass::None => {} + hir::StorageClass::Const => { + state.write("const "); + } + hir::StorageClass::In => { + state.write("in "); + } + hir::StorageClass::Out => { + state.write("out "); + } + hir::StorageClass::FragColor(index) => { + write!(state, "layout(location = 0, index = {}) out ", index); + } + hir::StorageClass::Uniform | hir::StorageClass::Sampler(..) => { + state.write("uniform "); + } + } +} + +pub fn show_sym_decl(state: &OutputState, i: &hir::SymRef) { + let sym = state.hir.sym(*i); + match &sym.decl { + hir::SymDecl::Global(storage, ..) => { + if !state.output_cxx { + show_storage_class(state, storage) + } + if storage == &hir::StorageClass::Const { + state.write("static constexpr "); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::Local(storage, ..) => { + if !state.output_cxx { + show_storage_class(state, storage) + } + if storage == &hir::StorageClass::Const { + state.write("const "); + } + let mut name = sym.name.as_str(); + if state.output_cxx { + name = glsl_primitive_type_name_to_cxx(name); + } + state.write(name); + } + hir::SymDecl::Struct(s) => { + let name = sym.name.as_str(); + + if state.output_cxx { + let name_scalar = format!("{}_scalar", name); + write!(state, "struct {} {{\n", name_scalar); + let is_scalar = state.is_scalar.replace(true); + for field in &s.fields { + show_struct_field(state, field); + } + write_default_constructor(state, &name_scalar); + write_constructor(state, &name_scalar, s); + state.is_scalar.set(is_scalar); + state.write("};\n"); + } + + write!(state, "struct {} {{\n", name); + for field in &s.fields { + show_struct_field(state, field); + } + + // write if_then_else + if state.output_cxx { + write_default_constructor(state, name); + write_constructor(state, name, s); + write_convert_constructor(state, name, s); + write_if_then_else(state, name, s); + } + state.write("}"); + } + _ => panic!(), + } +} + +pub fn show_type_name(state: &OutputState, t: &syntax::TypeName) { + state.write(&t.0); +} + +pub fn show_type_specifier_non_array(state: &mut OutputState, t: &syntax::TypeSpecifierNonArray) { + if let Some(kind) = hir::TypeKind::from_primitive_type_specifier(t) { + show_type_kind(state, &kind); + } else { + match t { + syntax::TypeSpecifierNonArray::Struct(ref _s) => panic!(), //show_struct_non_declaration(state, s), + syntax::TypeSpecifierNonArray::TypeName(ref tn) => show_type_name(state, tn), + _ => unreachable!(), + } + } +} + +pub fn show_type_kind(state: &OutputState, t: &hir::TypeKind) { + if state.output_cxx { + if state.is_scalar.get() { + if let Some(name) = t.cxx_primitive_scalar_type_name() { + state.write(name); + } else if let Some(name) = t.cxx_primitive_type_name() { + let mut scalar_name = String::from(name); + scalar_name.push_str("_scalar"); + state.write(scalar_name.as_str()); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + let mut scalar_name = String::from(state.hir.sym(*s).name.as_str()); + scalar_name.push_str("_scalar"); + state.write(scalar_name.as_str()); + } + _ => unreachable!(), + } + } + } else if let Some(name) = t.cxx_primitive_type_name() { + state.write(name); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + state.write(state.hir.sym(*s).name.as_str()); + } + _ => unreachable!(), + } + } + } else if let Some(name) = t.glsl_primitive_type_name() { + state.write(name); + } else { + match t { + hir::TypeKind::Struct(ref s) => { + state.write(state.hir.sym(*s).name.as_str()); + } + _ => unreachable!(), + } + } +} + +pub fn show_type_specifier(state: &mut OutputState, t: &syntax::TypeSpecifier) { + show_type_specifier_non_array(state, &t.ty); + + if let Some(ref arr_spec) = t.array_specifier { + show_array_spec(state, arr_spec); + } +} + +pub fn show_type(state: &OutputState, t: &Type) { + if !state.output_cxx { + if let Some(ref precision) = t.precision { + show_precision_qualifier(state, precision); + state.write(" "); + } + } + + if state.output_cxx { + if let Some(ref array) = t.array_sizes { + state.write("Array<"); + show_type_kind(state, &t.kind); + let size = match &array.sizes[..] { + [size] => size, + _ => panic!(), + }; + state.write(","); + show_hir_expr(state, size); + state.write(">"); + } else { + show_type_kind(state, &t.kind); + } + } else { + show_type_kind(state, &t.kind); + } + + /*if let Some(ref arr_spec) = t.array_sizes { + panic!(); + }*/ +} + +/*pub fn show_fully_specified_type(state: &mut OutputState, t: &FullySpecifiedType) { + state.flat = false; + if let Some(ref qual) = t.qualifier { + if !state.output_cxx { + show_type_qualifier(state, &qual); + } else { + state.flat = + qual.qualifiers.0.iter() + .flat_map(|q| match q { syntax::TypeQualifierSpec::Interpolation(Flat) => Some(()), _ => None}) + .next().is_some(); + } + state.write(" "); + } + + show_type_specifier(state, &t.ty); +}*/ + +/*pub fn show_struct_non_declaration(state: &mut OutputState, s: &syntax::StructSpecifier) { + state.write("struct "); + + if let Some(ref name) = s.name { + let _ = write!(state, "{} ", name); + } + + state.write("{\n"); + + for field in &s.fields.0 { + show_struct_field(state, field); + } + + state.write("}"); +}*/ + +pub fn show_struct(_state: &OutputState, _s: &syntax::StructSpecifier) { + panic!(); + //show_struct_non_declaration(state, s); + //state.write(";\n"); +} + +pub fn show_struct_field(state: &OutputState, field: &hir::StructField) { + show_type(state, &field.ty); + state.write(" "); + + show_identifier_and_type(state, &field.name, &field.ty); + + state.write(";\n"); +} + +pub fn show_array_spec(state: &OutputState, a: &syntax::ArraySpecifier) { + for dimension in &a.dimensions { + match dimension { + syntax::ArraySpecifierDimension::Unsized => { + state.write("[]"); + } + syntax::ArraySpecifierDimension::ExplicitlySized(ref e) => { + state.write("["); + show_expr(state, &e); + state.write("]"); + } + } + } +} + +pub fn show_identifier_and_type(state: &OutputState, ident: &syntax::Identifier, ty: &hir::Type) { + let _ = write!(state, "{}", ident); + + if !state.output_cxx { + if let Some(ref arr_spec) = ty.array_sizes { + show_array_sizes(state, &arr_spec); + } + } +} + +pub fn show_arrayed_identifier(state: &OutputState, ident: &syntax::ArrayedIdentifier) { + let _ = write!(state, "{}", ident.ident); + + if let Some(ref arr_spec) = ident.array_spec { + show_array_spec(state, &arr_spec); + } +} + +pub fn show_array_sizes(state: &OutputState, a: &hir::ArraySizes) { + state.write("["); + match &a.sizes[..] { + [a] => show_hir_expr(state, a), + _ => panic!(), + } + + state.write("]"); + /* + match *a { + syntax::ArraySpecifier::Unsized => { state.write("[]"); } + syntax::ArraySpecifier::ExplicitlySized(ref e) => { + state.write("["); + show_expr(state, &e); + state.write("]"); + } + }*/ +} + +pub fn show_type_qualifier(state: &OutputState, q: &hir::TypeQualifier) { + let mut qualifiers = q.qualifiers.0.iter(); + let first = qualifiers.next().unwrap(); + + show_type_qualifier_spec(state, first); + + for qual_spec in qualifiers { + state.write(" "); + show_type_qualifier_spec(state, qual_spec) + } +} + +pub fn show_type_qualifier_spec(state: &OutputState, q: &hir::TypeQualifierSpec) { + match *q { + hir::TypeQualifierSpec::Layout(ref l) => show_layout_qualifier(state, &l), + hir::TypeQualifierSpec::Parameter(ref _p) => panic!(), + hir::TypeQualifierSpec::Memory(ref _m) => panic!(), + hir::TypeQualifierSpec::Invariant => { + state.write("invariant"); + } + hir::TypeQualifierSpec::Precise => { + state.write("precise"); + } + } +} + +pub fn show_syntax_storage_qualifier(state: &OutputState, q: &syntax::StorageQualifier) { + match *q { + syntax::StorageQualifier::Const => { + state.write("const"); + } + syntax::StorageQualifier::InOut => { + state.write("inout"); + } + syntax::StorageQualifier::In => { + state.write("in"); + } + syntax::StorageQualifier::Out => { + state.write("out"); + } + syntax::StorageQualifier::Centroid => { + state.write("centroid"); + } + syntax::StorageQualifier::Patch => { + state.write("patch"); + } + syntax::StorageQualifier::Sample => { + state.write("sample"); + } + syntax::StorageQualifier::Uniform => { + state.write("uniform"); + } + syntax::StorageQualifier::Attribute => { + state.write("attribute"); + } + syntax::StorageQualifier::Varying => { + state.write("varying"); + } + syntax::StorageQualifier::Buffer => { + state.write("buffer"); + } + syntax::StorageQualifier::Shared => { + state.write("shared"); + } + syntax::StorageQualifier::Coherent => { + state.write("coherent"); + } + syntax::StorageQualifier::Volatile => { + state.write("volatile"); + } + syntax::StorageQualifier::Restrict => { + state.write("restrict"); + } + syntax::StorageQualifier::ReadOnly => { + state.write("readonly"); + } + syntax::StorageQualifier::WriteOnly => { + state.write("writeonly"); + } + syntax::StorageQualifier::Subroutine(ref n) => show_subroutine(state, &n), + } +} + +pub fn show_subroutine(state: &OutputState, types: &[syntax::TypeName]) { + state.write("subroutine"); + + if !types.is_empty() { + state.write("("); + + let mut types_iter = types.iter(); + let first = types_iter.next().unwrap(); + + show_type_name(state, first); + + for type_name in types_iter { + state.write(", "); + show_type_name(state, type_name); + } + + state.write(")"); + } +} + +pub fn show_layout_qualifier(state: &OutputState, l: &syntax::LayoutQualifier) { + let mut qualifiers = l.ids.0.iter(); + let first = qualifiers.next().unwrap(); + + state.write("layout ("); + show_layout_qualifier_spec(state, first); + + for qual_spec in qualifiers { + state.write(", "); + show_layout_qualifier_spec(state, qual_spec); + } + + state.write(")"); +} + +pub fn show_layout_qualifier_spec(state: &OutputState, l: &syntax::LayoutQualifierSpec) { + match *l { + syntax::LayoutQualifierSpec::Identifier(ref i, Some(ref e)) => { + let _ = write!(state, "{} = ", i); + show_expr(state, &e); + } + syntax::LayoutQualifierSpec::Identifier(ref i, None) => show_identifier(state, &i), + syntax::LayoutQualifierSpec::Shared => { + state.write("shared"); + } + } +} + +pub fn show_precision_qualifier(state: &OutputState, p: &syntax::PrecisionQualifier) { + match *p { + syntax::PrecisionQualifier::High => { + state.write("highp"); + } + syntax::PrecisionQualifier::Medium => { + state.write("mediump"); + } + syntax::PrecisionQualifier::Low => { + state.write("low"); + } + } +} + +pub fn show_interpolation_qualifier(state: &OutputState, i: &syntax::InterpolationQualifier) { + match *i { + syntax::InterpolationQualifier::Smooth => { + state.write("smooth"); + } + syntax::InterpolationQualifier::Flat => { + state.write("flat"); + } + syntax::InterpolationQualifier::NoPerspective => { + state.write("noperspective"); + } + } +} + +pub fn show_parameter_qualifier(state: &mut OutputState, i: &Option<hir::ParameterQualifier>) { + if let Some(i) = i { + if state.output_cxx { + match *i { + hir::ParameterQualifier::Out => { + state.write("&"); + } + hir::ParameterQualifier::InOut => { + state.write("&"); + } + _ => {} + } + } else { + match *i { + hir::ParameterQualifier::Const => { + state.write("const"); + } + hir::ParameterQualifier::In => { + state.write("in"); + } + hir::ParameterQualifier::Out => { + state.write("out"); + } + hir::ParameterQualifier::InOut => { + state.write("inout"); + } + } + } + } +} + +pub fn show_float(state: &OutputState, x: f32) { + if x.fract() == 0. { + write!(state, "{}.f", x); + } else { + write!(state, "{}f", x); + } +} + +pub fn show_double(state: &OutputState, x: f64) { + // force doubles to print as floats + if x.fract() == 0. { + write!(state, "{}.f", x); + } else { + write!(state, "{}f", x); + } +} + +fn expr_run_class(state: &OutputState, expr: &hir::Expr) -> hir::RunClass { + match &expr.kind { + hir::ExprKind::Variable(i) => symbol_run_class(&state.hir.sym(*i).decl, state.vector_mask), + hir::ExprKind::IntConst(_) + | hir::ExprKind::UIntConst(_) + | hir::ExprKind::BoolConst(_) + | hir::ExprKind::FloatConst(_) + | hir::ExprKind::DoubleConst(_) => hir::RunClass::Scalar, + hir::ExprKind::Unary(_, ref e) => expr_run_class(state, e), + hir::ExprKind::Binary(_, ref l, ref r) => { + expr_run_class(state, l).merge(expr_run_class(state, r)) + } + hir::ExprKind::Ternary(ref c, ref s, ref e) => expr_run_class(state, c) + .merge(expr_run_class(state, s)) + .merge(expr_run_class(state, e)), + hir::ExprKind::Assignment(ref v, _, ref e) => { + expr_run_class(state, v).merge(expr_run_class(state, e)) + } + hir::ExprKind::Bracket(ref e, ref indx) => { + indx.iter().fold( + expr_run_class(state, e), + |run_class, indx| run_class.merge(expr_run_class(state, indx)), + ) + } + hir::ExprKind::FunCall(ref fun, ref args) => { + let arg_mask: u32 = args.iter().enumerate().fold(0, |mask, (idx, e)| { + if expr_run_class(state, e) == hir::RunClass::Vector { + mask | (1 << idx) + } else { + mask + } + }); + match fun { + hir::FunIdentifier::Identifier(ref sym) => match &state.hir.sym(*sym).decl { + hir::SymDecl::NativeFunction(_, _, ref ret_class) => { + if *ret_class != hir::RunClass::Unknown { + *ret_class + } else if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + hir::SymDecl::UserFunction(ref fd, ref run_class) => { + let param_mask: u32 = fd.prototype.parameters.iter().enumerate().fold( + arg_mask, + |mask, (idx, param)| { + if let hir::FunctionParameterDeclaration::Named(Some(qual), p) = + param + { + match qual { + hir::ParameterQualifier::InOut + | hir::ParameterQualifier::Out => { + if symbol_run_class( + &state.hir.sym(p.sym).decl, + arg_mask, + ) == hir::RunClass::Vector + { + mask | (1 << idx) + } else { + mask + } + } + _ => mask, + } + } else { + mask + } + }, + ); + match *run_class { + hir::RunClass::Scalar => hir::RunClass::Scalar, + hir::RunClass::Dependent(mask) => { + if (mask & param_mask) != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => hir::RunClass::Vector, + } + } + hir::SymDecl::Struct(..) => { + if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => panic!(), + }, + hir::FunIdentifier::Constructor(..) => { + if arg_mask != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + } + } + hir::ExprKind::Dot(ref e, _) => expr_run_class(state, e), + hir::ExprKind::SwizzleSelector(ref e, _) => expr_run_class(state, e), + hir::ExprKind::PostInc(ref e) => expr_run_class(state, e), + hir::ExprKind::PostDec(ref e) => expr_run_class(state, e), + hir::ExprKind::Comma(_, ref e) => expr_run_class(state, e), + hir::ExprKind::Cond(_, ref e) => expr_run_class(state, e), + hir::ExprKind::CondMask => hir::RunClass::Vector, + } +} + +pub fn show_hir_expr(state: &OutputState, expr: &hir::Expr) { + show_hir_expr_inner(state, expr, false); +} + +pub fn show_hir_expr_inner(state: &OutputState, expr: &hir::Expr, top_level: bool) { + match expr.kind { + hir::ExprKind::Variable(ref i) => show_sym(state, i), + hir::ExprKind::IntConst(ref x) => { + let _ = write!(state, "{}", x); + } + hir::ExprKind::UIntConst(ref x) => { + let _ = write!(state, "{}u", x); + } + hir::ExprKind::BoolConst(ref x) => { + let _ = write!(state, "{}", x); + } + hir::ExprKind::FloatConst(ref x) => show_float(state, *x), + hir::ExprKind::DoubleConst(ref x) => show_double(state, *x), + hir::ExprKind::Unary(ref op, ref e) => { + show_unary_op(state, &op); + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + } + hir::ExprKind::Binary(ref op, ref l, ref r) => { + state.write("("); + show_hir_expr(state, &l); + state.write(")"); + show_binary_op(state, &op); + state.write("("); + show_hir_expr(state, &r); + state.write(")"); + } + hir::ExprKind::Ternary(ref c, ref s, ref e) => { + if state.output_cxx && expr_run_class(state, c) != hir::RunClass::Scalar { + state.write("if_then_else("); + show_hir_expr(state, &c); + state.write(", "); + show_hir_expr(state, &s); + state.write(", "); + show_hir_expr(state, &e); + state.write(")"); + } else { + show_hir_expr(state, &c); + state.write(" ? "); + show_hir_expr(state, &s); + state.write(" : "); + show_hir_expr(state, &e); + } + } + hir::ExprKind::Assignment(ref v, ref op, ref e) => { + let is_output = hir::is_output(v, &state.hir).is_some(); + let is_scalar_var = expr_run_class(state, v) == hir::RunClass::Scalar; + let is_scalar_expr = expr_run_class(state, e) == hir::RunClass::Scalar; + let force_scalar = is_scalar_var && !is_scalar_expr; + + if let Some(mask) = &state.mask { + let is_scalar_mask = expr_run_class(state, mask) == hir::RunClass::Scalar; + let force_scalar_mask = is_scalar_var && is_scalar_expr && !is_scalar_mask; + + if force_scalar || force_scalar_mask { + if top_level { + state.write("if ("); + } else { + state.write("("); + } + } else { + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" = if_then_else("); + } + + if is_output && state.return_declared { + state.write("(("); + show_hir_expr(state, mask); + state.write(")&ret_mask)"); + } else { + show_hir_expr(state, mask); + } + if force_scalar || force_scalar_mask { + if top_level { + state.write("[0]) { "); + } else { + state.write("[0] ? "); + } + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" = "); + } else { + state.write(","); + } + + if op != &syntax::AssignmentOp::Equal { + show_hir_expr(state, &v); + } + + match *op { + syntax::AssignmentOp::Equal => {} + syntax::AssignmentOp::Mult => { + state.write("*"); + } + syntax::AssignmentOp::Div => { + state.write("/"); + } + syntax::AssignmentOp::Mod => { + state.write("%"); + } + syntax::AssignmentOp::Add => { + state.write("+"); + } + syntax::AssignmentOp::Sub => { + state.write("-"); + } + syntax::AssignmentOp::LShift => { + state.write("<<"); + } + syntax::AssignmentOp::RShift => { + state.write(">>"); + } + syntax::AssignmentOp::And => { + state.write("&"); + } + syntax::AssignmentOp::Xor => { + state.write("^"); + } + syntax::AssignmentOp::Or => { + state.write("|"); + } + } + if force_scalar { + state.write("force_scalar("); + } + show_hir_expr(state, &e); + if force_scalar { + state.write(")"); + } + if force_scalar || force_scalar_mask { + if top_level { + state.write("; }"); + } else { + state.write(" : "); + show_hir_expr(state, &v); + state.write(")"); + } + } else { + state.write(","); + show_hir_expr(state, &v); + state.write(")"); + } + } else { + state.is_lval.set(true); + show_hir_expr(state, &v); + state.is_lval.set(false); + state.write(" "); + + if is_output && state.return_declared { + state.write("= "); + if force_scalar { + state.write("force_scalar("); + } + state.write("if_then_else(ret_mask,"); + + if op != &syntax::AssignmentOp::Equal { + show_hir_expr(state, &v); + } + + match *op { + syntax::AssignmentOp::Equal => {} + syntax::AssignmentOp::Mult => { + state.write("*"); + } + syntax::AssignmentOp::Div => { + state.write("/"); + } + syntax::AssignmentOp::Mod => { + state.write("%"); + } + syntax::AssignmentOp::Add => { + state.write("+"); + } + syntax::AssignmentOp::Sub => { + state.write("-"); + } + syntax::AssignmentOp::LShift => { + state.write("<<"); + } + syntax::AssignmentOp::RShift => { + state.write(">>"); + } + syntax::AssignmentOp::And => { + state.write("&"); + } + syntax::AssignmentOp::Xor => { + state.write("^"); + } + syntax::AssignmentOp::Or => { + state.write("|"); + } + } + show_hir_expr(state, &e); + state.write(","); + show_hir_expr(state, &v); + state.write(")"); + } else { + show_assignment_op(state, &op); + state.write(" "); + if force_scalar { + state.write("force_scalar("); + } + show_hir_expr(state, &e); + } + + if force_scalar { + state.write(")"); + } + } + } + hir::ExprKind::Bracket(ref e, ref indx) => { + show_hir_expr(state, &e); + state.write("["); + for dimension in indx { + show_hir_expr(state, dimension); + } + state.write("]"); + } + hir::ExprKind::FunCall(ref fun, ref args) => { + let mut cond_mask: u32 = 0; + let mut adapt_mask: u32 = 0; + let mut has_ret = false; + let mut array_constructor = false; + + let mut arg_mask: u32 = 0; + for (idx, e) in args.iter().enumerate() { + if expr_run_class(state, e) == hir::RunClass::Vector { + arg_mask |= 1 << idx; + } + } + + match fun { + hir::FunIdentifier::Constructor(t) => { + let is_scalar = state.is_scalar.replace(arg_mask == 0); + show_type(state, t); + state.is_scalar.set(is_scalar); + array_constructor = t.array_sizes.is_some(); + } + hir::FunIdentifier::Identifier(name) => { + if state.output_cxx { + let sym = state.hir.sym(*name); + match &sym.decl { + hir::SymDecl::NativeFunction(..) => { + if sym.name == "texelFetchOffset" && args.len() >= 4 { + if let Some((sampler, base, x, y)) = hir::get_texel_fetch_offset( + &state.hir, &args[0], &args[1], &args[3], + ) { + let base_sym = state.hir.sym(base); + let sampler_sym = state.hir.sym(sampler); + add_used_global(state, &sampler); + if let hir::SymDecl::Global(..) = &base_sym.decl { + add_used_global(state, &base); + } + write!( + state, + "texelFetchUnchecked({}, {}_{}_fetch, {}, {})", + sampler_sym.name, + sampler_sym.name, + base_sym.name, + x, + y, + ); + return; + } + } + show_sym(state, name) + } + hir::SymDecl::UserFunction(ref fd, ref _run_class) => { + if (state.mask.is_some() || state.return_declared) && + !fd.globals.is_empty() + { + cond_mask |= 1 << 31; + } + let mut param_mask: u32 = 0; + for (idx, (param, e)) in + fd.prototype.parameters.iter().zip(args.iter()).enumerate() + { + if let hir::FunctionParameterDeclaration::Named(qual, p) = param + { + if symbol_run_class(&state.hir.sym(p.sym).decl, arg_mask) + == hir::RunClass::Vector + { + param_mask |= 1 << idx; + } + match qual { + Some(hir::ParameterQualifier::InOut) + | Some(hir::ParameterQualifier::Out) => { + if state.mask.is_some() || state.return_declared { + cond_mask |= 1 << idx; + } + if (!arg_mask & param_mask & (1 << idx)) != 0 { + if adapt_mask == 0 { + state.write(if top_level { + "{ " + } else { + "({ " + }); + } + show_type(state, &p.ty); + write!(state, " _arg{}_ = ", idx); + show_hir_expr(state, e); + state.write("; "); + adapt_mask |= 1 << idx; + } + } + _ => {} + } + } + } + if adapt_mask != 0 && + fd.prototype.ty.kind != hir::TypeKind::Void && + !top_level + { + state.write("auto _ret_ = "); + has_ret = true; + } + show_sym(state, name); + let mut deps = state.deps.borrow_mut(); + let dep_key = ( + *name, + if cond_mask != 0 { + param_mask | (1 << 31) + } else { + param_mask + }, + ); + if !deps.contains(&dep_key) { + deps.push(dep_key); + } + } + hir::SymDecl::Struct(..) => { + show_sym(state, name); + if arg_mask == 0 { + state.write("_scalar"); + } + } + _ => panic!("bad identifier to function call"), + } + } + } + } + + if array_constructor { + state.write("{{"); + } else { + state.write("("); + } + + for (idx, e) in args.iter().enumerate() { + if idx != 0 { + state.write(", "); + } + if (adapt_mask & (1 << idx)) != 0 { + write!(state, "_arg{}_", idx); + } else { + show_hir_expr(state, e); + } + } + + if cond_mask != 0 { + if !args.is_empty() { + state.write(", "); + } + if let Some(mask) = &state.mask { + if state.return_declared { + state.write("("); + show_hir_expr(state, mask); + state.write(")&ret_mask"); + } else { + show_hir_expr(state, mask); + } + } else if state.return_declared { + state.write("ret_mask"); + } else { + state.write("~0"); + } + } + + if array_constructor { + state.write("}}"); + } else { + state.write(")"); + } + + if adapt_mask != 0 { + state.write("; "); + for (idx, e) in args.iter().enumerate() { + if (adapt_mask & (1 << idx)) != 0 { + state.is_lval.set(true); + show_hir_expr(state, e); + state.is_lval.set(false); + write!(state, " = force_scalar(_arg{}_); ", idx); + } + } + if has_ret { + state.write("_ret_; })"); + } else { + state.write(if top_level { "}" } else { "})" }); + } + } + } + hir::ExprKind::Dot(ref e, ref i) => { + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + state.write("."); + show_identifier(state, i); + } + hir::ExprKind::SwizzleSelector(ref e, ref s) => { + if state.output_cxx { + if let hir::ExprKind::Variable(ref sym) = &e.kind { + if state.hir.sym(*sym).name == "gl_FragCoord" { + state.used_fragcoord.set( + s.components.iter().fold( + state.used_fragcoord.get(), + |used, c| used | (1 << c))); + } + } + state.write("("); + show_hir_expr(state, &e); + state.write(")."); + if s.components.len() == 1 { + // For single component swizzles, output a field access to + // avoid stressing inlining of sel(). + state.write(&s.to_field_set(hir::FieldSet::Xyzw)); + } else { + if state.is_lval.get() && s.components.len() > 1 { + state.write("lsel("); + } else { + state.write("sel("); + } + for (i, c) in s.to_string().chars().enumerate() { + if i > 0 { + state.write(","); + } + write!(state, "{}", c.to_uppercase()); + } + state.write(")"); + } + } else { + state.write("("); + show_hir_expr(state, &e); + state.write(")"); + state.write("."); + state.write(&s.to_string()); + } + } + hir::ExprKind::PostInc(ref e) => { + show_hir_expr(state, &e); + state.write("++"); + } + hir::ExprKind::PostDec(ref e) => { + show_hir_expr(state, &e); + state.write("--"); + } + hir::ExprKind::Comma(ref a, ref b) => { + show_hir_expr(state, &a); + state.write(", "); + show_hir_expr(state, &b); + } + hir::ExprKind::Cond(index, _) => { + write!(state, "_c{}_", index); + } + hir::ExprKind::CondMask => { + state.write("_cond_mask_"); + } + } +} + +pub fn show_expr(state: &OutputState, expr: &syntax::Expr) { + match *expr { + syntax::Expr::Variable(ref i) => show_identifier(state, &i), + syntax::Expr::IntConst(ref x) => { + let _ = write!(state, "{}", x); + } + syntax::Expr::UIntConst(ref x) => { + let _ = write!(state, "{}u", x); + } + syntax::Expr::BoolConst(ref x) => { + let _ = write!(state, "{}", x); + } + syntax::Expr::FloatConst(ref x) => show_float(state, *x), + syntax::Expr::DoubleConst(ref x) => show_double(state, *x), + syntax::Expr::Unary(ref op, ref e) => { + show_unary_op(state, &op); + state.write("("); + show_expr(state, &e); + state.write(")"); + } + syntax::Expr::Binary(ref op, ref l, ref r) => { + state.write("("); + show_expr(state, &l); + state.write(")"); + show_binary_op(state, &op); + state.write("("); + show_expr(state, &r); + state.write(")"); + } + syntax::Expr::Ternary(ref c, ref s, ref e) => { + show_expr(state, &c); + state.write(" ? "); + show_expr(state, &s); + state.write(" : "); + show_expr(state, &e); + } + syntax::Expr::Assignment(ref v, ref op, ref e) => { + show_expr(state, &v); + state.write(" "); + show_assignment_op(state, &op); + state.write(" "); + show_expr(state, &e); + } + syntax::Expr::Bracket(ref e, ref a) => { + show_expr(state, &e); + show_array_spec(state, &a); + } + syntax::Expr::FunCall(ref fun, ref args) => { + show_function_identifier(state, &fun); + state.write("("); + + if !args.is_empty() { + let mut args_iter = args.iter(); + let first = args_iter.next().unwrap(); + show_expr(state, first); + + for e in args_iter { + state.write(", "); + show_expr(state, e); + } + } + + state.write(")"); + } + syntax::Expr::Dot(ref e, ref i) => { + state.write("("); + show_expr(state, &e); + state.write(")"); + state.write("."); + show_identifier(state, &i); + } + syntax::Expr::PostInc(ref e) => { + show_expr(state, &e); + state.write("++"); + } + syntax::Expr::PostDec(ref e) => { + show_expr(state, &e); + state.write("--"); + } + syntax::Expr::Comma(ref a, ref b) => { + show_expr(state, &a); + state.write(", "); + show_expr(state, &b); + } + } +} + +pub fn show_unary_op(state: &OutputState, op: &syntax::UnaryOp) { + match *op { + syntax::UnaryOp::Inc => { + state.write("++"); + } + syntax::UnaryOp::Dec => { + state.write("--"); + } + syntax::UnaryOp::Add => { + state.write("+"); + } + syntax::UnaryOp::Minus => { + state.write("-"); + } + syntax::UnaryOp::Not => { + state.write("!"); + } + syntax::UnaryOp::Complement => { + state.write("~"); + } + } +} + +pub fn show_binary_op(state: &OutputState, op: &syntax::BinaryOp) { + match *op { + syntax::BinaryOp::Or => { + state.write("||"); + } + syntax::BinaryOp::Xor => { + state.write("^^"); + } + syntax::BinaryOp::And => { + state.write("&&"); + } + syntax::BinaryOp::BitOr => { + state.write("|"); + } + syntax::BinaryOp::BitXor => { + state.write("^"); + } + syntax::BinaryOp::BitAnd => { + state.write("&"); + } + syntax::BinaryOp::Equal => { + state.write("=="); + } + syntax::BinaryOp::NonEqual => { + state.write("!="); + } + syntax::BinaryOp::LT => { + state.write("<"); + } + syntax::BinaryOp::GT => { + state.write(">"); + } + syntax::BinaryOp::LTE => { + state.write("<="); + } + syntax::BinaryOp::GTE => { + state.write(">="); + } + syntax::BinaryOp::LShift => { + state.write("<<"); + } + syntax::BinaryOp::RShift => { + state.write(">>"); + } + syntax::BinaryOp::Add => { + state.write("+"); + } + syntax::BinaryOp::Sub => { + state.write("-"); + } + syntax::BinaryOp::Mult => { + state.write("*"); + } + syntax::BinaryOp::Div => { + state.write("/"); + } + syntax::BinaryOp::Mod => { + state.write("%"); + } + } +} + +pub fn show_assignment_op(state: &OutputState, op: &syntax::AssignmentOp) { + match *op { + syntax::AssignmentOp::Equal => { + state.write("="); + } + syntax::AssignmentOp::Mult => { + state.write("*="); + } + syntax::AssignmentOp::Div => { + state.write("/="); + } + syntax::AssignmentOp::Mod => { + state.write("%="); + } + syntax::AssignmentOp::Add => { + state.write("+="); + } + syntax::AssignmentOp::Sub => { + state.write("-="); + } + syntax::AssignmentOp::LShift => { + state.write("<<="); + } + syntax::AssignmentOp::RShift => { + state.write(">>="); + } + syntax::AssignmentOp::And => { + state.write("&="); + } + syntax::AssignmentOp::Xor => { + state.write("^="); + } + syntax::AssignmentOp::Or => { + state.write("|="); + } + } +} + +pub fn show_function_identifier(state: &OutputState, i: &syntax::FunIdentifier) { + match *i { + syntax::FunIdentifier::Identifier(ref n) => show_identifier(state, &n), + syntax::FunIdentifier::Expr(ref e) => show_expr(state, &*e), + } +} + +pub fn show_hir_function_identifier(state: &OutputState, i: &hir::FunIdentifier) { + match *i { + hir::FunIdentifier::Identifier(ref n) => show_sym(state, n), + hir::FunIdentifier::Constructor(ref t) => show_type(state, &*t), + } +} + +pub fn show_declaration(state: &mut OutputState, d: &hir::Declaration) { + show_indent(state); + match *d { + hir::Declaration::FunctionPrototype(ref proto) => { + if !state.output_cxx { + show_function_prototype(state, &proto); + state.write(";\n"); + } + } + hir::Declaration::InitDeclaratorList(ref list) => { + show_init_declarator_list(state, &list); + state.write(";\n"); + + if state.output_cxx { + let base = list.head.name; + let base_sym = state.hir.sym(base); + if let hir::SymDecl::Local(..) = &base_sym.decl { + let mut texel_fetches = state.texel_fetches.borrow_mut(); + while let Some(idx) = texel_fetches.iter().position(|&(_, b, _)| b == base) + { + let (sampler, _, offsets) = texel_fetches.remove(idx); + let sampler_sym = state.hir.sym(sampler); + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } + } + } + } + hir::Declaration::Precision(ref qual, ref ty) => { + if !state.output_cxx { + show_precision_qualifier(state, &qual); + show_type_specifier(state, &ty); + state.write(";\n"); + } + } + hir::Declaration::Block(ref _block) => { + panic!(); + //show_block(state, &block); + //state.write(";\n"); + } + hir::Declaration::Global(ref qual, ref identifiers) => { + // We only want to output GLSL layout qualifiers if not C++ + if !state.output_cxx { + show_type_qualifier(state, &qual); + + if !identifiers.is_empty() { + let mut iter = identifiers.iter(); + let first = iter.next().unwrap(); + show_identifier(state, first); + + for identifier in iter { + let _ = write!(state, ", {}", identifier); + } + } + + state.write(";\n"); + } + } + hir::Declaration::StructDefinition(ref sym) => { + show_sym_decl(state, sym); + + state.write(";\n"); + } + } +} + +pub fn show_function_prototype(state: &mut OutputState, fp: &hir::FunctionPrototype) { + let is_scalar = state.is_scalar.replace(!state.return_vector); + show_type(state, &fp.ty); + state.is_scalar.set(is_scalar); + + state.write(" "); + show_identifier(state, &fp.name); + + state.write("("); + + if !fp.parameters.is_empty() { + let mut iter = fp.parameters.iter(); + let first = iter.next().unwrap(); + show_function_parameter_declaration(state, first); + + for param in iter { + state.write(", "); + show_function_parameter_declaration(state, param); + } + } + + if state.output_cxx && (state.vector_mask & (1 << 31)) != 0 { + if !fp.parameters.is_empty() { + state.write(", "); + } + state.write("I32 _cond_mask_"); + } + + state.write(")"); +} + +pub fn show_function_parameter_declaration( + state: &mut OutputState, + p: &hir::FunctionParameterDeclaration, +) { + match *p { + hir::FunctionParameterDeclaration::Named(ref qual, ref fpd) => { + if state.output_cxx { + let is_scalar = state.is_scalar.replace( + symbol_run_class(&state.hir.sym(fpd.sym).decl, state.vector_mask) + == hir::RunClass::Scalar, + ); + show_type(state, &fpd.ty); + state.is_scalar.set(is_scalar); + show_parameter_qualifier(state, qual); + } else { + show_parameter_qualifier(state, qual); + state.write(" "); + show_type(state, &fpd.ty); + } + state.write(" "); + show_identifier_and_type(state, &fpd.name, &fpd.ty); + } + hir::FunctionParameterDeclaration::Unnamed(ref qual, ref ty) => { + if state.output_cxx { + show_type_specifier(state, ty); + show_parameter_qualifier(state, qual); + } else { + show_parameter_qualifier(state, qual); + state.write(" "); + show_type_specifier(state, ty); + } + } + } +} + +pub fn show_init_declarator_list(state: &mut OutputState, i: &hir::InitDeclaratorList) { + show_single_declaration(state, &i.head); + + for decl in &i.tail { + state.write(", "); + show_single_declaration_no_type(state, decl); + } +} + +pub fn show_single_declaration(state: &mut OutputState, d: &hir::SingleDeclaration) { + if state.output_cxx { + show_single_declaration_cxx(state, d) + } else { + show_single_declaration_glsl(state, d) + } +} + +pub fn show_single_declaration_glsl(state: &mut OutputState, d: &hir::SingleDeclaration) { + if let Some(ref qual) = d.qualifier { + show_type_qualifier(state, &qual); + state.write(" "); + } + + let sym = state.hir.sym(d.name); + match &sym.decl { + hir::SymDecl::Global(storage, interpolation, ..) => { + show_storage_class(state, storage); + if let Some(i) = interpolation { + show_interpolation_qualifier(state, i); + } + } + hir::SymDecl::Local(storage, ..) => show_storage_class(state, storage), + _ => panic!("should be variable"), + } + + if let Some(ty_def) = d.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.ty); + } + + state.write(" "); + state.write(sym.name.as_str()); + + if let Some(ref arr_spec) = d.ty.array_sizes { + show_array_sizes(state, &arr_spec); + } + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +fn symbol_run_class(decl: &hir::SymDecl, vector_mask: u32) -> hir::RunClass { + let run_class = match decl { + hir::SymDecl::Global(_, _, _, run_class) => *run_class, + hir::SymDecl::Local(_, _, run_class) => *run_class, + _ => hir::RunClass::Vector, + }; + match run_class { + hir::RunClass::Scalar => hir::RunClass::Scalar, + hir::RunClass::Dependent(mask) => { + if (mask & vector_mask) != 0 { + hir::RunClass::Vector + } else { + hir::RunClass::Scalar + } + } + _ => hir::RunClass::Vector, + } +} + +pub fn show_single_declaration_cxx(state: &mut OutputState, d: &hir::SingleDeclaration) { + let sym = state.hir.sym(d.name); + if state.kind == ShaderKind::Vertex { + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Uniform, ..) | + hir::SymDecl::Global(hir::StorageClass::Sampler(_), ..) | + hir::SymDecl::Global(hir::StorageClass::Out, _, _, hir::RunClass::Scalar) => { + state.write("// "); + } + _ => {} + } + } else { + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::FragColor(index), ..) => { + let fragcolor = match index { + 0 => "gl_FragColor", + 1 => "gl_SecondaryFragColor", + _ => panic!(), + }; + write!(state, "#define {} {}\n", sym.name, fragcolor); + show_indent(state); + state.write("// "); + } + hir::SymDecl::Global(hir::StorageClass::Out, ..) => { + write!(state, "#define {} gl_FragColor\n", sym.name); + show_indent(state); + state.write("// "); + } + hir::SymDecl::Global(hir::StorageClass::Uniform, ..) | + hir::SymDecl::Global(hir::StorageClass::Sampler(_), ..) | + hir::SymDecl::Global(hir::StorageClass::In, _, _, hir::RunClass::Scalar) => { + state.write("// "); + } + _ => {} + } + } + let is_scalar = state + .is_scalar + .replace(symbol_run_class(&sym.decl, state.vector_mask) == hir::RunClass::Scalar); + + if let Some(ref _array) = d.ty.array_sizes { + show_type(state, &d.ty); + } else { + if let Some(ty_def) = d.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.ty); + } + } + + // XXX: this is pretty grotty + state.write(" "); + show_sym_decl(state, &d.name); + + state.is_scalar.set(is_scalar); + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +pub fn show_single_declaration_no_type(state: &OutputState, d: &hir::SingleDeclarationNoType) { + show_arrayed_identifier(state, &d.ident); + + if let Some(ref initializer) = d.initializer { + state.write(" = "); + show_initializer(state, initializer); + } +} + +pub fn show_initializer(state: &OutputState, i: &hir::Initializer) { + match *i { + hir::Initializer::Simple(ref e) => show_hir_expr(state, e), + hir::Initializer::List(ref list) => { + let mut iter = list.0.iter(); + let first = iter.next().unwrap(); + + state.write("{ "); + show_initializer(state, first); + + for ini in iter { + state.write(", "); + show_initializer(state, ini); + } + + state.write(" }"); + } + } +} + +/* +pub fn show_block(state: &mut OutputState, b: &hir::Block) { + show_type_qualifier(state, &b.qualifier); + state.write(" "); + show_identifier(state, &b.name); + state.write(" {"); + + for field in &b.fields { + show_struct_field(state, field); + state.write("\n"); + } + state.write("}"); + + if let Some(ref ident) = b.identifier { + show_arrayed_identifier(state, ident); + } +} +*/ + +// This is a hack to run through the first time with an empty writter to find if 'return' is declared. +pub fn has_conditional_return(state: &mut OutputState, cst: &hir::CompoundStatement) -> bool { + let buffer = state.push_buffer(); + show_compound_statement(state, cst); + state.pop_buffer(buffer); + let result = state.return_declared; + state.return_declared = false; + result +} + +fn define_texel_fetch_ptr( + state: &OutputState, + base_sym: &hir::Symbol, + sampler_sym: &hir::Symbol, + offsets: &hir::TexelFetchOffsets, +) { + show_indent(state); + write!( + state, + "auto {}_{}_fetch = texelFetchPtr({}, {}, {}, {}, {}, {});\n", + sampler_sym.name, + base_sym.name, + sampler_sym.name, + base_sym.name, + offsets.min_x, + offsets.max_x, + offsets.min_y, + offsets.max_y, + ); +} + +pub fn show_function_definition( + state: &mut OutputState, + fd: &hir::FunctionDefinition, + vector_mask: u32, +) { + // println!("start {:?} {:?}", fd.prototype.name, vector_mask); + if state.output_cxx && fd.prototype.name.as_str() == "main" { + state.write("ALWAYS_INLINE "); + } + show_function_prototype(state, &fd.prototype); + state.write(" "); + state.return_type = Some(Box::new(fd.prototype.ty.clone())); + + if state.output_cxx && (vector_mask & (1 << 31)) != 0 { + state.mask = Some(Box::new(hir::Expr { + kind: hir::ExprKind::CondMask, + ty: hir::Type::new(hir::TypeKind::Bool), + })); + } + + show_indent(state); + state.write("{\n"); + + state.indent(); + if has_conditional_return(state, &fd.body) { + show_indent(state); + state.write(if state.return_vector { + "I32" + } else { + "int32_t" + }); + state.write(" ret_mask = "); + if let Some(mask) = &state.mask { + show_hir_expr(state, mask); + } else { + state.write("~0"); + } + state.write(";\n"); + // XXX: the cloning here is bad + show_indent(state); + if fd.prototype.ty != Type::new(hir::TypeKind::Void) { + let is_scalar = state.is_scalar.replace(!state.return_vector); + show_type(state, &state.return_type.clone().unwrap()); + state.write(" ret;\n"); + state.is_scalar.set(is_scalar); + } + } + + if state.output_cxx { + match fd.prototype.name.as_str() { + "swgl_drawSpanRGBA8" | + "swgl_drawSpanR8" => { + // Partial spans are not drawn using span shaders, but rather drawn with a fragment shader + // where the span shader left off. We need to undo any changes to the interpolants made by + // the span shaders so that we can reset the interpolants to where the fragment shader + // expects them. We do this by saving them in an _Undo_ struct on entry to the span shader, + // and then restore them in the _Undo_ struct destructor. + let mut needs_undo = vec![]; + for global in &fd.globals { + let sym = state.hir.sym(*global); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::In, _, ty, hir::RunClass::Vector) => { + if needs_undo.is_empty() { + state.write("struct _Undo_ {\nSelf* self;\n"); + } + show_type(state, ty); + write!(state, " {};\n", sym.name); + needs_undo.push(sym.name.clone()); + } + _ => {} + } + } + if !needs_undo.is_empty() { + state.write("explicit _Undo_(Self* self) : self(self)"); + for name in &needs_undo { + write!(state, ", {0}(self->{0})", name); + } + state.write(" {}\n"); + state.write("~_Undo_() {\n"); + for name in &needs_undo { + write!(state, "self->{0} = {0};\n", name); + } + state.write("}} _undo_(this);\n"); + } + } + _ => {} + } + + let mut texel_fetches = state.texel_fetches.borrow_mut(); + texel_fetches.clear(); + for ((sampler, base), offsets) in fd.texel_fetches.iter() { + add_used_global(state, sampler); + let sampler_sym = state.hir.sym(*sampler); + let base_sym = state.hir.sym(*base); + match &base_sym.decl { + hir::SymDecl::Global(..) => { + add_used_global(state, base); + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } + hir::SymDecl::Local(..) => { + if fd.prototype.has_parameter(*base) { + define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + } else { + texel_fetches.push((*sampler, *base, offsets.clone())); + } + } + _ => panic!(), + } + } + } + + for st in &fd.body.statement_list { + show_statement(state, st); + } + + if state.return_declared { + show_indent(state); + if fd.prototype.ty == Type::new(hir::TypeKind::Void) { + state.write("return;\n"); + } else { + state.write("return ret;\n"); + } + } + state.outdent(); + + show_indent(state); + state.write("}\n"); + // println!("end {:?}", fd.prototype.name); + + state.return_type = None; + state.return_declared = false; + state.mask = None; +} + +pub fn show_compound_statement(state: &mut OutputState, cst: &hir::CompoundStatement) { + show_indent(state); + state.write("{\n"); + + state.indent(); + for st in &cst.statement_list { + show_statement(state, st); + } + state.outdent(); + + show_indent(state); + state.write("}\n"); +} + +pub fn show_statement(state: &mut OutputState, st: &hir::Statement) { + match *st { + hir::Statement::Compound(ref cst) => show_compound_statement(state, cst), + hir::Statement::Simple(ref sst) => show_simple_statement(state, sst), + } +} + +pub fn show_simple_statement(state: &mut OutputState, sst: &hir::SimpleStatement) { + match *sst { + hir::SimpleStatement::Declaration(ref d) => show_declaration(state, d), + hir::SimpleStatement::Expression(ref e) => show_expression_statement(state, e), + hir::SimpleStatement::Selection(ref s) => show_selection_statement(state, s), + hir::SimpleStatement::Switch(ref s) => show_switch_statement(state, s), + hir::SimpleStatement::Iteration(ref i) => show_iteration_statement(state, i), + hir::SimpleStatement::Jump(ref j) => show_jump_statement(state, j), + } +} + +pub fn show_indent(state: &OutputState) { + for _ in 0 .. state.indent { + state.write(" "); + } +} + +pub fn show_expression_statement(state: &mut OutputState, est: &hir::ExprStatement) { + show_indent(state); + + if let Some(ref e) = *est { + show_hir_expr_inner(state, e, true); + } + + state.write(";\n"); +} + +pub fn show_selection_statement(state: &mut OutputState, sst: &hir::SelectionStatement) { + show_indent(state); + + if state.output_cxx && + (state.return_declared || expr_run_class(state, &sst.cond) != hir::RunClass::Scalar) + { + let (cond_index, mask) = if state.mask.is_none() || sst.else_stmt.is_some() { + let cond = sst.cond.clone(); + state.cond_index += 1; + let cond_index = state.cond_index; + write!(state, "auto _c{}_ = ", cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + ( + cond_index, + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }), + ) + } else { + (0, sst.cond.clone()) + }; + + let previous = mem::replace(&mut state.mask, None); + state.mask = Some(match previous.clone() { + Some(e) => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::BitAnd, e, mask.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + state.cond_index += 1; + let nested_cond_index = state.cond_index; + show_indent(state); + write!(state, "auto _c{}_ = ", nested_cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(nested_cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }) + } + None => mask.clone(), + }); + + show_statement(state, &sst.body); + state.mask = previous; + + if let Some(rest) = &sst.else_stmt { + // invert the condition + let inverted_cond = Box::new(hir::Expr { + kind: hir::ExprKind::Unary(UnaryOp::Complement, mask), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + let previous = mem::replace(&mut state.mask, None); + state.mask = Some(match previous.clone() { + Some(e) => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::BitAnd, e, inverted_cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + show_indent(state); + write!(state, "_c{}_ = ", cond_index); + show_hir_expr(state, &cond); + state.write(";\n"); + Box::new(hir::Expr { + kind: hir::ExprKind::Cond(cond_index, cond), + ty: hir::Type::new(hir::TypeKind::Bool), + }) + } + None => inverted_cond, + }); + + show_statement(state, rest); + state.mask = previous; + } + } else { + state.write("if ("); + show_hir_expr(state, &sst.cond); + state.write(") {\n"); + + state.indent(); + show_statement(state, &sst.body); + state.outdent(); + + show_indent(state); + if let Some(rest) = &sst.else_stmt { + state.write("} else "); + show_statement(state, rest); + } else { + state.write("}\n"); + } + } +} + +fn case_stmts_to_if_stmts(stmts: &[Statement], last: bool) -> (Option<Box<Statement>>, bool) { + // Look for jump statements and remove them + // We currently are pretty strict on the form that the statement + // list needs to be in. This can be loosened as needed. + let mut fallthrough = false; + let cstmt = match &stmts[..] { + [hir::Statement::Compound(c)] => match c.statement_list.split_last() { + Some((hir::Statement::Simple(s), rest)) => match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: rest.to_owned(), + }, + _ => panic!("fall through not supported"), + }, + _ => panic!("empty compound"), + }, + [hir::Statement::Simple(s)] => { + match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: Vec::new(), + }, + _ => { + if last { + // we don't need a break at the end + hir::CompoundStatement { + statement_list: vec![hir::Statement::Simple(s.clone())], + } + } else { + panic!("fall through not supported {:?}", s) + } + } + } + } + [] => return (None, true), + stmts => match stmts.split_last() { + Some((hir::Statement::Simple(s), rest)) => match **s { + hir::SimpleStatement::Jump(hir::JumpStatement::Break) => hir::CompoundStatement { + statement_list: rest.to_owned(), + }, + _ => { + if !last { + fallthrough = true; + } + hir::CompoundStatement { + statement_list: stmts.to_owned(), + } + } + }, + _ => panic!("unexpected empty"), + }, + }; + let stmts = Box::new(hir::Statement::Compound(Box::new(cstmt))); + (Some(stmts), fallthrough) +} + +fn build_selection<'a, I: Iterator<Item = &'a hir::Case>>( + head: &Box<hir::Expr>, + case: &hir::Case, + mut cases: I, + default: Option<&hir::Case>, + previous_condition: Option<Box<hir::Expr>>, + previous_stmts: Option<Box<hir::Statement>>, +) -> hir::SelectionStatement { + let cond = match &case.label { + hir::CaseLabel::Case(e) => Some(Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::Equal, head.clone(), e.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + })), + hir::CaseLabel::Def => None, + }; + + // if we have two conditions join them + let cond = match (&previous_condition, &cond) { + (Some(prev), Some(cond)) => Some(Box::new(hir::Expr { + kind: hir::ExprKind::Binary(syntax::BinaryOp::Or, prev.clone(), cond.clone()), + ty: hir::Type::new(hir::TypeKind::Bool), + })), + (_, cond) => cond.clone(), + }; + + /* + + // find the next case that's not a default + let next_case = loop { + match cases.next() { + Some(hir::Case { label: hir::CaseLabel::Def, ..}) => { }, + case => break case, + } + };*/ + + let (cond, body, else_stmt) = match (cond, cases.next()) { + (None, Some(next_case)) => { + assert!(previous_stmts.is_none()); + // default so just move on to the next + return build_selection(head, next_case, cases, default, None, None); + } + (Some(cond), Some(next_case)) => { + assert!(previous_stmts.is_none()); + let (stmts, fallthrough) = case_stmts_to_if_stmts(&case.stmts, false); + if !fallthrough && stmts.is_some() { + ( + cond, + stmts.unwrap(), + Some(Box::new(hir::Statement::Simple(Box::new( + hir::SimpleStatement::Selection(build_selection( + head, next_case, cases, default, None, None, + )), + )))), + ) + } else { + // empty so fall through to the next + return build_selection(head, next_case, cases, default, Some(cond), stmts); + } + } + (Some(cond), None) => { + // non-default last + assert!(previous_stmts.is_none()); + let (stmts, _) = case_stmts_to_if_stmts(&case.stmts, default.is_none()); + let stmts = stmts.expect("empty case labels unsupported at the end"); + // add the default case at the end if we have one + ( + cond, + stmts, + match default { + Some(default) => { + let (default_stmts, fallthrough) = + case_stmts_to_if_stmts(&default.stmts, true); + assert!(!fallthrough); + Some(default_stmts.expect("empty default unsupported")) + } + None => None, + }, + ) + } + (None, None) => { + // default, last + + assert!(default.is_some()); + + let (stmts, fallthrough) = case_stmts_to_if_stmts(&case.stmts, true); + let stmts = stmts.expect("empty default unsupported"); + assert!(!fallthrough); + + match previous_stmts { + Some(previous_stmts) => { + let cond = previous_condition.expect("must have previous condition"); + (cond, previous_stmts, Some(stmts)) + } + None => { + let cond = Box::new(hir::Expr { + kind: hir::ExprKind::BoolConst(true), + ty: hir::Type::new(hir::TypeKind::Bool), + }); + (cond, stmts, None) + } + } + } + }; + + hir::SelectionStatement { + cond, + body, + else_stmt, + } +} + +pub fn lower_switch_to_ifs(sst: &hir::SwitchStatement) -> hir::SelectionStatement { + let default = sst.cases.iter().find(|x| x.label == hir::CaseLabel::Def); + let mut cases = sst.cases.iter(); + let r = build_selection(&sst.head, cases.next().unwrap(), cases, default, None, None); + r +} + +fn is_declaration(stmt: &hir::Statement) -> bool { + if let hir::Statement::Simple(s) = stmt { + if let hir::SimpleStatement::Declaration(..) = **s { + return true; + } + } + return false; +} + +pub fn show_switch_statement(state: &mut OutputState, sst: &hir::SwitchStatement) { + if state.output_cxx && expr_run_class(state, &sst.head) != hir::RunClass::Scalar { + // XXX: when lowering switches we end up with a mask that has + // a bunch of mutually exclusive conditions. + // It would be nice if we could fold them together. + let ifs = lower_switch_to_ifs(sst); + return show_selection_statement(state, &ifs); + } + + show_indent(state); + state.write("switch ("); + show_hir_expr(state, &sst.head); + state.write(") {\n"); + state.indent(); + + for case in &sst.cases { + show_case_label(state, &case.label); + state.indent(); + + let has_declaration = case.stmts.iter().any(|x| is_declaration(x)); + // glsl allows declarations in switch statements while C requires them to be + // in a compound statement. If we have a declaration wrap the statements in an block. + // This will break some glsl shaders but keeps the saner ones working + if has_declaration { + show_indent(state); + state.write("{\n"); + state.indent(); + } + for st in &case.stmts { + show_statement(state, st); + } + + if has_declaration { + show_indent(state); + state.write("}\n"); + state.outdent(); + } + + state.outdent(); + } + state.outdent(); + show_indent(state); + state.write("}\n"); +} + +pub fn show_case_label(state: &mut OutputState, cl: &hir::CaseLabel) { + show_indent(state); + match *cl { + hir::CaseLabel::Case(ref e) => { + state.write("case "); + show_hir_expr(state, e); + state.write(":\n"); + } + hir::CaseLabel::Def => { + state.write("default:\n"); + } + } +} + +pub fn show_iteration_statement(state: &mut OutputState, ist: &hir::IterationStatement) { + show_indent(state); + match *ist { + hir::IterationStatement::While(ref cond, ref body) => { + state.write("while ("); + show_condition(state, cond); + state.write(") "); + show_statement(state, body); + } + hir::IterationStatement::DoWhile(ref body, ref cond) => { + state.write("do "); + show_statement(state, body); + state.write(" while ("); + show_hir_expr(state, cond); + state.write(");\n"); + } + hir::IterationStatement::For(ref init, ref rest, ref body) => { + state.write("for ("); + show_for_init_statement(state, init); + show_for_rest_statement(state, rest); + state.write(") "); + show_statement(state, body); + } + } +} + +pub fn show_condition(state: &mut OutputState, c: &hir::Condition) { + match *c { + hir::Condition::Expr(ref e) => show_hir_expr(state, e), + /*hir::Condition::Assignment(ref ty, ref name, ref initializer) => { + show_type(state, ty); + state.write(" "); + show_identifier(f, name); + state.write(" = "); + show_initializer(state, initializer); + }*/ + } +} + +pub fn show_for_init_statement(state: &mut OutputState, i: &hir::ForInitStatement) { + match *i { + hir::ForInitStatement::Expression(ref expr) => { + if let Some(ref e) = *expr { + show_hir_expr(state, e); + } + } + hir::ForInitStatement::Declaration(ref d) => { + show_declaration(state, d); + } + } +} + +pub fn show_for_rest_statement(state: &mut OutputState, r: &hir::ForRestStatement) { + if let Some(ref cond) = r.condition { + show_condition(state, cond); + } + + state.write("; "); + + if let Some(ref e) = r.post_expr { + show_hir_expr(state, e); + } +} + +fn use_return_mask(state: &OutputState) -> bool { + if let Some(mask) = &state.mask { + mask.kind != hir::ExprKind::CondMask + } else { + false + } +} + +pub fn show_jump_statement(state: &mut OutputState, j: &hir::JumpStatement) { + show_indent(state); + match *j { + hir::JumpStatement::Continue => { + state.write("continue;\n"); + } + hir::JumpStatement::Break => { + state.write("break;\n"); + } + hir::JumpStatement::Discard => { + if state.output_cxx { + state.uses_discard = true; + if let Some(mask) = &state.mask { + state.write("swgl_IsPixelDiscarded |= ("); + show_hir_expr(state, mask); + state.write(")"); + if state.return_declared { + state.write("&ret_mask"); + } + state.write(";\n"); + } else { + state.write("swgl_IsPixelDiscarded = true;\n"); + } + } else { + state.write("discard;\n"); + } + } + hir::JumpStatement::Return(ref e) => { + if let Some(e) = e { + if state.output_cxx { + if use_return_mask(state) { + // We cast any conditions by `ret_mask_type` so that scalars nicely + // convert to -1. i.e. I32 &= bool will give the wrong result. while I32 &= I32(bool) works + let ret_mask_type = if state.return_vector { + "I32" + } else { + "int32_t" + }; + if state.return_declared { + // XXX: the cloning here is bad + write!(state, "ret = if_then_else(ret_mask & {}(", ret_mask_type); + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write("), "); + show_hir_expr(state, e); + state.write(", ret);\n"); + } else { + state.write("ret = "); + show_hir_expr(state, e); + state.write(";\n"); + } + + show_indent(state); + + if state.return_declared { + write!(state, "ret_mask &= ~{}(", ret_mask_type); + } else { + write!(state, "ret_mask = ~{}(", ret_mask_type); + } + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write(");\n"); + state.return_declared = true; + } else { + if state.return_declared { + state.write("ret = if_then_else(ret_mask, "); + show_hir_expr(state, e); + state.write(", ret);\n"); + } else { + state.write("return "); + show_hir_expr(state, e); + state.write(";\n"); + } + } + } else { + state.write("return "); + show_hir_expr(state, e); + state.write(";\n"); + } + } else { + if state.output_cxx { + if use_return_mask(state) { + show_indent(state); + let ret_mask_type = if state.return_vector { + "I32" + } else { + "int32_t" + }; + if state.return_declared { + write!(state, "ret_mask &= ~{}(", ret_mask_type); + } else { + write!(state, "ret_mask = ~{}(", ret_mask_type); + } + show_hir_expr(state, &state.mask.clone().unwrap()); + state.write(");\n"); + state.return_declared = true; + } else { + state.write("return;\n"); + } + } else { + state.write("return;\n"); + } + } + } + } +} + +pub fn show_path(state: &OutputState, path: &syntax::Path) { + match path { + syntax::Path::Absolute(s) => { + let _ = write!(state, "<{}>", s); + } + syntax::Path::Relative(s) => { + let _ = write!(state, "\"{}\"", s); + } + } +} + +pub fn show_preprocessor(state: &OutputState, pp: &syntax::Preprocessor) { + match *pp { + syntax::Preprocessor::Define(ref pd) => show_preprocessor_define(state, pd), + syntax::Preprocessor::Else => show_preprocessor_else(state), + syntax::Preprocessor::ElseIf(ref pei) => show_preprocessor_elseif(state, pei), + syntax::Preprocessor::EndIf => show_preprocessor_endif(state), + syntax::Preprocessor::Error(ref pe) => show_preprocessor_error(state, pe), + syntax::Preprocessor::If(ref pi) => show_preprocessor_if(state, pi), + syntax::Preprocessor::IfDef(ref pid) => show_preprocessor_ifdef(state, pid), + syntax::Preprocessor::IfNDef(ref pind) => show_preprocessor_ifndef(state, pind), + syntax::Preprocessor::Include(ref pi) => show_preprocessor_include(state, pi), + syntax::Preprocessor::Line(ref pl) => show_preprocessor_line(state, pl), + syntax::Preprocessor::Pragma(ref pp) => show_preprocessor_pragma(state, pp), + syntax::Preprocessor::Undef(ref pu) => show_preprocessor_undef(state, pu), + syntax::Preprocessor::Version(ref pv) => show_preprocessor_version(state, pv), + syntax::Preprocessor::Extension(ref pe) => show_preprocessor_extension(state, pe), + } +} + +pub fn show_preprocessor_define(state: &OutputState, pd: &syntax::PreprocessorDefine) { + match *pd { + syntax::PreprocessorDefine::ObjectLike { + ref ident, + ref value, + } => { + let _ = write!(state, "#define {} {}\n", ident, value); + } + + syntax::PreprocessorDefine::FunctionLike { + ref ident, + ref args, + ref value, + } => { + let _ = write!(state, "#define {}(", ident); + + if !args.is_empty() { + let _ = write!(state, "{}", &args[0]); + + for arg in &args[1 .. args.len()] { + let _ = write!(state, ", {}", arg); + } + } + + let _ = write!(state, ") {}\n", value); + } + } +} + +pub fn show_preprocessor_else(state: &OutputState) { + state.write("#else\n"); +} + +pub fn show_preprocessor_elseif(state: &OutputState, pei: &syntax::PreprocessorElseIf) { + let _ = write!(state, "#elseif {}\n", pei.condition); +} + +pub fn show_preprocessor_error(state: &OutputState, pe: &syntax::PreprocessorError) { + let _ = writeln!(state, "#error {}", pe.message); +} + +pub fn show_preprocessor_endif(state: &OutputState) { + state.write("#endif\n"); +} + +pub fn show_preprocessor_if(state: &OutputState, pi: &syntax::PreprocessorIf) { + let _ = write!(state, "#if {}\n", pi.condition); +} + +pub fn show_preprocessor_ifdef(state: &OutputState, pid: &syntax::PreprocessorIfDef) { + state.write("#ifdef "); + show_identifier(state, &pid.ident); + state.write("\n"); +} + +pub fn show_preprocessor_ifndef(state: &OutputState, pind: &syntax::PreprocessorIfNDef) { + state.write("#ifndef "); + show_identifier(state, &pind.ident); + state.write("\n"); +} + +pub fn show_preprocessor_include(state: &OutputState, pi: &syntax::PreprocessorInclude) { + state.write("#include "); + show_path(state, &pi.path); + state.write("\n"); +} + +pub fn show_preprocessor_line(state: &OutputState, pl: &syntax::PreprocessorLine) { + let _ = write!(state, "#line {}", pl.line); + if let Some(source_string_number) = pl.source_string_number { + let _ = write!(state, " {}", source_string_number); + } + state.write("\n"); +} + +pub fn show_preprocessor_pragma(state: &OutputState, pp: &syntax::PreprocessorPragma) { + let _ = writeln!(state, "#pragma {}", pp.command); +} + +pub fn show_preprocessor_undef(state: &OutputState, pud: &syntax::PreprocessorUndef) { + state.write("#undef "); + show_identifier(state, &pud.name); + state.write("\n"); +} + +pub fn show_preprocessor_version(state: &OutputState, pv: &syntax::PreprocessorVersion) { + let _ = write!(state, "#version {}", pv.version); + + if let Some(ref profile) = pv.profile { + match *profile { + syntax::PreprocessorVersionProfile::Core => { + state.write(" core"); + } + syntax::PreprocessorVersionProfile::Compatibility => { + state.write(" compatibility"); + } + syntax::PreprocessorVersionProfile::ES => { + state.write(" es"); + } + } + } + + state.write("\n"); +} + +pub fn show_preprocessor_extension(state: &OutputState, pe: &syntax::PreprocessorExtension) { + state.write("#extension "); + + match pe.name { + syntax::PreprocessorExtensionName::All => { + state.write("all"); + } + syntax::PreprocessorExtensionName::Specific(ref n) => { + state.write(n); + } + } + + if let Some(ref behavior) = pe.behavior { + match *behavior { + syntax::PreprocessorExtensionBehavior::Require => { + state.write(" : require"); + } + syntax::PreprocessorExtensionBehavior::Enable => { + state.write(" : enable"); + } + syntax::PreprocessorExtensionBehavior::Warn => { + state.write(" : warn"); + } + syntax::PreprocessorExtensionBehavior::Disable => { + state.write(" : disable"); + } + } + } + + state.write("\n"); +} + +pub fn show_external_declaration(state: &mut OutputState, ed: &hir::ExternalDeclaration) { + match *ed { + hir::ExternalDeclaration::Preprocessor(ref pp) => { + if !state.output_cxx { + show_preprocessor(state, pp) + } + } + hir::ExternalDeclaration::FunctionDefinition(ref fd) => { + if !state.output_cxx { + show_function_definition(state, fd, !0) + } + } + hir::ExternalDeclaration::Declaration(ref d) => show_declaration(state, d), + } +} + +pub fn show_cxx_function_definition(state: &mut OutputState, name: hir::SymRef, vector_mask: u32) { + if let Some((ref fd, run_class)) = state.hir.function_definition(name) { + state.vector_mask = vector_mask; + state.return_vector = (vector_mask & (1 << 31)) != 0 + || match run_class { + hir::RunClass::Scalar => false, + hir::RunClass::Dependent(mask) => (mask & vector_mask) != 0, + _ => true, + }; + match state.functions.get(&(name, vector_mask)) { + Some(true) => {} + Some(false) => { + show_function_prototype(state, &fd.prototype); + state.functions.insert((name, vector_mask), true); + } + None => { + state.functions.insert((name, vector_mask), false); + let buffer = state.push_buffer(); + show_function_definition(state, fd, vector_mask); + for (name, vector_mask) in state.deps.replace(Vec::new()) { + show_cxx_function_definition(state, name, vector_mask); + } + state.flush_buffer(); + state.pop_buffer(buffer); + state.functions.insert((name, vector_mask), true); + } + } + } +} + +pub fn show_translation_unit(state: &mut OutputState, tu: &hir::TranslationUnit) { + state.flush_buffer(); + + for ed in &(tu.0).0 { + show_external_declaration(state, ed); + state.flush_buffer(); + } + if state.output_cxx { + for name in &["main", "swgl_drawSpanRGBA8", "swgl_drawSpanR8"] { + if let Some(sym) = state.hir.lookup(name) { + show_cxx_function_definition(state, sym, 0); + state.flush_buffer(); + } + } + } +} + +fn write_abi(state: &mut OutputState) { + match state.kind { + ShaderKind::Fragment => { + state.write("static void run(FragmentShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + if state.uses_discard { + state.write(" self->swgl_IsPixelDiscarded = false;\n"); + } + state.write(" self->main();\n"); + state.write(" self->step_interp_inputs();\n"); + state.write("}\n"); + state.write("static void skip(FragmentShaderImpl* impl, int steps) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + state.write(" self->step_interp_inputs(steps);\n"); + state.write("}\n"); + if state.use_perspective { + state.write("static void run_perspective(FragmentShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + if state.uses_discard { + state.write(" self->swgl_IsPixelDiscarded = false;\n"); + } + state.write(" self->main();\n"); + state.write(" self->step_perspective_inputs();\n"); + state.write("}\n"); + state.write("static void skip_perspective(FragmentShaderImpl* impl, int steps) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + state.write(" self->step_perspective_inputs(steps);\n"); + state.write("}\n"); + } + if state.hir.lookup("swgl_drawSpanRGBA8").is_some() { + state.write("static int draw_span_RGBA8(FragmentShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl; DISPATCH_DRAW_SPAN(self, RGBA8); }\n"); + } + if state.hir.lookup("swgl_drawSpanR8").is_some() { + state.write("static int draw_span_R8(FragmentShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl; DISPATCH_DRAW_SPAN(self, R8); }\n"); + } + + write!(state, "public:\n{}_frag() {{\n", state.name); + } + ShaderKind::Vertex => { + state.write("static void run(VertexShaderImpl* impl, char* interps, size_t interp_stride) {\n"); + state.write(" Self* self = (Self*)impl;\n"); + state.write(" self->main();\n"); + state.write(" self->store_interp_outputs(interps, interp_stride);\n"); + state.write("}\n"); + state.write("static void init_batch(VertexShaderImpl* impl) {\n"); + state.write(" Self* self = (Self*)impl; self->bind_textures(); }\n"); + + write!(state, "public:\n{}_vert() {{\n", state.name); + } + } + match state.kind { + ShaderKind::Fragment => { + state.write(" init_span_func = &read_interp_inputs;\n"); + state.write(" run_func = &run;\n"); + state.write(" skip_func = &skip;\n"); + if state.hir.lookup("swgl_drawSpanRGBA8").is_some() { + state.write(" draw_span_RGBA8_func = &draw_span_RGBA8;\n"); + } + if state.hir.lookup("swgl_drawSpanR8").is_some() { + state.write(" draw_span_R8_func = &draw_span_R8;\n"); + } + if state.uses_discard { + state.write(" enable_discard();\n"); + } + if state.use_perspective { + state.write(" enable_perspective();\n"); + state.write(" init_span_w_func = &read_perspective_inputs;\n"); + state.write(" run_w_func = &run_perspective;\n"); + state.write(" skip_w_func = &skip_perspective;\n"); + } else { + state.write(" init_span_w_func = &read_interp_inputs;\n"); + state.write(" run_w_func = &run;\n"); + state.write(" skip_w_func = &skip;\n"); + } + } + ShaderKind::Vertex => { + state.write(" set_uniform_1i_func = &set_uniform_1i;\n"); + state.write(" set_uniform_4fv_func = &set_uniform_4fv;\n"); + state.write(" set_uniform_matrix4fv_func = &set_uniform_matrix4fv;\n"); + state.write(" init_batch_func = &init_batch;\n"); + state.write(" load_attribs_func = &load_attribs;\n"); + state.write(" run_primitive_func = &run;\n"); + if state.hir.used_clip_dist != 0 { + state.write(" enable_clip_distance();\n"); + } + } + } + state.write("}\n"); +} + +pub fn define_global_consts(state: &mut OutputState, tu: &hir::TranslationUnit, part_name: &str) { + for i in tu { + match i { + hir::ExternalDeclaration::Declaration(hir::Declaration::InitDeclaratorList(ref d)) => { + let sym = state.hir.sym(d.head.name); + match &sym.decl { + hir::SymDecl::Global(hir::StorageClass::Const, ..) => { + let is_scalar = state.is_scalar.replace( + symbol_run_class(&sym.decl, state.vector_mask) == hir::RunClass::Scalar, + ); + if let Some(ref _array) = d.head.ty.array_sizes { + show_type(state, &d.head.ty); + } else { + if let Some(ty_def) = d.head.ty_def { + show_sym_decl(state, &ty_def); + } else { + show_type(state, &d.head.ty); + } + } + write!(state, " constexpr {}::{};\n", part_name, sym.name); + state.is_scalar.set(is_scalar); + } + _ => {} + } + } + _ => {} + } + } +} |