Require active program to update uniform
Description
One challenge with multiple programs is that a program needs to be active before you can update the uniform. If we tried to update a uniform for all programs at once, only the active one will actually be updated.
self.cube_program.activate();
self.cube_program.set_uniform_f32("camera_pitch", 0.0);
// This won't actually update since the skybox program isn't active
self.skybox_program.set_uniform_f32("camera_pitch", 0.0);
We've been lax with our error handling in OpenGL which may have caught this, but it would also be great if the type system could prevent this.
This change introduces a new ActiveProgram
struct that's basically a
wrapper for our existing Program
. The set_uniform_*
methods are
moved to this new struct, and we can mutably borrow the active program
if we need to update a uniform.
However, if we mutably borrow one program, we can't borrow the other at the same time (I think). This prevents activating both programs at once.
Commands
git clone git@github.com:atsheehan/iridium
cd iridium
git checkout 81f37ed072bc0777a3704489d57aed074a1eb500
cargo run --release
Code Changes
Modified src/render.rsGitHub
@@ -275,8 +275,9 @@275275 }
276276
277277 pub(crate) fn draw_cubes(&mut self) {
278+ self.activate_cube_program();
279+
278280 unsafe {
279- gl::UseProgram(self.cube_program.gl_id());
280281 gl::BindVertexArray(self.cube_vertex_array_id);
281282 gl::BindTexture(gl::TEXTURE_2D, self.cube_texture_id);
282283 gl::DrawArraysInstanced(gl::TRIANGLES, 0, 36, self.cube_count as GLint);
@@ -284,8 +285,9 @@284285 }
285286
286287 pub(crate) fn draw_skybox(&mut self) {
288+ self.activate_skybox_program();
289+
287290 unsafe {
288- gl::UseProgram(self.skybox_program.gl_id());
289291 gl::BindVertexArray(self.skybox_vertex_array_id);
290292 gl::BindTexture(gl::TEXTURE_CUBE_MAP, self.skybox_texture_id);
291293 gl::DrawArrays(gl::TRIANGLES, 0, 36);
@@ -318,12 +320,9 @@318320 let window_size = self.window.inner_size();
319321 let aspect_ratio = window_size.width as f32 / window_size.height as f32;
320322
321- self.cube_program.activate();
322- self.cube_program
323+ self.activate_cube_program()
323324 .set_uniform_f32("aspect_ratio", &aspect_ratio);
324-
325- self.skybox_program.activate();
326- self.skybox_program
325+ self.activate_skybox_program()
327326 .set_uniform_f32("aspect_ratio", &aspect_ratio);
328327
329328 unsafe {
@@ -332,24 +331,62 @@332331 }
333332
334333 pub(crate) fn set_camera(&mut self, camera: &Camera) {
335- self.cube_program.activate();
336- self.cube_program
337- .set_uniform_vec3("camera_position", camera.position());
338- self.cube_program
339- .set_uniform_f32("camera_heading", &camera.heading());
340- self.cube_program
341- .set_uniform_f32("camera_pitch", &camera.pitch());
334+ let mut program = self.activate_cube_program();
335+ program.set_uniform_vec3("camera_position", camera.position());
336+ program.set_uniform_f32("camera_heading", &camera.heading());
337+ program.set_uniform_f32("camera_pitch", &camera.pitch());
338+
339+ let mut program = self.activate_skybox_program();
340+ program.set_uniform_vec3("camera_position", camera.position());
341+ program.set_uniform_f32("camera_heading", &camera.heading());
342+ program.set_uniform_f32("camera_pitch", &camera.pitch());
343+ }
344+
345+ fn activate_cube_program(&mut self) -> ActiveProgram<'_> {
346+ unsafe {
347+ gl::UseProgram(self.cube_program.gl_id());
348+ gl::BindVertexArray(self.cube_vertex_array_id);
349+ gl::BindTexture(gl::TEXTURE_2D, self.cube_texture_id);
350+ }
342351
343- self.skybox_program.activate();
344- self.skybox_program
345- .set_uniform_vec3("camera_position", camera.position());
346- self.skybox_program
347- .set_uniform_f32("camera_heading", &camera.heading());
348- self.skybox_program
349- .set_uniform_f32("camera_pitch", &camera.pitch());
352+ ActiveProgram {
353+ program: &mut self.cube_program,
354+ }
350355 }
356+
357+ fn activate_skybox_program(&mut self) -> ActiveProgram<'_> {
358+ unsafe {
359+ gl::UseProgram(self.skybox_program.gl_id());
360+ gl::BindVertexArray(self.skybox_vertex_array_id);
361+ gl::BindTexture(gl::TEXTURE_CUBE_MAP, self.skybox_texture_id);
362+ }
363+
364+ ActiveProgram {
365+ program: &mut self.skybox_program,
366+ }
367+ }
368+ }
369+
370+ struct ActiveProgram<'a> {
371+ program: &'a mut Program,
351372 }
352373
374+ impl<'a> ActiveProgram<'a> {
375+ fn set_uniform_vec3(&mut self, name: &'static str, value: &Vec3) {
376+ let Vec3(x, y, z) = *value;
377+
378+ unsafe {
379+ gl::Uniform3f(self.program.uniform_location(name), x, y, z);
380+ }
381+ }
382+
383+ fn set_uniform_f32(&mut self, name: &'static str, value: &f32) {
384+ unsafe {
385+ gl::Uniform1f(self.program.uniform_location(name), *value);
386+ }
387+ }
388+ }
389+
353390 struct ProgramId(GLuint);
354391
355392 struct Program {
@@ -410,22 +447,8 @@410447
411448 fn gl_id(&self) -> GLuint {
412449 self.id.0
413- }
414-
415- fn set_uniform_vec3(&mut self, name: &'static str, value: &Vec3) {
416- let Vec3(x, y, z) = *value;
417-
418- unsafe {
419- gl::Uniform3f(self.uniform_location(name), x, y, z);
420- }
421450 }
422451
423- fn set_uniform_f32(&mut self, name: &'static str, value: &f32) {
424- unsafe {
425- gl::Uniform1f(self.uniform_location(name), *value);
426- }
427- }
428-
429452 fn uniform_location(&mut self, name: &'static str) -> GLint {
430453 match self.cached_uniform_locations.get(name) {
431454 Some(location) => *location,
@@ -435,12 +458,6 @@435458 self.cached_uniform_locations.insert(name, location);
436459 location
437460 }
438- }
439- }
440-
441- fn activate(&mut self) {
442- unsafe {
443- gl::UseProgram(self.gl_id());
444461 }
445462 }
446463 }
@@ -275,8 +275,9 @@275 }
276
277 pub(crate) fn draw_cubes(&mut self) {
278 unsafe {
279- gl::UseProgram(self.cube_program.gl_id());
280 gl::BindVertexArray(self.cube_vertex_array_id);
281 gl::BindTexture(gl::TEXTURE_2D, self.cube_texture_id);
282 gl::DrawArraysInstanced(gl::TRIANGLES, 0, 36, self.cube_count as GLint);
@@ -284,8 +285,9 @@284 }
285
286 pub(crate) fn draw_skybox(&mut self) {
287 unsafe {
288- gl::UseProgram(self.skybox_program.gl_id());
289 gl::BindVertexArray(self.skybox_vertex_array_id);
290 gl::BindTexture(gl::TEXTURE_CUBE_MAP, self.skybox_texture_id);
291 gl::DrawArrays(gl::TRIANGLES, 0, 36);
@@ -318,12 +320,9 @@318 let window_size = self.window.inner_size();
319 let aspect_ratio = window_size.width as f32 / window_size.height as f32;
320
321- self.cube_program.activate();
322- self.cube_program
323 .set_uniform_f32("aspect_ratio", &aspect_ratio);
324-
325- self.skybox_program.activate();
326- self.skybox_program
327 .set_uniform_f32("aspect_ratio", &aspect_ratio);
328
329 unsafe {
@@ -332,24 +331,62 @@332 }
333
334 pub(crate) fn set_camera(&mut self, camera: &Camera) {
335- self.cube_program.activate();
336- self.cube_program
337- .set_uniform_vec3("camera_position", camera.position());
338- self.cube_program
339- .set_uniform_f32("camera_heading", &camera.heading());
340- self.cube_program
341- .set_uniform_f32("camera_pitch", &camera.pitch());
342
343- self.skybox_program.activate();
344- self.skybox_program
345- .set_uniform_vec3("camera_position", camera.position());
346- self.skybox_program
347- .set_uniform_f32("camera_heading", &camera.heading());
348- self.skybox_program
349- .set_uniform_f32("camera_pitch", &camera.pitch());
350 }
351 }
352
353 struct ProgramId(GLuint);
354
355 struct Program {
@@ -410,22 +447,8 @@410
411 fn gl_id(&self) -> GLuint {
412 self.id.0
413- }
414-
415- fn set_uniform_vec3(&mut self, name: &'static str, value: &Vec3) {
416- let Vec3(x, y, z) = *value;
417-
418- unsafe {
419- gl::Uniform3f(self.uniform_location(name), x, y, z);
420- }
421 }
422
423- fn set_uniform_f32(&mut self, name: &'static str, value: &f32) {
424- unsafe {
425- gl::Uniform1f(self.uniform_location(name), *value);
426- }
427- }
428-
429 fn uniform_location(&mut self, name: &'static str) -> GLint {
430 match self.cached_uniform_locations.get(name) {
431 Some(location) => *location,
@@ -435,12 +458,6 @@435 self.cached_uniform_locations.insert(name, location);
436 location
437 }
438- }
439- }
440-
441- fn activate(&mut self) {
442- unsafe {
443- gl::UseProgram(self.gl_id());
444 }
445 }
446 }
@@ -275,8 +275,9 @@275 }
276
277 pub(crate) fn draw_cubes(&mut self) {
278+ self.activate_cube_program();
279+
280 unsafe {
281 gl::BindVertexArray(self.cube_vertex_array_id);
282 gl::BindTexture(gl::TEXTURE_2D, self.cube_texture_id);
283 gl::DrawArraysInstanced(gl::TRIANGLES, 0, 36, self.cube_count as GLint);
@@ -284,8 +285,9 @@285 }
286
287 pub(crate) fn draw_skybox(&mut self) {
288+ self.activate_skybox_program();
289+
290 unsafe {
291 gl::BindVertexArray(self.skybox_vertex_array_id);
292 gl::BindTexture(gl::TEXTURE_CUBE_MAP, self.skybox_texture_id);
293 gl::DrawArrays(gl::TRIANGLES, 0, 36);
@@ -318,12 +320,9 @@320 let window_size = self.window.inner_size();
321 let aspect_ratio = window_size.width as f32 / window_size.height as f32;
322
323+ self.activate_cube_program()
324 .set_uniform_f32("aspect_ratio", &aspect_ratio);
325+ self.activate_skybox_program()
326 .set_uniform_f32("aspect_ratio", &aspect_ratio);
327
328 unsafe {
@@ -332,24 +331,62 @@331 }
332
333 pub(crate) fn set_camera(&mut self, camera: &Camera) {
334+ let mut program = self.activate_cube_program();
335+ program.set_uniform_vec3("camera_position", camera.position());
336+ program.set_uniform_f32("camera_heading", &camera.heading());
337+ program.set_uniform_f32("camera_pitch", &camera.pitch());
338+
339+ let mut program = self.activate_skybox_program();
340+ program.set_uniform_vec3("camera_position", camera.position());
341+ program.set_uniform_f32("camera_heading", &camera.heading());
342+ program.set_uniform_f32("camera_pitch", &camera.pitch());
343+ }
344+
345+ fn activate_cube_program(&mut self) -> ActiveProgram<'_> {
346+ unsafe {
347+ gl::UseProgram(self.cube_program.gl_id());
348+ gl::BindVertexArray(self.cube_vertex_array_id);
349+ gl::BindTexture(gl::TEXTURE_2D, self.cube_texture_id);
350+ }
351
352+ ActiveProgram {
353+ program: &mut self.cube_program,
354+ }
355 }
356+
357+ fn activate_skybox_program(&mut self) -> ActiveProgram<'_> {
358+ unsafe {
359+ gl::UseProgram(self.skybox_program.gl_id());
360+ gl::BindVertexArray(self.skybox_vertex_array_id);
361+ gl::BindTexture(gl::TEXTURE_CUBE_MAP, self.skybox_texture_id);
362+ }
363+
364+ ActiveProgram {
365+ program: &mut self.skybox_program,
366+ }
367+ }
368+ }
369+
370+ struct ActiveProgram<'a> {
371+ program: &'a mut Program,
372 }
373
374+ impl<'a> ActiveProgram<'a> {
375+ fn set_uniform_vec3(&mut self, name: &'static str, value: &Vec3) {
376+ let Vec3(x, y, z) = *value;
377+
378+ unsafe {
379+ gl::Uniform3f(self.program.uniform_location(name), x, y, z);
380+ }
381+ }
382+
383+ fn set_uniform_f32(&mut self, name: &'static str, value: &f32) {
384+ unsafe {
385+ gl::Uniform1f(self.program.uniform_location(name), *value);
386+ }
387+ }
388+ }
389+
390 struct ProgramId(GLuint);
391
392 struct Program {
@@ -410,22 +447,8 @@447
448 fn gl_id(&self) -> GLuint {
449 self.id.0
450 }
451
452 fn uniform_location(&mut self, name: &'static str) -> GLint {
453 match self.cached_uniform_locations.get(name) {
454 Some(location) => *location,
@@ -435,12 +458,6 @@458 self.cached_uniform_locations.insert(name, location);
459 location
460 }
461 }
462 }
463 }