glium/program/
shader.rs

1use crate::gl;
2use crate::version::Version;
3use crate::version::Api;
4
5use crate::CapabilitiesSource;
6use crate::backend::Facade;
7use crate::context::Context;
8use crate::ContextExt;
9
10use std::{ffi, ptr};
11use std::rc::Rc;
12
13use crate::GlObject;
14use crate::Handle;
15
16use crate::program::{ProgramCreationError, ShaderType, SpirvEntryPoint};
17
18/// A single, compiled but unlinked, shader.
19pub struct Shader {
20    context: Rc<Context>,
21    id: Handle,
22}
23
24impl GlObject for Shader {
25    type Id = Handle;
26
27    #[inline]
28    fn get_id(&self) -> Handle {
29        self.id
30    }
31}
32
33impl Drop for Shader {
34    fn drop(&mut self) {
35        let ctxt = self.context.make_current();
36
37        unsafe {
38            match self.id {
39                Handle::Id(id) => {
40                    assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
41                            ctxt.version >= &Version(Api::GlEs, 2, 0));
42                    ctxt.gl.DeleteShader(id);
43                },
44                Handle::Handle(id) => {
45                    assert!(ctxt.extensions.gl_arb_shader_objects);
46                    ctxt.gl.DeleteObjectARB(id);
47                }
48            }
49        }
50    }
51}
52
53/// Builds an individual shader.
54pub fn build_shader<F: ?Sized>(facade: &F, shader_type: gl::types::GLenum, source_code: &str)
55                       -> Result<Shader, ProgramCreationError> where F: Facade
56{
57    unsafe {
58        let ctxt = facade.get_context().make_current();
59
60        if ctxt.capabilities.supported_glsl_versions.is_empty() {
61            return Err(ProgramCreationError::CompilationNotSupported);
62        }
63
64        if !check_shader_type_compatibility(&ctxt, shader_type) {
65            return Err(ProgramCreationError::ShaderTypeNotSupported);
66        }
67
68        let source_code = ffi::CString::new(source_code.as_bytes()).unwrap();
69
70        let id = if ctxt.version >= &Version(Api::Gl, 2, 0) ||
71                    ctxt.version >= &Version(Api::GlEs, 2, 0)
72        {
73            Handle::Id(ctxt.gl.CreateShader(shader_type))
74        } else if ctxt.extensions.gl_arb_shader_objects {
75            Handle::Handle(ctxt.gl.CreateShaderObjectARB(shader_type))
76        } else {
77            unreachable!()
78        };
79
80        if id == Handle::Id(0) || id == Handle::Handle(0 as gl::types::GLhandleARB) {
81            return Err(ProgramCreationError::ShaderTypeNotSupported);
82        }
83
84        match id {
85            Handle::Id(id) => {
86                assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
87                        ctxt.version >= &Version(Api::GlEs, 2, 0));
88                ctxt.gl.ShaderSource(id, 1, [ source_code.as_ptr() ].as_ptr(), ptr::null());
89            },
90            Handle::Handle(id) => {
91                assert!(ctxt.extensions.gl_arb_shader_objects);
92                ctxt.gl.ShaderSourceARB(id, 1, [ source_code.as_ptr() ].as_ptr(), ptr::null());
93            }
94        }
95
96        // compiling
97        {
98            ctxt.report_debug_output_errors.set(false);
99
100            match id {
101                Handle::Id(id) => {
102                    assert!(ctxt.version >= &Version(Api::Gl, 2, 0)||
103                            ctxt.version >= &Version(Api::GlEs, 2, 0));
104                    ctxt.gl.CompileShader(id);
105                },
106                Handle::Handle(id) => {
107                    assert!(ctxt.extensions.gl_arb_shader_objects);
108                    ctxt.gl.CompileShaderARB(id);
109                }
110            }
111
112            ctxt.report_debug_output_errors.set(true);
113        }
114
115        // checking compilation success by reading a flag on the shader
116        let compilation_success = {
117            let mut compilation_success: gl::types::GLint = 0;
118            match id {
119                Handle::Id(id) => {
120                    assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
121                            ctxt.version >= &Version(Api::GlEs, 2, 0));
122                    ctxt.gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut compilation_success);
123                },
124                Handle::Handle(id) => {
125                    assert!(ctxt.extensions.gl_arb_shader_objects);
126                    ctxt.gl.GetObjectParameterivARB(id, gl::OBJECT_COMPILE_STATUS_ARB,
127                                                    &mut compilation_success);
128                }
129            }
130            compilation_success
131        };
132
133        if compilation_success == 1 {
134            Ok(Shader {
135                context: facade.get_context().clone(),
136                id
137            })
138
139        } else {
140            // compilation error
141            let mut error_log_size: gl::types::GLint = 0;
142
143            match id {
144                Handle::Id(id) => {
145                    assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
146                            ctxt.version >= &Version(Api::GlEs, 2, 0));
147                    ctxt.gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut error_log_size);
148                },
149                Handle::Handle(id) => {
150                    assert!(ctxt.extensions.gl_arb_shader_objects);
151                    ctxt.gl.GetObjectParameterivARB(id, gl::OBJECT_INFO_LOG_LENGTH_ARB,
152                                                    &mut error_log_size);
153                }
154            }
155
156            let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as usize);
157
158            match id {
159                Handle::Id(id) => {
160                    assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
161                            ctxt.version >= &Version(Api::GlEs, 2, 0));
162                    ctxt.gl.GetShaderInfoLog(id, error_log_size, &mut error_log_size,
163                                             error_log.as_mut_ptr() as *mut gl::types::GLchar);
164                },
165                Handle::Handle(id) => {
166                    assert!(ctxt.extensions.gl_arb_shader_objects);
167                    ctxt.gl.GetInfoLogARB(id, error_log_size, &mut error_log_size,
168                                          error_log.as_mut_ptr() as *mut gl::types::GLchar);
169                }
170            }
171
172            error_log.set_len(error_log_size as usize);
173
174            match String::from_utf8(error_log) {
175                Ok(msg) => Err(ProgramCreationError::CompilationError(msg, ShaderType::from_opengl_type(shader_type))),
176                Err(_) => Err(
177                    ProgramCreationError::CompilationError("Could not convert the log \
178                                                            message to UTF-8".to_owned(), ShaderType::from_opengl_type(shader_type))
179                ),
180            }
181        }
182    }
183}
184
185/// Builds an individual shader from a SPIR-V binary.
186pub fn build_spirv_shader<F: ?Sized>(facade: &F, shader_type: gl::types::GLenum, spirv: &SpirvEntryPoint)
187                       -> Result<Shader, ProgramCreationError> where F: Facade
188{
189    unsafe {
190        let ctxt = facade.get_context().make_current();
191
192        if ctxt.capabilities.supported_glsl_versions.is_empty() {
193            return Err(ProgramCreationError::CompilationNotSupported);
194        }
195
196        if !check_shader_type_compatibility(&ctxt, shader_type) {
197            return Err(ProgramCreationError::ShaderTypeNotSupported);
198        }
199
200        let id = if ctxt.version >= &Version(Api::Gl, 4, 6) ||
201            (ctxt.version >= &Version(Api::Gl, 4, 1) && ctxt.extensions.gl_arb_gl_spirv)
202        {
203            ctxt.gl.CreateShader(shader_type)
204        } else {
205            return Err(ProgramCreationError::CompilationNotSupported);
206        };
207
208        if id == 0 {
209            return Err(ProgramCreationError::ShaderTypeNotSupported);
210        }
211
212        let binary = &spirv.binary;
213        let entry_point = ffi::CString::new(spirv.entry_point.as_bytes()).unwrap();
214
215        const GL_SHADER_BINARY_FORMAT_SPIR_V: gl::types::GLenum = 0x9551;
216
217        ctxt.gl.ShaderBinary(1, &id, GL_SHADER_BINARY_FORMAT_SPIR_V, binary.as_ptr() as _, binary.len() as _);
218
219        // compiling
220        {
221            ctxt.report_debug_output_errors.set(false);
222
223            ctxt.gl.SpecializeShader(id, entry_point.as_ptr() as _, 0, ptr::null(), ptr::null());
224
225            ctxt.report_debug_output_errors.set(true);
226        }
227
228        // checking compilation success by reading a flag on the shader
229        let compilation_success = {
230            let mut compilation_success: gl::types::GLint = 0;
231            ctxt.gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut compilation_success);
232            compilation_success
233        };
234
235        if compilation_success == 1 {
236            Ok(Shader {
237                context: facade.get_context().clone(),
238                id: Handle::Id(id)
239            })
240        } else {
241            // compilation error
242            let mut error_log_size: gl::types::GLint = 0;
243
244            ctxt.gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut error_log_size);
245
246            let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as usize);
247
248            ctxt.gl.GetShaderInfoLog(id, error_log_size, &mut error_log_size,
249                                     error_log.as_mut_ptr() as *mut gl::types::GLchar);
250
251            error_log.set_len(error_log_size as usize);
252
253            match String::from_utf8(error_log) {
254                Ok(msg) => Err(ProgramCreationError::CompilationError(msg, ShaderType::from_opengl_type(shader_type))),
255                Err(_) => Err(
256                    ProgramCreationError::CompilationError("Could not convert the log \
257                                                            message to UTF-8".to_owned(), ShaderType::from_opengl_type(shader_type))
258                ),
259            }
260        }
261    }
262}
263
264pub fn check_shader_type_compatibility<C: ?Sized>(ctxt: &C, shader_type: gl::types::GLenum)
265                                          -> bool where C: CapabilitiesSource
266{
267    match shader_type {
268        gl::VERTEX_SHADER | gl::FRAGMENT_SHADER => (),
269        gl::GEOMETRY_SHADER => {
270            if !(ctxt.get_version() >= &Version(Api::Gl, 3, 2))
271                && !(ctxt.get_version() >= &Version(Api::GlEs, 3, 2))
272                && !ctxt.get_extensions().gl_arb_geometry_shader4
273                && !ctxt.get_extensions().gl_ext_geometry_shader4
274                && !ctxt.get_extensions().gl_ext_geometry_shader
275                && !ctxt.get_extensions().gl_oes_geometry_shader
276            {
277                return false;
278            }
279        },
280        gl::TESS_CONTROL_SHADER | gl::TESS_EVALUATION_SHADER => {
281            if !(ctxt.get_version() >= &Version(Api::Gl, 4, 0))
282                && !(ctxt.get_version() >= &Version(Api::GlEs, 3, 2))
283                && !ctxt.get_extensions().gl_arb_tessellation_shader
284                && !ctxt.get_extensions().gl_oes_tessellation_shader
285            {
286                return false;
287            }
288        },
289        gl::COMPUTE_SHADER => {
290            if !(ctxt.get_version() >= &Version(Api::Gl, 4, 3))
291                && !(ctxt.get_version() >= &Version(Api::GlEs, 3, 1))
292                && !ctxt.get_extensions().gl_arb_compute_shader
293            {
294                return false;
295            }
296        },
297        _ => unreachable!()
298    };
299
300    true
301}