Merge "Expand the displayFrame for shadows" into sc-dev
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 364c939..2970116 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,8 +132,7 @@
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
int width, int height, int32_t format)
- : mName(name),
- mSurfaceControl(surface),
+ : mSurfaceControl(surface),
mSize(width, height),
mRequestedSize(mSize),
mFormat(format),
@@ -150,8 +149,9 @@
GraphicBuffer::USAGE_HW_TEXTURE,
1, false);
static int32_t id = 0;
+ mName = name + "#" + std::to_string(id);
auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
- mPendingBufferTrace = "PendingBuffer - " + mName + "BLAST#" + std::to_string(id);
+ mQueuedBufferTrace = "QueuedBuffer - " + mName + "BLAST#" + std::to_string(id);
id++;
mBufferItemConsumer->setName(String8(consumerName.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
@@ -313,24 +313,24 @@
// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client.
// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
// Otherwise, this is a no-op.
-static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, uint64_t graphicBufferId,
+static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id,
const sp<Fence>& releaseFence, uint32_t transformHint,
uint32_t currentMaxAcquiredBufferCount) {
sp<BLASTBufferQueue> blastBufferQueue = context.promote();
- ALOGV("releaseBufferCallbackThunk graphicBufferId=%" PRIu64 " blastBufferQueue=%s",
- graphicBufferId, blastBufferQueue ? "alive" : "dead");
if (blastBufferQueue) {
- blastBufferQueue->releaseBufferCallback(graphicBufferId, releaseFence, transformHint,
+ blastBufferQueue->releaseBufferCallback(id, releaseFence, transformHint,
currentMaxAcquiredBufferCount);
+ } else {
+ ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
}
}
-void BLASTBufferQueue::releaseBufferCallback(uint64_t graphicBufferId,
+void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id,
const sp<Fence>& releaseFence, uint32_t transformHint,
uint32_t currentMaxAcquiredBufferCount) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
- BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
+ BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
if (mSurfaceControl != nullptr) {
mTransformHint = transformHint;
@@ -343,34 +343,34 @@
// on a lower refresh rate than the max supported. We only do that for EGL
// clients as others don't care about latency
const bool isEGL = [&] {
- const auto it = mSubmitted.find(graphicBufferId);
+ const auto it = mSubmitted.find(id);
return it != mSubmitted.end() && it->second.mApi == NATIVE_WINDOW_API_EGL;
}();
const auto numPendingBuffersToHold =
isEGL ? std::max(0u, mMaxAcquiredBuffers - currentMaxAcquiredBufferCount) : 0;
- mPendingRelease.emplace_back(ReleasedBuffer{graphicBufferId, releaseFence});
+ mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
// Release all buffers that are beyond the ones that we need to hold
while (mPendingRelease.size() > numPendingBuffersToHold) {
const auto releaseBuffer = mPendingRelease.front();
mPendingRelease.pop_front();
- auto it = mSubmitted.find(releaseBuffer.bufferId);
+ auto it = mSubmitted.find(releaseBuffer.callbackId);
if (it == mSubmitted.end()) {
- BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
- graphicBufferId);
+ BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
+ releaseBuffer.callbackId.to_string().c_str());
return;
}
-
+ mNumAcquired--;
+ BQA_LOGV("released %s", id.to_string().c_str());
mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
mSubmitted.erase(it);
+ processNextBufferLocked(false /* useNextTransaction */);
}
ATRACE_INT("PendingRelease", mPendingRelease.size());
-
- mNumAcquired--;
- ATRACE_INT(mPendingBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired);
- processNextBufferLocked(false /* useNextTransaction */);
+ ATRACE_INT(mQueuedBufferTrace.c_str(),
+ mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
mCallbackCV.notify_all();
}
@@ -429,7 +429,9 @@
}
mNumAcquired++;
- mSubmitted[buffer->getId()] = bufferItem;
+ mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
+ ReleaseCallbackId releaseCallbackId(buffer->getId(), mLastAcquiredFrameNumber);
+ mSubmitted[releaseCallbackId] = bufferItem;
bool needsDisconnect = false;
mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -443,7 +445,6 @@
incStrong((void*)transactionCallbackThunk);
Rect crop = computeCrop(bufferItem);
- mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
bufferItem.mScalingMode, crop);
@@ -452,7 +453,7 @@
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4);
- t->setBuffer(mSurfaceControl, buffer, releaseBufferCallback);
+ t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback);
t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
@@ -511,11 +512,11 @@
BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
" applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
- " graphicBufferId=%" PRIu64,
+ " graphicBufferId=%" PRIu64 "%s",
mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
- static_cast<uint32_t>(mPendingTransactions.size()),
- bufferItem.mGraphicBuffer->getId());
+ static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(),
+ bufferItem.mAutoRefresh ? " mAutoRefresh" : "");
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -538,7 +539,8 @@
}
// add to shadow queue
mNumFrameAvailable++;
- ATRACE_INT(mPendingBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired);
+ ATRACE_INT(mQueuedBufferTrace.c_str(),
+ mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
toString(nextTransactionSet));
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 17499ec..98e8b54 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -125,7 +125,7 @@
for (const auto& data : jankData) {
SAFE_PARCEL(output->writeParcelable, data);
}
- SAFE_PARCEL(output->writeUint64, previousBufferId);
+ SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId);
return NO_ERROR;
}
@@ -149,7 +149,7 @@
SAFE_PARCEL(input->readParcelable, &data);
jankData.push_back(data);
}
- SAFE_PARCEL(input->readUint64, &previousBufferId);
+ SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId);
return NO_ERROR;
}
@@ -253,11 +253,11 @@
stats);
}
- void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence, uint32_t transformHint,
- uint32_t currentMaxAcquiredBufferCount) override {
+ void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
+ uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override {
callRemoteAsync<decltype(
&ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
- graphicBufferId, releaseFence,
+ callbackId, releaseFence,
transformHint,
currentMaxAcquiredBufferCount);
}
@@ -308,4 +308,18 @@
return NO_ERROR;
}
+status_t ReleaseCallbackId::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeUint64, bufferId);
+ SAFE_PARCEL(output->writeUint64, framenumber);
+ return NO_ERROR;
+}
+
+status_t ReleaseCallbackId::readFromParcel(const Parcel* input) {
+ SAFE_PARCEL(input->readUint64, &bufferId);
+ SAFE_PARCEL(input->readUint64, &framenumber);
+ return NO_ERROR;
+}
+
+const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0);
+
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c69435d..6ea070ea 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -182,12 +182,12 @@
void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
sp<SurfaceControl> surfaceControl) {
- std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
+ std::lock_guard<std::mutex> lock(mMutex);
mJankListeners.insert({surfaceControl->getHandle(), listener});
}
void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
- std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
+ std::lock_guard<std::mutex> lock(mMutex);
for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
if (it->second == listener) {
it = mJankListeners.erase(it);
@@ -197,15 +197,16 @@
}
}
-void TransactionCompletedListener::setReleaseBufferCallback(uint64_t graphicBufferId,
+void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbackId& callbackId,
ReleaseBufferCallback listener) {
std::scoped_lock<std::mutex> lock(mMutex);
- mReleaseBufferCallbacks[graphicBufferId] = listener;
+ mReleaseBufferCallbacks[callbackId] = listener;
}
-void TransactionCompletedListener::removeReleaseBufferCallback(uint64_t graphicBufferId) {
+void TransactionCompletedListener::removeReleaseBufferCallback(
+ const ReleaseCallbackId& callbackId) {
std::scoped_lock<std::mutex> lock(mMutex);
- mReleaseBufferCallbacks.erase(graphicBufferId);
+ mReleaseBufferCallbacks.erase(callbackId);
}
void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
@@ -242,6 +243,7 @@
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
+ std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -257,6 +259,7 @@
* sp<SurfaceControl> that could possibly exist for the callbacks.
*/
callbacksMap = mCallbacks;
+ jankListenersMap = mJankListeners;
for (const auto& transactionStats : listenerStats.transactionStats) {
for (auto& callbackId : transactionStats.callbackIds) {
mCallbacks.erase(callbackId);
@@ -317,14 +320,15 @@
// and call them. This is a performance optimization when we have a transaction
// callback and a release buffer callback happening at the same time to avoid an
// additional ipc call from the server.
- if (surfaceStats.previousBufferId) {
+ if (surfaceStats.previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
ReleaseBufferCallback callback;
{
std::scoped_lock<std::mutex> lock(mMutex);
- callback = popReleaseBufferCallbackLocked(surfaceStats.previousBufferId);
+ callback = popReleaseBufferCallbackLocked(
+ surfaceStats.previousReleaseCallbackId);
}
if (callback) {
- callback(surfaceStats.previousBufferId,
+ callback(surfaceStats.previousReleaseCallbackId,
surfaceStats.previousReleaseFence
? surfaceStats.previousReleaseFence
: Fence::NO_FENCE,
@@ -352,12 +356,7 @@
}
if (surfaceStats.jankData.empty()) continue;
-
- // Acquire jank listener lock such that we guarantee that after calling unregister,
- // there won't be any further callback.
- std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
- auto copy = mJankListeners;
- auto jankRange = copy.equal_range(surfaceStats.surfaceControl);
+ auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl);
for (auto it = jankRange.first; it != jankRange.second; it++) {
it->second->onJankDataAvailable(surfaceStats.jankData);
}
@@ -365,25 +364,26 @@
}
}
-void TransactionCompletedListener::onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence,
- uint32_t transformHint,
+void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
+ sp<Fence> releaseFence, uint32_t transformHint,
uint32_t currentMaxAcquiredBufferCount) {
ReleaseBufferCallback callback;
{
std::scoped_lock<std::mutex> lock(mMutex);
- callback = popReleaseBufferCallbackLocked(graphicBufferId);
+ callback = popReleaseBufferCallbackLocked(callbackId);
}
if (!callback) {
- ALOGE("Could not call release buffer callback, buffer not found %" PRIu64, graphicBufferId);
+ ALOGE("Could not call release buffer callback, buffer not found %s",
+ callbackId.to_string().c_str());
return;
}
- callback(graphicBufferId, releaseFence, transformHint, currentMaxAcquiredBufferCount);
+ callback(callbackId, releaseFence, transformHint, currentMaxAcquiredBufferCount);
}
ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
- uint64_t graphicBufferId) {
+ const ReleaseCallbackId& callbackId) {
ReleaseBufferCallback callback;
- auto itr = mReleaseBufferCallbacks.find(graphicBufferId);
+ auto itr = mReleaseBufferCallbacks.find(callbackId);
if (itr == mReleaseBufferCallbacks.end()) {
return nullptr;
}
@@ -1261,7 +1261,7 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
- const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+ const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, const ReleaseCallbackId& id,
ReleaseBufferCallback callback) {
layer_state_t* s = getLayerState(sc);
if (!s) {
@@ -1274,7 +1274,7 @@
if (mIsAutoTimestamp) {
mDesiredPresentTime = systemTime();
}
- setReleaseBufferCallback(s, callback);
+ setReleaseBufferCallback(s, id, callback);
registerSurfaceControlForCallback(sc);
@@ -1289,10 +1289,13 @@
s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
s->releaseBufferListener = nullptr;
- TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(s->buffer->getId());
+ auto listener = TransactionCompletedListener::getInstance();
+ listener->removeReleaseBufferCallback(s->releaseCallbackId);
+ s->releaseCallbackId = ReleaseCallbackId::INVALID_ID;
}
void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
+ const ReleaseCallbackId& id,
ReleaseBufferCallback callback) {
if (!callback) {
return;
@@ -1306,8 +1309,9 @@
s->what |= layer_state_t::eReleaseBufferListenerChanged;
s->releaseBufferListener = TransactionCompletedListener::getIInstance();
+ s->releaseCallbackId = id;
auto listener = TransactionCompletedListener::getInstance();
- listener->setReleaseBufferCallback(s->buffer->getId(), callback);
+ listener->setReleaseBufferCallback(id, callback);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index cb0e65e..0d6a673 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -89,7 +89,7 @@
void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
- void releaseBufferCallback(uint64_t graphicBufferId, const sp<Fence>& releaseFence,
+ void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
void setNextTransaction(SurfaceComposerClient::Transaction *t);
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
@@ -125,7 +125,11 @@
static PixelFormat convertBufferFormat(PixelFormat& format);
std::string mName;
- std::string mPendingBufferTrace;
+ // Represents the queued buffer count from buffer queue,
+ // pre-BLAST. This is mNumFrameAvailable (buffers that queued to blast) +
+ // mNumAcquired (buffers that queued to SF) mPendingRelease.size() (buffers that are held by
+ // blast). This counter is read by android studio profiler.
+ std::string mQueuedBufferTrace;
sp<SurfaceControl> mSurfaceControl;
std::mutex mMutex;
@@ -140,13 +144,14 @@
// Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
// buffer or the buffer has been presented and a new buffer is ready to be presented.
- std::unordered_map<uint64_t /* bufferId */, BufferItem> mSubmitted GUARDED_BY(mMutex);
+ std::unordered_map<ReleaseCallbackId, BufferItem, ReleaseBufferCallbackIdHash> mSubmitted
+ GUARDED_BY(mMutex);
// Keep a queue of the released buffers instead of immediately releasing
// the buffers back to the buffer queue. This would be controlled by SF
// setting the max acquired buffer count.
struct ReleasedBuffer {
- uint64_t bufferId;
+ ReleaseCallbackId callbackId;
sp<Fence> releaseFence;
};
std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex);
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index d286c34..937095c 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -53,6 +53,36 @@
std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
};
+class ReleaseCallbackId : public Parcelable {
+public:
+ static const ReleaseCallbackId INVALID_ID;
+
+ uint64_t bufferId;
+ uint64_t framenumber;
+ ReleaseCallbackId() {}
+ ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
+ : bufferId(bufferId), framenumber(framenumber) {}
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ bool operator==(const ReleaseCallbackId& rhs) const {
+ return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
+ }
+ bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
+ std::string to_string() const {
+ if (*this == INVALID_ID) return "INVALID_ID";
+
+ return "bufferId:" + std::to_string(bufferId) +
+ " framenumber:" + std::to_string(framenumber);
+ }
+};
+
+struct ReleaseBufferCallbackIdHash {
+ std::size_t operator()(const ReleaseCallbackId& key) const {
+ return std::hash<uint64_t>()(key.bufferId);
+ }
+};
+
class FrameEventHistoryStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
@@ -103,7 +133,7 @@
SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
uint32_t hint, uint32_t currentMaxAcquiredBuffersCount,
FrameEventHistoryStats frameEventStats, std::vector<JankData> jankData,
- uint64_t previousBufferId)
+ ReleaseCallbackId previousReleaseCallbackId)
: surfaceControl(sc),
acquireTime(time),
previousReleaseFence(prevReleaseFence),
@@ -111,7 +141,7 @@
currentMaxAcquiredBufferCount(currentMaxAcquiredBuffersCount),
eventStats(frameEventStats),
jankData(std::move(jankData)),
- previousBufferId(previousBufferId) {}
+ previousReleaseCallbackId(previousReleaseCallbackId) {}
sp<IBinder> surfaceControl;
nsecs_t acquireTime = -1;
@@ -120,7 +150,7 @@
uint32_t currentMaxAcquiredBufferCount = 0;
FrameEventHistoryStats eventStats;
std::vector<JankData> jankData;
- uint64_t previousBufferId;
+ ReleaseCallbackId previousReleaseCallbackId;
};
class TransactionStats : public Parcelable {
@@ -161,7 +191,7 @@
virtual void onTransactionCompleted(ListenerStats stats) = 0;
- virtual void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence,
+ virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
uint32_t transformHint,
uint32_t currentMaxAcquiredBufferCount) = 0;
};
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 16430b3..8ac1e5d 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -234,6 +234,12 @@
// layers only. The callback includes a release fence as well as the graphic
// buffer id to identify the buffer.
sp<ITransactionCompletedListener> releaseBufferListener;
+
+ // Keeps track of the release callback id associated with the listener. This
+ // is not sent to the server since the id can be reconstructed there. This
+ // is used to remove the old callback from the client process map if it is
+ // overwritten by another setBuffer call.
+ ReleaseCallbackId releaseCallbackId;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index fa91bfa..c2963b5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -82,7 +82,7 @@
std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
const std::vector<SurfaceControlStats>& /*stats*/)>;
using ReleaseBufferCallback =
- std::function<void(uint64_t /* graphicsBufferId */, const sp<Fence>& /*releaseFence*/,
+ std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount)>;
using SurfaceStatsCallback =
@@ -397,8 +397,9 @@
void cacheBuffers();
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
- void setReleaseBufferCallback(layer_state_t* state, ReleaseBufferCallback callback);
- void removeReleaseBufferCallback(layer_state_t* state);
+ void setReleaseBufferCallback(layer_state_t*, const ReleaseCallbackId&,
+ ReleaseBufferCallback);
+ void removeReleaseBufferCallback(layer_state_t*);
public:
Transaction();
@@ -470,6 +471,7 @@
Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
bool transformToDisplayInverse);
Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+ const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID,
ReleaseBufferCallback callback = nullptr);
Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
@@ -653,9 +655,6 @@
std::mutex mMutex;
// This lock needs to be recursive so we can unregister a callback from within that callback.
- std::recursive_mutex mJankListenerMutex;
-
- // This lock needs to be recursive so we can unregister a callback from within that callback.
std::recursive_mutex mSurfaceStatsListenerMutex;
bool mListening GUARDED_BY(mMutex) = false;
@@ -680,11 +679,8 @@
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
GUARDED_BY(mMutex);
-
- // This is protected by mJankListenerMutex, but GUARDED_BY isn't supported for
- // std::recursive_mutex
- std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners;
- std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
+ std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
+ std::unordered_map<ReleaseCallbackId, ReleaseBufferCallback, ReleaseBufferCallbackIdHash>
mReleaseBufferCallbacks GUARDED_BY(mMutex);
// This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for
@@ -723,16 +719,16 @@
SurfaceStatsCallback listener);
void removeSurfaceStatsListener(void* context, void* cookie);
- void setReleaseBufferCallback(uint64_t /* graphicsBufferId */, ReleaseBufferCallback);
- void removeReleaseBufferCallback(uint64_t /* graphicsBufferId */);
+ void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
+ void removeReleaseBufferCallback(const ReleaseCallbackId&);
// BnTransactionCompletedListener overrides
void onTransactionCompleted(ListenerStats stats) override;
- void onReleaseBuffer(uint64_t /* graphicsBufferId */, sp<Fence> releaseFence,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override;
+ void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint,
+ uint32_t currentMaxAcquiredBufferCount) override;
private:
- ReleaseBufferCallback popReleaseBufferCallbackLocked(uint64_t /* graphicsBufferId */);
+ ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
};
} // namespace android
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index b3975b0..725f57b 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -72,6 +72,8 @@
// have color correction added, and important that it be srgb, so the *vertex* shader
// doesn't have color correction added.
.sourceDataspace = kDestDataSpace,
+ // setting this is mandatory for shadows and blurs
+ .skipContentDraw = true,
};
auto layers = std::vector<const LayerSettings*>{&layer};
@@ -173,9 +175,12 @@
.boundaries = rect,
},
.alpha = 1,
+ // setting this is mandatory for shadows and blurs
+ .skipContentDraw = true,
};
auto layers = std::vector<const LayerSettings*>{&layer};
+ // Different blur code is invoked for radii less and greater than 30 pixels
for (int radius : {9, 60}) {
layer.backgroundBlurRadius = radius;
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
@@ -290,10 +295,13 @@
ExternalTexture::Usage::READABLE |
ExternalTexture::Usage::WRITEABLE);
+ // 6 shaders
drawSolidLayers(renderengine, display, dstTexture);
+ // 8 shaders
drawShadowLayers(renderengine, display, srcTexture);
if (renderengine->supportsBackgroundBlur()) {
+ // 2 shaders
drawBlurLayers(renderengine, display, dstTexture);
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 2281721..726fe8e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2161,8 +2161,9 @@
bool SensorService::isRateCappedBasedOnPermission(const String16& opPackageName) {
int targetSdk = getTargetSdkVersion(opPackageName);
- bool hasSamplingRatePermission = PermissionCache::checkCallingPermission(
- sAccessHighSensorSamplingRatePermission);
+ bool hasSamplingRatePermission = checkPermission(sAccessHighSensorSamplingRatePermission,
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
if (targetSdk < __ANDROID_API_S__ ||
(targetSdk >= __ANDROID_API_S__ && hasSamplingRatePermission)) {
return false;
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index cd3d80e..760c8b9 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -133,6 +133,7 @@
bool mTransformToDisplayInverse{false};
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
+ uint64_t mFrameNumber;
int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
bool mFrameLatencyNeeded{false};
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a3b7b13..2a49a0a 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -44,25 +44,18 @@
using PresentState = frametimeline::SurfaceFrame::PresentState;
namespace {
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
- const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) {
+ const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+ const sp<Fence>& releaseFence, uint32_t transformHint,
+ uint32_t currentMaxAcquiredBufferCount) {
if (!listener) {
return;
}
- listener->onReleaseBuffer(buffer->getId(), releaseFence ? releaseFence : Fence::NO_FENCE,
- transformHint, currentMaxAcquiredBufferCount);
+ listener->onReleaseBuffer({buffer->getId(), framenumber},
+ releaseFence ? releaseFence : Fence::NO_FENCE, transformHint,
+ currentMaxAcquiredBufferCount);
}
} // namespace
-// clang-format off
-const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
-};
-// clang-format on
-
BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
: BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
@@ -75,8 +68,8 @@
// issue with the clone layer trying to use the texture.
if (mBufferInfo.mBuffer != nullptr && !isClone()) {
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
- mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFence,
- mTransformHint,
+ mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
+ mBufferInfo.mFence, mTransformHint,
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
mOwnerUid));
}
@@ -87,7 +80,7 @@
if (ch == nullptr) {
return OK;
}
- ch->previousBufferId = mPreviousBufferId;
+ ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
if (!ch->previousReleaseFence.get()) {
ch->previousReleaseFence = fence;
return OK;
@@ -214,7 +207,7 @@
// see BufferStateLayer::onLayerDisplayed.
for (auto& handle : mDrawingState.callbackHandles) {
if (handle->releasePreviousBuffer) {
- handle->previousBufferId = mPreviousBufferId;
+ handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
break;
}
}
@@ -438,8 +431,8 @@
// dropped and we should decrement the pending buffer count and
// call any release buffer callbacks if set.
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
- mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence,
- mTransformHint,
+ mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+ mDrawingState.acquireFence, mTransformHint,
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
mOwnerUid));
decrementPendingBufferCount();
@@ -540,10 +533,16 @@
bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
if (mDrawingState.sidebandStream == sidebandStream) return false;
+
+ if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
+ mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
+ } else if (sidebandStream != nullptr) {
+ mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount();
+ }
+
mDrawingState.sidebandStream = sidebandStream;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
-
if (!mSidebandStreamChanged.exchange(true)) {
// mSidebandStreamChanged was false
mFlinger->signalLayerUpdate();
@@ -678,7 +677,7 @@
* DeferTransactionUntil -> frameNumber = 2
* Random other stuff
* }
- * Now imagine getHeadFrameNumber returned mDrawingState.mFrameNumber (or mCurrentFrameNumber).
+ * Now imagine mFrameNumber returned mDrawingState.frameNumber (or mCurrentFrameNumber).
* Prior to doTransaction SurfaceFlinger will call notifyAvailableFrames, but because we
* haven't swapped mDrawingState to mDrawingState yet we will think the sync point
* is not ready. So we will return false from applyPendingState and not swap
@@ -719,7 +718,7 @@
bool BufferStateLayer::hasFrameUpdate() const {
const State& c(getDrawingState());
- return mDrawingStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
+ return (mDrawingStateModified || mDrawingState.modified) && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}
status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
@@ -786,9 +785,10 @@
decrementPendingBufferCount();
}
- mPreviousBufferId = getCurrentBufferId();
+ mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
mBufferInfo.mBuffer = s.buffer;
mBufferInfo.mFence = s.acquireFence;
+ mBufferInfo.mFrameNumber = s.frameNumber;
return NO_ERROR;
}
@@ -963,8 +963,8 @@
// then we will drop a buffer and should decrement the pending buffer count and
// call any release buffer callbacks if set.
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
- mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence,
- mTransformHint,
+ mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+ mDrawingState.acquireFence, mTransformHint,
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
mOwnerUid));
decrementPendingBufferCount();
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 2747018..e567478 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -143,15 +143,8 @@
bool bufferNeedsFiltering() const override;
- static const std::array<float, 16> IDENTITY_MATRIX;
-
- std::unique_ptr<renderengine::Image> mTextureImage;
-
- mutable uint64_t mFrameNumber{0};
- uint64_t mFrameCounter{0};
-
sp<Fence> mPreviousReleaseFence;
- uint64_t mPreviousBufferId = 0;
+ ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
uint64_t mPreviousReleasedFrameNumber = 0;
bool mReleasePreviousBuffer = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 7cb0f6b..2bf931c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -141,7 +141,7 @@
compositionengine::OutputLayer* getBlurLayer() const;
- bool hasHdrLayers() const;
+ bool hasUnsupportedDataspace() const;
bool hasProtectedLayers() const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index a20d7b3..bce438f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -242,13 +242,6 @@
ui::Dataspace getDataspace() const { return mOutputDataspace.get(); }
- bool isHdr() const {
- const ui::Dataspace transfer =
- static_cast<ui::Dataspace>(getDataspace() & ui::Dataspace::TRANSFER_MASK);
- return (transfer == ui::Dataspace::TRANSFER_ST2084 ||
- transfer == ui::Dataspace::TRANSFER_HLG);
- }
-
bool isProtected() const {
return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index acc7ed2..b24274e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -295,9 +295,7 @@
return false;
}
- // Do not use a hole punch with an HDR layer; this should be done in client
- // composition to properly mix HDR with SDR.
- if (hasHdrLayers()) {
+ if (hasUnsupportedDataspace()) {
return false;
}
@@ -352,9 +350,22 @@
return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr;
}
-bool CachedSet::hasHdrLayers() const {
- return std::any_of(mLayers.cbegin(), mLayers.cend(),
- [](const Layer& layer) { return layer.getState()->isHdr(); });
+bool CachedSet::hasUnsupportedDataspace() const {
+ return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
+ auto dataspace = layer.getState()->getDataspace();
+ const auto transfer = static_cast<ui::Dataspace>(dataspace & ui::Dataspace::TRANSFER_MASK);
+ if (transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG) {
+ // Skip HDR.
+ return true;
+ }
+
+ if ((dataspace & HAL_DATASPACE_STANDARD_MASK) == HAL_DATASPACE_STANDARD_BT601_625) {
+ // RenderEngine does not match some DPUs, so skip
+ // to avoid flickering/color differences.
+ return true;
+ }
+ return false;
+ });
}
bool CachedSet::hasProtectedLayers() const {
@@ -378,10 +389,14 @@
if (mLayers.size() == 1) {
base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get());
+ base::StringAppendF(&result, " Protected [%s]",
+ mLayers[0].getState()->isProtected() ? "true" : "false");
} else {
result.append(" Cached set of:");
for (const Layer& layer : mLayers) {
base::StringAppendF(&result, "\n Layer [%s]", layer.getName().c_str());
+ base::StringAppendF(&result, "\n Protected [%s]",
+ layer.getState()->isProtected() ? "true" : "false");
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 2bcaf60..f033279 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -420,7 +420,7 @@
const bool layerIsInactive = now - currentSet->getLastUpdate() > mActiveLayerTimeout;
const bool layerHasBlur = currentSet->hasBlurBehind();
if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
- !currentSet->hasHdrLayers() && !currentSet->hasProtectedLayers()) {
+ !currentSet->hasUnsupportedDataspace()) {
if (isPartOfRun) {
builder.append(currentSet->getLayerCount());
} else {
@@ -491,6 +491,14 @@
return;
}
+ for (const CachedSet& layer : mLayers) {
+ // TODO (b/191997217): make it less aggressive, and sync with findCandidateRuns
+ if (layer.hasProtectedLayers()) {
+ ATRACE_NAME("layer->hasProtectedLayers()");
+ return;
+ }
+ }
+
std::vector<Run> runs = findCandidateRuns(now);
std::optional<Run> bestRun = findBestRun(runs);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 8423a12..936dba3 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -42,7 +42,13 @@
}
Flags<LayerStateField> LayerState::update(compositionengine::OutputLayer* layer) {
- ALOGE_IF(layer != mOutputLayer, "[%s] Expected mOutputLayer to never change", __func__);
+ ALOGE_IF(mOutputLayer != layer && layer->getLayerFE().getSequence() != mId.get(),
+ "[%s] Expected mOutputLayer ID to never change: %d, %d", __func__,
+ layer->getLayerFE().getSequence(), mId.get());
+
+ // It's possible for the OutputLayer pointer to change even when the layer is logically the
+ // same, i.e., the LayerFE is the same. An example use-case is screen rotation.
+ mOutputLayer = layer;
Flags<LayerStateField> differences;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index be2510f..f077470 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -113,15 +113,6 @@
}
}
- for (LayerId removedLayer : removedLayers) {
- if (const auto layerEntry = mPreviousLayers.find(removedLayer);
- layerEntry != mPreviousLayers.end()) {
- const auto& [id, state] = *layerEntry;
- ALOGV("Removed layer %s", state.getName().c_str());
- mPreviousLayers.erase(removedLayer);
- }
- }
-
mCurrentLayers.clear();
mCurrentLayers.reserve(currentLayerIds.size());
std::transform(currentLayerIds.cbegin(), currentLayerIds.cend(),
@@ -135,6 +126,7 @@
mFlattenedHash =
mFlattener.flattenLayers(mCurrentLayers, hash, std::chrono::steady_clock::now());
const bool layersWereFlattened = hash != mFlattenedHash;
+
ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
if (mPredictorEnabled) {
@@ -148,6 +140,17 @@
ALOGV("[%s] No prediction found\n", __func__);
}
}
+
+ // Clean up the set of previous layers now that the view of the LayerStates in the flattener are
+ // up-to-date.
+ for (LayerId removedLayer : removedLayers) {
+ if (const auto layerEntry = mPreviousLayers.find(removedLayer);
+ layerEntry != mPreviousLayers.end()) {
+ const auto& [id, state] = *layerEntry;
+ ALOGV("Removed layer %s", state.getName().c_str());
+ mPreviousLayers.erase(removedLayer);
+ }
+ }
}
void Planner::reportFinalPlan(
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 0acc317..7f0e186 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -457,6 +457,20 @@
EXPECT_FALSE(cachedSet.requiresHolePunch());
}
+TEST_F(CachedSetTest, holePunch_requiresNonBT601_625) {
+ mTestLayers[0]->outputLayerCompositionState.dataspace = ui::Dataspace::STANDARD_BT601_625;
+ mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer);
+
+ CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+ mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+ sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+
+ CachedSet cachedSet(layer);
+ EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
TEST_F(CachedSetTest, requiresHolePunch) {
CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 334b855..f5cfd2f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -937,5 +937,152 @@
(kCachedSetRenderDuration + 10ms));
}
+TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // The third layer uses a dataspace that will not be flattened due to
+ // possible mismatch with DPU rendering.
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ mTestLayers[2]->outputLayerCompositionState.dataspace = ui::Dataspace::STANDARD_BT601_625;
+ mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ mTime += 200ms;
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_skipsHDR) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // The third layer uses a dataspace that will not be flattened due to
+ // possible mismatch with DPU rendering.
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ mTestLayers[2]->outputLayerCompositionState.dataspace = ui::Dataspace::BT2020_ITU_HLG;
+ mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ mTime += 200ms;
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_skipsHDR2) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // The third layer uses a dataspace that will not be flattened due to
+ // possible mismatch with DPU rendering.
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ mTestLayers[2]->outputLayerCompositionState.dataspace = ui::Dataspace::BT2020_PQ;
+ mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ mTime += 200ms;
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index a09ce14..9ad3ab4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -117,6 +117,22 @@
EXPECT_EQ(&mOutputLayer, mLayerState->getOutputLayer());
}
+TEST_F(LayerStateTest, updateOutputLayer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(&mOutputLayer, mLayerState->getOutputLayer());
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(&newOutputLayer, mLayerState->getOutputLayer());
+}
+
TEST_F(LayerStateTest, getId) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6c9905e..ad31b3f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -70,6 +70,7 @@
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
+#include "TunnelModeEnabledReporter.h"
#include "input/InputWindow.h"
#define DEBUG_RESIZE 0
@@ -172,6 +173,10 @@
mFrameTracker.logAndResetStats(mName);
mFlinger->onLayerDestroyed(this);
+
+ if (mDrawingState.sidebandStream != nullptr) {
+ mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
+ }
}
LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
@@ -700,11 +705,6 @@
mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering();
}
- if (mDrawingState.inputInfoChanged) {
- flags |= eInputInfoChanged;
- mDrawingState.inputInfoChanged = false;
- }
-
commitTransaction(mDrawingState);
return flags;
@@ -777,6 +777,8 @@
mDrawingState.z = z;
mDrawingState.modified = true;
+ mFlinger->mSomeChildrenChanged = true;
+
// Discard all relative layering.
if (mDrawingState.zOrderRelativeOf != nullptr) {
sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote();
@@ -827,6 +829,8 @@
return false;
}
+ mFlinger->mSomeChildrenChanged = true;
+
mDrawingState.sequence++;
mDrawingState.modified = true;
mDrawingState.z = relativeZ;
@@ -1986,7 +1990,7 @@
mDrawingState.inputInfo = info;
mDrawingState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
mDrawingState.modified = true;
- mDrawingState.inputInfoChanged = true;
+ mFlinger->mInputInfoChanged = true;
setTransactionFlags(eTransactionNeeded);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 84d6d3f..ec9bb7c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -186,7 +186,6 @@
float cornerRadius;
int backgroundBlurRadius;
- bool inputInfoChanged;
InputWindowInfo inputInfo;
wp<Layer> touchableRegionCrop;
@@ -997,7 +996,7 @@
// This layer can be a cursor on some displays.
bool mPotentialCursor{false};
- LayerVector mCurrentChildren{LayerVector::StateSet::Drawing};
+ LayerVector mCurrentChildren{LayerVector::StateSet::Current};
LayerVector mDrawingChildren{LayerVector::StateSet::Drawing};
wp<Layer> mCurrentParent;
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 18a2891..6b2d745 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -140,6 +140,11 @@
outTransformMatrix);
}
+status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+ Rect* outRect, uint32_t* outTransform) {
+ return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
+}
+
void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
mProducer->getFrameTimestamps(outDelta);
}
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 788919b..3778277 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -64,6 +64,8 @@
virtual status_t setLegacyBufferDrop(bool drop) override;
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) override;
+ virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+ Rect* outRect, uint32_t* outTransform) override;
virtual IBinder* onAsBinder();
virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
virtual status_t setAutoRefresh(bool autoRefresh) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 0eb16e2..0334d70 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -107,9 +107,39 @@
return {quotient, remainder};
}
+bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
+ const RefreshRate& refreshRate) const {
+ switch (layer.vote) {
+ case LayerVoteType::ExplicitExactOrMultiple:
+ case LayerVoteType::Heuristic:
+ if (mConfig.frameRateMultipleThreshold != 0 &&
+ refreshRate.fps.greaterThanOrEqualWithMargin(
+ Fps(mConfig.frameRateMultipleThreshold)) &&
+ layer.desiredRefreshRate.lessThanWithMargin(
+ Fps(mConfig.frameRateMultipleThreshold / 2))) {
+ // Don't vote high refresh rates past the threshold for layers with a low desired
+ // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
+ // 120 Hz, but desired 60 fps should have a vote.
+ return false;
+ }
+ break;
+ case LayerVoteType::ExplicitDefault:
+ case LayerVoteType::ExplicitExact:
+ case LayerVoteType::Max:
+ case LayerVoteType::Min:
+ case LayerVoteType::NoVote:
+ break;
+ }
+ return true;
+}
+
float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
const RefreshRate& refreshRate,
bool isSeamlessSwitch) const {
+ if (!isVoteAllowed(layer, refreshRate)) {
+ return 0;
+ }
+
// Slightly prefer seamless switches.
constexpr float kSeamedSwitchPenalty = 0.95f;
const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
@@ -331,8 +361,9 @@
const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
for (const auto& layer : layers) {
- ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
- layerVoteTypeString(layer.vote).c_str(), layer.weight);
+ ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
+ layerVoteTypeString(layer.vote).c_str(), layer.weight,
+ layer.desiredRefreshRate.getValue());
if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
continue;
}
@@ -646,9 +677,8 @@
}
RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
- bool enableFrameRateOverride)
- : mKnownFrameRates(constructKnownFrameRates(modes)),
- mEnableFrameRateOverride(enableFrameRateOverride) {
+ Config config)
+ : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
updateDisplayModes(modes, currentModeId);
}
@@ -685,7 +715,7 @@
mMaxSupportedRefreshRate = sortedModes.back();
mSupportsFrameRateOverride = false;
- if (mEnableFrameRateOverride) {
+ if (mConfig.enableFrameRateOverride) {
for (const auto& mode1 : sortedModes) {
for (const auto& mode2 : sortedModes) {
if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) {
@@ -859,6 +889,7 @@
const auto& deviceMin = *mMinSupportedRefreshRate;
const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+ const auto& currentPolicy = getCurrentPolicyLocked();
// Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
// the min allowed refresh rate is higher than the device min, we do not want to enable the
@@ -867,6 +898,10 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
}
if (minByPolicy == maxByPolicy) {
+ // when min primary range in display manager policy is below device min turn on the timer.
+ if (currentPolicy->primaryRange.min.lessThanWithMargin(deviceMin.getFps())) {
+ return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+ }
return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
}
// Turn on the timer in all other cases.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 6cd0f42..dfd1395 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -53,7 +53,7 @@
public:
// Margin used when matching refresh rates to the content desired ones.
static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
- std::chrono::nanoseconds(800us).count();
+ std::chrono::nanoseconds(800us).count();
class RefreshRate {
private:
@@ -302,8 +302,19 @@
// Returns a known frame rate that is the closest to frameRate
Fps findClosestKnownFrameRate(Fps frameRate) const;
+ // Configuration flags.
+ struct Config {
+ bool enableFrameRateOverride = false;
+
+ // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple
+ // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
+ // no threshold is set.
+ int frameRateMultipleThreshold = 0;
+ };
+
RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
- bool enableFrameRateOverride = false);
+ Config config = {.enableFrameRateOverride = false,
+ .frameRateMultipleThreshold = 0});
void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
@@ -315,8 +326,7 @@
return mRefreshRates.size() > 1;
}
- // Class to enumerate options around toggling the kernel timer on and off. We have an option
- // for no change to avoid extra calls to kernel.
+ // Class to enumerate options around toggling the kernel timer on and off.
enum class KernelIdleTimerAction {
TurnOff, // Turn off the idle timer.
TurnOn // Turn on the idle timer.
@@ -387,6 +397,9 @@
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
+ // Returns whether the layer is allowed to vote for the given refresh rate.
+ bool isVoteAllowed(const LayerRequirement&, const RefreshRate&) const;
+
// calculates a score for a layer. Used to determine the display refresh rate
// and the frame rate override for certains applications.
float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
@@ -424,7 +437,7 @@
// from based on the closest value.
const std::vector<Fps> mKnownFrameRates;
- const bool mEnableFrameRateOverride;
+ const Config mConfig;
bool mSupportsFrameRateOverride;
struct GetBestRefreshRateInvocation {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b99531b..e2f3ebb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -342,6 +342,7 @@
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
+ mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {
ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
@@ -2664,9 +2665,7 @@
sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
- if (maxFrameBufferAcquiredBuffers >= 3) {
- nativeWindowSurface->preallocateBuffers();
- }
+ nativeWindowSurface->preallocateBuffers();
ColorMode defaultColorMode = ColorMode::NATIVE;
Dataspace defaultDataSpace = Dataspace::UNKNOWN;
@@ -2923,24 +2922,8 @@
processDisplayChangesLocked();
processDisplayHotplugEventsLocked();
}
-
- // Commit layer transactions. This needs to happen after display transactions are
- // committed because some geometry logic relies on display orientation.
- if ((transactionFlags & eTraversalNeeded) || mForceTraversal || displayTransactionNeeded) {
- mForceTraversal = false;
- mCurrentState.traverse([&](Layer* layer) {
- uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
- if (!trFlags && !displayTransactionNeeded) return;
-
- const uint32_t flags = layer->doTransaction(0);
- if (flags & Layer::eVisibleRegion)
- mVisibleRegionsDirty = true;
-
- if (flags & Layer::eInputInfoChanged) {
- mInputInfoChanged = true;
- }
- });
- }
+ mForceTraversal = false;
+ mForceTransactionDisplayChange = displayTransactionNeeded;
if (mSomeChildrenChanged) {
mVisibleRegionsDirty = true;
@@ -3135,10 +3118,15 @@
return;
}
const auto displayId = displayState.physical->id;
- mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
- displayState.physical->activeMode->getId(),
- android::sysprop::enable_frame_rate_override(false));
+ scheduler::RefreshRateConfigs::Config config =
+ {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
+ .frameRateMultipleThreshold =
+ base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0)};
+ mRefreshRateConfigs =
+ std::make_unique<scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
+ displayState.physical->activeMode
+ ->getId(),
+ config);
const auto currRefreshRate = displayState.physical->activeMode->getFps();
mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
hal::PowerMode::OFF);
@@ -3169,7 +3157,6 @@
mRegionSamplingThread =
new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
- mTunnelModeEnabledReporter = new TunnelModeEnabledReporter(*this);
// Dispatch a mode change request for the primary display on scheduler
// initialization, so that the EventThreads always contain a reference to a
// prior configuration.
@@ -3240,9 +3227,12 @@
// clear the "changed" flags in current state
mCurrentState.colorMatrixChanged = false;
- for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
- rootLayer->commitChildList();
+ if (mVisibleRegionsDirty) {
+ for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
+ rootLayer->commitChildList();
+ }
}
+
// TODO(b/163019109): See if this traversal is needed at all...
if (!mOffscreenLayers.empty()) {
mDrawingState.traverse([&](Layer* layer) {
@@ -3301,7 +3291,14 @@
// Display is now waiting on Layer 1's frame, which is behind layer 0's
// second frame. But layer 0's second frame could be waiting on display.
mDrawingState.traverse([&](Layer* layer) {
- if (layer->hasReadyFrame()) {
+ uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
+ if (trFlags || mForceTransactionDisplayChange) {
+ const uint32_t flags = layer->doTransaction(0);
+ if (flags & Layer::eVisibleRegion)
+ mVisibleRegionsDirty = true;
+ }
+
+ if (layer->hasReadyFrame()) {
frameQueued = true;
if (layer->shouldPresentNow(expectedPresentTime)) {
mLayersWithQueuedFrames.emplace(layer);
@@ -3309,10 +3306,11 @@
ATRACE_NAME("!layer->shouldPresentNow()");
layer->useEmptyDamage();
}
- } else {
+ } else {
layer->useEmptyDamage();
}
});
+ mForceTransactionDisplayChange = false;
// The client can continue submitting buffers for offscreen layers, but they will not
// be shown on screen. Therefore, we need to latch and release buffers of offscreen
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 81f569a..f33df86 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -364,6 +364,7 @@
// For unit tests
friend class TestableSurfaceFlinger;
friend class TransactionApplicationTest;
+ friend class TunnelModeEnabledReporterTest;
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using VsyncModulator = scheduler::VsyncModulator;
@@ -1234,6 +1235,7 @@
// TODO: Also move visibleRegions over to a boolean system.
bool mInputInfoChanged = false;
bool mSomeChildrenChanged;
+ bool mForceTransactionDisplayChange = false;
bool mGeometryInvalid = false;
bool mAnimCompositionPending = false;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index fdf16a7..6af69f0 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -237,7 +237,8 @@
handle->previousReleaseFence,
handle->transformHint,
handle->currentMaxAcquiredBufferCount,
- eventStats, jankData, handle->previousBufferId);
+ eventStats, jankData,
+ handle->previousReleaseCallbackId);
}
return NO_ERROR;
}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 444bec6..6f4d812 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -51,7 +51,7 @@
nsecs_t refreshStartTime = 0;
nsecs_t dequeueReadyTime = 0;
uint64_t frameNumber = 0;
- uint64_t previousBufferId = 0;
+ ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
};
class TransactionCallbackInvoker {
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.cpp b/services/surfaceflinger/TunnelModeEnabledReporter.cpp
index 48e3216..4497caf 100644
--- a/services/surfaceflinger/TunnelModeEnabledReporter.cpp
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.cpp
@@ -26,17 +26,10 @@
namespace android {
-TunnelModeEnabledReporter::TunnelModeEnabledReporter(SurfaceFlinger& flinger) : mFlinger(flinger) {}
+TunnelModeEnabledReporter::TunnelModeEnabledReporter() {}
void TunnelModeEnabledReporter::updateTunnelModeStatus() {
- bool tunnelModeEnabled = false;
- mFlinger.mCurrentState.traverse([&](Layer* layer) {
- auto& state = layer->getDrawingState();
- if (state.sidebandStream != nullptr) {
- tunnelModeEnabled = true;
- return;
- }
- });
+ bool tunnelModeEnabled = mTunnelModeCount > 0;
dispatchTunnelModeEnabled(tunnelModeEnabled);
}
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h
index d55507a..935502a 100644
--- a/services/surfaceflinger/TunnelModeEnabledReporter.h
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.h
@@ -29,7 +29,7 @@
class TunnelModeEnabledReporter : public IBinder::DeathRecipient {
public:
- TunnelModeEnabledReporter(SurfaceFlinger& flinger);
+ TunnelModeEnabledReporter();
// Checks if there is a tunnel mode enabled state change and if so, dispatches the updated
// tunnel mode enabled/disabled state to the registered listeners
@@ -49,6 +49,9 @@
// Deregisters a TunnelModeEnabled listener
void removeListener(const sp<gui::ITunnelModeEnabledListener>& listener);
+ inline void incrementTunnelModeCount() { mTunnelModeCount++; }
+ inline void decrementTunnelModeCount() { mTunnelModeCount--; }
+
private:
mutable std::mutex mMutex;
struct WpHash {
@@ -57,10 +60,10 @@
}
};
- SurfaceFlinger& mFlinger;
std::unordered_map<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>, WpHash> mListeners
GUARDED_BY(mMutex);
bool mTunnelModeEnabled GUARDED_BY(mMutex) = false;
+ uint32_t mTunnelModeCount = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index 5aa809d..579a26e 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -29,7 +29,7 @@
// b/181132765 - disabled until cuttlefish failures are investigated
class ReleaseBufferCallbackHelper {
public:
- static void function(void* callbackContext, uint64_t graphicsBufferId,
+ static void function(void* callbackContext, ReleaseCallbackId callbackId,
const sp<Fence>& releaseFence,
uint32_t /*currentMaxAcquiredBufferCount*/) {
if (!callbackContext) {
@@ -38,11 +38,11 @@
ReleaseBufferCallbackHelper* helper =
static_cast<ReleaseBufferCallbackHelper*>(callbackContext);
std::lock_guard lock(helper->mMutex);
- helper->mCallbackDataQueue.emplace(graphicsBufferId, releaseFence);
+ helper->mCallbackDataQueue.emplace(callbackId, releaseFence);
helper->mConditionVariable.notify_all();
}
- void getCallbackData(uint64_t* bufferId) {
+ void getCallbackData(ReleaseCallbackId* callbackId) {
std::unique_lock lock(mMutex);
if (mCallbackDataQueue.empty()) {
if (!mConditionVariable.wait_for(lock, std::chrono::seconds(3),
@@ -53,7 +53,7 @@
auto callbackData = mCallbackDataQueue.front();
mCallbackDataQueue.pop();
- *bufferId = callbackData.first;
+ *callbackId = callbackData.first;
}
void verifyNoCallbacks() {
@@ -72,7 +72,7 @@
std::mutex mMutex;
std::condition_variable mConditionVariable;
- std::queue<std::pair<uint64_t, sp<Fence>>> mCallbackDataQueue;
+ std::queue<std::pair<ReleaseCallbackId, sp<Fence>>> mCallbackDataQueue;
};
class ReleaseBufferCallbackTest : public LayerTransactionTest {
@@ -82,10 +82,11 @@
}
static void submitBuffer(const sp<SurfaceControl>& layer, sp<GraphicBuffer> buffer,
- sp<Fence> fence, CallbackHelper& callback,
+ sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id,
ReleaseBufferCallbackHelper& releaseCallback) {
Transaction t;
- t.setBuffer(layer, buffer, releaseCallback.getCallback());
+ t.setFrameNumber(layer, id.framenumber);
+ t.setBuffer(layer, buffer, id, releaseCallback.getCallback());
t.setAcquireFence(layer, fence);
t.addTransactionCompletedCallback(callback.function, callback.getContext());
t.apply();
@@ -98,10 +99,10 @@
}
static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback,
- uint64_t expectedReleaseBufferId) {
- uint64_t actualReleaseBufferId;
+ const ReleaseCallbackId& expectedCallbackId) {
+ ReleaseCallbackId actualReleaseBufferId;
releaseCallback.getCallbackData(&actualReleaseBufferId);
- EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId);
+ EXPECT_EQ(expectedCallbackId, actualReleaseBufferId);
releaseCallback.verifyNoCallbacks();
}
static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() {
@@ -116,6 +117,10 @@
BufferUsage::COMPOSER_OVERLAY,
"test");
}
+ static uint64_t generateFrameNumber() {
+ static uint64_t sFrameNumber = 0;
+ return ++sFrameNumber;
+ }
};
TEST_F(ReleaseBufferCallbackTest, DISABLED_PresentBuffer) {
@@ -125,7 +130,9 @@
// If a buffer is being presented, we should not emit a release callback.
sp<GraphicBuffer> firstBuffer = getBuffer();
- submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+ submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, firstBufferCallbackId,
+ *releaseCallback);
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
ExpectedResult::Buffer::NOT_ACQUIRED);
@@ -143,13 +150,15 @@
// If a presented buffer is replaced, we should emit a release callback for the
// previously presented buffer.
sp<GraphicBuffer> secondBuffer = getBuffer();
- submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+ submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, secondBufferCallbackId,
+ *releaseCallback);
expected = ExpectedResult();
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
ExpectedResult::Buffer::NOT_ACQUIRED,
ExpectedResult::PreviousBuffer::RELEASED);
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
- ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}
TEST_F(ReleaseBufferCallbackTest, DISABLED_OffScreenLayer) {
@@ -160,7 +169,9 @@
// If a buffer is being presented, we should not emit a release callback.
sp<GraphicBuffer> firstBuffer = getBuffer();
- submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+ submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, firstBufferCallbackId,
+ *releaseCallback);
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
ExpectedResult::Buffer::NOT_ACQUIRED);
@@ -184,23 +195,27 @@
// If a presented buffer is replaced, we should emit a release callback for the
// previously presented buffer.
sp<GraphicBuffer> secondBuffer = getBuffer();
- submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+ submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, secondBufferCallbackId,
+ *releaseCallback);
expected = ExpectedResult();
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
ExpectedResult::Buffer::NOT_ACQUIRED,
ExpectedResult::PreviousBuffer::NOT_RELEASED);
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
- ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
// If continue to submit buffer we continue to get release callbacks
sp<GraphicBuffer> thirdBuffer = getBuffer();
- submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+ ReleaseCallbackId thirdBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+ submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, thirdBufferCallbackId,
+ *releaseCallback);
expected = ExpectedResult();
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
ExpectedResult::Buffer::NOT_ACQUIRED,
ExpectedResult::PreviousBuffer::NOT_RELEASED);
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
- ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBufferCallbackId));
}
TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_layerdestroy) {
@@ -210,7 +225,9 @@
// If a buffer is being presented, we should not emit a release callback.
sp<GraphicBuffer> firstBuffer = getBuffer();
- submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+ submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, firstBufferCallbackId,
+ *releaseCallback);
{
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
@@ -225,7 +242,7 @@
t.apply();
layer = nullptr;
- ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}
// Destroying a never presented layer emits a callback.
@@ -242,7 +259,9 @@
// Submitting a buffer does not emit a callback.
sp<GraphicBuffer> firstBuffer = getBuffer();
- submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+ submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, firstBufferCallbackId,
+ *releaseCallback);
{
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
@@ -253,19 +272,21 @@
// Submitting a second buffer will replace the drawing state buffer and emit a callback.
sp<GraphicBuffer> secondBuffer = getBuffer();
- submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+ submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, secondBufferCallbackId,
+ *releaseCallback);
{
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
ExpectedResult::Buffer::NOT_ACQUIRED);
ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
ASSERT_NO_FATAL_FAILURE(
- waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+ waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}
// Destroying the offscreen layer emits a callback.
layer = nullptr;
- ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBufferCallbackId));
}
TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) {
@@ -275,12 +296,13 @@
// If a buffer is being presented, we should not emit a release callback.
sp<GraphicBuffer> firstBuffer = getBuffer();
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
// Try to present 100ms in the future
nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
Transaction t;
- t.setBuffer(layer, firstBuffer, releaseCallback->getCallback());
+ t.setBuffer(layer, firstBuffer, firstBufferCallbackId, releaseCallback->getCallback());
t.setAcquireFence(layer, Fence::NO_FENCE);
t.addTransactionCompletedCallback(transactionCallback.function,
transactionCallback.getContext());
@@ -295,7 +317,8 @@
// Dropping frames in transaction queue emits a callback
sp<GraphicBuffer> secondBuffer = getBuffer();
- t.setBuffer(layer, secondBuffer, releaseCallback->getCallback());
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+ t.setBuffer(layer, secondBuffer, secondBufferCallbackId, releaseCallback->getCallback());
t.setAcquireFence(layer, Fence::NO_FENCE);
t.addTransactionCompletedCallback(transactionCallback.function,
transactionCallback.getContext());
@@ -307,7 +330,7 @@
ExpectedResult::Buffer::NOT_ACQUIRED,
ExpectedResult::PreviousBuffer::RELEASED);
ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
- ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index d4b229f..3423bd5 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -165,6 +165,7 @@
RefreshRate::ConstructorTag(0)};
RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
RefreshRate::ConstructorTag(0)};
+
private:
DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
ui::Size resolution = ui::Size());
@@ -487,6 +488,52 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
+ RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 90};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ lr.name = "Min";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.vote = LayerVoteType::Max;
+ lr.name = "Max";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.desiredRefreshRate = Fps(90.0f);
+ lr.vote = LayerVoteType::Heuristic;
+ lr.name = "90Hz Heuristic";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.desiredRefreshRate = Fps(60.0f);
+ lr.name = "60Hz Heuristic";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.desiredRefreshRate = Fps(45.0f);
+ lr.name = "45Hz Heuristic";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.desiredRefreshRate = Fps(30.0f);
+ lr.name = "30Hz Heuristic";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.desiredRefreshRate = Fps(24.0f);
+ lr.name = "24Hz Heuristic";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_72_90Device,
@@ -649,6 +696,99 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
+ RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "60Hz Heuristic";
+ EXPECT_EQ(mExpected120Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "60Hz Heuristic";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "60Hz ExplicitDefault";
+ EXPECT_EQ(mExpected72Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(mExpected72Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.name = "24Hz Heuristic";
+ lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz ExplicitDefault";
+ EXPECT_EQ(mExpected72Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz ExplicitDefault";
+ EXPECT_EQ(mExpected72Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.name = "90Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60Device,
@@ -819,6 +959,24 @@
}
}
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
+ RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = Fps(fps);
+ const auto& refreshRate =
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+ EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+ }
+}
+
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -1732,10 +1890,10 @@
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
+ RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
- /*currentConfigId=*/HWC_CONFIG_ID_60,
- /*enableFrameRateOverride=*/true);
+ /*currentConfigId=*/HWC_CONFIG_ID_60, config);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
LayerRequirement{.weight = 0.5f}};
@@ -1846,10 +2004,10 @@
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) {
+ RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_120Device,
- /*currentConfigId=*/HWC_CONFIG_ID_60,
- /*enableFrameRateOverride=*/true);
+ /*currentConfigId=*/HWC_CONFIG_ID_60, config);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
LayerRequirement{.weight = 0.5f}};
@@ -1908,6 +2066,35 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
+TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
+ using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+ // Tests with 120Hz
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_120);
+ // SetPolicy(0, 60), current 60Hz => TurnOn.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(0), Fps(60)}}),
+ 0);
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(60, 60), current 60Hz => TurnOff.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+ 0);
+ EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(60, 120), current 60Hz => TurnOn.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(120)}}),
+ 0);
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(120, 120), current 120Hz => TurnOff.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_120, {Fps(120), Fps(120)}}),
+ 0);
+ EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+}
+
TEST_F(RefreshRateConfigsTest, getFrameRateDivider) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
@@ -1950,10 +2137,10 @@
}
TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
+ RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
- HWC_CONFIG_ID_120,
- /*enableFrameRateOverride=*/true);
+ HWC_CONFIG_ID_120, config);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
layers[0].name = "Test layer";
@@ -1995,10 +2182,10 @@
}
TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) {
+ RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
- HWC_CONFIG_ID_120,
- /*enableFrameRateOverride=*/true);
+ HWC_CONFIG_ID_120, config);
auto layers = std::vector<LayerRequirement>{
LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
@@ -2035,10 +2222,10 @@
}
TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) {
+ RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
- HWC_CONFIG_ID_120,
- /*enableFrameRateOverride=*/true);
+ HWC_CONFIG_ID_120, config);
auto layers = std::vector<LayerRequirement>{
LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index d7d7ea7..e4f7469 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -27,6 +27,7 @@
#include "TunnelModeEnabledReporter.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
namespace android {
@@ -71,15 +72,20 @@
sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
new TestableTunnelModeEnabledListener();
sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
- new TunnelModeEnabledReporter(*(mFlinger.flinger()));
+ new TunnelModeEnabledReporter();
+
+ mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
};
TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ mFlinger.mutableEventQueue().reset(mMessageQueue);
setupScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter;
mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false);
}
@@ -156,16 +162,18 @@
sp<NativeHandle> stream =
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
- mFlinger.setLayerSidebandStream(layer, stream);
+ layer->setSidebandStream(stream);
mFlinger.mutableCurrentState().layersSortedByZ.add(layer);
mTunnelModeEnabledReporter->updateTunnelModeStatus();
mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled);
mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
-
mFlinger.mutableCurrentState().layersSortedByZ.remove(layer);
+ layer = nullptr;
+
mTunnelModeEnabledReporter->updateTunnelModeStatus();
mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
+
EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled);
}
@@ -178,7 +186,7 @@
sp<NativeHandle> stream =
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
- mFlinger.setLayerSidebandStream(layerWithSidebandStream, stream);
+ layerWithSidebandStream->setSidebandStream(stream);
mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer);
mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream);
@@ -186,6 +194,7 @@
EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled);
mFlinger.mutableCurrentState().layersSortedByZ.remove(layerWithSidebandStream);
+ layerWithSidebandStream = nullptr;
mTunnelModeEnabledReporter->updateTunnelModeStatus();
EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled);
}
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index f15a963..a375808 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -135,6 +135,18 @@
if (mInfoCache.mSupportedBraking.isFailed()) {
mInfoCache.mSupportedBraking = getSupportedBrakingInternal();
}
+ if (mInfoCache.mPrimitiveDelayMax.isFailed()) {
+ mInfoCache.mPrimitiveDelayMax = getPrimitiveDelayMaxInternal();
+ }
+ if (mInfoCache.mPwlePrimitiveDurationMax.isFailed()) {
+ mInfoCache.mPwlePrimitiveDurationMax = getPrimitiveDurationMaxInternal();
+ }
+ if (mInfoCache.mCompositionSizeMax.isFailed()) {
+ mInfoCache.mCompositionSizeMax = getCompositionSizeMaxInternal();
+ }
+ if (mInfoCache.mPwleSizeMax.isFailed()) {
+ mInfoCache.mPwleSizeMax = getPwleSizeMaxInternal();
+ }
if (mInfoCache.mMinFrequency.isFailed()) {
mInfoCache.mMinFrequency = getMinFrequencyInternal();
}
@@ -209,6 +221,26 @@
return HalResult<std::vector<milliseconds>>::unsupported();
}
+HalResult<milliseconds> HalWrapper::getPrimitiveDelayMaxInternal() {
+ ALOGV("Skipped getPrimitiveDelayMaxInternal because it's not available in Vibrator HAL");
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<milliseconds> HalWrapper::getPrimitiveDurationMaxInternal() {
+ ALOGV("Skipped getPrimitiveDurationMaxInternal because it's not available in Vibrator HAL");
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<int32_t> HalWrapper::getCompositionSizeMaxInternal() {
+ ALOGV("Skipped getCompositionSizeMaxInternal because it's not available in Vibrator HAL");
+ return HalResult<int32_t>::unsupported();
+}
+
+HalResult<int32_t> HalWrapper::getPwleSizeMaxInternal() {
+ ALOGV("Skipped getPwleSizeMaxInternal because it's not available in Vibrator HAL");
+ return HalResult<int32_t>::unsupported();
+}
+
HalResult<float> HalWrapper::getMinFrequencyInternal() {
ALOGV("Skipped getMinFrequency because it's not available in Vibrator HAL");
return HalResult<float>::unsupported();
@@ -383,6 +415,30 @@
return HalResult<std::vector<milliseconds>>::ok(durations);
}
+HalResult<milliseconds> AidlHalWrapper::getPrimitiveDelayMaxInternal() {
+ int32_t delay = 0;
+ auto result = getHal()->getCompositionDelayMax(&delay);
+ return HalResult<milliseconds>::fromStatus(result, milliseconds(delay));
+}
+
+HalResult<milliseconds> AidlHalWrapper::getPrimitiveDurationMaxInternal() {
+ int32_t delay = 0;
+ auto result = getHal()->getPwlePrimitiveDurationMax(&delay);
+ return HalResult<milliseconds>::fromStatus(result, milliseconds(delay));
+}
+
+HalResult<int32_t> AidlHalWrapper::getCompositionSizeMaxInternal() {
+ int32_t size = 0;
+ auto result = getHal()->getCompositionSizeMax(&size);
+ return HalResult<int32_t>::fromStatus(result, size);
+}
+
+HalResult<int32_t> AidlHalWrapper::getPwleSizeMaxInternal() {
+ int32_t size = 0;
+ auto result = getHal()->getPwleCompositionSizeMax(&size);
+ return HalResult<int32_t>::fromStatus(result, size);
+}
+
HalResult<float> AidlHalWrapper::getMinFrequencyInternal() {
float minFrequency = 0;
auto result = getHal()->getFrequencyMinimum(&minFrequency);
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 87bc34e..68d6647 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -182,6 +182,10 @@
const HalResult<std::vector<hardware::vibrator::Braking>> supportedBraking;
const HalResult<std::vector<hardware::vibrator::CompositePrimitive>> supportedPrimitives;
const HalResult<std::vector<std::chrono::milliseconds>> primitiveDurations;
+ const HalResult<std::chrono::milliseconds> primitiveDelayMax;
+ const HalResult<std::chrono::milliseconds> pwlePrimitiveDurationMax;
+ const HalResult<int32_t> compositionSizeMax;
+ const HalResult<int32_t> pwleSizeMax;
const HalResult<float> minFrequency;
const HalResult<float> resonantFrequency;
const HalResult<float> frequencyResolution;
@@ -194,6 +198,10 @@
supportedBraking.checkAndLogFailure("getSupportedBraking") ||
supportedPrimitives.checkAndLogFailure("getSupportedPrimitives") ||
primitiveDurations.checkAndLogFailure("getPrimitiveDuration") ||
+ primitiveDelayMax.checkAndLogFailure("getPrimitiveDelayMax") ||
+ pwlePrimitiveDurationMax.checkAndLogFailure("getPwlePrimitiveDurationMax") ||
+ compositionSizeMax.checkAndLogFailure("getCompositionSizeMax") ||
+ pwleSizeMax.checkAndLogFailure("getPwleSizeMax") ||
minFrequency.checkAndLogFailure("getMinFrequency") ||
resonantFrequency.checkAndLogFailure("getResonantFrequency") ||
frequencyResolution.checkAndLogFailure("getFrequencyResolution") ||
@@ -205,9 +213,19 @@
class InfoCache {
public:
Info get() {
- return {mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mPrimitiveDurations, mMinFrequency,
- mResonantFrequency, mFrequencyResolution, mQFactor,
+ return {mCapabilities,
+ mSupportedEffects,
+ mSupportedBraking,
+ mSupportedPrimitives,
+ mPrimitiveDurations,
+ mPrimitiveDelayMax,
+ mPwlePrimitiveDurationMax,
+ mCompositionSizeMax,
+ mPwleSizeMax,
+ mMinFrequency,
+ mResonantFrequency,
+ mFrequencyResolution,
+ mQFactor,
mMaxAmplitudes};
}
@@ -222,6 +240,12 @@
HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::failed(MSG);
HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations =
HalResult<std::vector<std::chrono::milliseconds>>::failed(MSG);
+ HalResult<std::chrono::milliseconds> mPrimitiveDelayMax =
+ HalResult<std::chrono::milliseconds>::failed(MSG);
+ HalResult<std::chrono::milliseconds> mPwlePrimitiveDurationMax =
+ HalResult<std::chrono::milliseconds>::failed(MSG);
+ HalResult<int32_t> mCompositionSizeMax = HalResult<int>::failed(MSG);
+ HalResult<int32_t> mPwleSizeMax = HalResult<int>::failed(MSG);
HalResult<float> mMinFrequency = HalResult<float>::failed(MSG);
HalResult<float> mResonantFrequency = HalResult<float>::failed(MSG);
HalResult<float> mFrequencyResolution = HalResult<float>::failed(MSG);
@@ -285,6 +309,10 @@
getSupportedPrimitivesInternal();
virtual HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives);
+ virtual HalResult<std::chrono::milliseconds> getPrimitiveDelayMaxInternal();
+ virtual HalResult<std::chrono::milliseconds> getPrimitiveDurationMaxInternal();
+ virtual HalResult<int32_t> getCompositionSizeMaxInternal();
+ virtual HalResult<int32_t> getPwleSizeMaxInternal();
virtual HalResult<float> getMinFrequencyInternal();
virtual HalResult<float> getResonantFrequencyInternal();
virtual HalResult<float> getFrequencyResolutionInternal();
@@ -347,6 +375,10 @@
HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives)
override final;
+ HalResult<std::chrono::milliseconds> getPrimitiveDelayMaxInternal() override final;
+ HalResult<std::chrono::milliseconds> getPrimitiveDurationMaxInternal() override final;
+ HalResult<int32_t> getCompositionSizeMaxInternal() override final;
+ HalResult<int32_t> getPwleSizeMaxInternal() override final;
HalResult<float> getMinFrequencyInternal() override final;
HalResult<float> getResonantFrequencyInternal() override final;
HalResult<float> getFrequencyResolutionInternal() override final;
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 7813303..03c9e77 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -301,6 +301,10 @@
constexpr float F0 = 123.f;
constexpr float F_RESOLUTION = 0.5f;
constexpr float Q_FACTOR = 123.f;
+ constexpr int32_t COMPOSITION_SIZE_MAX = 10;
+ constexpr int32_t PWLE_SIZE_MAX = 20;
+ constexpr int32_t PRIMITIVE_DELAY_MAX = 100;
+ constexpr int32_t PWLE_DURATION_MAX = 200;
std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
std::vector<Braking> supportedBraking = {Braking::CLAB};
@@ -331,6 +335,22 @@
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
.Times(Exactly(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(10), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getCompositionSizeMax(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getCompositionDelayMax(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPwlePrimitiveDurationMax(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPwleCompositionSizeMax(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(Status())));
EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
.Times(Exactly(2))
.WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
@@ -358,6 +378,10 @@
ASSERT_TRUE(failed.supportedBraking.isFailed());
ASSERT_TRUE(failed.supportedPrimitives.isFailed());
ASSERT_TRUE(failed.primitiveDurations.isFailed());
+ ASSERT_TRUE(failed.primitiveDelayMax.isFailed());
+ ASSERT_TRUE(failed.pwlePrimitiveDurationMax.isFailed());
+ ASSERT_TRUE(failed.compositionSizeMax.isFailed());
+ ASSERT_TRUE(failed.pwleSizeMax.isFailed());
ASSERT_TRUE(failed.minFrequency.isFailed());
ASSERT_TRUE(failed.resonantFrequency.isFailed());
ASSERT_TRUE(failed.frequencyResolution.isFailed());
@@ -370,6 +394,11 @@
ASSERT_EQ(supportedBraking, successful.supportedBraking.value());
ASSERT_EQ(supportedPrimitives, successful.supportedPrimitives.value());
ASSERT_EQ(primitiveDurations, successful.primitiveDurations.value());
+ ASSERT_EQ(std::chrono::milliseconds(PRIMITIVE_DELAY_MAX), successful.primitiveDelayMax.value());
+ ASSERT_EQ(std::chrono::milliseconds(PWLE_DURATION_MAX),
+ successful.pwlePrimitiveDurationMax.value());
+ ASSERT_EQ(COMPOSITION_SIZE_MAX, successful.compositionSizeMax.value());
+ ASSERT_EQ(PWLE_SIZE_MAX, successful.pwleSizeMax.value());
ASSERT_EQ(F_MIN, successful.minFrequency.value());
ASSERT_EQ(F0, successful.resonantFrequency.value());
ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value());
@@ -380,6 +409,10 @@
TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
constexpr float F_MIN = 100.f;
constexpr float F0 = 123.f;
+ constexpr int32_t COMPOSITION_SIZE_MAX = 10;
+ constexpr int32_t PWLE_SIZE_MAX = 20;
+ constexpr int32_t PRIMITIVE_DELAY_MAX = 100;
+ constexpr int32_t PWLE_DURATION_MAX = 200;
std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
@@ -395,6 +428,18 @@
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+ EXPECT_CALL(*mMockHal.get(), getCompositionSizeMax(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getCompositionDelayMax(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPwlePrimitiveDurationMax(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), getPwleCompositionSizeMax(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(Status())));
EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
.Times(Exactly(1))
.WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
@@ -426,6 +471,10 @@
ASSERT_TRUE(info.supportedBraking.isUnsupported());
ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+ ASSERT_EQ(std::chrono::milliseconds(PRIMITIVE_DELAY_MAX), info.primitiveDelayMax.value());
+ ASSERT_EQ(std::chrono::milliseconds(PWLE_DURATION_MAX), info.pwlePrimitiveDurationMax.value());
+ ASSERT_EQ(COMPOSITION_SIZE_MAX, info.compositionSizeMax.value());
+ ASSERT_EQ(PWLE_SIZE_MAX, info.pwleSizeMax.value());
ASSERT_EQ(F_MIN, info.minFrequency.value());
ASSERT_EQ(F0, info.resonantFrequency.value());
ASSERT_TRUE(info.frequencyResolution.isUnsupported());
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index 96b2582..0c27fc7 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -206,6 +206,10 @@
ASSERT_TRUE(info.supportedBraking.isUnsupported());
ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+ ASSERT_TRUE(info.primitiveDelayMax.isUnsupported());
+ ASSERT_TRUE(info.pwlePrimitiveDurationMax.isUnsupported());
+ ASSERT_TRUE(info.compositionSizeMax.isUnsupported());
+ ASSERT_TRUE(info.pwleSizeMax.isUnsupported());
ASSERT_TRUE(info.minFrequency.isUnsupported());
ASSERT_TRUE(info.resonantFrequency.isUnsupported());
ASSERT_TRUE(info.frequencyResolution.isUnsupported());
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 8d6681c..2715587 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -537,6 +537,30 @@
}
}
+int get_min_buffer_count(ANativeWindow* window,
+ uint32_t* out_min_buffer_count) {
+ constexpr int kExtraBuffers = 2;
+
+ int err;
+ int min_undequeued_buffers;
+ err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &min_undequeued_buffers);
+ if (err != android::OK || min_undequeued_buffers < 0) {
+ ALOGE(
+ "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
+ "value=%d",
+ strerror(-err), err, min_undequeued_buffers);
+ if (err == android::OK) {
+ err = android::UNKNOWN_ERROR;
+ }
+ return err;
+ }
+
+ *out_min_buffer_count =
+ static_cast<uint32_t>(min_undequeued_buffers + kExtraBuffers);
+ return android::OK;
+}
+
} // anonymous namespace
VKAPI_ATTR
@@ -848,15 +872,13 @@
int err;
int query_value;
+ uint32_t min_buffer_count;
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
- err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
- if (err != android::OK || query_value < 0) {
- ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) value=%d",
- strerror(-err), err, query_value);
+ err = get_min_buffer_count(window, &min_buffer_count);
+ if (err != android::OK) {
return VK_ERROR_SURFACE_LOST_KHR;
}
- uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
if (err != android::OK || query_value < 0) {
@@ -867,7 +889,7 @@
uint32_t max_buffer_count = static_cast<uint32_t>(query_value);
std::vector<VkPresentModeKHR> present_modes;
- if (min_undequeued_buffers + 1 < max_buffer_count)
+ if (min_buffer_count < max_buffer_count)
present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
@@ -1188,19 +1210,14 @@
}
}
- int query_value;
- err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &query_value);
- if (err != android::OK || query_value < 0) {
- ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
- query_value);
+ uint32_t min_buffer_count;
+ err = get_min_buffer_count(window, &min_buffer_count);
+ if (err != android::OK) {
return VK_ERROR_SURFACE_LOST_KHR;
}
- uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
+
uint32_t num_images =
- (swap_interval ? create_info->minImageCount
- : std::max(3u, create_info->minImageCount)) -
- 1 + min_undequeued_buffers;
+ std::max(min_buffer_count, create_info->minImageCount);
// Lower layer insists that we have at least two buffers. This is wasteful
// and we'd like to relax it in the shared case, but not all the pieces are