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