/* * 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 "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 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 = false; if (Caches::hasInstance()) { 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::initialize(const sp& surface) { mRenderThread.queue().post( [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); }); } void RenderProxy::updateSurface(const sp& surface) { mRenderThread.queue().post( [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); }); } bool RenderProxy::pauseSurface(const sp& surface) { return mRenderThread.queue().runSync([this]() -> bool { return mContext->pauseSurface(); }); } void RenderProxy::setStopped(bool stopped) { mRenderThread.queue().runSync([this, stopped]() { mContext->setStopped(stopped); }); } void RenderProxy::setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { mRenderThread.queue().post( [=]() { mContext->setup(lightRadius, ambientShadowAlpha, spotShadowAlpha); }); } void RenderProxy::setLightCenter(const Vector3& lightCenter) { mRenderThread.queue().post([=]() { mContext->setLightCenter(lightCenter); }); } 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)); } } 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) { return mRenderThread.queue().runSync( [&]() -> bool { return mContext->copyLayerInto(layer, &bitmap); }); } 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([]() {}); } void RenderProxy::staticFence() { RenderThread::getInstance().queue().runSync([]() {}); } 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) { 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::setFrameCallback(std::function&& callback) { mDrawFrameTask.setFrameCallback(std::move(callback)); } void RenderProxy::setFrameCompleteCallback(std::function&& callback) { mDrawFrameTask.setFrameCompleteCallback(std::move(callback)); } void RenderProxy::serializeDisplayListTree() { mRenderThread.queue().post([=]() { mContext->serializeDisplayListTree(); }); } 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()); }); } int RenderProxy::copySurfaceInto(sp& surface, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); return static_cast(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); } } sk_sp RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) { auto& thread = RenderThread::getInstance(); return thread.queue().runSync([&]() -> auto { return thread.allocateHardwareBitmap(bitmap); }); } int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) { RenderThread& thread = RenderThread::getInstance(); if (Properties::isSkiaEnabled() && gettid() == thread.getTid()) { // TODO: fix everything that hits this. We should never be triggering a readback ourselves. return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap); } else { return thread.queue().runSync([&]() -> int { return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap); }); } } void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) { if (!RenderThread::hasInstance()) return; RenderThread& thread = RenderThread::getInstance(); thread.queue().post( [&thread, pixelRefId]() { thread.renderState().onBitmapDestroyed(pixelRefId); }); } 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(); } }); } } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */