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 #[deprecated = "Instead, create normal textures, eg. Texture2D, using the new_unchecked() methods, eg. Texture2D::new_unchecked()"]
280 pub fn use_raw_texture(&self, name: &str, target: u32, id: crate::context::Texture) {
281 self.use_texture_internal(name);
282 unsafe {
283 self.context.bind_texture(target, Some(id));
284 }
285 }
286
287 fn use_texture_internal(&self, name: &str) -> u32 {
288 if !self.textures.read().unwrap().contains_key(name) {
289 let mut map = self.textures.write().unwrap();
290 let index = map.len() as u32;
291 map.insert(name.to_owned(), index);
292 };
293 let index = *self.textures.read().unwrap().get(name).unwrap();
294 self.use_uniform(name, index as i32);
295 unsafe {
296 self.context
297 .active_texture(crate::context::TEXTURE0 + index);
298 }
299 index
300 }
301
302 ///
303 /// Use the given [UniformBuffer] in this shader program and associate it with the given named variable.
304 ///
305 pub fn use_uniform_block(&self, name: &str, buffer: &UniformBuffer) {
306 if !self.uniform_blocks.read().unwrap().contains_key(name) {
307 let mut map = self.uniform_blocks.write().unwrap();
308 let location = unsafe {
309 self.context
310 .get_uniform_block_index(self.id, name)
311 .unwrap_or_else(|| panic!("the uniform block {} is sent to the shader but not defined or never used",
312 name))
313 };
314 let index = map.len() as u32;
315 map.insert(name.to_owned(), (location, index));
316 };
317 let (location, index) = *self.uniform_blocks.read().unwrap().get(name).unwrap();
318 unsafe {
319 self.context.uniform_block_binding(self.id, location, index);
320 buffer.bind(index);
321 self.context
322 .bind_buffer(crate::context::UNIFORM_BUFFER, None);
323 }
324 }
325
326 ///
327 /// Uses the given [VertexBuffer] data in this shader program and associates it with the given named variable.
328 /// Each value in the buffer is used when rendering one vertex using the [Program::draw_arrays] or [Program::draw_elements] methods.
329 /// Therefore the buffer must contain the same number of values as the number of vertices specified in those draw calls.
330 ///
331 /// # Panic
332 /// Will panic if the attribute is not defined in the shader code or not used.
333 /// In the latter case the variable is removed by the shader compiler.
334 ///
335 pub fn use_vertex_attribute<T: BufferDataType>(&self, name: &str, buffer: &VertexBuffer<T>) {
336 if buffer.count() > 0 {
337 buffer.bind();
338 let loc = self.location(name);
339 unsafe {
340 self.context.bind_vertex_array(Some(self.context.vao));
341 self.context.enable_vertex_attrib_array(loc);
342 if !T::normalized()
343 && (T::data_type() == crate::context::UNSIGNED_BYTE
344 || T::data_type() == crate::context::BYTE
345 || T::data_type() == crate::context::UNSIGNED_SHORT
346 || T::data_type() == crate::context::SHORT
347 || T::data_type() == crate::context::UNSIGNED_INT
348 || T::data_type() == crate::context::INT)
349 {
350 self.context.vertex_attrib_pointer_i32(
351 loc,
352 T::size() as i32,
353 T::data_type(),
354 0,
355 0,
356 );
357 } else {
358 self.context.vertex_attrib_pointer_f32(
359 loc,
360 T::size() as i32,
361 T::data_type(),
362 T::normalized(),
363 0,
364 0,
365 );
366 }
367 self.context.vertex_attrib_divisor(loc, 0);
368 self.context.bind_buffer(crate::context::ARRAY_BUFFER, None);
369 }
370 self.unuse_program();
371 }
372 }
373
374 ///
375 /// Uses the given [InstanceBuffer] data in this shader program and associates it with the given named variable.
376 /// Each value in the buffer is used when rendering one instance using the [Program::draw_arrays_instanced] or [Program::draw_elements_instanced] methods.
377 /// Therefore the buffer must contain the same number of values as the number of instances specified in those draw calls.
378 ///
379 /// # Panic
380 /// Will panic if the attribute is not defined in the shader code or not used.
381 /// In the latter case the variable is removed by the shader compiler.
382 ///
383 pub fn use_instance_attribute<T: BufferDataType>(
384 &self,
385 name: &str,
386 buffer: &InstanceBuffer<T>,
387 ) {
388 if buffer.count() > 0 {
389 buffer.bind();
390 let loc = self.location(name);
391 unsafe {
392 self.context.bind_vertex_array(Some(self.context.vao));
393 self.context.enable_vertex_attrib_array(loc);
394 if !T::normalized()
395 && (T::data_type() == crate::context::UNSIGNED_BYTE
396 || T::data_type() == crate::context::BYTE
397 || T::data_type() == crate::context::UNSIGNED_SHORT
398 || T::data_type() == crate::context::SHORT
399 || T::data_type() == crate::context::UNSIGNED_INT
400 || T::data_type() == crate::context::INT)
401 {
402 self.context.vertex_attrib_pointer_i32(
403 loc,
404 T::size() as i32,
405 T::data_type(),
406 0,
407 0,
408 );
409 } else {
410 self.context.vertex_attrib_pointer_f32(
411 loc,
412 T::size() as i32,
413 T::data_type(),
414 T::normalized(),
415 0,
416 0,
417 );
418 }
419 self.context.vertex_attrib_divisor(loc, 1);
420 self.context.bind_buffer(crate::context::ARRAY_BUFFER, None);
421 }
422 self.unuse_program();
423 }
424 }
425
426 ///
427 /// Draws triangles with the given render states and viewport using this shader program.
428 /// The number of vertices to draw is defined by the `count` parameter.
429 /// Requires that all attributes and uniforms have been defined using the use_attribute and use_uniform methods.
430 /// Assumes that the data for the three vertices in a triangle is defined contiguous in each vertex buffer.
431 /// If you want to use an [ElementBuffer], see [Program::draw_elements].
432 ///
433 pub fn draw_arrays(&self, render_states: RenderStates, viewport: Viewport, count: u32) {
434 self.draw_with(render_states, viewport, move || unsafe {
435 self.context
436 .draw_arrays(crate::context::TRIANGLES, 0, count as i32);
437 })
438 }
439
440 ///
441 /// Same as [Program::draw_arrays] except it renders 'instance_count' instances of the same set of triangles.
442 /// Use the [Program::use_instance_attribute], method to send unique data for each instance to the shader.
443 ///
444 pub fn draw_arrays_instanced(
445 &self,
446 render_states: RenderStates,
447 viewport: Viewport,
448 count: u32,
449 instance_count: u32,
450 ) {
451 self.draw_with(render_states, viewport, move || unsafe {
452 self.context.draw_arrays_instanced(
453 crate::context::TRIANGLES,
454 0,
455 count as i32,
456 instance_count as i32,
457 );
458 self.context
459 .bind_buffer(crate::context::ELEMENT_ARRAY_BUFFER, None);
460 })
461 }
462
463 ///
464 /// Draws the triangles defined by the given [ElementBuffer] with the given render states and viewport using this shader program.
465 /// Requires that all attributes and uniforms have been defined using the use_attribute and use_uniform methods.
466 /// 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].
467 ///
468 pub fn draw_elements<T: ElementBufferDataType>(
469 &self,
470 render_states: RenderStates,
471 viewport: Viewport,
472 element_buffer: &ElementBuffer<T>,
473 ) {
474 self.draw_subset_of_elements(
475 render_states,
476 viewport,
477 element_buffer,
478 0,
479 element_buffer.count(),
480 )
481 }
482
483 ///
484 /// Draws a subset of the triangles defined by the given [ElementBuffer] with the given render states and viewport using this shader program.
485 /// The number of vertices to draw is defined by the `count` parameter.
486 /// Requires that all attributes and uniforms have been defined using the use_attribute and use_uniform methods.
487 /// If you do not want to use an [ElementBuffer], see [Program::draw_arrays].
488 ///
489 pub fn draw_subset_of_elements<T: ElementBufferDataType>(
490 &self,
491 render_states: RenderStates,
492 viewport: Viewport,
493 element_buffer: &ElementBuffer<T>,
494 first: u32,
495 count: u32,
496 ) {
497 self.draw_elements_with(render_states, viewport, element_buffer, move || unsafe {
498 self.context.draw_elements(
499 crate::context::TRIANGLES,
500 count as i32,
501 T::data_type(),
502 first as i32,
503 );
504 })
505 }
506
507 ///
508 /// Same as [Program::draw_elements] except it renders 'instance_count' instances of the same set of triangles.
509 /// Use the [Program::use_instance_attribute] method to send unique data for each instance to the shader.
510 ///
511 pub fn draw_elements_instanced<T: ElementBufferDataType>(
512 &self,
513 render_states: RenderStates,
514 viewport: Viewport,
515 element_buffer: &ElementBuffer<T>,
516 instance_count: u32,
517 ) {
518 self.draw_subset_of_elements_instanced(
519 render_states,
520 viewport,
521 element_buffer,
522 0,
523 element_buffer.count(),
524 instance_count,
525 )
526 }
527
528 ///
529 /// Same as [Program::draw_subset_of_elements] except it renders 'instance_count' instances of the same set of triangles.
530 /// Use the [Program::use_instance_attribute] method to send unique data for each instance to the shader.
531 ///
532 pub fn draw_subset_of_elements_instanced<T: ElementBufferDataType>(
533 &self,
534 render_states: RenderStates,
535 viewport: Viewport,
536 element_buffer: &ElementBuffer<T>,
537 first: u32,
538 count: u32,
539 instance_count: u32,
540 ) {
541 self.draw_elements_with(render_states, viewport, element_buffer, move || unsafe {
542 self.context.draw_elements_instanced(
543 crate::context::TRIANGLES,
544 count as i32,
545 T::data_type(),
546 first as i32,
547 instance_count as i32,
548 );
549 })
550 }
551
552 ///
553 /// Calls drawing callback `draw` after setting up rendering to use this shader program, cleaning up before return.
554 /// Requires that all attributes and uniforms have been defined using the [use_attribute] and [use_uniform] methods.
555 ///
556 pub fn draw_with(&self, render_states: RenderStates, viewport: Viewport, draw: impl FnOnce()) {
557 self.context.set_viewport(viewport);
558 self.context.set_render_states(render_states);
559 self.use_program();
560
561 draw();
562
563 unsafe {
564 for location in self.attributes.values() {
565 self.context.disable_vertex_attrib_array(*location);
566 }
567 self.context.bind_vertex_array(None);
568 }
569
570 self.unuse_program();
571
572 #[cfg(debug_assertions)]
573 self.context
574 .error_check()
575 .expect("Unexpected rendering error occured")
576 }
577
578 ///
579 /// Calls drawing callback `draw` after setting up rendering to use this shader program and element buffer `elements`, cleaning up before return.
580 /// Requires that all attributes and uniforms have been defined using the [use_attribute] and [use_uniform] methods.
581 ///
582 pub fn draw_elements_with<T: ElementBufferDataType>(
583 &self,
584 render_states: RenderStates,
585 viewport: Viewport,
586 element_buffer: &ElementBuffer<T>,
587 draw: impl FnOnce(),
588 ) {
589 self.draw_with(render_states, viewport, move || {
590 element_buffer.bind();
591 draw();
592 unsafe {
593 self.context
594 .bind_buffer(crate::context::ELEMENT_ARRAY_BUFFER, None);
595 }
596 })
597 }
598
599 ///
600 /// Returns true if this program uses the uniform with the given name.
601 ///
602 pub fn requires_uniform(&self, name: &str) -> bool {
603 self.uniforms.contains_key(name)
604 }
605
606 ///
607 /// Returns true if this program uses the attribute with the given name.
608 ///
609 pub fn requires_attribute(&self, name: &str) -> bool {
610 self.attributes.contains_key(name)
611 }
612
613 fn location(&self, name: &str) -> u32 {
614 self.use_program();
615 *self.attributes.get(name).unwrap_or_else(|| {
616 panic!(
617 "the attribute {} is sent to the shader but not defined or never used",
618 name
619 )
620 })
621 }
622
623 fn use_program(&self) {
624 unsafe {
625 self.context.use_program(Some(self.id));
626 }
627 }
628
629 fn unuse_program(&self) {
630 unsafe {
631 self.context.use_program(None);
632 }
633 }
634}
635
636impl Drop for Program {
637 fn drop(&mut self) {
638 unsafe {
639 self.context.delete_program(self.id);
640 }
641 }
642}
643fn shader_compilation_error(typ: &str, log: String, source: String) -> CoreError {
644 let lines: Vec<String> = source
645 .lines()
646 .enumerate()
647 .map(|(index, l)| format!("{:0>3}: {}", index + 1, l))
648 .collect();
649 CoreError::ShaderCompilation(typ.to_string(), lines.join("\n"), log)
650}