| /* | |
| * 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 <SkBitmap.h> | |
| #include <SkImage.h> | |
| #include <SkPicture.h> | |
| #include <gui/TraceUtils.h> | |
| #include <pthread.h> | |
| #ifdef __ANDROID__ | |
| #include <gui/SurfaceControl.h> | |
| #endif | |
| #include <ui/GraphicBufferAllocator.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" | |
| 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(sp<SurfaceControl> surfaceControl) { | |
| #ifdef __ANDROID__ | |
| mRenderThread.queue().post([this, control = std::move(surfaceControl)]() mutable { | |
| mContext->setSurfaceControl(std::move(control)); | |
| }); | |
| #endif | |
| } | |
| 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("RenderProxy#copyLayerInto readback"); | |
| 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<TrimLevel>(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<CacheTrimLevel>(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<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<std::function<void(bool)>(int32_t, int64_t)>&& callback) { | |
| mDrawFrameTask.setFrameCallback(std::move(callback)); | |
| } | |
| void RenderProxy::setFrameCommitCallback(std::function<void(bool)>&& callback) { | |
| mDrawFrameTask.setFrameCommitCallback(std::move(callback)); | |
| } | |
| void RenderProxy::setFrameCompleteCallback(std::function<void()>&& callback) { | |
| mDrawFrameTask.setFrameCompleteCallback(std::move(callback)); | |
| } | |
| void RenderProxy::addFrameMetricsObserver(sp<FrameMetricsObserver>&& observer) { | |
| mRenderThread.queue().post([this, observer = std::move(observer)]() mutable { | |
| mContext->addFrameMetricsObserver(std::move(observer)); | |
| }); | |
| } | |
| void RenderProxy::removeFrameMetricsObserver(sp<FrameMetricsObserver>&& observer) { | |
| mRenderThread.queue().post([this, observer = std::move(observer)]() { | |
| mContext->removeFrameMetricsObserver(observer); | |
| }); | |
| } | |
| void RenderProxy::setForceDark(ForceDarkType type) { | |
| mRenderThread.queue().post([this, type]() { mContext->setForceDark(type); }); | |
| } | |
| void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& 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("RenderProxy#copyHWBitmapInto 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<SkImage>& image, SkBitmap* bitmap) { | |
| ATRACE_NAME("RenderProxy#copyImageInto 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().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 */ |