HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
renderThread.h
Go to the documentation of this file.
1 //
2 // Copyright 2018 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_IMAGING_HD_RENDER_THREAD_H
8 #define PXR_IMAGING_HD_RENDER_THREAD_H
9 
10 #include "pxr/pxr.h"
11 #include "pxr/imaging/hd/api.h"
12 
13 #include <atomic>
14 #include <condition_variable>
15 #include <functional>
16 #include <mutex>
17 #include <thread>
18 
20 
21 
22 /// \class HdRenderThread
23 ///
24 /// HdRenderThread is a utility that specific render delegates can choose to
25 /// use depending on their needs. It provides a system for rendering in a
26 /// background thread, and synchronizing between hydra (either in the main
27 /// thread, or the sync threadpool) and the rendering thread.
28 ///
29 /// \section HdRenderThread_StateMachine State Machine
30 ///
31 /// The render thread is implemented in terms of a state machine, and hydra
32 /// requests to the render thread are implemented in terms of transitions on
33 /// that state machine.
34 ///
35 /// States:
36 /// - \em StateInitial - indicates the render thread hasn't been started.
37 /// - \em StateIdle - indicates the render thread is running, but not rendering.
38 /// - \em StateRendering - indicates the render thread is rendering.
39 /// - \em StateTerminated - indicates the render thread is shutting down.
40 ///
41 /// Transitions:
42 /// - StartThread(): StateInitial => StateIdle
43 /// - StartRender(): StateIdle, StateRendering => StateRendering
44 /// - StopRender(): StateIdle, StateRendering => StateIdle
45 /// - StopThread(): StateIdle, StateRendering => StateTerminated
46 /// - StopThread(): StateTerminated => StateInitial
47 ///
48 /// \section HdRenderThread_Usage Example Usage
49 ///
50 /// \code{.cpp}
51 /// class ExampleRenderDelegate : HdRenderDelegate {
52 /// public:
53 /// ExampleRenderDelegate() {
54 /// _renderThread.SetRenderCallback(
55 /// std::bind(&ExampleRenderDelegate::_RenderCallback, this));
56 /// _renderThread.StartThread();
57 /// }
58 /// ~ExampleRenderDelegate() {
59 /// _renderThread.StopThread();
60 /// }
61 /// private:
62 /// void _RenderCallback() {
63 /// bool renderComplete = false;
64 /// while(!renderComplete) {
65 /// // Check if we have been asked to pause.
66 /// while(_renderThread.IsPauseRequested()) {
67 /// if(_renderThread.IsStopRequested()) {
68 /// break;
69 /// }
70 /// std::this_thread::sleep_for(std::chrono::milliseconds(10));
71 /// }
72 /// if(_renderThread.IsStopRequested()) {
73 /// break;
74 /// }
75 /// // generate N pixels.
76 /// auto lock = _renderThread.LockFramebuffer();
77 /// // resolve pixels to shared buffer.
78 /// // Set renderComplete = true when finished rendering.
79 /// }
80 /// }
81 /// HdRenderThread _renderThread;
82 /// };
83 ///
84 /// class ExampleRenderParam : HdRenderParam {
85 /// public:
86 /// ExampleRenderParam(HdRenderThread* renderThread, SceneData *scene);
87 /// SceneData* AcquireSceneForEdit() {
88 /// _renderThread->StopRender();
89 /// return _scene;
90 /// }
91 /// };
92 ///
93 /// class ExamplePrim : HdMesh {
94 /// public:
95 /// void Sync(...) {
96 /// SceneData *scene = renderParam->AcquireSceneForEdit();
97 /// ...
98 /// }
99 /// };
100 ///
101 /// class ExampleRenderPass : HdRenderPass {
102 /// public:
103 /// ExampleRenderPass(HdRenderThread *renderThread);
104 /// protected:
105 /// void _Execute(...) {
106 /// _renderThread->StartRendering();
107 /// auto lock = _renderThread->LockFramebuffer();
108 /// // blit pixels from shared to application buffer.
109 /// }
110 /// };
111 /// \endcode
112 ///
113 /// Having a locked and shared framebuffer is important if you want to avoid
114 /// tearing, or if the rendering API disallows multithreaded access to buffers
115 /// (for example, if your framebuffers are on a GPU). It might be unnecessary
116 /// for some renderers.
117 ///
118 /// Stopping the render only when you're about to make a scene edit means that
119 /// long-running renders aren't interrupted if the scene is static. Hiding the
120 /// renderer's scene data handle behind AcquireSceneForEdit helps callers
121 /// use the synchronization mechanisms correctly.
122 ///
123 /// The render is restarted at the last possible second, in the render pass,
124 /// after we know scene edits are done.
125 ///
126 /// The render callback should use IsStopRequested() as a cancellation
127 /// mechanism.
128 ///
130 public:
131 
132  HD_API
133  HdRenderThread();
134 
135  HD_API
136  ~HdRenderThread();
137 
138  /// \anchor Management
139  /// \name API for thread management
140  ///
141  /// Methods to configure, start, and stop the render thread. These functions
142  /// are not threadsafe.
143  ///
144  /// @{
145 
146  /// Set the rendering callback for the render thread to use.
147  HD_API
148  void SetRenderCallback(std::function<void()> renderCallback);
149 
150  /// Set the shutdown callback for the render thread to use. This will be
151  /// called once, right before the render thread exits, regardless of whether
152  /// the render callback has been called. This can be used to clean up
153  /// thread-specific rendering resources.
154  HD_API
155  void SetShutdownCallback(std::function<void()> shutdownCallback);
156 
157  /// Start the rendering background thread.
158  /// Note: it's an error to call this function when the render thread is
159  /// already running, but it's acceptable to stop the render thread and then
160  /// start it again.
161  HD_API
162  void StartThread();
163 
164  /// Stop the rendering background thread. This function will ask the render
165  /// thread to transition to StateTerminated, and then join on the thread,
166  /// so it will block. After this function returns, the rendering state
167  /// machine will be back in its initial state, and the render thread can be
168  /// started again.
169  HD_API
170  void StopThread();
171 
172  /// Check whether the background thread is running (i.e. StartThread was
173  /// called successfully, but StopThread has not been).
174  HD_API
175  bool IsThreadRunning();
176 
177  /// @}
178 
179  /// \anchor HydraAPI
180  /// \name API for hydra threads
181  ///
182  /// Methods for hydra to communicate with the render thread. These methods
183  /// can be called from the application thread or hydra threadpool threads.
184  ///
185  /// @{
186 
187  /// Ask the render thread to start rendering. This call is a no-op if the
188  /// render thread is already rendering. Otherwise, it may block briefly.
189  /// This is threadsafe against the render thread, but it shouldn't be
190  /// called at the same time as StopRender(), and it shouldn't be called
191  /// from multiple hydra threads at once.
192  HD_API
193  void StartRender();
194 
195  /// Ask the render thread to stop rendering, and block until the render
196  /// thread is idle. This is fully threadsafe, and can be called from
197  /// multiple hydra threads at once.
198  HD_API
199  void StopRender();
200 
201  /// Query whether the render thread is currently rendering. This is set by
202  /// StartRender() and reset after the render callback exits, or reset by
203  /// StopRender() if the render callback never runs. This does not
204  /// block, and is fully threadsafe.
205  HD_API
206  bool IsRendering();
207 
208  /// Ask the render thread to pause rendering. The speed at which the
209  /// renderer actually enters the pause state depends on the delegate.
210  HD_API
211  void PauseRender();
212 
213  /// Ask the render thread to resume rendering. Pause and Resume calls do
214  /// not need to be paired. The last call (to Pause or Resume) decides the
215  /// current state.
216  HD_API
217  void ResumeRender();
218 
219  /// @}
220 
221  /// \anchor RenderThreadAPI
222  /// \name API for hydra threads
223  ///
224  /// Methods for the render thread to communicate with hydra. These should
225  /// only be called from the render thread, from inside the render callback.
226  ///
227  /// @{
228 
229  /// Query whether hydra has asked to interrupt the current frame since
230  /// the last time StartRender() was called. The render callback can check
231  /// this to determine whether to cancel rendering.
232  HD_API
233  bool IsStopRequested();
234 
235  /// Query whether hydra has asked to pause rendering. This will continue
236  /// to return true until a request has been made for rendering to resume.
237  /// Remember to check for a stop request while paused.
238  HD_API
239  bool IsPauseRequested();
240 
241  /// Query whether the pause/resume state has changed since the last time
242  /// we called IsPauseDirty.
243  HD_API
244  bool IsPauseDirty();
245 
246  /// @}
247 
248  /// \anchor CommonAPI
249  /// \name API for both hydra and render threads
250  ///
251  /// Methods for both hydra and the render threads to synchronize access to
252  /// other data.
253  ///
254  /// @{
255 
256  /// Return a scoped lock on the render delegate's framebuffer. Hydra and the
257  /// render thread can use this to synchronize blits between render-thread
258  /// owned resources, and application-owned resources.
259  HD_API
260  std::unique_lock<std::mutex> LockFramebuffer();
261 
262  /// @}
263 
264 private:
265  // _RenderLoop implements the render thread's state machine; see
266  // \ref HdRenderThread_StateMachine for details. It runs in a background
267  // thread and manages synchronization with hydra. To implement rendering,
268  // it calls out to the render callback provided via SetRenderCallback.
269  void _RenderLoop();
270 
271  // _renderCallback is the render-delegate-provided function responsible for
272  // actually rendering. It's called from _RenderLoop.
273  std::function<void()> _renderCallback;
274 
275  // _shutdownCallback is the render-delegate-provided function responsible
276  // for cleaning up thread-specific resources. It's called once, right
277  // before _RenderLoop exits.
278  std::function<void()> _shutdownCallback;
279 
280  // A placeholder initial value for _renderCallback.
281  static void _DefaultRenderCallback();
282 
283  // A placeholder initial value for _shutdownCallback.
284  static void _DefaultShutdownCallback();
285 
286  // The state enumeration of the render thread state machine; see
287  // \ref HdRenderThread_StateMachine for details.
288  enum State {
289  // Initial constructed state. Render thread is not running.
290  StateInitial,
291  // Render thread is running and ready for scene edits. No rendering
292  // is taking place.
293  StateIdle,
294  // Render thread is running and rendering; no scene edits are allowed.
295  StateRendering,
296  // Render thread is shutting down.
297  StateTerminated,
298  };
299 
300  // _requestedState is set by hydra to direct the render thread's state
301  // machine; see \ref HdRenderThread_StateMachine for details.
302  // _requestedState is protected by a mutex/condition variable combination.
303  // The render thread holds _requestedStateMutex while rendering; the
304  // frequency with which it can give it up is the interruption frequency.
305  //
306  // StartRender() and StopRender() lock and write to _requestedState.
307  // _RenderLoop() locks and reads _requestedState.
308  State _requestedState;
309  std::mutex _requestedStateMutex;
310  std::condition_variable _requestedStateCV;
311 
312  // _enableRender provides an out-of-band way for hydra to cancel a
313  // render while the render thread is still holding _requestedStateMutex.
314  //
315  // StartRender() and StopRender() will write true/false to _enableRender.
316  // IsStopRequested() will read from _enableRender.
317  std::atomic_flag _enableRender;
318  // _stopRequested keeps track of whether _enableRender has gone low since
319  // the last time the render callback was called (since _enableRender is
320  // reset on read).
321  bool _stopRequested;
322 
323  // _pauseRender provides a properly locked boolean flag that holds the
324  // current pause state of the thread. Toggled by calling PauseRender and
325  // ResumeRender, tested by IsPauseRequested.
326  std::atomic<bool> _pauseRender;
327 
328  // _pauseDirty provides a properly locked boolean flag that holds the
329  // current dirtyness of the pause state of the thread. Toggled by
330  // calling PauseRender and ResumeRender, tested by IsPauseDirty.
331  std::atomic<bool> _pauseDirty;
332 
333  // _rendering records whether the render thread is currently inside the
334  // render callback, or planning to be inside the render callback.
335  // It is managed by StartRender(), StopRender(), and _RenderLoop().
336  // IsRendering() will read from _rendering.
337  std::atomic<bool> _rendering;
338 
339  // _renderThread is the background render thread; it runs _RenderLoop().
340  std::thread _renderThread;
341 
342  // _frameBufferMutex protects access to the render delegate's framebuffer,
343  // and provides an optional synchronization point for blits between the
344  // render thread's resources and the application's resources.
345  std::mutex _frameBufferMutex;
346 };
347 
348 
350 
351 #endif // PXR_IMAGING_HD_RENDER_THREAD_H
HD_API bool IsPauseDirty()
HD_API ~HdRenderThread()
HD_API void ResumeRender()
HD_API bool IsThreadRunning()
#define HD_API
Definition: api.h:23
HD_API void PauseRender()
HD_API void StartThread()
HD_API void StopRender()
HD_API void StartRender()
HD_API bool IsRendering()
HD_API void StopThread()
HD_API std::unique_lock< std::mutex > LockFramebuffer()
HD_API void SetShutdownCallback(std::function< void()> shutdownCallback)
**Note that the tasks the is the thread number *for the or if it s being executed by a non pool thread(this *can happen in cases where the whole pool is occupied and the calling *thread contributes to running the work load).**Thread pool.Have fun
HD_API bool IsStopRequested()
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
HD_API HdRenderThread()
HD_API bool IsPauseRequested()
HD_API void SetRenderCallback(std::function< void()> renderCallback)
Set the rendering callback for the render thread to use.