|  | /* | 
|  | * Copyright (C) 2013 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "RenderProxy.h" | 
|  |  | 
|  | #include "DeferredLayerUpdater.h" | 
|  | #include "DisplayList.h" | 
|  | #include "Properties.h" | 
|  | #include "Readback.h" | 
|  | #include "Rect.h" | 
|  | #include "WebViewFunctorManager.h" | 
|  | #include "renderthread/CanvasContext.h" | 
|  | #include "renderthread/RenderTask.h" | 
|  | #include "renderthread/RenderThread.h" | 
|  | #include "utils/Macros.h" | 
|  | #include "utils/TimeUtils.h" | 
|  | #include "utils/TraceUtils.h" | 
|  |  | 
|  | namespace android { | 
|  | namespace uirenderer { | 
|  | namespace renderthread { | 
|  |  | 
|  | RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, | 
|  | IContextFactory* contextFactory) | 
|  | : mRenderThread(RenderThread::getInstance()), mContext(nullptr) { | 
|  | mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* { | 
|  | return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory); | 
|  | }); | 
|  | mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode); | 
|  | } | 
|  |  | 
|  | RenderProxy::~RenderProxy() { | 
|  | destroyContext(); | 
|  | } | 
|  |  | 
|  | void RenderProxy::destroyContext() { | 
|  | if (mContext) { | 
|  | mDrawFrameTask.setContext(nullptr, nullptr, nullptr); | 
|  | // This is also a fence as we need to be certain that there are no | 
|  | // outstanding mDrawFrame tasks posted before it is destroyed | 
|  | mRenderThread.queue().runSync([this]() { delete mContext; }); | 
|  | mContext = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) { | 
|  | mRenderThread.queue().post([this, swapBehavior]() { mContext->setSwapBehavior(swapBehavior); }); | 
|  | } | 
|  |  | 
|  | bool RenderProxy::loadSystemProperties() { | 
|  | return mRenderThread.queue().runSync([this]() -> bool { | 
|  | bool needsRedraw = Properties::load(); | 
|  | if (mContext->profiler().consumeProperties()) { | 
|  | needsRedraw = true; | 
|  | } | 
|  | return needsRedraw; | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setName(const char* name) { | 
|  | // block since name/value pointers owned by caller | 
|  | // TODO: Support move arguments | 
|  | mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { | 
|  | ANativeWindow_acquire(window); | 
|  | mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { | 
|  | mContext->setSurface(win, enableTimeout); | 
|  | ANativeWindow_release(win); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::allocateBuffers() { | 
|  | mRenderThread.queue().post([=]() { mContext->allocateBuffers(); }); | 
|  | } | 
|  |  | 
|  | bool RenderProxy::pause() { | 
|  | return mRenderThread.queue().runSync([this]() -> bool { return mContext->pauseSurface(); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setStopped(bool stopped) { | 
|  | mRenderThread.queue().runSync([this, stopped]() { mContext->setStopped(stopped); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { | 
|  | mRenderThread.queue().post( | 
|  | [=]() { mContext->setLightAlpha(ambientShadowAlpha, spotShadowAlpha); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setLightGeometry(const Vector3& lightCenter, float lightRadius) { | 
|  | mRenderThread.queue().post([=]() { mContext->setLightGeometry(lightCenter, lightRadius); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setOpaque(bool opaque) { | 
|  | mRenderThread.queue().post([=]() { mContext->setOpaque(opaque); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setWideGamut(bool wideGamut) { | 
|  | mRenderThread.queue().post([=]() { mContext->setWideGamut(wideGamut); }); | 
|  | } | 
|  |  | 
|  | int64_t* RenderProxy::frameInfo() { | 
|  | return mDrawFrameTask.frameInfo(); | 
|  | } | 
|  |  | 
|  | int RenderProxy::syncAndDrawFrame() { | 
|  | return mDrawFrameTask.drawFrame(); | 
|  | } | 
|  |  | 
|  | void RenderProxy::destroy() { | 
|  | // destroyCanvasAndSurface() needs a fence as when it returns the | 
|  | // underlying BufferQueue is going to be released from under | 
|  | // the render thread. | 
|  | mRenderThread.queue().runSync([=]() { mContext->destroy(); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { | 
|  | ATRACE_CALL(); | 
|  | RenderThread& thread = RenderThread::getInstance(); | 
|  | auto invoke = [&thread, functor]() { CanvasContext::invokeFunctor(thread, functor); }; | 
|  | if (waitForCompletion) { | 
|  | // waitForCompletion = true is expected to be fairly rare and only | 
|  | // happen in destruction. Thus it should be fine to temporarily | 
|  | // create a Mutex | 
|  | thread.queue().runSync(std::move(invoke)); | 
|  | } else { | 
|  | thread.queue().post(std::move(invoke)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RenderProxy::destroyFunctor(int functor) { | 
|  | ATRACE_CALL(); | 
|  | RenderThread& thread = RenderThread::getInstance(); | 
|  | thread.queue().post([=]() { WebViewFunctorManager::instance().destroyFunctor(functor); }); | 
|  | } | 
|  |  | 
|  | DeferredLayerUpdater* RenderProxy::createTextureLayer() { | 
|  | return mRenderThread.queue().runSync([this]() -> auto { | 
|  | return mContext->createTextureLayer(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::buildLayer(RenderNode* node) { | 
|  | mRenderThread.queue().runSync([&]() { mContext->buildLayer(node); }); | 
|  | } | 
|  |  | 
|  | bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) { | 
|  | ATRACE_NAME("TextureView#getBitmap"); | 
|  | auto& thread = RenderThread::getInstance(); | 
|  | return thread.queue().runSync([&]() -> bool { | 
|  | return thread.readback().copyLayerInto(layer, &bitmap) == CopyResult::Success; | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) { | 
|  | mDrawFrameTask.pushLayerUpdate(layer); | 
|  | } | 
|  |  | 
|  | void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) { | 
|  | mDrawFrameTask.removeLayerUpdate(layer); | 
|  | } | 
|  |  | 
|  | void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) { | 
|  | return mRenderThread.queue().runSync([&]() { layer->detachSurfaceTexture(); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::destroyHardwareResources() { | 
|  | return mRenderThread.queue().runSync([&]() { mContext->destroyHardwareResources(); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::trimMemory(int level) { | 
|  | // Avoid creating a RenderThread to do a trimMemory. | 
|  | if (RenderThread::hasInstance()) { | 
|  | RenderThread& thread = RenderThread::getInstance(); | 
|  | thread.queue().post([&thread, level]() { CanvasContext::trimMemory(thread, level); }); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RenderProxy::overrideProperty(const char* name, const char* value) { | 
|  | // expensive, but block here since name/value pointers owned by caller | 
|  | RenderThread::getInstance().queue().runSync( | 
|  | [&]() { Properties::overrideProperty(name, value); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::fence() { | 
|  | mRenderThread.queue().runSync([]() {}); | 
|  | } | 
|  |  | 
|  | int RenderProxy::maxTextureSize() { | 
|  | static int maxTextureSize = RenderThread::getInstance().queue().runSync( | 
|  | []() { return DeviceInfo::get()->maxTextureSize(); }); | 
|  | return maxTextureSize; | 
|  | } | 
|  |  | 
|  | void RenderProxy::stopDrawing() { | 
|  | mRenderThread.queue().runSync([this]() { mContext->stopDrawing(); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::notifyFramePending() { | 
|  | mRenderThread.queue().post([this]() { mContext->notifyFramePending(); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { | 
|  | mRenderThread.queue().runSync([&]() { | 
|  | mContext->profiler().dumpData(fd); | 
|  | if (dumpFlags & DumpFlags::FrameStats) { | 
|  | mContext->dumpFrames(fd); | 
|  | } | 
|  | if (dumpFlags & DumpFlags::JankStats) { | 
|  | mRenderThread.globalProfileData()->dump(fd); | 
|  | } | 
|  | if (dumpFlags & DumpFlags::Reset) { | 
|  | mContext->resetFrameStats(); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::resetProfileInfo() { | 
|  | mRenderThread.queue().runSync([=]() { mContext->resetFrameStats(); }); | 
|  | } | 
|  |  | 
|  | uint32_t RenderProxy::frameTimePercentile(int percentile) { | 
|  | return mRenderThread.queue().runSync([&]() -> auto { | 
|  | return mRenderThread.globalProfileData()->findPercentile(percentile); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::dumpGraphicsMemory(int fd) { | 
|  | if (RenderThread::hasInstance()) { | 
|  | auto& thread = RenderThread::getInstance(); | 
|  | thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd); }); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RenderProxy::setProcessStatsBuffer(int fd) { | 
|  | auto& rt = RenderThread::getInstance(); | 
|  | rt.queue().post([&rt, fd = dup(fd)]() { | 
|  | rt.globalProfileData().switchStorageToAshmem(fd); | 
|  | close(fd); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::rotateProcessStatsBuffer() { | 
|  | auto& rt = RenderThread::getInstance(); | 
|  | rt.queue().post([&rt]() { rt.globalProfileData().rotateStorage(); }); | 
|  | } | 
|  |  | 
|  | int RenderProxy::getRenderThreadTid() { | 
|  | return mRenderThread.getTid(); | 
|  | } | 
|  |  | 
|  | void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) { | 
|  | mRenderThread.queue().post([=]() { mContext->addRenderNode(node, placeFront); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::removeRenderNode(RenderNode* node) { | 
|  | mRenderThread.queue().post([=]() { mContext->removeRenderNode(node); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::drawRenderNode(RenderNode* node) { | 
|  | mRenderThread.queue().runSync([=]() { mContext->prepareAndDraw(node); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) { | 
|  | mDrawFrameTask.setContentDrawBounds(left, top, right, bottom); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setPictureCapturedCallback( | 
|  | const std::function<void(sk_sp<SkPicture>&&)>& callback) { | 
|  | mRenderThread.queue().post( | 
|  | [this, cb = callback]() { mContext->setPictureCapturedCallback(cb); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) { | 
|  | mDrawFrameTask.setFrameCallback(std::move(callback)); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& callback) { | 
|  | mDrawFrameTask.setFrameCompleteCallback(std::move(callback)); | 
|  | } | 
|  |  | 
|  | void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) { | 
|  | mRenderThread.queue().post([this, observer = sp{observerPtr}]() { | 
|  | mContext->addFrameMetricsObserver(observer.get()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr) { | 
|  | mRenderThread.queue().post([this, observer = sp{observerPtr}]() { | 
|  | mContext->removeFrameMetricsObserver(observer.get()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setForceDark(bool enable) { | 
|  | mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); }); | 
|  | } | 
|  |  | 
|  | void RenderProxy::setRenderAheadDepth(int renderAhead) { | 
|  | mRenderThread.queue().post( | 
|  | [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); }); | 
|  | } | 
|  |  | 
|  | int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom, | 
|  | SkBitmap* bitmap) { | 
|  | auto& thread = RenderThread::getInstance(); | 
|  | return static_cast<int>(thread.queue().runSync([&]() -> auto { | 
|  | return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap); | 
|  | })); | 
|  | } | 
|  |  | 
|  | void RenderProxy::prepareToDraw(Bitmap& bitmap) { | 
|  | // If we haven't spun up a hardware accelerated window yet, there's no | 
|  | // point in precaching these bitmaps as it can't impact jank. | 
|  | // We also don't know if we even will spin up a hardware-accelerated | 
|  | // window or not. | 
|  | if (!RenderThread::hasInstance()) return; | 
|  | RenderThread* renderThread = &RenderThread::getInstance(); | 
|  | bitmap.ref(); | 
|  | auto task = [renderThread, &bitmap]() { | 
|  | CanvasContext::prepareToDraw(*renderThread, &bitmap); | 
|  | bitmap.unref(); | 
|  | }; | 
|  | nsecs_t lastVsync = renderThread->timeLord().latestVsync(); | 
|  | nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos(); | 
|  | nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(SYSTEM_TIME_MONOTONIC); | 
|  | // We expect the UI thread to take 4ms and for RT to be active from VSYNC+4ms to | 
|  | // VSYNC+12ms or so, so aim for the gap during which RT is expected to | 
|  | // be idle | 
|  | // TODO: Make this concept a first-class supported thing? RT could use | 
|  | // knowledge of pending draws to better schedule this task | 
|  | if (timeToNextVsync > -6_ms && timeToNextVsync < 1_ms) { | 
|  | renderThread->queue().postAt(estimatedNextVsync + 8_ms, task); | 
|  | } else { | 
|  | renderThread->queue().post(task); | 
|  | } | 
|  | } | 
|  |  | 
|  | int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) { | 
|  | ATRACE_NAME("HardwareBitmap readback"); | 
|  | RenderThread& thread = RenderThread::getInstance(); | 
|  | if (gettid() == thread.getTid()) { | 
|  | // TODO: fix everything that hits this. We should never be triggering a readback ourselves. | 
|  | return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap); | 
|  | } else { | 
|  | return thread.queue().runSync( | 
|  | [&]() -> int { return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap); }); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RenderProxy::disableVsync() { | 
|  | Properties::disableVsync = true; | 
|  | } | 
|  |  | 
|  | void RenderProxy::preload() { | 
|  | // Create RenderThread object and start the thread. Then preload Vulkan/EGL driver. | 
|  | auto& thread = RenderThread::getInstance(); | 
|  | thread.queue().post([&thread]() { thread.preload(); }); | 
|  | } | 
|  |  | 
|  | } /* namespace renderthread */ | 
|  | } /* namespace uirenderer */ | 
|  | } /* namespace android */ |