glium/program/
program.rs

1use crate::gl;
2
3use crate::context::CommandContext;
4use crate::version::Version;
5use crate::version::Api;
6
7use crate::backend::Facade;
8use crate::CapabilitiesSource;
9
10use std::fmt;
11use std::collections::hash_map::{self, HashMap};
12use std::hash::BuildHasherDefault;
13
14use fnv::FnvHasher;
15
16use crate::GlObject;
17use crate::ProgramExt;
18use crate::Handle;
19use crate::RawUniformValue;
20
21use crate::program::{COMPILER_GLOBAL_LOCK, ProgramCreationInput, ProgramCreationError, ShaderType, Binary, SpirvProgram};
22use crate::program::GetBinaryError;
23
24use crate::program::reflection::{Uniform, UniformBlock, OutputPrimitives};
25use crate::program::reflection::{Attribute, TransformFeedbackBuffer};
26use crate::program::reflection::{SubroutineData, ShaderStage, SubroutineUniform};
27use crate::program::shader::{build_shader, build_spirv_shader};
28
29use crate::program::raw::RawProgram;
30
31use crate::vertex::VertexFormat;
32
33/// A combination of shaders linked together.
34pub struct Program {
35    raw: RawProgram,
36    outputs_srgb: bool,
37    uses_point_size: bool,
38}
39
40impl Program {
41    /// Builds a new program.
42    pub fn new<'a, F: ?Sized, I>(facade: &F, input: I) -> Result<Program, ProgramCreationError>
43                         where I: Into<ProgramCreationInput<'a>>, F: Facade
44    {
45        let input = input.into();
46
47        let (raw, outputs_srgb, uses_point_size) = match input {
48            ProgramCreationInput::SourceCode { vertex_shader, tessellation_control_shader,
49                                               tessellation_evaluation_shader, geometry_shader,
50                                               fragment_shader, transform_feedback_varyings,
51                                               outputs_srgb, uses_point_size } =>
52            {
53                let mut has_geometry_shader = false;
54                let mut has_tessellation_control_shader = false;
55                let mut has_tessellation_evaluation_shader = false;
56
57                let mut shaders = vec![
58                    (vertex_shader, ShaderType::Vertex),
59                    (fragment_shader, ShaderType::Fragment)
60                ];
61
62                if let Some(gs) = geometry_shader {
63                    shaders.push((gs, ShaderType::Geometry));
64                    has_geometry_shader = true;
65                }
66
67                if let Some(ts) = tessellation_control_shader {
68                    shaders.push((ts, ShaderType::TesselationControl));
69                    has_tessellation_control_shader = true;
70                }
71
72                if let Some(ts) = tessellation_evaluation_shader {
73                    shaders.push((ts, ShaderType::TesselationEvaluation));
74                    has_tessellation_evaluation_shader = true;
75                }
76
77                // TODO: move somewhere else
78                if transform_feedback_varyings.is_some() &&
79                    !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) &&
80                    !facade.get_context().get_extensions().gl_ext_transform_feedback
81                {
82                    return Err(ProgramCreationError::TransformFeedbackNotSupported);
83                }
84
85                if uses_point_size && (facade.get_context().get_version().0 == Api::Gl) && !(facade.get_context().get_version() >= &Version(Api::Gl, 2, 0)) {
86                    return Err(ProgramCreationError::PointSizeNotSupported);
87                }
88
89                let _lock = COMPILER_GLOBAL_LOCK.lock();
90
91                let shaders_store = {
92                    let mut shaders_store = Vec::new();
93                    for (src, ty) in shaders.into_iter() {
94                        shaders_store.push(build_shader(facade, ty.to_opengl_type(), src)?);
95                    }
96                    shaders_store
97                };
98
99                (RawProgram::from_shaders(facade, &shaders_store, has_geometry_shader,
100                                               has_tessellation_control_shader, has_tessellation_evaluation_shader,
101                                               transform_feedback_varyings)?,
102                 outputs_srgb, uses_point_size)
103            },
104
105            ProgramCreationInput::Binary { data, outputs_srgb, uses_point_size } => {
106                if uses_point_size && (facade.get_context().get_version().0 == Api::Gl) && !(facade.get_context().get_version() >= &Version(Api::Gl, 2, 0)) {
107                    return Err(ProgramCreationError::PointSizeNotSupported);
108                }
109
110                (RawProgram::from_binary(facade, data)?, outputs_srgb, uses_point_size)
111            },
112
113            ProgramCreationInput::SpirV(SpirvProgram { vertex_shader, tessellation_control_shader,
114                                               tessellation_evaluation_shader, geometry_shader,
115                                               fragment_shader, transform_feedback_varyings,
116                                               outputs_srgb, uses_point_size }) =>
117            {
118                let mut has_geometry_shader = false;
119                let mut has_tessellation_control_shader = false;
120                let mut has_tessellation_evaluation_shader = false;
121
122                let mut shaders = vec![
123                    (vertex_shader, ShaderType::Vertex),
124                    (fragment_shader, ShaderType::Fragment)
125                ];
126
127                if let Some(gs) = geometry_shader {
128                    shaders.push((gs, ShaderType::Geometry));
129                    has_geometry_shader = true;
130                }
131
132                if let Some(ts) = tessellation_control_shader {
133                    shaders.push((ts, ShaderType::TesselationControl));
134                    has_tessellation_control_shader = true;
135                }
136
137                if let Some(ts) = tessellation_evaluation_shader {
138                    shaders.push((ts, ShaderType::TesselationEvaluation));
139                    has_tessellation_evaluation_shader = true;
140                }
141
142                // TODO: move somewhere else
143                if transform_feedback_varyings.is_some() &&
144                    !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) &&
145                    !facade.get_context().get_extensions().gl_ext_transform_feedback
146                {
147                    return Err(ProgramCreationError::TransformFeedbackNotSupported);
148                }
149
150                if uses_point_size && (facade.get_context().get_version().0 == Api::Gl) && !(facade.get_context().get_version() >= &Version(Api::Gl, 2, 0)) {
151                    return Err(ProgramCreationError::PointSizeNotSupported);
152                }
153
154                let _lock = COMPILER_GLOBAL_LOCK.lock();
155
156                let shaders_store = {
157                    let mut shaders_store = Vec::new();
158                    for (src, ty) in shaders.into_iter() {
159                        shaders_store.push(build_spirv_shader(facade, ty.to_opengl_type(), &src)?);
160                    }
161                    shaders_store
162                };
163
164                (RawProgram::from_shaders(facade, &shaders_store, has_geometry_shader,
165                                               has_tessellation_control_shader, has_tessellation_evaluation_shader,
166                                               transform_feedback_varyings)?,
167                 outputs_srgb, uses_point_size)
168            }
169        };
170        Ok(Program {
171            raw,
172            outputs_srgb,
173            uses_point_size,
174        })
175    }
176
177    /// Builds a new program from GLSL source code.
178    ///
179    /// A program is a group of shaders linked together.
180    ///
181    /// # Parameters
182    ///
183    /// - `vertex_shader`: Source code of the vertex shader.
184    /// - `fragment_shader`: Source code of the fragment shader.
185    /// - `geometry_shader`: Source code of the geometry shader.
186    ///
187    /// # Example
188    ///
189    /// ```no_run
190    /// # use glutin::surface::{ResizeableSurface, SurfaceTypeTrait};
191    /// # fn example<T>(display: glium::Display<T>) where T: SurfaceTypeTrait + ResizeableSurface {
192    /// # let vertex_source = ""; let fragment_source = ""; let geometry_source = "";
193    /// let program = glium::Program::from_source(&display, vertex_source, fragment_source,
194    ///     Some(geometry_source));
195    /// # }
196    /// ```
197    ///
198    #[inline]
199    pub fn from_source<'a, F: ?Sized>(facade: &F, vertex_shader: &'a str, fragment_shader: &'a str,
200                              geometry_shader: Option<&'a str>)
201                              -> Result<Program, ProgramCreationError> where F: Facade
202    {
203        Program::new(facade, ProgramCreationInput::SourceCode {
204            vertex_shader,
205            fragment_shader,
206            geometry_shader,
207            tessellation_control_shader: None,
208            tessellation_evaluation_shader: None,
209            transform_feedback_varyings: None,
210            outputs_srgb: true,
211            uses_point_size: false,
212        })
213    }
214
215    /// Returns the program's compiled binary.
216    ///
217    /// You can store the result in a file, then reload it later. This avoids having to compile
218    /// the source code every time.
219    #[inline]
220    pub fn get_binary(&self) -> Result<Binary, GetBinaryError> {
221        self.raw.get_binary()
222    }
223
224    /// Returns the *location* of an output fragment, if it exists.
225    ///
226    /// The *location* is low-level information that is used internally by glium.
227    /// You probably don't need to call this function.
228    ///
229    /// You can declare output fragments in your shaders by writing:
230    ///
231    /// ```notrust
232    /// out vec4 foo;
233    /// ```
234    ///
235    #[inline]
236    pub fn get_frag_data_location(&self, name: &str) -> Option<u32> {
237        self.raw.get_frag_data_location(name)
238    }
239
240    /// Returns informations about a uniform variable, if it exists.
241    #[inline]
242    pub fn get_uniform(&self, name: &str) -> Option<&Uniform> {
243        self.raw.get_uniform(name)
244    }
245
246    /// Returns an iterator to the list of uniforms.
247    ///
248    /// ## Example
249    ///
250    /// ```no_run
251    /// # fn example(program: glium::Program) {
252    /// for (name, uniform) in program.uniforms() {
253    ///     println!("Name: {} - Type: {:?}", name, uniform.ty);
254    /// }
255    /// # }
256    /// ```
257    #[inline]
258    pub fn uniforms(&self) -> hash_map::Iter<'_, String, Uniform> {
259        self.raw.uniforms()
260    }
261
262    /// Returns a list of uniform blocks.
263    ///
264    /// ## Example
265    ///
266    /// ```no_run
267    /// # fn example(program: glium::Program) {
268    /// for (name, uniform) in program.get_uniform_blocks() {
269    ///     println!("Name: {}", name);
270    /// }
271    /// # }
272    /// ```
273    #[inline]
274    pub fn get_uniform_blocks(&self)
275                              -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
276        self.raw.get_uniform_blocks()
277    }
278
279    /// Returns the list of transform feedback varyings.
280    #[inline]
281    pub fn get_transform_feedback_buffers(&self) -> &[TransformFeedbackBuffer] {
282        self.raw.get_transform_feedback_buffers()
283    }
284
285    /// True if the transform feedback output of this program matches the specified `VertexFormat`
286    /// and `stride`.
287    ///
288    /// The `stride` is the number of bytes between two vertices.
289    #[inline]
290    pub fn transform_feedback_matches(&self, format: &VertexFormat, stride: usize) -> bool {
291        self.raw.transform_feedback_matches(format, stride)
292    }
293
294    /// Returns the type of geometry that transform feedback would generate, or `None` if it
295    /// depends on the vertex/index data passed when drawing.
296    ///
297    /// This corresponds to `GL_GEOMETRY_OUTPUT_TYPE` or `GL_TESS_GEN_MODE`. If the program doesn't
298    /// contain either a geometry shader or a tessellation evaluation shader, returns `None`.
299    #[inline]
300    pub fn get_output_primitives(&self) -> Option<OutputPrimitives> {
301        self.raw.get_output_primitives()
302    }
303
304    /// Returns true if the program contains a tessellation stage.
305    #[inline]
306    pub fn has_tessellation_shaders(&self) -> bool {
307        self.raw.has_tessellation_shaders()
308    }
309
310    /// Returns true if the program contains a tessellation control stage.
311    #[inline]
312    pub fn has_tessellation_control_shader(&self) -> bool {
313        self.raw.has_tessellation_control_shader()
314    }
315
316    /// Returns true if the program contains a tessellation evaluation stage.
317    #[inline]
318    pub fn has_tessellation_evaluation_shader(&self) -> bool {
319        self.raw.has_tessellation_evaluation_shader()
320    }
321
322    /// Returns true if the program contains a geometry shader.
323    #[inline]
324    pub fn has_geometry_shader(&self) -> bool {
325        self.raw.has_geometry_shader()
326    }
327
328    /// Returns informations about an attribute, if it exists.
329    #[inline]
330    pub fn get_attribute(&self, name: &str) -> Option<&Attribute> {
331        self.raw.get_attribute(name)
332    }
333
334    /// Returns an iterator to the list of attributes.
335    ///
336    /// ## Example
337    ///
338    /// ```no_run
339    /// # fn example(program: glium::Program) {
340    /// for (name, attribute) in program.attributes() {
341    ///     println!("Name: {} - Type: {:?}", name, attribute.ty);
342    /// }
343    /// # }
344    /// ```
345    #[inline]
346    pub fn attributes(&self) -> hash_map::Iter<'_, String, Attribute> {
347        self.raw.attributes()
348    }
349
350    /// Returns true if the program has been configured to output sRGB instead of RGB.
351    #[inline]
352    pub fn has_srgb_output(&self) -> bool {
353        self.outputs_srgb
354    }
355
356    /// Returns the list of shader storage blocks.
357    ///
358    /// ## Example
359    ///
360    /// ```no_run
361    /// # fn example(program: glium::Program) {
362    /// for (name, uniform) in program.get_shader_storage_blocks() {
363    ///     println!("Name: {}", name);
364    /// }
365    /// # }
366    /// ```
367    #[inline]
368    pub fn get_shader_storage_blocks(&self)
369            -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
370        self.raw.get_shader_storage_blocks()
371    }
372
373    /// Returns the list of shader storage blocks.
374    ///
375    /// ## Example
376    ///
377    /// ```no_run
378    /// # fn example(program: glium::Program) {
379    /// for (name, uniform) in program.get_atomic_counters() {
380    ///     println!("Name: {}", name);
381    /// }
382    /// # }
383    /// ```
384    #[inline]
385    pub fn get_atomic_counters(&self)
386            -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
387        self.raw.get_atomic_counters()
388    }
389
390    /// Returns the subroutine uniforms of this program.
391    ///
392    /// Since subroutine uniforms are unique per shader and *not* per program,
393    /// the keys of the `HashMap` are in the format `("subroutine_name", ShaderStage)`.
394    /// ## Example
395    ///
396    /// ```no_run
397    /// # fn example(program: glium::Program) {
398    /// for (&(ref name, shader), uniform) in program.get_subroutine_uniforms() {
399    ///     println!("Name: {}", name);
400    /// }
401    /// # }
402    /// ```
403    #[inline]
404    pub fn get_subroutine_uniforms(&self)
405            -> &HashMap<(String, ShaderStage), SubroutineUniform, BuildHasherDefault<FnvHasher>> {
406        &self.raw.get_subroutine_data().subroutine_uniforms
407    }
408
409    /// Returns true if the program has been configured to use the `gl_PointSize` variable.
410    ///
411    /// If the program uses `gl_PointSize` without having been configured appropriately, then
412    /// setting the value of `gl_PointSize` will have no effect.
413    #[inline]
414    pub fn uses_point_size(&self) -> bool {
415      self.uses_point_size
416    }
417}
418
419impl fmt::Debug for Program {
420    #[inline]
421    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
422        write!(formatter, "{:?}", self.raw)
423    }
424}
425
426impl GlObject for Program {
427    type Id = Handle;
428
429    #[inline]
430    fn get_id(&self) -> Handle {
431        self.raw.get_id()
432    }
433}
434
435impl ProgramExt for Program {
436    fn use_program(&self, ctxt: &mut CommandContext<'_>) {
437        // compatibility was checked at program creation
438        if ctxt.version.0 == Api::Gl {
439            if self.uses_point_size && !ctxt.state.enabled_program_point_size {
440                unsafe { ctxt.gl.Enable(gl::PROGRAM_POINT_SIZE); }
441            } else if !self.uses_point_size && ctxt.state.enabled_program_point_size {
442                unsafe { ctxt.gl.Disable(gl::PROGRAM_POINT_SIZE); }
443            }
444        }
445
446        if (ctxt.version >= &Version(Api::Gl, 3, 0) || ctxt.extensions.gl_arb_framebuffer_srgb ||
447           ctxt.extensions.gl_ext_framebuffer_srgb || ctxt.extensions.gl_ext_srgb_write_control) && ctxt.state.enabled_framebuffer_srgb == self.outputs_srgb {
448            ctxt.state.enabled_framebuffer_srgb = !self.outputs_srgb;
449
450            if self.outputs_srgb {
451                unsafe { ctxt.gl.Disable(gl::FRAMEBUFFER_SRGB) };
452            } else {
453                unsafe { ctxt.gl.Enable(gl::FRAMEBUFFER_SRGB) };
454            }
455        }
456
457        self.raw.use_program(ctxt)
458    }
459
460    #[inline]
461    fn set_uniform(&self, ctxt: &mut CommandContext<'_>, uniform_location: gl::types::GLint,
462                   value: &RawUniformValue)
463    {
464        self.raw.set_uniform(ctxt, uniform_location, value)
465    }
466
467    #[inline]
468    fn set_uniform_block_binding(&self, ctxt: &mut CommandContext<'_>, block_location: gl::types::GLuint,
469                                 value: gl::types::GLuint)
470    {
471        self.raw.set_uniform_block_binding(ctxt, block_location, value)
472    }
473
474    #[inline]
475    fn set_shader_storage_block_binding(&self, ctxt: &mut CommandContext<'_>,
476                                        block_location: gl::types::GLuint,
477                                        value: gl::types::GLuint)
478    {
479        self.raw.set_shader_storage_block_binding(ctxt, block_location, value)
480    }
481
482    #[inline]
483    fn set_subroutine_uniforms_for_stage(&self, ctxt: &mut CommandContext<'_>,
484                                         stage: ShaderStage,
485                                         indices: &[gl::types::GLuint])
486    {
487        self.raw.set_subroutine_uniforms_for_stage(ctxt, stage, indices);
488    }
489
490    #[inline]
491    fn get_uniform(&self, name: &str) -> Option<&Uniform> {
492        self.raw.get_uniform(name)
493    }
494
495    #[inline]
496    fn get_uniform_blocks(&self) -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
497        self.raw.get_uniform_blocks()
498    }
499
500    #[inline]
501    fn get_shader_storage_blocks(&self)
502                                 -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
503        self.raw.get_shader_storage_blocks()
504    }
505
506    #[inline]
507    fn get_atomic_counters(&self)
508                                 -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
509        self.raw.get_atomic_counters()
510    }
511
512    #[inline]
513    fn get_subroutine_data(&self) -> &SubroutineData {
514        self.raw.get_subroutine_data()
515    }
516}