/* 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; 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) -> 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(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, should_indent: bool, output_cxx: bool, indent: i32, mask: Option>, cond_index: usize, return_type: Option>, return_declared: bool, return_vector: bool, is_scalar: Cell, is_lval: Cell, name: String, kind: ShaderKind, functions: HashMap<(hir::SymRef, u32), bool>, deps: RefCell>, vector_mask: u32, uses_discard: bool, used_fragcoord: Cell, use_perspective: bool, used_globals: RefCell>, texel_fetches: RefCell>, } 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) { 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>, 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>( head: &Box, case: &hir::Case, mut cases: I, default: Option<&hir::Case>, previous_condition: Option>, previous_stmts: Option>, ) -> 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); } _ => {} } } _ => {} } } }