Skip to content

Update to v2025.1, bump version, and use errors #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions shaderc-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shaderc"
version = "0.8.3"
version = "0.9.0"
authors = ["Lei Zhang <[email protected]>"]
description = "Rust bindings for shaderc"
documentation = "https://docs.rs/shaderc"
Expand All @@ -14,7 +14,7 @@ path = "src/lib.rs"

[dependencies]
libc = "0.2"
shaderc-sys = { version = "0.8.3", path = "../shaderc-sys" }
shaderc-sys = { version = "0.9.0", path = "../shaderc-sys" }

[dev-dependencies]
assert_matches = "1.5"
Expand Down
167 changes: 105 additions & 62 deletions shaderc-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,34 +80,45 @@ use std::{error, fmt, ptr, result, slice, str};

/// Error.
///
/// Each enumerants has an affixed string describing detailed reasons for
/// the error. The string can be empty in cases.
/// Each enumerant has an affixed string describing detailed reasons for
/// the error. The string can be empty in certain cases.
#[derive(Debug, PartialEq)]
pub enum Error {
/// Compilation error.
///
/// Contains the number of errors and detailed error string.
/// Contains the number of errors and a detailed error string.
CompilationError(u32, String),
InternalError(String),
InvalidStage(String),
InvalidAssembly(String),
NullResultObject(String),

/// Returned when an internal initialization fails, for instance creating
/// a `Compiler` or `CompileOptions` fails because the underlying shaderc
/// function returned null.
InitializationError(String),

/// Returned when string parsing fails (e.g., version/profile parsing).
ParseError(String),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::CompilationError(c, ref r) => {
if c == 1 {
Error::CompilationError(count, ref reason) => {
if count == 1 {
write!(f, "compilation error")?;
} else {
write!(f, "{c} compilation errors")?;
write!(f, "{count} compilation errors")?;
}

if !r.is_empty() {
write!(f, ":{}{}", if r.contains('\n') { "\n" } else { " " }, r)?;
if !reason.is_empty() {
write!(
f,
":{}{}",
if reason.contains('\n') { "\n" } else { " " },
reason
)?;
}

Ok(())
}
Error::InternalError(ref r) => {
Expand Down Expand Up @@ -138,6 +149,20 @@ impl fmt::Display for Error {
write!(f, "null result object: {r}")
}
}
Error::InitializationError(ref r) => {
if r.is_empty() {
write!(f, "initialization error")
} else {
write!(f, "initialization error: {r}")
}
}
Error::ParseError(ref r) => {
if r.is_empty() {
write!(f, "parse error")
} else {
write!(f, "parse error: {r}")
}
}
}
}
}
Expand All @@ -150,6 +175,8 @@ impl error::Error for Error {
Error::InvalidStage(_) => "invalid stage",
Error::InvalidAssembly(_) => "invalid assembly",
Error::NullResultObject(_) => "null result object",
Error::InitializationError(_) => "initialization error",
Error::ParseError(_) => "parse error",
}
}
}
Expand Down Expand Up @@ -455,7 +482,7 @@ where
/// a UTF-8 string
fn safe_str_from_utf8(bytes: &[u8]) -> String {
match str::from_utf8(bytes) {
Ok(str) => str.to_string(),
Ok(s) => s.to_string(),
Err(err) => {
if err.valid_up_to() > 0 {
format!(
Expand All @@ -470,16 +497,18 @@ fn safe_str_from_utf8(bytes: &[u8]) -> String {
}

impl Compiler {
/// Returns an compiler object that can be used to compile SPIR-V modules.
/// Returns a `Compiler` object that can be used to compile SPIR-V modules.
///
/// A return of `None` indicates that there was an error initializing
/// the underlying compiler.
pub fn new() -> Option<Compiler> {
/// A return of `Err` indicates that there was an error initializing
/// the underlying compiler (i.e., `shaderc_compiler_initialize()` returned null).
pub fn new() -> Result<Self> {
let p = unsafe { scs::shaderc_compiler_initialize() };
if p.is_null() {
None
Err(Error::InitializationError(
"failed to create a shaderc compiler".to_string(),
))
} else {
Some(Compiler { raw: p })
Ok(Compiler { raw: p })
}
}

Expand Down Expand Up @@ -708,14 +737,15 @@ impl<'a> CompileOptions<'a> {
/// * Target environment: Vulkan
/// * Source language: GLSL
///
/// A return of `None` indicates that there was an error initializing
/// the underlying options object.
pub fn new() -> Option<CompileOptions<'a>> {
/// Returns `Err(Error::InitializationError)` if creation failed.
pub fn new() -> Result<CompileOptions<'a>> {
let p = unsafe { scs::shaderc_compile_options_initialize() };
if p.is_null() {
None
Err(Error::InitializationError(
"failed to create CompileOptions".to_string(),
))
} else {
Some(CompileOptions {
Ok(CompileOptions {
raw: p,
include_callback_fn: None,
})
Expand All @@ -724,30 +754,34 @@ impl<'a> CompileOptions<'a> {

/// Returns a copy of the given compilation options object.
///
/// A return of `None` indicates that there was an error copying
/// the underlying options object.
/// Returns `Err(Error::InitializationError)` if the clone operation failed (underlying call
/// returned null).
#[allow(clippy::should_implement_trait)]
pub fn clone(&self) -> Option<CompileOptions> {
pub fn clone(&self) -> Result<CompileOptions<'a>> {
let p = unsafe { scs::shaderc_compile_options_clone(self.raw) };
if p.is_null() {
None
Err(Error::InitializationError(
"failed to clone CompileOptions".to_string(),
))
} else {
Some(CompileOptions {
Ok(CompileOptions {
raw: p,
include_callback_fn: None,
})
}
}

/// Sets the target enviroment to `env`, affecting which warnings or errors
/// Sets the target environment to `env`, affecting which warnings or errors
/// will be issued.
///
/// The default is Vulkan if not set.
///
/// `version` will be used for distinguishing between different versions
/// of the target environment.
/// Note that EnvVersion must be cast to u32 when calling set_target_env.
/// For example: `options.set_target_env(shaderc::TargetEnv::Vulkan, shaderc::EnvVersion::Vulkan1_1 as u32);`
/// For example:
/// ```ignore
/// options.set_target_env(shaderc::TargetEnv::Vulkan, shaderc::EnvVersion::Vulkan1_1 as u32);
/// ```
pub fn set_target_env(&mut self, env: TargetEnv, version: u32) {
unsafe { scs::shaderc_compile_options_set_target_env(self.raw, env as i32, version) }
}
Expand Down Expand Up @@ -1005,9 +1039,9 @@ impl<'a> CompileOptions<'a> {

/// Sets a descriptor set and binding for an HLSL register in all shader stages.
pub fn set_hlsl_register_set_and_binding(&mut self, register: &str, set: &str, binding: &str) {
let c_register = CString::new(register).expect("cannot convert string to c string");
let c_set = CString::new(set).expect("cannot convert string to c string");
let c_binding = CString::new(binding).expect("cannot convert string to c string");
let c_register = CString::new(register).expect("cannot convert register to c string");
let c_set = CString::new(set).expect("cannot convert set to c string");
let c_binding = CString::new(binding).expect("cannot convert binding to c string");
unsafe {
scs::shaderc_compile_options_set_hlsl_register_set_and_binding(
self.raw,
Expand Down Expand Up @@ -1055,7 +1089,7 @@ impl<'a> CompileOptions<'a> {
}
}

/// Sets whether the compiler should invert position.Y output in vertex shader.
/// Sets whether the compiler should invert position.Y output in a vertex shader.
pub fn set_invert_y(&mut self, enable: bool) {
unsafe {
scs::shaderc_compile_options_set_invert_y(self.raw, enable);
Expand All @@ -1080,9 +1114,9 @@ impl<'a> CompileOptions<'a> {
/// same name has previously been added, the value is replaced with the
/// new value.
pub fn add_macro_definition(&mut self, name: &str, value: Option<&str>) {
let c_name = CString::new(name).expect("cannot convert name to c string");
let c_name = CString::new(name).expect("cannot convert macro name to c string");
if let Some(value) = value {
let c_value = CString::new(value).expect("cannot convert value to c string");
let c_value = CString::new(value).expect("cannot convert macro value to c string");
unsafe {
scs::shaderc_compile_options_add_macro_definition(
self.raw,
Expand All @@ -1107,7 +1141,7 @@ impl<'a> CompileOptions<'a> {

/// Sets the optimization level to `level`.
///
/// If mulitple invocations for this method, only the last one takes effect.
/// If multiple invocations for this method, only the last one takes effect.
pub fn set_optimization_level(&mut self, level: OptimizationLevel) {
unsafe { scs::shaderc_compile_options_set_optimization_level(self.raw, level as i32) }
}
Expand Down Expand Up @@ -1168,8 +1202,7 @@ impl CompilationArtifact {
///
/// # Panics
///
/// This method will panic if the compilation does not generate a
/// binary output.
/// Panics if the compilation does not generate a binary output.
pub fn as_binary(&self) -> &[u32] {
if !self.is_binary {
panic!("not binary result")
Expand All @@ -1189,8 +1222,7 @@ impl CompilationArtifact {
///
/// # Panics
///
/// This method will panic if the compilation does not generate a
/// binary output.
/// Panics if the compilation does not generate a binary output.
pub fn as_binary_u8(&self) -> &[u8] {
if !self.is_binary {
panic!("not binary result")
Expand All @@ -1208,8 +1240,7 @@ impl CompilationArtifact {
///
/// # Panics
///
/// This method will panic if the compilation does not generate a
/// text output.
/// Panics if the compilation does not generate a text output.
pub fn as_text(&self) -> String {
if self.is_binary {
panic!("not text result")
Expand All @@ -1225,7 +1256,7 @@ impl CompilationArtifact {

/// Returns the number of warnings generated during the compilation.
pub fn get_num_warnings(&self) -> u32 {
(unsafe { scs::shaderc_result_get_num_warnings(self.raw) }) as u32
unsafe { scs::shaderc_result_get_num_warnings(self.raw) as u32 }
}

/// Returns the detailed warnings as a string.
Expand Down Expand Up @@ -1256,28 +1287,30 @@ pub fn get_spirv_version() -> (u32, u32) {
(version as u32, revision as u32)
}

/// Parses the version and profile from the given `string`.
/// Parses the version and profile from the given `string`, returning a `Result`.
///
/// The string should contain both version and profile, like: `450core`.
/// Returns `None` if the string can not be parsed.
pub fn parse_version_profile(string: &str) -> Option<(u32, GlslProfile)> {
/// The string should contain both version and profile, like `450core`.
/// Returns an error if the string cannot be parsed.
pub fn parse_version_profile(string: &str) -> Result<(u32, GlslProfile)> {
let mut version: i32 = 0;
let mut profile: i32 = 0;
let c_string = CString::new(string).expect("cannot convert string to c string");
let result = unsafe {
scs::shaderc_parse_version_profile(c_string.as_ptr(), &mut version, &mut profile)
};
if !result {
None
Err(Error::ParseError(format!(
"failed to parse version/profile from '{string}'"
)))
} else {
let p = match profile {
0 => GlslProfile::None,
1 => GlslProfile::Core,
2 => GlslProfile::Compatibility,
3 => GlslProfile::Es,
_ => panic!("internal error: unhandled profile"),
_ => panic!("internal error: unhandled profile value {}", profile),
};
Some((version as u32, p))
Ok((version as u32, p))
}
}

Expand Down Expand Up @@ -1392,7 +1425,7 @@ void main() { my_ssbo.x = 1.0; }";
let result = c
.preprocess(VOID_E, "shader.glsl", "main", Some(&options))
.unwrap();
assert_eq!("#version 310 es\n void main(){ }\n", result.as_text());
assert_eq!("#version 310 es\n void main() { }\n", result.as_text());
}

#[test]
Expand Down Expand Up @@ -1602,11 +1635,11 @@ void main() { my_ssbo.x = 1.0; }";
}

#[test]
#[should_panic(expected = "Panic in include resolver!")]
#[should_panic(expected = "panic in include resolver!")]
fn test_include_directive_panic() {
let c = Compiler::new().unwrap();
let mut options = CompileOptions::new().unwrap();
options.set_include_callback(|_, _, _, _| panic!("Panic in include resolver!"));
options.set_include_callback(|_, _, _, _| panic!("panic in include resolver!"));
drop(c.compile_into_spirv_assembly(
r#"
#version 400
Expand All @@ -1624,7 +1657,7 @@ void main() { my_ssbo.x = 1.0; }";
let c = Compiler::new().unwrap();
let mut options = CompileOptions::new().unwrap();
options
.set_include_callback(|name, _, _, _| Err(format!("Couldn't find header \"{name}\"")));
.set_include_callback(|name, _, _, _| Err(format!("couldn't find header \"{name}\"")));
let result = c.compile_into_spirv_assembly(
r#"
#version 400
Expand All @@ -1638,7 +1671,7 @@ void main() { my_ssbo.x = 1.0; }";
assert!(result.is_err());
assert_matches!(result.err(),
Some(Error::CompilationError(1, ref s))
if s.contains("Couldn't find header \"foo.glsl\""));
if s.contains("couldn't find header \"foo.glsl\""));
}

#[test]
Expand All @@ -1658,7 +1691,7 @@ void main() { my_ssbo.x = 1.0; }";
.to_string(),
})
} else {
Err(format!("Couldn't find header \"{name}\""))
Err(format!("couldn't find header \"{name}\""))
}
});
let result = c.compile_into_spirv_assembly(
Expand Down Expand Up @@ -2001,13 +2034,23 @@ void main() { my_ssbo.x = 1.0; }";

#[test]
fn test_parse_version_profile() {
assert_eq!(Some((310, GlslProfile::Es)), parse_version_profile("310es"));
assert_eq!(Ok((310, GlslProfile::Es)), parse_version_profile("310es"));
assert_eq!(
Some((450, GlslProfile::Compatibility)),
Ok((450, GlslProfile::Compatibility)),
parse_version_profile("450compatibility")
);
assert_eq!(Some((140, GlslProfile::None)), parse_version_profile("140"));
assert_eq!(None, parse_version_profile("something"));
assert_eq!(None, parse_version_profile(""));
assert_eq!(Ok((140, GlslProfile::None)), parse_version_profile("140"));
assert_eq!(
Err(Error::ParseError(
"failed to parse version/profile from 'something'".to_string()
)),
parse_version_profile("something")
);
assert_eq!(
Err(Error::ParseError(
"failed to parse version/profile from ''".to_string()
)),
parse_version_profile("")
);
}
}
Loading
Loading