mod metal;
#[derive(Debug)]
pub struct Error {
pub error: String,
pub line: Option<u32>,
}
#[derive(Default, Debug)]
pub struct GlslOutput {
pub vertex: String,
pub fragment: String,
}
#[derive(Default)]
pub struct Output {
pub v100: GlslOutput,
pub v100_webgl: GlslOutput,
pub v130: GlslOutput,
pub v330: GlslOutput,
pub v300es: GlslOutput,
pub metal: String,
}
#[derive(Default, Debug, PartialEq)]
pub struct Options {
pub precision: String,
pub metal_flip_y: bool,
pub defines: Vec<String>,
}
enum ShaderKind {
Vertex,
Fragment,
}
fn lower_gl_missing_math() -> &'static str {
r#"mat3 transpose(mat3 m) {
return mat3(vec3(m[0].x, m[1].x, m[2].x),
vec3(m[0].y, m[1].y, m[2].y),
vec3(m[0].z, m[1].z, m[2].z));
}
"#
}
fn glsl_v100(input: &str, _kind: ShaderKind, defines: &[String]) -> String {
let mut processed = String::new();
processed.push_str("#version 100\n");
processed.push_str("precision mediump float;\n");
processed.push_str("float dFdx(float x) {return 0.0;}\n");
processed.push_str("float dFdy(float x) {return 0.0;}\n");
processed.push_str("vec2 dFdx(vec2 x) {return vec2(0.0);}\n");
processed.push_str("vec2 dFdy(vec2 x) {return vec2(0.0);}\n");
processed.push_str("vec3 dFdx(vec3 x) {return vec3(0.0);}\n");
processed.push_str("vec3 dFdy(vec3 x) {return vec3(0.0);}\n");
processed.push_str("#define NO_DERIVATIVES 1\n");
processed.push_str("#define textureCubeLod(x, y, z) textureCube(x, y)\n");
processed.push_str("#define sm_level(x) x\n");
processed.push_str(lower_gl_missing_math());
for define in defines {
processed.push_str(&format!("#define {} 1\n", define));
}
processed.push_str("#define __GL 1\n");
for line in input.lines() {
processed.push_str(&line);
processed.push('\n');
}
processed
}
fn glsl_v100_webgl(input: &str, _kind: ShaderKind, defines: &[String]) -> String {
let mut processed = String::new();
processed.push_str("#version 100\n");
processed.push_str("#extension GL_EXT_shader_texture_lod: enable\n");
processed.push_str("#extension GL_OES_standard_derivatives: enable\n");
processed.push_str("precision mediump float;\n");
processed.push_str(lower_gl_missing_math());
for define in defines {
processed.push_str(&format!("#define {} 1\n", define));
}
processed.push_str("#define __GL 1\n");
processed.push_str("#define sm_level(x) x\n");
for line in input.lines() {
let line = line.replace("textureCubeLod", "textureCubeLodEXT");
processed.push_str(&line);
processed.push('\n');
}
processed
}
fn glsl_v130(input: &str, _kind: ShaderKind, defines: &[String]) -> String {
let mut processed = String::new();
processed.push_str("#version 130\n");
processed.push_str("#define sm_level(x) x\n");
processed.push_str(lower_gl_missing_math());
for define in defines {
processed.push_str(&format!("#define {} 1\n", define));
}
processed.push_str("#define __GL 1\n");
for line in input.lines() {
processed.push_str(&line);
processed.push('\n');
}
processed
}
fn glsl_v330(input: &str, kind: ShaderKind, defines: &[String]) -> String {
let mut processed = String::new();
processed.push_str("#version 330\n");
for define in defines {
processed.push_str(&format!("#define {} 1\n", define));
}
processed.push_str("#define __GL 1\n");
if let ShaderKind::Fragment = kind {
processed.push_str("out vec4 output_FragColor;\n");
}
processed.push_str("#define sm_level(x) x\n");
for line in input.lines() {
let line = line
.replace("attribute", "in")
.replace("texture2D", "texture")
.replace(
"varying",
match kind {
ShaderKind::Vertex => "out",
ShaderKind::Fragment => "in",
},
)
.replace("textureCube", "texture")
.replace("gl_FragColor", "output_FragColor");
processed.push_str(&line);
processed.push('\n');
}
processed
}
fn glsl_v300es(input: &str, kind: ShaderKind, defines: &[String]) -> String {
let mut processed = String::new();
processed.push_str("#version 300 es\n");
processed.push_str("precision mediump float;\n");
for define in defines {
processed.push_str(&format!("#define {} 1\n", define));
}
processed.push_str("#define __GL 1\n");
if let ShaderKind::Fragment = kind {
processed.push_str("out vec4 output_FragColor;\n");
}
processed.push_str("#define sm_level(x) x\n");
for line in input.lines() {
let line = line
.replace("attribute", "in")
.replace("texture2D", "texture")
.replace(
"varying",
match kind {
ShaderKind::Vertex => "out",
ShaderKind::Fragment => "in",
},
)
.replace("textureCube", "texture")
.replace("gl_FragColor", "output_FragColor");
processed.push_str(&line);
processed.push('\n');
}
processed
}
pub fn transform(
fragment: &str,
vertex: &str,
meta: &miniquad::ShaderMeta,
options: &Options,
) -> Result<Output, Error> {
let mut output = Output::default();
output.v100 = GlslOutput {
fragment: glsl_v100(fragment, ShaderKind::Fragment, &options.defines),
vertex: glsl_v100(vertex, ShaderKind::Vertex, &options.defines),
};
output.v130 = GlslOutput {
fragment: glsl_v130(fragment, ShaderKind::Fragment, &options.defines),
vertex: glsl_v130(vertex, ShaderKind::Vertex, &options.defines),
};
output.v100_webgl = GlslOutput {
fragment: glsl_v100_webgl(fragment, ShaderKind::Fragment, &options.defines),
vertex: glsl_v100_webgl(vertex, ShaderKind::Vertex, &options.defines),
};
output.v330 = GlslOutput {
fragment: glsl_v330(fragment, ShaderKind::Fragment, &options.defines),
vertex: glsl_v330(vertex, ShaderKind::Vertex, &options.defines),
};
output.v300es = GlslOutput {
fragment: glsl_v300es(fragment, ShaderKind::Fragment, &options.defines),
vertex: glsl_v300es(vertex, ShaderKind::Vertex, &options.defines),
};
output.metal = metal::metal(fragment, vertex, meta, &options);
Ok(output)
}
pub fn choose_appropriate_shader<'a>(
shader: &'a Output,
context_info: &miniquad::ContextInfo,
) -> miniquad::ShaderSource<'a> {
use miniquad::{Backend, ShaderSource};
match context_info.backend {
Backend::OpenGl => {
if context_info.glsl_support.v300es {
ShaderSource::Glsl {
vertex: &shader.v300es.vertex,
fragment: &shader.v300es.fragment,
}
} else if context_info.glsl_support.v330 {
ShaderSource::Glsl {
vertex: &shader.v330.vertex,
fragment: &shader.v330.fragment,
}
} else if context_info.glsl_support.v130 {
ShaderSource::Glsl {
vertex: &shader.v130.vertex,
fragment: &shader.v130.fragment,
}
} else if context_info.glsl_support.v100_ext {
ShaderSource::Glsl {
vertex: &shader.v100_webgl.vertex,
fragment: &shader.v100_webgl.fragment,
}
} else {
ShaderSource::Glsl {
vertex: &shader.v100.vertex,
fragment: &shader.v100.fragment,
}
}
}
Backend::Metal => ShaderSource::Msl {
program: &shader.metal,
},
}
}