Loading libs/hwui/renderthread/CanvasContext.cpp +34 −14 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include "renderstate/RenderState.h" #include "renderstate/Stencil.h" #include "protos/hwui.pb.h" #include "utils/TimeUtils.h" #if HWUI_NEW_OPS #include "BakedOpRenderer.h" Loading Loading @@ -108,6 +109,7 @@ void CanvasContext::setSurface(ANativeWindow* window) { const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); mHaveNewSurface = true; mSwapHistory.clear(); makeCurrent(); } else { mRenderThread.removeFrameCallback(this); Loading Loading @@ -217,13 +219,30 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, return; } if (CC_LIKELY(mSwapHistory.size())) { nsecs_t latestVsync = mRenderThread.timeLord().latestVsync(); const SwapHistory& lastSwap = mSwapHistory.back(); int vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync); // The slight fudge-factor is to deal with cases where // the vsync was estimated due to being slow handling the signal. // See the logic in TimeLord#computeFrameTimeNanos or in // Choreographer.java for details on when this happens if (vsyncDelta < 2_ms) { // Already drew for this vsync pulse, UI draw request missed // the deadline for RT animations info.out.canDrawThisFrame = false; } else if (lastSwap.swapTime < latestVsync) { info.out.canDrawThisFrame = true; } else { // We're maybe behind? Find out for sure int runningBehind = 0; // TODO: This query is moderately expensive, investigate adding some sort // of fast-path based off when we last called eglSwapBuffers() as well as // last vsync time. Or something. mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); info.out.canDrawThisFrame = !runningBehind; } } else { info.out.canDrawThisFrame = true; } if (!info.out.canDrawThisFrame) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); Loading Loading @@ -297,7 +316,7 @@ void CanvasContext::draw() { // last frame so there's nothing to union() against // Therefore we only care about the > 1 case. if (frame.bufferAge() > 1) { if (frame.bufferAge() > (int) mDamageHistory.size()) { if (frame.bufferAge() > (int) mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw dirty.set(0, 0, frame.width(), frame.height()); Loading @@ -305,16 +324,13 @@ void CanvasContext::draw() { // At this point we haven't yet added the latest frame // to the damage history (happens below) // So we need to damage for (int i = mDamageHistory.size() - 1; i > ((int) mDamageHistory.size()) - frame.bufferAge(); i--) { dirty.join(mDamageHistory[i]); for (int i = mSwapHistory.size() - 1; i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { dirty.join(mSwapHistory[i].damage); } } } // Add the screen damage to the ring buffer. mDamageHistory.next() = screenDirty; mEglManager.damageFrame(frame, dirty); #if HWUI_NEW_OPS Loading Loading @@ -445,6 +461,10 @@ void CanvasContext::draw() { if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) { setSurface(nullptr); } SwapHistory& swap = mSwapHistory.next(); swap.damage = screenDirty; swap.swapTime = systemTime(CLOCK_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); mHaveNewSurface = false; } Loading libs/hwui/renderthread/CanvasContext.h +7 −1 Original line number Diff line number Diff line Loading @@ -150,7 +150,13 @@ private: EGLSurface mEglSurface = EGL_NO_SURFACE; bool mBufferPreserved = false; SwapBehavior mSwapBehavior = kSwap_default; RingBuffer<SkRect, 3> mDamageHistory; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; nsecs_t swapTime; }; RingBuffer<SwapHistory, 3> mSwapHistory; bool mOpaque; OpenGLRenderer* mCanvas = nullptr; Loading libs/hwui/utils/TimeUtils.h 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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. */ #ifndef UTILS_TIMEUTILS_H #define UTILS_TIMEUTILS_H #include <utils/Timers.h> namespace android { namespace uirenderer { constexpr nsecs_t operator"" _ms (unsigned long long ms) { return milliseconds_to_nanoseconds(ms); } } /* namespace uirenderer */ } /* namespace android */ #endif /* UTILS_TIMEUTILS_H */ Loading
libs/hwui/renderthread/CanvasContext.cpp +34 −14 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include "renderstate/RenderState.h" #include "renderstate/Stencil.h" #include "protos/hwui.pb.h" #include "utils/TimeUtils.h" #if HWUI_NEW_OPS #include "BakedOpRenderer.h" Loading Loading @@ -108,6 +109,7 @@ void CanvasContext::setSurface(ANativeWindow* window) { const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); mHaveNewSurface = true; mSwapHistory.clear(); makeCurrent(); } else { mRenderThread.removeFrameCallback(this); Loading Loading @@ -217,13 +219,30 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, return; } if (CC_LIKELY(mSwapHistory.size())) { nsecs_t latestVsync = mRenderThread.timeLord().latestVsync(); const SwapHistory& lastSwap = mSwapHistory.back(); int vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync); // The slight fudge-factor is to deal with cases where // the vsync was estimated due to being slow handling the signal. // See the logic in TimeLord#computeFrameTimeNanos or in // Choreographer.java for details on when this happens if (vsyncDelta < 2_ms) { // Already drew for this vsync pulse, UI draw request missed // the deadline for RT animations info.out.canDrawThisFrame = false; } else if (lastSwap.swapTime < latestVsync) { info.out.canDrawThisFrame = true; } else { // We're maybe behind? Find out for sure int runningBehind = 0; // TODO: This query is moderately expensive, investigate adding some sort // of fast-path based off when we last called eglSwapBuffers() as well as // last vsync time. Or something. mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); info.out.canDrawThisFrame = !runningBehind; } } else { info.out.canDrawThisFrame = true; } if (!info.out.canDrawThisFrame) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); Loading Loading @@ -297,7 +316,7 @@ void CanvasContext::draw() { // last frame so there's nothing to union() against // Therefore we only care about the > 1 case. if (frame.bufferAge() > 1) { if (frame.bufferAge() > (int) mDamageHistory.size()) { if (frame.bufferAge() > (int) mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw dirty.set(0, 0, frame.width(), frame.height()); Loading @@ -305,16 +324,13 @@ void CanvasContext::draw() { // At this point we haven't yet added the latest frame // to the damage history (happens below) // So we need to damage for (int i = mDamageHistory.size() - 1; i > ((int) mDamageHistory.size()) - frame.bufferAge(); i--) { dirty.join(mDamageHistory[i]); for (int i = mSwapHistory.size() - 1; i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { dirty.join(mSwapHistory[i].damage); } } } // Add the screen damage to the ring buffer. mDamageHistory.next() = screenDirty; mEglManager.damageFrame(frame, dirty); #if HWUI_NEW_OPS Loading Loading @@ -445,6 +461,10 @@ void CanvasContext::draw() { if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) { setSurface(nullptr); } SwapHistory& swap = mSwapHistory.next(); swap.damage = screenDirty; swap.swapTime = systemTime(CLOCK_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); mHaveNewSurface = false; } Loading
libs/hwui/renderthread/CanvasContext.h +7 −1 Original line number Diff line number Diff line Loading @@ -150,7 +150,13 @@ private: EGLSurface mEglSurface = EGL_NO_SURFACE; bool mBufferPreserved = false; SwapBehavior mSwapBehavior = kSwap_default; RingBuffer<SkRect, 3> mDamageHistory; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; nsecs_t swapTime; }; RingBuffer<SwapHistory, 3> mSwapHistory; bool mOpaque; OpenGLRenderer* mCanvas = nullptr; Loading
libs/hwui/utils/TimeUtils.h 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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. */ #ifndef UTILS_TIMEUTILS_H #define UTILS_TIMEUTILS_H #include <utils/Timers.h> namespace android { namespace uirenderer { constexpr nsecs_t operator"" _ms (unsigned long long ms) { return milliseconds_to_nanoseconds(ms); } } /* namespace uirenderer */ } /* namespace android */ #endif /* UTILS_TIMEUTILS_H */