# Optimizing a deferred renderer

I've been creating a game engine with Kotlin and LWJGL. I wanted to render scenes that support many light sources, so I worked on implementing a deferred renderer.

My basic pipeline is as follows (just point lights for now):

1. Render scene to GBuffer [2ms]
3. Fill an UBO with data (color, strength, matrix, ...) for all the lights [0ms]
4. Draw light volume spheres (instanced) [25ms]:
if (numActiveLights > 0) {
OpenGL.enable(GL_CULL_FACE)
OpenGL.cullFace(GL_FRONT)
OpenGL.enable(GL_DEPTH_TEST)
OpenGL.depthFunc(GL_GEQUAL)

Primitives.unitSphere.renderInstanced(numActiveLights)
}

1. Post processing (AO, FXAA etc.) [1-2ms]

I've added the per-frame GPU times of each step in the pipeline, and I'm sure the problem is fairly obvious. Even at just 95 lights, this call takes upwards of 25ms, which ruins the framerate.
Since the problem also occurs (15+ ms) with just an extremely simple shader (FragColor = vec4(1,1,1,1), no lighting calculation / texture accesses), I think the problem is not with the lighting calculations themselves. I suspect it is because of the large amounts of overdraw.

I've seen deferred renderers handle thousands of lights, so clearly I'm doing something wrong. How can I go about optimizing this?

Edit: About the sphere, I'm generating triangles for a sphere with a resolution of 20 vertices using this bit of code (did not write it myself). The sphere is centered at [0,0,0] and has a radius of 1 (I called that a "unit sphere"). The lighting shader later has a matrix to make the actual light volume out of that.

The sphere mesh is loaded to a simple VAO with one GL_FLOAT VBO attached to it for the vertices. renderInstanced() then just draws them instanced like this:

fun renderInstanced(amount: Int) {
glBindVertexArray(vao)
// type: GL_TRIANGLES
// vertexCount: Class attribute that contains number of verts
glDrawArraysInstanced(type, 0, vertexCount, amount)
}

• What does Primitives.unitSphere.renderInstanced do? Your problem is obviously here but it's impossible to know unless you provide some moer detail. I don't think it's necessary for you to give all your code at the moment, just a paragraph explaining what kind of sphere you draw, how you draw it, etc should be sufficient to prompt further investigation. Oct 1 '21 at 9:14
• @MaximusMinimus I've added an edit to explain how I draw and use the sphere, I hope this clarifies a bit more Oct 1 '21 at 10:04
• That looks fine. I've personally written the exact same process a number of times, and I've tended to use icosahedrons rather than spheres, even going so far as to generate them in a geometry shader, but for 20 vertices it shouldn't matter much for performance, if at all. I typically get performance in the region of 1 to 2ms for a similar number of lights to what you have. Oct 1 '21 at 14:51
• I suspect that you might be bottlenecking on your UBO update now; perhaps you're incurring a pipeline stall. Oct 1 '21 at 14:52
• @MaximusMinimus I'm actually having the Light Upload to the UBO as a separate profiler section, and it only takes a fraction of a millisecond. I'm also only updating the UBO when the lights change. Oct 1 '21 at 15:16

Many hours of debugging later, and while looking at an unrelated problem, I noticed that for whatever reason, I configured all my G-buffers with RGBA_32F precision, which with all textures resulted in 48 bytes per pixel that were fetched by multiple shaders, multiple times per frame.