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 <private/gui/SyncFeatures.h>
27 #include <processgroup/processgroup.h>
28 #include <utils/Trace.h>
29
30 #include "gl/GLESRenderEngine.h"
31
32 using namespace std::chrono_literals;
33
34 namespace android {
35 namespace renderengine {
36 namespace threaded {
37
create(CreateInstanceFactory factory,RenderEngineType type)38 std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory,
39 RenderEngineType type) {
40 return std::make_unique<RenderEngineThreaded>(std::move(factory), type);
41 }
42
RenderEngineThreaded(CreateInstanceFactory factory,RenderEngineType type)43 RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type)
44 : RenderEngine(type) {
45 ATRACE_CALL();
46
47 std::lock_guard lockThread(mThreadMutex);
48 mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
49 }
50
~RenderEngineThreaded()51 RenderEngineThreaded::~RenderEngineThreaded() {
52 mRunning = false;
53 mCondition.notify_one();
54
55 if (mThread.joinable()) {
56 mThread.join();
57 }
58 }
59
setSchedFifo(bool enabled)60 status_t RenderEngineThreaded::setSchedFifo(bool enabled) {
61 static constexpr int kFifoPriority = 2;
62 static constexpr int kOtherPriority = 0;
63
64 struct sched_param param = {0};
65 int sched_policy;
66 if (enabled) {
67 sched_policy = SCHED_FIFO;
68 param.sched_priority = kFifoPriority;
69 } else {
70 sched_policy = SCHED_OTHER;
71 param.sched_priority = kOtherPriority;
72 }
73
74 if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
75 return -errno;
76 }
77 return NO_ERROR;
78 }
79
80 // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
threadMain(CreateInstanceFactory factory)81 void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
82 ATRACE_CALL();
83
84 if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) {
85 ALOGW("Failed to set render-engine task profile!");
86 }
87
88 if (setSchedFifo(true) != NO_ERROR) {
89 ALOGW("Couldn't set SCHED_FIFO");
90 }
91
92 mRenderEngine = factory();
93 mIsProtected = mRenderEngine->isProtected();
94
95 pthread_setname_np(pthread_self(), mThreadName);
96
97 {
98 std::scoped_lock lock(mInitializedMutex);
99 mIsInitialized = true;
100 }
101 mInitializedCondition.notify_all();
102
103 while (mRunning) {
104 const auto getNextTask = [this]() -> std::optional<Work> {
105 std::scoped_lock lock(mThreadMutex);
106 if (!mFunctionCalls.empty()) {
107 Work task = mFunctionCalls.front();
108 mFunctionCalls.pop();
109 return std::make_optional<Work>(task);
110 }
111 return std::nullopt;
112 };
113
114 const auto task = getNextTask();
115
116 if (task) {
117 (*task)(*mRenderEngine);
118 }
119
120 std::unique_lock<std::mutex> lock(mThreadMutex);
121 mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
122 return !mRunning || !mFunctionCalls.empty();
123 });
124 }
125
126 // we must release the RenderEngine on the thread that created it
127 mRenderEngine.reset();
128 }
129
waitUntilInitialized() const130 void RenderEngineThreaded::waitUntilInitialized() const {
131 std::unique_lock<std::mutex> lock(mInitializedMutex);
132 mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
133 }
134
primeCache()135 std::future<void> RenderEngineThreaded::primeCache() {
136 const auto resultPromise = std::make_shared<std::promise<void>>();
137 std::future<void> resultFuture = resultPromise->get_future();
138 ATRACE_CALL();
139 // This function is designed so it can run asynchronously, so we do not need to wait
140 // for the futures.
141 {
142 std::lock_guard lock(mThreadMutex);
143 mFunctionCalls.push([resultPromise](renderengine::RenderEngine& instance) {
144 ATRACE_NAME("REThreaded::primeCache");
145 if (setSchedFifo(false) != NO_ERROR) {
146 ALOGW("Couldn't set SCHED_OTHER for primeCache");
147 }
148
149 instance.primeCache();
150 resultPromise->set_value();
151
152 if (setSchedFifo(true) != NO_ERROR) {
153 ALOGW("Couldn't set SCHED_FIFO for primeCache");
154 }
155 });
156 }
157 mCondition.notify_one();
158
159 return resultFuture;
160 }
161
dump(std::string & result)162 void RenderEngineThreaded::dump(std::string& result) {
163 std::promise<std::string> resultPromise;
164 std::future<std::string> resultFuture = resultPromise.get_future();
165 {
166 std::lock_guard lock(mThreadMutex);
167 mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
168 ATRACE_NAME("REThreaded::dump");
169 std::string localResult = result;
170 instance.dump(localResult);
171 resultPromise.set_value(std::move(localResult));
172 });
173 }
174 mCondition.notify_one();
175 // Note: This is an rvalue.
176 result.assign(resultFuture.get());
177 }
178
genTextures(size_t count,uint32_t * names)179 void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
180 ATRACE_CALL();
181 std::promise<void> resultPromise;
182 std::future<void> resultFuture = resultPromise.get_future();
183 {
184 std::lock_guard lock(mThreadMutex);
185 mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
186 ATRACE_NAME("REThreaded::genTextures");
187 instance.genTextures(count, names);
188 resultPromise.set_value();
189 });
190 }
191 mCondition.notify_one();
192 resultFuture.wait();
193 }
194
deleteTextures(size_t count,uint32_t const * names)195 void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
196 ATRACE_CALL();
197 std::promise<void> resultPromise;
198 std::future<void> resultFuture = resultPromise.get_future();
199 {
200 std::lock_guard lock(mThreadMutex);
201 mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
202 ATRACE_NAME("REThreaded::deleteTextures");
203 instance.deleteTextures(count, names);
204 resultPromise.set_value();
205 });
206 }
207 mCondition.notify_one();
208 resultFuture.wait();
209 }
210
mapExternalTextureBuffer(const sp<GraphicBuffer> & buffer,bool isRenderable)211 void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
212 bool isRenderable) {
213 ATRACE_CALL();
214 // This function is designed so it can run asynchronously, so we do not need to wait
215 // for the futures.
216 {
217 std::lock_guard lock(mThreadMutex);
218 mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
219 ATRACE_NAME("REThreaded::mapExternalTextureBuffer");
220 instance.mapExternalTextureBuffer(buffer, isRenderable);
221 });
222 }
223 mCondition.notify_one();
224 }
225
unmapExternalTextureBuffer(const sp<GraphicBuffer> & buffer)226 void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
227 ATRACE_CALL();
228 // This function is designed so it can run asynchronously, so we do not need to wait
229 // for the futures.
230 {
231 std::lock_guard lock(mThreadMutex);
232 mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
233 ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
234 instance.unmapExternalTextureBuffer(buffer);
235 });
236 }
237 mCondition.notify_one();
238 }
239
getMaxTextureSize() const240 size_t RenderEngineThreaded::getMaxTextureSize() const {
241 waitUntilInitialized();
242 return mRenderEngine->getMaxTextureSize();
243 }
244
getMaxViewportDims() const245 size_t RenderEngineThreaded::getMaxViewportDims() const {
246 waitUntilInitialized();
247 return mRenderEngine->getMaxViewportDims();
248 }
249
isProtected() const250 bool RenderEngineThreaded::isProtected() const {
251 waitUntilInitialized();
252 std::lock_guard lock(mThreadMutex);
253 return mIsProtected;
254 }
255
supportsProtectedContent() const256 bool RenderEngineThreaded::supportsProtectedContent() const {
257 waitUntilInitialized();
258 return mRenderEngine->supportsProtectedContent();
259 }
260
useProtectedContext(bool useProtectedContext)261 void RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
262 if (isProtected() == useProtectedContext ||
263 (useProtectedContext && !supportsProtectedContent())) {
264 return;
265 }
266
267 {
268 std::lock_guard lock(mThreadMutex);
269 mFunctionCalls.push([useProtectedContext, this](renderengine::RenderEngine& instance) {
270 ATRACE_NAME("REThreaded::useProtectedContext");
271 instance.useProtectedContext(useProtectedContext);
272 if (instance.isProtected() != useProtectedContext) {
273 ALOGE("Failed to switch RenderEngine context.");
274 // reset the cached mIsProtected value to a good state, but this does not
275 // prevent other callers of this method and isProtected from reading the
276 // invalid cached value.
277 mIsProtected = instance.isProtected();
278 }
279 });
280 mIsProtected = useProtectedContext;
281 }
282 mCondition.notify_one();
283 }
284
cleanupPostRender()285 void RenderEngineThreaded::cleanupPostRender() {
286 if (canSkipPostRenderCleanup()) {
287 return;
288 }
289
290 // This function is designed so it can run asynchronously, so we do not need to wait
291 // for the futures.
292 {
293 std::lock_guard lock(mThreadMutex);
294 mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
295 ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
296 instance.cleanupPostRender();
297 });
298 }
299 mCondition.notify_one();
300 }
301
canSkipPostRenderCleanup() const302 bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
303 waitUntilInitialized();
304 return mRenderEngine->canSkipPostRenderCleanup();
305 }
306
drawLayers(const DisplaySettings & display,const std::vector<const LayerSettings * > & layers,const std::shared_ptr<ExternalTexture> & buffer,const bool useFramebufferCache,base::unique_fd && bufferFence,base::unique_fd * drawFence)307 status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
308 const std::vector<const LayerSettings*>& layers,
309 const std::shared_ptr<ExternalTexture>& buffer,
310 const bool useFramebufferCache,
311 base::unique_fd&& bufferFence,
312 base::unique_fd* drawFence) {
313 ATRACE_CALL();
314 std::promise<status_t> resultPromise;
315 std::future<status_t> resultFuture = resultPromise.get_future();
316 {
317 std::lock_guard lock(mThreadMutex);
318 mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
319 &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
320 ATRACE_NAME("REThreaded::drawLayers");
321 status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
322 std::move(bufferFence), drawFence);
323 resultPromise.set_value(status);
324 });
325 }
326 mCondition.notify_one();
327 return resultFuture.get();
328 }
329
cleanFramebufferCache()330 void RenderEngineThreaded::cleanFramebufferCache() {
331 ATRACE_CALL();
332 // This function is designed so it can run asynchronously, so we do not need to wait
333 // for the futures.
334 {
335 std::lock_guard lock(mThreadMutex);
336 mFunctionCalls.push([](renderengine::RenderEngine& instance) {
337 ATRACE_NAME("REThreaded::cleanFramebufferCache");
338 instance.cleanFramebufferCache();
339 });
340 }
341 mCondition.notify_one();
342 }
343
getContextPriority()344 int RenderEngineThreaded::getContextPriority() {
345 std::promise<int> resultPromise;
346 std::future<int> resultFuture = resultPromise.get_future();
347 {
348 std::lock_guard lock(mThreadMutex);
349 mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
350 ATRACE_NAME("REThreaded::getContextPriority");
351 int priority = instance.getContextPriority();
352 resultPromise.set_value(priority);
353 });
354 }
355 mCondition.notify_one();
356 return resultFuture.get();
357 }
358
supportsBackgroundBlur()359 bool RenderEngineThreaded::supportsBackgroundBlur() {
360 waitUntilInitialized();
361 return mRenderEngine->supportsBackgroundBlur();
362 }
363
onPrimaryDisplaySizeChanged(ui::Size size)364 void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) {
365 // This function is designed so it can run asynchronously, so we do not need to wait
366 // for the futures.
367 {
368 std::lock_guard lock(mThreadMutex);
369 mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
370 ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged");
371 instance.onPrimaryDisplaySizeChanged(size);
372 });
373 }
374 mCondition.notify_one();
375 }
376
377 } // namespace threaded
378 } // namespace renderengine
379 } // namespace android
380