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
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
int count = 0;
|
||||
int reportFrametimeWeight = 0;
|
||||
bool renderOffscreen = true;
|
||||
int renderAhead = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user