Add support for render-ahead
For periods of time during which latency is less important
allow a client to request a deeper render-ahead pipeline.
The latency tradeoff results in less overall visual jank
Test: none, only used by macrobench
Change-Id: I516203b70bdc75b6415fa08bf9c4fb1b598b0102
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f4d8051..609d26c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -17,6 +17,7 @@
#include "CanvasContext.h"
#include <GpuMemoryTracker.h>
+#include "../Properties.h"
#include "AnimationContext.h"
#include "Caches.h"
#include "EglManager.h"
@@ -34,7 +35,6 @@
#include "renderstate/Stencil.h"
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
-#include "../Properties.h"
#include <cutils/properties.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -194,6 +194,7 @@
if (hasSurface) {
mHaveNewSurface = true;
mSwapHistory.clear();
+ updateBufferCount();
} else {
mRenderThread.removeFrameCallback(this);
}
@@ -427,6 +428,9 @@
waitOnFences();
+ frame.setPresentTime(mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
+ (mRenderThread.timeLord().frameIntervalNanos() * (mRenderAheadDepth + 1)));
+
bool requireSwap = false;
bool didSwap =
mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
@@ -705,6 +709,26 @@
return mFrameNumber;
}
+void overrideBufferCount(const sp<Surface>& surface, int bufferCount) {
+ struct SurfaceExposer : Surface {
+ using Surface::setBufferCount;
+ };
+ // Protected is just a sign, not a cop
+ ((*surface.get()).*&SurfaceExposer::setBufferCount)(bufferCount);
+}
+
+void CanvasContext::updateBufferCount() {
+ overrideBufferCount(mNativeSurface, 3 + mRenderAheadDepth);
+}
+
+void CanvasContext::setRenderAheadDepth(int renderAhead) {
+ if (renderAhead < 0 || renderAhead > 2 || renderAhead == mRenderAheadDepth) {
+ return;
+ }
+ mRenderAheadDepth = renderAhead;
+ updateBufferCount();
+}
+
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
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index c2cc72a..8aaa8c1 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -78,7 +78,7 @@
bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator,
ErrorHandler* errorHandler) {
return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut,
- errorHandler);
+ errorHandler);
}
/**
@@ -190,6 +190,8 @@
IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); }
+ void setRenderAheadDepth(int renderAhead);
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -202,6 +204,7 @@
void freePrefetchedLayers();
bool isSwapChainStuffed();
+ void updateBufferCount();
SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
@@ -227,6 +230,7 @@
RingBuffer<SwapHistory, 3> mSwapHistory;
int64_t mFrameNumber = -1;
+ int mRenderAheadDepth = 0;
// last vsync for a dropped frame due to stuffed queue
nsecs_t mLastDropVsync = 0;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 778e768..5b0e281 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -103,9 +103,8 @@
// Even if we aren't drawing this vsync pulse the next frame number will still be accurate
if (CC_UNLIKELY(callback)) {
- context->enqueueFrameWork([callback, frameNr = context->getFrameNumber()]() {
- callback(frameNr);
- });
+ context->enqueueFrameWork(
+ [ callback, frameNr = context->getFrameNumber() ]() { callback(frameNr); });
}
if (CC_LIKELY(canDrawThisFrame)) {
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 6e239e3..3a49beb 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -83,6 +83,7 @@
bool glColorSpace = false;
bool scRGB = false;
bool contextPriority = false;
+ bool presentTime = false;
} EglExtensions;
EglManager::EglManager(RenderThread& thread)
@@ -170,6 +171,7 @@
EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
#endif
EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
+ EglExtensions.presentTime = extensions.has("EGL_ANDROID_presentation_time");
}
bool EglManager::hasEglContext() {
@@ -242,7 +244,7 @@
&numConfigs) ||
numConfigs != 1) {
ALOGE("Device claims wide gamut support, cannot find matching config, error = %s",
- eglErrorString());
+ eglErrorString());
EglExtensions.pixelFormatFloat = false;
}
}
@@ -437,6 +439,13 @@
fence();
}
+ if (EglExtensions.presentTime && Properties::usePresentTime) {
+ if (!eglPresentationTimeANDROID(mEglDisplay, frame.mSurface, frame.mPresentTime)) {
+ LOG_ALWAYS_FATAL("Failed to set presentation time on surface %p, error=%s",
+ (void*)frame.mSurface, eglErrorString());
+ }
+ }
+
EGLint rects[4];
frame.map(screenDirty, rects);
eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects, screenDirty.isEmpty() ? 0 : 1);
diff --git a/libs/hwui/renderthread/Frame.h b/libs/hwui/renderthread/Frame.h
index d266faa..03cf95d 100644
--- a/libs/hwui/renderthread/Frame.h
+++ b/libs/hwui/renderthread/Frame.h
@@ -37,6 +37,8 @@
// for what this means
int32_t bufferAge() const { return mBufferAge; }
+ void setPresentTime(int64_t presentTime) { mPresentTime = presentTime; }
+
private:
Frame() {}
friend class EglManager;
@@ -44,6 +46,7 @@
int32_t mWidth;
int32_t mHeight;
int32_t mBufferAge;
+ int64_t mPresentTime;
EGLSurface mSurface;
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index 876af47..1925808 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -22,8 +22,8 @@
#include "GlLayer.h"
#include "OpenGLReadback.h"
#include "ProfileRenderer.h"
-#include "renderstate/RenderState.h"
#include "TreeInfo.h"
+#include "renderstate/RenderState.h"
#include <cutils/properties.h>
#include <strings.h>
@@ -203,8 +203,7 @@
bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
const DamageAccumulator& damageAccumulator,
- bool wideColorGamut,
- ErrorHandler* errorHandler) {
+ bool wideColorGamut, ErrorHandler* errorHandler) {
RenderState& renderState = mRenderThread.renderState();
OffscreenBufferPool& layerPool = renderState.layerPool();
bool transformUpdateNeeded = false;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c6a9b55f..00645d6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -290,6 +290,12 @@
});
}
+void RenderProxy::setRenderAheadDepth(int renderAhead) {
+ mRenderThread.queue().post([ context = mContext, renderAhead ]() {
+ context->setRenderAheadDepth(renderAhead);
+ });
+}
+
int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
SkBitmap* bitmap) {
auto& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 3425c5c..bd5c9a9 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -119,7 +119,23 @@
ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer);
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
- ANDROID_API long getDroppedFrameReportCount();
+
+ /**
+ * Sets a render-ahead depth on the backing renderer. This will increase latency by
+ * <swapInterval> * renderAhead and increase memory usage by (3 + renderAhead) * <resolution>.
+ * In return the renderer will be less susceptible to jitter, resulting in a smoother animation.
+ *
+ * Not recommended to use in response to anything touch driven, but for canned animations
+ * where latency is not a concern careful use may be beneficial.
+ *
+ * Note that when increasing this there will be a frame gap of N frames where N is
+ * renderAhead - <current renderAhead>. When decreasing this if there are any pending
+ * frames they will retain their prior renderAhead value, so it will take a few frames
+ * for the decrease to flush through.
+ *
+ * @param renderAhead How far to render ahead, must be in the range [0..2]
+ */
+ ANDROID_API void setRenderAheadDepth(int renderAhead);
ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right,
int bottom, SkBitmap* bitmap);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 6a2a025..4b154e6 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -25,8 +25,8 @@
#include "hwui/Bitmap.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaOpenGLReadback.h"
-#include "pipeline/skia/SkiaVulkanReadback.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
+#include "pipeline/skia/SkiaVulkanReadback.h"
#include "renderstate/RenderState.h"
#include "renderthread/OpenGLPipeline.h"
#include "utils/FatVector.h"
@@ -93,14 +93,11 @@
DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
- mRenderThread->queue().postDelayed(16_ms, [this]() {
- mRenderThread->drainDisplayEventQueue();
- });
+ mRenderThread->queue().postDelayed(16_ms,
+ [this]() { mRenderThread->drainDisplayEventQueue(); });
}
- virtual nsecs_t latestVsyncEvent() override {
- return systemTime(CLOCK_MONOTONIC);
- }
+ virtual nsecs_t latestVsyncEvent() override { return systemTime(CLOCK_MONOTONIC); }
private:
RenderThread* mRenderThread;
@@ -147,13 +144,13 @@
auto receiver = std::make_unique<DisplayEventReceiver>();
status_t status = receiver->initCheck();
LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "Initialization of DisplayEventReceiver "
- "failed with status: %d",
- status);
+ "Initialization of DisplayEventReceiver "
+ "failed with status: %d",
+ status);
// Register the FD
mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
- RenderThread::displayEventReceiverCallback, this);
+ RenderThread::displayEventReceiverCallback, this);
mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver));
} else {
mVsyncSource = new DummyVsyncSource(this);