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, &param) != 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