/* * 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 "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/VectorDrawableAtlas.h" #include "renderstate/RenderState.h" #include "renderthread/CanvasContext.h" #include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/Macros.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" #include <ui/GraphicBuffer.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(const sp<Surface>& surface) { mRenderThread.queue().post( [this, surf = surface]() mutable { mContext->setSurface(std::move(surf)); }); } 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(sp<Surface>& surface, 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(*surface, 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(CLOCK_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::repackVectorDrawableAtlas() { RenderThread& thread = RenderThread::getInstance(); thread.queue().post([&thread]() { // The context may be null if trimMemory executed, but then the atlas was deleted too. if (thread.getGrContext() != nullptr) { thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded( thread.getGrContext()); } }); } void RenderProxy::releaseVDAtlasEntries() { RenderThread& thread = RenderThread::getInstance(); thread.queue().post([&thread]() { // The context may be null if trimMemory executed, but then the atlas was deleted too. if (thread.getGrContext() != nullptr) { thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries(); } }); } 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 */