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 #include "pxr/imaging/hd/renderThread.h"
25 #include "pxr/base/tf/diagnostic.h"
26 
27 PXR_NAMESPACE_OPEN_SCOPE
28 
HdRenderThread()29 HdRenderThread::HdRenderThread()
30     : _renderCallback(_DefaultRenderCallback)
31     , _shutdownCallback(_DefaultShutdownCallback)
32     , _requestedState(StateInitial)
33     , _stopRequested(false)
34     , _pauseRender(false)
35     , _rendering(false)
36 {
37 }
38 
~HdRenderThread()39 HdRenderThread::~HdRenderThread()
40 {
41     StopThread();
42 }
43 
44 void
SetRenderCallback(std::function<void ()> renderCallback)45 HdRenderThread::SetRenderCallback(std::function<void()> renderCallback)
46 {
47     _renderCallback = renderCallback;
48 }
49 
50 void
SetShutdownCallback(std::function<void ()> shutdownCallback)51 HdRenderThread::SetShutdownCallback(std::function<void()> shutdownCallback)
52 {
53     _shutdownCallback = shutdownCallback;
54 }
55 
56 void
StartThread()57 HdRenderThread::StartThread()
58 {
59     if (_renderThread.joinable()) {
60         TF_CODING_ERROR("StartThread() called while render thread is "
61                         "already running");
62         return;
63     }
64 
65     _requestedState = StateIdle;
66     _renderThread = std::thread(&HdRenderThread::_RenderLoop, this);
67 }
68 
69 void
StopThread()70 HdRenderThread::StopThread()
71 {
72     if (!_renderThread.joinable()) {
73         return;
74     }
75 
76     {
77         _enableRender.clear();
78         std::unique_lock<std::mutex> lock(_requestedStateMutex);
79         _requestedState = StateTerminated;
80         _requestedStateCV.notify_one();
81     }
82     _renderThread.join();
83 }
84 
85 bool
IsThreadRunning()86 HdRenderThread::IsThreadRunning()
87 {
88     return _renderThread.joinable();
89 }
90 
91 void
StartRender()92 HdRenderThread::StartRender()
93 {
94     if (!IsRendering()) {
95         std::unique_lock<std::mutex> lock(_requestedStateMutex);
96         _enableRender.test_and_set();
97         _requestedState = StateRendering;
98         _rendering.store(true);
99         _requestedStateCV.notify_one();
100     }
101 }
102 
103 void
StopRender()104 HdRenderThread::StopRender()
105 {
106     if (IsRendering()) {
107         _enableRender.clear();
108         std::unique_lock<std::mutex> lock(_requestedStateMutex);
109         _requestedState = StateIdle;
110         _rendering.store(false);
111     }
112 }
113 
114 bool
IsRendering()115 HdRenderThread::IsRendering()
116 {
117     return _rendering.load();
118 }
119 
120 void
PauseRender()121 HdRenderThread::PauseRender()
122 {
123     _pauseDirty.store(true);
124     _pauseRender.store(true);
125 }
126 
127 void
ResumeRender()128 HdRenderThread::ResumeRender()
129 {
130     _pauseDirty.store(true);
131     _pauseRender.store(false);
132 }
133 
134 bool
IsStopRequested()135 HdRenderThread::IsStopRequested()
136 {
137     if (!_enableRender.test_and_set()) {
138         _stopRequested = true;
139     }
140 
141     return _stopRequested;
142 }
143 
144 bool
IsPauseRequested()145 HdRenderThread::IsPauseRequested()
146 {
147     return _pauseRender.load();
148 }
149 
150 bool
IsPauseDirty()151 HdRenderThread::IsPauseDirty() {
152     return _pauseDirty.exchange(false);
153 }
154 
155 std::unique_lock<std::mutex>
LockFramebuffer()156 HdRenderThread::LockFramebuffer()
157 {
158     return std::unique_lock<std::mutex>(_frameBufferMutex);
159 }
160 
161 void
_RenderLoop()162 HdRenderThread::_RenderLoop()
163 {
164     while (1) {
165         std::unique_lock<std::mutex> lock(_requestedStateMutex);
166         _requestedStateCV.wait(lock, [this]() {
167             return _requestedState != StateIdle;
168         });
169         if (_requestedState == StateRendering) {
170             _renderCallback();
171             _stopRequested = false;
172             _rendering.store(false);
173             _requestedState = StateIdle;
174         }
175         else if (_requestedState == StateTerminated) {
176             break;
177         }
178     }
179     _shutdownCallback();
180 }
181 
182 /*static*/
183 void
_DefaultRenderCallback()184 HdRenderThread::_DefaultRenderCallback()
185 {
186     TF_CODING_ERROR("StartThread() called without a render callback set");
187 }
188 
189 /*static*/
190 void
_DefaultShutdownCallback()191 HdRenderThread::_DefaultShutdownCallback()
192 {
193 }
194 
195 PXR_NAMESPACE_CLOSE_SCOPE
196 
197