diff options
author | John Reck <jreck@google.com> | 2018-04-05 16:41:41 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2018-04-09 10:58:15 -0700 |
commit | ec100976e0655acaa204c8800dfb83dadae20cc8 (patch) | |
tree | d7bd2d10c7be8ea0ccec63e85c8365efb87edfe4 /libs | |
parent | 413293ff626301e1ee9837f247c4d7321c59f472 (diff) | |
download | base-ec100976e0655acaa204c8800dfb83dadae20cc8.tar.gz |
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
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/Properties.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 7 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 26 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 6 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/renderthread/EglManager.cpp | 11 | ||||
-rw-r--r-- | libs/hwui/renderthread/Frame.h | 3 | ||||
-rw-r--r-- | libs/hwui/renderthread/OpenGLPipeline.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 18 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.cpp | 19 | ||||
-rw-r--r-- | libs/hwui/tests/common/TestScene.h | 1 | ||||
-rw-r--r-- | libs/hwui/tests/macrobench/TestSceneRunner.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/tests/macrobench/main.cpp | 13 |
14 files changed, 107 insertions, 21 deletions
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 0a6c45beedf9..7b143224f728 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -36,6 +36,7 @@ bool Properties::showDirtyRegions = false; bool Properties::skipEmptyFrames = true; bool Properties::useBufferAge = true; bool Properties::enablePartialUpdates = true; +bool Properties::usePresentTime = true; DebugLevel Properties::debugLevel = kDebugDisabled; OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default; @@ -135,6 +136,7 @@ bool Properties::load() { skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true); useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true); enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true); + usePresentTime = property_get_bool(PROPERTY_USE_PRESENT_TIME, true); filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 764c50259540..1a0bdfd4c237 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -151,6 +151,12 @@ enum DebugLevel { */ #define PROPERTY_ENABLE_PARTIAL_UPDATES "debug.hwui.use_partial_updates" +/** + * Setting this to "false" will disable the use of the EGL_ANDROID_presentation_time extension + * and prevents more precise control over swap behavior & timings. + */ +#define PROPERTY_USE_PRESENT_TIME "debug.hwui.use_present_time" + #define PROPERTY_FILTER_TEST_OVERHEAD "debug.hwui.filter_test_overhead" /** @@ -220,6 +226,7 @@ public: static bool skipEmptyFrames; static bool useBufferAge; static bool enablePartialUpdates; + static bool usePresentTime; // TODO: Move somewhere else? static constexpr float textGamma = 1.45f; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index f4d8051466f0..609d26c53bfe 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 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { if (hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); + updateBufferCount(); } else { mRenderThread.removeFrameCallback(this); } @@ -427,6 +428,9 @@ void CanvasContext::draw() { 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 @@ int64_t CanvasContext::getFrameNumber() { 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 c2cc72a6917f..8aaa8c1ffe47 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -78,7 +78,7 @@ public: bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator, ErrorHandler* errorHandler) { return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut, - errorHandler); + errorHandler); } /** @@ -190,6 +190,8 @@ public: 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 @@ private: void freePrefetchedLayers(); bool isSwapChainStuffed(); + void updateBufferCount(); SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); @@ -227,6 +230,7 @@ private: 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 778e7689d0f9..5b0e28125502 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -103,9 +103,8 @@ void DrawFrameTask::run() { // 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 6e239e357cf6..3a49bebe7936 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -83,6 +83,7 @@ static struct { bool glColorSpace = false; bool scRGB = false; bool contextPriority = false; + bool presentTime = false; } EglExtensions; EglManager::EglManager(RenderThread& thread) @@ -170,6 +171,7 @@ void EglManager::initExtensions() { 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 @@ void EglManager::loadConfigs() { &numConfigs) || numConfigs != 1) { ALOGE("Device claims wide gamut support, cannot find matching config, error = %s", - eglErrorString()); + eglErrorString()); EglExtensions.pixelFormatFloat = false; } } @@ -437,6 +439,13 @@ bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) { 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 d266faa4f7c9..03cf95d797d4 100644 --- a/libs/hwui/renderthread/Frame.h +++ b/libs/hwui/renderthread/Frame.h @@ -37,6 +37,8 @@ public: // 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 @@ private: 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 876af47e256f..19258084eb42 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 @@ static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) { 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 c6a9b55f8ac1..00645d6d8487 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -290,6 +290,12 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr) }); } +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 3425c5c68a72..bd5c9a9fc922 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -119,7 +119,23 @@ public: 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 6a2a025da121..4b154e6f560d 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 @@ public: 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 @@ void RenderThread::initializeDisplayEventReceiver() { 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); diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index 91022cfe734b..74a039b3d090 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -38,6 +38,7 @@ public: int count = 0; int reportFrametimeWeight = 0; bool renderOffscreen = true; + int renderAhead = 0; }; template <class T> diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 9428f532434a..854a449c73a6 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -153,6 +153,12 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, proxy->resetProfileInfo(); proxy->fence(); + if (opts.renderAhead) { + // Need to let the queue drain to see render-ahead in action. + usleep(33000); + } + proxy->setRenderAheadDepth(opts.renderAhead); + ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); nsecs_t start = systemTime(CLOCK_MONOTONIC); diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 0a9a74eccfc9..4cb5cfdd729b 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -68,6 +68,7 @@ OPTIONS: are offscreen rendered --benchmark_format Set output format. Possible values are tabular, json, csv --renderer=TYPE Sets the render pipeline to use. May be opengl, skiagl, or skiavk + --render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2. )"); } @@ -173,6 +174,7 @@ enum { Onscreen, Offscreen, Renderer, + RenderAhead, }; } @@ -188,6 +190,7 @@ static const struct option LONG_OPTIONS[] = { {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, {"renderer", required_argument, nullptr, LongOpts::Renderer}, + {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead}, {0, 0, 0, 0}}; static const char* SHORT_OPTIONS = "c:r:h"; @@ -286,6 +289,16 @@ void parseOptions(int argc, char* argv[]) { gOpts.renderOffscreen = true; break; + case LongOpts::RenderAhead: + if (!optarg) { + error = true; + } + gOpts.renderAhead = atoi(optarg); + if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) { + error = true; + } + break; + case 'h': printHelp(); exit(EXIT_SUCCESS); |