| /* | |
| * 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 <gui/TraceUtils.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 <pthread.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, | |
| pthread_gettid_np(pthread_self()), getRenderThreadTid()); | |
| } | |
| RenderProxy::~RenderProxy() { | |
| destroyContext(); | |
| } | |
| void RenderProxy::destroyContext() { | |
| if (mContext) { | |
| mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1); | |
| // 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) { | |
| 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([=]() { 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::setColorMode(ColorMode mode) { | |
| mRenderThread.queue().post([=]() { mContext->setColorMode(mode); }); | |
| } | |
| 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::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::purgeCaches() { | |
| if (RenderThread::hasInstance()) { | |
| RenderThread& thread = RenderThread::getInstance(); | |
| thread.queue().post([&thread]() { | |
| if (thread.getGrContext()) { | |
| thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::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::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([=]() { | |
| 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) { | |
| if (RenderThread::hasInstance()) { | |
| auto& thread = RenderThread::getInstance(); | |
| thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd, includeProfileData); }); | |
| } | |
| } | |
| 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() { | |
| 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::setASurfaceTransactionCallback( | |
| const std::function<bool(int64_t, int64_t, int64_t)>& callback) { | |
| mRenderThread.queue().post( | |
| [this, cb = callback]() { mContext->setASurfaceTransactionCallback(cb); }); | |
| } | |
| void RenderProxy::setPrepareSurfaceControlForWebviewCallback( | |
| const std::function<void()>& callback) { | |
| mRenderThread.queue().post( | |
| [this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(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); }); | |
| } | |
| 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); }); | |
| } | |
| } | |
| int RenderProxy::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) { | |
| 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().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(); }); | |
| } | |
| } /* namespace renderthread */ | |
| } /* namespace uirenderer */ | |
| } /* namespace android */ |