diff options
author | 2022-11-23 22:39:25 +0000 | |
---|---|---|
committer | 2022-12-02 03:49:59 +0000 | |
commit | e9023cf4cc93e06f8c3cf4441e4ad980c9edf37b (patch) | |
tree | 48fdae1e204c31b46e21f3d71bc0e61920d0c2ce | |
parent | 30adb922a51bd06518adb0e36b8a75b8ba994c08 (diff) |
Refactor HintSessionWrapper and move into CanvasContext
* Move HintSessionWrapper into its own file
* Refactor HintSessionWrapper initialization and lifecycle
* Move HintSessionWrapper instance into CanvasContext
* Have HintSessionWrapper do its own validity checks
* Have HintSessionWrapper implicitly initialize when called
Bug: b/260267128
Bug: b/260224719
Test: manual
Change-Id: I69f89bb5ef601d1b5d9fca1a5da62256f2556da8
-rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
-rw-r--r-- | libs/hwui/FrameInfo.h | 1 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 58 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 48 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 159 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 29 | ||||
-rw-r--r-- | libs/hwui/renderthread/HintSessionWrapper.cpp | 162 | ||||
-rw-r--r-- | libs/hwui/renderthread/HintSessionWrapper.h | 57 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 14 | ||||
-rw-r--r-- | libs/hwui/tests/unit/CanvasContextTests.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/tests/unit/RenderNodeDrawableTests.cpp | 10 | ||||
-rw-r--r-- | libs/hwui/tests/unit/RenderNodeTests.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/tests/unit/SkiaDisplayListTests.cpp | 4 |
13 files changed, 323 insertions, 226 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 88cfed9357d8..fcc0126652bc 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -570,6 +570,7 @@ cc_defaults { "renderthread/VulkanSurface.cpp", "renderthread/RenderProxy.cpp", "renderthread/RenderThread.cpp", + "renderthread/HintSessionWrapper.cpp", "service/GraphicsStatsService.cpp", "thread/CommonPool.cpp", "utils/GLUtils.cpp", diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index 564ee4f53a54..b15b6cb9a9ec 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -104,6 +104,7 @@ public: set(FrameInfoIndex::AnimationStart) = vsyncTime; set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime; set(FrameInfoIndex::DrawStart) = vsyncTime; + set(FrameInfoIndex::FrameStartTime) = vsyncTime; set(FrameInfoIndex::FrameDeadline) = frameDeadline; set(FrameInfoIndex::FrameInterval) = frameInterval; return *this; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index d09bc47cf8fd..64839d0147f8 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -71,16 +71,19 @@ CanvasContext* ScopedActiveContext::sActiveContext = nullptr; } /* namespace */ CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent, - RenderNode* rootRenderNode, IContextFactory* contextFactory) { + RenderNode* rootRenderNode, IContextFactory* contextFactory, + int32_t uiThreadId, int32_t renderThreadId) { auto renderType = Properties::getRenderPipelineType(); switch (renderType) { case RenderPipelineType::SkiaGL: return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, - std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread)); + std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread), + uiThreadId, renderThreadId); case RenderPipelineType::SkiaVulkan: return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, - std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread)); + std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread), + uiThreadId, renderThreadId); default: LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; @@ -110,7 +113,8 @@ void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, - std::unique_ptr<IRenderPipeline> renderPipeline) + std::unique_ptr<IRenderPipeline> renderPipeline, pid_t uiThreadId, + pid_t renderThreadId) : mRenderThread(thread) , mGenerationID(0) , mOpaque(!translucent) @@ -118,7 +122,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mJankTracker(&thread.globalProfileData()) , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos()) , mContentDrawBounds(0, 0, 0, 0) - , mRenderPipeline(std::move(renderPipeline)) { + , mRenderPipeline(std::move(renderPipeline)) + , mHintSessionWrapper(uiThreadId, renderThreadId) { mRenderThread.cacheManager().registerCanvasContext(this); rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); @@ -472,16 +477,22 @@ void CanvasContext::notifyFramePending() { mRenderThread.pushBackFrameCallback(this); } -std::optional<nsecs_t> CanvasContext::draw() { +void CanvasContext::draw() { if (auto grContext = getGrContext()) { if (grContext->abandoned()) { LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw"); - return std::nullopt; + return; } } SkRect dirty; mDamageAccumulator.finish(&dirty); + // reset syncDelayDuration each time we draw + nsecs_t syncDelayDuration = mSyncDelayDuration; + nsecs_t idleDuration = mIdleDuration; + mSyncDelayDuration = 0; + mIdleDuration = 0; + if (!Properties::isDrawingEnabled() || (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); @@ -498,7 +509,7 @@ std::optional<nsecs_t> CanvasContext::draw() { std::invoke(func, false /* didProduceBuffer */); } mFrameCommitCallbacks.clear(); - return std::nullopt; + return; } ScopedActiveContext activeContext(this); @@ -650,10 +661,25 @@ std::optional<nsecs_t> CanvasContext::draw() { } } + int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync); + int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline); + int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration); + + mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync); + + if (didDraw) { + int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); + int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime; + int64_t actualDuration = frameDuration - + (std::min(syncDelayDuration, mLastDequeueBufferDuration)) - + dequeueBufferDuration - idleDuration; + mHintSessionWrapper.reportActualWorkDuration(actualDuration); + } + + mLastDequeueBufferDuration = dequeueBufferDuration; + mRenderThread.cacheManager().onFrameCompleted(); - return didDraw ? std::make_optional( - mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration)) - : std::nullopt; + return; } void CanvasContext::reportMetricsWithPresentTime() { @@ -766,6 +792,8 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro // Called by choreographer to do an RT-driven animation void CanvasContext::doFrame() { if (!mRenderPipeline->isSurfaceReady()) return; + mIdleDuration = + systemTime(SYSTEM_TIME_MONOTONIC) - mRenderThread.timeLord().computeFrameTimeNanos(); prepareAndDraw(nullptr); } @@ -974,6 +1002,14 @@ void CanvasContext::prepareSurfaceControlForWebview() { } } +void CanvasContext::sendLoadResetHint() { + mHintSessionWrapper.sendLoadResetHint(); +} + +void CanvasContext::setSyncDelayDuration(nsecs_t duration) { + mSyncDelayDuration = duration; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index db96cfb978e9..e875c42e9eba 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -16,10 +16,26 @@ #pragma once +#include <SkBitmap.h> +#include <SkRect.h> +#include <SkSize.h> +#include <cutils/compiler.h> +#include <utils/Functor.h> +#include <utils/Mutex.h> + +#include <functional> +#include <future> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "ColorMode.h" #include "DamageAccumulator.h" #include "FrameInfo.h" #include "FrameInfoVisualizer.h" #include "FrameMetricsReporter.h" +#include "HintSessionWrapper.h" #include "IContextFactory.h" #include "IRenderPipeline.h" #include "JankTracker.h" @@ -30,21 +46,6 @@ #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/RingBuffer.h" -#include "ColorMode.h" - -#include <SkBitmap.h> -#include <SkRect.h> -#include <SkSize.h> -#include <cutils/compiler.h> -#include <utils/Functor.h> -#include <utils/Mutex.h> - -#include <functional> -#include <future> -#include <set> -#include <string> -#include <utility> -#include <vector> namespace android { namespace uirenderer { @@ -66,7 +67,8 @@ class Frame; class CanvasContext : public IFrameCallback { public: static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, - IContextFactory* contextFactory); + IContextFactory* contextFactory, pid_t uiThreadId, + pid_t renderThreadId); virtual ~CanvasContext(); /** @@ -138,7 +140,7 @@ public: bool makeCurrent(); void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target); // Returns the DequeueBufferDuration. - std::optional<nsecs_t> draw(); + void draw(); void destroy(); // IFrameCallback, Choreographer-driven frame callback entry point @@ -214,9 +216,14 @@ public: static CanvasContext* getActiveContext(); + void sendLoadResetHint(); + + void setSyncDelayDuration(nsecs_t duration); + private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, - IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline); + IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline, + pid_t uiThreadId, pid_t renderThreadId); friend class RegisterFrameCallbackTask; // TODO: Replace with something better for layer & other GL object @@ -330,6 +337,11 @@ private: std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback; std::function<void()> mPrepareSurfaceControlForWebviewCallback; + + HintSessionWrapper mHintSessionWrapper; + nsecs_t mLastDequeueBufferDuration = 0; + nsecs_t mSyncDelayDuration = 0; + nsecs_t mIdleDuration = 0; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index cb306144ffd6..1cc82fd0ff64 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -16,7 +16,6 @@ #include "DrawFrameTask.h" -#include <dlfcn.h> #include <gui/TraceUtils.h> #include <utils/Log.h> @@ -28,70 +27,11 @@ #include "../RenderNode.h" #include "CanvasContext.h" #include "RenderThread.h" -#include "thread/CommonPool.h" -#include "utils/TimeUtils.h" namespace android { namespace uirenderer { namespace renderthread { -namespace { - -typedef APerformanceHintManager* (*APH_getManager)(); -typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*, - size_t, int64_t); -typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t); -typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); -typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t); -typedef void (*APH_closeSession)(APerformanceHintSession* session); - -bool gAPerformanceHintBindingInitialized = false; -APH_getManager gAPH_getManagerFn = nullptr; -APH_createSession gAPH_createSessionFn = nullptr; -APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr; -APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; -APH_sendHint gAPH_sendHintFn = nullptr; -APH_closeSession gAPH_closeSessionFn = nullptr; - -void ensureAPerformanceHintBindingInitialized() { - if (gAPerformanceHintBindingInitialized) return; - - void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); - LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!"); - - gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager"); - LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr, - "Failed to find required symbol APerformanceHint_getManager!"); - - gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); - LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr, - "Failed to find required symbol APerformanceHint_createSession!"); - - gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym( - handle_, "APerformanceHint_updateTargetWorkDuration"); - LOG_ALWAYS_FATAL_IF( - gAPH_updateTargetWorkDurationFn == nullptr, - "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!"); - - gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym( - handle_, "APerformanceHint_reportActualWorkDuration"); - LOG_ALWAYS_FATAL_IF( - gAPH_reportActualWorkDurationFn == nullptr, - "Failed to find required symbol APerformanceHint_reportActualWorkDuration!"); - - gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint"); - LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr, - "Failed to find required symbol APerformanceHint_sendHint!"); - - gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); - LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr, - "Failed to find required symbol APerformanceHint_closeSession!"); - - gAPerformanceHintBindingInitialized = true; -} - -} // namespace - DrawFrameTask::DrawFrameTask() : mRenderThread(nullptr) , mContext(nullptr) @@ -100,13 +40,11 @@ DrawFrameTask::DrawFrameTask() DrawFrameTask::~DrawFrameTask() {} -void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode, - int32_t uiThreadId, int32_t renderThreadId) { +void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, + RenderNode* targetNode) { mRenderThread = thread; mContext = context; mTargetNode = targetNode; - mUiThreadId = uiThreadId; - mRenderThreadId = renderThreadId; } void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) { @@ -150,11 +88,11 @@ void DrawFrameTask::postAndWait() { void DrawFrameTask::run() { const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)]; ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId); - nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued; + + mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued); bool canUnblockUiThread; bool canDrawThisFrame; - bool didDraw = false; { TreeInfo info(TreeInfo::MODE_FULL, *mContext); info.forceDrawFrame = mForceDrawFrame; @@ -175,9 +113,6 @@ void DrawFrameTask::run() { std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback); mFrameCallback = nullptr; mFrameCompleteCallback = nullptr; - int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)]; - int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)]; - int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)]; // From this point on anything in "this" is *UNSAFE TO ACCESS* if (canUnblockUiThread) { @@ -188,18 +123,15 @@ void DrawFrameTask::run() { if (CC_UNLIKELY(frameCallback)) { context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult, frameNr = context->getFrameNumber()]() { - auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr)); + auto frameCommitCallback = frameCallback(syncResult, frameNr); if (frameCommitCallback) { context->addFrameCommitListener(std::move(frameCommitCallback)); } }); } - nsecs_t dequeueBufferDuration = 0; if (CC_LIKELY(canDrawThisFrame)) { - std::optional<nsecs_t> drawResult = context->draw(); - didDraw = drawResult.has_value(); - dequeueBufferDuration = drawResult.value_or(0); + context->draw(); } else { // Do a flush in case syncFrameState performed any texture uploads. Since we skipped // the draw() call, those uploads (or deletes) will end up sitting in the queue. @@ -218,41 +150,6 @@ void DrawFrameTask::run() { if (!canUnblockUiThread) { unblockUiThread(); } - - if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId); - - constexpr int64_t kSanityCheckLowerBound = 100_us; - constexpr int64_t kSanityCheckUpperBound = 10_s; - int64_t targetWorkDuration = frameDeadline - intendedVsync; - targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100; - if (targetWorkDuration > kSanityCheckLowerBound && - targetWorkDuration < kSanityCheckUpperBound && - targetWorkDuration != mLastTargetWorkDuration) { - mLastTargetWorkDuration = targetWorkDuration; - mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration); - } - - if (didDraw) { - int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime; - int64_t actualDuration = frameDuration - - (std::min(syncDelayDuration, mLastDequeueBufferDuration)) - - dequeueBufferDuration; - if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) { - mHintSessionWrapper->reportActualWorkDuration(actualDuration); - } - } - - mLastDequeueBufferDuration = dequeueBufferDuration; -} - -void DrawFrameTask::sendLoadResetHint() { - if (!(Properties::useHintManager && Properties::isDrawingEnabled())) return; - if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId); - nsecs_t now = systemTime(); - if (now - mLastFrameNotification > kResetHintTimeout) { - mHintSessionWrapper->sendHint(SessionHint::CPU_LOAD_RESET); - } - mLastFrameNotification = now; } bool DrawFrameTask::syncFrameState(TreeInfo& info) { @@ -305,50 +202,6 @@ void DrawFrameTask::unblockUiThread() { mSignal.signal(); } -DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) { - if (!Properties::useHintManager) return; - if (uiThreadId < 0 || renderThreadId < 0) return; - - ensureAPerformanceHintBindingInitialized(); - - APerformanceHintManager* manager = gAPH_getManagerFn(); - if (!manager) return; - - std::vector<int32_t> tids = CommonPool::getThreadIds(); - tids.push_back(uiThreadId); - tids.push_back(renderThreadId); - - // DrawFrameTask code will always set a target duration before reporting actual durations. - // So this is just a placeholder value that's never used. - int64_t dummyTargetDurationNanos = 16666667; - mHintSession = - gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos); -} - -DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() { - if (mHintSession) { - gAPH_closeSessionFn(mHintSession); - } -} - -void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) { - if (mHintSession) { - gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos); - } -} - -void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) { - if (mHintSession) { - gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos); - } -} - -void DrawFrameTask::HintSessionWrapper::sendHint(SessionHint hint) { - if (mHintSession && Properties::isDrawingEnabled()) { - gAPH_sendHintFn(mHintSession, static_cast<int>(hint)); - } -} - } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 7eae41c07e64..fafab24cbce7 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -16,7 +16,6 @@ #ifndef DRAWFRAMETASK_H #define DRAWFRAMETASK_H -#include <android/performance_hint.h> #include <utils/Condition.h> #include <utils/Mutex.h> #include <utils/StrongPointer.h> @@ -28,7 +27,6 @@ #include "../Rect.h" #include "../TreeInfo.h" #include "RenderTask.h" -#include "utils/TimeUtils.h" namespace android { namespace uirenderer { @@ -62,8 +60,7 @@ public: DrawFrameTask(); virtual ~DrawFrameTask(); - void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode, - int32_t uiThreadId, int32_t renderThreadId); + void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode); void setContentDrawBounds(int left, int top, int right, int bottom) { mContentDrawBounds.set(left, top, right, bottom); } @@ -91,22 +88,7 @@ public: void forceDrawNextFrame() { mForceDrawFrame = true; } - void sendLoadResetHint(); - private: - class HintSessionWrapper { - public: - HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId); - ~HintSessionWrapper(); - - void updateTargetWorkDuration(long targetDurationNanos); - void reportActualWorkDuration(long actualDurationNanos); - void sendHint(SessionHint hint); - - private: - APerformanceHintSession* mHintSession = nullptr; - }; - void postAndWait(); bool syncFrameState(TreeInfo& info); void unblockUiThread(); @@ -117,8 +99,6 @@ private: RenderThread* mRenderThread; CanvasContext* mContext; RenderNode* mTargetNode = nullptr; - int32_t mUiThreadId = -1; - int32_t mRenderThreadId = -1; Rect mContentDrawBounds; /********************************************* @@ -135,13 +115,6 @@ private: std::function<void(bool)> mFrameCommitCallback; std::function<void()> mFrameCompleteCallback; - nsecs_t mLastDequeueBufferDuration = 0; - nsecs_t mLastTargetWorkDuration = 0; - std::optional<HintSessionWrapper> mHintSessionWrapper; - - nsecs_t mLastFrameNotification = 0; - nsecs_t kResetHintTimeout = 100_ms; - bool mForceDrawFrame = false; }; diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp new file mode 100644 index 000000000000..edacef04b50a --- /dev/null +++ b/libs/hwui/renderthread/HintSessionWrapper.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2022 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 "HintSessionWrapper.h" + +#include <dlfcn.h> +#include <utils/Log.h> + +#include <vector> + +#include "../Properties.h" +#include "thread/CommonPool.h" + +namespace android { +namespace uirenderer { +namespace renderthread { + +namespace { + +typedef APerformanceHintManager* (*APH_getManager)(); +typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*, + size_t, int64_t); +typedef void (*APH_closeSession)(APerformanceHintSession* session); +typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t); +typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); +typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t); + +bool gAPerformanceHintBindingInitialized = false; +APH_getManager gAPH_getManagerFn = nullptr; +APH_createSession gAPH_createSessionFn = nullptr; +APH_closeSession gAPH_closeSessionFn = nullptr; +APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr; +APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; +APH_sendHint gAPH_sendHintFn = nullptr; + +void ensureAPerformanceHintBindingInitialized() { + if (gAPerformanceHintBindingInitialized) return; + + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!"); + + gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager"); + LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr, + "Failed to find required symbol APerformanceHint_getManager!"); + + gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); + LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr, + "Failed to find required symbol APerformanceHint_createSession!"); + + gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); + LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr, + "Failed to find required symbol APerformanceHint_closeSession!"); + + gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym( + handle_, "APerformanceHint_updateTargetWorkDuration"); + LOG_ALWAYS_FATAL_IF( + gAPH_updateTargetWorkDurationFn == nullptr, + "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!"); + + gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym( + handle_, "APerformanceHint_reportActualWorkDuration"); + LOG_ALWAYS_FATAL_IF( + gAPH_reportActualWorkDurationFn == nullptr, + "Failed to find required symbol APerformanceHint_reportActualWorkDuration!"); + + gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint"); + LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr, + "Failed to find required symbol APerformanceHint_sendHint!"); + + gAPerformanceHintBindingInitialized = true; +} + +} // namespace + +HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId) + : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {} + +HintSessionWrapper::~HintSessionWrapper() { + if (mHintSession) { + gAPH_closeSessionFn(mHintSession); + } +} + +bool HintSessionWrapper::useHintSession() { + if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false; + if (mHintSession) return true; + // If session does not exist, create it; + // this defers session creation until we try to actually use it. + if (!mSessionValid) return false; + return init(); +} + +bool HintSessionWrapper::init() { + if (mUiThreadId < 0 || mRenderThreadId < 0) return false; + + // Assume that if we return before the end, it broke + mSessionValid = false; + + ensureAPerformanceHintBindingInitialized(); + + APerformanceHintManager* manager = gAPH_getManagerFn(); + if (!manager) return false; + + std::vector<pid_t> tids = CommonPool::getThreadIds(); + tids.push_back(mUiThreadId); + tids.push_back(mRenderThreadId); + + // Use a placeholder target value to initialize, + // this will always be replaced elsewhere before it gets used + int64_t defaultTargetDurationNanos = 16666667; + mHintSession = + gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos); + + mSessionValid = !!mHintSession; + return mSessionValid; +} + +void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) { + if (!useHintSession()) return; + targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100; + if (targetWorkDurationNanos != mLastTargetWorkDuration && + targetWorkDurationNanos > kSanityCheckLowerBound && + targetWorkDurationNanos < kSanityCheckUpperBound) { + mLastTargetWorkDuration = targetWorkDurationNanos; + gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos); + } + mLastFrameNotification = systemTime(); +} + +void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) { + if (!useHintSession()) return; + if (actualDurationNanos > kSanityCheckLowerBound && + actualDurationNanos < kSanityCheckUpperBound) { + gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos); + } +} + +void HintSessionWrapper::sendLoadResetHint() { + if (!useHintSession()) return; + nsecs_t now = systemTime(); + if (now - mLastFrameNotification > kResetHintTimeout) { + gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET)); + } + mLastFrameNotification = now; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h new file mode 100644 index 000000000000..fcbc10185255 --- /dev/null +++ b/libs/hwui/renderthread/HintSessionWrapper.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once + +#include <android/performance_hint.h> + +#include "utils/TimeUtils.h" + +namespace android { +namespace uirenderer { + +namespace renderthread { + +class HintSessionWrapper { +public: + HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId); + ~HintSessionWrapper(); + + void updateTargetWorkDuration(long targetDurationNanos); + void reportActualWorkDuration(long actualDurationNanos); + void sendLoadResetHint(); + +private: + bool useHintSession(); + bool init(); + APerformanceHintSession* mHintSession = nullptr; + + nsecs_t mLastFrameNotification = 0; + nsecs_t mLastTargetWorkDuration = 0; + + pid_t mUiThreadId; + pid_t mRenderThreadId; + + bool mSessionValid = true; + + static constexpr nsecs_t kResetHintTimeout = 100_ms; + static constexpr int64_t kSanityCheckLowerBound = 100_us; + static constexpr int64_t kSanityCheckUpperBound = 10_s; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 03a2bc9f7e51..07f5a78fc1ec 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -42,11 +42,13 @@ 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); + pid_t uiThreadId = pthread_gettid_np(pthread_self()); + pid_t renderThreadId = getRenderThreadTid(); + mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* { + return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory, + uiThreadId, renderThreadId); }); - mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode, - pthread_gettid_np(pthread_self()), getRenderThreadTid()); + mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode); } RenderProxy::~RenderProxy() { @@ -55,7 +57,7 @@ RenderProxy::~RenderProxy() { void RenderProxy::destroyContext() { if (mContext) { - mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1); + 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; }); @@ -237,7 +239,7 @@ void RenderProxy::notifyFramePending() { } void RenderProxy::notifyCallbackPending() { - mDrawFrameTask.sendLoadResetHint(); + mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); }); } void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp index 1771c3590e10..88420a5d5c23 100644 --- a/libs/hwui/tests/unit/CanvasContextTests.cpp +++ b/libs/hwui/tests/unit/CanvasContextTests.cpp @@ -36,7 +36,7 @@ RENDERTHREAD_TEST(CanvasContext, create) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0)); ASSERT_FALSE(canvasContext->hasSurface()); diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index ec949b80ea55..67b4d2c1a4e4 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -334,7 +334,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { "A"); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); + CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; @@ -398,7 +398,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { "A"); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); + CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; @@ -518,7 +518,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { // prepareTree is required to find, which receivers have backward projected nodes ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); + CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; @@ -618,7 +618,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { // prepareTree is required to find, which receivers have backward projected nodes ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); + CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; @@ -634,7 +634,7 @@ namespace { static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) { ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory)); + CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index 61bd646b0a76..80796f4a4111 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -274,7 +274,7 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; @@ -310,7 +310,7 @@ RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) { }); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0)); canvasContext->setSurface(nullptr); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 3d5aca4bf05a..f825d7c5d9cc 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -142,7 +142,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0)); TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; @@ -201,7 +201,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( - CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0)); // Set up a Surface so that we can position the VectorDrawable offscreen. test::TestContext testContext; |