glium/program/
mod.rs

1//! Items related to creating an OpenGL program.
2
3use std::fmt;
4use std::error::Error;
5use std::sync::Mutex;
6use crate::CapabilitiesSource;
7
8use crate::gl;
9use crate::version::Api;
10use crate::version::Version;
11
12pub use self::compute::{ComputeShader, ComputeCommand};
13pub use self::program::Program;
14pub use self::reflection::{Uniform, UniformBlock, BlockLayout, OutputPrimitives};
15pub use self::reflection::{Attribute, TransformFeedbackVarying, TransformFeedbackBuffer, TransformFeedbackMode};
16pub use self::reflection::{ShaderStage, SubroutineData, SubroutineUniform};
17
18mod compute;
19mod program;
20mod raw;
21mod reflection;
22mod shader;
23mod uniforms_storage;
24mod binary_header;
25
26/// Returns true if the backend supports geometry shaders.
27#[inline]
28pub fn is_geometry_shader_supported<C: ?Sized>(ctxt: &C) -> bool where C: CapabilitiesSource {
29    shader::check_shader_type_compatibility(ctxt, gl::GEOMETRY_SHADER)
30}
31
32/// Returns true if the backend supports tessellation shaders.
33#[inline]
34pub fn is_tessellation_shader_supported<C: ?Sized>(ctxt: &C) -> bool where C: CapabilitiesSource {
35    shader::check_shader_type_compatibility(ctxt, gl::TESS_CONTROL_SHADER)
36}
37
38/// Returns true if the backend supports creating and retrieving binary format.
39#[inline]
40pub fn is_binary_supported<C: ?Sized>(ctxt: &C) -> bool where C: CapabilitiesSource {
41    ctxt.get_version() >= &Version(Api::Gl, 4, 1) || ctxt.get_version() >= &Version(Api::GlEs, 2, 0)
42        || ctxt.get_extensions().gl_arb_get_programy_binary
43}
44
45/// Returns true if the backend supports shader subroutines.
46#[inline]
47pub fn is_subroutine_supported<C: ?Sized>(ctxt: &C) -> bool where C: CapabilitiesSource {
48    // WORKAROUND: Windows only; NVIDIA doesn't actually return a valid function pointer for
49    //              GetProgramStageiv despite supporting ARB_shader_subroutine; see #1439
50    if cfg!(target_os = "windows")
51        && ctxt.get_version() <= &Version(Api::Gl, 4, 0)
52        && ctxt.get_capabilities().vendor == "NVIDIA Corporation" {
53        return false;
54    }
55    ctxt.get_version() >= &Version(Api::Gl, 4, 0) || ctxt.get_extensions().gl_arb_shader_subroutine
56}
57
58// Some shader compilers have race-condition issues, so we lock this mutex
59// in the GL thread every time we compile a shader or link a program.
60static COMPILER_GLOBAL_LOCK: Mutex<()> = Mutex::new(());
61
62/// Used in ProgramCreationError::CompilationError to explain which shader stage failed compilation
63#[derive(Copy, Clone, Debug, PartialEq, Eq)]
64pub enum ShaderType {
65    /// Vertex shader, maps to gl::VERTEX_SHADER
66    Vertex,
67    /// Geometry shader, maps to gl::GEOMETRY_SHADER
68    Geometry,
69    /// Fragment shader, maps to gl::FRAGMENT_SHADER
70    Fragment,
71    /// Tesselation control shader, maps to gl::TESS_CONTROL_SHADER
72    TesselationControl,
73    /// Tesselation evaluation shader, maps to gl::TESS_EVALUATION_SHADER
74    TesselationEvaluation,
75    /// Compute shader, maps to gl::COMPUTE_SHADER
76    Compute,
77}
78
79impl ShaderType {
80    /// Creates an instance of gl::types::GLenum corresponding to the given ShaderType
81    pub fn to_opengl_type(self) -> gl::types::GLenum {
82        match self {
83            ShaderType::Vertex => gl::VERTEX_SHADER,
84            ShaderType::Geometry => gl::GEOMETRY_SHADER,
85            ShaderType::Fragment => gl::FRAGMENT_SHADER,
86            ShaderType::TesselationControl => gl::TESS_CONTROL_SHADER,
87            ShaderType::TesselationEvaluation => gl::TESS_EVALUATION_SHADER,
88            ShaderType::Compute => gl::COMPUTE_SHADER,
89        }
90    }
91    /// Creates an instance of ShaderType corresponding to the given gl::types::GLenum.
92    /// This routine will panic if the given shadertype is not supported by glium.
93    pub fn from_opengl_type(gl_type: gl::types::GLenum) -> Self {
94        match gl_type {
95            gl::VERTEX_SHADER => ShaderType::Vertex,
96            gl::GEOMETRY_SHADER => ShaderType::Geometry,
97            gl::FRAGMENT_SHADER => ShaderType::Fragment,
98            gl::TESS_CONTROL_SHADER => ShaderType::TesselationControl,
99            gl::TESS_EVALUATION_SHADER => ShaderType::TesselationEvaluation,
100            gl::COMPUTE_SHADER  => ShaderType::Compute,
101            _ => {
102                panic!("Unsupported shader type")
103            }
104        }
105    }
106}
107
108/// Error that can be triggered when creating a `Program`.
109#[derive(Clone, Debug)]
110pub enum ProgramCreationError {
111    /// Error while compiling one of the shaders.
112    CompilationError(String, ShaderType),
113
114    /// Error while linking the program.
115    LinkingError(String),
116
117    /// One of the requested shader types is not supported by the backend.
118    ///
119    /// Usually the case for geometry shaders.
120    ShaderTypeNotSupported,
121
122    /// The OpenGL implementation doesn't provide a compiler.
123    CompilationNotSupported,
124
125    /// You have requested transform feedback varyings, but transform feedback is not supported
126    /// by the backend.
127    TransformFeedbackNotSupported,
128
129    /// You have requested point size setting from the shader, but it's not
130    /// supported by the backend.
131    PointSizeNotSupported,
132
133    /// The glium-specific binary header was not found or is corrupt.
134    BinaryHeaderError,
135}
136
137impl fmt::Display for ProgramCreationError {
138    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
139        use self::ProgramCreationError::*;
140        let desc = match *self {
141            CompilationError(_,typ) => {
142                match typ {
143                    ShaderType::Vertex => "Compilation error in vertex shader",
144                    ShaderType::Geometry => "Compilation error in geometry shader",
145                    ShaderType::Fragment => "Compilation error in fragment shader",
146                    ShaderType::TesselationControl => "Compilation error in tesselation control shader",
147                    ShaderType::TesselationEvaluation => "Compilation error in tesselation evaluation shader",
148                    ShaderType::Compute => "Compilation error in compute shader"
149                }
150            },
151            LinkingError(_) =>
152                "Error while linking shaders together",
153            ShaderTypeNotSupported =>
154                "One of the request shader type is not supported by the backend",
155            CompilationNotSupported =>
156                "The backend doesn't support shaders compilation",
157            TransformFeedbackNotSupported =>
158                "Transform feedback is not supported by the backend.",
159            PointSizeNotSupported =>
160                "Point size is not supported by the backend.",
161            BinaryHeaderError =>
162                "The glium-specific binary header was not found or is corrupt.",
163        };
164        match *self {
165            CompilationError(ref s, _) =>
166                write!(fmt, "{}: {}", desc, s),
167            LinkingError(ref s) =>
168                write!(fmt, "{}: {}", desc, s),
169            _ =>
170                write!(fmt, "{}", desc),
171        }
172    }
173}
174
175impl Error for ProgramCreationError {}
176
177/// Error type that is returned by the `program!` macro.
178#[derive(Clone, Debug)]
179pub enum ProgramChooserCreationError {
180    /// No available version has been found.
181    NoVersion,
182
183    /// A version has been found but it triggered the given error.
184    ProgramCreationError(ProgramCreationError),
185}
186
187impl fmt::Display for ProgramChooserCreationError {
188    #[inline]
189    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
190        use self::ProgramChooserCreationError::*;
191        match *self {
192            ProgramCreationError(ref err) => write!(fmt, "{}", err),
193            NoVersion => fmt.write_str("No version of the program has been found for the current OpenGL version."),
194        }
195    }
196}
197
198impl Error for ProgramChooserCreationError {
199    #[inline]
200    fn source(&self) -> Option<&(dyn Error + 'static)> {
201        use self::ProgramChooserCreationError::*;
202        match *self {
203            ProgramCreationError(ref err) => Some(err),
204            _ => None,
205        }
206    }
207}
208
209impl From<ProgramCreationError> for ProgramChooserCreationError {
210    fn from(err: ProgramCreationError) -> ProgramChooserCreationError {
211        ProgramChooserCreationError::ProgramCreationError(err)
212    }
213}
214
215/// Error while retrieving the binary representation of a program.
216#[derive(Copy, Clone, Debug)]
217pub enum GetBinaryError {
218    /// The backend doesn't support binary.
219    NotSupported,
220    /// The backend does not supply any binary formats.
221    NoFormats,
222}
223
224impl fmt::Display for GetBinaryError {
225    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
226        use self::GetBinaryError::*;
227        let desc = match *self {
228            NotSupported => "The backend doesn't support binary",
229            NoFormats => "The backend does not supply any binary formats.",
230        };
231        fmt.write_str(desc)
232    }
233}
234
235impl Error for GetBinaryError {}
236
237/// Input when creating a program.
238pub enum ProgramCreationInput<'a> {
239    /// Use GLSL source code.
240    SourceCode {
241        /// Source code of the vertex shader.
242        vertex_shader: &'a str,
243
244        /// Source code of the optional tessellation control shader.
245        tessellation_control_shader: Option<&'a str>,
246
247        /// Source code of the optional tessellation evaluation shader.
248        tessellation_evaluation_shader: Option<&'a str>,
249
250        /// Source code of the optional geometry shader.
251        geometry_shader: Option<&'a str>,
252
253        /// Source code of the fragment shader.
254        fragment_shader: &'a str,
255
256        /// The list of variables and mode to use for transform feedback.
257        ///
258        /// The information specified here will be passed to the OpenGL linker. If you pass
259        /// `None`, then you won't be able to use transform feedback.
260        transform_feedback_varyings: Option<(Vec<String>, TransformFeedbackMode)>,
261
262        /// Whether the fragment shader outputs colors in `sRGB` or `RGB`. This is true by default,
263        /// meaning that the program is responsible for outputting correct `sRGB` values.
264        ///
265        /// If this is false, then `GL_FRAMEBUFFER_SRGB` will be enabled when this program is used
266        /// (if it is supported).
267        outputs_srgb: bool,
268
269        /// Whether the shader uses point size.
270        uses_point_size: bool,
271    },
272
273    /// Use a precompiled binary.
274    Binary {
275        /// The data.
276        data: Binary,
277
278        /// See [`ProgramCreationInput::SourceCode::outputs_srgb`].
279        outputs_srgb: bool,
280
281        /// Whether the shader uses point size.
282        uses_point_size: bool,
283    },
284
285    /// Use a SPIR-V binary.
286    SpirV(SpirvProgram<'a>),
287}
288
289/// Represents a SPIR-V program. The shaders can refer to entry points in the same binary.
290#[derive(Clone)]
291pub struct SpirvProgram<'a> {
292    /// The vertex shader.
293    pub vertex_shader: SpirvEntryPoint<'a>,
294
295    /// The fragment shader.
296    pub fragment_shader: SpirvEntryPoint<'a>,
297
298    /// Optional tessellation control shader.
299    pub tessellation_control_shader: Option<SpirvEntryPoint<'a>>,
300
301    /// Optional tessellation evaluation shader.
302    pub tessellation_evaluation_shader: Option<SpirvEntryPoint<'a>>,
303
304    /// Optional geometry shader.
305    pub geometry_shader: Option<SpirvEntryPoint<'a>>,
306
307    /// The list of variables and mode to use for transform feedback.
308    ///
309    /// The information specified here will be passed to the OpenGL linker. If you pass
310    /// `None`, then you won't be able to use transform feedback.
311    pub transform_feedback_varyings: Option<(Vec<String>, TransformFeedbackMode)>,
312
313    /// See [`ProgramCreationInput::SourceCode::outputs_srgb`].
314    pub outputs_srgb: bool,
315
316    /// Whether the shader uses point size.
317    pub uses_point_size: bool,
318}
319
320impl<'a> SpirvProgram<'a> {
321    /// Create new `SpirvProgram` from vertex and fragment shaders.
322    pub fn from_vs_and_fs(
323        vertex_shader: SpirvEntryPoint<'a>,
324        fragment_shader: SpirvEntryPoint<'a>,
325    ) -> Self {
326        Self {
327            vertex_shader,
328            fragment_shader,
329            tessellation_control_shader: None,
330            tessellation_evaluation_shader: None,
331            geometry_shader: None,
332            transform_feedback_varyings: None,
333            outputs_srgb: true,
334            uses_point_size: false,
335        }
336    }
337
338    /// Builder method to set `tessellation_control_shader`.
339    pub fn tessellation_control_shader(mut self, tessellation_control_shader: Option<SpirvEntryPoint<'a>>) -> Self {
340        self.tessellation_control_shader = tessellation_control_shader;
341        self
342    }
343
344    /// Builder method to set `tessellation_evaluation_shader`.
345    pub fn tessellation_evaluation_shader(mut self, tessellation_evaluation_shader: Option<SpirvEntryPoint<'a>>) -> Self {
346        self.tessellation_evaluation_shader = tessellation_evaluation_shader;
347        self
348    }
349
350    /// Builder method to set `geometry_shader`.
351    pub fn geometry_shader(mut self, geometry_shader: Option<SpirvEntryPoint<'a>>) -> Self {
352        self.geometry_shader = geometry_shader;
353        self
354    }
355
356    /// Builder method to set `transform_feedback_varyings`.
357    pub fn transform_feedback_varyings(mut self, transform_feedback_varyings: Option<(Vec<String>, TransformFeedbackMode)>) -> Self {
358        self.transform_feedback_varyings = transform_feedback_varyings;
359        self
360    }
361
362    /// Builder method to set `outputs_srgb`.
363    pub fn outputs_srgb(mut self, outputs_srgb: bool) -> Self {
364        self.outputs_srgb = outputs_srgb;
365        self
366    }
367
368    /// Builder method to set `uses_point_size`.
369    pub fn uses_point_size(mut self, uses_point_size: bool) -> Self {
370        self.uses_point_size = uses_point_size;
371        self
372    }
373}
374
375/// Represents an entry point of a binary SPIR-V module.
376#[derive(Copy, Clone)]
377pub struct SpirvEntryPoint<'a> {
378    /// The binary module data.
379    pub binary: &'a [u8],
380
381    /// The entry point to use, e.g. "main".
382    pub entry_point: &'a str,
383}
384
385/// Represents the source code of a program.
386pub struct SourceCode<'a> {
387    /// Source code of the vertex shader.
388    pub vertex_shader: &'a str,
389
390    /// Source code of the optional tessellation control shader.
391    pub tessellation_control_shader: Option<&'a str>,
392
393    /// Source code of the optional tessellation evaluation shader.
394    pub tessellation_evaluation_shader: Option<&'a str>,
395
396    /// Source code of the optional geometry shader.
397    pub geometry_shader: Option<&'a str>,
398
399    /// Source code of the fragment shader.
400    pub fragment_shader: &'a str,
401}
402
403impl<'a> From<SourceCode<'a>> for ProgramCreationInput<'a> {
404    #[inline]
405    fn from(code: SourceCode<'a>) -> ProgramCreationInput<'a> {
406        let SourceCode { vertex_shader, fragment_shader, geometry_shader,
407                         tessellation_control_shader, tessellation_evaluation_shader } = code;
408
409        ProgramCreationInput::SourceCode {
410            vertex_shader,
411            tessellation_control_shader,
412            tessellation_evaluation_shader,
413            geometry_shader,
414            fragment_shader,
415            transform_feedback_varyings: None,
416            outputs_srgb: true,
417            uses_point_size: false,
418        }
419    }
420}
421
422/// Represents the compiled binary data of a program.
423pub struct Binary {
424    /// An implementation-defined format.
425    pub format: u32,
426
427    /// The binary data.
428    pub content: Vec<u8>,
429}
430
431impl<'a> From<Binary> for ProgramCreationInput<'a> {
432    #[inline]
433    fn from(binary: Binary) -> ProgramCreationInput<'a> {
434        ProgramCreationInput::Binary {
435            data: binary,
436            outputs_srgb: true,
437            uses_point_size: false,
438        }
439    }
440}