xref: /aosp_15_r20/frameworks/native/libs/renderengine/threaded/RenderEngineThreaded.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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, &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     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