/* * 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 #include #include #include #include #include #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" namespace android { namespace uirenderer { namespace renderthread { RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) : mRenderThread(RenderThread::getInstance()), mContext(nullptr) { #ifdef __ANDROID__ pid_t uiThreadId = pthread_gettid_np(pthread_self()); #else pid_t uiThreadId = 0; #endif pid_t renderThreadId = getRenderThreadTid(); mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* { CanvasContext* context = CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory, uiThreadId, renderThreadId); if (context != nullptr) { mRenderThread.queue().post([=] { context->startHintSession(); }); } return context; }); 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::setHardwareBuffer(AHardwareBuffer* buffer) { #ifdef __ANDROID__ if (buffer) { AHardwareBuffer_acquire(buffer); } mRenderThread.queue().post([this, hardwareBuffer = buffer]() mutable { mContext->setHardwareBuffer(hardwareBuffer); if (hardwareBuffer) { AHardwareBuffer_release(hardwareBuffer); } }); #endif } void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { if (window) { ANativeWindow_acquire(window); } mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { mContext->setSurface(win, enableTimeout); if (win) { ANativeWindow_release(win); } }); } void RenderProxy::setSurfaceControl(ASurfaceControl* surfaceControl) { auto funcs = mRenderThread.getASurfaceControlFunctions(); if (surfaceControl) { funcs.acquireFunc(surfaceControl); } mRenderThread.queue().post([this, control = surfaceControl, funcs]() mutable { mContext->setSurfaceControl(control); if (control) { funcs.releaseFunc(control); } }); } void RenderProxy::allocateBuffers() { mRenderThread.queue().post([this]() { 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( [=, this]() { mContext->setLightAlpha(ambientShadowAlpha, spotShadowAlpha); }); } void RenderProxy::setLightGeometry(const Vector3& lightCenter, float lightRadius) { mRenderThread.queue().post( [=, this]() { mContext->setLightGeometry(lightCenter, lightRadius); }); } void RenderProxy::setOpaque(bool opaque) { mRenderThread.queue().post([=, this]() { mContext->setOpaque(opaque); }); } float RenderProxy::setColorMode(ColorMode mode) { // We only need to figure out what the renderer supports for HDR, otherwise this can stay // an async call since we already know the return value if (mode == ColorMode::Hdr || mode == ColorMode::Hdr10) { return mRenderThread.queue().runSync( [=, this]() -> float { return mContext->setColorMode(mode); }); } else { mRenderThread.queue().post([=, this]() { mContext->setColorMode(mode); }); return 1.f; } } void RenderProxy::setRenderSdrHdrRatio(float ratio) { mDrawFrameTask.setRenderSdrHdrRatio(ratio); } int64_t* RenderProxy::frameInfo() { return mDrawFrameTask.frameInfo(); } void RenderProxy::forceDrawNextFrame() { mDrawFrameTask.forceDrawNextFrame(); } 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([this]() { mContext->destroy(); }); } 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) { #ifdef __ANDROID__ return mRenderThread.queue().runSync([&]() { layer->detachSurfaceTexture(); }); #endif } 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(); const auto trimLevel = static_cast(level); thread.queue().post([&thread, trimLevel]() { thread.trimMemory(trimLevel); }); } } void RenderProxy::trimCaches(int level) { // Avoid creating a RenderThread to do a trimMemory. if (RenderThread::hasInstance()) { RenderThread& thread = RenderThread::getInstance(); const auto trimLevel = static_cast(level); thread.queue().post([&thread, trimLevel]() { thread.trimCaches(trimLevel); }); } } void RenderProxy::purgeCaches() { if (RenderThread::hasInstance()) { RenderThread& thread = RenderThread::getInstance(); thread.queue().post([&thread]() { if (thread.getGrContext()) { thread.cacheManager().trimMemory(TrimLevel::COMPLETE); } }); } } 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::notifyCallbackPending() { mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); }); } void RenderProxy::notifyExpensiveFrame() { mRenderThread.queue().post([this]() { mContext->sendLoadIncreaseHint(); }); } void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { mRenderThread.queue().runSync([&]() { std::lock_guard lock(mRenderThread.getJankDataMutex()); 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([this]() { std::lock_guard lock(mRenderThread.getJankDataMutex()); mContext->resetFrameStats(); }); } uint32_t RenderProxy::frameTimePercentile(int percentile) { return mRenderThread.queue().runSync([&]() -> auto { std::lock_guard lock(mRenderThread.globalProfileData().getDataMutex()); return mRenderThread.globalProfileData()->findPercentile(percentile); }); } void RenderProxy::dumpGraphicsMemory(int fd, bool includeProfileData, bool resetProfile) { if (RenderThread::hasInstance()) { auto& thread = RenderThread::getInstance(); thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd, includeProfileData); if (resetProfile) { thread.globalProfileData()->reset(); } }); } #ifdef __ANDROID__ if (!Properties::isolatedProcess) { std::string grallocInfo; GraphicBufferAllocator::getInstance().dump(grallocInfo); dprintf(fd, "%s\n", grallocInfo.c_str()); } #endif } void RenderProxy::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) { if (RenderThread::hasInstance()) { auto& thread = RenderThread::getInstance(); thread.queue().runSync([&]() { thread.getMemoryUsage(cpuUsage, gpuUsage); }); } } 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() { #ifdef __ANDROID__ return mRenderThread.getTid(); #else return 0; #endif } void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) { mRenderThread.queue().post([=, this]() { mContext->addRenderNode(node, placeFront); }); } void RenderProxy::removeRenderNode(RenderNode* node) { mRenderThread.queue().post([=, this]() { mContext->removeRenderNode(node); }); } void RenderProxy::drawRenderNode(RenderNode* node) { mRenderThread.queue().runSync([=, this]() { mContext->prepareAndDraw(node); }); } void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) { mDrawFrameTask.setContentDrawBounds(left, top, right, bottom); } void RenderProxy::setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) { mDrawFrameTask.setHardwareBufferRenderParams(params); } void RenderProxy::setPictureCapturedCallback( const std::function&&)>& callback) { mRenderThread.queue().post( [this, cb = callback]() { mContext->setPictureCapturedCallback(cb); }); } void RenderProxy::setASurfaceTransactionCallback( const std::function& callback) { mRenderThread.queue().post( [this, cb = callback]() { mContext->setASurfaceTransactionCallback(cb); }); } void RenderProxy::setPrepareSurfaceControlForWebviewCallback( const std::function& callback) { mRenderThread.queue().post( [this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(cb); }); } void RenderProxy::setFrameCallback( std::function(int32_t, int64_t)>&& callback) { mDrawFrameTask.setFrameCallback(std::move(callback)); } void RenderProxy::setFrameCommitCallback(std::function&& callback) { mDrawFrameTask.setFrameCommitCallback(std::move(callback)); } void RenderProxy::setFrameCompleteCallback(std::function&& 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(ForceDarkType type) { mRenderThread.queue().post([this, type]() { mContext->setForceDark(type); }); } void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr&& request) { auto& thread = RenderThread::getInstance(); ANativeWindow_acquire(window); thread.queue().post([&thread, window, request = std::move(request)] { thread.readback().copySurfaceInto(window, request); ANativeWindow_release(window); }); } 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 (RenderThread::isCurrent()) { // 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); }); } } int RenderProxy::copyImageInto(const sk_sp& image, SkBitmap* bitmap) { RenderThread& thread = RenderThread::getInstance(); if (RenderThread::isCurrent()) { // TODO: fix everything that hits this. We should never be triggering a readback ourselves. return (int)thread.readback().copyImageInto(image, bitmap); } else { return thread.queue().runSync( [&]() -> int { return (int)thread.readback().copyImageInto(image, 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(); }); } void RenderProxy::setRtAnimationsEnabled(bool enabled) { if (RenderThread::hasInstance()) { RenderThread::getInstance().queue().post( [enabled]() { Properties::enableRTAnimations = enabled; }); } else { Properties::enableRTAnimations = enabled; } } } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */