three_d/core/
program.rs

1use crate::core::*;
2use std::collections::HashMap;
3use std::sync::RwLock;
4
5///
6/// A shader program consisting of a programmable vertex shader followed by a programmable fragment shader.
7/// Functionality includes transferring per vertex data to the vertex shader (see the use_attribute functionality)
8/// and transferring uniform data to both shader stages (see the use_uniform and use_texture functionality)
9/// and execute the shader program (see the draw functionality).
10///
11pub struct Program {
12    context: Context,
13    id: crate::context::Program,
14    attributes: HashMap<String, u32>,
15    textures: RwLock<HashMap<String, u32>>,
16    uniforms: HashMap<String, crate::context::UniformLocation>,
17    uniform_blocks: RwLock<HashMap<String, (u32, u32)>>,
18}
19
20impl Program {
21    ///
22    /// Creates a new shader program from the given vertex and fragment glsl shader source.
23    ///
24    pub fn from_source(
25        context: &Context,
26        vertex_shader_source: &str,
27        fragment_shader_source: &str,
28    ) -> Result<Self, CoreError> {
29        unsafe {
30            let vert_shader = context
31                .create_shader(crate::context::VERTEX_SHADER)
32                .expect("Failed creating vertex shader");
33            let frag_shader = context
34                .create_shader(crate::context::FRAGMENT_SHADER)
35                .expect("Failed creating fragment shader");
36
37            let header: &str = if context.version().is_embedded {
38                "#version 300 es
39                    #ifdef GL_FRAGMENT_PRECISION_HIGH
40                        precision highp float;
41                        precision highp int;
42                        precision highp sampler2DArray;
43                        precision highp sampler3D;
44                    #else
45                        precision mediump float;
46                        precision mediump int;
47                        precision mediump sampler2DArray;
48                        precision mediump sampler3D;
49                    #endif\n"
50            } else {
51                "#version 330 core\n"
52            };
53            let vertex_shader_source = format!("{}{}", header, vertex_shader_source);
54            let fragment_shader_source = format!("{}{}", header, fragment_shader_source);
55
56            context.shader_source(vert_shader, &vertex_shader_source);
57            context.shader_source(frag_shader, &fragment_shader_source);
58            context.compile_shader(vert_shader);
59            context.compile_shader(frag_shader);
60
61            let id = context.create_program().expect("Failed creating program");
62            context.attach_shader(id, vert_shader);
63            context.attach_shader(id, frag_shader);
64            context.link_program(id);
65
66            if !context.get_program_link_status(id) {
67                let log = context.get_shader_info_log(vert_shader);
68                if !log.is_empty() {
69                    Err(shader_compilation_error(
70                        "vertex",
71                        log,
72                        vertex_shader_source,
73                    ))?;
74                }
75                let log = context.get_shader_info_log(frag_shader);
76                if !log.is_empty() {
77                    Err(shader_compilation_error(
78                        "fragment",
79                        log,
80                        fragment_shader_source,
81                    ))?;
82                }
83                let log = context.get_program_info_log(id);
84                if !log.is_empty() {
85                    Err(CoreError::ShaderLink(log))?;
86                }
87                Err(CoreError::ShaderCompilerError)?;
88            }
89
90            context.detach_shader(id, vert_shader);
91            context.detach_shader(id, frag_shader);
92            context.delete_shader(vert_shader);
93            context.delete_shader(frag_shader);
94
95            // Init vertex attributes
96            let num_attribs = context.get_active_attributes(id);
97            let mut attributes = HashMap::new();
98            for i in 0..num_attribs {
99                if let Some(crate::context::ActiveAttribute { name, .. }) = context
100                    .get_active_attribute(id, i)
101                    .filter(|a| !a.name.starts_with("gl_"))
102                {
103                    if let Some(location) = context.get_attrib_location(id, &name) {
104                        attributes.insert(name, location);
105                    }
106                }
107            }
108
109            // Init uniforms
110            let num_uniforms = context.get_active_uniforms(id);
111            let mut uniforms = HashMap::new();
112            for i in 0..num_uniforms {
113                if let Some(crate::context::ActiveUniform { name, .. }) = context
114                    .get_active_uniform(id, i)
115                    .filter(|a| !a.name.starts_with("gl_"))
116                {
117                    if let Some(location) = context.get_uniform_location(id, &name) {
118                        let name = name.split('[').next().unwrap().to_string();
119                        uniforms.insert(name, location);
120                    }
121                }
122            }
123
124            Ok(Program {
125                context: context.clone(),
126                id,
127                attributes,
128                uniforms,
129                uniform_blocks: RwLock::new(HashMap::new()),
130                textures: RwLock::new(HashMap::new()),
131            })
132        }
133    }
134
135    ///
136    /// Send the given uniform data to this shader program and associate it with the given named variable.
137    /// The glsl shader variable must be of type `uniform int` if the data is an integer, `uniform vec2` if it is of type [Vec2] etc.
138    /// The uniform variable is uniformly available across all processing of vertices and fragments.
139    ///
140    /// # Panic
141    /// Will panic if the uniform is not defined or not used in the shader code.
142    /// In the latter case the variable is removed by the shader compiler.
143    ///
144    pub fn use_uniform<T: UniformDataType>(&self, name: &str, data: T) {
145        let location = self.get_uniform_location(name);
146        T::send_uniform(&self.context, location, &[data]);
147        self.unuse_program();
148    }
149
150    ///
151    /// Calls [Self::use_uniform] if [Self::requires_uniform] returns true.
152    ///
153    pub fn use_uniform_if_required<T: UniformDataType>(&self, name: &str, data: T) {
154        if self.requires_uniform(name) {
155            self.use_uniform(name, data);
156        }
157    }
158
159    ///
160    /// Send the given array of uniform data to this shader program and associate it with the given named variable.
161    /// The glsl shader variable must be of same type and length as the data, so if the data is an array of three [Vec2], the variable must be `uniform vec2[3]`.
162    /// The uniform variable is uniformly available across all processing of vertices and fragments.
163    ///
164    /// # Panic
165    /// Will panic if the uniform is not defined in the shader code or not used.
166    /// In the latter case the variable is removed by the shader compiler.
167    ///
168    pub fn use_uniform_array<T: UniformDataType>(&self, name: &str, data: &[T]) {
169        let location = self.get_uniform_location(name);
170        T::send_uniform(&self.context, location, data);
171        self.unuse_program();
172    }
173
174    fn get_uniform_location(&self, name: &str) -> &crate::context::UniformLocation {
175        self.use_program();
176        self.uniforms.get(name).unwrap_or_else(|| {
177            panic!(
178                "the uniform {} is sent to the shader but not defined or never used",
179                name
180            )
181        })
182    }
183
184    ///
185    /// Use the given [Texture2D] in this shader program and associate it with the given named variable.
186    /// The glsl shader variable must be of type `uniform sampler2D` and can only be accessed in the fragment shader.
187    ///
188    /// # Panic
189    /// Will panic if the texture is not defined in the shader code or not used.
190    /// In the latter case the variable is removed by the shader compiler.
191    ///
192    pub fn use_texture(&self, name: &str, texture: &Texture2D) {
193        self.use_texture_internal(name);
194        texture.bind();
195    }
196
197    ///
198    /// Use the given [DepthTexture2D] in this shader program and associate it with the given named variable.
199    /// The glsl shader variable must be of type `uniform sampler2D` and can only be accessed in the fragment shader.
200    ///
201    /// # Panic
202    /// Will panic if the texture is not defined in the shader code or not used.
203    /// In the latter case the variable is removed by the shader compiler.
204    ///
205    pub fn use_depth_texture(&self, name: &str, texture: &DepthTexture2D) {
206        self.use_texture_internal(name);
207        texture.bind();
208    }
209
210    ///
211    /// Use the given texture array in this shader program and associate it with the given named variable.
212    /// The glsl shader variable must be of type `uniform sampler2DArray` and can only be accessed in the fragment shader.
213    ///
214    /// # Panic
215    /// Will panic if the texture is not defined in the shader code or not used.
216    /// In the latter case the variable is removed by the shader compiler.
217    ///
218    pub fn use_texture_array(&self, name: &str, texture: &Texture2DArray) {
219        self.use_texture_internal(name);
220        texture.bind();
221    }
222
223    ///
224    /// Use the given texture array in this shader program and associate it with the given named variable.
225    /// The glsl shader variable must be of type `uniform sampler2DArray` and can only be accessed in the fragment shader.
226    ///
227    /// # Panic
228    /// Will panic if the texture is not defined in the shader code or not used.
229    /// In the latter case the variable is removed by the shader compiler.
230    ///
231    pub fn use_depth_texture_array(&self, name: &str, texture: &DepthTexture2DArray) {
232        self.use_texture_internal(name);
233        texture.bind();
234    }
235
236    ///
237    /// Use the given texture cube map in this shader program and associate it with the given named variable.
238    /// The glsl shader variable must be of type `uniform samplerCube` and can only be accessed in the fragment shader.
239    ///
240    /// # Panic
241    /// Will panic if the texture is not defined in the shader code or not used.
242    /// In the latter case the variable is removed by the shader compiler.
243    ///
244    pub fn use_texture_cube(&self, name: &str, texture: &TextureCubeMap) {
245        self.use_texture_internal(name);
246        texture.bind();
247    }
248
249    ///
250    /// Use the given texture cube map in this shader program and associate it with the given named variable.
251    /// The glsl shader variable must be of type `uniform samplerCube` and can only be accessed in the fragment shader.
252    ///
253    /// # Panic
254    /// Will panic if the texture is not defined in the shader code or not used.
255    /// In the latter case the variable is removed by the shader compiler.
256    ///
257    pub fn use_depth_texture_cube(&self, name: &str, texture: &DepthTextureCubeMap) {
258        self.use_texture_internal(name);
259        texture.bind();
260    }
261
262    ///
263    /// Use the given 3D texture in this shader program and associate it with the given named variable.
264    /// The glsl shader variable must be of type `uniform sampler3D` and can only be accessed in the fragment shader.
265    ///
266    /// # Panic
267    /// Will panic if the texture is not defined in the shader code or not used.
268    /// In the latter case the variable is removed by the shader compiler.
269    ///
270    pub fn use_texture_3d(&self, name: &str, texture: &Texture3D) {
271        self.use_texture_internal(name);
272        texture.bind();
273    }
274
275    ///
276    /// Use this function if you want to use a texture which was created using low-level context calls and not using the functionality in the [texture] module.
277    /// This function is only needed in special cases for example if you have a special source of texture data.
278    ///
279    pub fn use_raw_texture(&self, name: &str, target: u32, id: crate::context::Texture) {
280        self.use_texture_internal(name);
281        unsafe {
282            self.context.bind_texture(target, Some(id));
283        }
284    }
285
286    fn use_texture_internal(&self, name: &str) -> u32 {
287        if !self.textures.read().unwrap().contains_key(name) {
288            let mut map = self.textures.write().unwrap();
289            let index = map.len() as u32;
290            map.insert(name.to_owned(), index);
291        };
292        let index = *self.textures.read().unwrap().get(name).unwrap();
293        self.use_uniform(name, index as i32);
294        unsafe {
295            self.context
296                .active_texture(crate::context::TEXTURE0 + index);
297        }
298        index
299    }
300
301    ///
302    /// Use the given [UniformBuffer] in this shader program and associate it with the given named variable.
303    ///
304    pub fn use_uniform_block(&self, name: &str, buffer: &UniformBuffer) {
305        if !self.uniform_blocks.read().unwrap().contains_key(name) {
306            let mut map = self.uniform_blocks.write().unwrap();
307            let location = unsafe {
308                self.context
309                    .get_uniform_block_index(self.id, name)
310                    .unwrap_or_else(|| panic!("the uniform block {} is sent to the shader but not defined or never used",
311                        name))
312            };
313            let index = map.len() as u32;
314            map.insert(name.to_owned(), (location, index));
315        };
316        let (location, index) = *self.uniform_blocks.read().unwrap().get(name).unwrap();
317        unsafe {
318            self.context.uniform_block_binding(self.id, location, index);
319            buffer.bind(index);
320            self.context
321                .bind_buffer(crate::context::UNIFORM_BUFFER, None);
322        }
323    }
324
325    ///
326    /// Uses the given [VertexBuffer] data in this shader program and associates it with the given named variable.
327    /// Each value in the buffer is used when rendering one vertex using the [Program::draw_arrays] or [Program::draw_elements] methods.
328    /// Therefore the buffer must contain the same number of values as the number of vertices specified in those draw calls.
329    ///
330    /// # Panic
331    /// Will panic if the attribute is not defined in the shader code or not used.
332    /// In the latter case the variable is removed by the shader compiler.
333    ///
334    pub fn use_vertex_attribute<T: BufferDataType>(&self, name: &str, buffer: &VertexBuffer<T>) {
335        if buffer.count() > 0 {
336            buffer.bind();
337            let loc = self.location(name);
338            unsafe {
339                self.context.bind_vertex_array(Some(self.context.vao));
340                self.context.enable_vertex_attrib_array(loc);
341                if !T::normalized()
342                    && (T::data_type() == crate::context::UNSIGNED_BYTE
343                        || T::data_type() == crate::context::BYTE
344                        || T::data_type() == crate::context::UNSIGNED_SHORT
345                        || T::data_type() == crate::context::SHORT
346                        || T::data_type() == crate::context::UNSIGNED_INT
347                        || T::data_type() == crate::context::INT)
348                {
349                    self.context.vertex_attrib_pointer_i32(
350                        loc,
351                        T::size() as i32,
352                        T::data_type(),
353                        0,
354                        0,
355                    );
356                } else {
357                    self.context.vertex_attrib_pointer_f32(
358                        loc,
359                        T::size() as i32,
360                        T::data_type(),
361                        T::normalized(),
362                        0,
363                        0,
364                    );
365                }
366                self.context.vertex_attrib_divisor(loc, 0);
367                self.context.bind_buffer(crate::context::ARRAY_BUFFER, None);
368            }
369            self.unuse_program();
370        }
371    }
372
373    ///
374    /// Uses the given [InstanceBuffer] data in this shader program and associates it with the given named variable.
375    /// Each value in the buffer is used when rendering one instance using the [Program::draw_arrays_instanced] or [Program::draw_elements_instanced] methods.
376    /// Therefore the buffer must contain the same number of values as the number of instances specified in those draw calls.
377    ///
378    /// # Panic
379    /// Will panic if the attribute is not defined in the shader code or not used.
380    /// In the latter case the variable is removed by the shader compiler.
381    ///
382    pub fn use_instance_attribute<T: BufferDataType>(
383        &self,
384        name: &str,
385        buffer: &InstanceBuffer<T>,
386    ) {
387        if buffer.count() > 0 {
388            buffer.bind();
389            let loc = self.location(name);
390            unsafe {
391                self.context.bind_vertex_array(Some(self.context.vao));
392                self.context.enable_vertex_attrib_array(loc);
393                if !T::normalized()
394                    && (T::data_type() == crate::context::UNSIGNED_BYTE
395                        || T::data_type() == crate::context::BYTE
396                        || T::data_type() == crate::context::UNSIGNED_SHORT
397                        || T::data_type() == crate::context::SHORT
398                        || T::data_type() == crate::context::UNSIGNED_INT
399                        || T::data_type() == crate::context::INT)
400                {
401                    self.context.vertex_attrib_pointer_i32(
402                        loc,
403                        T::size() as i32,
404                        T::data_type(),
405                        0,
406                        0,
407                    );
408                } else {
409                    self.context.vertex_attrib_pointer_f32(
410                        loc,
411                        T::size() as i32,
412                        T::data_type(),
413                        T::normalized(),
414                        0,
415                        0,
416                    );
417                }
418                self.context.vertex_attrib_divisor(loc, 1);
419                self.context.bind_buffer(crate::context::ARRAY_BUFFER, None);
420            }
421            self.unuse_program();
422        }
423    }
424
425    ///
426    /// Draws `count` number of triangles with the given render states and viewport using this shader program.
427    /// Requires that all attributes and uniforms have been defined using the use_attribute and use_uniform methods.
428    /// Assumes that the data for the three vertices in a triangle is defined contiguous in each vertex buffer.
429    /// If you want to use an [ElementBuffer], see [Program::draw_elements].
430    ///
431    pub fn draw_arrays(&self, render_states: RenderStates, viewport: Viewport, count: u32) {
432        self.context.set_viewport(viewport);
433        self.context.set_render_states(render_states);
434        self.use_program();
435        unsafe {
436            self.context
437                .draw_arrays(crate::context::TRIANGLES, 0, count as i32);
438            for location in self.attributes.values() {
439                self.context.disable_vertex_attrib_array(*location);
440            }
441            self.context.bind_vertex_array(None);
442        }
443        self.unuse_program();
444
445        #[cfg(debug_assertions)]
446        self.context
447            .error_check()
448            .expect("Unexpected rendering error occured")
449    }
450
451    ///
452    /// Same as [Program::draw_arrays] except it renders 'instance_count' instances of the same set of triangles.
453    /// Use the [Program::use_instance_attribute], method to send unique data for each instance to the shader.
454    ///
455    pub fn draw_arrays_instanced(
456        &self,
457        render_states: RenderStates,
458        viewport: Viewport,
459        count: u32,
460        instance_count: u32,
461    ) {
462        self.context.set_viewport(viewport);
463        self.context.set_render_states(render_states);
464        self.use_program();
465        unsafe {
466            self.context.draw_arrays_instanced(
467                crate::context::TRIANGLES,
468                0,
469                count as i32,
470                instance_count as i32,
471            );
472            self.context
473                .bind_buffer(crate::context::ELEMENT_ARRAY_BUFFER, None);
474            for location in self.attributes.values() {
475                self.context.disable_vertex_attrib_array(*location);
476            }
477            self.context.bind_vertex_array(None);
478        }
479        self.unuse_program();
480
481        #[cfg(debug_assertions)]
482        self.context
483            .error_check()
484            .expect("Unexpected rendering error occured")
485    }
486
487    ///
488    /// Draws the triangles defined by the given [ElementBuffer] with the given render states and viewport using this shader program.
489    /// Requires that all attributes and uniforms have been defined using the use_attribute and use_uniform methods.
490    /// If you do not want to use an [ElementBuffer], see [Program::draw_arrays]. If you only want to draw a subset of the triangles in the given [ElementBuffer], see [Program::draw_subset_of_elements].
491    ///
492    pub fn draw_elements<T: ElementBufferDataType>(
493        &self,
494        render_states: RenderStates,
495        viewport: Viewport,
496        element_buffer: &ElementBuffer<T>,
497    ) {
498        self.draw_subset_of_elements(
499            render_states,
500            viewport,
501            element_buffer,
502            0,
503            element_buffer.count() as u32,
504        )
505    }
506
507    ///
508    /// Draws a subset of the triangles defined by the given [ElementBuffer] with the given render states and viewport using this shader program.
509    /// Requires that all attributes and uniforms have been defined using the use_attribute and use_uniform methods.
510    /// If you do not want to use an [ElementBuffer], see [Program::draw_arrays].
511    ///
512    pub fn draw_subset_of_elements<T: ElementBufferDataType>(
513        &self,
514        render_states: RenderStates,
515        viewport: Viewport,
516        element_buffer: &ElementBuffer<T>,
517        first: u32,
518        count: u32,
519    ) {
520        self.context.set_viewport(viewport);
521        self.context.set_render_states(render_states);
522        self.use_program();
523        element_buffer.bind();
524        unsafe {
525            self.context.draw_elements(
526                crate::context::TRIANGLES,
527                count as i32,
528                T::data_type(),
529                first as i32,
530            );
531            self.context
532                .bind_buffer(crate::context::ELEMENT_ARRAY_BUFFER, None);
533
534            for location in self.attributes.values() {
535                self.context.disable_vertex_attrib_array(*location);
536            }
537            self.context.bind_vertex_array(None);
538        }
539        self.unuse_program();
540
541        #[cfg(debug_assertions)]
542        self.context
543            .error_check()
544            .expect("Unexpected rendering error occured")
545    }
546
547    ///
548    /// Same as [Program::draw_elements] except it renders 'instance_count' instances of the same set of triangles.
549    /// Use the [Program::use_instance_attribute] method to send unique data for each instance to the shader.
550    ///
551    pub fn draw_elements_instanced<T: ElementBufferDataType>(
552        &self,
553        render_states: RenderStates,
554        viewport: Viewport,
555        element_buffer: &ElementBuffer<T>,
556        instance_count: u32,
557    ) {
558        self.draw_subset_of_elements_instanced(
559            render_states,
560            viewport,
561            element_buffer,
562            0,
563            element_buffer.count() as u32,
564            instance_count,
565        )
566    }
567
568    ///
569    /// Same as [Program::draw_subset_of_elements] except it renders 'instance_count' instances of the same set of triangles.
570    /// Use the [Program::use_instance_attribute] method to send unique data for each instance to the shader.
571    ///
572    pub fn draw_subset_of_elements_instanced<T: ElementBufferDataType>(
573        &self,
574        render_states: RenderStates,
575        viewport: Viewport,
576        element_buffer: &ElementBuffer<T>,
577        first: u32,
578        count: u32,
579        instance_count: u32,
580    ) {
581        self.context.set_viewport(viewport);
582        self.context.set_render_states(render_states);
583        self.use_program();
584        element_buffer.bind();
585        unsafe {
586            self.context.draw_elements_instanced(
587                crate::context::TRIANGLES,
588                count as i32,
589                T::data_type(),
590                first as i32,
591                instance_count as i32,
592            );
593            self.context
594                .bind_buffer(crate::context::ELEMENT_ARRAY_BUFFER, None);
595            for location in self.attributes.values() {
596                self.context.disable_vertex_attrib_array(*location);
597            }
598            self.context.bind_vertex_array(None);
599        }
600        self.unuse_program();
601
602        #[cfg(debug_assertions)]
603        self.context
604            .error_check()
605            .expect("Unexpected rendering error occured")
606    }
607
608    ///
609    /// Returns true if this program uses the uniform with the given name.
610    ///
611    pub fn requires_uniform(&self, name: &str) -> bool {
612        self.uniforms.contains_key(name)
613    }
614
615    ///
616    /// Returns true if this program uses the attribute with the given name.
617    ///
618    pub fn requires_attribute(&self, name: &str) -> bool {
619        self.attributes.contains_key(name)
620    }
621
622    fn location(&self, name: &str) -> u32 {
623        self.use_program();
624        *self.attributes.get(name).unwrap_or_else(|| {
625            panic!(
626                "the attribute {} is sent to the shader but not defined or never used",
627                name
628            )
629        })
630    }
631
632    fn use_program(&self) {
633        unsafe {
634            self.context.use_program(Some(self.id));
635        }
636    }
637
638    fn unuse_program(&self) {
639        unsafe {
640            self.context.use_program(None);
641        }
642    }
643}
644
645impl Drop for Program {
646    fn drop(&mut self) {
647        unsafe {
648            self.context.delete_program(self.id);
649        }
650    }
651}
652fn shader_compilation_error(typ: &str, log: String, source: String) -> CoreError {
653    let lines: Vec<String> = source
654        .lines()
655        .enumerate()
656        .map(|(index, l)| format!("{:0>3}: {}", index + 1, l))
657        .collect();
658    CoreError::ShaderCompilation(typ.to_string(), lines.join("\n"), log)
659}