/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
externcrate webrender_build;
use std::borrow::Cow; use std::env; use std::fs::{canonicalize, read_dir, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; use webrender_build::shader::*; use webrender_build::shader_features::{ShaderFeatureFlags, get_shader_features};
// glsopt is known to leak, but we don't particularly care. #[no_mangle] pubextern"C"fn __lsan_default_options() -> *const u8 {
b"detect_leaks=0\0".as_ptr()
}
/// Compute the shader path for insertion into the include_str!() macro. /// This makes for more compact generated code than inserting the literal /// shader source into the generated file. /// /// If someone is building on a network share, I'm sorry. fn escape_include_path(path: &Path) -> String { let full_path = canonicalize(path).unwrap(); let full_name = full_path.as_os_str().to_str().unwrap(); let full_name = full_name.replace("\\\\?\\", ""); let full_name = full_name.replace("\\", "/");
// Sort the file list so that the shaders.rs file is filled // deterministically.
glsl_files.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
for glsl in glsl_files { // Compute the shader name.
assert!(glsl.is_file()); let shader_name = glsl.file_name().unwrap().to_str().unwrap(); let shader_name = shader_name.replace(".glsl", "");
// Compute a digest of the #include-expanded shader source. We store // this as a literal alongside the source string so that we don't need // to hash large strings at runtime. letmut hasher = DefaultHasher::new(); let base = glsl.parent().unwrap();
assert!(base.is_dir());
ShaderSourceParser::new().parse(
Cow::Owned(shader_source_from_file(&glsl)),
&|f| Cow::Owned(shader_source_from_file(&base.join(&format!("{}.glsl", f)))),
&mut |s| hasher.write(s.as_bytes()),
); let digest: ProgramSourceDigest = hasher.into();
/// Prepends the line number to each line of a shader source. fn enumerate_shader_source_lines(shader_src: &str) -> String { // For some reason the glsl-opt errors are offset by 1 compared // to the provided shader source string. letmut out = format!("0\t|"); for (n, line) in shader_src.split('\n').enumerate() { let line_number = n + 1;
out.push_str(&format!("{}\t|{}\n", line_number, line));
}
out
}
// The full set of optimized shaders can be quite large, so only optimize // for the GL version we expect to be used on the target platform. If a different GL // version is used we will simply fall back to the unoptimized shaders. let shader_versions = match env::var("CARGO_CFG_TARGET_OS").as_ref().map(|s| &**s) {
Ok("android") | Ok("windows") => [ShaderVersion::Gles],
_ => [ShaderVersion::Gl],
};
letmut shaders = Vec::default(); for &gl_version in &shader_versions { letmut flags = ShaderFeatureFlags::all(); if gl_version != ShaderVersion::Gl {
flags.remove(ShaderFeatureFlags::GL);
} if gl_version != ShaderVersion::Gles {
flags.remove(ShaderFeatureFlags::GLES);
flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL);
} if !matches!(
env::var("CARGO_CFG_TARGET_OS").as_ref().map(|s| &**s),
Ok("android")
) {
flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1);
} // The optimizer cannot handle the required EXT_YUV_target extension
flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL_BT709);
flags.remove(ShaderFeatureFlags::DITHERING);
for (shader_name, configs) in get_shader_features(flags) { for config in configs {
shaders.push(ShaderOptimizationInput {
shader_name,
config,
gl_version,
});
}
}
}
let outputs = build_parallel::compile_objects::<_, _, ShaderOptimizationError, _>(
&|shader: &ShaderOptimizationInput| {
println!("Optimizing shader {:?}", shader); let target = match shader.gl_version {
ShaderVersion::Gl => glslopt::Target::OpenGl,
ShaderVersion::Gles => glslopt::Target::OpenGles30,
}; let glslopt_ctx = glslopt::Context::new(target);
let features = shader
.config
.split(",")
.filter(|f| !f.is_empty())
.collect::<Vec<_>>();
let full_shader_name = if shader.config.is_empty() {
shader.shader_name.to_string()
} else {
format!("{}_{}", shader.shader_name, shader.config.replace(",", "_"))
};
// Compute a digest of the optimized shader sources. We store this // as a literal alongside the source string so that we don't need // to hash large strings at runtime. letmut hasher = DefaultHasher::new();
let [vert_file_path, frag_file_path] = [
(glslopt::ShaderType::Vertex, vert_src, "vert"),
(glslopt::ShaderType::Fragment, frag_src, "frag"),
]
.map(|(shader_type, shader_src, extension)| { let output = glslopt_ctx.optimize(shader_type, shader_src.clone()); if !output.get_status() { let source = enumerate_shader_source_lines(&shader_src); return Err(ShaderOptimizationError {
shader: shader.clone(),
message: format!("{}\n{}", source, output.get_log()),
});
}
match outputs {
Ok(mut outputs) => { // Sort the shader list so that the shaders.rs file is filled // deterministically.
outputs.sort_by(|a, b| {
(a.gl_version, a.full_shader_name.clone())
.cmp(&(b.gl_version, b.full_shader_name.clone()))
});
fn write_optimized_shader_file(
path: &Path,
source: &str,
shader_name: &str,
features: &[&str],
hasher: &mut DefaultHasher,
) { letmut file = File::create(&path).unwrap(); for (line_number, line) in source.lines().enumerate() { // We embed the shader name and features as a comment in the // source to make debugging easier. // The #version directive must be on the first line so we insert // the extra information on the next line. if line_number == 1 { let prelude = format!("// {}\n// features: {:?}\n\n", shader_name, features);
file.write_all(prelude.as_bytes()).unwrap();
hasher.write(prelude.as_bytes());
}
file.write_all(line.as_bytes()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
hasher.write(line.as_bytes());
hasher.write("\n".as_bytes());
}
}
fn main() -> Result<(), std::io::Error> { let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());
let shaders_file_path = Path::new(&out_dir).join("shaders.rs"); letmut glsl_files = vec![];
println!("cargo:rerun-if-changed=res"); let res_dir = Path::new("res"); for entry in read_dir(res_dir)? { let entry = entry?; let path = entry.path();
if entry.file_name().to_str().unwrap().ends_with(".glsl") {
println!("cargo:rerun-if-changed={}", path.display());
glsl_files.push(path.to_owned());
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.