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 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     ATRACE_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, &param) != 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     ATRACE_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     ATRACE_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             ATRACE_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             ATRACE_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     ATRACE_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             ATRACE_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     ATRACE_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                     ATRACE_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             ATRACE_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 
drawLayers(const DisplaySettings & display,const std::vector<LayerSettings> & layers,const std::shared_ptr<ExternalTexture> & buffer,base::unique_fd && bufferFence)252 ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
253         const DisplaySettings& display, const std::vector<LayerSettings>& layers,
254         const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
255     ATRACE_CALL();
256     const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
257     std::future<FenceResult> resultFuture = resultPromise->get_future();
258     int fd = bufferFence.release();
259     {
260         std::lock_guard lock(mThreadMutex);
261         mNeedsPostRenderCleanup = true;
262         mFunctionCalls.push(
263                 [resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) {
264                     ATRACE_NAME("REThreaded::drawLayers");
265                     instance.updateProtectedContext(layers, buffer);
266                     instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
267                                                 base::unique_fd(fd));
268                 });
269     }
270     mCondition.notify_one();
271     return resultFuture;
272 }
273 
getContextPriority()274 int RenderEngineThreaded::getContextPriority() {
275     std::promise<int> resultPromise;
276     std::future<int> resultFuture = resultPromise.get_future();
277     {
278         std::lock_guard lock(mThreadMutex);
279         mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
280             ATRACE_NAME("REThreaded::getContextPriority");
281             int priority = instance.getContextPriority();
282             resultPromise.set_value(priority);
283         });
284     }
285     mCondition.notify_one();
286     return resultFuture.get();
287 }
288 
supportsBackgroundBlur()289 bool RenderEngineThreaded::supportsBackgroundBlur() {
290     waitUntilInitialized();
291     return mRenderEngine->supportsBackgroundBlur();
292 }
293 
onActiveDisplaySizeChanged(ui::Size size)294 void RenderEngineThreaded::onActiveDisplaySizeChanged(ui::Size size) {
295     // This function is designed so it can run asynchronously, so we do not need to wait
296     // for the futures.
297     {
298         std::lock_guard lock(mThreadMutex);
299         mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
300             ATRACE_NAME("REThreaded::onActiveDisplaySizeChanged");
301             instance.onActiveDisplaySizeChanged(size);
302         });
303     }
304     mCondition.notify_one();
305 }
306 
getRenderEngineTid() const307 std::optional<pid_t> RenderEngineThreaded::getRenderEngineTid() const {
308     std::promise<pid_t> tidPromise;
309     std::future<pid_t> tidFuture = tidPromise.get_future();
310     {
311         std::lock_guard lock(mThreadMutex);
312         mFunctionCalls.push([&tidPromise](renderengine::RenderEngine& instance) {
313             tidPromise.set_value(gettid());
314         });
315     }
316 
317     mCondition.notify_one();
318     return std::make_optional(tidFuture.get());
319 }
320 
setEnableTracing(bool tracingEnabled)321 void RenderEngineThreaded::setEnableTracing(bool tracingEnabled) {
322     // This function is designed so it can run asynchronously, so we do not need to wait
323     // for the futures.
324     {
325         std::lock_guard lock(mThreadMutex);
326         mFunctionCalls.push([tracingEnabled](renderengine::RenderEngine& instance) {
327             ATRACE_NAME("REThreaded::setEnableTracing");
328             instance.setEnableTracing(tracingEnabled);
329         });
330     }
331     mCondition.notify_one();
332 }
333 } // namespace threaded
334 } // namespace renderengine
335 } // namespace android
336