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