Cache uniform locations
Description
In the last commit we profiled our application and generated a
flamegraph. Ignoring the iridium:gdrv0
thread, we can see where we're
spending our time on the right side of the graph. There's a lot of setup
on the bottom half, until we reach iridium::main
which is the main
function in our program.
Moving up the stack, we're spending almost all of our time in
Renderer::draw_cube
. Within that method, a large chunk is consumed
getting the uniform locations within our shaders.
These uniform locations don't change once the program is linked, so we could try caching them. This change introduces a HashMap to store the locations by name after their first lookup.
The names are changed to static strings since we're hardcoding them in the source for now.
Running this on my machine improved performance from ~10 FPS to ~16 FPS for a 400x400 world. That's significant, but we still need to do quite a bit more. I've included an updated flamegraph from the latest run.
Commands
git clone git@github.com:atsheehan/iridium
cd iridium
git checkout 59f2e05b6f92a824b189eb0f39bc1eba2c2f78d6
cargo run --release
Code Changes
Modified analysis/flamegraph.svgGitHub
Modified src/render.rsGitHub
@@ -1,4 +1,5 @@11 use std::{
2+ collections::HashMap,
23 ffi::{c_void, CString},
34 fmt::Display,
45 };
@@ -197,6 +198,7 @@197198
198199 struct Program {
199200 id: ProgramId,
201+ cached_uniform_locations: HashMap<&'static str, GLint>,
200202 }
201203
202204 impl Program {
@@ -222,6 +224,7 @@222224 if linking_was_successful {
223225 Ok(Self {
224226 id: ProgramId(program_id),
227+ cached_uniform_locations: HashMap::new(),
225228 })
226229 } else {
227230 let error_message_len: usize = unsafe {
@@ -253,7 +256,7 @@253256 self.id.0
254257 }
255258
256- fn set_uniform_vec3(&mut self, name: &str, value: &Vec3) {
259+ fn set_uniform_vec3(&mut self, name: &'static str, value: &Vec3) {
257260 let Vec3(x, y, z) = *value;
258261
259262 unsafe {
@@ -261,15 +264,22 @@261264 }
262265 }
263266
264- fn set_uniform_f32(&mut self, name: &str, value: &f32) {
267+ fn set_uniform_f32(&mut self, name: &'static str, value: &f32) {
265268 unsafe {
266269 gl::Uniform1f(self.uniform_location(name), *value);
267270 }
268271 }
269272
270- fn uniform_location(&self, name: &str) -> GLint {
271- let cstr_name = CString::new(name).unwrap();
272- unsafe { gl::GetUniformLocation(self.gl_id(), cstr_name.as_ptr()) }
273+ fn uniform_location(&mut self, name: &'static str) -> GLint {
274+ match self.cached_uniform_locations.get(name) {
275+ Some(location) => *location,
276+ None => {
277+ let cstr_name = CString::new(name).unwrap();
278+ let location = unsafe { gl::GetUniformLocation(self.gl_id(), cstr_name.as_ptr()) };
279+ self.cached_uniform_locations.insert(name, location);
280+ location
281+ }
282+ }
273283 }
274284 }
275285
@@ -1,4 +1,5 @@1 use std::{
2 ffi::{c_void, CString},
3 fmt::Display,
4 };
@@ -197,6 +198,7 @@197
198 struct Program {
199 id: ProgramId,
200 }
201
202 impl Program {
@@ -222,6 +224,7 @@222 if linking_was_successful {
223 Ok(Self {
224 id: ProgramId(program_id),
225 })
226 } else {
227 let error_message_len: usize = unsafe {
@@ -253,7 +256,7 @@253 self.id.0
254 }
255
256- fn set_uniform_vec3(&mut self, name: &str, value: &Vec3) {
257 let Vec3(x, y, z) = *value;
258
259 unsafe {
@@ -261,15 +264,22 @@261 }
262 }
263
264- fn set_uniform_f32(&mut self, name: &str, value: &f32) {
265 unsafe {
266 gl::Uniform1f(self.uniform_location(name), *value);
267 }
268 }
269
270- fn uniform_location(&self, name: &str) -> GLint {
271- let cstr_name = CString::new(name).unwrap();
272- unsafe { gl::GetUniformLocation(self.gl_id(), cstr_name.as_ptr()) }
273 }
274 }
275
@@ -1,4 +1,5 @@1 use std::{
2+ collections::HashMap,
3 ffi::{c_void, CString},
4 fmt::Display,
5 };
@@ -197,6 +198,7 @@198
199 struct Program {
200 id: ProgramId,
201+ cached_uniform_locations: HashMap<&'static str, GLint>,
202 }
203
204 impl Program {
@@ -222,6 +224,7 @@224 if linking_was_successful {
225 Ok(Self {
226 id: ProgramId(program_id),
227+ cached_uniform_locations: HashMap::new(),
228 })
229 } else {
230 let error_message_len: usize = unsafe {
@@ -253,7 +256,7 @@256 self.id.0
257 }
258
259+ fn set_uniform_vec3(&mut self, name: &'static str, value: &Vec3) {
260 let Vec3(x, y, z) = *value;
261
262 unsafe {
@@ -261,15 +264,22 @@264 }
265 }
266
267+ fn set_uniform_f32(&mut self, name: &'static str, value: &f32) {
268 unsafe {
269 gl::Uniform1f(self.uniform_location(name), *value);
270 }
271 }
272
273+ fn uniform_location(&mut self, name: &'static str) -> GLint {
274+ match self.cached_uniform_locations.get(name) {
275+ Some(location) => *location,
276+ None => {
277+ let cstr_name = CString::new(name).unwrap();
278+ let location = unsafe { gl::GetUniformLocation(self.gl_id(), cstr_name.as_ptr()) };
279+ self.cached_uniform_locations.insert(name, location);
280+ location
281+ }
282+ }
283 }
284 }
285