Merge changes I18b28a3a,I4439342c
* changes:
Use vector in VelocityTracker_test
Refactor VelocityTracker_test
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 6cbe4ae..5c1cab6 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -40,7 +40,6 @@
<feature name="android.software.voice_recognizers" notLowRam="true" />
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
- <feature name="android.software.input_methods" />
<feature name="android.software.print" />
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index d75de1e..44883cc 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -26,6 +26,7 @@
#ifndef ANDROID_CHOREOGRAPHER_H
#define ANDROID_CHOREOGRAPHER_H
+#include <stdint.h>
#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -43,6 +44,16 @@
*/
typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);
+/**
+ * Prototype of the function that is called when a new frame is being rendered.
+ * It's passed the time that the frame is being rendered as nanoseconds in the
+ * CLOCK_MONOTONIC time base, as well as the data pointer provided by the
+ * application that registered a callback. All callbacks that run as part of
+ * rendering a frame will observe the same frame time, so it should be used
+ * whenever events need to be synchronized (e.g. animations).
+ */
+typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
+
#if __ANDROID_API__ >= 24
/**
@@ -52,23 +63,39 @@
AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24);
/**
- * Post a callback to be run on the next frame. The data pointer provided will
- * be passed to the callback function when it's called.
+ * Deprecated: Use AChoreographer_postFrameCallback64 instead.
*/
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24);
+ AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
/**
- * Post a callback to be run on the frame following the specified delay. The
- * data pointer provided will be passed to the callback function when it's
- * called.
+ * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead.
*/
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
- long delayMillis) __INTRODUCED_IN(24);
+ long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
#endif /* __ANDROID_API__ >= 24 */
+#if __ANDROID_API__ >= 29
+
+/**
+ * Power a callback to be run on the next frame. The data pointer provided will
+ * be passed to the callback function when it's called.
+ */
+void AChoreographer_postFrameCallback64(AChoreographer* chroreographer,
+ AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
+
+/**
+ * Post a callback to be run on the frame following the specified delay. The
+ * data pointer provided will be passed to the callback function when it's
+ * called.
+ */
+void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
__END_DECLS
#endif // ANDROID_CHOREOGRAPHER_H
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0a4ad46..d6708ab 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1490,6 +1490,19 @@
outIsWideColorDisplay);
}
+status_t SurfaceComposerClient::addRegionSamplingListener(
+ const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) {
+ return ComposerService::getComposerService()->addRegionSamplingListener(samplingArea,
+ stopLayerHandle,
+ listener);
+}
+
+status_t SurfaceComposerClient::removeRegionSamplingListener(
+ const sp<IRegionSamplingListener>& listener) {
+ return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
+}
+
// ----------------------------------------------------------------------------
status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2c483ee..48c978f 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -50,6 +50,7 @@
class HdrCapabilities;
class ISurfaceComposerClient;
class IGraphicBufferProducer;
+class IRegionSamplingListener;
class Region;
// ---------------------------------------------------------------------------
@@ -449,6 +450,10 @@
static status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
uint64_t timestamp, DisplayedFrameStats* outStats);
+ static status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener);
+ static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
private:
virtual void onFirstRef();
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 7d2dcba..cc78ece 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -496,7 +496,7 @@
BufferLayerConsumer::Image::~Image() {
if (mGraphicBuffer != nullptr) {
- ALOGE("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
+ ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
}
}
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index da34083..e4179ee 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -393,9 +393,8 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
if (mFlinger->mUseSmart90ForVideo) {
- // Report the requested present time to the Scheduler, if the feature is turned on.
- mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp,
- item.mIsAutoTimestamp, mName.c_str());
+ // Report mApi ID for each layer.
+ mFlinger->mScheduler->addNativeWindowApi(item.mApi);
}
Mutex::Autolock lock(mQueueItemLock);
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 2d6d33c..04e8796 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -92,12 +92,10 @@
void LayerProtoHelper::writeToProto(const ui::Transform& transform,
TransformProto* transformProto) {
- const uint32_t type = transform.getType();
+ const uint32_t type = transform.getType() | (transform.getOrientation() << 8);
transformProto->set_type(type);
- if (type &
- (ui::Transform::SCALE | ui::Transform::ROTATE | ui::Transform::TRANSLATE |
- ui::Transform::UNKNOWN)) {
+ if (type & (ui::Transform::SCALE | ui::Transform::UNKNOWN)) {
transformProto->set_dsdx(transform[0][0]);
transformProto->set_dtdx(transform[0][1]);
transformProto->set_dsdy(transform[1][0]);
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index bd8548c..4f0b3bb 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -21,27 +21,168 @@
#include "RegionSamplingThread.h"
+#include <cutils/properties.h>
#include <gui/IRegionSamplingListener.h>
#include <utils/Trace.h>
+#include <string>
#include "DisplayDevice.h"
#include "Layer.h"
#include "SurfaceFlinger.h"
namespace android {
+using namespace std::chrono_literals;
template <typename T>
struct SpHash {
size_t operator()(const sp<T>& p) const { return std::hash<T*>()(p.get()); }
};
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger) : mFlinger(flinger) {
- std::lock_guard threadLock(mThreadMutex);
- mThread = std::thread([this]() { threadMain(); });
- pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+constexpr auto lumaSamplingStepTag = "LumaSamplingStep";
+enum class samplingStep {
+ noWorkNeeded,
+ idleTimerWaiting,
+ waitForZeroPhase,
+ waitForSamplePhase,
+ sample
+};
+
+constexpr auto defaultRegionSamplingOffset = -3ms;
+constexpr auto defaultRegionSamplingPeriod = 100ms;
+constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+// TODO: (b/127403193) duration to string conversion could probably be constexpr
+template <typename Rep, typename Per>
+inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
+ return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(t).count());
}
+RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
+ char value[PROPERTY_VALUE_MAX] = {};
+
+ property_get("debug.sf.region_sampling_offset_ns", value,
+ toNsString(defaultRegionSamplingOffset).c_str());
+ int const samplingOffsetNsRaw = atoi(value);
+
+ property_get("debug.sf.region_sampling_period_ns", value,
+ toNsString(defaultRegionSamplingPeriod).c_str());
+ int const samplingPeriodNsRaw = atoi(value);
+
+ property_get("debug.sf.region_sampling_timer_timeout_ns", value,
+ toNsString(defaultRegionSamplingTimerTimeout).c_str());
+ int const samplingTimerTimeoutNsRaw = atoi(value);
+
+ if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
+ ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
+ mSamplingOffset = defaultRegionSamplingOffset;
+ mSamplingPeriod = defaultRegionSamplingPeriod;
+ mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
+ } else {
+ mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
+ mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
+ mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
+ }
+}
+
+struct SamplingOffsetCallback : DispSync::Callback {
+ SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
+ std::chrono::nanoseconds targetSamplingOffset)
+ : mRegionSamplingThread(samplingThread),
+ mScheduler(scheduler),
+ mTargetSamplingOffset(targetSamplingOffset) {}
+
+ ~SamplingOffsetCallback() { stopVsyncListener(); }
+
+ SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
+ SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
+
+ void startVsyncListener() {
+ std::lock_guard lock(mMutex);
+ if (mVsyncListening) return;
+
+ mPhaseIntervalSetting = Phase::ZERO;
+ mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+ sync.addEventListener("SamplingThreadDispSyncListener", 0, this);
+ });
+ mVsyncListening = true;
+ }
+
+ void stopVsyncListener() {
+ std::lock_guard lock(mMutex);
+ stopVsyncListenerLocked();
+ }
+
+private:
+ void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
+ if (!mVsyncListening) return;
+
+ mScheduler.withPrimaryDispSync(
+ [this](android::DispSync& sync) { sync.removeEventListener(this); });
+ mVsyncListening = false;
+ }
+
+ void onDispSyncEvent(nsecs_t /* when */) final {
+ std::unique_lock<decltype(mMutex)> lock(mMutex);
+
+ if (mPhaseIntervalSetting == Phase::ZERO) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+ mPhaseIntervalSetting = Phase::SAMPLING;
+ mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+ sync.changePhaseOffset(this, mTargetSamplingOffset.count());
+ });
+ return;
+ }
+
+ if (mPhaseIntervalSetting == Phase::SAMPLING) {
+ mPhaseIntervalSetting = Phase::ZERO;
+ mScheduler.withPrimaryDispSync(
+ [this](android::DispSync& sync) { sync.changePhaseOffset(this, 0); });
+ stopVsyncListenerLocked();
+ lock.unlock();
+ mRegionSamplingThread.notifySamplingOffset();
+ return;
+ }
+ }
+
+ RegionSamplingThread& mRegionSamplingThread;
+ Scheduler& mScheduler;
+ const std::chrono::nanoseconds mTargetSamplingOffset;
+ mutable std::mutex mMutex;
+ enum class Phase {
+ ZERO,
+ SAMPLING
+ } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
+ = Phase::ZERO;
+ bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
+};
+
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
+ const TimingTunables& tunables)
+ : mFlinger(flinger),
+ mScheduler(scheduler),
+ mTunables(tunables),
+ mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
+ mTunables.mSamplingTimerTimeout),
+ [] {}, [this] { checkForStaleLuma(); }),
+ mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
+ tunables.mSamplingOffset)),
+ lastSampleTime(0ns) {
+ {
+ std::lock_guard threadLock(mThreadMutex);
+ mThread = std::thread([this]() { threadMain(); });
+ pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+ }
+ mIdleTimer.start();
+}
+
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
+ : RegionSamplingThread(flinger, scheduler,
+ TimingTunables{defaultRegionSamplingOffset,
+ defaultRegionSamplingPeriod,
+ defaultRegionSamplingTimerTimeout}) {}
+
RegionSamplingThread::~RegionSamplingThread() {
+ mIdleTimer.stop();
+
{
std::lock_guard lock(mMutex);
mRunning = false;
@@ -71,8 +212,41 @@
mDescriptors.erase(wp<IBinder>(IInterface::asBinder(listener)));
}
-void RegionSamplingThread::sampleNow() {
+void RegionSamplingThread::checkForStaleLuma() {
std::lock_guard lock(mMutex);
+
+ if (mDiscardedFrames) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
+ mDiscardedFrames = false;
+ mPhaseCallback->startVsyncListener();
+ }
+}
+
+void RegionSamplingThread::notifyNewContent() {
+ doSample();
+}
+
+void RegionSamplingThread::notifySamplingOffset() {
+ doSample();
+}
+
+void RegionSamplingThread::doSample() {
+ std::lock_guard lock(mMutex);
+ auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+ if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
+ mDiscardedFrames = true;
+ return;
+ }
+
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
+
+ mDiscardedFrames = false;
+ lastSampleTime = now;
+
+ mIdleTimer.reset();
+ mPhaseCallback->stopVsyncListener();
+
mSampleRequested = true;
mCondition.notify_one();
}
@@ -238,6 +412,7 @@
for (size_t d = 0; d < activeDescriptors.size(); ++d) {
activeDescriptors[d].listener->onSampleCollected(lumas[d]);
}
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
}
void RegionSamplingThread::threadMain() {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index ab06513..d4e57bf 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -16,6 +16,7 @@
#pragma once
+#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
@@ -25,17 +26,42 @@
#include <binder/IBinder.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include "Scheduler/IdleTimer.h"
namespace android {
class GraphicBuffer;
class IRegionSamplingListener;
class Layer;
+class Scheduler;
class SurfaceFlinger;
+struct SamplingOffsetCallback;
class RegionSamplingThread : public IBinder::DeathRecipient {
public:
- explicit RegionSamplingThread(SurfaceFlinger& flinger);
+ struct TimingTunables {
+ // debug.sf.sampling_offset_ns
+ // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline
+ // at which the sampling should start.
+ std::chrono::nanoseconds mSamplingOffset;
+ // debug.sf.sampling_period_ns
+ // This is the maximum amount of time the luma recieving client
+ // should have to wait for a new luma value after a frame is updated. The inverse of this is
+ // roughly the sampling rate. Sampling system rounds up sub-vsync sampling period to vsync
+ // period.
+ std::chrono::nanoseconds mSamplingPeriod;
+ // debug.sf.sampling_timer_timeout_ns
+ // This is the interval at which the luma sampling system will check that the luma clients
+ // have up to date information. It defaults to the mSamplingPeriod.
+ std::chrono::nanoseconds mSamplingTimerTimeout;
+ };
+ struct EnvironmentTimingTunables : TimingTunables {
+ EnvironmentTimingTunables();
+ };
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
+ const TimingTunables& tunables);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+
~RegionSamplingThread();
// Add a listener to receive luma notifications. The luma reported via listener will
@@ -44,8 +70,13 @@
const sp<IRegionSamplingListener>& listener);
// Remove the listener to stop receiving median luma notifications.
void removeListener(const sp<IRegionSamplingListener>& listener);
- // Instruct the thread to perform a median luma sampling on the layers.
- void sampleNow();
+
+ // Notifies sampling engine that new content is available. This will trigger a sampling
+ // pass at some point in the future.
+ void notifyNewContent();
+
+ // Notifies the sampling engine that it has a good timing window in which to sample.
+ void notifySamplingOffset();
private:
struct Descriptor {
@@ -63,12 +94,19 @@
const sp<GraphicBuffer>& buffer, const Point& leftTop,
const std::vector<RegionSamplingThread::Descriptor>& descriptors);
+ void doSample();
void binderDied(const wp<IBinder>& who) override;
+ void checkForStaleLuma();
void captureSample() REQUIRES(mMutex);
void threadMain();
SurfaceFlinger& mFlinger;
+ Scheduler& mScheduler;
+ const TimingTunables mTunables;
+ scheduler::IdleTimer mIdleTimer;
+
+ std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
std::mutex mThreadMutex;
std::thread mThread GUARDED_BY(mThreadMutex);
@@ -79,6 +117,8 @@
bool mSampleRequested GUARDED_BY(mMutex) = false;
std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mMutex);
+ std::chrono::nanoseconds lastSampleTime GUARDED_BY(mMutex);
+ bool mDiscardedFrames GUARDED_BY(mMutex) = false;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 1eccf9a..5178836 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -76,9 +76,18 @@
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
if (mTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- mPeriod = period;
+
mPhase = phase;
mReferenceTime = referenceTime;
+ if (mPeriod != period && mReferenceTime != 0) {
+ // Inflate the reference time to be the most recent predicted
+ // vsync before the current time.
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t baseTime = now - mReferenceTime;
+ const nsecs_t numOldPeriods = baseTime / mPeriod;
+ mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod;
+ }
+ mPeriod = period;
if (mTraceDetailedInfo) {
ATRACE_INT64("DispSync:Period", mPeriod);
ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
@@ -429,8 +438,16 @@
void DispSync::resetLocked() {
mPhase = 0;
- mReferenceTime = 0;
+ const size_t lastSampleIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
+ // Keep the most recent sample, when we resync to hardware we'll overwrite this
+ // with a more accurate signal
+ if (mResyncSamples[lastSampleIdx] != 0) {
+ mReferenceTime = mResyncSamples[lastSampleIdx];
+ }
mModelUpdated = false;
+ for (size_t i = 0; i < MAX_RESYNC_SAMPLES; i++) {
+ mResyncSamples[i] = 0;
+ }
mNumResyncSamples = 0;
mFirstResyncSample = 0;
mNumResyncSamplesSincePresent = 0;
@@ -530,7 +547,6 @@
Mutex::Autolock lock(mMutex);
mPeriod = period;
mPhase = 0;
- mReferenceTime = 0;
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index f629697..6f3bd00 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -213,7 +213,7 @@
// These member variables are the state used during the resynchronization
// process to store information about the hardware vsync event times used
// to compute the model.
- nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES];
+ nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0};
size_t mFirstResyncSample;
size_t mNumResyncSamples;
int mNumResyncSamplesSincePresent;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f3a7f87..0063c8a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -28,6 +28,7 @@
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
#include <cutils/properties.h>
+#include <system/window.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -296,21 +297,33 @@
mPrimaryDispSync->dump(result);
}
-void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
- const std::string layerName) {
- // This is V1 logic. It calculates the average FPS based on the timestamp frequency
- // regardless of which layer the timestamp came from.
- // For now, the averages and FPS are recorded in the systrace.
- determineTimestampAverage(isAutoTimestamp, framePresentTime);
-
- // This is V2 logic. It calculates the average and median timestamp difference based on the
- // individual layer history. The results are recorded in the systrace.
- determineLayerTimestampStats(layerName, framePresentTime);
+void Scheduler::addNativeWindowApi(int apiId) {
+ std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
+ mWindowApiHistory[mApiHistoryCounter] = apiId;
+ mApiHistoryCounter++;
+ mApiHistoryCounter = mApiHistoryCounter % scheduler::ARRAY_SIZE;
}
-void Scheduler::incrementFrameCounter() {
- std::lock_guard<std::mutex> lock(mLayerHistoryLock);
- mLayerHistory.incrementCounter();
+void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
+ fn(*mPrimaryDispSync);
+}
+
+void Scheduler::updateFpsBasedOnNativeWindowApi() {
+ int mode;
+ {
+ std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
+ mode = scheduler::calculate_mode(mWindowApiHistory);
+ }
+ ATRACE_INT("NativeWindowApiMode", mode);
+
+ if (mode == NATIVE_WINDOW_API_MEDIA) {
+ // TODO(b/127365162): These callback names are not accurate anymore. Update.
+ mediaChangeRefreshRate(MediaFeatureState::MEDIA_PLAYING);
+ ATRACE_INT("DetectedVideo", 1);
+ } else {
+ mediaChangeRefreshRate(MediaFeatureState::MEDIA_OFF);
+ ATRACE_INT("DetectedVideo", 0);
+ }
}
void Scheduler::setChangeRefreshRateCallback(
@@ -328,85 +341,6 @@
}
}
-void Scheduler::determineLayerTimestampStats(const std::string layerName,
- const nsecs_t framePresentTime) {
- std::vector<int64_t> differencesMs;
- std::string differencesText = "";
- {
- std::lock_guard<std::mutex> lock(mLayerHistoryLock);
- mLayerHistory.insert(layerName, framePresentTime);
-
- // Traverse through the layer history, and determine the differences in present times.
- nsecs_t newestPresentTime = framePresentTime;
- for (int i = 1; i < mLayerHistory.getSize(); i++) {
- std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i);
- for (auto layer : layers) {
- if (layer.first != layerName) {
- continue;
- }
- int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
- // Dismiss noise.
- if (differenceMs > 10 && differenceMs < 60) {
- differencesMs.push_back(differenceMs);
- }
- IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
- newestPresentTime = layer.second;
- }
- }
- }
- ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str());
-
- if (!differencesMs.empty()) {
- // Mean/Average is a good indicator for when 24fps videos are playing, because the frames
- // come in 33, and 49 ms intervals with occasional 41ms.
- const int64_t meanMs = scheduler::calculate_mean(differencesMs);
- const auto tagMean = "TimestampMean_" + layerName;
- ATRACE_INT(tagMean.c_str(), meanMs);
-
- // Mode and median are good indicators for 30 and 60 fps videos, because the majority of
- // frames come in 16, or 33 ms intervals.
- const auto tagMedian = "TimestampMedian_" + layerName;
- ATRACE_INT(tagMedian.c_str(), scheduler::calculate_median(&differencesMs));
-
- const auto tagMode = "TimestampMode_" + layerName;
- ATRACE_INT(tagMode.c_str(), scheduler::calculate_mode(differencesMs));
- }
-}
-
-void Scheduler::determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime) {
- ATRACE_INT("AutoTimestamp", isAutoTimestamp);
-
- // Video does not have timestamp automatically set, so we discard timestamps that are
- // coming in from other sources for now.
- if (isAutoTimestamp) {
- return;
- }
- int64_t differenceMs = (framePresentTime - mPreviousFrameTimestamp) / 1000000;
- mPreviousFrameTimestamp = framePresentTime;
-
- if (differenceMs < 10 || differenceMs > 100) {
- // Dismiss noise.
- return;
- }
- ATRACE_INT("TimestampDiff", differenceMs);
-
- mTimeDifferences[mCounter % scheduler::ARRAY_SIZE] = differenceMs;
- mCounter++;
- int64_t mean = scheduler::calculate_mean(mTimeDifferences);
- ATRACE_INT("AutoTimestampMean", mean);
-
- // TODO(b/113612090): This are current numbers from trial and error while running videos
- // from YouTube at 24, 30, and 60 fps.
- if (mean > 14 && mean < 18) {
- ATRACE_INT("MediaFPS", 60);
- } else if (mean > 31 && mean < 34) {
- ATRACE_INT("MediaFPS", 30);
- return;
- } else if (mean > 39 && mean < 42) {
- ATRACE_INT("MediaFPS", 24);
- }
-}
-
void Scheduler::resetIdleTimer() {
if (mIdleTimer) {
mIdleTimer->reset();
@@ -414,21 +348,15 @@
}
void Scheduler::resetTimerCallback() {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mChangeRefreshRateCallback) {
- // We do not notify the applications about config changes when idle timer is reset.
- mChangeRefreshRateCallback(RefreshRateType::PERFORMANCE, ConfigEvent::None);
- ATRACE_INT("ExpiredIdleTimer", 0);
- }
+ // We do not notify the applications about config changes when idle timer is reset.
+ timerChangeRefreshRate(IdleTimerState::RESET);
+ ATRACE_INT("ExpiredIdleTimer", 0);
}
void Scheduler::expiredTimerCallback() {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mChangeRefreshRateCallback) {
- // We do not notify the applications about config changes when idle timer expires.
- mChangeRefreshRateCallback(RefreshRateType::DEFAULT, ConfigEvent::None);
- ATRACE_INT("ExpiredIdleTimer", 1);
- }
+ // We do not notify the applications about config changes when idle timer expires.
+ timerChangeRefreshRate(IdleTimerState::EXPIRED);
+ ATRACE_INT("ExpiredIdleTimer", 1);
}
std::string Scheduler::doDump() {
@@ -437,4 +365,46 @@
return stream.str();
}
+void Scheduler::mediaChangeRefreshRate(MediaFeatureState mediaFeatureState) {
+ // Default playback for media is DEFAULT when media is on.
+ RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ ConfigEvent configEvent = ConfigEvent::None;
+
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mCurrentMediaFeatureState = mediaFeatureState;
+ // Only switch to PERFORMANCE if idle timer was reset, when turning
+ // media off. If the timer is IDLE, stay at DEFAULT.
+ if (mediaFeatureState == MediaFeatureState::MEDIA_OFF &&
+ mCurrentIdleTimerState == IdleTimerState::RESET) {
+ refreshRateType = RefreshRateType::PERFORMANCE;
+ }
+ }
+ changeRefreshRate(refreshRateType, configEvent);
+}
+
+void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
+ RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ ConfigEvent configEvent = ConfigEvent::None;
+
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mCurrentIdleTimerState = idleTimerState;
+ // Only switch to PERFOMANCE if the idle timer was reset, and media is
+ // not playing. Otherwise, stay at DEFAULT.
+ if (idleTimerState == IdleTimerState::RESET &&
+ mCurrentMediaFeatureState == MediaFeatureState::MEDIA_OFF) {
+ refreshRateType = RefreshRateType::PERFORMANCE;
+ }
+ }
+ changeRefreshRate(refreshRateType, configEvent);
+}
+
+void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ if (mChangeRefreshRateCallback) {
+ mChangeRefreshRateCallback(refreshRateType, configEvent);
+ }
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 5b9759b..73896d5 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -17,6 +17,7 @@
#pragma once
#include <cstdint>
+#include <functional>
#include <memory>
#include <ui/DisplayStatInfo.h>
@@ -104,6 +105,9 @@
// Getter methods.
EventThread* getEventThread(const sp<ConnectionHandle>& handle);
+ // Provides access to the DispSync object for the primary display.
+ void withPrimaryDispSync(std::function<void(DispSync&)> const& fn);
+
sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle);
// Should be called when receiving a hotplug event.
@@ -141,11 +145,11 @@
void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
void setIgnorePresentFences(bool ignore);
nsecs_t expectedPresentTime();
- // Adds the present time for given layer to the history of present times.
- void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
- const std::string layerName);
- // Increments counter in the layer history to indicate that SF has started a new frame.
- void incrementFrameCounter();
+ // apiId indicates the API (NATIVE_WINDOW_API_xxx) that queues the buffer.
+ // TODO(b/123956502): Remove this call with V1 go/content-fps-detection-in-scheduler.
+ void addNativeWindowApi(int apiId);
+ // Updates FPS based on the most occured request for Native Window API.
+ void updateFpsBasedOnNativeWindowApi();
// Callback that gets invoked when Scheduler wants to change the refresh rate.
void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
@@ -165,17 +169,16 @@
private:
friend class TestableScheduler;
+ // In order to make sure that the features don't override themselves, we need a state machine
+ // to keep track which feature requested the config change.
+ enum class MediaFeatureState { MEDIA_PLAYING, MEDIA_OFF };
+ enum class IdleTimerState { EXPIRED, RESET };
+
// Creates a connection on the given EventThread and forwards the given callbacks.
sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
nsecs_t calculateAverage() const;
void updateFrameSkipping(const int64_t skipCount);
- // Collects the statistical mean (average) and median between timestamp
- // intervals for each frame for each layer.
- void determineLayerTimestampStats(const std::string layerName, const nsecs_t framePresentTime);
- // Collects the average difference between timestamps for each frame regardless
- // of which layer the timestamp came from.
- void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
// Function that resets the idle timer.
void resetIdleTimer();
// Function that is called when the timer resets.
@@ -184,6 +187,12 @@
void expiredTimerCallback();
// Sets vsync period.
void setVsyncPeriod(const nsecs_t period);
+ // Media feature's function to change the refresh rate.
+ void mediaChangeRefreshRate(MediaFeatureState mediaFeatureState);
+ // Idle timer feature's function to change the refresh rate.
+ void timerChangeRefreshRate(IdleTimerState idleTimerState);
+ // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
+ void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent);
// If fences from sync Framework are supported.
const bool mHasSyncFramework;
@@ -217,10 +226,13 @@
std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
size_t mCounter = 0;
- // DetermineLayerTimestampStats is called from BufferQueueLayer::onFrameAvailable which
- // can run on any thread, and cause failure.
- std::mutex mLayerHistoryLock;
- LayerHistory mLayerHistory GUARDED_BY(mLayerHistoryLock);
+ // The following few fields follow native window api bits that come with buffers. If there are
+ // more buffers with NATIVE_WINDOW_API_MEDIA we render at 60Hz, otherwise we render at 90Hz.
+ // There is not dependency on timestamp for V0.
+ // TODO(b/123956502): Remove this when more robust logic for content fps detection is developed.
+ std::mutex mWindowApiHistoryLock;
+ std::array<int, scheduler::ARRAY_SIZE> mWindowApiHistory GUARDED_BY(mWindowApiHistoryLock);
+ int64_t mApiHistoryCounter = 0;
// Timer that records time between requests for next vsync. If the time is higher than a given
// interval, a callback is fired. Set this variable to >0 to use this feature.
@@ -229,6 +241,13 @@
std::mutex mCallbackLock;
ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+
+ // In order to make sure that the features don't override themselves, we need a state machine
+ // to keep track which feature requested the config change.
+ std::mutex mFeatureStateLock;
+ MediaFeatureState mCurrentMediaFeatureState GUARDED_BY(mFeatureStateLock) =
+ MediaFeatureState::MEDIA_OFF;
+ IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
index 191022d..fb5414f 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -34,23 +34,5 @@
return v->at(n);
}
-int64_t calculate_mode(const std::vector<int64_t>& v) {
- if (v.empty()) {
- return 0;
- }
-
- // Create a map with all the counts for the indivicual values in the vector.
- std::unordered_map<int64_t, int64_t> counts;
- for (int64_t value : v) {
- counts[value]++;
- }
-
- // Sort the map, and return the number with the highest count. If two numbers have
- // the same count, first one is returned.
- using ValueType = const decltype(counts)::value_type&;
- const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
- return std::max_element(counts.begin(), counts.end(), compareCounts)->first;
-}
-
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index edd23de..9e6e8c7 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -18,6 +18,7 @@
#include <cinttypes>
#include <numeric>
+#include <unordered_map>
#include <vector>
namespace android {
@@ -45,7 +46,24 @@
int64_t calculate_median(std::vector<int64_t>* v);
// Calculates the statistical mode in the vector. Return 0 if the vector is empty.
-int64_t calculate_mode(const std::vector<int64_t>& v);
+template <typename T>
+auto calculate_mode(const T& v) {
+ if (v.empty()) {
+ return 0;
+ }
+
+ // Create a map with all the counts for the indivicual values in the vector.
+ std::unordered_map<int64_t, int> counts;
+ for (int64_t value : v) {
+ counts[value]++;
+ }
+
+ // Sort the map, and return the number with the highest count. If two numbers have
+ // the same count, first one is returned.
+ using ValueType = const decltype(counts)::value_type&;
+ const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
+ return static_cast<int>(std::max_element(counts.begin(), counts.end(), compareCounts)->first);
+}
} // namespace scheduler
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9b0e8c1..c7c0867 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -370,8 +370,13 @@
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
+ mUseSmart90ForVideo = use_smart_90_for_video(false);
property_get("debug.sf.use_smart_90_for_video", value, "0");
- mUseSmart90ForVideo = atoi(value);
+
+ int int_value = atoi(value);
+ if (int_value) {
+ mUseSmart90ForVideo = true;
+ }
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
@@ -568,12 +573,8 @@
mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
- mPhaseOffsets->setRefreshRateType(
- scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
} else {
- mPhaseOffsets->setRefreshRateType(
- scheduler::RefreshRateConfigs::RefreshRateType::DEFAULT);
setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
}
}));
@@ -632,6 +633,10 @@
mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
mSfConnectionHandle.get());
+ mRegionSamplingThread =
+ new RegionSamplingThread(*this, *mScheduler,
+ RegionSamplingThread::EnvironmentTimingTunables());
+
// Get a RenderEngine for the given display / config (can't fail)
int32_t renderEngineFeature = 0;
renderEngineFeature |= (useColorManagement ?
@@ -970,6 +975,8 @@
display->setActiveConfig(mUpcomingActiveConfig.configId);
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.setPhaseOffsets(early, gl, late);
ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
@@ -979,15 +986,6 @@
bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
- // we may be in the process of changing the active state
- if (mWaitForNextInvalidate) {
- mWaitForNextInvalidate = false;
-
- repaintEverythingForHWC();
- // We do not want to give another frame to HWC while we are transitioning.
- return true;
- }
-
if (mCheckPendingFence) {
if (mPreviousPresentFence != Fence::NO_FENCE &&
(mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled)) {
@@ -1039,7 +1037,6 @@
getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
// we need to submit an empty frame to HWC to start the process
- mWaitForNextInvalidate = true;
mCheckPendingFence = true;
return false;
@@ -1413,15 +1410,10 @@
}
void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
- ATRACE_CALL();
- mPhaseOffsets->setRefreshRateType(refreshRate);
-
- const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
- mVsyncModulator.setPhaseOffsets(early, gl, late);
-
if (mBootStage != BootStage::FINISHED) {
return;
}
+ ATRACE_CALL();
// Don't do any updating if the current fps is the same as the new one.
const auto displayId = getInternalDisplayIdLocked();
@@ -1439,6 +1431,7 @@
return;
}
+ mPhaseOffsets->setRefreshRateType(refreshRate);
setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
}
@@ -1583,23 +1576,28 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
- if (performSetActiveConfig()) {
- break;
+ bool frameMissed = mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
+ bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
+ bool gpuFrameMissed = mHadClientComposition && frameMissed;
+ ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
+ ATRACE_INT("HwcFrameMissed", static_cast<int>(hwcFrameMissed));
+ ATRACE_INT("GpuFrameMissed", static_cast<int>(gpuFrameMissed));
+ if (frameMissed) {
+ mFrameMissedCount++;
+ mTimeStats->incrementMissedFrames();
}
if (mUseSmart90ForVideo) {
// This call is made each time SF wakes up and creates a new frame. It is part
// of video detection feature.
- mScheduler->incrementFrameCounter();
+ mScheduler->updateFpsBasedOnNativeWindowApi();
}
- bool frameMissed = mPreviousPresentFence != Fence::NO_FENCE &&
- (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
- bool hwcFrameMissed = !mHadClientComposition && frameMissed;
- if (frameMissed) {
- ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
- mFrameMissedCount++;
- mTimeStats->incrementMissedFrames();
+
+ if (performSetActiveConfig()) {
+ break;
}
+
// For now, only propagate backpressure when missing a hwc frame.
if (hwcFrameMissed) {
if (mPropagateBackpressure) {
@@ -1680,11 +1678,14 @@
postComposition();
mHadClientComposition = false;
+ mHadDeviceComposition = false;
for (const auto& [token, displayDevice] : mDisplays) {
auto display = displayDevice->getCompositionDisplay();
const auto displayId = display->getId();
mHadClientComposition =
mHadClientComposition || getHwComposer().hasClientComposition(displayId);
+ mHadDeviceComposition =
+ mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);
}
mVsyncModulator.onRefreshed(mHadClientComposition);
@@ -2057,8 +2058,8 @@
mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
mTransactionCompletedThread.sendCallbacks();
- if (mLumaSampling) {
- mRegionSamplingThread->sampleNow();
+ if (mLumaSampling && mRegionSamplingThread) {
+ mRegionSamplingThread->notifyNewContent();
}
}
@@ -4744,6 +4745,7 @@
*/
result.append("\nScheduler state:\n");
result.append(mScheduler->doDump() + "\n");
+ StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
result.append(mRefreshRateStats->doDump() + "\n");
}
@@ -5664,8 +5666,6 @@
const auto performanceRefreshRate =
mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
- mPhaseOffsets->setRefreshRateType(
- scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8de1e97..1181886 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -970,7 +970,12 @@
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
+ // True if in the previous frame at least one layer was composed via the GPU.
bool mHadClientComposition = false;
+ // True if in the previous frame at least one layer was composed via HW Composer.
+ // Note that it is possible for a frame to be composed via both client and device
+ // composition, for example in the case of overlays.
+ bool mHadDeviceComposition = false;
enum class BootStage {
BOOTLOADER,
@@ -1011,9 +1016,6 @@
TransactionCompletedThread mTransactionCompletedThread;
- bool mLumaSampling = true;
- sp<RegionSamplingThread> mRegionSamplingThread = new RegionSamplingThread(*this);
-
// Restrict layers to use two buffers in their bufferqueues.
bool mLayerTripleBufferingDisabled = false;
@@ -1135,10 +1137,12 @@
// below flags are set by main thread only
bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
- bool mWaitForNextInvalidate = false;
bool mCheckPendingFence = false;
/* ------------------------------------------------------------------------ */
+ bool mLumaSampling = true;
+ sp<RegionSamplingThread> mRegionSamplingThread;
+
sp<IInputFlinger> mInputFlinger;
InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 875fca8..e130511 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -217,6 +217,14 @@
return defaultValue;
}
+bool use_smart_90_for_video(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
#define DISPLAY_PRIMARY_SIZE 3
constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 0199d79..6f90117 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -57,6 +57,8 @@
int32_t set_idle_timer_ms(int32_t defaultValue);
+bool use_smart_90_for_video(bool defaultValue);
+
android::ui::DisplayPrimaries getDisplayNativePrimaries();
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 949c23c..fe6dc93 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -297,3 +297,13 @@
access: Readonly
prop_name: "ro.surface_flinger.set_idle_timer_ms"
}
+
+# useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
+# screen refresh rate based on that.
+prop {
+ api_name: "use_smart_90_for_video"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_smart_90_for_video"
+}
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 91999ae..be862c9 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*"
+ "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*"
}
}
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 34cdff7..319e01c 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -198,12 +198,15 @@
class ScreenCapture : public RefBase {
public:
static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
+ captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+ }
+
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
const auto sf = ComposerService::getComposerService();
- const auto token = sf->getInternalDisplayToken();
SurfaceComposerClient::Transaction().apply(true);
sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(token, &outBuffer, Rect(), 0, 0, false));
+ ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
*sc = std::make_unique<ScreenCapture>(outBuffer);
}
@@ -482,6 +485,12 @@
return screenshot;
}
+ void asTransaction(const std::function<void(Transaction&)>& exec) {
+ Transaction t;
+ exec(t);
+ t.apply(true);
+ }
+
static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
static BufferGenerator bufferGenerator;
return bufferGenerator.get(outBuffer, outFence);
@@ -4086,11 +4095,6 @@
fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
}
- void asTransaction(const std::function<void(Transaction&)>& exec) {
- Transaction t;
- exec(t);
- t.apply(true);
- }
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
@@ -5421,4 +5425,102 @@
}
}
+class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
+
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
+ }
+
+ virtual void TearDown() {
+ SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+ LayerTransactionTest::TearDown();
+ mColorLayer = 0;
+ }
+
+ void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
+ mVirtualDisplay =
+ SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+ asTransaction([&](Transaction& t) {
+ t.setDisplaySurface(mVirtualDisplay, mProducer);
+ t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+ t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
+ Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
+ });
+ }
+
+ void createColorLayer(uint32_t layerStack) {
+ mColorLayer =
+ createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+ ASSERT_TRUE(mColorLayer != nullptr);
+ ASSERT_TRUE(mColorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setLayerStack(mColorLayer, layerStack);
+ t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+ t.setLayer(mColorLayer, INT32_MAX - 2);
+ t.setColor(mColorLayer,
+ half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
+ mExpectedColor.b / 255.0f});
+ t.show(mColorLayer);
+ });
+ }
+
+ DisplayInfo mMainDisplayInfo;
+ sp<IBinder> mMainDisplay;
+ sp<IBinder> mVirtualDisplay;
+ sp<IGraphicBufferProducer> mProducer;
+ sp<SurfaceControl> mColorLayer;
+ Color mExpectedColor = {63, 63, 195, 255};
+};
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+ createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
+ createColorLayer(1 /* layerStack */);
+
+ asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+ // Verify color layer does not render on main display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ // Verify color layer renders correctly on virtual display.
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
+}
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+ // Create a display and set its layer stack to the main display's layer stack so
+ // the contents of the main display are mirrored on to the virtual display.
+
+ // Assumption here is that the new mirrored display has the same viewport as the
+ // primary display that it is mirroring.
+ createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
+ createColorLayer(0 /* layerStack */);
+
+ asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+ // Verify color layer renders correctly on main display and it is mirrored on the
+ // virtual display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 79b5ca0..13059e8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -341,6 +341,8 @@
// still be referenced by something despite our best efforts to destroy
// it after each test is done.
mutableDisplays().clear();
+ mutableCurrentState().displays.clear();
+ mutableDrawingState().displays.clear();
mutableEventQueue().reset();
mutableInterceptor().reset();
mutableScheduler().reset();