Merge "Remove libpdx*, libbufferhub* from VNDK" into pi-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index f65f4f8..70164ea 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -125,6 +125,7 @@
{ OPT, "events/sched/sched_waking/enable" },
{ OPT, "events/sched/sched_blocked_reason/enable" },
{ OPT, "events/sched/sched_cpu_hotplug/enable" },
+ { OPT, "events/sched/sched_pi_setprio/enable" },
{ OPT, "events/cgroup/enable" },
} },
{ "irq", "IRQ Events", 0, {
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 8421568..a9c5c82 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -1,6 +1,6 @@
## Permissions to allow system-wide tracing to the kernel trace buffer.
##
-on post-fs
+on late-init
# Allow writing to the kernel trace log.
chmod 0222 /sys/kernel/debug/tracing/trace_marker
@@ -29,6 +29,8 @@
chmod 0666 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable
chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable
chmod 0666 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_pi_setprio/enable
+ chmod 0666 /sys/kernel/tracing/events/sched/sched_pi_setprio/enable
chmod 0666 /sys/kernel/debug/tracing/events/cgroup/enable
chmod 0666 /sys/kernel/tracing/events/cgroup/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5b1e631..6a69844 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -45,6 +45,7 @@
"libpdx_default_transport",
"libprotobuf-cpp-lite",
"libsync",
+ "libtimestats_proto",
"libui",
"libutils",
"libvulkan",
@@ -124,6 +125,7 @@
"SurfaceFlinger.cpp",
"SurfaceInterceptor.cpp",
"SurfaceTracing.cpp",
+ "TimeStats/TimeStats.cpp",
"Transform.cpp",
],
}
@@ -172,6 +174,7 @@
"liblayers_proto",
"liblog",
"libsurfaceflinger",
+ "libtimestats_proto",
"libutils",
],
static_libs: [
@@ -213,5 +216,6 @@
subdirs = [
"layerproto",
+ "TimeStats/timestatsproto",
"tests",
]
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 7fd9d01..6feec53 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -316,6 +316,9 @@
nsecs_t desiredPresentTime = mConsumer->getTimestamp();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+ const std::string layerName(getName().c_str());
+ mTimeStats.setDesiredTime(layerName, mCurrentFrameNumber, desiredPresentTime);
+
std::shared_ptr<FenceTime> frameReadyFence = mConsumer->getCurrentFenceTime();
if (frameReadyFence->isValid()) {
mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
@@ -326,12 +329,15 @@
}
if (presentFence->isValid()) {
+ mTimeStats.setPresentFence(layerName, mCurrentFrameNumber, presentFence);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
} else {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
- mFrameTracker.setActualPresentTime(
- mFlinger->getHwComposer().getRefreshTimestamp(HWC_DISPLAY_PRIMARY));
+ const nsecs_t actualPresentTime =
+ mFlinger->getHwComposer().getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
+ mTimeStats.setPresentTime(layerName, mCurrentFrameNumber, actualPresentTime);
+ mFrameTracker.setActualPresentTime(actualPresentTime);
}
mFrameTracker.advanceFrame();
@@ -441,6 +447,7 @@
// and return early
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
+ mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
android_atomic_dec(&mQueuedFrames);
}
@@ -454,6 +461,7 @@
Mutex::Autolock lock(mQueueItemLock);
mQueueItems.clear();
android_atomic_and(0, &mQueuedFrames);
+ mTimeStats.clearLayerRecord(getName().c_str());
}
// Once we have hit this state, the shadow queue may no longer
@@ -474,10 +482,15 @@
// Remove any stale buffers that have been dropped during
// updateTexImage
while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
+ mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
android_atomic_dec(&mQueuedFrames);
}
+ const std::string layerName(getName().c_str());
+ mTimeStats.setAcquireFence(layerName, currentFrameNumber, mQueueItems[0].mFenceTime);
+ mTimeStats.setLatchTime(layerName, currentFrameNumber, latchTime);
+
mQueueItems.removeAt(0);
}
@@ -515,11 +528,9 @@
recomputeVisibleRegions = true;
}
- // Dataspace::V0_SRGB and Dataspace::V0_SRGB_LINEAR are not legacy
- // data space, however since framework doesn't distinguish them out of
- // legacy SRGB, we have to treat them as the same for now.
- // UNKNOWN is treated as legacy SRGB when the connected api is EGL.
ui::Dataspace dataSpace = mConsumer->getCurrentDataSpace();
+ // treat modern dataspaces as legacy dataspaces whenever possible, until
+ // we can trust the buffer producers
switch (dataSpace) {
case ui::Dataspace::V0_SRGB:
dataSpace = ui::Dataspace::SRGB;
@@ -527,10 +538,17 @@
case ui::Dataspace::V0_SRGB_LINEAR:
dataSpace = ui::Dataspace::SRGB_LINEAR;
break;
- case ui::Dataspace::UNKNOWN:
- if (mConsumer->getCurrentApi() == NATIVE_WINDOW_API_EGL) {
- dataSpace = ui::Dataspace::SRGB;
- }
+ case ui::Dataspace::V0_JFIF:
+ dataSpace = ui::Dataspace::JFIF;
+ break;
+ case ui::Dataspace::V0_BT601_625:
+ dataSpace = ui::Dataspace::BT601_625;
+ break;
+ case ui::Dataspace::V0_BT601_525:
+ dataSpace = ui::Dataspace::BT601_525;
+ break;
+ case ui::Dataspace::V0_BT709:
+ dataSpace = ui::Dataspace::BT709;
break;
default:
break;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index bbc974d..9043234 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1532,10 +1532,16 @@
void Layer::onDisconnect() {
Mutex::Autolock lock(mFrameEventHistoryMutex);
mFrameEventHistory.onDisconnect();
+ mTimeStats.onDisconnect(getName().c_str());
}
void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
+ if (newTimestamps) {
+ mTimeStats.setPostTime(getName().c_str(), newTimestamps->frameNumber,
+ newTimestamps->postedTime);
+ }
+
Mutex::Autolock lock(mFrameEventHistoryMutex);
if (newTimestamps) {
// If there are any unsignaled fences in the aquire timeline at this
@@ -1644,9 +1650,10 @@
return true;
}
-bool Layer::isLegacySrgbDataSpace() const {
- return mDrawingState.dataSpace == ui::Dataspace::SRGB ||
- mDrawingState.dataSpace == ui::Dataspace::SRGB_LINEAR;
+bool Layer::isLegacyDataSpace() const {
+ // return true when no higher bits are set
+ return !(mDrawingState.dataSpace & (ui::Dataspace::STANDARD_MASK |
+ ui::Dataspace::TRANSFER_MASK | ui::Dataspace::RANGE_MASK));
}
void Layer::setParent(const sp<Layer>& layer) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index be3967b..632efbe 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -41,6 +41,7 @@
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
+#include "TimeStats/TimeStats.h"
#include "Transform.h"
#include <layerproto/LayerProtoHeader.h>
@@ -302,8 +303,8 @@
// desaturated in order to match what they appears like visually.
// With color management, these contents will appear desaturated, thus
// needed to be saturated so that they match what they are designed for
- // visually. When returns true, legacy SRGB data space is passed to HWC.
- bool isLegacySrgbDataSpace() const;
+ // visually.
+ bool isLegacyDataSpace() const;
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
@@ -737,6 +738,8 @@
FenceTimeline mAcquireTimeline;
FenceTimeline mReleaseTimeline;
+ TimeStats& mTimeStats = TimeStats::getInstance();
+
// main thread
int mActiveBufferSlot;
sp<GraphicBuffer> mActiveBuffer;
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 08cd5b0..64095dd 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -259,10 +259,8 @@
mState.setTexture(texture);
}
-mat4 GLES20RenderEngine::setupColorTransform(const mat4& colorTransform) {
- mat4 oldTransform = mState.getColorMatrix();
+void GLES20RenderEngine::setupColorTransform(const mat4& colorTransform) {
mState.setColorMatrix(colorTransform);
- return oldTransform;
}
void GLES20RenderEngine::disableTexturing() {
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
index 9acd79b..c9e402d 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
@@ -80,7 +80,7 @@
virtual void setupLayerTexturing(const Texture& texture);
virtual void setupLayerBlackedOut();
virtual void setupFillWithColor(float r, float g, float b, float a);
- virtual mat4 setupColorTransform(const mat4& colorTransform);
+ virtual void setupColorTransform(const mat4& colorTransform);
virtual void disableTexturing();
virtual void disableBlending();
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 89ee64b..5f09ac0 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -251,7 +251,7 @@
const float maxMasteringLumi = 1000.0;
const float maxContentLumi = 1000.0;
const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
- const float maxOutLumi = displayMaxLuminance;
+ float maxOutLumi = displayMaxLuminance;
// Calculate Y value in XYZ color space.
float colorY = CalculateY(color);
@@ -269,18 +269,18 @@
// three control points
const float x0 = 10.0;
const float y0 = 17.0;
- const float x1 = maxOutLumi * 0.75;
- const float y1 = x1;
- const float x2 = x1 + (maxInLumi - x1) / 2.0;
- const float y2 = y1 + (maxOutLumi - y1) * 0.75;
+ float x1 = maxOutLumi * 0.75;
+ float y1 = x1;
+ float x2 = x1 + (maxInLumi - x1) / 2.0;
+ float y2 = y1 + (maxOutLumi - y1) * 0.75;
// horizontal distances between the last three control points
- const float h12 = x2 - x1;
- const float h23 = maxInLumi - x2;
+ float h12 = x2 - x1;
+ float h23 = maxInLumi - x2;
// tangents at the last three control points
- const float m1 = (y2 - y1) / h12;
- const float m3 = (maxOutLumi - y2) / h23;
- const float m2 = (m1 + m3) / 2.0;
+ float m1 = (y2 - y1) / h12;
+ float m3 = (maxOutLumi - y2) / h23;
+ float m2 = (m1 + m3) / 2.0;
if (nits < x0) {
// scale [0.0, x0] to [0.0, y0] linearly
@@ -288,7 +288,7 @@
nits *= slope;
} else if (nits < x1) {
// scale [x0, x1] to [y0, y1] linearly
- const float slope = (y1 - y0) / (x1 - x0);
+ float slope = (y1 - y0) / (x1 - x0);
nits = y0 + (nits - x0) * slope;
} else if (nits < x2) {
// scale [x1, x2] to [y1, y2] using Hermite interp
@@ -448,7 +448,7 @@
if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
// Currently, only the OOTF of BT2020 PQ needs display maximum luminance.
if (needs.getInputTF() == Key::INPUT_TF_ST2084) {
- fs << "uniform float displayMaxLuminance";
+ fs << "uniform float displayMaxLuminance;";
}
if (needs.hasInputTransformMatrix()) {
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index df9e6a7..d559464 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -112,7 +112,7 @@
virtual void setupLayerBlackedOut() = 0;
virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
- virtual mat4 setupColorTransform(const mat4& /* colorTransform */) = 0;
+ virtual void setupColorTransform(const mat4& /* colorTransform */) = 0;
virtual void disableTexturing() = 0;
virtual void disableBlending() = 0;
@@ -224,7 +224,7 @@
void checkErrors() const override;
- mat4 setupColorTransform(const mat4& /* colorTransform */) override { return mat4(); }
+ void setupColorTransform(const mat4& /* colorTransform */) override {}
// internal to RenderEngine
EGLDisplay getEGLDisplay() const;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f736c8c..be70244 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -244,7 +244,6 @@
mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
- mHasColorMatrix(false),
mHasPoweredOff(false),
mNumLayers(0),
mVrFlingerRequestsDisplay(false),
@@ -723,10 +722,13 @@
}
void SurfaceFlinger::readPersistentProperties() {
+ Mutex::Autolock _l(mStateLock);
+
char value[PROPERTY_VALUE_MAX];
property_get("persist.sys.sf.color_saturation", value, "1.0");
mGlobalSaturationFactor = atof(value);
+ updateColorMatrixLocked();
ALOGV("Saturation is set to %.2f", mGlobalSaturationFactor);
property_get("persist.sys.sf.native_mode", value, "0");
@@ -1469,9 +1471,12 @@
(mPreviousPresentFence->getSignalTime() ==
Fence::SIGNAL_TIME_PENDING);
ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
- if (mPropagateBackpressure && frameMissed) {
- signalLayerUpdate();
- break;
+ if (frameMissed) {
+ mTimeStats.incrementMissedFrames();
+ if (mPropagateBackpressure) {
+ signalLayerUpdate();
+ break;
+ }
}
// Now that we're going to make it to the handleMessageTransaction()
@@ -1771,6 +1776,11 @@
mAnimFrameTracker.advanceFrame();
}
+ mTimeStats.incrementTotalFrames();
+ if (mHadClientComposition) {
+ mTimeStats.incrementClientCompositionFrames();
+ }
+
if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) &&
hw->getPowerMode() == HWC_POWER_MODE_OFF) {
return;
@@ -1859,32 +1869,16 @@
}
}
-mat4 SurfaceFlinger::computeSaturationMatrix() const {
- if (mGlobalSaturationFactor == 1.0f) {
- return mat4();
- }
-
- // Rec.709 luma coefficients
- float3 luminance{0.213f, 0.715f, 0.072f};
- luminance *= 1.0f - mGlobalSaturationFactor;
- return mat4(
- vec4{luminance.r + mGlobalSaturationFactor, luminance.r, luminance.r, 0.0f},
- vec4{luminance.g, luminance.g + mGlobalSaturationFactor, luminance.g, 0.0f},
- vec4{luminance.b, luminance.b, luminance.b + mGlobalSaturationFactor, 0.0f},
- vec4{0.0f, 0.0f, 0.0f, 1.0f}
- );
-}
-
// Returns a dataspace that fits all visible layers. The returned dataspace
// can only be one of
//
-// - Dataspace::V0_SRGB
+// - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
// - Dataspace::DISPLAY_P3
// - Dataspace::V0_SCRGB_LINEAR
// TODO(b/73825729) Add BT2020 data space.
ui::Dataspace SurfaceFlinger::getBestDataspace(
const sp<const DisplayDevice>& displayDevice) const {
- Dataspace bestDataspace = Dataspace::V0_SRGB;
+ Dataspace bestDataspace = Dataspace::SRGB;
for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
switch (layer->getDataSpace()) {
case Dataspace::V0_SCRGB:
@@ -1935,7 +1929,7 @@
break;
default:
*outMode = ColorMode::SRGB;
- *outDataSpace = Dataspace::V0_SRGB;
+ *outDataSpace = Dataspace::SRGB;
break;
}
}
@@ -2000,9 +1994,6 @@
}
}
-
- mat4 colorMatrix = mColorMatrix * computeSaturationMatrix() * mDaltonizer();
-
// Set the per-frame data
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
@@ -2011,9 +2002,9 @@
if (hwcId < 0) {
continue;
}
- if (colorMatrix != mPreviousColorMatrix) {
- displayDevice->setColorTransform(colorMatrix);
- status_t result = getBE().mHwc->setColorTransform(hwcId, colorMatrix);
+ if (mDrawingState.colorMatrixChanged) {
+ displayDevice->setColorTransform(mDrawingState.colorMatrix);
+ status_t result = getBE().mHwc->setColorTransform(hwcId, mDrawingState.colorMatrix);
ALOGE_IF(result != NO_ERROR, "Failed to set color transform on "
"display %zd: %d", displayId, result);
}
@@ -2046,7 +2037,7 @@
}
}
- mPreviousColorMatrix = colorMatrix;
+ mDrawingState.colorMatrixChanged = false;
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
@@ -2635,6 +2626,9 @@
mAnimCompositionPending = mAnimTransactionPending;
mDrawingState = mCurrentState;
+ // clear the "changed" flags in current state
+ mCurrentState.colorMatrixChanged = false;
+
mDrawingState.traverseInZOrder([](Layer* layer) {
layer->commitChildList();
});
@@ -2878,19 +2872,13 @@
const DisplayRenderArea renderArea(displayDevice);
const auto hwcId = displayDevice->getHwcDisplayId();
const bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId);
- const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId);
- const bool skipClientColorTransform = getBE().mHwc->hasCapability(
- HWC2::Capability::SkipClientColorTransform);
ATRACE_INT("hasClientComposition", hasClientComposition);
- mat4 oldColorMatrix;
- mat4 legacySrgbSaturationMatrix = mLegacySrgbSaturationMatrix;
- const bool applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
- if (applyColorMatrix) {
- mat4 colorMatrix = mColorMatrix * computeSaturationMatrix() * mDaltonizer();
- oldColorMatrix = getRenderEngine().setupColorTransform(colorMatrix);
- legacySrgbSaturationMatrix = colorMatrix * legacySrgbSaturationMatrix;
- }
+ bool applyColorMatrix = false;
+ bool applyLegacyColorMatrix = false;
+ mat4 colorMatrix;
+ mat4 legacyColorMatrix;
+ const mat4* currentColorMatrix = nullptr;
if (hasClientComposition) {
ALOGV("hasClientComposition");
@@ -2903,6 +2891,26 @@
getBE().mRenderEngine->setDisplayMaxLuminance(
displayDevice->getHdrCapabilities().getDesiredMaxLuminance());
+ const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId);
+ const bool skipClientColorTransform = getBE().mHwc->hasCapability(
+ HWC2::Capability::SkipClientColorTransform);
+
+ applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
+ if (applyColorMatrix) {
+ colorMatrix = mDrawingState.colorMatrix;
+ }
+
+ applyLegacyColorMatrix = (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
+ outputDataspace != Dataspace::UNKNOWN &&
+ outputDataspace != Dataspace::SRGB);
+ if (applyLegacyColorMatrix) {
+ if (applyColorMatrix) {
+ legacyColorMatrix = colorMatrix * mLegacySrgbSaturationMatrix;
+ } else {
+ legacyColorMatrix = mLegacySrgbSaturationMatrix;
+ }
+ }
+
if (!displayDevice->makeCurrent()) {
ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
displayDevice->getDisplayName().string());
@@ -2964,69 +2972,57 @@
ALOGV("Rendering client layers");
const Transform& displayTransform = displayDevice->getTransform();
- if (hwcId >= 0) {
- // we're using h/w composer
- bool firstLayer = true;
- for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- const Region clip(bounds.intersect(
- displayTransform.transform(layer->visibleRegion)));
- ALOGV("Layer: %s", layer->getName().string());
- ALOGV(" Composition type: %s",
- to_string(layer->getCompositionType(hwcId)).c_str());
- if (!clip.isEmpty()) {
- switch (layer->getCompositionType(hwcId)) {
- case HWC2::Composition::Cursor:
- case HWC2::Composition::Device:
- case HWC2::Composition::Sideband:
- case HWC2::Composition::SolidColor: {
- const Layer::State& state(layer->getDrawingState());
- if (layer->getClearClientTarget(hwcId) && !firstLayer &&
- layer->isOpaque(state) && (state.color.a == 1.0f)
- && hasClientComposition) {
- // never clear the very first layer since we're
- // guaranteed the FB is already cleared
- layer->clearWithOpenGL(renderArea);
- }
- break;
+ bool firstLayer = true;
+ for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+ const Region clip(bounds.intersect(
+ displayTransform.transform(layer->visibleRegion)));
+ ALOGV("Layer: %s", layer->getName().string());
+ ALOGV(" Composition type: %s",
+ to_string(layer->getCompositionType(hwcId)).c_str());
+ if (!clip.isEmpty()) {
+ switch (layer->getCompositionType(hwcId)) {
+ case HWC2::Composition::Cursor:
+ case HWC2::Composition::Device:
+ case HWC2::Composition::Sideband:
+ case HWC2::Composition::SolidColor: {
+ const Layer::State& state(layer->getDrawingState());
+ if (layer->getClearClientTarget(hwcId) && !firstLayer &&
+ layer->isOpaque(state) && (state.color.a == 1.0f)
+ && hasClientComposition) {
+ // never clear the very first layer since we're
+ // guaranteed the FB is already cleared
+ layer->clearWithOpenGL(renderArea);
}
- case HWC2::Composition::Client: {
- // Only apply saturation matrix layer that is legacy SRGB dataspace
- // when auto color mode is on.
- bool restore = false;
- mat4 savedMatrix;
- if (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
- layer->isLegacySrgbDataSpace()) {
- savedMatrix =
- getRenderEngine().setupColorTransform(legacySrgbSaturationMatrix);
- restore = true;
- }
- layer->draw(renderArea, clip);
- if (restore) {
- getRenderEngine().setupColorTransform(savedMatrix);
- }
- break;
- }
- default:
- break;
+ break;
}
- } else {
- ALOGV(" Skipping for empty clip");
+ case HWC2::Composition::Client: {
+ // switch color matrices lazily
+ if (layer->isLegacyDataSpace()) {
+ if (applyLegacyColorMatrix && currentColorMatrix != &legacyColorMatrix) {
+ getRenderEngine().setupColorTransform(legacyColorMatrix);
+ currentColorMatrix = &legacyColorMatrix;
+ }
+ } else {
+ if (applyColorMatrix && currentColorMatrix != &colorMatrix) {
+ getRenderEngine().setupColorTransform(colorMatrix);
+ currentColorMatrix = &colorMatrix;
+ }
+ }
+
+ layer->draw(renderArea, clip);
+ break;
+ }
+ default:
+ break;
}
- firstLayer = false;
+ } else {
+ ALOGV(" Skipping for empty clip");
}
- } else {
- // we're not using h/w composer
- for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- const Region clip(bounds.intersect(
- displayTransform.transform(layer->visibleRegion)));
- if (!clip.isEmpty()) {
- layer->draw(renderArea, clip);
- }
- }
+ firstLayer = false;
}
- if (applyColorMatrix) {
- getRenderEngine().setupColorTransform(oldColorMatrix);
+ if (applyColorMatrix || applyLegacyColorMatrix) {
+ getRenderEngine().setupColorTransform(mat4());
}
// disable scissor at the end of the frame
@@ -3824,12 +3820,6 @@
size_t index = 0;
size_t numArgs = args.size();
- if (asProto) {
- LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
- result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
- dumpAll = false;
- }
-
if (numArgs) {
if ((index < numArgs) &&
(args[index] == String16("--list"))) {
@@ -3906,10 +3896,21 @@
mLayerStats.dump(result);
dumpAll = false;
}
+
+ if ((index < numArgs) && (args[index] == String16("--timestats"))) {
+ index++;
+ mTimeStats.parseArgs(asProto, args, index, result);
+ dumpAll = false;
+ }
}
if (dumpAll) {
- dumpAllLocked(args, index, result);
+ if (asProto) {
+ LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
+ result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
+ } else {
+ dumpAllLocked(args, index, result);
+ }
}
if (locked) {
@@ -4349,6 +4350,30 @@
return true;
}
+void SurfaceFlinger::updateColorMatrixLocked() {
+ mat4 colorMatrix;
+ if (mGlobalSaturationFactor != 1.0f) {
+ // Rec.709 luma coefficients
+ float3 luminance{0.213f, 0.715f, 0.072f};
+ luminance *= 1.0f - mGlobalSaturationFactor;
+ mat4 saturationMatrix = mat4(
+ vec4{luminance.r + mGlobalSaturationFactor, luminance.r, luminance.r, 0.0f},
+ vec4{luminance.g, luminance.g + mGlobalSaturationFactor, luminance.g, 0.0f},
+ vec4{luminance.b, luminance.b, luminance.b + mGlobalSaturationFactor, 0.0f},
+ vec4{0.0f, 0.0f, 0.0f, 1.0f}
+ );
+ colorMatrix = mClientColorMatrix * saturationMatrix * mDaltonizer();
+ } else {
+ colorMatrix = mClientColorMatrix * mDaltonizer();
+ }
+
+ if (mCurrentState.colorMatrix != colorMatrix) {
+ mCurrentState.colorMatrix = colorMatrix;
+ mCurrentState.colorMatrixChanged = true;
+ setTransactionFlags(eTransactionNeeded);
+ }
+}
+
status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
switch (code) {
case CREATE_CONNECTION:
@@ -4483,6 +4508,7 @@
return NO_ERROR;
}
case 1014: {
+ Mutex::Autolock _l(mStateLock);
// daltonize
n = data.readInt32();
switch (n % 10) {
@@ -4504,33 +4530,33 @@
} else {
mDaltonizer.setMode(ColorBlindnessMode::Simulation);
}
- invalidateHwcGeometry();
- repaintEverything();
+
+ updateColorMatrixLocked();
return NO_ERROR;
}
case 1015: {
+ Mutex::Autolock _l(mStateLock);
// apply a color matrix
n = data.readInt32();
if (n) {
// color matrix is sent as a column-major mat4 matrix
for (size_t i = 0 ; i < 4; i++) {
for (size_t j = 0; j < 4; j++) {
- mColorMatrix[i][j] = data.readFloat();
+ mClientColorMatrix[i][j] = data.readFloat();
}
}
} else {
- mColorMatrix = mat4();
+ mClientColorMatrix = mat4();
}
// Check that supplied matrix's last row is {0,0,0,1} so we can avoid
// the division by w in the fragment shader
- float4 lastRow(transpose(mColorMatrix)[3]);
+ float4 lastRow(transpose(mClientColorMatrix)[3]);
if (any(greaterThan(abs(lastRow - float4{0, 0, 0, 1}), float4{1e-4f}))) {
ALOGE("The color transform's last row must be (0, 0, 0, 1)");
}
- invalidateHwcGeometry();
- repaintEverything();
+ updateColorMatrixLocked();
return NO_ERROR;
}
// This is an experimental interface
@@ -4573,10 +4599,10 @@
return NO_ERROR;
}
case 1022: { // Set saturation boost
+ Mutex::Autolock _l(mStateLock);
mGlobalSaturationFactor = std::max(0.0f, std::min(data.readFloat(), 2.0f));
- invalidateHwcGeometry();
- repaintEverything();
+ updateColorMatrixLocked();
return NO_ERROR;
}
case 1023: { // Set native mode
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 33706da..54cf63c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -63,6 +63,7 @@
#include "SurfaceInterceptor.h"
#include "SurfaceTracing.h"
#include "StartPropertySetThread.h"
+#include "TimeStats/TimeStats.h"
#include "VSyncModulator.h"
#include "DisplayHardware/HWC2.h"
@@ -372,6 +373,10 @@
// always uses the Drawing StateSet.
layersSortedByZ = other.layersSortedByZ;
displays = other.displays;
+ colorMatrixChanged = other.colorMatrixChanged;
+ if (colorMatrixChanged) {
+ colorMatrix = other.colorMatrix;
+ }
return *this;
}
@@ -379,6 +384,9 @@
LayerVector layersSortedByZ;
DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
+ bool colorMatrixChanged = true;
+ mat4 colorMatrix;
+
void traverseInZOrder(const LayerVector::Visitor& visitor) const;
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
@@ -652,8 +660,6 @@
ui::ColorMode* outMode,
ui::Dataspace* outDataSpace) const;
- mat4 computeSaturationMatrix() const;
-
void setUpHWComposer();
void doComposition();
void doDebugFlashRegions();
@@ -740,6 +746,8 @@
// Check to see if we should handoff to vr flinger.
void updateVrFlinger();
+ void updateColorMatrixLocked();
+
/* ------------------------------------------------------------------------
* Attributes
*/
@@ -753,6 +761,11 @@
bool mAnimTransactionPending;
SortedVector< sp<Layer> > mLayersPendingRemoval;
+ // global color transform states
+ Daltonizer mDaltonizer;
+ float mGlobalSaturationFactor = 1.0f;
+ mat4 mClientColorMatrix;
+
// Can't be unordered_set because wp<> isn't hashable
std::set<wp<IBinder>> mGraphicBufferProducerList;
size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS;
@@ -815,6 +828,7 @@
std::make_unique<impl::SurfaceInterceptor>(this);
SurfaceTracing mTracing;
LayerStats mLayerStats;
+ TimeStats& mTimeStats = TimeStats::getInstance();
bool mUseHwcVirtualDisplays = false;
// Restrict layers to use two buffers in their bufferqueues.
@@ -842,12 +856,6 @@
bool mInjectVSyncs;
- Daltonizer mDaltonizer;
-
- mat4 mPreviousColorMatrix;
- mat4 mColorMatrix;
- bool mHasColorMatrix;
-
// Static screen stats
bool mHasPoweredOff;
@@ -865,8 +873,6 @@
DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::MANAGED;
// Applied on sRGB layers when the render intent is non-colorimetric.
mat4 mLegacySrgbSaturationMatrix;
- // Applied globally.
- float mGlobalSaturationFactor = 1.0f;
bool mBuiltinDisplaySupportsEnhance = false;
using CreateBufferQueueFunction =
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
new file mode 100644
index 0000000..a6833a5
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -0,0 +1,495 @@
+/*
+ * Copyright 2018 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.
+ */
+#undef LOG_TAG
+#define LOG_TAG "TimeStats"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "TimeStats.h"
+
+#include <android-base/stringprintf.h>
+
+#include <log/log.h>
+
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <regex>
+
+namespace android {
+
+TimeStats& TimeStats::getInstance() {
+ static std::unique_ptr<TimeStats> sInstance;
+ static std::once_flag sOnceFlag;
+
+ std::call_once(sOnceFlag, [] { sInstance.reset(new TimeStats); });
+ return *sInstance.get();
+}
+
+void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
+ String8& result) {
+ ATRACE_CALL();
+
+ if (args.size() > index + 10) {
+ ALOGD("Invalid args count");
+ return;
+ }
+
+ std::unordered_map<std::string, int32_t> argsMap;
+ while (index < args.size()) {
+ argsMap[std::string(String8(args[index]).c_str())] = index;
+ ++index;
+ }
+
+ if (argsMap.count("-disable")) {
+ disable();
+ }
+
+ if (argsMap.count("-dump")) {
+ int64_t maxLayers = 0;
+ auto iter = argsMap.find("-maxlayers");
+ if (iter != argsMap.end() && iter->second + 1 < static_cast<int32_t>(args.size())) {
+ maxLayers = strtol(String8(args[iter->second + 1]).c_str(), nullptr, 10);
+ maxLayers = std::clamp(maxLayers, int64_t(0), int64_t(UINT32_MAX));
+ }
+
+ dump(asProto, static_cast<uint32_t>(maxLayers), result);
+ }
+
+ if (argsMap.count("-clear")) {
+ clear();
+ }
+
+ if (argsMap.count("-enable")) {
+ enable();
+ }
+}
+
+void TimeStats::incrementTotalFrames() {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ timeStats.totalFrames++;
+}
+
+void TimeStats::incrementMissedFrames() {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ timeStats.missedFrames++;
+}
+
+void TimeStats::incrementClientCompositionFrames() {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ timeStats.clientCompositionFrames++;
+}
+
+bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord) {
+ if (!timeRecord->ready) {
+ ALOGV("[%s]-[%" PRIu64 "]-presentFence is still not received", layerName.c_str(),
+ timeRecord->frameNumber);
+ return false;
+ }
+
+ if (timeRecord->acquireFence != nullptr) {
+ if (timeRecord->acquireFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+ return false;
+ }
+ if (timeRecord->acquireFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) {
+ timeRecord->acquireTime = timeRecord->acquireFence->getSignalTime();
+ timeRecord->acquireFence = nullptr;
+ } else {
+ ALOGV("[%s]-[%" PRIu64 "]-acquireFence signal time is invalid", layerName.c_str(),
+ timeRecord->frameNumber);
+ }
+ }
+
+ if (timeRecord->presentFence != nullptr) {
+ if (timeRecord->presentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+ return false;
+ }
+ if (timeRecord->presentFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) {
+ timeRecord->presentTime = timeRecord->presentFence->getSignalTime();
+ timeRecord->presentFence = nullptr;
+ } else {
+ ALOGV("[%s]-[%" PRIu64 "]-presentFence signal time invalid", layerName.c_str(),
+ timeRecord->frameNumber);
+ }
+ }
+
+ return true;
+}
+
+static int32_t msBetween(nsecs_t start, nsecs_t end) {
+ int64_t delta = (end - start) / 1000000;
+ delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
+ return static_cast<int32_t>(delta);
+}
+
+void TimeStats::flushAvailableRecordsToStatsLocked(const std::string& layerName) {
+ ATRACE_CALL();
+
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
+ std::vector<TimeRecord>& timeRecords = layerRecord.timeRecords;
+ while (!timeRecords.empty()) {
+ if (!recordReadyLocked(layerName, &timeRecords[0])) break;
+ ALOGV("[%s]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerName.c_str(),
+ timeRecords[0].frameNumber, timeRecords[0].presentTime);
+
+ if (prevTimeRecord.ready) {
+ if (!timeStats.stats.count(layerName)) {
+ timeStats.stats[layerName].layerName = layerName;
+ timeStats.stats[layerName].statsStart = static_cast<int64_t>(std::time(0));
+ }
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timeStats.stats[layerName];
+ timeStatsLayer.totalFrames++;
+
+ const int32_t postToPresentMs =
+ msBetween(timeRecords[0].postTime, timeRecords[0].presentTime);
+ ALOGV("[%s]-[%" PRIu64 "]-post2present[%d]", layerName.c_str(),
+ timeRecords[0].frameNumber, postToPresentMs);
+ timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
+
+ const int32_t acquireToPresentMs =
+ msBetween(timeRecords[0].acquireTime, timeRecords[0].presentTime);
+ ALOGV("[%s]-[%" PRIu64 "]-acquire2present[%d]", layerName.c_str(),
+ timeRecords[0].frameNumber, acquireToPresentMs);
+ timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
+
+ const int32_t latchToPresentMs =
+ msBetween(timeRecords[0].latchTime, timeRecords[0].presentTime);
+ ALOGV("[%s]-[%" PRIu64 "]-latch2present[%d]", layerName.c_str(),
+ timeRecords[0].frameNumber, latchToPresentMs);
+ timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
+
+ const int32_t desiredToPresentMs =
+ msBetween(timeRecords[0].desiredTime, timeRecords[0].presentTime);
+ ALOGV("[%s]-[%" PRIu64 "]-desired2present[%d]", layerName.c_str(),
+ timeRecords[0].frameNumber, desiredToPresentMs);
+ timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
+
+ const int32_t presentToPresentMs =
+ msBetween(prevTimeRecord.presentTime, timeRecords[0].presentTime);
+ ALOGV("[%s]-[%" PRIu64 "]-present2present[%d]", layerName.c_str(),
+ timeRecords[0].frameNumber, presentToPresentMs);
+ timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
+
+ timeStats.stats[layerName].statsEnd = static_cast<int64_t>(std::time(0));
+ }
+ prevTimeRecord = timeRecords[0];
+ // TODO(zzyiwei): change timeRecords to use std::deque
+ timeRecords.erase(timeRecords.begin());
+ layerRecord.waitData--;
+ }
+}
+
+static bool layerNameIsValid(const std::string& layerName) {
+ // This regular expression captures the following layer names for instance:
+ // 1) StatusBat#0
+ // 2) NavigationBar#1
+ // 3) com.*#0
+ // 4) SurfaceView - com.*#0
+ // Using [-\\s\t]+ for the conjunction part between SurfaceView and com.* is
+ // a bit more robust in case there's a slight change.
+ // The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
+ std::regex re("(((SurfaceView[-\\s\\t]+)?com\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
+ return std::regex_match(layerName.begin(), layerName.end(), re);
+}
+
+void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerName.c_str(), frameNumber, postTime);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName) && !layerNameIsValid(layerName)) {
+ return;
+ }
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
+ ALOGV("[%s]-timeRecords is already at its maximum size[%zu]", layerName.c_str(),
+ MAX_NUM_TIME_RECORDS);
+ // TODO(zzyiwei): if this happens, there must be a present fence missing
+ // or waitData is not in the correct position. Need to think out a
+ // reasonable way to recover from this state.
+ return;
+ }
+ // For most media content, the acquireFence is invalid because the buffer is
+ // ready at the queueBuffer stage. In this case, acquireTime should be given
+ // a default value as postTime.
+ TimeRecord timeRecord = {
+ .frameNumber = frameNumber,
+ .postTime = postTime,
+ .acquireTime = postTime,
+ };
+ layerRecord.timeRecords.push_back(timeRecord);
+ if (layerRecord.waitData < 0 ||
+ layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
+ layerRecord.waitData = layerRecord.timeRecords.size() - 1;
+}
+
+void TimeStats::setLatchTime(const std::string& layerName, uint64_t frameNumber,
+ nsecs_t latchTime) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerName.c_str(), frameNumber, latchTime);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
+ if (timeRecord.frameNumber == frameNumber) {
+ timeRecord.latchTime = latchTime;
+ }
+}
+
+void TimeStats::setDesiredTime(const std::string& layerName, uint64_t frameNumber,
+ nsecs_t desiredTime) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ desiredTime);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
+ if (timeRecord.frameNumber == frameNumber) {
+ timeRecord.desiredTime = desiredTime;
+ }
+}
+
+void TimeStats::setAcquireTime(const std::string& layerName, uint64_t frameNumber,
+ nsecs_t acquireTime) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ acquireTime);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
+ if (timeRecord.frameNumber == frameNumber) {
+ timeRecord.acquireTime = acquireTime;
+ }
+}
+
+void TimeStats::setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& acquireFence) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ acquireFence->getSignalTime());
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
+ if (timeRecord.frameNumber == frameNumber) {
+ timeRecord.acquireFence = acquireFence;
+ }
+}
+
+void TimeStats::setPresentTime(const std::string& layerName, uint64_t frameNumber,
+ nsecs_t presentTime) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ presentTime);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
+ if (timeRecord.frameNumber == frameNumber) {
+ timeRecord.presentTime = presentTime;
+ timeRecord.ready = true;
+ layerRecord.waitData++;
+ }
+
+ flushAvailableRecordsToStatsLocked(layerName);
+}
+
+void TimeStats::setPresentFence(const std::string& layerName, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& presentFence) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ presentFence->getSignalTime());
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
+ if (timeRecord.frameNumber == frameNumber) {
+ timeRecord.presentFence = presentFence;
+ timeRecord.ready = true;
+ layerRecord.waitData++;
+ }
+
+ flushAvailableRecordsToStatsLocked(layerName);
+}
+
+void TimeStats::onDisconnect(const std::string& layerName) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-onDisconnect", layerName.c_str());
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ flushAvailableRecordsToStatsLocked(layerName);
+ timeStatsTracker.erase(layerName);
+}
+
+void TimeStats::clearLayerRecord(const std::string& layerName) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-clearLayerRecord", layerName.c_str());
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ layerRecord.timeRecords.clear();
+ layerRecord.prevTimeRecord.ready = false;
+ layerRecord.waitData = -1;
+}
+
+void TimeStats::removeTimeRecord(const std::string& layerName, uint64_t frameNumber) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%s]-[%" PRIu64 "]-removeTimeRecord", layerName.c_str(), frameNumber);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!timeStatsTracker.count(layerName)) return;
+ LayerRecord& layerRecord = timeStatsTracker[layerName];
+ size_t removeAt = 0;
+ for (const TimeRecord& record : layerRecord.timeRecords) {
+ if (record.frameNumber == frameNumber) break;
+ removeAt++;
+ }
+ if (removeAt == layerRecord.timeRecords.size()) return;
+ layerRecord.timeRecords.erase(layerRecord.timeRecords.begin() + removeAt);
+ if (layerRecord.waitData > static_cast<int32_t>(removeAt)) {
+ --layerRecord.waitData;
+ }
+}
+
+void TimeStats::enable() {
+ if (mEnabled.load()) return;
+
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ ALOGD("Enabled");
+ mEnabled.store(true);
+ timeStats.statsStart = static_cast<int64_t>(std::time(0));
+}
+
+void TimeStats::disable() {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ ALOGD("Disabled");
+ mEnabled.store(false);
+ timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+}
+
+void TimeStats::clear() {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ ALOGD("Cleared");
+ timeStats.dumpStats.clear();
+ timeStats.stats.clear();
+ timeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
+ timeStats.statsEnd = 0;
+ timeStats.totalFrames = 0;
+ timeStats.missedFrames = 0;
+ timeStats.clientCompositionFrames = 0;
+}
+
+bool TimeStats::isEnabled() {
+ return mEnabled.load();
+}
+
+void TimeStats::dump(bool asProto, uint32_t maxLayers, String8& result) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (timeStats.statsStart == 0) {
+ return;
+ }
+
+ timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+
+ // TODO(zzyiwei): refactor dumpStats into TimeStatsHelper
+ timeStats.dumpStats.clear();
+ for (auto& ele : timeStats.stats) {
+ timeStats.dumpStats.push_back(&ele.second);
+ }
+
+ std::sort(timeStats.dumpStats.begin(), timeStats.dumpStats.end(),
+ [](TimeStatsHelper::TimeStatsLayer* const& l,
+ TimeStatsHelper::TimeStatsLayer* const& r) {
+ return l->totalFrames > r->totalFrames;
+ });
+
+ if (maxLayers != 0 && maxLayers < timeStats.dumpStats.size()) {
+ timeStats.dumpStats.resize(maxLayers);
+ }
+
+ if (asProto) {
+ dumpAsProtoLocked(result);
+ } else {
+ dumpAsTextLocked(result);
+ }
+}
+
+void TimeStats::dumpAsTextLocked(String8& result) {
+ ALOGD("Dumping TimeStats as text");
+ result.append(timeStats.toString().c_str());
+ result.append("\n");
+}
+
+void TimeStats::dumpAsProtoLocked(String8& result) {
+ ALOGD("Dumping TimeStats as proto");
+ SFTimeStatsGlobalProto timeStatsProto = timeStats.toProto();
+ result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
new file mode 100644
index 0000000..f76a62e
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <timestatsproto/TimeStatsHelper.h>
+#include <timestatsproto/TimeStatsProtoHeader.h>
+
+#include <ui/FenceTime.h>
+
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+class String8;
+
+class TimeStats {
+ // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
+ // static const size_t MAX_NUM_LAYER_RECORDS = 200;
+ static const size_t MAX_NUM_TIME_RECORDS = 64;
+
+ struct TimeRecord {
+ bool ready = false;
+ uint64_t frameNumber = 0;
+ nsecs_t postTime = 0;
+ nsecs_t latchTime = 0;
+ nsecs_t acquireTime = 0;
+ nsecs_t desiredTime = 0;
+ nsecs_t presentTime = 0;
+ std::shared_ptr<FenceTime> acquireFence;
+ std::shared_ptr<FenceTime> presentFence;
+ };
+
+ struct LayerRecord {
+ // This is the index in timeRecords, at which the timestamps for that
+ // specific frame are still not fully received. This is not waiting for
+ // fences to signal, but rather waiting to receive those fences/timestamps.
+ int32_t waitData = -1;
+ TimeRecord prevTimeRecord;
+ std::vector<TimeRecord> timeRecords;
+ };
+
+public:
+ static TimeStats& getInstance();
+ void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
+ void incrementTotalFrames();
+ void incrementMissedFrames();
+ void incrementClientCompositionFrames();
+
+ void setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime);
+ void setLatchTime(const std::string& layerName, uint64_t frameNumber, nsecs_t latchTime);
+ void setDesiredTime(const std::string& layerName, uint64_t frameNumber, nsecs_t desiredTime);
+ void setAcquireTime(const std::string& layerName, uint64_t frameNumber, nsecs_t acquireTime);
+ void setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& acquireFence);
+ void setPresentTime(const std::string& layerName, uint64_t frameNumber, nsecs_t presentTime);
+ void setPresentFence(const std::string& layerName, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& presentFence);
+ void onDisconnect(const std::string& layerName);
+ void clearLayerRecord(const std::string& layerName);
+ void removeTimeRecord(const std::string& layerName, uint64_t frameNumber);
+
+private:
+ TimeStats() = default;
+
+ bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord);
+ void flushAvailableRecordsToStatsLocked(const std::string& layerName);
+
+ void enable();
+ void disable();
+ void clear();
+ bool isEnabled();
+ void dump(bool asProto, uint32_t maxLayer, String8& result);
+ void dumpAsTextLocked(String8& result);
+ void dumpAsProtoLocked(String8& result);
+
+ std::atomic<bool> mEnabled = false;
+ std::mutex mMutex;
+ TimeStatsHelper::TimeStatsGlobal timeStats;
+ std::unordered_map<std::string, LayerRecord> timeStatsTracker;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
new file mode 100644
index 0000000..66aa719
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -0,0 +1,55 @@
+cc_library_shared {
+ name: "libtimestats_proto",
+ vendor_available: true,
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "TimeStatsHelper.cpp",
+ "timestats.proto",
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.common@1.1",
+ "libui",
+ "libprotobuf-cpp-lite",
+ "libbase",
+ "liblog",
+ ],
+
+ proto: {
+ export_proto_headers: true,
+ },
+
+ cppflags: [
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-format",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-float-conversion",
+ "-Wno-disabled-macro-expansion",
+ "-Wno-float-equal",
+ "-Wno-sign-conversion",
+ "-Wno-padded",
+ "-Wno-old-style-cast",
+ "-Wno-undef",
+ ],
+
+}
+
+java_library_static {
+ name: "timestatsprotosnano",
+ host_supported: true,
+ proto: {
+ type: "nano",
+ },
+ srcs: ["*.proto"],
+ no_framework_libs: true,
+ target: {
+ android: {
+ jarjar_rules: "jarjar-rules.txt",
+ },
+ host: {
+ static_libs: ["libprotobuf-java-nano"],
+ },
+ },
+}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
new file mode 100644
index 0000000..3e5007c
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include <android-base/stringprintf.h>
+#include <timestatsproto/TimeStatsHelper.h>
+
+#include <array>
+#include <regex>
+
+#define HISTOGRAM_SIZE 85
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
+namespace android {
+namespace surfaceflinger {
+
+// Time buckets for histogram, the calculated time deltas will be lower bounded
+// to the buckets in this array.
+static const std::array<int32_t, HISTOGRAM_SIZE> histogramConfig =
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 36, 38, 40, 42, 44, 46, 48, 50, 54, 58, 62, 66, 70, 74, 78, 82,
+ 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126, 130, 134, 138, 142, 146, 150,
+ 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000};
+
+void TimeStatsHelper::Histogram::insert(int32_t delta) {
+ if (delta < 0) return;
+ // std::lower_bound won't work on out of range values
+ if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) {
+ hist[histogramConfig[HISTOGRAM_SIZE - 1]]++;
+ return;
+ }
+ auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta);
+ hist[*iter]++;
+}
+
+float TimeStatsHelper::Histogram::averageTime() {
+ int64_t ret = 0;
+ int64_t count = 0;
+ for (auto ele : hist) {
+ count += ele.second;
+ ret += ele.first * ele.second;
+ }
+ return static_cast<float>(ret) / count;
+}
+
+std::string TimeStatsHelper::Histogram::toString() {
+ std::string result;
+ for (int32_t i = 0; i < HISTOGRAM_SIZE; ++i) {
+ int32_t bucket = histogramConfig[i];
+ int32_t count = (hist.count(bucket) == 0) ? 0 : hist[bucket];
+ StringAppendF(&result, "%dms=%d ", bucket, count);
+ }
+ result.back() = '\n';
+ return result;
+}
+
+static std::string getPackageName(const std::string& layerName) {
+ // This regular expression captures the following for instance:
+ // StatusBar in StatusBar#0
+ // com.appname in com.appname/com.appname.activity#0
+ // com.appname in SurfaceView - com.appname/com.appname.activity#0
+ const std::regex re("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
+ std::smatch match;
+ if (std::regex_match(layerName.begin(), layerName.end(), match, re)) {
+ // There must be a match for group 1 otherwise the whole string is not
+ // matched and the above will return false
+ return match[1];
+ }
+ return "";
+}
+
+std::string TimeStatsHelper::TimeStatsLayer::toString() {
+ std::string result = "";
+ StringAppendF(&result, "layerName = %s\n", layerName.c_str());
+ packageName = getPackageName(layerName);
+ StringAppendF(&result, "packageName = %s\n", packageName.c_str());
+ StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart));
+ StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd));
+ StringAppendF(&result, "totalFrames= %d\n", totalFrames);
+ if (deltas.find("present2present") != deltas.end()) {
+ StringAppendF(&result, "averageFPS = %.3f\n",
+ 1000.0 / deltas["present2present"].averageTime());
+ }
+ for (auto ele : deltas) {
+ StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
+ StringAppendF(&result, "%s", ele.second.toString().c_str());
+ }
+
+ return result;
+}
+
+std::string TimeStatsHelper::TimeStatsGlobal::toString() {
+ std::string result = "SurfaceFlinger TimeStats:\n";
+ StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart));
+ StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd));
+ StringAppendF(&result, "totalFrames= %d\n", totalFrames);
+ StringAppendF(&result, "missedFrames= %d\n", missedFrames);
+ StringAppendF(&result, "clientCompositionFrames= %d\n", clientCompositionFrames);
+ StringAppendF(&result, "TimeStats for each layer is as below:\n");
+ for (auto ele : dumpStats) {
+ StringAppendF(&result, "%s", ele->toString().c_str());
+ }
+
+ return result;
+}
+
+SFTimeStatsLayerProto TimeStatsHelper::TimeStatsLayer::toProto() {
+ SFTimeStatsLayerProto layerProto;
+ layerProto.set_layer_name(layerName);
+ packageName = getPackageName(layerName);
+ layerProto.set_package_name(packageName);
+ layerProto.set_stats_start(statsStart);
+ layerProto.set_stats_end(statsEnd);
+ layerProto.set_total_frames(totalFrames);
+ for (auto ele : deltas) {
+ SFTimeStatsDeltaProto* deltaProto = layerProto.add_deltas();
+ deltaProto->set_delta_name(ele.first);
+ for (auto histEle : ele.second.hist) {
+ SFTimeStatsHistogramBucketProto* histProto = deltaProto->add_histograms();
+ histProto->set_render_millis(histEle.first);
+ histProto->set_frame_count(histEle.second);
+ }
+ }
+ return layerProto;
+}
+
+SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto() {
+ SFTimeStatsGlobalProto globalProto;
+ globalProto.set_stats_start(statsStart);
+ globalProto.set_stats_end(statsEnd);
+ globalProto.set_total_frames(totalFrames);
+ globalProto.set_missed_frames(missedFrames);
+ globalProto.set_client_composition_frames(clientCompositionFrames);
+ for (auto ele : dumpStats) {
+ SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
+ layerProto->CopyFrom(ele->toProto());
+ }
+ return globalProto;
+}
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
new file mode 100644
index 0000000..c876f21
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#pragma once
+
+#include <timestatsproto/TimeStatsProtoHeader.h>
+
+#include <math/vec4.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace surfaceflinger {
+
+class TimeStatsHelper {
+public:
+ class Histogram {
+ public:
+ // Key is the delta time between timestamps
+ // Value is the number of appearances of that delta
+ std::unordered_map<int32_t, int32_t> hist;
+
+ void insert(int32_t delta);
+ float averageTime();
+ std::string toString();
+ };
+
+ class TimeStatsLayer {
+ public:
+ std::string layerName;
+ std::string packageName;
+ int64_t statsStart = 0;
+ int64_t statsEnd = 0;
+ int32_t totalFrames = 0;
+ std::unordered_map<std::string, Histogram> deltas;
+
+ std::string toString();
+ SFTimeStatsLayerProto toProto();
+ };
+
+ class TimeStatsGlobal {
+ public:
+ int64_t statsStart = 0;
+ int64_t statsEnd = 0;
+ int32_t totalFrames = 0;
+ int32_t missedFrames = 0;
+ int32_t clientCompositionFrames = 0;
+ std::unordered_map<std::string, TimeStatsLayer> stats;
+ std::vector<TimeStatsLayer*> dumpStats;
+
+ std::string toString();
+ SFTimeStatsGlobalProto toProto();
+ };
+};
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsProtoHeader.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsProtoHeader.h
new file mode 100644
index 0000000..fe0d150
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsProtoHeader.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Projectlayerproto/LayerProtoHeader.h
+ *
+ * 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.
+ */
+
+// pragma is used here to disable the warnings emitted from the protobuf
+// headers. By adding #pragma before including layer.pb.h, it supresses
+// protobuf warnings, but allows the rest of the files to continuing using
+// the current flags.
+// This file should be included instead of directly including layer.b.h
+#pragma GCC system_header
+#include <timestats.pb.h>
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/jarjar-rules.txt b/services/surfaceflinger/TimeStats/timestatsproto/jarjar-rules.txt
new file mode 100644
index 0000000..40043a8
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsproto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
new file mode 100644
index 0000000..a8f6fa8
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 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.
+ */
+
+syntax = "proto2";
+
+package android.surfaceflinger;
+
+option optimize_for = LITE_RUNTIME;
+
+// frameworks/base/core/proto/android/service/sftimestats.proto is based on
+// this proto. Please only make valid protobuf changes to these messages, and
+// keep the other file in sync with this one.
+
+message SFTimeStatsGlobalProto {
+ // The start & end timestamps in UTC as
+ // milliseconds since January 1, 1970
+ optional int64 stats_start = 1;
+ optional int64 stats_end = 2;
+ // Total frames
+ optional int32 total_frames = 3;
+ // Total missed frames of SurfaceFlinger.
+ optional int32 missed_frames = 4;
+ // Total frames fallback to client composition.
+ optional int32 client_composition_frames = 5;
+
+ repeated SFTimeStatsLayerProto stats = 6;
+}
+
+message SFTimeStatsLayerProto {
+ // The layer name
+ optional string layer_name = 1;
+ // The package name
+ optional string package_name = 2;
+ // The start & end timestamps in UTC as
+ // milliseconds since January 1, 1970
+ optional int64 stats_start = 3;
+ optional int64 stats_end = 4;
+ // Distinct frame count.
+ optional int32 total_frames = 5;
+
+ repeated SFTimeStatsDeltaProto deltas = 6;
+}
+
+message SFTimeStatsDeltaProto {
+ // Name of the time interval
+ optional string delta_name = 1;
+ // Histogram of the delta time
+ repeated SFTimeStatsHistogramBucketProto histograms = 2;
+}
+
+message SFTimeStatsHistogramBucketProto {
+ // Lower bound of render time in milliseconds.
+ optional int32 render_millis = 1;
+ // Number of frames in the bucket.
+ optional int32 frame_count = 2;
+}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 7523399..322e8a0 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -36,6 +36,7 @@
"liblayers_proto",
"liblog",
"libprotobuf-cpp-full",
+ "libtimestats_proto",
"libui",
"libutils",
]
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 00bc621..520df2d 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -25,6 +25,7 @@
"liblog",
"libnativewindow",
"libsync",
+ "libtimestats_proto",
"libui",
"libutils",
],
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index 29cd2d5..93769a5 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -60,7 +60,7 @@
MOCK_METHOD1(setupLayerTexturing, void(const Texture&));
MOCK_METHOD0(setupLayerBlackedOut, void());
MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
- MOCK_METHOD1(setupColorTransform, mat4(const mat4&));
+ MOCK_METHOD1(setupColorTransform, void(const mat4&));
MOCK_METHOD0(disableTexturing, void());
MOCK_METHOD0(disableBlending, void());
MOCK_METHOD1(setSourceY410BT2020, void(bool));