1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
18
19 #include "RenderEngineThreaded.h"
20
21 #include <sched.h>
22 #include <chrono>
23 #include <future>
24
25 #include <android-base/stringprintf.h>
26 #include <common/trace.h>
27 #include <private/gui/SyncFeatures.h>
28 #include <processgroup/processgroup.h>
29
30 using namespace std::chrono_literals;
31
32 namespace android {
33 namespace renderengine {
34 namespace threaded {
35
create(CreateInstanceFactory factory)36 std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
37 return std::make_unique<RenderEngineThreaded>(std::move(factory));
38 }
39
RenderEngineThreaded(CreateInstanceFactory factory)40 RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory)
41 : RenderEngine(Threaded::YES) {
42 SFTRACE_CALL();
43
44 std::lock_guard lockThread(mThreadMutex);
45 mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
46 }
47
~RenderEngineThreaded()48 RenderEngineThreaded::~RenderEngineThreaded() {
49 mRunning = false;
50 mCondition.notify_one();
51
52 if (mThread.joinable()) {
53 mThread.join();
54 }
55 }
56
setSchedFifo(bool enabled)57 status_t RenderEngineThreaded::setSchedFifo(bool enabled) {
58 static constexpr int kFifoPriority = 2;
59 static constexpr int kOtherPriority = 0;
60
61 struct sched_param param = {0};
62 int sched_policy;
63 if (enabled) {
64 sched_policy = SCHED_FIFO;
65 param.sched_priority = kFifoPriority;
66 } else {
67 sched_policy = SCHED_OTHER;
68 param.sched_priority = kOtherPriority;
69 }
70
71 if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
72 return -errno;
73 }
74 return NO_ERROR;
75 }
76
77 // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
threadMain(CreateInstanceFactory factory)78 void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
79 SFTRACE_CALL();
80
81 if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) {
82 ALOGW("Failed to set render-engine task profile!");
83 }
84
85 if (setSchedFifo(true) != NO_ERROR) {
86 ALOGW("Couldn't set SCHED_FIFO");
87 }
88
89 mRenderEngine = factory();
90
91 pthread_setname_np(pthread_self(), mThreadName);
92
93 {
94 std::scoped_lock lock(mInitializedMutex);
95 mIsInitialized = true;
96 }
97 mInitializedCondition.notify_all();
98
99 while (mRunning) {
100 const auto getNextTask = [this]() -> std::optional<Work> {
101 std::scoped_lock lock(mThreadMutex);
102 if (!mFunctionCalls.empty()) {
103 Work task = mFunctionCalls.front();
104 mFunctionCalls.pop();
105 return std::make_optional<Work>(task);
106 }
107 return std::nullopt;
108 };
109
110 const auto task = getNextTask();
111
112 if (task) {
113 (*task)(*mRenderEngine);
114 }
115
116 std::unique_lock<std::mutex> lock(mThreadMutex);
117 mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
118 return !mRunning || !mFunctionCalls.empty();
119 });
120 }
121
122 // we must release the RenderEngine on the thread that created it
123 mRenderEngine.reset();
124 }
125
waitUntilInitialized() const126 void RenderEngineThreaded::waitUntilInitialized() const {
127 if (!mIsInitialized) {
128 std::unique_lock<std::mutex> lock(mInitializedMutex);
129 mInitializedCondition.wait(lock, [this] { return mIsInitialized.load(); });
130 }
131 }
132
primeCache(PrimeCacheConfig config)133 std::future<void> RenderEngineThreaded::primeCache(PrimeCacheConfig config) {
134 const auto resultPromise = std::make_shared<std::promise<void>>();
135 std::future<void> resultFuture = resultPromise->get_future();
136 SFTRACE_CALL();
137 // This function is designed so it can run asynchronously, so we do not need to wait
138 // for the futures.
139 {
140 std::lock_guard lock(mThreadMutex);
141 mFunctionCalls.push([resultPromise, config](renderengine::RenderEngine& instance) {
142 SFTRACE_NAME("REThreaded::primeCache");
143 if (setSchedFifo(false) != NO_ERROR) {
144 ALOGW("Couldn't set SCHED_OTHER for primeCache");
145 }
146
147 instance.primeCache(config);
148 resultPromise->set_value();
149
150 if (setSchedFifo(true) != NO_ERROR) {
151 ALOGW("Couldn't set SCHED_FIFO for primeCache");
152 }
153 });
154 }
155 mCondition.notify_one();
156
157 return resultFuture;
158 }
159
dump(std::string & result)160 void RenderEngineThreaded::dump(std::string& result) {
161 std::promise<std::string> resultPromise;
162 std::future<std::string> resultFuture = resultPromise.get_future();
163 {
164 std::lock_guard lock(mThreadMutex);
165 mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
166 SFTRACE_NAME("REThreaded::dump");
167 std::string localResult = result;
168 instance.dump(localResult);
169 resultPromise.set_value(std::move(localResult));
170 });
171 }
172 mCondition.notify_one();
173 // Note: This is an rvalue.
174 result.assign(resultFuture.get());
175 }
176
mapExternalTextureBuffer(const sp<GraphicBuffer> & buffer,bool isRenderable)177 void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
178 bool isRenderable) {
179 SFTRACE_CALL();
180 // This function is designed so it can run asynchronously, so we do not need to wait
181 // for the futures.
182 {
183 std::lock_guard lock(mThreadMutex);
184 mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
185 SFTRACE_NAME("REThreaded::mapExternalTextureBuffer");
186 instance.mapExternalTextureBuffer(buffer, isRenderable);
187 });
188 }
189 mCondition.notify_one();
190 }
191
unmapExternalTextureBuffer(sp<GraphicBuffer> && buffer)192 void RenderEngineThreaded::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
193 SFTRACE_CALL();
194 // This function is designed so it can run asynchronously, so we do not need to wait
195 // for the futures.
196 {
197 std::lock_guard lock(mThreadMutex);
198 mFunctionCalls.push(
199 [=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable {
200 SFTRACE_NAME("REThreaded::unmapExternalTextureBuffer");
201 instance.unmapExternalTextureBuffer(std::move(buffer));
202 });
203 }
204 mCondition.notify_one();
205 }
206
getMaxTextureSize() const207 size_t RenderEngineThreaded::getMaxTextureSize() const {
208 waitUntilInitialized();
209 return mRenderEngine->getMaxTextureSize();
210 }
211
getMaxViewportDims() const212 size_t RenderEngineThreaded::getMaxViewportDims() const {
213 waitUntilInitialized();
214 return mRenderEngine->getMaxViewportDims();
215 }
216
supportsProtectedContent() const217 bool RenderEngineThreaded::supportsProtectedContent() const {
218 waitUntilInitialized();
219 return mRenderEngine->supportsProtectedContent();
220 }
221
cleanupPostRender()222 void RenderEngineThreaded::cleanupPostRender() {
223 if (canSkipPostRenderCleanup()) {
224 return;
225 }
226
227 // This function is designed so it can run asynchronously, so we do not need to wait
228 // for the futures.
229 {
230 std::lock_guard lock(mThreadMutex);
231 mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
232 SFTRACE_NAME("REThreaded::cleanupPostRender");
233 instance.cleanupPostRender();
234 });
235 mNeedsPostRenderCleanup = false;
236 }
237 mCondition.notify_one();
238 }
239
canSkipPostRenderCleanup() const240 bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
241 return !mNeedsPostRenderCleanup;
242 }
243
drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>> && resultPromise,const DisplaySettings & display,const std::vector<LayerSettings> & layers,const std::shared_ptr<ExternalTexture> & buffer,base::unique_fd && bufferFence)244 void RenderEngineThreaded::drawLayersInternal(
245 const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
246 const DisplaySettings& display, const std::vector<LayerSettings>& layers,
247 const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
248 resultPromise->set_value(Fence::NO_FENCE);
249 return;
250 }
251
drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>> && resultPromise,const std::shared_ptr<ExternalTexture> & sdr,base::borrowed_fd && sdrFence,const std::shared_ptr<ExternalTexture> & hdr,base::borrowed_fd && hdrFence,float hdrSdrRatio,ui::Dataspace dataspace,const std::shared_ptr<ExternalTexture> & gainmap)252 void RenderEngineThreaded::drawGainmapInternal(
253 const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
254 const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
255 const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
256 float hdrSdrRatio, ui::Dataspace dataspace,
257 const std::shared_ptr<ExternalTexture>& gainmap) {
258 resultPromise->set_value(Fence::NO_FENCE);
259 return;
260 }
261
drawLayers(const DisplaySettings & display,const std::vector<LayerSettings> & layers,const std::shared_ptr<ExternalTexture> & buffer,base::unique_fd && bufferFence)262 ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
263 const DisplaySettings& display, const std::vector<LayerSettings>& layers,
264 const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
265 SFTRACE_CALL();
266 const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
267 std::future<FenceResult> resultFuture = resultPromise->get_future();
268 int fd = bufferFence.release();
269 {
270 std::lock_guard lock(mThreadMutex);
271 mNeedsPostRenderCleanup = true;
272 mFunctionCalls.push(
273 [resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) {
274 SFTRACE_NAME("REThreaded::drawLayers");
275 instance.updateProtectedContext(layers, {buffer.get()});
276 instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
277 base::unique_fd(fd));
278 });
279 }
280 mCondition.notify_one();
281 return resultFuture;
282 }
283
drawGainmap(const std::shared_ptr<ExternalTexture> & sdr,base::borrowed_fd && sdrFence,const std::shared_ptr<ExternalTexture> & hdr,base::borrowed_fd && hdrFence,float hdrSdrRatio,ui::Dataspace dataspace,const std::shared_ptr<ExternalTexture> & gainmap)284 ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap(
285 const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
286 const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
287 float hdrSdrRatio, ui::Dataspace dataspace,
288 const std::shared_ptr<ExternalTexture>& gainmap) {
289 SFTRACE_CALL();
290 const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
291 std::future<FenceResult> resultFuture = resultPromise->get_future();
292 {
293 std::lock_guard lock(mThreadMutex);
294 mNeedsPostRenderCleanup = true;
295 mFunctionCalls.push([resultPromise, sdr, sdrFence = std::move(sdrFence), hdr,
296 hdrFence = std::move(hdrFence), hdrSdrRatio, dataspace,
297 gainmap](renderengine::RenderEngine& instance) mutable {
298 SFTRACE_NAME("REThreaded::drawGainmap");
299 instance.updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()});
300 instance.drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr,
301 std::move(hdrFence), hdrSdrRatio, dataspace, gainmap);
302 });
303 }
304 mCondition.notify_one();
305 return resultFuture;
306 }
307
getContextPriority()308 int RenderEngineThreaded::getContextPriority() {
309 std::promise<int> resultPromise;
310 std::future<int> resultFuture = resultPromise.get_future();
311 {
312 std::lock_guard lock(mThreadMutex);
313 mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
314 SFTRACE_NAME("REThreaded::getContextPriority");
315 int priority = instance.getContextPriority();
316 resultPromise.set_value(priority);
317 });
318 }
319 mCondition.notify_one();
320 return resultFuture.get();
321 }
322
supportsBackgroundBlur()323 bool RenderEngineThreaded::supportsBackgroundBlur() {
324 waitUntilInitialized();
325 return mRenderEngine->supportsBackgroundBlur();
326 }
327
onActiveDisplaySizeChanged(ui::Size size)328 void RenderEngineThreaded::onActiveDisplaySizeChanged(ui::Size size) {
329 // This function is designed so it can run asynchronously, so we do not need to wait
330 // for the futures.
331 {
332 std::lock_guard lock(mThreadMutex);
333 mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
334 SFTRACE_NAME("REThreaded::onActiveDisplaySizeChanged");
335 instance.onActiveDisplaySizeChanged(size);
336 });
337 }
338 mCondition.notify_one();
339 }
340
getRenderEngineTid() const341 std::optional<pid_t> RenderEngineThreaded::getRenderEngineTid() const {
342 std::promise<pid_t> tidPromise;
343 std::future<pid_t> tidFuture = tidPromise.get_future();
344 {
345 std::lock_guard lock(mThreadMutex);
346 mFunctionCalls.push([&tidPromise](renderengine::RenderEngine& instance) {
347 tidPromise.set_value(gettid());
348 });
349 }
350
351 mCondition.notify_one();
352 return std::make_optional(tidFuture.get());
353 }
354
setEnableTracing(bool tracingEnabled)355 void RenderEngineThreaded::setEnableTracing(bool tracingEnabled) {
356 // This function is designed so it can run asynchronously, so we do not need to wait
357 // for the futures.
358 {
359 std::lock_guard lock(mThreadMutex);
360 mFunctionCalls.push([tracingEnabled](renderengine::RenderEngine& instance) {
361 SFTRACE_NAME("REThreaded::setEnableTracing");
362 instance.setEnableTracing(tracingEnabled);
363 });
364 }
365 mCondition.notify_one();
366 }
367 } // namespace threaded
368 } // namespace renderengine
369 } // namespace android
370