262 lines
7.7 KiB
Rust
262 lines
7.7 KiB
Rust
use std::borrow::Borrow;
|
|
|
|
use super::{
|
|
snippets::{merge_includes, CodeType, Snippet, Variable},
|
|
CodeComponent,
|
|
};
|
|
use crate::{
|
|
errors::{Error, Result},
|
|
utils::{find_file, Code, CodeBlock},
|
|
};
|
|
use glow::HasContext;
|
|
use log::{info, warn};
|
|
pub use utils::fetchcode;
|
|
pub type ShaderType = u32;
|
|
#[derive(Debug)]
|
|
pub struct Shader {
|
|
target: u32,
|
|
#[cfg(feature = "inparser")]
|
|
parsed: CodeBlock,
|
|
parsed: String,
|
|
#[cfg(feature = "inparser")]
|
|
pub hooks: Vec<String>,
|
|
}
|
|
|
|
impl std::fmt::Display for Shader {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.parsed)
|
|
}
|
|
}
|
|
|
|
impl Shader {
|
|
pub fn new(target: ShaderType, code: CodeType) -> Result<Self> {
|
|
let code = match code {
|
|
CodeType::Code(code) => code,
|
|
CodeType::Path(path) => {
|
|
let code = find_file(&path).expect(&format!(
|
|
"Failed to find file {}",
|
|
&path.as_os_str().to_str().unwrap()
|
|
));
|
|
code
|
|
}
|
|
};
|
|
|
|
#[cfg(feature = "inparser")]
|
|
{
|
|
let code = merge_includes(code).map_err(|e| Error::InvalidSnippet(e.to_string()))?;
|
|
let parsed = CodeBlock::new(&code)?;
|
|
let hooks = parsed.all_hooks().iter().map(|h| h.name.clone()).collect();
|
|
info!("Shader:{} Hooks: {:?}", target, hooks);
|
|
return Ok(Self {
|
|
target,
|
|
parsed,
|
|
hooks,
|
|
});
|
|
}
|
|
|
|
Ok(Self {
|
|
target,
|
|
parsed: code,
|
|
})
|
|
}
|
|
|
|
pub fn compile(
|
|
&self,
|
|
version: &str,
|
|
gl: &glow::Context,
|
|
) -> <glow::Context as HasContext>::Shader {
|
|
let shader = unsafe {
|
|
let shader = gl.create_shader(self.target).expect("Cannot create shader");
|
|
gl.shader_source(
|
|
shader,
|
|
&format!("{}\n{}", &format!("#version {}", version), self.to_string()),
|
|
);
|
|
gl.compile_shader(shader);
|
|
if !gl.get_shader_compile_status(shader) {
|
|
panic!("{}", gl.get_shader_info_log(shader))
|
|
}
|
|
shader
|
|
};
|
|
shader
|
|
}
|
|
|
|
#[cfg(feature = "inparser")]
|
|
pub fn define(&mut self, s: &str) {
|
|
self.parsed
|
|
.insert(Code::new(&format!("#define {}\n", s)), 0);
|
|
}
|
|
|
|
#[cfg(feature = "inparser")]
|
|
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
|
|
self.parsed.set_hook(hook, code);
|
|
}
|
|
|
|
pub fn target(&self) -> u32 {
|
|
self.target
|
|
}
|
|
}
|
|
|
|
impl CodeComponent for Shader {
|
|
fn add_snippet_after(self, snippet: Snippet) -> Self {
|
|
let new_code = self.to_string() + &snippet.to_string();
|
|
let result = Self::new(self.target, CodeType::Code(new_code)).unwrap();
|
|
result
|
|
}
|
|
|
|
fn add_snippet_before(self, snippet: Snippet) -> Self {
|
|
let new_code = snippet.to_string() + &self.to_string();
|
|
let result = Self::new(self.target, CodeType::Code(new_code)).unwrap();
|
|
result
|
|
}
|
|
}
|
|
|
|
mod utils {
|
|
use once_cell::sync::Lazy;
|
|
use std::collections::HashMap;
|
|
|
|
use crate::{
|
|
components::{CodeType, Snippet},
|
|
utils::CodeBlock,
|
|
};
|
|
|
|
static TYPES: Lazy<HashMap<usize, &'static str>> = Lazy::new(|| {
|
|
[
|
|
(1, "float"),
|
|
(2, "vec2 "),
|
|
(3, "vec3 "),
|
|
(4, "vec4 "),
|
|
(9, "mat3 "),
|
|
(16, "mat4 "),
|
|
]
|
|
.iter()
|
|
.cloned()
|
|
.collect::<std::collections::HashMap<_, _>>()
|
|
});
|
|
|
|
pub fn fetchcode<'a, V: AsRef<[(&'a str, usize)]>>(data_types: V, prefix: &str) -> Snippet {
|
|
let mut header = String::from(
|
|
"
|
|
uniform sampler2D uniforms;
|
|
uniform vec3 uniforms_shape;
|
|
layout(location = 0) in float collection_index;\n
|
|
",
|
|
);
|
|
|
|
for data_type in data_types.as_ref().iter() {
|
|
if data_type.0 != "__unused__" {
|
|
if let Some(type_name) = TYPES.get(&data_type.1) {
|
|
header.push_str(&format!("out {} {}{};\n", type_name, prefix, data_type.0));
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut body = String::from(
|
|
"
|
|
void fetch_uniforms() {
|
|
float rows = uniforms_shape.x;
|
|
float cols = uniforms_shape.y;
|
|
float count = uniforms_shape.z;
|
|
float index = collection_index;
|
|
int index_x = int(mod(index, (floor(cols/(count/4.0))))) * int(count/4.0);
|
|
int index_y = int(floor(index / (floor(cols/(count/4.0)))));
|
|
float size_x = cols - 1.0;
|
|
float size_y = rows - 1.0;
|
|
float ty = 0.0;
|
|
if (size_y > 0.0)
|
|
ty = float(index_y)/size_y;
|
|
int i = index_x;
|
|
vec4 _uniform;
|
|
",
|
|
);
|
|
|
|
for data_type in data_types.as_ref().iter() {
|
|
if data_type.0 != "__unused__" {
|
|
let component_count = data_type.1;
|
|
let mut store = 0;
|
|
let mut shift = 0;
|
|
let mut count = component_count;
|
|
while count > 0 {
|
|
if store == 0 {
|
|
body.push_str(&format!(
|
|
"\n _uniform = texture2D(uniforms, vec2(float(i++)/size_x,ty));\n"
|
|
));
|
|
store = 4;
|
|
}
|
|
let (a, b) = match store {
|
|
4 => (
|
|
"xyzw",
|
|
match shift {
|
|
0 => "xyzw",
|
|
1 => "yzw",
|
|
2 => "zw",
|
|
3 => "w",
|
|
_ => "",
|
|
},
|
|
),
|
|
3 => (
|
|
"yzw",
|
|
match shift {
|
|
0 => "yzw",
|
|
1 => "zw",
|
|
2 => "w",
|
|
_ => "",
|
|
},
|
|
),
|
|
2 => (
|
|
"zw",
|
|
match shift {
|
|
0 => "zw",
|
|
1 => "w",
|
|
_ => "",
|
|
},
|
|
),
|
|
1 => ("w", "w"),
|
|
_ => ("", ""),
|
|
};
|
|
let min_length = std::cmp::min(b.len(), count);
|
|
body.push_str(&format!(
|
|
" {}{}{} = _uniform.{};\n",
|
|
prefix,
|
|
data_type.0,
|
|
&b[0..min_length],
|
|
&a[0..min_length]
|
|
));
|
|
count -= min_length;
|
|
shift += min_length;
|
|
store -= min_length;
|
|
}
|
|
}
|
|
}
|
|
|
|
body.push_str("}\n\n");
|
|
let input = header + &body;
|
|
|
|
let result =
|
|
Snippet::new("fetch_uniform", CodeType::from_code(input), false, None).unwrap();
|
|
|
|
result
|
|
}
|
|
}
|
|
|
|
mod test {
|
|
use super::utils::fetchcode;
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_shader() {
|
|
let shader = Shader::new(
|
|
glow::VERTEX_SHADER,
|
|
CodeType::from_path("agg-fast-path.vert"),
|
|
)
|
|
.unwrap();
|
|
|
|
println!("{}", shader.parsed);
|
|
}
|
|
|
|
#[test]
|
|
fn test_fetchcode() {
|
|
let code = fetchcode([("position", 3), ("color", 4)], "a_");
|
|
println!("{}", code);
|
|
}
|
|
}
|