1#[cfg(test)]
57#[macro_use]
58extern crate assert_matches;
59extern crate libc;
60extern crate shaderc_sys_dyn;
61
62use shaderc_sys_dyn as scs;
63
64use libc::{c_char, c_int, c_void, size_t};
65use std::any::Any;
66use std::cell::RefCell;
67use std::ffi::{CStr, CString};
68use std::panic;
69use std::rc::Rc;
70use std::{error, fmt, ptr, result, slice, str};
71
72#[derive(Debug, PartialEq)]
77pub enum Error {
78 CompilationError(u32, String),
82 InternalError(String),
83 InvalidStage(String),
84 InvalidAssembly(String),
85 NullResultObject(String),
86
87 InitializationError(String),
91
92 ParseError(String),
94}
95
96impl fmt::Display for Error {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98 match *self {
99 Error::CompilationError(count, ref reason) => {
100 if count == 1 {
101 write!(f, "compilation error")?;
102 } else {
103 write!(f, "{count} compilation errors")?;
104 }
105 if !reason.is_empty() {
106 write!(
107 f,
108 ":{}{}",
109 if reason.contains('\n') { "\n" } else { " " },
110 reason
111 )?;
112 }
113 Ok(())
114 }
115 Error::InternalError(ref r) => {
116 if r.is_empty() {
117 write!(f, "internal error")
118 } else {
119 write!(f, "internal error: {r}")
120 }
121 }
122 Error::InvalidStage(ref r) => {
123 if r.is_empty() {
124 write!(f, "invalid stage")
125 } else {
126 write!(f, "invalid stage: {r}")
127 }
128 }
129 Error::InvalidAssembly(ref r) => {
130 if r.is_empty() {
131 write!(f, "invalid assembly")
132 } else {
133 write!(f, "invalid assembly: {r}")
134 }
135 }
136 Error::NullResultObject(ref r) => {
137 if r.is_empty() {
138 write!(f, "null result object")
139 } else {
140 write!(f, "null result object: {r}")
141 }
142 }
143 Error::InitializationError(ref r) => {
144 if r.is_empty() {
145 write!(f, "initialization error")
146 } else {
147 write!(f, "initialization error: {r}")
148 }
149 }
150 Error::ParseError(ref r) => {
151 if r.is_empty() {
152 write!(f, "parse error")
153 } else {
154 write!(f, "parse error: {r}")
155 }
156 }
157 }
158 }
159}
160
161impl error::Error for Error {
162 fn description(&self) -> &str {
163 match *self {
164 Error::CompilationError(_, _) => "compilation error",
165 Error::InternalError(_) => "internal error",
166 Error::InvalidStage(_) => "invalid stage",
167 Error::InvalidAssembly(_) => "invalid assembly",
168 Error::NullResultObject(_) => "null result object",
169 Error::InitializationError(_) => "initialization error",
170 Error::ParseError(_) => "parse error",
171 }
172 }
173}
174
175pub type Result<T> = result::Result<T, Error>;
177
178#[repr(C)]
180#[derive(Clone, Copy, Debug, PartialEq, Eq)]
181pub enum TargetEnv {
182 Vulkan,
184 OpenGL,
186 OpenGLCompat,
188}
189
190#[repr(C)]
192#[derive(Clone, Copy, Debug, PartialEq, Eq)]
193pub enum EnvVersion {
194 Vulkan1_0 = 1 << 22,
197 Vulkan1_1 = (1 << 22) | (1 << 12),
198 Vulkan1_2 = (1 << 22) | (2 << 12),
199 Vulkan1_3 = (1 << 22) | (3 << 12),
200 Vulkan1_4 = (1 << 22) | (4 << 12),
201 OpenGL4_5 = 450,
206 WebGPU,
208}
209
210#[repr(C)]
212#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213pub enum SpirvVersion {
214 V1_0 = 0x0001_0000,
220 V1_1 = 0x0001_0100,
221 V1_2 = 0x0001_0200,
222 V1_3 = 0x0001_0300,
223 V1_4 = 0x0001_0400,
224 V1_5 = 0x0001_0500,
225 V1_6 = 0x0001_0600,
226}
227
228#[repr(C)]
230#[derive(Clone, Copy, Debug, PartialEq, Eq)]
231pub enum SourceLanguage {
232 GLSL,
233 HLSL,
234}
235
236#[repr(C)]
241#[derive(Clone, Copy, Debug, PartialEq, Eq)]
242pub enum ResourceKind {
243 Image,
245 Sampler,
247 Texture,
249 Buffer,
251 StorageBuffer,
253 UnorderedAccessView,
255}
256
257#[repr(C)]
267#[derive(Clone, Copy, Debug, PartialEq, Eq)]
268pub enum ShaderKind {
269 Vertex,
270 Fragment,
271 Compute,
272 Geometry,
273 TessControl,
274 TessEvaluation,
275
276 InferFromSource,
280
281 DefaultVertex,
282 DefaultFragment,
283 DefaultCompute,
284 DefaultGeometry,
285 DefaultTessControl,
286 DefaultTessEvaluation,
287
288 SpirvAssembly,
289
290 RayGeneration,
291 AnyHit,
292 ClosestHit,
293 Miss,
294 Intersection,
295 Callable,
296
297 DefaultRayGeneration,
298 DefaultAnyHit,
299 DefaultClosestHit,
300 DefaultMiss,
301 DefaultIntersection,
302 DefaultCallable,
303
304 Task,
305 Mesh,
306
307 DefaultTask,
308 DefaultMesh,
309}
310
311#[repr(C)]
313#[derive(Clone, Copy, Debug, PartialEq, Eq)]
314pub enum GlslProfile {
315 None,
317 Core,
318 Compatibility,
319 Es,
320}
321
322#[repr(C)]
324#[derive(Clone, Copy, Debug, PartialEq, Eq)]
325pub enum OptimizationLevel {
326 Zero,
328 Size,
330 Performance,
331}
332
333#[repr(C)]
335#[derive(Clone, Copy, Debug, PartialEq, Eq)]
336pub enum Limit {
337 MaxLights,
338 MaxClipPlanes,
339 MaxTextureUnits,
340 MaxTextureCoords,
341 MaxVertexAttribs,
342 MaxVertexUniformComponents,
343 MaxVaryingFloats,
344 MaxVertexTextureImageUnits,
345 MaxCombinedTextureImageUnits,
346 MaxTextureImageUnits,
347 MaxFragmentUniformComponents,
348 MaxDrawBuffers,
349 MaxVertexUniformVectors,
350 MaxVaryingVectors,
351 MaxFragmentUniformVectors,
352 MaxVertexOutputVectors,
353 MaxFragmentInputVectors,
354 MinProgramTexelOffset,
355 MaxProgramTexelOffset,
356 MaxClipDistances,
357 MaxComputeWorkGroupCountX,
358 MaxComputeWorkGroupCountY,
359 MaxComputeWorkGroupCountZ,
360 MaxComputeWorkGroupSizeX,
361 MaxComputeWorkGroupSizeY,
362 MaxComputeWorkGroupSizeZ,
363 MaxComputeUniformComponents,
364 MaxComputeTextureImageUnits,
365 MaxComputeImageUniforms,
366 MaxComputeAtomicCounters,
367 MaxComputeAtomicCounterBuffers,
368 MaxVaryingComponents,
369 MaxVertexOutputComponents,
370 MaxGeometryInputComponents,
371 MaxGeometryOutputComponents,
372 MaxFragmentInputComponents,
373 MaxImageUnits,
374 MaxCombinedImageUnitsAndFragmentOutputs,
375 MaxCombinedShaderOutputResources,
376 MaxImageSamples,
377 MaxVertexImageUniforms,
378 MaxTessControlImageUniforms,
379 MaxTessEvaluationImageUniforms,
380 MaxGeometryImageUniforms,
381 MaxFragmentImageUniforms,
382 MaxCombinedImageUniforms,
383 MaxGeometryTextureImageUnits,
384 MaxGeometryOutputVertices,
385 MaxGeometryTotalOutputComponents,
386 MaxGeometryUniformComponents,
387 MaxGeometryVaryingComponents,
388 MaxTessControlInputComponents,
389 MaxTessControlOutputComponents,
390 MaxTessControlTextureImageUnits,
391 MaxTessControlUniformComponents,
392 MaxTessControlTotalOutputComponents,
393 MaxTessEvaluationInputComponents,
394 MaxTessEvaluationOutputComponents,
395 MaxTessEvaluationTextureImageUnits,
396 MaxTessEvaluationUniformComponents,
397 MaxTessPatchComponents,
398 MaxPatchVertices,
399 MaxTessGenLevel,
400 MaxViewports,
401 MaxVertexAtomicCounters,
402 MaxTessControlAtomicCounters,
403 MaxTessEvaluationAtomicCounters,
404 MaxGeometryAtomicCounters,
405 MaxFragmentAtomicCounters,
406 MaxCombinedAtomicCounters,
407 MaxAtomicCounterBindings,
408 MaxVertexAtomicCounterBuffers,
409 MaxTessControlAtomicCounterBuffers,
410 MaxTessEvaluationAtomicCounterBuffers,
411 MaxGeometryAtomicCounterBuffers,
412 MaxFragmentAtomicCounterBuffers,
413 MaxCombinedAtomicCounterBuffers,
414 MaxAtomicCounterBufferSize,
415 MaxTransformFeedbackBuffers,
416 MaxTransformFeedbackInterleavedComponents,
417 MaxCullDistances,
418 MaxCombinedClipAndCullDistances,
419 MaxSamples,
420 MaxMeshOutputVerticesNv,
421 MaxMeshOutputPrimitivesNv,
422 MaxMeshWorkGroupSizeXNv,
423 MaxMeshWorkGroupSizeYNv,
424 MaxMeshWorkGroupSizeZNv,
425 MaxTaskWorkGroupSizeXNv,
426 MaxTaskWorkGroupSizeYNv,
427 MaxTaskWorkGroupSizeZNv,
428 MaxMeshViewCountNv,
429 MaxMeshOutputVerticesExt,
430 MaxMeshOutputPrimitivesExt,
431 MaxMeshWorkGroupSizeXExt,
432 MaxMeshWorkGroupSizeYExt,
433 MaxMeshWorkGroupSizeZExt,
434 MaxTaskWorkGroupSizeXExt,
435 MaxTaskWorkGroupSizeYExt,
436 MaxTaskWorkGroupSizeZExt,
437 MaxMeshViewCountExt,
438 MaxDualSourceDrawBuffersExt,
439}
440
441#[derive(Debug)]
446pub struct Compiler {
447 raw: *mut scs::ShadercCompiler,
448}
449
450unsafe impl Send for Compiler {}
451unsafe impl Sync for Compiler {}
452
453fn propagate_panic<F, T>(f: F) -> T
454where
455 F: FnOnce() -> T,
456{
457 PANIC_ERROR.with(|panic_error| {
458 *panic_error.borrow_mut() = None;
459 });
460 let result = f();
461 let err = PANIC_ERROR.with(|panic_error| panic_error.borrow_mut().take());
462 if let Some(err) = err {
463 panic::resume_unwind(err)
464 } else {
465 result
466 }
467}
468
469fn safe_str_from_utf8(bytes: &[u8]) -> String {
476 match str::from_utf8(bytes) {
477 Ok(s) => s.to_string(),
478 Err(err) => {
479 if err.valid_up_to() > 0 {
480 format!(
481 "{} (followed by invalid UTF-8 characters)",
482 safe_str_from_utf8(&bytes[..err.valid_up_to()])
483 )
484 } else {
485 format!("invalid UTF-8 string: {err}")
486 }
487 }
488 }
489}
490
491impl Compiler {
492 pub fn new() -> Result<Self> {
497 let p = unsafe { scs::shaderc_compiler_initialize() };
498 if p.is_null() {
499 Err(Error::InitializationError(
500 "failed to create a shaderc compiler".to_string(),
501 ))
502 } else {
503 Ok(Compiler { raw: p })
504 }
505 }
506
507 fn handle_compilation_result(
508 result: *mut scs::ShadercCompilationResult,
509 is_binary: bool,
510 ) -> Result<CompilationArtifact> {
511 let status = unsafe { scs::shaderc_result_get_compilation_status(result) };
512 if status == 0 {
513 Ok(CompilationArtifact::new(result, is_binary))
514 } else {
515 let num_errors = unsafe { scs::shaderc_result_get_num_errors(result) } as u32;
516 let reason = unsafe {
517 let p = scs::shaderc_result_get_error_message(result);
518 let bytes = CStr::from_ptr(p).to_bytes();
519 safe_str_from_utf8(bytes)
520 };
521 match status {
522 1 => Err(Error::InvalidStage(reason)),
523 2 => Err(Error::CompilationError(num_errors, reason)),
524 3 => Err(Error::InternalError(reason)),
525 4 => Err(Error::NullResultObject(reason)),
526 5 => Err(Error::InvalidAssembly(reason)),
527 _ => panic!("unhandled shaderc error case"),
528 }
529 }
530 }
531
532 pub fn compile_into_spirv(
553 &self,
554 source_text: &str,
555 shader_kind: ShaderKind,
556 input_file_name: &str,
557 entry_point_name: &str,
558 additional_options: Option<&CompileOptions>,
559 ) -> Result<CompilationArtifact> {
560 let source_size = source_text.len();
561 let c_source = CString::new(source_text).expect("cannot convert source_text to c string");
562 let c_file =
563 CString::new(input_file_name).expect("cannot convert input_file_name to c string");
564 let c_entry_point =
565 CString::new(entry_point_name).expect("cannot convert entry_point_name to c string");
566 propagate_panic(|| {
567 let result = unsafe {
568 scs::shaderc_compile_into_spv(
569 self.raw,
570 c_source.as_ptr(),
571 source_size,
572 shader_kind as i32,
573 c_file.as_ptr(),
574 c_entry_point.as_ptr(),
575 additional_options.map_or(ptr::null(), |o| o.raw),
576 )
577 };
578 Compiler::handle_compilation_result(result, true)
579 })
580 }
581
582 pub fn compile_into_spirv_assembly(
589 &self,
590 source_text: &str,
591 shader_kind: ShaderKind,
592 input_file_name: &str,
593 entry_point_name: &str,
594 additional_options: Option<&CompileOptions>,
595 ) -> Result<CompilationArtifact> {
596 let source_size = source_text.len();
597 let c_source = CString::new(source_text).expect("cannot convert source_text to c string");
598 let c_file =
599 CString::new(input_file_name).expect("cannot convert input_file_name to c string");
600 let c_entry_point =
601 CString::new(entry_point_name).expect("cannot convert entry_point_name to c string");
602 propagate_panic(|| {
603 let result = unsafe {
604 scs::shaderc_compile_into_spv_assembly(
605 self.raw,
606 c_source.as_ptr(),
607 source_size,
608 shader_kind as i32,
609 c_file.as_ptr(),
610 c_entry_point.as_ptr(),
611 additional_options.map_or(ptr::null(), |o| o.raw),
612 )
613 };
614 Compiler::handle_compilation_result(result, false)
615 })
616 }
617
618 pub fn preprocess(
621 &self,
622 source_text: &str,
623 input_file_name: &str,
624 entry_point_name: &str,
625 additional_options: Option<&CompileOptions>,
626 ) -> Result<CompilationArtifact> {
627 let source_size = source_text.len();
628 let c_source = CString::new(source_text).expect("cannot convert source to c string");
629 let c_file =
630 CString::new(input_file_name).expect("cannot convert input_file_name to c string");
631 let c_entry_point =
632 CString::new(entry_point_name).expect("cannot convert entry_point_name to c string");
633 propagate_panic(|| {
634 let result = unsafe {
635 scs::shaderc_compile_into_preprocessed_text(
636 self.raw,
637 c_source.as_ptr(),
638 source_size,
639 ShaderKind::Vertex as i32,
641 c_file.as_ptr(),
642 c_entry_point.as_ptr(),
643 additional_options.map_or(ptr::null(), |o| o.raw),
644 )
645 };
646 Compiler::handle_compilation_result(result, false)
647 })
648 }
649
650 pub fn assemble(
660 &self,
661 source_assembly: &str,
662 additional_options: Option<&CompileOptions>,
663 ) -> Result<CompilationArtifact> {
664 let source_size = source_assembly.len();
665 let c_source =
666 CString::new(source_assembly).expect("cannot convert source_assembly to c string");
667 propagate_panic(|| {
668 let result = unsafe {
669 scs::shaderc_assemble_into_spv(
670 self.raw,
671 c_source.as_ptr(),
672 source_size,
673 additional_options.map_or(ptr::null(), |o| o.raw),
674 )
675 };
676 Compiler::handle_compilation_result(result, true)
677 })
678 }
679}
680
681impl Drop for Compiler {
682 fn drop(&mut self) {
683 unsafe { scs::shaderc_compiler_release(self.raw) }
684 }
685}
686
687pub type IncludeCallbackResult = result::Result<ResolvedInclude, String>;
689
690type BoxedIncludeCallback<'a> =
691 Rc<dyn Fn(&str, IncludeType, &str, usize) -> IncludeCallbackResult + 'a>;
692
693pub struct CompileOptions<'a> {
695 raw: *mut scs::ShadercCompileOptions,
696 include_callback_fn: Option<BoxedIncludeCallback<'a>>,
697}
698
699#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)]
702pub enum IncludeType {
703 Relative,
704 Standard,
705}
706
707#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
710pub struct ResolvedInclude {
711 pub resolved_name: String,
717 pub content: String,
719}
720
721thread_local! {
722 static PANIC_ERROR: RefCell<Option<Box<dyn Any + Send + 'static>>> = RefCell::new(None);
723}
724
725impl<'a> CompileOptions<'a> {
726 pub fn new() -> Result<CompileOptions<'a>> {
734 let p = unsafe { scs::shaderc_compile_options_initialize() };
735 if p.is_null() {
736 Err(Error::InitializationError(
737 "failed to create CompileOptions".to_string(),
738 ))
739 } else {
740 Ok(CompileOptions {
741 raw: p,
742 include_callback_fn: None,
743 })
744 }
745 }
746
747 pub fn set_target_env(&mut self, env: TargetEnv, version: u32) {
760 unsafe { scs::shaderc_compile_options_set_target_env(self.raw, env as i32, version) }
761 }
762
763 pub fn set_target_spirv(&mut self, version: SpirvVersion) {
769 unsafe { scs::shaderc_compile_options_set_target_spirv(self.raw, version as i32) }
770 }
771
772 pub fn set_source_language(&mut self, language: SourceLanguage) {
776 unsafe { scs::shaderc_compile_options_set_source_language(self.raw, language as i32) }
777 }
778
779 pub fn set_forced_version_profile(&mut self, version: u32, profile: GlslProfile) {
787 unsafe {
788 scs::shaderc_compile_options_set_forced_version_profile(
789 self.raw,
790 version as c_int,
791 profile as i32,
792 )
793 }
794 }
795
796 pub fn set_include_callback<F>(&mut self, f: F)
812 where
813 F: Fn(&str, IncludeType, &str, usize) -> IncludeCallbackResult + 'a,
814 {
815 use std::mem;
816
817 let f = Rc::new(f);
818 let f_ptr = &*f as *const F;
819 self.include_callback_fn = Some(f as BoxedIncludeCallback<'a>);
820 unsafe {
821 scs::shaderc_compile_options_set_include_callbacks(
822 self.raw,
823 resolver::<'a, F>,
824 releaser,
825 f_ptr as *const c_void as *mut c_void,
826 );
827 }
828
829 struct OkResultWrapper {
830 source_name: CString,
831 content: CString,
832 wrapped: scs::shaderc_include_result,
833 }
834
835 struct ErrResultWrapper {
836 error_message: CString,
837 wrapped: scs::shaderc_include_result,
838 }
839
840 extern "C" fn resolver<'a, F>(
841 user_data: *mut c_void,
842 requested_source: *const c_char,
843 type_: c_int,
844 requesting_source: *const c_char,
845 include_depth: size_t,
846 ) -> *mut scs::shaderc_include_result
847 where
848 F: Fn(&str, IncludeType, &str, usize) -> IncludeCallbackResult + 'a,
849 {
850 let result = panic::catch_unwind(move || {
851 let f = unsafe { &*(user_data as *const F) };
852 let requested_source =
853 unsafe { CStr::from_ptr(requested_source).to_string_lossy() };
854 let type_ = match type_ {
855 0 => IncludeType::Relative,
856 1 => IncludeType::Standard,
857 x => panic!(
858 "include callback: unknown include type returned from libshaderc: {}",
859 x
860 ),
861 };
862 let requesting_source =
863 unsafe { CStr::from_ptr(requesting_source).to_string_lossy() };
864 match f(&requested_source, type_, &requesting_source, include_depth) {
865 Ok(ResolvedInclude {
866 resolved_name,
867 content,
868 }) => {
869 if resolved_name.is_empty() {
870 panic!("include callback: empty strings for resolved include names not allowed");
871 }
872 let mut result = Box::new(OkResultWrapper {
873 source_name: CString::new(resolved_name).expect("include callback: could not convert resolved source name to a c string"),
874 content: CString::new(content).expect("include callback: could not convert content string to a c string"),
875 wrapped: unsafe { mem::zeroed() },
876 });
877 result.wrapped = scs::shaderc_include_result {
878 source_name: result.source_name.as_ptr(),
879 source_name_length: result.source_name.as_bytes().len(),
880 content: result.content.as_ptr(),
881 content_length: result.content.as_bytes().len(),
882 user_data: &mut *result as *mut OkResultWrapper as *mut c_void,
883 };
884 let r = &mut result.wrapped as *mut scs::shaderc_include_result;
885 mem::forget(result);
886 r
887 }
888 Err(error_message) => {
889 let mut result = Box::new(ErrResultWrapper {
890 error_message: CString::new(error_message).expect(
891 "include callback: could not convert error message to a c string",
892 ),
893 wrapped: unsafe { mem::zeroed() },
894 });
895 result.wrapped = scs::shaderc_include_result {
896 source_name: CStr::from_bytes_with_nul(b"\0").unwrap().as_ptr(),
897 source_name_length: 0,
898 content: result.error_message.as_ptr(),
899 content_length: result.error_message.as_bytes().len(),
900 user_data: &mut *result as *mut ErrResultWrapper as *mut c_void,
901 };
902 let r = &mut result.wrapped as *mut scs::shaderc_include_result;
903 mem::forget(result);
904 r
905 }
906 }
907 });
908 match result {
909 Ok(r) => r,
910 Err(e) => {
911 PANIC_ERROR.with(|panic_error| {
912 *panic_error.borrow_mut() = Some(e);
913 });
914 let mut result = Box::new(ErrResultWrapper {
915 error_message: CString::new("").unwrap(),
916 wrapped: unsafe { mem::zeroed() },
917 });
918 result.wrapped = scs::shaderc_include_result {
919 source_name: CStr::from_bytes_with_nul(b"\0").unwrap().as_ptr(),
920 source_name_length: 0,
921 content: result.error_message.as_ptr(),
922 content_length: 0,
923 user_data: &mut *result as *mut ErrResultWrapper as *mut c_void,
924 };
925 let r = &mut result.wrapped as *mut scs::shaderc_include_result;
926 mem::forget(result);
927 r
928 }
929 }
930 }
931
932 extern "C" fn releaser(_: *mut c_void, include_result: *mut scs::shaderc_include_result) {
933 let user_data = unsafe { &*include_result }.user_data;
934 if unsafe { &*include_result }.source_name_length == 0 {
935 let wrapper = unsafe { Box::from_raw(user_data as *mut ErrResultWrapper) };
936 drop(wrapper);
937 } else {
938 let wrapper = unsafe { Box::from_raw(user_data as *mut OkResultWrapper) };
939 drop(wrapper);
940 }
941 }
942 }
943
944 pub fn set_limit(&mut self, limit: Limit, value: i32) {
946 unsafe { scs::shaderc_compile_options_set_limit(self.raw, limit as i32, value as c_int) }
947 }
948
949 pub fn set_auto_bind_uniforms(&mut self, auto_bind: bool) {
952 unsafe {
953 scs::shaderc_compile_options_set_auto_bind_uniforms(self.raw, auto_bind);
954 }
955 }
956
957 pub fn set_auto_combined_image_sampler(&mut self, auto_combine: bool) {
960 unsafe {
961 scs::shaderc_compile_options_set_auto_combined_image_sampler(self.raw, auto_combine);
962 }
963 }
964
965 pub fn set_hlsl_io_mapping(&mut self, hlsl_iomap: bool) {
969 unsafe {
970 scs::shaderc_compile_options_set_hlsl_io_mapping(self.raw, hlsl_iomap);
971 }
972 }
973
974 pub fn set_hlsl_offsets(&mut self, hlsl_offsets: bool) {
980 unsafe {
981 scs::shaderc_compile_options_set_hlsl_offsets(self.raw, hlsl_offsets);
982 }
983 }
984
985 pub fn set_binding_base(&mut self, resource_kind: ResourceKind, base: u32) {
992 unsafe {
993 scs::shaderc_compile_options_set_binding_base(self.raw, resource_kind as i32, base);
994 }
995 }
996
997 pub fn set_binding_base_for_stage(
999 &mut self,
1000 shader_kind: ShaderKind,
1001 resource_kind: ResourceKind,
1002 base: u32,
1003 ) {
1004 unsafe {
1005 scs::shaderc_compile_options_set_binding_base_for_stage(
1006 self.raw,
1007 shader_kind as i32,
1008 resource_kind as i32,
1009 base,
1010 );
1011 }
1012 }
1013
1014 pub fn set_hlsl_register_set_and_binding(&mut self, register: &str, set: &str, binding: &str) {
1016 let c_register = CString::new(register).expect("cannot convert register to c string");
1017 let c_set = CString::new(set).expect("cannot convert set to c string");
1018 let c_binding = CString::new(binding).expect("cannot convert binding to c string");
1019 unsafe {
1020 scs::shaderc_compile_options_set_hlsl_register_set_and_binding(
1021 self.raw,
1022 c_register.as_ptr(),
1023 c_set.as_ptr(),
1024 c_binding.as_ptr(),
1025 );
1026 }
1027 }
1028
1029 pub fn set_auto_map_locations(&mut self, auto_map: bool) {
1031 unsafe {
1032 scs::shaderc_compile_options_set_auto_map_locations(self.raw, auto_map);
1033 }
1034 }
1035
1036 pub fn set_hlsl_register_set_and_binding_for_stage(
1039 &mut self,
1040 kind: ShaderKind,
1041 register: &str,
1042 set: &str,
1043 binding: &str,
1044 ) {
1045 let c_register = CString::new(register).expect("cannot convert string to c string");
1046 let c_set = CString::new(set).expect("cannot convert string to c string");
1047 let c_binding = CString::new(binding).expect("cannot convert string to c string");
1048 unsafe {
1049 scs::shaderc_compile_options_set_hlsl_register_set_and_binding_for_stage(
1050 self.raw,
1051 kind as i32,
1052 c_register.as_ptr(),
1053 c_set.as_ptr(),
1054 c_binding.as_ptr(),
1055 );
1056 }
1057 }
1058
1059 pub fn set_hlsl_functionality1(&mut self, enable: bool) {
1061 unsafe {
1062 scs::shaderc_compile_options_set_hlsl_functionality1(self.raw, enable);
1063 }
1064 }
1065
1066 pub fn set_invert_y(&mut self, enable: bool) {
1068 unsafe {
1069 scs::shaderc_compile_options_set_invert_y(self.raw, enable);
1070 }
1071 }
1072
1073 pub fn set_nan_clamp(&mut self, enable: bool) {
1078 unsafe {
1079 scs::shaderc_compile_options_set_nan_clamp(self.raw, enable);
1080 }
1081 }
1082
1083 pub fn add_macro_definition(&mut self, name: &str, value: Option<&str>) {
1091 let c_name = CString::new(name).expect("cannot convert macro name to c string");
1092 if let Some(value) = value {
1093 let c_value = CString::new(value).expect("cannot convert macro value to c string");
1094 unsafe {
1095 scs::shaderc_compile_options_add_macro_definition(
1096 self.raw,
1097 c_name.as_ptr(),
1098 name.len(),
1099 c_value.as_ptr(),
1100 value.len(),
1101 )
1102 }
1103 } else {
1104 unsafe {
1105 scs::shaderc_compile_options_add_macro_definition(
1106 self.raw,
1107 c_name.as_ptr(),
1108 name.len(),
1109 ptr::null(),
1110 0,
1111 )
1112 }
1113 }
1114 }
1115
1116 pub fn set_optimization_level(&mut self, level: OptimizationLevel) {
1120 unsafe { scs::shaderc_compile_options_set_optimization_level(self.raw, level as i32) }
1121 }
1122
1123 pub fn set_generate_debug_info(&mut self) {
1125 unsafe { scs::shaderc_compile_options_set_generate_debug_info(self.raw) }
1126 }
1127
1128 pub fn set_suppress_warnings(&mut self) {
1134 unsafe { scs::shaderc_compile_options_set_suppress_warnings(self.raw) }
1135 }
1136
1137 pub fn set_warnings_as_errors(&mut self) {
1141 unsafe { scs::shaderc_compile_options_set_warnings_as_errors(self.raw) }
1142 }
1143}
1144
1145impl Clone for CompileOptions<'_> {
1146 fn clone(&self) -> Self {
1147 let p = unsafe { scs::shaderc_compile_options_clone(self.raw) };
1148
1149 assert!(
1155 !p.is_null(),
1156 "shaderc_compile_options_clone() returned NULL"
1157 );
1158
1159 CompileOptions {
1160 raw: p,
1161 include_callback_fn: self.include_callback_fn.clone(),
1162 }
1163 }
1164}
1165
1166impl Drop for CompileOptions<'_> {
1167 fn drop(&mut self) {
1168 unsafe { scs::shaderc_compile_options_release(self.raw) }
1169 }
1170}
1171
1172pub struct CompilationArtifact {
1174 raw: *mut scs::ShadercCompilationResult,
1175 is_binary: bool,
1176}
1177
1178impl CompilationArtifact {
1179 fn new(result: *mut scs::ShadercCompilationResult, is_binary: bool) -> CompilationArtifact {
1180 CompilationArtifact {
1181 raw: result,
1182 is_binary,
1183 }
1184 }
1185
1186 pub fn len(&self) -> usize {
1188 unsafe { scs::shaderc_result_get_length(self.raw) }
1189 }
1190
1191 pub fn is_empty(&self) -> bool {
1193 self.len() == 0
1194 }
1195
1196 pub fn as_binary(&self) -> &[u32] {
1202 if !self.is_binary {
1203 panic!("not binary result")
1204 }
1205
1206 assert_eq!(0, self.len() % 4);
1207 let num_words = self.len() / 4;
1208
1209 unsafe {
1210 let p = scs::shaderc_result_get_bytes(self.raw);
1211 slice::from_raw_parts(p as *const u32, num_words)
1212 }
1213 }
1214
1215 pub fn as_binary_u8(&self) -> &[u8] {
1222 if !self.is_binary {
1223 panic!("not binary result")
1224 }
1225
1226 assert_eq!(0, self.len() % 4);
1227
1228 unsafe {
1229 let p = scs::shaderc_result_get_bytes(self.raw);
1230 slice::from_raw_parts(p as *const u8, self.len())
1231 }
1232 }
1233
1234 pub fn as_text(&self) -> String {
1240 if self.is_binary {
1241 panic!("not text result")
1242 }
1243 unsafe {
1244 let p = scs::shaderc_result_get_bytes(self.raw);
1245 let bytes = CStr::from_ptr(p).to_bytes();
1246 str::from_utf8(bytes)
1247 .expect("invalid utf-8 string")
1248 .to_string()
1249 }
1250 }
1251
1252 pub fn get_num_warnings(&self) -> u32 {
1254 unsafe { scs::shaderc_result_get_num_warnings(self.raw) as u32 }
1255 }
1256
1257 pub fn get_warning_messages(&self) -> String {
1259 unsafe {
1260 let p = scs::shaderc_result_get_error_message(self.raw);
1261 let bytes = CStr::from_ptr(p).to_bytes();
1262 safe_str_from_utf8(bytes)
1263 }
1264 }
1265}
1266
1267impl Drop for CompilationArtifact {
1268 fn drop(&mut self) {
1269 unsafe { scs::shaderc_result_release(self.raw) }
1270 }
1271}
1272
1273pub fn get_spirv_version() -> (u32, u32) {
1279 let mut version: i32 = 0;
1280 let mut revision: i32 = 0;
1281 unsafe { scs::shaderc_get_spv_version(&mut version, &mut revision) };
1282 (version as u32, revision as u32)
1283}
1284
1285pub fn parse_version_profile(string: &str) -> Result<(u32, GlslProfile)> {
1290 let mut version: i32 = 0;
1291 let mut profile: i32 = 0;
1292 let c_string = CString::new(string).expect("cannot convert string to c string");
1293 let result = unsafe {
1294 scs::shaderc_parse_version_profile(c_string.as_ptr(), &mut version, &mut profile)
1295 };
1296 if !result {
1297 Err(Error::ParseError(format!(
1298 "failed to parse version/profile from '{string}'"
1299 )))
1300 } else {
1301 let p = match profile {
1302 0 => GlslProfile::None,
1303 1 => GlslProfile::Core,
1304 2 => GlslProfile::Compatibility,
1305 3 => GlslProfile::Es,
1306 _ => panic!("internal error: unhandled profile value {}", profile),
1307 };
1308 Ok((version as u32, p))
1309 }
1310}
1311
1312#[cfg(test)]
1313mod tests {
1314 use super::*;
1315
1316 static VOID_MAIN: &str = "#version 310 es\n void main() {}";
1317 static VOID_E: &str = "#version 310 es\n void E() {}";
1318 static EXTRA_E: &str = "#version 310 es\n E\n void main() {}";
1319 static IFDEF_E: &str = "#version 310 es\n #ifdef E\n void main() {}\n\
1320 #else\n #error\n #endif";
1321 static HLSL_VERTEX: &str = "float4 main(uint index: SV_VERTEXID): SV_POSITION\n\
1322 { return float4(1., 2., 3., 4.); }";
1323 static TWO_ERROR: &str = "#version 310 es\n #error one\n #error two\n void main() {}";
1324 static TWO_ERROR_MSG: &str = "shader.glsl:2: error: '#error' : one\n\
1325 shader.glsl:3: error: '#error' : two\n";
1326 static ONE_WARNING: &str = "#version 400\n\
1327 layout(location = 0) attribute float x;\n void main() {}";
1328 static ONE_WARNING_MSG: &str = "\
1329shader.glsl:2: warning: attribute deprecated in version 130; may be removed in future release
1330";
1331 static DEBUG_INFO: &str = "#version 140\n \
1332 void main() {\n vec2 debug_info_sample = vec2(1.0);\n }";
1333 static CORE_PROFILE: &str = "void main() {\n gl_ClipDistance[0] = 5.;\n }";
1334
1335 static TWO_FN: &str = "\
1336#version 450
1337layout(location=0) in int inVal;
1338layout(location=0) out int outVal;
1339int foo(int a) { return a; }
1340void main() { outVal = foo(inVal); }";
1341
1342 static COMPAT_FRAG: &str = "\
1344#version 100
1345layout(binding = 0) uniform highp sampler2D tex;
1346void main() {
1347 gl_FragColor = texture2D(tex, vec2(0.));
1348}";
1349
1350 static VOID_MAIN_ASSEMBLY: &str = "\
1351; SPIR-V
1352; Version: 1.0
1353; Generator: Google Shaderc over Glslang; 11
1354; Bound: 6
1355; Schema: 0
1356 OpCapability Shader
1357 %1 = OpExtInstImport \"GLSL.std.450\"
1358 OpMemoryModel Logical GLSL450
1359 OpEntryPoint Vertex %main \"main\"
1360 OpSource ESSL 310
1361 OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"
1362 OpSourceExtension \"GL_GOOGLE_include_directive\"
1363 OpName %main \"main\"
1364 %void = OpTypeVoid
1365 %3 = OpTypeFunction %void
1366 %main = OpFunction %void None %3
1367 %5 = OpLabel
1368 OpReturn
1369 OpFunctionEnd
1370";
1371
1372 static UNIFORMS_NO_BINDINGS: &str = "\
1373#version 450
1374#extension GL_ARB_sparse_texture2 : enable
1375uniform texture2D my_tex;
1376uniform sampler my_sam;
1377layout(rgba32f) uniform image2D my_img;
1378layout(rgba32f) uniform imageBuffer my_imbuf;
1379uniform block { float x; float y; } my_ubo;
1380void main() {
1381 texture(sampler2D(my_tex,my_sam),vec2(1.0));
1382 vec4 t;
1383 sparseImageLoadARB(my_img,ivec2(0),t);
1384 imageLoad(my_imbuf,42);
1385 float x = my_ubo.x;
1386}";
1387
1388 static GLSL_EXPLICT_BINDING: &str = "\
1389#version 450
1390layout(set=0, binding=0)
1391buffer B { float x; vec3 y; } my_ssbo;
1392void main() { my_ssbo.x = 1.0; }";
1393
1394 #[test]
1395 fn test_compile_vertex_shader_into_spirv() {
1396 let c = Compiler::new().unwrap();
1397 let result = c
1398 .compile_into_spirv(VOID_MAIN, ShaderKind::Vertex, "shader.glsl", "main", None)
1399 .unwrap();
1400 assert!(result.len() > 20);
1401 assert!(result.as_binary().first() == Some(&0x0723_0203));
1402 let function_end_word: u32 = (1 << 16) | 56;
1403 assert!(result.as_binary().last() == Some(&function_end_word));
1404 }
1405
1406 #[test]
1407 fn test_compile_vertex_shader_into_spirv_assembly() {
1408 let c = Compiler::new().unwrap();
1409 let result = c
1410 .compile_into_spirv_assembly(VOID_MAIN, ShaderKind::Vertex, "shader.glsl", "main", None)
1411 .unwrap();
1412 assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1413 }
1414
1415 #[test]
1416 fn test_preprocess() {
1417 let c = Compiler::new().unwrap();
1418 let mut options = CompileOptions::new().unwrap();
1419 options.add_macro_definition("E", Some("main"));
1420 let result = c
1421 .preprocess(VOID_E, "shader.glsl", "main", Some(&options))
1422 .unwrap();
1423 assert_eq!("#version 310 es\n void main() { }\n", result.as_text());
1424 }
1425
1426 #[test]
1427 fn test_assemble() {
1428 let c = Compiler::new().unwrap();
1429 let result = c.assemble(VOID_MAIN_ASSEMBLY, None).unwrap();
1430 assert!(result.len() > 20);
1431 assert!(result.as_binary().first() == Some(&0x0723_0203));
1432 let function_end_word: u32 = (1 << 16) | 56;
1433 assert!(result.as_binary().last() == Some(&function_end_word));
1434 }
1435
1436 #[test]
1437 fn test_compile_options_add_macro_definition_normal_value() {
1438 let c = Compiler::new().unwrap();
1439 let mut options = CompileOptions::new().unwrap();
1440 options.add_macro_definition("E", Some("main"));
1441 let result = c
1442 .compile_into_spirv_assembly(
1443 VOID_E,
1444 ShaderKind::Vertex,
1445 "shader.glsl",
1446 "main",
1447 Some(&options),
1448 )
1449 .unwrap();
1450 assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1451 }
1452
1453 #[test]
1454 fn test_compile_options_add_macro_definition_empty_value() {
1455 let c = Compiler::new().unwrap();
1456 let mut options = CompileOptions::new().unwrap();
1457 options.add_macro_definition("E", Some(""));
1458 let result = c
1459 .compile_into_spirv_assembly(
1460 EXTRA_E,
1461 ShaderKind::Vertex,
1462 "shader.glsl",
1463 "main",
1464 Some(&options),
1465 )
1466 .unwrap();
1467 assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1468 }
1469
1470 #[test]
1471 fn test_compile_options_add_macro_definition_no_value() {
1472 let c = Compiler::new().unwrap();
1473 let mut options = CompileOptions::new().unwrap();
1474 options.add_macro_definition("E", None);
1475 let result = c
1476 .compile_into_spirv_assembly(
1477 IFDEF_E,
1478 ShaderKind::Vertex,
1479 "shader.glsl",
1480 "main",
1481 Some(&options),
1482 )
1483 .unwrap();
1484 assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1485 }
1486
1487 #[test]
1488 fn test_compile_options_clone() {
1489 let c = Compiler::new().unwrap();
1490 let mut options = CompileOptions::new().unwrap();
1491 options.add_macro_definition("E", None);
1492 let o = options.clone();
1493 let result = c
1494 .compile_into_spirv_assembly(
1495 IFDEF_E,
1496 ShaderKind::Vertex,
1497 "shader.glsl",
1498 "main",
1499 Some(&o),
1500 )
1501 .unwrap();
1502 assert_eq!(VOID_MAIN_ASSEMBLY, result.as_text());
1503 }
1504
1505 #[test]
1506 fn test_compile_options_set_source_language() {
1507 let c = Compiler::new().unwrap();
1508 let mut options = CompileOptions::new().unwrap();
1509 options.set_source_language(SourceLanguage::HLSL);
1510 let result = c
1511 .compile_into_spirv(
1512 HLSL_VERTEX,
1513 ShaderKind::Vertex,
1514 "shader.hlsl",
1515 "main",
1516 Some(&options),
1517 )
1518 .unwrap();
1519 assert!(result.len() > 20);
1520 assert!(result.as_binary().first() == Some(&0x0723_0203));
1521 let function_end_word: u32 = (1 << 16) | 56;
1522 assert!(result.as_binary().last() == Some(&function_end_word));
1523 }
1524
1525 #[test]
1526 fn test_compile_options_set_generate_debug_info() {
1527 let c = Compiler::new().unwrap();
1528 let mut options = CompileOptions::new().unwrap();
1529 options.set_generate_debug_info();
1530 let result = c
1531 .compile_into_spirv_assembly(
1532 DEBUG_INFO,
1533 ShaderKind::Vertex,
1534 "shader.glsl",
1535 "main",
1536 Some(&options),
1537 )
1538 .unwrap();
1539 assert!(result.as_text().contains("debug_info_sample"));
1540 }
1541
1542 #[test]
1543 fn test_compile_options_set_optimization_level_zero() {
1544 let c = Compiler::new().unwrap();
1545 let mut options = CompileOptions::new().unwrap();
1546 options.set_optimization_level(OptimizationLevel::Zero);
1547 let result = c
1548 .compile_into_spirv_assembly(
1549 DEBUG_INFO,
1550 ShaderKind::Vertex,
1551 "shader.glsl",
1552 "main",
1553 Some(&options),
1554 )
1555 .unwrap();
1556 assert!(result.as_text().contains("OpName"));
1557 assert!(result.as_text().contains("OpSource"));
1558 }
1559
1560 #[test]
1561 fn test_compile_options_set_optimization_level_size() {
1562 let c = Compiler::new().unwrap();
1563 let mut options = CompileOptions::new().unwrap();
1564 options.set_optimization_level(OptimizationLevel::Size);
1565 let result = c
1566 .compile_into_spirv_assembly(
1567 TWO_FN,
1568 ShaderKind::Vertex,
1569 "shader.glsl",
1570 "main",
1571 Some(&options),
1572 )
1573 .unwrap();
1574 assert!(!result.as_text().contains("OpFunctionCall"));
1575 }
1576
1577 #[test]
1578 fn test_compile_options_set_optimization_level_performance() {
1579 let c = Compiler::new().unwrap();
1580 let mut options = CompileOptions::new().unwrap();
1581 options.set_optimization_level(OptimizationLevel::Performance);
1582 let result = c
1583 .compile_into_spirv_assembly(
1584 TWO_FN,
1585 ShaderKind::Vertex,
1586 "shader.glsl",
1587 "main",
1588 Some(&options),
1589 )
1590 .unwrap();
1591 assert!(!result.as_text().contains("OpFunctionCall"));
1592 }
1593
1594 #[test]
1595 fn test_compile_options_set_forced_version_profile_ok() {
1596 let c = Compiler::new().unwrap();
1597 let mut options = CompileOptions::new().unwrap();
1598 options.set_forced_version_profile(450, GlslProfile::Core);
1599 let result = c
1600 .compile_into_spirv(
1601 CORE_PROFILE,
1602 ShaderKind::Vertex,
1603 "shader.glsl",
1604 "main",
1605 Some(&options),
1606 )
1607 .unwrap();
1608 assert!(result.len() > 20);
1609 assert!(result.as_binary().first() == Some(&0x0723_0203));
1610 let function_end_word: u32 = (1 << 16) | 56;
1611 assert!(result.as_binary().last() == Some(&function_end_word));
1612 }
1613
1614 #[test]
1615 fn test_compile_options_set_forced_version_profile_err() {
1616 let c = Compiler::new().unwrap();
1617 let mut options = CompileOptions::new().unwrap();
1618 options.set_forced_version_profile(310, GlslProfile::Es);
1619 let result = c.compile_into_spirv(
1620 CORE_PROFILE,
1621 ShaderKind::Vertex,
1622 "shader.glsl",
1623 "main",
1624 Some(&options),
1625 );
1626 assert!(result.is_err());
1627 assert_matches!(result.err(),
1628 Some(Error::CompilationError(3, ref s))
1629 if s.contains("error: 'gl_ClipDistance' : undeclared identifier"));
1630 }
1631
1632 #[test]
1633 #[should_panic(expected = "panic in include resolver!")]
1634 fn test_include_directive_panic() {
1635 let c = Compiler::new().unwrap();
1636 let mut options = CompileOptions::new().unwrap();
1637 options.set_include_callback(|_, _, _, _| panic!("panic in include resolver!"));
1638 drop(c.compile_into_spirv_assembly(
1639 r#"
1640 #version 400
1641 #include "foo.glsl"
1642 "#,
1643 ShaderKind::Vertex,
1644 "shader.glsl",
1645 "main",
1646 Some(&options),
1647 ));
1648 }
1649
1650 #[test]
1651 fn test_include_directive_err() {
1652 let c = Compiler::new().unwrap();
1653 let mut options = CompileOptions::new().unwrap();
1654 options
1655 .set_include_callback(|name, _, _, _| Err(format!("couldn't find header \"{name}\"")));
1656 let result = c.compile_into_spirv_assembly(
1657 r#"
1658 #version 400
1659 #include "foo.glsl"
1660 "#,
1661 ShaderKind::Vertex,
1662 "shader.glsl",
1663 "main",
1664 Some(&options),
1665 );
1666 assert!(result.is_err());
1667 assert_matches!(result.err(),
1668 Some(Error::CompilationError(1, ref s))
1669 if s.contains("couldn't find header \"foo.glsl\""));
1670 }
1671
1672 #[test]
1673 fn test_include_directive_success() {
1674 let c = Compiler::new().unwrap();
1675 let mut options = CompileOptions::new().unwrap();
1676 options.set_include_callback(|name, type_, _, _| {
1677 if name == "foo.glsl" && type_ == IncludeType::Relative {
1678 Ok(ResolvedInclude {
1679 resolved_name: "std/foo.glsl".to_string(),
1680 content: r#"
1681 #ifndef FOO_H
1682 #define FOO_H
1683 void main() {}
1684 #endif
1685 "#
1686 .to_string(),
1687 })
1688 } else {
1689 Err(format!("couldn't find header \"{name}\""))
1690 }
1691 });
1692 let result = c.compile_into_spirv_assembly(
1693 r#"
1694 #version 400
1695 #include "foo.glsl"
1696 #include "foo.glsl"
1697 "#,
1698 ShaderKind::Vertex,
1699 "shader.glsl",
1700 "main",
1701 Some(&options),
1702 );
1703 assert_matches!(result.err(), None);
1704 }
1705
1706 #[test]
1707 fn test_compile_options_set_suppress_warnings() {
1708 let c = Compiler::new().unwrap();
1709 let mut options = CompileOptions::new().unwrap();
1710 options.set_suppress_warnings();
1711 let result = c
1712 .compile_into_spirv(
1713 ONE_WARNING,
1714 ShaderKind::Vertex,
1715 "shader.glsl",
1716 "main",
1717 Some(&options),
1718 )
1719 .unwrap();
1720 assert_eq!(0, result.get_num_warnings());
1721 }
1722
1723 #[test]
1724 fn test_compile_options_set_warnings_as_errors() {
1725 let c = Compiler::new().unwrap();
1726 let mut options = CompileOptions::new().unwrap();
1727 options.set_warnings_as_errors();
1728 let result = c.compile_into_spirv(
1729 ONE_WARNING,
1730 ShaderKind::Vertex,
1731 "shader.glsl",
1732 "main",
1733 Some(&options),
1734 );
1735 assert!(result.is_err());
1736 assert_matches!(result.err(),
1737 Some(Error::CompilationError(1, ref s))
1738 if s.contains("error: attribute deprecated in version 130;"));
1739 }
1740
1741 #[test]
1742 fn test_compile_options_set_target_env_err_vulkan() {
1743 let c = Compiler::new().unwrap();
1744 let result = c.compile_into_spirv(
1745 COMPAT_FRAG,
1746 ShaderKind::Fragment,
1747 "shader.glsl",
1748 "main",
1749 None,
1750 );
1751 assert!(result.is_err());
1752 assert_matches!(result.err(),
1753 Some(Error::CompilationError(4, ref s))
1754 if s.contains("error: #version: ES shaders for SPIR-V \
1755 require version 310 or higher"));
1756 }
1757
1758 #[test]
1759 fn test_compile_options_set_target_env_err_opengl() {
1760 let c = Compiler::new().unwrap();
1761 let mut options = CompileOptions::new().unwrap();
1762 options.set_target_env(TargetEnv::OpenGL, 0);
1763 let result = c.compile_into_spirv(
1764 COMPAT_FRAG,
1765 ShaderKind::Fragment,
1766 "shader.glsl",
1767 "main",
1768 Some(&options),
1769 );
1770 assert!(result.is_err());
1771 assert_matches!(result.err(),
1772 Some(Error::CompilationError(3, ref s))
1773 if s.contains("error: #version: ES shaders for SPIR-V require \
1774 version 310 or higher"));
1775 }
1776
1777 macro_rules! texture_offset {
1779 ($offset:expr) => {{
1780 let mut s = "#version 450
1781 layout (binding=0) uniform sampler1D tex;
1782 void main() {
1783 vec4 x = textureOffset(tex, 1., "
1784 .to_string();
1785 s.push_str(stringify!($offset));
1786 s.push_str(");\n}");
1787 s
1788 }};
1789 }
1790
1791 #[test]
1792 fn test_compile_options_set_limit() {
1793 let c = Compiler::new().unwrap();
1794 let mut options = CompileOptions::new().unwrap();
1795 assert!(c
1796 .compile_into_spirv(
1797 &texture_offset!(7),
1798 ShaderKind::Fragment,
1799 "shader.glsl",
1800 "main",
1801 Some(&options)
1802 )
1803 .is_ok());
1804 assert!(c
1805 .compile_into_spirv(
1806 &texture_offset!(8),
1807 ShaderKind::Fragment,
1808 "shader.glsl",
1809 "main",
1810 Some(&options)
1811 )
1812 .is_err());
1813 options.set_limit(Limit::MaxProgramTexelOffset, 10);
1814 assert!(c
1815 .compile_into_spirv(
1816 &texture_offset!(8),
1817 ShaderKind::Fragment,
1818 "shader.glsl",
1819 "main",
1820 Some(&options)
1821 )
1822 .is_ok());
1823 assert!(c
1824 .compile_into_spirv(
1825 &texture_offset!(10),
1826 ShaderKind::Fragment,
1827 "shader.glsl",
1828 "main",
1829 Some(&options)
1830 )
1831 .is_ok());
1832 assert!(c
1833 .compile_into_spirv(
1834 &texture_offset!(11),
1835 ShaderKind::Fragment,
1836 "shader.glsl",
1837 "main",
1838 Some(&options)
1839 )
1840 .is_err());
1841 }
1842
1843 #[test]
1844 fn test_compile_options_set_auto_bind_uniforms_false() {
1845 let c = Compiler::new().unwrap();
1846 let mut options = CompileOptions::new().unwrap();
1847 options.set_auto_bind_uniforms(false);
1848 let result = c.compile_into_spirv_assembly(
1849 UNIFORMS_NO_BINDINGS,
1850 ShaderKind::Vertex,
1851 "shader.glsl",
1852 "main",
1853 Some(&options),
1854 );
1855 assert!(result.is_err());
1856 assert_matches!(result.err(),
1857 Some(Error::CompilationError(_, ref s))
1858 if s.contains("error: 'binding' : sampler/texture/image requires layout(binding=X)"));
1859 }
1860
1861 #[test]
1862 fn test_compile_options_set_auto_bind_uniforms_true() {
1863 let c = Compiler::new().unwrap();
1864 let mut options = CompileOptions::new().unwrap();
1865 options.set_auto_bind_uniforms(true);
1866 let result = c
1867 .compile_into_spirv_assembly(
1868 UNIFORMS_NO_BINDINGS,
1869 ShaderKind::Vertex,
1870 "shader.glsl",
1871 "main",
1872 Some(&options),
1873 )
1874 .unwrap()
1875 .as_text();
1876 assert!(result.contains("OpDecorate %my_tex Binding 0"));
1877 assert!(result.contains("OpDecorate %my_sam Binding 1"));
1878 assert!(result.contains("OpDecorate %my_img Binding 2"));
1879 assert!(result.contains("OpDecorate %my_imbuf Binding 3"));
1880 assert!(result.contains("OpDecorate %my_ubo Binding 4"));
1881 }
1882
1883 #[test]
1884 fn test_compile_options_set_hlsl_offsets_false() {
1885 let c = Compiler::new().unwrap();
1886 let mut options = CompileOptions::new().unwrap();
1887 options.set_hlsl_offsets(false);
1888 let result = c
1889 .compile_into_spirv_assembly(
1890 GLSL_EXPLICT_BINDING,
1891 ShaderKind::Vertex,
1892 "shader.glsl",
1893 "main",
1894 Some(&options),
1895 )
1896 .unwrap()
1897 .as_text();
1898 assert!(result.contains("OpMemberDecorate %B 1 Offset 16"));
1899 }
1900
1901 #[test]
1902 fn test_compile_options_set_hlsl_offsets_true() {
1903 let c = Compiler::new().unwrap();
1904 let mut options = CompileOptions::new().unwrap();
1905 options.set_hlsl_offsets(true);
1906 let result = c
1907 .compile_into_spirv_assembly(
1908 GLSL_EXPLICT_BINDING,
1909 ShaderKind::Vertex,
1910 "shader.glsl",
1911 "main",
1912 Some(&options),
1913 )
1914 .unwrap()
1915 .as_text();
1916 assert!(result.contains("OpMemberDecorate %B 1 Offset 4"));
1917 }
1918
1919 #[test]
1920 fn test_compile_options_set_binding_base() {
1921 let c = Compiler::new().unwrap();
1922 let mut options = CompileOptions::new().unwrap();
1923 options.set_auto_bind_uniforms(true);
1924 options.set_binding_base(ResourceKind::Image, 44);
1925 let result = c
1926 .compile_into_spirv_assembly(
1927 UNIFORMS_NO_BINDINGS,
1928 ShaderKind::Vertex,
1929 "shader.glsl",
1930 "main",
1931 Some(&options),
1932 )
1933 .unwrap()
1934 .as_text();
1935 assert!(result.contains("OpDecorate %my_tex Binding 0"));
1936 assert!(result.contains("OpDecorate %my_sam Binding 1"));
1937 assert!(result.contains("OpDecorate %my_img Binding 44"));
1938 assert!(result.contains("OpDecorate %my_imbuf Binding 45"));
1939 assert!(result.contains("OpDecorate %my_ubo Binding 2"));
1940 }
1941
1942 #[test]
1943 fn test_compile_options_set_binding_base_for_stage_effective() {
1944 let c = Compiler::new().unwrap();
1945 let mut options = CompileOptions::new().unwrap();
1946 options.set_auto_bind_uniforms(true);
1947 options.set_binding_base_for_stage(ShaderKind::Vertex, ResourceKind::Texture, 100);
1948 let result = c
1949 .compile_into_spirv_assembly(
1950 UNIFORMS_NO_BINDINGS,
1951 ShaderKind::Vertex,
1952 "shader.glsl",
1953 "main",
1954 Some(&options),
1955 )
1956 .unwrap()
1957 .as_text();
1958 assert!(result.contains("OpDecorate %my_tex Binding 100"));
1959 assert!(result.contains("OpDecorate %my_sam Binding 0"));
1960 assert!(result.contains("OpDecorate %my_img Binding 1"));
1961 assert!(result.contains("OpDecorate %my_imbuf Binding 2"));
1962 assert!(result.contains("OpDecorate %my_ubo Binding 3"));
1963 }
1964
1965 #[test]
1966 fn test_compile_options_set_binding_base_for_stage_ignore() {
1967 let c = Compiler::new().unwrap();
1968 let mut options = CompileOptions::new().unwrap();
1969 options.set_auto_bind_uniforms(true);
1970 options.set_binding_base_for_stage(ShaderKind::Fragment, ResourceKind::Texture, 100);
1971 let result = c
1972 .compile_into_spirv_assembly(
1973 UNIFORMS_NO_BINDINGS,
1974 ShaderKind::Vertex,
1975 "shader.glsl",
1976 "main",
1977 Some(&options),
1978 )
1979 .unwrap()
1980 .as_text();
1981 assert!(result.contains("OpDecorate %my_tex Binding 0"));
1982 assert!(result.contains("OpDecorate %my_sam Binding 1"));
1983 assert!(result.contains("OpDecorate %my_img Binding 2"));
1984 assert!(result.contains("OpDecorate %my_imbuf Binding 3"));
1985 assert!(result.contains("OpDecorate %my_ubo Binding 4"));
1986 }
1987
1988 #[test]
1989 fn test_error_compilation_error() {
1990 let c = Compiler::new().unwrap();
1991 let result =
1992 c.compile_into_spirv(TWO_ERROR, ShaderKind::Vertex, "shader.glsl", "main", None);
1993 assert!(result.is_err());
1994 assert_eq!(
1995 Some(Error::CompilationError(2, TWO_ERROR_MSG.to_string())),
1996 result.err()
1997 );
1998 }
1999
2000 #[test]
2001 fn test_error_invalid_stage() {
2002 let c = Compiler::new().unwrap();
2003 let result = c.compile_into_spirv(
2004 VOID_MAIN,
2005 ShaderKind::InferFromSource,
2006 "shader.glsl",
2007 "main",
2008 None,
2009 );
2010 assert!(result.is_err());
2011 assert_eq!(Some(Error::InvalidStage("".to_string())), result.err());
2012 }
2013
2014 #[test]
2015 fn test_warning() {
2016 let c = Compiler::new().unwrap();
2017 let result = c
2018 .compile_into_spirv(ONE_WARNING, ShaderKind::Vertex, "shader.glsl", "main", None)
2019 .unwrap();
2020 assert_eq!(1, result.get_num_warnings());
2021 assert_eq!(ONE_WARNING_MSG.to_string(), result.get_warning_messages());
2022 }
2023
2024 #[test]
2025 fn test_get_spirv_version() {
2026 let (version, _) = get_spirv_version();
2027 assert_eq!((1 << 16) + (6 << 8), version);
2028 }
2029
2030 #[test]
2031 fn test_parse_version_profile() {
2032 assert_eq!(Ok((310, GlslProfile::Es)), parse_version_profile("310es"));
2033 assert_eq!(
2034 Ok((450, GlslProfile::Compatibility)),
2035 parse_version_profile("450compatibility")
2036 );
2037 assert_eq!(Ok((140, GlslProfile::None)), parse_version_profile("140"));
2038 assert_eq!(
2039 Err(Error::ParseError(
2040 "failed to parse version/profile from 'something'".to_string()
2041 )),
2042 parse_version_profile("something")
2043 );
2044 assert_eq!(
2045 Err(Error::ParseError(
2046 "failed to parse version/profile from ''".to_string()
2047 )),
2048 parse_version_profile("")
2049 );
2050 }
2051}