summaryrefslogtreecommitdiff
path: root/libs/hwui/renderthread/CanvasContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/renderthread/CanvasContext.cpp')
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp242
1 files changed, 176 insertions, 66 deletions
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 667a7517a24c..891a99489fcf 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -56,6 +56,22 @@ namespace android {
namespace uirenderer {
namespace renderthread {
+namespace {
+class ScopedActiveContext {
+public:
+ ScopedActiveContext(CanvasContext* context) { sActiveContext = context; }
+
+ ~ScopedActiveContext() { sActiveContext = nullptr; }
+
+ static CanvasContext* getActiveContext() { return sActiveContext; }
+
+private:
+ static CanvasContext* sActiveContext;
+};
+
+CanvasContext* ScopedActiveContext::sActiveContext = nullptr;
+} /* namespace */
+
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory) {
auto renderType = Properties::getRenderPipelineType();
@@ -108,7 +124,6 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
- setRenderAheadDepth(Properties::defaultRenderAhead);
}
CanvasContext::~CanvasContext() {
@@ -134,6 +149,7 @@ void CanvasContext::removeRenderNode(RenderNode* node) {
void CanvasContext::destroy() {
stopDrawing();
setSurface(nullptr);
+ setSurfaceControl(nullptr);
freePrefetchedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
@@ -157,26 +173,45 @@ static void setBufferCount(ANativeWindow* window) {
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
- if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
- mFixedRenderAhead = false;
- mRenderAheadCapacity = 1;
- } else {
- mFixedRenderAhead = true;
- mRenderAheadCapacity = mRenderAheadDepth;
- }
-
if (window) {
+ int extraBuffers = 0;
+ native_window_get_extra_buffer_count(window, &extraBuffers);
+
mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
if (enableTimeout) {
// TODO: Fix error handling & re-shorten timeout
ANativeWindow_setDequeueTimeout(window, 4000_ms);
}
- mNativeSurface->setExtraBufferCount(mRenderAheadCapacity);
+ mNativeSurface->setExtraBufferCount(extraBuffers);
} else {
mNativeSurface = nullptr;
}
+ setupPipelineSurface();
+}
+
+void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) {
+ if (surfaceControl == mSurfaceControl) return;
+
+ auto funcs = mRenderThread.getASurfaceControlFunctions();
+
+ if (surfaceControl == nullptr) {
+ setASurfaceTransactionCallback(nullptr);
+ }
+ if (mSurfaceControl != nullptr) {
+ funcs.unregisterListenerFunc(this, &onSurfaceStatsAvailable);
+ funcs.releaseFunc(mSurfaceControl);
+ }
+ mSurfaceControl = surfaceControl;
+ mExpectSurfaceStats = surfaceControl != nullptr;
+ if (mSurfaceControl != nullptr) {
+ funcs.acquireFunc(mSurfaceControl);
+ funcs.registerListenerFunc(surfaceControl, this, &onSurfaceStatsAvailable);
+ }
+}
+
+void CanvasContext::setupPipelineSurface() {
bool hasSurface = mRenderPipeline->setSurface(
mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);
@@ -187,7 +222,7 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
mFrameNumber = -1;
- if (window != nullptr && hasSurface) {
+ if (mNativeSurface != nullptr && hasSurface) {
mHaveNewSurface = true;
mSwapHistory.clear();
// Enable frame stats after the surface has been bound to the appropriate graphics API.
@@ -242,9 +277,9 @@ void CanvasContext::setOpaque(bool opaque) {
mOpaque = opaque;
}
-void CanvasContext::setWideGamut(bool wideGamut) {
- ColorMode colorMode = wideGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
- mRenderPipeline->setSurfaceColorProperties(colorMode);
+void CanvasContext::setColorMode(ColorMode mode) {
+ mRenderPipeline->setSurfaceColorProperties(mode);
+ setupPipelineSurface();
}
bool CanvasContext::makeCurrent() {
@@ -321,8 +356,8 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
// just keep using the previous frame's structure instead
if (!wasSkipped(mCurrentFrameInfo)) {
mCurrentFrameInfo = mJankTracker.startFrame();
- mLast4FrameInfos.next().first = mCurrentFrameInfo;
}
+
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
mCurrentFrameInfo->markSyncStart();
@@ -436,25 +471,13 @@ void CanvasContext::notifyFramePending() {
mRenderThread.pushBackFrameCallback(this);
}
-void CanvasContext::setPresentTime() {
- int64_t presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO;
- int renderAhead = 0;
- const auto frameIntervalNanos = mRenderThread.timeLord().frameIntervalNanos();
- if (mFixedRenderAhead) {
- renderAhead = std::min(mRenderAheadDepth, mRenderAheadCapacity);
- } else if (frameIntervalNanos < 15_ms) {
- renderAhead = std::min(1, static_cast<int>(mRenderAheadCapacity));
- }
-
- if (renderAhead) {
- presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
- (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() +
- (frameIntervalNanos / 2);
+nsecs_t CanvasContext::draw() {
+ if (auto grContext = getGrContext()) {
+ if (grContext->abandoned()) {
+ LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
+ return 0;
+ }
}
- native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
-}
-
-void CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -462,18 +485,21 @@ void CanvasContext::draw() {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
// Notify the callbacks, even if there's nothing to draw so they aren't waiting
// indefinitely
+ waitOnFences();
for (auto& func : mFrameCompleteCallbacks) {
std::invoke(func, mFrameNumber);
}
mFrameCompleteCallbacks.clear();
- return;
+ return 0;
}
+ ScopedActiveContext activeContext(this);
+ mCurrentFrameInfo->set(FrameInfoIndex::FrameInterval) =
+ mRenderThread.timeLord().frameIntervalNanos();
+
mCurrentFrameInfo->markIssueDrawCommandsStart();
Frame frame = mRenderPipeline->getFrame();
- setPresentTime();
-
SkRect windowDirty = computeDirtyRect(frame, &dirty);
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
@@ -484,6 +510,17 @@ void CanvasContext::draw() {
waitOnFences();
+ if (mNativeSurface) {
+ // TODO(b/165985262): measure performance impact
+ const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
+ const auto inputEventId =
+ static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
+ native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId,
+ inputEventId);
+ }
+ }
+
bool requireSwap = false;
int error = OK;
bool didSwap =
@@ -536,17 +573,14 @@ void CanvasContext::draw() {
}
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration;
- mLast4FrameInfos[-1].second = frameCompleteNr;
mHaveNewSurface = false;
mFrameNumber = -1;
} else {
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0;
- mLast4FrameInfos[-1].second = -1;
}
- // TODO: Use a fence for real completion?
- mCurrentFrameInfo->markFrameCompleted();
+ mCurrentFrameInfo->markSwapBuffersCompleted();
#if LOG_FRAMETIME_MMA
float thisFrame = mCurrentFrameInfo->duration(FrameInfoIndex::IssueDrawCommandsStart,
@@ -570,28 +604,96 @@ void CanvasContext::draw() {
mFrameCompleteCallbacks.clear();
}
- mJankTracker.finishFrame(*mCurrentFrameInfo);
- if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
- mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data());
+ if (requireSwap) {
+ if (mExpectSurfaceStats) {
+ reportMetricsWithPresentTime();
+ std::lock_guard lock(mLast4FrameInfosMutex);
+ std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
+ next.first = mCurrentFrameInfo;
+ next.second = frameCompleteNr;
+ } else {
+ mCurrentFrameInfo->markFrameCompleted();
+ mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
+ = mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
+ mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter);
+ }
+ }
+
+ mRenderThread.cacheManager().onFrameCompleted();
+ return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+}
+
+void CanvasContext::reportMetricsWithPresentTime() {
+ if (mFrameMetricsReporter == nullptr) {
+ return;
}
+ if (mNativeSurface == nullptr) {
+ return;
+ }
+ FrameInfo* forthBehind;
+ int64_t frameNumber;
+ { // acquire lock
+ std::scoped_lock lock(mLast4FrameInfosMutex);
+ if (mLast4FrameInfos.size() != mLast4FrameInfos.capacity()) {
+ // Not enough frames yet
+ return;
+ }
+ // Surface object keeps stats for the last 8 frames.
+ std::tie(forthBehind, frameNumber) = mLast4FrameInfos.front();
+ } // release lock
+
+ nsecs_t presentTime = 0;
+ native_window_get_frame_timestamps(
+ mNativeSurface->getNativeWindow(), frameNumber, nullptr /*outRequestedPresentTime*/,
+ nullptr /*outAcquireTime*/, nullptr /*outLatchTime*/,
+ nullptr /*outFirstRefreshStartTime*/, nullptr /*outLastRefreshStartTime*/,
+ nullptr /*outGpuCompositionDoneTime*/, &presentTime, nullptr /*outDequeueReadyTime*/,
+ nullptr /*outReleaseTime*/);
+
+ forthBehind->set(FrameInfoIndex::DisplayPresentTime) = presentTime;
+ mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/);
+}
+
+void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
+ ASurfaceControlStats* stats) {
+
+ CanvasContext* instance = static_cast<CanvasContext*>(context);
- if (mLast4FrameInfos.size() == mLast4FrameInfos.capacity()) {
- // By looking 4 frames back, we guarantee all SF stats are available. There are at
- // most 3 buffers in BufferQueue. Surface object keeps stats for the last 8 frames.
- FrameInfo* forthBehind = mLast4FrameInfos.front().first;
- int64_t composedFrameId = mLast4FrameInfos.front().second;
- nsecs_t acquireTime = -1;
- if (mNativeSurface) {
- native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId,
- nullptr, &acquireTime, nullptr, nullptr, nullptr,
- nullptr, nullptr, nullptr, nullptr);
+ const ASurfaceControlFunctions& functions =
+ instance->mRenderThread.getASurfaceControlFunctions();
+
+ nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
+ uint64_t frameNumber = functions.getFrameNumberFunc(stats);
+
+ FrameInfo* frameInfo = nullptr;
+ {
+ std::lock_guard(instance->mLast4FrameInfosMutex);
+ for (size_t i = 0; i < instance->mLast4FrameInfos.size(); i++) {
+ if (instance->mLast4FrameInfos[i].second == frameNumber) {
+ frameInfo = instance->mLast4FrameInfos[i].first;
+ break;
+ }
}
- // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING
- forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1;
- mJankTracker.finishGpuDraw(*forthBehind);
}
- mRenderThread.cacheManager().onFrameCompleted();
+ if (frameInfo != nullptr) {
+ if (gpuCompleteTime == -1) {
+ gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
+ }
+ if (gpuCompleteTime < frameInfo->get(FrameInfoIndex::IssueDrawCommandsStart)) {
+ // On Vulkan the GPU commands are flushed to the GPU during IssueDrawCommands rather
+ // than after SwapBuffers. So if the GPU signals before issue draw commands, then
+ // something probably went wrong. Anything after that could just be expected
+ // pipeline differences
+ ALOGW("Impossible GPU complete time issueCommandsStart=%" PRIi64
+ " gpuComplete=%" PRIi64,
+ frameInfo->get(FrameInfoIndex::IssueDrawCommandsStart), gpuCompleteTime);
+ gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
+ }
+ frameInfo->set(FrameInfoIndex::FrameCompleted) = gpuCompleteTime;
+ frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
+ instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter);
+ }
}
// Called by choreographer to do an RT-driven animation
@@ -617,8 +719,13 @@ void CanvasContext::prepareAndDraw(RenderNode* node) {
ATRACE_CALL();
nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
+ int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
+ int64_t frameDeadline = mRenderThread.timeLord().lastFrameDeadline();
+ int64_t frameInterval = mRenderThread.timeLord().frameIntervalNanos();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
- UiFrameInfoBuilder(frameInfo).addFlag(FrameInfoFlags::RTAnimation).setVsync(vsync, vsync);
+ UiFrameInfoBuilder(frameInfo)
+ .addFlag(FrameInfoFlags::RTAnimation)
+ .setVsync(vsync, vsync, vsyncId, frameDeadline, frameInterval);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
@@ -745,14 +852,6 @@ bool CanvasContext::surfaceRequiresRedraw() {
return width != mLastFrameWidth || height != mLastFrameHeight;
}
-void CanvasContext::setRenderAheadDepth(int renderAhead) {
- if (renderAhead > 2 || renderAhead < 0 || mNativeSurface) {
- return;
- }
- mFixedRenderAhead = true;
- mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
-}
-
SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
// can't rely on prior content of window if viewport size changes
@@ -803,6 +902,17 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
return windowDirty;
}
+CanvasContext* CanvasContext::getActiveContext() {
+ return ScopedActiveContext::getActiveContext();
+}
+
+bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control) {
+ if (!mASurfaceTransactionCallback) return false;
+ std::invoke(mASurfaceTransactionCallback, reinterpret_cast<int64_t>(transaction),
+ reinterpret_cast<int64_t>(control), getFrameNumber());
+ return true;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */