Use shaderc instead of glsl-to-spirv for shader compilation (#947)

* Use shaderc instead of glsl-to-spirv for shader compilation
* added some setup notes
This commit is contained in:
Nicholas Lordello 2018-09-20 16:24:24 +02:00 committed by Lucas Kent
parent e3bfe4270c
commit b507034df7
19 changed files with 64 additions and 257 deletions

View File

@ -13,7 +13,6 @@ stages:
job1:
stage: test
script:
- cargo test -v --manifest-path glsl-to-spirv/Cargo.toml
- cargo test -v --manifest-path vulkano-shaders/Cargo.toml
- cargo test --no-run -v --manifest-path vulkano/Cargo.toml

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "glsl-to-spirv/glslang"]
path = glsl-to-spirv/glslang
url = https://github.com/KhronosGroup/glslang

View File

@ -22,7 +22,7 @@ addons:
- cmake-data
script:
- cargo test --all -j 1
- travis_wait cargo test --all -j 1
- cd examples
- cargo build
- cd .. # this is very important or else the below `cargo publish` will fail
@ -40,10 +40,6 @@ after_success:
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
cargo publish --token ${CRATESIO_TOKEN} --manifest-path vulkano-win/Cargo.toml
- |
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
cargo publish --token ${CRATESIO_TOKEN} --manifest-path glsl-to-spirv/Cargo.toml
- |
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&

View File

@ -5,6 +5,8 @@
- Fix instance_count when using draw_index with instance buffers
- Added a `reinterpret` function to `BufferSlice`
- Use [google/shaderc](https://github.com/google/shaderc-rs) for shader compilation
# Version 0.10.0 (2018-08-10)
- Use dynamically loaded `libvulkan` like on other platforms instead of linking to MoltenVK on macOS

View File

@ -1,7 +1,6 @@
[workspace]
members = [
"examples",
"glsl-to-spirv",
"vk-sys",
"vulkano",
"vulkano-shaders",

View File

@ -51,7 +51,17 @@ To get started you are encouraged to use the following resources:
examples in the repo and also a list of projects that use vulkano.
* [docs.rs](https://docs.rs/vulkano) - Full Vulkano API documentation
## macOS and iOS Setup
## Setup
Vulkano uses [shaderc-rs](https://github.com/google/shaderc-rs) for shader compilation. In order to
build the shaderc-rs crate the following tools must be installed and available on `PATH`:
- [CMake](https://cmake.org/)
- [Python](https://www.python.org/) (works with both Python 2.x and 3.x)
These requirements can be either installed with your favourite package manager or with installers
from the projects' websites.
### macOS and iOS Specific Setup
Vulkan is not natively supported by macOS and iOS. However, there exists [MoltenVK](https://github.com/KhronosGroup/MoltenVK)
a Vulkan implementation on top of Apple's Metal API. This allows vulkano to build and run on macOS
@ -107,14 +117,10 @@ This repository contains six libraries:
easily integrate your GLSL shaders within the rest of your source code.
- `vulkano-win` provides a safe link between vulkano and the `winit` library which can create
a window to render to.
- `glsl-to-spirv` can compile GLSL to SPIR-V by wrapping around `glslang`. `glsl-to-spirv` is an
implementation detail that you don't need to use manually if you use vulkano.
- `vk-sys` contains raw bindings for Vulkan. You can use it even if you don't care about vulkano.
Once procedural macros are stabilized in Rust, the `vulkano-shaders` and `vulkano-shader-derive`
crates will be merged with the `vulkano` crate. The `glsl-to-spirv` crate is an implementation
detail of vulkano and is not supposed to be used directly if you use vulkano. You are, however,
free to use it if you want to write an alternative to vulkano.
crates will be merged with the `vulkano` crate.
In order to run tests, run `cargo test --all` at the root of the repository. Make sure your Vulkan
driver is up to date before doing so.

View File

@ -1,16 +0,0 @@
[package]
name = "glsl-to-spirv"
version = "0.1.7"
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>", "The vulkano contributors"]
repository = "https://github.com/vulkano-rs/vulkano"
description = "Deprecated. Use shaderc-rs instead."
license = "MIT/Apache-2.0"
build = "build/build.rs"
categories = ["rendering::graphics-api"]
[dependencies]
tempfile = "3"
[build-dependencies]
cmake = "0.1.27"
sha2 = "0.7"

View File

@ -1,44 +0,0 @@
extern crate cmake;
extern crate sha2;
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;
use sha2::{Sha256, Digest};
fn main() {
println!("cargo:rerun-if-changed=build/glslangValidator.exe");
let target = env::var("TARGET").unwrap();
let out_file = Path::new(&env::var("OUT_DIR").unwrap()).join("glslang_validator");
let path = if target.contains("windows") {
const SHA256SUM: &'static str =
"90b377479fb137f4ac69460d5f5cdc54cd23bace5eb6e6812516fdfa693b25cf";
let path = Path::new("build/glslangValidator.exe").to_owned();
let content = fs::read(&path).expect("failed to open executable");
let mut hasher = Sha256::default();
hasher.input(&content);
let result = hasher.result();
let sha256sum = format!("{:x}", result);
assert_eq!(sha256sum, SHA256SUM, "glslangValidator.exe checksum failed");
path
} else {
// Try to initialize submodules. Don't care if it fails, since this code also runs for
// the crates.io package.
let _ = Command::new("git")
.arg("submodule")
.arg("update")
.arg("--init")
.status();
cmake::build("glslang");
Path::new(&env::var("OUT_DIR").unwrap())
.join("bin")
.join("glslangValidator")
};
fs::copy(&path, &out_file).expect("failed to copy executable");
}

@ -1 +0,0 @@
Subproject commit 4fbb8cb45e144ff63383b48fb1d3244522602438

View File

@ -1 +0,0 @@
This crate is deprecated please use [shaderc-rs](https://github.com/google/shaderc-rs) instead.

View File

@ -1,76 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
extern crate tempfile;
use std::fs::File;
use std::io::Write;
use std::process::Command;
pub type SpirvOutput = File;
pub fn compile(code: &str, ty: ShaderType) -> Result<SpirvOutput, String> {
compile_inner(Some((code, ty)))
}
// Eventually the API will look like this, with an iterator for multiple shader stages.
// However for the moment GLSLang doesn't like that, so we only pass one shader at a time.
fn compile_inner<'a, I>(shaders: I) -> Result<SpirvOutput, String>
where I: IntoIterator<Item = (&'a str, ShaderType)>
{
let temp_dir = tempfile::tempdir().unwrap();
let output_file = temp_dir.path().join("compilation_output.spv");
let mut command = Command::new(concat!(env!("OUT_DIR"), "/glslang_validator"));
command.arg("-V");
command.arg("-l");
command.arg("-o").arg(&output_file);
for (num, (source, ty)) in shaders.into_iter().enumerate() {
let extension = match ty {
ShaderType::Vertex => ".vert",
ShaderType::Fragment => ".frag",
ShaderType::Geometry => ".geom",
ShaderType::TessellationControl => ".tesc",
ShaderType::TessellationEvaluation => ".tese",
ShaderType::Compute => ".comp",
};
let file_path = temp_dir.path().join(format!("{}{}", num, extension));
File::create(&file_path)
.unwrap()
.write_all(source.as_bytes())
.unwrap();
command.arg(file_path);
}
let output = command
.output()
.expect("Failed to execute glslangValidator");
if output.status.success() {
let spirv_output = File::open(output_file).expect("failed to open SPIR-V output file");
return Ok(spirv_output);
}
let error1 = String::from_utf8_lossy(&output.stdout);
let error2 = String::from_utf8_lossy(&output.stderr);
return Err(error1.into_owned() + &error2);
}
/// Type of shader.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ShaderType {
Vertex,
Fragment,
Geometry,
TessellationControl,
TessellationEvaluation,
Compute,
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
extern crate glsl_to_spirv;
#[test]
fn test1() {
let shader = r#"
#version 330
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(1.0);
}
"#;
glsl_to_spirv::compile(shader, glsl_to_spirv::ShaderType::Fragment).unwrap();
}

View File

@ -13,7 +13,6 @@ name = "vulkano_shader_derive"
proc-macro = true
[dependencies]
glsl-to-spirv = { version = "0.1.6", path = "../glsl-to-spirv" }
syn = "0.14"
vulkano-shaders = { version = "0.10", path = "../vulkano-shaders" }

View File

@ -155,7 +155,6 @@
//! [SpecializationConstants]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/trait.SpecializationConstants.html
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html
extern crate glsl_to_spirv;
extern crate proc_macro;
extern crate syn;
extern crate vulkano_shaders;
@ -235,19 +234,18 @@ pub fn derive(input: TokenStream) -> TokenStream {
}).next().expect("Can't find `ty` attribute ; put #[ty = \"vertex\"] for example.");
let ty = match &ty_str[..] {
"vertex" => glsl_to_spirv::ShaderType::Vertex,
"fragment" => glsl_to_spirv::ShaderType::Fragment,
"geometry" => glsl_to_spirv::ShaderType::Geometry,
"tess_ctrl" => glsl_to_spirv::ShaderType::TessellationControl,
"tess_eval" => glsl_to_spirv::ShaderType::TessellationEvaluation,
"compute" => glsl_to_spirv::ShaderType::Compute,
"vertex" => vulkano_shaders::ShaderKind::Vertex,
"fragment" => vulkano_shaders::ShaderKind::Fragment,
"geometry" => vulkano_shaders::ShaderKind::Geometry,
"tess_ctrl" => vulkano_shaders::ShaderKind::TessControl,
"tess_eval" => vulkano_shaders::ShaderKind::TessEvaluation,
"compute" => vulkano_shaders::ShaderKind::Compute,
_ => panic!("Unexpected shader type ; valid values: vertex, fragment, geometry, tess_ctrl, tess_eval, compute")
};
let spirv_data = match glsl_to_spirv::compile(&source_code, ty) {
Ok(compiled) => compiled,
Err(message) => panic!("{}\nfailed to compile shader", message),
};
vulkano_shaders::reflect("Shader", spirv_data).unwrap().parse().unwrap()
let content = vulkano_shaders::compile(&source_code, ty).unwrap();
vulkano_shaders::reflect("Shader", content.as_binary())
.unwrap()
.parse()
.unwrap()
}

View File

@ -9,4 +9,4 @@ documentation = "http://tomaka.github.io/vulkano/vulkano/index.html"
categories = ["rendering::graphics-api"]
[dependencies]
glsl-to-spirv = { version = "0.1.6", path = "../glsl-to-spirv" }
shaderc = "0.3"

View File

@ -7,7 +7,6 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
extern crate glsl_to_spirv;
extern crate vulkano_shaders;
fn main() {
@ -40,7 +39,7 @@ void main() {
"#;
let content = glsl_to_spirv::compile(shader, glsl_to_spirv::ShaderType::Fragment).unwrap();
let output = vulkano_shaders::reflect("Shader", content).unwrap();
let content = vulkano_shaders::compile(shader, vulkano_shaders::ShaderKind::Fragment).unwrap();
let output = vulkano_shaders::reflect("Shader", content.as_binary()).unwrap();
println!("{}", output);
}

View File

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
extern crate glsl_to_spirv;
extern crate shaderc;
use std::env;
use std::fs;
@ -17,7 +17,9 @@ use std::io::Read;
use std::io::Write;
use std::path::Path;
pub use glsl_to_spirv::ShaderType;
use shaderc::{Compiler, CompileOptions};
pub use shaderc::{CompilationArtifact, ShaderKind};
pub use parse::ParseError;
mod descriptor_sets;
@ -28,7 +30,7 @@ mod spec_consts;
mod structs;
pub fn build_glsl_shaders<'a, I>(shaders: I)
where I: IntoIterator<Item = (&'a str, ShaderType)>
where I: IntoIterator<Item = (&'a str, ShaderKind)>
{
let destination = env::var("OUT_DIR").unwrap();
let destination = Path::new(&destination);
@ -55,23 +57,25 @@ pub fn build_glsl_shaders<'a, I>(shaders: I)
let mut file_output = File::create(&destination.join("shaders").join(shader))
.expect("failed to open shader output");
let content = match glsl_to_spirv::compile(&shader_content, ty) {
Ok(compiled) => compiled,
Err(message) => panic!("{}\nfailed to compile shader", message),
};
let output = reflect("Shader", content).unwrap();
let content = compile(&shader_content, ty).unwrap();
let output = reflect("Shader", content.as_binary()).unwrap();
write!(file_output, "{}", output).unwrap();
}
}
pub fn reflect<R>(name: &str, mut spirv: R) -> Result<String, Error>
where R: Read
{
let mut data = Vec::new();
spirv.read_to_end(&mut data)?;
pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> {
let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?;
let compile_options = CompileOptions::new().ok_or("failed to initialize compile option")?;
// now parsing the document
let doc = parse::parse_spirv(&data)?;
let content = compiler
.compile_into_spirv(&code, ty, "shader.glsl", "main", Some(&compile_options))
.map_err(|e| e.to_string())?;
Ok(content)
}
pub fn reflect(name: &str, spirv: &[u32]) -> Result<String, Error> {
let doc = parse::parse_spirv(spirv)?;
let mut output = String::new();
output.push_str(
@ -118,8 +122,8 @@ pub fn reflect<R>(name: &str, mut spirv: R) -> Result<String, Error>
{
// contains the data that was passed as input to this function
let spirv_data = data.iter()
.map(|&byte| byte.to_string())
let spirv_words = spirv.iter()
.map(|&word| word.to_string())
.collect::<Vec<String>>()
.join(", ");
@ -162,10 +166,10 @@ impl {name} {{
output.push_str(&format!(
r#"
unsafe {{
let data = [{spirv_data}];
let words = [{spirv_words}];
Ok({name} {{
shader: try!(::vulkano::pipeline::shader::ShaderModule::new(device, &data))
shader: try!(::vulkano::pipeline::shader::ShaderModule::from_words(device, &words))
}})
}}
}}
@ -178,7 +182,7 @@ impl {name} {{
}}
"#,
name = name,
spirv_data = spirv_data
spirv_words = spirv_words
));
// writing one method for each entry point of this module

View File

@ -9,43 +9,8 @@
use enums::*;
/// Parses a SPIR-V document.
pub fn parse_spirv(data: &[u8]) -> Result<Spirv, ParseError> {
if data.len() < 20 {
return Err(ParseError::MissingHeader);
}
// we need to determine whether we are in big endian order or little endian order depending
// on the magic number at the start of the file
let data = if data[0] == 0x07 && data[1] == 0x23 && data[2] == 0x02 && data[3] == 0x03 {
// big endian
data.chunks(4)
.map(|c| {
((c[0] as u32) << 24) | ((c[1] as u32) << 16) | ((c[2] as u32) << 8) |
c[3] as u32
})
.collect::<Vec<_>>()
} else if data[3] == 0x07 && data[2] == 0x23 && data[1] == 0x02 && data[0] == 0x03 {
// little endian
data.chunks(4)
.map(|c| {
((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) |
c[0] as u32
})
.collect::<Vec<_>>()
} else {
return Err(ParseError::MissingHeader);
};
parse_u32s(&data)
}
/// Parses a SPIR-V document from a list of u32s.
///
/// Endianness has already been handled.
fn parse_u32s(i: &[u32]) -> Result<Spirv, ParseError> {
/// Parses a SPIR-V document from a list of words.
pub fn parse_spirv(i: &[u32]) -> Result<Spirv, ParseError> {
if i.len() < 5 {
return Err(ParseError::MissingHeader);
}
@ -399,6 +364,12 @@ mod test {
#[test]
fn test() {
let data = include_bytes!("../tests/frag.spv");
parse::parse_spirv(data).unwrap();
let insts: Vec<_> = data.chunks(4)
.map(|c| {
((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32
})
.collect();
parse::parse_spirv(&insts).unwrap();
}
}