4
\$\begingroup\$

Working on optimizing the rendering core of my game engine and I have a couple of questions.

Is it more efficient to transform vertices to world space on the CPU before you send then stream to the GPU, or is it better to do this on the GPU? If done on the GPU this means I have to send the world matrix to the GPU for every different mesh however the GPU is highly optimized to deal with these calculations. It seems to me that doing it on the CPU would save some time, however, because I can transform the vertices and send over world positions all at one time.

\$\endgroup\$
  • 6
    \$\begingroup\$ Sounds like you are trying to optimize something that you have not yet tested to be a bottleneck. Try first, optimize later. \$\endgroup\$ – Lasse Jan 7 '15 at 2:59
  • \$\begingroup\$ I'm doing some stress testing right now. Finally got a batching system working that organizes by render context and then organizes those sections by material. Just wondering if I can decrease MS per Frame any more even if it isn't a bottle neck just because I'm curious and love figuring out how to optimize programs. It's like a puzzle. Thanks for the input! \$\endgroup\$ – user3355098 Jan 7 '15 at 7:45
6
\$\begingroup\$

In short - it is better to do the transformation on the GPU.

Firstly, the GPU is designed to support huge amounts of parallelisation. Your CPU on the other hand is not nearly as capable. The NVIDIA GTX 980, for example, has 2048 CUDA cores to process those vertices with in comparison to the 2-16 threads/cores a processor might support.

So from a number crunching point of view alone, the GPU is going to be better.

However, there is a far more obvious reason to do this on the GPU. While passing data data across the bus to the GPU might be expensive, it's not so expensive that passing a world, view (worldview) and projection matrix for every mesh is going to be a concern. However, passing all meshes every frame will be, and that's what you need to do if you process the vertices CPU-side.

In the normal pipeline you generate a vertex/index buffer, pass it to the GPU and render. At this point the GPU has stored the data in it's own internal memory. You then only update when something in that mesh changes. Usually this is only for dynamic buffers, such as might be used for a particle or sprite batching system. Most objects should be in static buffers which are only passed once - when the object is loaded for the first time.

If you are then manipulating that mesh each frame to change it's position then you are saving on passing the matrix by working CPU-side but now have to re-send all of the vertex data instead.

If your object is static and so will always use the same transform matrix you still get no saving by doing the transform CPU-side as you still probably have a moving camera. You will, in a normal render pipeline, eventually need the vertices in camera space so you need to perform this transformation as well and quite often people combine the world matrix with the view matrix so there's a single object to camera space transform matrix - often called the worldview matrix.

Note on Geometry Batches

One of the only occasions I can think of where you would want to perform this transformation CPU-side is when batching multiple meshes into a single buffer as might be done for static geometry (that shares materials), particles or sprites.

In this situation what you would really be doing, however, is translating the vertices into a common space for the buffer. Having one local reference space for each buffer means no extra vertex data is required to determine how to later translate that vertex to screen-space.

In other words, if you batched two buildings into a single buffer but didn't translate the vertex positions to a common space you would have to pass two world matrices and flag each vertex to determine which to use on each vertex. This can actually be achieved by using two separate index buffers (one for each building) and rendering in two draw calls with different world matrices but with the same vertex buffer in place.

Usually the common space is simply world-space, but could equally be camera space or some other local space if that was convenient for some reason.

Even in this case, however, you probably do this as little as possible and perform the translation to camera-space, then screen-space, using the GPU each frame.

\$\endgroup\$
  • \$\begingroup\$ You can use a single vertex buffer to store two buildings without translating them in non-common space. You just have to render them with separate render calls then, using different indices. This can be used for optimizing out buffer bindings. This is useful with instanced rendering where you want to draw a single mesh many times in different positions. \$\endgroup\$ – Lasse Jan 7 '15 at 15:42
  • \$\begingroup\$ @Lasse - I have incorporated that information into the answer. Thanks. \$\endgroup\$ – OriginalDaemon Jan 7 '15 at 16:07
  • \$\begingroup\$ Ah thanks. That makes a lot of sense. So I can keep one large buffer for static geometry then (in same space) and separate buffers for my dynamic objects, only updating as they change. In which case the only thing I will need to update and change on a frame by frame basis would be the viewproj matrix. \$\endgroup\$ – user3355098 Jan 8 '15 at 3:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.