0
\$\begingroup\$

I am developing an interactive visualizer for a project using Pangolin and OpenGL. The idea is as follows.

There are two threads:

1. Render Thread 2. Process Thread
- acquire lock and swap dataNew with dataDisplay, release lock
- render dataDisplay on the window
- read data
- process data
- acquire lock, copy data to dataNew buffer, release lock

Sample code:

// Render loop
while (!pangolin::ShouldQuit()) {
  if (mDataReadDone) break;

  // ....some code.....

  /*Critical section*/
  // Can acquire lock only after mDataNew 
  // is updated with new data in the process thread
  {
      std::unique_lock<std::mutex> lock(mRenderMutex);
      std::swap(mDataDisplay, mDataNew);
  } /*Critical section*/

  // .....Render mDataDisplay data.........
}

// data process loop
while (!mWindowClosed) {
  // .....Read data....

  // .....Process data....

  // ....Copy data.....
  /*Critical section*/
  {
      std::unique_lock<std::mutex> lock(mRenderMutex);
      copyDataFromPipeline(mDataNew);
  }

}

Question: In the render loop, for the std::swap() to happen, it has to wait till new data is filled in mDataNew buffer by the process thread and hence the render loop is blocked for some time. Can the render loop be blocked for some time by design in general?

Constraint - In my case, I have a pause option and also a time travel option to go back in frames to debug a particular frame if something is wrong there, so I cannot miss any frames for rendering

\$\endgroup\$
4
  • \$\begingroup\$ If you cannot miss any frame for rendering, maybe you don't need a multithread approach, given that your software runs at the max rate w.r.t. your display device... \$\endgroup\$
    – Vaillancourt
    Jul 2, 2023 at 20:39
  • \$\begingroup\$ True, But I wan to parallelise the data processing part in the process thread \$\endgroup\$
    – Nitron_707
    Jul 2, 2023 at 21:04
  • \$\begingroup\$ FWIW, I'd like to point out that typically, the rendering must be done on the main thread. \$\endgroup\$
    – Vaillancourt
    Jul 3, 2023 at 13:55
  • 1
    \$\begingroup\$ @Vaillancourt, yes rendering is happening in the main thread \$\endgroup\$
    – Nitron_707
    Jul 3, 2023 at 15:06

1 Answer 1

0
\$\begingroup\$

You can do this in a lock-free way if you set aside a third data buffer (triple buffering).

Call them:

  • next to write

  • most recently written

  • currently rendering

and store a pointer to each.

When the process thread finishes writing freshly-processed data into next to write, it performs an atomic swap of the pointers to next to write and most recently written. So when the render thread looks for the latest data, it finds it via the most recently written pointer, and the process thread is free to write new data into the next to write buffer, knowing that no one else is relying on its contents.

When the rendering thread finishes rendering a frame and needs the next one, it performs an atomic swap of the pointers to most recently written and currently rendering. That keeps the data being currently used for rendering out of reach of the process thread, and releases the previously rendered data to be overwritten.

Because these two swaps are done atomically, they happen all at once - one fully after the other, never overlapping or interleaved. So you don't have to worry about one of the pointers getting duplicated/misplaced due to a race condition.

This avoids locking for a full data copy at any stage. You can see some more discussion of the implementation of such atomic pointer swaps here.

If the process thread is fast, it can process multiple data frames between renders, and the render thread will automatically pick up the latest and greatest data as fast as it can. If your process thread sometimes runs slower than your render thread, you'll want to put the render thread to sleep until the process thread finishes the next swap and wakes it - otherwise out of the box it will flip back and forth re-rendering the two most recent frames until the process thread finishes.

\$\endgroup\$
6
  • \$\begingroup\$ So for this to work, should I spawn the process thread just before the render while loop ? Because If there is a delay between the time of spawn of process thread and the start the render loop, we will miss staring few data points to render right ? \$\endgroup\$
    – Nitron_707
    Jul 2, 2023 at 17:31
  • \$\begingroup\$ With a scheme like this, you usually don't worry about missed data frames. As long as you're rendering the latest fully-processed data as fast as you can, you maintain the illusion of realtime continuous animation. \$\endgroup\$
    – DMGregory
    Jul 2, 2023 at 17:41
  • \$\begingroup\$ In my case, I have a pause option and also a time travel option to go back in frames to debug a particular frame if something is wrong there, so if I miss frames in the start, then this scheme will not work in this case \$\endgroup\$
    – Nitron_707
    Jul 2, 2023 at 17:46
  • 1
    \$\begingroup\$ @Nitron_707 Fully stating your requirements in your questions help get more accurate answers. If you have constraints, better include them in the question so answers can address them. \$\endgroup\$
    – Vaillancourt
    Jul 2, 2023 at 17:50
  • \$\begingroup\$ Nothing prevents you from forcing the render thread to sleep until the next data is ready to render, or forcing the processing thread to sleep until the old data has been swapped for rendering before performing its next swap to begin overwriting. This just is not a typical behaviour in games, where usually we want both the sim and rendering to race ahead as fast as the hardware permits. Ask on the GameDev SE, expect game-style answers by default. 😉 \$\endgroup\$
    – DMGregory
    Jul 2, 2023 at 17:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .