diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 5c65c659e5766..7b3a8a64fc37d 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -737,7 +737,7 @@ public final class Choreographer { } mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id, - vsyncEventData.frameDeadline); + vsyncEventData.frameDeadline, startNanos); mFrameScheduled = false; mLastFrameTimeNanos = frameTimeNanos; mLastVsyncEventData = vsyncEventData; diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java index eb49e52d50503..9cdf91a55c39d 100644 --- a/core/java/android/view/FrameMetrics.java +++ b/core/java/android/view/FrameMetrics.java @@ -242,18 +242,19 @@ public final class FrameMetrics { int PERFORM_TRAVERSALS_START = 7; int DRAW_START = 8; int FRAME_DEADLINE = 9; - int SYNC_QUEUED = 10; - int SYNC_START = 11; - int ISSUE_DRAW_COMMANDS_START = 12; - int SWAP_BUFFERS = 13; - int FRAME_COMPLETED = 14; - int DEQUEUE_BUFFER_DURATION = 15; - int QUEUE_BUFFER_DURATION = 16; - int GPU_COMPLETED = 17; - int SWAP_BUFFERS_COMPLETED = 18; - int DISPLAY_PRESENT_TIME = 19; + int FRAME_START_TIME = 10; + int SYNC_QUEUED = 11; + int SYNC_START = 12; + int ISSUE_DRAW_COMMANDS_START = 13; + int SWAP_BUFFERS = 14; + int FRAME_COMPLETED = 15; + int DEQUEUE_BUFFER_DURATION = 16; + int QUEUE_BUFFER_DURATION = 17; + int GPU_COMPLETED = 18; + int SWAP_BUFFERS_COMPLETED = 19; + int DISPLAY_PRESENT_TIME = 20; - int FRAME_STATS_COUNT = 20; // must always be last and in sync with + int FRAME_STATS_COUNT = 21; // must always be last and in sync with // FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h } diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java index 189be53a397f1..786c03ba51505 100644 --- a/graphics/java/android/graphics/FrameInfo.java +++ b/graphics/java/android/graphics/FrameInfo.java @@ -87,18 +87,22 @@ public final class FrameInfo { // When the frame needs to be ready by public static final int FRAME_DEADLINE = 9; + // When frame actually started. + public static final int FRAME_START_TIME = 10; + // Must be the last one // This value must be in sync with `UI_THREAD_FRAME_INFO_SIZE` in FrameInfo.h - private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1; + private static final int FRAME_INFO_SIZE = FRAME_START_TIME + 1; /** checkstyle */ public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId, - long frameDeadline) { + long frameDeadline, long frameStartTime) { frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId; frameInfo[INTENDED_VSYNC] = intendedVsync; frameInfo[VSYNC] = usedVsync; frameInfo[FLAGS] = 0; frameInfo[FRAME_DEADLINE] = frameDeadline; + frameInfo[FRAME_START_TIME] = frameStartTime; } /** checkstyle */ diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 88cf96a9eb4ea..7589435fc3b7a 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -28,6 +28,7 @@ import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.PerformanceHintManager; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; @@ -165,7 +166,7 @@ public class HardwareRenderer { * to opaque with no light source configured. */ public HardwareRenderer() { - ProcessInitializer.sInstance.initDisplayInfo(); + ProcessInitializer.sInstance.initUsingContext(); mRootNode = RenderNode.adopt(nCreateRootRenderNode()); mRootNode.setClipToBounds(false); mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode); @@ -365,7 +366,8 @@ public class HardwareRenderer { */ public @NonNull FrameRenderRequest setVsyncTime(long vsyncTime) { // TODO(b/168552873): populate vsync Id once available to Choreographer public API - mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE); + mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, + vsyncTime); mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS); return this; } @@ -835,6 +837,36 @@ public class HardwareRenderer { callback.onPictureCaptured(picture); } + /** called by native */ + static PerformanceHintManager.Session createHintSession(int[] tids) { + PerformanceHintManager performanceHintManager = + ProcessInitializer.sInstance.getHintManager(); + if (performanceHintManager == null) { + return null; + } + // Native code will always set a target duration before reporting actual durations. + // So this is just a placeholder value that's never used. + long targetDurationNanos = 16666667; + return performanceHintManager.createHintSession(tids, targetDurationNanos); + } + + /** called by native */ + static void updateTargetWorkDuration(PerformanceHintManager.Session session, + long targetDurationNanos) { + session.updateTargetWorkDuration(targetDurationNanos); + } + + /** called by native */ + static void reportActualWorkDuration(PerformanceHintManager.Session session, + long actualDurationNanos) { + session.reportActualWorkDuration(actualDurationNanos); + } + + /** called by native */ + static void closeHintSession(PerformanceHintManager.Session session) { + session.close(); + } + /** * Interface used to receive callbacks when a frame is being drawn. * @@ -1071,6 +1103,7 @@ public class HardwareRenderer { private boolean mIsolated = false; private Context mContext; private String mPackageName; + private PerformanceHintManager mPerformanceHintManager; private IGraphicsStats mGraphicsStatsService; private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() { @Override @@ -1082,6 +1115,10 @@ public class HardwareRenderer { private ProcessInitializer() { } + synchronized PerformanceHintManager getHintManager() { + return mPerformanceHintManager; + } + synchronized void setPackageName(String name) { if (mInitialized) return; mPackageName = name; @@ -1127,15 +1164,23 @@ public class HardwareRenderer { } } - synchronized void initDisplayInfo() { - if (mDisplayInitialized) return; + synchronized void initUsingContext() { if (mContext == null) return; - // If we're in an isolated sandbox mode then we shouldn't try to communicate with DMS + initDisplayInfo(); + + // HintManager and HintSession are designed to be accessible from isoalted processes + // so not checking for isolated process here. + initHintSession(); + + // Defensively clear out the context in case we were passed a context that can leak + // if we live longer than it, e.g. an activity context. + mContext = null; + } + + private void initDisplayInfo() { + if (mDisplayInitialized) return; if (mIsolated) { - // Defensively clear out the context in case we were passed a context that can leak - // if we live longer than it, e.g. an activity context. - mContext = null; mDisplayInitialized = true; return; } @@ -1167,11 +1212,14 @@ public class HardwareRenderer { display.getRefreshRate(), wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos()); - // Defensively clear out the context - mContext = null; mDisplayInitialized = true; } + private void initHintSession() { + if (mContext == null) return; + mPerformanceHintManager = mContext.getSystemService(PerformanceHintManager.class); + } + private void rotateBuffer() { nRotateProcessStatsBuffer(); requestBuffer(); diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index 2448cc9041043..51fbf363f51c1 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -21,29 +21,16 @@ namespace android { namespace uirenderer { const std::array FrameInfoNames{ - "Flags", - "FrameTimelineVsyncId", - "IntendedVsync", - "Vsync", - "InputEventId", - "HandleInputStart", - "AnimationStart", - "PerformTraversalsStart", - "DrawStart", - "FrameDeadline", - "SyncQueued", - "SyncStart", - "IssueDrawCommandsStart", - "SwapBuffers", - "FrameCompleted", - "DequeueBufferDuration", - "QueueBufferDuration", - "GpuCompleted", - "SwapBuffersCompleted", - "DisplayPresentTime", + "Flags", "FrameTimelineVsyncId", "IntendedVsync", + "Vsync", "InputEventId", "HandleInputStart", + "AnimationStart", "PerformTraversalsStart", "DrawStart", + "FrameDeadline", "FrameStartTime", "SyncQueued", + "SyncStart", "IssueDrawCommandsStart", "SwapBuffers", + "FrameCompleted", "DequeueBufferDuration", "QueueBufferDuration", + "GpuCompleted", "SwapBuffersCompleted", "DisplayPresentTime", }; -static_assert(static_cast(FrameInfoIndex::NumIndexes) == 20, +static_assert(static_cast(FrameInfoIndex::NumIndexes) == 21, "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)"); void FrameInfo::importUiThreadInfo(int64_t* info) { diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index e9b2f4a9bb3be..62ac4ca5fdad5 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -28,7 +28,7 @@ namespace android { namespace uirenderer { -static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 10; +static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 11; enum class FrameInfoIndex { Flags = 0, @@ -41,6 +41,7 @@ enum class FrameInfoIndex { PerformTraversalsStart, DrawStart, FrameDeadline, + FrameStartTime, // End of UI frame info SyncQueued, diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index e58f31fd15eb2..7af0a221d2280 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -81,6 +81,9 @@ bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; float Properties::defaultSdrWhitePoint = 200.f; +bool Properties::useHintManager = true; +int Properties::targetCpuTimePercentage = 70; + bool Properties::load() { bool prevDebugLayersUpdates = debugLayersUpdates; bool prevDebugOverdraw = debugOverdraw; @@ -128,6 +131,10 @@ bool Properties::load() { runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false); + useHintManager = base::GetBoolProperty(PROPERTY_USE_HINT_MANAGER, true); + targetCpuTimePercentage = base::GetIntProperty(PROPERTY_TARGET_CPU_TIME_PERCENTAGE, 70); + if (targetCpuTimePercentage <= 0 || targetCpuTimePercentage > 100) targetCpuTimePercentage = 70; + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index ea9cbd592d292..1cb87be98051f 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -157,6 +157,20 @@ enum DebugLevel { */ #define PROPERTY_CAPTURE_SKP_FILENAME "debug.hwui.skp_filename" +/** + * Controls whether HWUI will send timing hints to HintManager for + * better CPU scheduling. Accepted values are "true" and "false". + */ +#define PROPERTY_USE_HINT_MANAGER "debug.hwui.use_hint_manager" + +/** + * Percentage of frame time that's used for CPU work. The rest is + * reserved for GPU work. This is used with use_hint_manager to + * provide timing hints to HintManager. Accepted values are + * integer from 1-100. + */ +#define PROPERTY_TARGET_CPU_TIME_PERCENTAGE "debug.hwui.target_cpu_time_percent" + /** * Property for whether this is running in the emulator. */ @@ -253,6 +267,9 @@ public: static float defaultSdrWhitePoint; + static bool useHintManager; + static int targetCpuTimePercentage; + private: static ProfileType sProfileType; static bool sDisableProfileBars; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index f24ba5c1c8785..bd1da985a33ed 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -34,14 +34,18 @@ #include #include #include +#include #include #include #include #include #include +#include + #include #include +#include #include "android_graphics_HardwareRendererObserver.h" @@ -53,6 +57,10 @@ using namespace android::uirenderer::renderthread; struct { jclass clazz; jmethodID invokePictureCapturedCallback; + jmethodID createHintSession; + jmethodID updateTargetWorkDuration; + jmethodID reportActualWorkDuration; + jmethodID closeHintSession; } gHardwareRenderer; struct { @@ -71,6 +79,14 @@ static JNIEnv* getenv(JavaVM* vm) { return env; } +static bool hasExceptionAndClear(JNIEnv* env) { + if (GraphicsJNI::hasException(env)) { + env->ExceptionClear(); + return true; + } + return false; +} + typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface); ANW_fromSurface fromSurface; @@ -120,6 +136,67 @@ private: } }; +class HintSessionWrapper : public LightRefBase { +public: + static sp create(JNIEnv* env, RenderProxy* proxy) { + if (!Properties::useHintManager) return nullptr; + + // Include UI thread (self), render thread, and thread pool. + std::vector tids = CommonPool::getThreadIds(); + tids.push_back(proxy->getRenderThreadTid()); + tids.push_back(pthread_gettid_np(pthread_self())); + + jintArray tidsArray = env->NewIntArray(tids.size()); + if (hasExceptionAndClear(env)) return nullptr; + env->SetIntArrayRegion(tidsArray, 0, tids.size(), reinterpret_cast(tids.data())); + if (hasExceptionAndClear(env)) return nullptr; + jobject session = env->CallStaticObjectMethod( + gHardwareRenderer.clazz, gHardwareRenderer.createHintSession, tidsArray); + if (hasExceptionAndClear(env) || !session) return nullptr; + return new HintSessionWrapper(env, session); + } + + ~HintSessionWrapper() { + if (!mSession) return; + JNIEnv* env = getenv(mVm); + env->CallStaticVoidMethod(gHardwareRenderer.clazz, gHardwareRenderer.closeHintSession, + mSession); + hasExceptionAndClear(env); + env->DeleteGlobalRef(mSession); + mSession = nullptr; + } + + void updateTargetWorkDuration(long targetDurationNanos) { + if (!mSession) return; + JNIEnv* env = getenv(mVm); + env->CallStaticVoidMethod(gHardwareRenderer.clazz, + gHardwareRenderer.updateTargetWorkDuration, mSession, + static_cast(targetDurationNanos)); + hasExceptionAndClear(env); + } + + void reportActualWorkDuration(long actualDurationNanos) { + if (!mSession) return; + JNIEnv* env = getenv(mVm); + env->CallStaticVoidMethod(gHardwareRenderer.clazz, + gHardwareRenderer.reportActualWorkDuration, mSession, + static_cast(actualDurationNanos)); + hasExceptionAndClear(env); + } + +private: + HintSessionWrapper(JNIEnv* env, jobject jobject) { + env->GetJavaVM(&mVm); + if (jobject) { + mSession = env->NewGlobalRef(jobject); + LOG_ALWAYS_FATAL_IF(!mSession, "Failed to make global ref"); + } + } + + JavaVM* mVm = nullptr; + jobject mSession = nullptr; +}; + static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) { RenderProxy::rotateProcessStatsBuffer(); } @@ -147,6 +224,12 @@ static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject claz RootRenderNode* rootRenderNode = reinterpret_cast(rootRenderNodePtr); ContextFactoryImpl factory(rootRenderNode); RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory); + sp wrapper = HintSessionWrapper::create(env, proxy); + if (wrapper) { + proxy->setHintSessionCallbacks( + [wrapper](int64_t nanos) { wrapper->updateTargetWorkDuration(nanos); }, + [wrapper](int64_t nanos) { wrapper->reportActualWorkDuration(nanos); }); + } return (jlong) proxy; } @@ -769,6 +852,18 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) { gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer, "invokePictureCapturedCallback", "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V"); + gHardwareRenderer.createHintSession = + GetStaticMethodIDOrDie(env, hardwareRenderer, "createHintSession", + "([I)Landroid/os/PerformanceHintManager$Session;"); + gHardwareRenderer.updateTargetWorkDuration = + GetStaticMethodIDOrDie(env, hardwareRenderer, "updateTargetWorkDuration", + "(Landroid/os/PerformanceHintManager$Session;J)V"); + gHardwareRenderer.reportActualWorkDuration = + GetStaticMethodIDOrDie(env, hardwareRenderer, "reportActualWorkDuration", + "(Landroid/os/PerformanceHintManager$Session;J)V"); + gHardwareRenderer.closeHintSession = + GetStaticMethodIDOrDie(env, hardwareRenderer, "closeHintSession", + "(Landroid/os/PerformanceHintManager$Session;)V"); jclass frameCallbackClass = FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameDrawingCallback"); diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 3408ffda3f9de..7a38a3bc9c05c 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -21,6 +21,7 @@ #include "../DeferredLayerUpdater.h" #include "../DisplayList.h" +#include "../Properties.h" #include "../RenderNode.h" #include "CanvasContext.h" #include "RenderThread.h" @@ -44,6 +45,12 @@ void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, mTargetNode = targetNode; } +void DrawFrameTask::setHintSessionCallbacks(std::function updateTargetWorkDuration, + std::function reportActualWorkDuration) { + mUpdateTargetWorkDuration = std::move(updateTargetWorkDuration); + mReportActualWorkDuration = std::move(reportActualWorkDuration); +} + void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) { LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to pushLayerUpdate with!"); @@ -102,6 +109,9 @@ void DrawFrameTask::run() { CanvasContext* context = mContext; std::function callback = std::move(mFrameCallback); mFrameCallback = nullptr; + int64_t intendedVsync = mFrameInfo[static_cast(FrameInfoIndex::IntendedVsync)]; + int64_t frameDeadline = mFrameInfo[static_cast(FrameInfoIndex::FrameDeadline)]; + int64_t frameStartTime = mFrameInfo[static_cast(FrameInfoIndex::FrameStartTime)]; // From this point on anything in "this" is *UNSAFE TO ACCESS* if (canUnblockUiThread) { @@ -124,6 +134,25 @@ void DrawFrameTask::run() { if (!canUnblockUiThread) { unblockUiThread(); } + + // These member callbacks are effectively const as they are set once during init, so it's safe + // to use these directly instead of making local copies. + if (mUpdateTargetWorkDuration && mReportActualWorkDuration) { + constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms + constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s + int64_t targetWorkDuration = frameDeadline - intendedVsync; + targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100; + if (targetWorkDuration > kSanityCheckLowerBound && + targetWorkDuration < kSanityCheckUpperBound && + targetWorkDuration != mLastTargetWorkDuration) { + mLastTargetWorkDuration = targetWorkDuration; + mUpdateTargetWorkDuration(targetWorkDuration); + } + int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime; + if (frameDuration > kSanityCheckLowerBound && frameDuration < kSanityCheckUpperBound) { + mReportActualWorkDuration(frameDuration); + } + } } bool DrawFrameTask::syncFrameState(TreeInfo& info) { diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 696cfaef3cd7b..3bb574aff59b8 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -61,6 +61,8 @@ public: virtual ~DrawFrameTask(); void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode); + void setHintSessionCallbacks(std::function updateTargetWorkDuration, + std::function reportActualWorkDuration); void setContentDrawBounds(int left, int top, int right, int bottom) { mContentDrawBounds.set(left, top, right, bottom); } @@ -107,6 +109,10 @@ private: std::function mFrameCallback; std::function mFrameCompleteCallback; + + nsecs_t mLastTargetWorkDuration = 0; + std::function mUpdateTargetWorkDuration; + std::function mReportActualWorkDuration; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 423cc08189cab..9361abd208525 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -76,6 +76,12 @@ void RenderProxy::setName(const char* name) { mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); } +void RenderProxy::setHintSessionCallbacks(std::function updateTargetWorkDuration, + std::function reportActualWorkDuration) { + mDrawFrameTask.setHintSessionCallbacks(std::move(updateTargetWorkDuration), + std::move(reportActualWorkDuration)); +} + void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { if (window) { ANativeWindow_acquire(window); } mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 366d6b5a172c3..8d55d3c55570e 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -71,6 +71,8 @@ public: void setSwapBehavior(SwapBehavior swapBehavior); bool loadSystemProperties(); void setName(const char* name); + void setHintSessionCallbacks(std::function updateTargetWorkDuration, + std::function reportActualWorkDuration); void setSurface(ANativeWindow* window, bool enableTimeout = true); void setSurfaceControl(ASurfaceControl* surfaceControl); diff --git a/libs/hwui/thread/CommonPool.cpp b/libs/hwui/thread/CommonPool.cpp index d011bdfe945ea..dc92f9f0d39a2 100644 --- a/libs/hwui/thread/CommonPool.cpp +++ b/libs/hwui/thread/CommonPool.cpp @@ -29,14 +29,23 @@ CommonPool::CommonPool() { ATRACE_CALL(); CommonPool* pool = this; + std::mutex mLock; + std::vector tids(THREAD_COUNT); + std::vector tidConditionVars(THREAD_COUNT); + // Create 2 workers for (int i = 0; i < THREAD_COUNT; i++) { - std::thread worker([pool, i] { + std::thread worker([pool, i, &mLock, &tids, &tidConditionVars] { { std::array name{"hwuiTask"}; snprintf(name.data(), name.size(), "hwuiTask%d", i); auto self = pthread_self(); pthread_setname_np(self, name.data()); + { + std::unique_lock lock(mLock); + tids[i] = pthread_gettid_np(self); + tidConditionVars[i].notify_one(); + } setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND); auto startHook = renderthread::RenderThread::getOnStartHook(); if (startHook) { @@ -47,6 +56,15 @@ CommonPool::CommonPool() { }); worker.detach(); } + { + std::unique_lock lock(mLock); + for (int i = 0; i < THREAD_COUNT; i++) { + while (!tids[i]) { + tidConditionVars[i].wait(lock); + } + } + } + mWorkerThreadIds = std::move(tids); } CommonPool& CommonPool::instance() { @@ -58,6 +76,10 @@ void CommonPool::post(Task&& task) { instance().enqueue(std::move(task)); } +std::vector CommonPool::getThreadIds() { + return instance().mWorkerThreadIds; +} + void CommonPool::enqueue(Task&& task) { std::unique_lock lock(mLock); while (!mWorkQueue.hasSpace()) { @@ -104,4 +126,4 @@ void CommonPool::doWaitForIdle() { } } // namespace uirenderer -} // namespace android \ No newline at end of file +} // namespace android diff --git a/libs/hwui/thread/CommonPool.h b/libs/hwui/thread/CommonPool.h index 7603eeef46928..74f852bd14137 100644 --- a/libs/hwui/thread/CommonPool.h +++ b/libs/hwui/thread/CommonPool.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace android { namespace uirenderer { @@ -97,6 +98,8 @@ public: return task.get_future().get(); }; + static std::vector getThreadIds(); + // For testing purposes only, blocks until all worker threads are parked. static void waitForIdle(); @@ -111,6 +114,8 @@ private: void workerLoop(); + std::vector mWorkerThreadIds; + std::mutex mLock; std::condition_variable mCondition; int mWaitingThreads = 0;