# Should calculations be done on the CPU or GPU?

I'm currently learning OpenGL and it's become obvious that I can do most calculations on the CPU or the GPU. For example, I can do something like lightColor * objectColor on the CPU, then send it to the GPU via a GLSL uniform. On the contrary, I can send them separately and have GLSL perform the calculation.

Is there a best practice on where graphic calculations should be performed? My thoughts are that the GPU might be optimized for matrix or vector calculations. However, I'm also thinking that the CPU might be a better place for it since the GPU might have better things to do.

A few rules of thumb around this, if the calculation only needs to be done once and applies to the entire object, then you will get best bang for buck calculating it prior to loading onto the GPU. IF though the calculation does change then you also need to consider where in the GPU pipeline it should be calculated. For instance, if you can calculate the value prior to pixel shader, say in the vertex shader then this in itself is an important optimisation.

Where you can relieve pressure on the most heavily used parts of the GPU pipeline also is also useful to consider. You have the right thought process on questioning where and when.

For Matrices, in my experience, sometimes you need to pass the individual Rotation/Translation matrices into the GPU rather than premult them (if you are using instancing). But you can also use tricks like deriving the rotation component by casting your 4x4 to a 3x3 (this is very helpful when you are calculating face normals).

But questioning everything is the best way to improve.

The CPU could perform the calculations in a faster rate than a GPU due to higher clock frequency. However, the GPU can do a lot more calculations in parallel. If you are working on a large data set, where you operate on each item in the same way, the GPU can be a lot more efficient, however it requires some setup, like uploading data to the GPU, executing a shader, maybe downloading data from GPU.

In a game rendering example, you will usually precompute what you can on the CPU, like multiplying view and projection matrices, so the vertex shader can do only one matrix multiply of vertexpos * viewProjectionMatrix instead of two. But you wouldn't want to precompute the matrix transform for each vertex on the CPU, that would be more efficient in the GPU vertex shader.

In your example, lightColor*objectColor makes more sense to do in the pixel shader if you are doing per pixel lighting, or in the vertex shader if you are very old school. :)

• I should add that you should always try to minimize the time the program takes to run, if you have a small amount of calculations to be done, latency will win (the CPU is faster than the GPU), but if the amount of calculations is huge, throughput will win (the GPU has many more cores than the CPU). – Felipe Gutierrez Oct 7 '19 at 21:46
• Can back this up with Compute shader, I did a test calculation a grid space in the CPU, took about 170ms to crunch the data. To set up in compute, copy to GPU and back, it was about 300ms due to having to stream so much data into a resource then write it to GPU. The actual compute was < 1ms to crunch on the GPU. That was just a test to sum all the normals at a shared point in the grid and normalise them, then write back to shared vertices. GPU was blindingly fast, and if I disregard the creation and copy of the buffer, it wins hands down. – ErnieDingo Nov 15 '19 at 0:19
• Yep, that's a common problem. There are some architectures with UMA (Unified Memory Architecture), where the memory is shared between CPU and GPU and you don't need to copy, just flush some caches. – János Turánszki Nov 17 '19 at 21:14

If the data is not expected to change between gpu threads, then it should be performed on the CPU, and sent to the shader via a uniform. Examples of this include:

world to screen transform (sometimes called MVP transform). It is computed by multiplying the model (or world) matrix by the view matrix and then the result by the projection matrix like so:

mat4 mvp = projection * view * model;


It would then be sent the shader as a uniform mat4.

Normal matrix, eye position and light data all fall into this category.

You can do mvp computation on the GPU, but it is wasteful.

For each data, ask yourself: Can I compute this once and use many times? If the answer is yes, do it on the cpu, and set it as a uniform.

If you expect the data to vary between vertices or fragments, then compute it on the GPU.