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 
36 PXR_NAMESPACE_OPEN_SCOPE
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 ///
146 class HdRenderThread {
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 
366 PXR_NAMESPACE_CLOSE_SCOPE
367 
368 #endif // PXR_IMAGING_HD_RENDER_THREAD_H
369