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
94 pthread_setname_np(pthread_self(), mThreadName);
95
96 {
97 std::scoped_lock lock(mInitializedMutex);
98 mIsInitialized = true;
99 }
100 mInitializedCondition.notify_all();
101
102 while (mRunning) {
103 const auto getNextTask = [this]() -> std::optional<Work> {
104 std::scoped_lock lock(mThreadMutex);
105 if (!mFunctionCalls.empty()) {
106 Work task = mFunctionCalls.front();
107 mFunctionCalls.pop();
108 return std::make_optional<Work>(task);
109 }
110 return std::nullopt;
111 };
112
113 const auto task = getNextTask();
114
115 if (task) {
116 (*task)(*mRenderEngine);
117 }
118
119 std::unique_lock<std::mutex> lock(mThreadMutex);
120 mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
121 return !mRunning || !mFunctionCalls.empty();
122 });
123 }
124
125 // we must release the RenderEngine on the thread that created it
126 mRenderEngine.reset();
127 }
128
waitUntilInitialized() const129 void RenderEngineThreaded::waitUntilInitialized() const {
130 std::unique_lock<std::mutex> lock(mInitializedMutex);
131 mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
132 }
133
primeCache()134 std::future<void> RenderEngineThreaded::primeCache() {
135 const auto resultPromise = std::make_shared<std::promise<void>>();
136 std::future<void> resultFuture = resultPromise->get_future();
137 ATRACE_CALL();
138 // This function is designed so it can run asynchronously, so we do not need to wait
139 // for the futures.
140 {
141 std::lock_guard lock(mThreadMutex);
142 mFunctionCalls.push([resultPromise](renderengine::RenderEngine& instance) {
143 ATRACE_NAME("REThreaded::primeCache");
144 if (setSchedFifo(false) != NO_ERROR) {
145 ALOGW("Couldn't set SCHED_OTHER for primeCache");
146 }
147
148 instance.primeCache();
149 resultPromise->set_value();
150
151 if (setSchedFifo(true) != NO_ERROR) {
152 ALOGW("Couldn't set SCHED_FIFO for primeCache");
153 }
154 });
155 }
156 mCondition.notify_one();
157
158 return resultFuture;
159 }
160
dump(std::string & result)161 void RenderEngineThreaded::dump(std::string& result) {
162 std::promise<std::string> resultPromise;
163 std::future<std::string> resultFuture = resultPromise.get_future();
164 {
165 std::lock_guard lock(mThreadMutex);
166 mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
167 ATRACE_NAME("REThreaded::dump");
168 std::string localResult = result;
169 instance.dump(localResult);
170 resultPromise.set_value(std::move(localResult));
171 });
172 }
173 mCondition.notify_one();
174 // Note: This is an rvalue.
175 result.assign(resultFuture.get());
176 }
177
genTextures(size_t count,uint32_t * names)178 void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
179 ATRACE_CALL();
180 // This is a no-op in SkiaRenderEngine.
181 if (getRenderEngineType() != RenderEngineType::THREADED) {
182 return;
183 }
184 std::promise<void> resultPromise;
185 std::future<void> resultFuture = resultPromise.get_future();
186 {
187 std::lock_guard lock(mThreadMutex);
188 mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
189 ATRACE_NAME("REThreaded::genTextures");
190 instance.genTextures(count, names);
191 resultPromise.set_value();
192 });
193 }
194 mCondition.notify_one();
195 resultFuture.wait();
196 }
197
deleteTextures(size_t count,uint32_t const * names)198 void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
199 ATRACE_CALL();
200 // This is a no-op in SkiaRenderEngine.
201 if (getRenderEngineType() != RenderEngineType::THREADED) {
202 return;
203 }
204 std::promise<void> resultPromise;
205 std::future<void> resultFuture = resultPromise.get_future();
206 {
207 std::lock_guard lock(mThreadMutex);
208 mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
209 ATRACE_NAME("REThreaded::deleteTextures");
210 instance.deleteTextures(count, names);
211 resultPromise.set_value();
212 });
213 }
214 mCondition.notify_one();
215 resultFuture.wait();
216 }
217
mapExternalTextureBuffer(const sp<GraphicBuffer> & buffer,bool isRenderable)218 void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
219 bool isRenderable) {
220 ATRACE_CALL();
221 // This function is designed so it can run asynchronously, so we do not need to wait
222 // for the futures.
223 {
224 std::lock_guard lock(mThreadMutex);
225 mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
226 ATRACE_NAME("REThreaded::mapExternalTextureBuffer");
227 instance.mapExternalTextureBuffer(buffer, isRenderable);
228 });
229 }
230 mCondition.notify_one();
231 }
232
unmapExternalTextureBuffer(sp<GraphicBuffer> && buffer)233 void RenderEngineThreaded::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
234 ATRACE_CALL();
235 // This function is designed so it can run asynchronously, so we do not need to wait
236 // for the futures.
237 {
238 std::lock_guard lock(mThreadMutex);
239 mFunctionCalls.push(
240 [=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable {
241 ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
242 instance.unmapExternalTextureBuffer(std::move(buffer));
243 });
244 }
245 mCondition.notify_one();
246 }
247
getMaxTextureSize() const248 size_t RenderEngineThreaded::getMaxTextureSize() const {
249 waitUntilInitialized();
250 return mRenderEngine->getMaxTextureSize();
251 }
252
getMaxViewportDims() const253 size_t RenderEngineThreaded::getMaxViewportDims() const {
254 waitUntilInitialized();
255 return mRenderEngine->getMaxViewportDims();
256 }
257
supportsProtectedContent() const258 bool RenderEngineThreaded::supportsProtectedContent() const {
259 waitUntilInitialized();
260 return mRenderEngine->supportsProtectedContent();
261 }
262
cleanupPostRender()263 void RenderEngineThreaded::cleanupPostRender() {
264 if (canSkipPostRenderCleanup()) {
265 return;
266 }
267
268 // This function is designed so it can run asynchronously, so we do not need to wait
269 // for the futures.
270 {
271 std::lock_guard lock(mThreadMutex);
272 mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
273 ATRACE_NAME("REThreaded::cleanupPostRender");
274 instance.cleanupPostRender();
275 });
276 }
277 mCondition.notify_one();
278 }
279
canSkipPostRenderCleanup() const280 bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
281 waitUntilInitialized();
282 return mRenderEngine->canSkipPostRenderCleanup();
283 }
284
drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>> && resultPromise,const DisplaySettings & display,const std::vector<LayerSettings> & layers,const std::shared_ptr<ExternalTexture> & buffer,const bool useFramebufferCache,base::unique_fd && bufferFence)285 void RenderEngineThreaded::drawLayersInternal(
286 const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
287 const DisplaySettings& display, const std::vector<LayerSettings>& layers,
288 const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
289 base::unique_fd&& bufferFence) {
290 resultPromise->set_value(Fence::NO_FENCE);
291 return;
292 }
293
drawLayers(const DisplaySettings & display,const std::vector<LayerSettings> & layers,const std::shared_ptr<ExternalTexture> & buffer,const bool useFramebufferCache,base::unique_fd && bufferFence)294 ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
295 const DisplaySettings& display, const std::vector<LayerSettings>& layers,
296 const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
297 base::unique_fd&& bufferFence) {
298 ATRACE_CALL();
299 const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
300 std::future<FenceResult> resultFuture = resultPromise->get_future();
301 int fd = bufferFence.release();
302 {
303 std::lock_guard lock(mThreadMutex);
304 mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache,
305 fd](renderengine::RenderEngine& instance) {
306 ATRACE_NAME("REThreaded::drawLayers");
307 instance.updateProtectedContext(layers, buffer);
308 instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
309 useFramebufferCache, base::unique_fd(fd));
310 });
311 }
312 mCondition.notify_one();
313 return resultFuture;
314 }
315
cleanFramebufferCache()316 void RenderEngineThreaded::cleanFramebufferCache() {
317 ATRACE_CALL();
318 // This function is designed so it can run asynchronously, so we do not need to wait
319 // for the futures.
320 {
321 std::lock_guard lock(mThreadMutex);
322 mFunctionCalls.push([](renderengine::RenderEngine& instance) {
323 ATRACE_NAME("REThreaded::cleanFramebufferCache");
324 instance.cleanFramebufferCache();
325 });
326 }
327 mCondition.notify_one();
328 }
329
getContextPriority()330 int RenderEngineThreaded::getContextPriority() {
331 std::promise<int> resultPromise;
332 std::future<int> resultFuture = resultPromise.get_future();
333 {
334 std::lock_guard lock(mThreadMutex);
335 mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
336 ATRACE_NAME("REThreaded::getContextPriority");
337 int priority = instance.getContextPriority();
338 resultPromise.set_value(priority);
339 });
340 }
341 mCondition.notify_one();
342 return resultFuture.get();
343 }
344
supportsBackgroundBlur()345 bool RenderEngineThreaded::supportsBackgroundBlur() {
346 waitUntilInitialized();
347 return mRenderEngine->supportsBackgroundBlur();
348 }
349
onActiveDisplaySizeChanged(ui::Size size)350 void RenderEngineThreaded::onActiveDisplaySizeChanged(ui::Size size) {
351 // This function is designed so it can run asynchronously, so we do not need to wait
352 // for the futures.
353 {
354 std::lock_guard lock(mThreadMutex);
355 mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
356 ATRACE_NAME("REThreaded::onActiveDisplaySizeChanged");
357 instance.onActiveDisplaySizeChanged(size);
358 });
359 }
360 mCondition.notify_one();
361 }
362
getRenderEngineTid() const363 std::optional<pid_t> RenderEngineThreaded::getRenderEngineTid() const {
364 std::promise<pid_t> tidPromise;
365 std::future<pid_t> tidFuture = tidPromise.get_future();
366 {
367 std::lock_guard lock(mThreadMutex);
368 mFunctionCalls.push([&tidPromise](renderengine::RenderEngine& instance) {
369 tidPromise.set_value(gettid());
370 });
371 }
372
373 mCondition.notify_one();
374 return std::make_optional(tidFuture.get());
375 }
376
setEnableTracing(bool tracingEnabled)377 void RenderEngineThreaded::setEnableTracing(bool tracingEnabled) {
378 // This function is designed so it can run asynchronously, so we do not need to wait
379 // for the futures.
380 {
381 std::lock_guard lock(mThreadMutex);
382 mFunctionCalls.push([tracingEnabled](renderengine::RenderEngine& instance) {
383 ATRACE_NAME("REThreaded::setEnableTracing");
384 instance.setEnableTracing(tracingEnabled);
385 });
386 }
387 mCondition.notify_one();
388 }
389 } // namespace threaded
390 } // namespace renderengine
391 } // namespace android
392