I am creating a graphics application in Metal. One of the things we are suggested to do is to use triple buffers in order to allow for the GPU and CPU to work more in parallel. Essentially instead of writing frequently changing data to the same spot in memory you alternate between three offsets and then pass in a drawing offset for the GPU that way the GPU is not reading something the CPU is currently trying to write.

One problem with this is that it means that what is drawn on screen will be from 2 frames ago.

So my question is what does this mean for time-stepping? Heaven forbid I have an incredibly variable frame-rate how do I make things seem smooth when the dt value I have for the CPU simulation frame is actually two frames behind?

  • \$\begingroup\$ Why not double buffering? \$\endgroup\$ – Bálint Aug 26 '18 at 16:49
  • \$\begingroup\$ Great question and I would have to go back to the developer video to see why. Truthfully right now I have it as a #define constant so at the end when it comes time to tune performance it might end up being one. Still that will have latency to it. \$\endgroup\$ – J.Doe Aug 26 '18 at 16:51
  • \$\begingroup\$ Triple buffering should make the frame rate more stable than other approaches. CPU does not need to wait as much for the gpu to complete a frame before going to the next. If you are still worried about frame rate just use fixed time stepping: gafferongames.com/post/fix_your_timestep. The delta time value would be calculated and used on the CPU each new frame, it is not related to what the gpu is doing (rendering). There would be no “old” dt. \$\endgroup\$ – schteppe Aug 27 '18 at 6:17
  • \$\begingroup\$ And just in case you haven’t read it already: developer.apple.com/documentation/metal/advanced_command_setup/… \$\endgroup\$ – schteppe Aug 27 '18 at 6:17

What technology you are creating your app in is largely irrelevant.

Time stepping only applies to app state updates. Rendering should be performed on the most up to date data available at rendering time:

In a single thread scenario, updates and rendering are sequential, and therefore frame rate is dependent upon how fast you can get through the update phase.

In a two thread scenario, you offload the update work to a second thread, and that is where your app/game state is updated, or the memory is written to. In your main thread, you just loop through the renderer, only reading the latest game state updates for rendering. In this way, your rendering is never a frame behind.

Double/triple buffering is a graphics hardware technique to prevent screen tearing, by always ensuring that there is a complete frame to render. It is not necessary to allow the cpu and gpu to work in parallel.

I would advise you to learn the fundamentals and best practices of multithreaded programming, and do some research on multithreaded game engine design.

  • \$\begingroup\$ Where do I start on that last suggestion? \$\endgroup\$ – J.Doe Aug 27 '18 at 8:50
  • \$\begingroup\$ software.intel.com/en-us/articles/… \$\endgroup\$ – Ian Young Aug 27 '18 at 8:51
  • \$\begingroup\$ @IanYoung: J.Doe is not talking about double/triple buffering to avoid screen tearing. I believe J is talking about double/triple buffering dynamic meshes so that the update loop isn't forced to wait for the GPU to be done with a buffer before the CPU updates it: developer.apple.com/library/archive/documentation/3DDrawing/… \$\endgroup\$ – Jibb Smart Aug 27 '18 at 9:36
  • \$\begingroup\$ @JibbSmart It sounds like you're talking about buffering animation frame, which seems sensible, if doing mesh updates on the CPU. A far better way is to reserve a space for "current frame" and let the GPU do it in a compute/geometry shader, though not necessarily simpler. \$\endgroup\$ – Ian Young Aug 27 '18 at 10:04

It's worth noting that even without double/triple buffering, if the framerate jumps from 30 to 60, for example, the first 60fps frame would've been calculated with the previous frame's time-step -- 33.333ms instead of 16.667ms. So even if you're not double/triple buffering your dynamic buffers for animation or what not, one still faces the problem of highly variable framerates making the entire simulation more jittery than the actual framerate.

Using a smoothed time-step can help. You just keep a rolling average of several frames' time-step. When the framerate is constant, it makes no difference. When the framerate varies widely and wildly, the average will still mean you're simulating and animating at the correct rate on a macro level, but you'll avoid having your smallest time-steps used in your longest frames (and vice versa).


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.