1/*
2 * Copyright (C) 2013 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#include "RenderProxy.h"
18
19#include "DeferredLayerUpdater.h"
20#include "DisplayList.h"
21#include "Properties.h"
22#include "Readback.h"
23#include "Rect.h"
24#include "pipeline/skia/VectorDrawableAtlas.h"
25#include "renderstate/RenderState.h"
26#include "renderthread/CanvasContext.h"
27#include "renderthread/EglManager.h"
28#include "renderthread/RenderTask.h"
29#include "renderthread/RenderThread.h"
30#include "utils/Macros.h"
31#include "utils/TimeUtils.h"
32
33#include <ui/GraphicBuffer.h>
34
35namespace android {
36namespace uirenderer {
37namespace renderthread {
38
39RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
40                         IContextFactory* contextFactory)
41        : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
42    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
43        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
44    });
45    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
46}
47
48RenderProxy::~RenderProxy() {
49    destroyContext();
50}
51
52void RenderProxy::destroyContext() {
53    if (mContext) {
54        mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
55        // This is also a fence as we need to be certain that there are no
56        // outstanding mDrawFrame tasks posted before it is destroyed
57        mRenderThread.queue().runSync([this]() { delete mContext; });
58        mContext = nullptr;
59    }
60}
61
62void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) {
63    mRenderThread.queue().post([this, swapBehavior]() { mContext->setSwapBehavior(swapBehavior); });
64}
65
66bool RenderProxy::loadSystemProperties() {
67    return mRenderThread.queue().runSync([this]() -> bool {
68        bool needsRedraw = false;
69        if (Caches::hasInstance()) {
70            needsRedraw = Properties::load();
71        }
72        if (mContext->profiler().consumeProperties()) {
73            needsRedraw = true;
74        }
75        return needsRedraw;
76    });
77}
78
79void RenderProxy::setName(const char* name) {
80    // block since name/value pointers owned by caller
81    // TODO: Support move arguments
82    mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
83}
84
85void RenderProxy::initialize(const sp<Surface>& surface) {
86    mRenderThread.queue().post(
87            [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
88}
89
90void RenderProxy::updateSurface(const sp<Surface>& surface) {
91    mRenderThread.queue().post(
92            [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
93}
94
95bool RenderProxy::pauseSurface(const sp<Surface>& surface) {
96    return mRenderThread.queue().runSync([this]() -> bool { return mContext->pauseSurface(); });
97}
98
99void RenderProxy::setStopped(bool stopped) {
100    mRenderThread.queue().runSync([this, stopped]() { mContext->setStopped(stopped); });
101}
102
103void RenderProxy::setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
104    mRenderThread.queue().post(
105            [=]() { mContext->setup(lightRadius, ambientShadowAlpha, spotShadowAlpha); });
106}
107
108void RenderProxy::setLightCenter(const Vector3& lightCenter) {
109    mRenderThread.queue().post([=]() { mContext->setLightCenter(lightCenter); });
110}
111
112void RenderProxy::setOpaque(bool opaque) {
113    mRenderThread.queue().post([=]() { mContext->setOpaque(opaque); });
114}
115
116void RenderProxy::setWideGamut(bool wideGamut) {
117    mRenderThread.queue().post([=]() { mContext->setWideGamut(wideGamut); });
118}
119
120int64_t* RenderProxy::frameInfo() {
121    return mDrawFrameTask.frameInfo();
122}
123
124int RenderProxy::syncAndDrawFrame() {
125    return mDrawFrameTask.drawFrame();
126}
127
128void RenderProxy::destroy() {
129    // destroyCanvasAndSurface() needs a fence as when it returns the
130    // underlying BufferQueue is going to be released from under
131    // the render thread.
132    mRenderThread.queue().runSync([=]() { mContext->destroy(); });
133}
134
135void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
136    ATRACE_CALL();
137    RenderThread& thread = RenderThread::getInstance();
138    auto invoke = [&thread, functor]() { CanvasContext::invokeFunctor(thread, functor); };
139    if (waitForCompletion) {
140        // waitForCompletion = true is expected to be fairly rare and only
141        // happen in destruction. Thus it should be fine to temporarily
142        // create a Mutex
143        thread.queue().runSync(std::move(invoke));
144    } else {
145        thread.queue().post(std::move(invoke));
146    }
147}
148
149DeferredLayerUpdater* RenderProxy::createTextureLayer() {
150    return mRenderThread.queue().runSync([this]() -> auto {
151        return mContext->createTextureLayer();
152    });
153}
154
155void RenderProxy::buildLayer(RenderNode* node) {
156    mRenderThread.queue().runSync([&]() { mContext->buildLayer(node); });
157}
158
159bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
160    return mRenderThread.queue().runSync(
161            [&]() -> bool { return mContext->copyLayerInto(layer, &bitmap); });
162}
163
164void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
165    mDrawFrameTask.pushLayerUpdate(layer);
166}
167
168void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) {
169    mDrawFrameTask.removeLayerUpdate(layer);
170}
171
172void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) {
173    return mRenderThread.queue().runSync([&]() { layer->detachSurfaceTexture(); });
174}
175
176void RenderProxy::destroyHardwareResources() {
177    return mRenderThread.queue().runSync([&]() { mContext->destroyHardwareResources(); });
178}
179
180void RenderProxy::trimMemory(int level) {
181    // Avoid creating a RenderThread to do a trimMemory.
182    if (RenderThread::hasInstance()) {
183        RenderThread& thread = RenderThread::getInstance();
184        thread.queue().post([&thread, level]() { CanvasContext::trimMemory(thread, level); });
185    }
186}
187
188void RenderProxy::overrideProperty(const char* name, const char* value) {
189    // expensive, but block here since name/value pointers owned by caller
190    RenderThread::getInstance().queue().runSync(
191            [&]() { Properties::overrideProperty(name, value); });
192}
193
194void RenderProxy::fence() {
195    mRenderThread.queue().runSync([]() {});
196}
197
198void RenderProxy::staticFence() {
199    RenderThread::getInstance().queue().runSync([]() {});
200}
201
202void RenderProxy::stopDrawing() {
203    mRenderThread.queue().runSync([this]() { mContext->stopDrawing(); });
204}
205
206void RenderProxy::notifyFramePending() {
207    mRenderThread.queue().post([this]() { mContext->notifyFramePending(); });
208}
209
210void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
211    mRenderThread.queue().runSync([&]() {
212        mContext->profiler().dumpData(fd);
213        if (dumpFlags & DumpFlags::FrameStats) {
214            mContext->dumpFrames(fd);
215        }
216        if (dumpFlags & DumpFlags::JankStats) {
217            mRenderThread.globalProfileData()->dump(fd);
218        }
219        if (dumpFlags & DumpFlags::Reset) {
220            mContext->resetFrameStats();
221        }
222    });
223}
224
225void RenderProxy::resetProfileInfo() {
226    mRenderThread.queue().runSync([=]() { mContext->resetFrameStats(); });
227}
228
229uint32_t RenderProxy::frameTimePercentile(int percentile) {
230    return mRenderThread.queue().runSync([&]() -> auto {
231        return mRenderThread.globalProfileData()->findPercentile(percentile);
232    });
233}
234
235void RenderProxy::dumpGraphicsMemory(int fd) {
236    auto& thread = RenderThread::getInstance();
237    thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd); });
238}
239
240void RenderProxy::setProcessStatsBuffer(int fd) {
241    auto& rt = RenderThread::getInstance();
242    rt.queue().post([&rt, fd = dup(fd) ]() {
243        rt.globalProfileData().switchStorageToAshmem(fd);
244        close(fd);
245    });
246}
247
248void RenderProxy::rotateProcessStatsBuffer() {
249    auto& rt = RenderThread::getInstance();
250    rt.queue().post([&rt]() { rt.globalProfileData().rotateStorage(); });
251}
252
253int RenderProxy::getRenderThreadTid() {
254    return mRenderThread.getTid();
255}
256
257void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) {
258    mRenderThread.queue().post([=]() { mContext->addRenderNode(node, placeFront); });
259}
260
261void RenderProxy::removeRenderNode(RenderNode* node) {
262    mRenderThread.queue().post([=]() { mContext->removeRenderNode(node); });
263}
264
265void RenderProxy::drawRenderNode(RenderNode* node) {
266    mRenderThread.queue().runSync([=]() { mContext->prepareAndDraw(node); });
267}
268
269void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) {
270    mDrawFrameTask.setContentDrawBounds(left, top, right, bottom);
271}
272
273void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
274    mDrawFrameTask.setFrameCallback(std::move(callback));
275}
276
277void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
278    mDrawFrameTask.setFrameCompleteCallback(std::move(callback));
279}
280
281void RenderProxy::serializeDisplayListTree() {
282    mRenderThread.queue().post([=]() { mContext->serializeDisplayListTree(); });
283}
284
285void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) {
286    mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() {
287        mContext->addFrameMetricsObserver(observer.get());
288    });
289}
290
291void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr) {
292    mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() {
293        mContext->removeFrameMetricsObserver(observer.get());
294    });
295}
296
297int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
298                                 SkBitmap* bitmap) {
299    auto& thread = RenderThread::getInstance();
300    return static_cast<int>(thread.queue().runSync([&]() -> auto {
301        return thread.readback().copySurfaceInto(*surface, Rect(left, top, right, bottom), bitmap);
302    }));
303}
304
305void RenderProxy::prepareToDraw(Bitmap& bitmap) {
306    // If we haven't spun up a hardware accelerated window yet, there's no
307    // point in precaching these bitmaps as it can't impact jank.
308    // We also don't know if we even will spin up a hardware-accelerated
309    // window or not.
310    if (!RenderThread::hasInstance()) return;
311    RenderThread* renderThread = &RenderThread::getInstance();
312    bitmap.ref();
313    auto task = [renderThread, &bitmap]() {
314        CanvasContext::prepareToDraw(*renderThread, &bitmap);
315        bitmap.unref();
316    };
317    nsecs_t lastVsync = renderThread->timeLord().latestVsync();
318    nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos();
319    nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC);
320    // We expect the UI thread to take 4ms and for RT to be active from VSYNC+4ms to
321    // VSYNC+12ms or so, so aim for the gap during which RT is expected to
322    // be idle
323    // TODO: Make this concept a first-class supported thing? RT could use
324    // knowledge of pending draws to better schedule this task
325    if (timeToNextVsync > -6_ms && timeToNextVsync < 1_ms) {
326        renderThread->queue().postAt(estimatedNextVsync + 8_ms, task);
327    } else {
328        renderThread->queue().post(task);
329    }
330}
331
332sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
333    auto& thread = RenderThread::getInstance();
334    return thread.queue().runSync([&]() -> auto { return thread.allocateHardwareBitmap(bitmap); });
335}
336
337int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
338    RenderThread& thread = RenderThread::getInstance();
339    if (Properties::isSkiaEnabled() && gettid() == thread.getTid()) {
340        // TODO: fix everything that hits this. We should never be triggering a readback ourselves.
341        return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap);
342    } else {
343        return thread.queue().runSync([&]() -> int {
344            return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap);
345        });
346    }
347}
348
349void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) {
350    if (!RenderThread::hasInstance()) return;
351    RenderThread& thread = RenderThread::getInstance();
352    thread.queue().post(
353            [&thread, pixelRefId]() { thread.renderState().onBitmapDestroyed(pixelRefId); });
354}
355
356void RenderProxy::disableVsync() {
357    Properties::disableVsync = true;
358}
359
360void RenderProxy::repackVectorDrawableAtlas() {
361    RenderThread& thread = RenderThread::getInstance();
362    thread.queue().post([&thread]() {
363        // The context may be null if trimMemory executed, but then the atlas was deleted too.
364        if (thread.getGrContext() != nullptr) {
365            thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded(
366                    thread.getGrContext());
367        }
368    });
369}
370
371void RenderProxy::releaseVDAtlasEntries() {
372    RenderThread& thread = RenderThread::getInstance();
373    thread.queue().post([&thread]() {
374        // The context may be null if trimMemory executed, but then the atlas was deleted too.
375        if (thread.getGrContext() != nullptr) {
376            thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries();
377        }
378    });
379}
380
381} /* namespace renderthread */
382} /* namespace uirenderer */
383} /* namespace android */
384