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