Skip to main content

bevy_glsl_to_spirv/
lib.rs

1// Copyright (c) 2016 The vulkano developers
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10use std::ffi::{CStr, CString};
11use std::os::raw::c_char;
12
13mod glslang_c_interface;
14
15use glslang_c_interface::*;
16
17pub type SpirvOutput = Vec<u32>;
18
19pub fn compile(
20    code: &str,
21    ty: ShaderType,
22    shader_defs: Option<&[String]>,
23) -> Result<SpirvOutput, String> {
24    compile_inner(Some((code, ty)), shader_defs)
25}
26
27// Eventually the API will look like this, with an iterator for multiple shader stages.
28// However for the moment GLSLang doesn't like that, so we only pass one shader at a time.
29fn compile_inner<'a, I>(shaders: I, shader_defs: Option<&[String]>) -> Result<SpirvOutput, String>
30where
31    I: IntoIterator<Item = (&'a str, ShaderType)>,
32{
33    let mut preamble = String::new();
34    if let Some(defs) = shader_defs {
35        for def in defs {
36            preamble.push_str("#define ");
37
38            let mut def = def.clone();
39            if let Some(end) = def.find('\n') {
40                def.truncate(end);
41            }
42
43            let def = def.replacen('=', " ", 1);
44
45            preamble.push_str(&def);
46            preamble.push('\n');
47        }
48    }
49
50    let mut data = Vec::new();
51
52    unsafe {
53        glslang_initialize_process();
54
55        for (source, ty) in shaders.into_iter() {
56            let stage = match ty {
57                ShaderType::Vertex => glslang_stage_t_GLSLANG_STAGE_VERTEX,
58                ShaderType::Fragment => glslang_stage_t_GLSLANG_STAGE_FRAGMENT,
59                ShaderType::Geometry => glslang_stage_t_GLSLANG_STAGE_GEOMETRY,
60                ShaderType::TessellationControl => glslang_stage_t_GLSLANG_STAGE_TESSCONTROL,
61                ShaderType::TessellationEvaluation => glslang_stage_t_GLSLANG_STAGE_TESSEVALUATION,
62                ShaderType::Compute => glslang_stage_t_GLSLANG_STAGE_COMPUTE,
63            };
64
65            let mut source = String::from(source);
66            if shader_defs.is_some() {
67                if let Some(version) = source.find(r##"#version"##) {
68                    if let Some(newline) = &source[version..].find('\n') {
69                        source.insert_str(version + newline + 1, &preamble);
70                    }
71                }
72            }
73
74            let c_str = CString::new(source.as_str()).unwrap();
75            let code: *const c_char = c_str.as_ptr();
76
77            let input = &glslang_input_t {
78                language: glslang_source_t_GLSLANG_SOURCE_GLSL,
79                stage,
80                client: glslang_client_t_GLSLANG_CLIENT_VULKAN,
81                client_version: glslang_target_client_version_t_GLSLANG_TARGET_VULKAN_1_0,
82                target_language: glslang_target_language_t_GLSLANG_TARGET_SPV,
83                target_language_version: glslang_target_language_version_t_GLSLANG_TARGET_SPV_1_0,
84                code,
85                default_version: 100,
86                default_profile: glslang_profile_t_GLSLANG_NO_PROFILE,
87                force_default_version_and_profile: 0,
88                forward_compatible: 0,
89                messages: glslang_messages_t_GLSLANG_MSG_DEFAULT_BIT
90                    | glslang_messages_t_GLSLANG_MSG_SPV_RULES_BIT
91                    | glslang_messages_t_GLSLANG_MSG_VULKAN_RULES_BIT,
92                resource: DEFAULT_RESOURCE_LIMITS,
93            };
94
95            let shader = glslang_shader_create(input);
96
97            if glslang_shader_preprocess(shader, input) == 0 {
98                let c_info: &CStr = CStr::from_ptr(glslang_shader_get_info_log(shader));
99                let c_debug: &CStr = CStr::from_ptr(glslang_shader_get_info_debug_log(shader));
100
101                let mut error = String::from("glslang_shader_preprocess:\n");
102                error.push_str(&format!("Info log:\n{}\n", c_info.to_str().unwrap()));
103                error.push_str(&format!("Debug log:\n{}\n", c_debug.to_str().unwrap()));
104
105                return Err(error);
106            }
107            if glslang_shader_parse(shader, input) == 0 {
108                let c_info: &CStr = CStr::from_ptr(glslang_shader_get_info_log(shader));
109                let c_debug: &CStr = CStr::from_ptr(glslang_shader_get_info_debug_log(shader));
110
111                let mut error = String::from("glslang_shader_parse:\n");
112                error.push_str(&format!("Info log:\n{}\n", c_info.to_str().unwrap()));
113                error.push_str(&format!("Debug log:\n{}\n", c_debug.to_str().unwrap()));
114
115                return Err(error);
116            }
117
118            let program = glslang_program_create();
119            glslang_program_add_shader(program, shader);
120
121            if glslang_program_link(
122                program,
123                glslang_messages_t_GLSLANG_MSG_SPV_RULES_BIT
124                    | glslang_messages_t_GLSLANG_MSG_VULKAN_RULES_BIT,
125            ) == 0
126            {
127                let c_info: &CStr = CStr::from_ptr(glslang_program_get_info_log(program));
128                let c_debug: &CStr = CStr::from_ptr(glslang_program_get_info_debug_log(program));
129
130                let mut error = String::from("glslang_program_link:\n");
131                error.push_str(&format!("Info log:\n{}\n", c_info.to_str().unwrap()));
132                error.push_str(&format!("Debug log:\n{}\n", c_debug.to_str().unwrap()));
133
134                return Err(error);
135            }
136
137            glslang_program_SPIRV_generate(program, input.stage);
138
139            if glslang_program_SPIRV_get_messages(program) != std::ptr::null() {
140                let c_messages: &CStr = CStr::from_ptr(glslang_program_SPIRV_get_messages(program));
141
142                println!("{:?}", c_messages);
143            }
144
145            let size = glslang_program_SPIRV_get_size(program) as usize;
146            let ptr = glslang_program_SPIRV_get_ptr(program) as *mut u32;
147            data = std::slice::from_raw_parts(ptr, size).to_vec();
148
149            glslang_program_delete(program);
150            glslang_shader_delete(shader);
151        }
152
153        glslang_finalize_process();
154    }
155
156    return Ok(data);
157}
158
159/// Type of shader.
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub enum ShaderType {
162    Vertex,
163    Fragment,
164    Geometry,
165    TessellationControl,
166    TessellationEvaluation,
167    Compute,
168}
169
170// values copied from glslang default resource limits
171const DEFAULT_RESOURCE_LIMITS: &glslang_resource_t = &glslang_resource_t {
172    max_lights: 32,
173    max_clip_planes: 6,
174    max_texture_units: 32,
175    max_texture_coords: 32,
176    max_vertex_attribs: 64,
177    max_vertex_uniform_components: 4096,
178    max_varying_floats: 64,
179    max_vertex_texture_image_units: 32,
180    max_combined_texture_image_units: 80,
181    max_texture_image_units: 32,
182    max_fragment_uniform_components: 4096,
183    max_draw_buffers: 32,
184    max_vertex_uniform_vectors: 128,
185    max_varying_vectors: 8,
186    max_fragment_uniform_vectors: 16,
187    max_vertex_output_vectors: 16,
188    max_fragment_input_vectors: 15,
189    min_program_texel_offset: -8,
190    max_program_texel_offset: 7,
191    max_clip_distances: 8,
192    max_compute_work_group_count_x: 65535,
193    max_compute_work_group_count_y: 65535,
194    max_compute_work_group_count_z: 65535,
195    max_compute_work_group_size_x: 1024,
196    max_compute_work_group_size_y: 1024,
197    max_compute_work_group_size_z: 64,
198    max_compute_uniform_components: 1024,
199    max_compute_texture_image_units: 16,
200    max_compute_image_uniforms: 8,
201    max_compute_atomic_counters: 8,
202    max_compute_atomic_counter_buffers: 1,
203    max_varying_components: 60,
204    max_vertex_output_components: 64,
205    max_geometry_input_components: 64,
206    max_geometry_output_components: 128,
207    max_fragment_input_components: 128,
208    max_image_units: 8,
209    max_combined_image_units_and_fragment_outputs: 8,
210    max_combined_shader_output_resources: 8,
211    max_image_samples: 0,
212    max_vertex_image_uniforms: 0,
213    max_tess_control_image_uniforms: 0,
214    max_tess_evaluation_image_uniforms: 0,
215    max_geometry_image_uniforms: 0,
216    max_fragment_image_uniforms: 8,
217    max_combined_image_uniforms: 8,
218    max_geometry_texture_image_units: 16,
219    max_geometry_output_vertices: 256,
220    max_geometry_total_output_components: 1024,
221    max_geometry_uniform_components: 1024,
222    max_geometry_varying_components: 64,
223    max_tess_control_input_components: 128,
224    max_tess_control_output_components: 128,
225    max_tess_control_texture_image_units: 16,
226    max_tess_control_uniform_components: 1024,
227    max_tess_control_total_output_components: 4096,
228    max_tess_evaluation_input_components: 128,
229    max_tess_evaluation_output_components: 128,
230    max_tess_evaluation_texture_image_units: 16,
231    max_tess_evaluation_uniform_components: 1024,
232    max_tess_patch_components: 120,
233    max_patch_vertices: 32,
234    max_tess_gen_level: 64,
235    max_viewports: 16,
236    max_vertex_atomic_counters: 0,
237    max_tess_control_atomic_counters: 0,
238    max_tess_evaluation_atomic_counters: 0,
239    max_geometry_atomic_counters: 0,
240    max_fragment_atomic_counters: 8,
241    max_combined_atomic_counters: 8,
242    max_atomic_counter_bindings: 1,
243    max_vertex_atomic_counter_buffers: 0,
244    max_tess_control_atomic_counter_buffers: 0,
245    max_tess_evaluation_atomic_counter_buffers: 0,
246    max_geometry_atomic_counter_buffers: 0,
247    max_fragment_atomic_counter_buffers: 1,
248    max_combined_atomic_counter_buffers: 1,
249    max_atomic_counter_buffer_size: 16384,
250    max_transform_feedback_buffers: 4,
251    max_transform_feedback_interleaved_components: 64,
252    max_cull_distances: 8,
253    max_combined_clip_and_cull_distances: 8,
254    max_samples: 4,
255    max_mesh_output_vertices_nv: 256,
256    max_mesh_output_primitives_nv: 512,
257    max_mesh_work_group_size_x_nv: 32,
258    max_mesh_work_group_size_y_nv: 1,
259    max_mesh_work_group_size_z_nv: 1,
260    max_task_work_group_size_x_nv: 32,
261    max_task_work_group_size_y_nv: 1,
262    max_task_work_group_size_z_nv: 1,
263    max_mesh_view_count_nv: 4,
264    limits: glslang_limits_s {
265        non_inductive_for_loops: true,
266        while_loops: true,
267        do_while_loops: true,
268        general_uniform_indexing: true,
269        general_attribute_matrix_vector_indexing: true,
270        general_varying_indexing: true,
271        general_sampler_indexing: true,
272        general_variable_indexing: true,
273        general_constant_matrix_vector_indexing: true,
274    },
275};