Debugging

Last updated 2018/12/18

It's not just programming

Debugging is a term that is usually expected to be used in a programming context but it shouldn't be surprising that people also spend a considerable amount of time debugging their work in a computer graphics environment. From baking artifacts to noise in renderings, we experience many situations where proper debugging skills can be a huge time saver.
You might be tempted to say that this is just another term for problem-solving. However, solving a problem can also mean using a workaround and not tackle the underlying cause altogether. I consider debugging to be specifically about finding (and solving) a problem where it is caused.

Divide and conquer

This a concept that is taught in every computer science 101 course but it applies to many problems in many contexts, even in daily life.
The idea is to reduce the space you have to search to find the problem. You divide a malfunctioning entity into two equal parts and see in which half the problem persists. You pick the part that is causing the issue and divide it again into two parts, looking for the one malfunctioning. You do this over and over again until you find the problem.

It's a bit abstract so let's go over an example. Assume we've got a 3D scene, a complete interior with hundreds of assets, shaders, and lights. When we try to render we see a nonsensical error message popping up and the renderer refuses to calculate the image. That's all we know at this point. We can't possibly check every single object in the scene so we hide half of the scene and check if it starts rendering. If it does, we know that the hidden half must have caused the problem. If it doesn't we know that the problem is caused by the remaining half of objects. So we divide it again and again until we end up with one broken object that our renderer can't process.

Note that this method of repeatedly dividing has an exponential nature. Instead of, let's say, checking 1024 objects one by one we only need 10 attempts to find the right one. Each step we eliminate half of all the possible causes. From 1024 to 512 to 256, 128, 64, 32, 16, 8, 4, 2, 1. You see the pattern here? Exactly. 2^10 = 1024. So for 2048 objects, we need 11 attempts, for 4096 we need 12 attempts and so on. We can easily eliminate hundreds of thousands of objects with a few attempts this way.

Reduce variables first

This is another spin on the notion to not waste time by having too many possible causes present at once. You should generally try to reduce as many variables as you can. A variable is anything that can change and influence the result. So instead of trying to debug a production scene with everything activated, try to reduce it as much as possible first. Get rid of all the shaders, effects, lights, proxies etc. Strip down the scene to the bare minimum and see if the issue at hand persists. If it's gone, go ahead and reactivate one feature at a time and test if the problem still hasn't come back. Repeat it until the problem pops up again.
This way you will always be aware of all the variables that affect the scene. You know exactly what is influencing the result.
If you do it the other way around and start with everything activated, just subtracting one or even several features at a time, you will always have to deal with lots of possible interacting causes at a time which might influence each other in unknown ways. Plus, the scene is much slower to render or interact with.