Merge "DO NOT MERGE: Move blast sync handling to BBQ" into sc-v2-dev
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index e8e664b..94864e5 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -46,6 +46,8 @@
namespace android {
// Macros to include adapter info in log messages
+#define BQA_LOGD(x, ...) \
+ ALOGD("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
#define BQA_LOGV(x, ...) \
ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
// enable logs for a single layer
@@ -243,6 +245,67 @@
}
}
+static std::optional<SurfaceControlStats> findMatchingStat(
+ const std::vector<SurfaceControlStats>& stats, const sp<SurfaceControl>& sc) {
+ for (auto stat : stats) {
+ if (SurfaceControl::isSameSurface(sc, stat.surfaceControl)) {
+ return stat;
+ }
+ }
+ return std::nullopt;
+}
+
+static void transactionCommittedCallbackThunk(void* context, nsecs_t latchTime,
+ const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) {
+ if (context == nullptr) {
+ return;
+ }
+ sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
+ bq->transactionCommittedCallback(latchTime, presentFence, stats);
+}
+
+void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/,
+ const sp<Fence>& /*presentFence*/,
+ const std::vector<SurfaceControlStats>& stats) {
+ {
+ std::unique_lock _lock{mMutex};
+ ATRACE_CALL();
+ BQA_LOGV("transactionCommittedCallback");
+ if (!mSurfaceControlsWithPendingCallback.empty()) {
+ sp<SurfaceControl> pendingSC = mSurfaceControlsWithPendingCallback.front();
+ std::optional<SurfaceControlStats> stat = findMatchingStat(stats, pendingSC);
+ if (stat) {
+ uint64_t currFrameNumber = stat->frameEventStats.frameNumber;
+
+ // We need to check if we were waiting for a transaction callback in order to
+ // process any pending buffers and unblock. It's possible to get transaction
+ // callbacks for previous requests so we need to ensure the frame from this
+ // transaction callback matches the last acquired buffer. Since acquireNextBuffer
+ // will stop processing buffers when mWaitForTransactionCallback is set, we know
+ // that mLastAcquiredFrameNumber is the frame we're waiting on.
+ // We also want to check if mNextTransaction is null because it's possible another
+ // sync request came in while waiting, but it hasn't started processing yet. In that
+ // case, we don't actually want to flush the frames in between since they will get
+ // processed and merged with the sync transaction and released earlier than if they
+ // were sent to SF
+ if (mWaitForTransactionCallback && mNextTransaction == nullptr &&
+ currFrameNumber >= mLastAcquiredFrameNumber) {
+ mWaitForTransactionCallback = false;
+ flushShadowQueueLocked();
+ }
+ } else {
+ BQA_LOGE("Failed to find matching SurfaceControl in transaction callback");
+ }
+ } else {
+ BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
+ "empty.");
+ }
+
+ decStrong((void*)transactionCommittedCallbackThunk);
+ }
+}
+
static void transactionCallbackThunk(void* context, nsecs_t latchTime,
const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats) {
@@ -266,12 +329,9 @@
if (!mSurfaceControlsWithPendingCallback.empty()) {
sp<SurfaceControl> pendingSC = mSurfaceControlsWithPendingCallback.front();
mSurfaceControlsWithPendingCallback.pop();
- bool found = false;
- for (auto stat : stats) {
- if (!SurfaceControl::isSameSurface(pendingSC, stat.surfaceControl)) {
- continue;
- }
-
+ std::optional<SurfaceControlStats> statsOptional = findMatchingStat(stats, pendingSC);
+ if (statsOptional) {
+ SurfaceControlStats stat = *statsOptional;
mTransformHint = stat.transformHint;
mBufferItemConsumer->setTransformHint(mTransformHint);
BQA_LOGV("updated mTransformHint=%d", mTransformHint);
@@ -299,12 +359,7 @@
transactionCompleteCallback = std::move(mTransactionCompleteCallback);
mTransactionCompleteFrameNumber = 0;
}
-
- found = true;
- break;
- }
-
- if (!found) {
+ } else {
BQA_LOGE("Failed to find matching SurfaceControl in transaction callback");
}
} else {
@@ -336,6 +391,20 @@
}
}
+void BLASTBufferQueue::flushShadowQueueLocked() {
+ BQA_LOGV("flushShadowQueueLocked");
+ int numFramesToFlush = mNumFrameAvailable;
+ while (numFramesToFlush > 0) {
+ acquireNextBufferLocked(std::nullopt);
+ numFramesToFlush--;
+ }
+}
+
+void BLASTBufferQueue::flushShadowQueue() {
+ std::unique_lock _lock{mMutex};
+ flushShadowQueueLocked();
+}
+
void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id,
const sp<Fence>& releaseFence, uint32_t transformHint,
uint32_t currentMaxAcquiredBufferCount) {
@@ -377,7 +446,11 @@
BQA_LOGV("released %s", id.to_string().c_str());
mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
mSubmitted.erase(it);
- processNextBufferLocked(false /* useNextTransaction */);
+ // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
+ // onFrameAvailable handle processing them since it will merge with the nextTransaction.
+ if (!mWaitForTransactionCallback) {
+ acquireNextBufferLocked(std::nullopt);
+ }
}
ATRACE_INT("PendingRelease", mPendingRelease.size());
@@ -386,13 +459,15 @@
mCallbackCV.notify_all();
}
-void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
+void BLASTBufferQueue::acquireNextBufferLocked(
+ const std::optional<SurfaceComposerClient::Transaction*> transaction) {
ATRACE_CALL();
// If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
// include the extra buffer when checking if we can acquire the next buffer.
- const bool includeExtraAcquire = !useNextTransaction;
- if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) {
- mCallbackCV.notify_all();
+ const bool includeExtraAcquire = !transaction;
+ const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire);
+ if (mNumFrameAvailable == 0 || maxAcquired) {
+ BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired));
return;
}
@@ -404,9 +479,8 @@
SurfaceComposerClient::Transaction localTransaction;
bool applyTransaction = true;
SurfaceComposerClient::Transaction* t = &localTransaction;
- if (mNextTransaction != nullptr && useNextTransaction) {
- t = mNextTransaction;
- mNextTransaction = nullptr;
+ if (transaction) {
+ t = *transaction;
applyTransaction = false;
}
@@ -436,7 +510,7 @@
mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height,
buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
- processNextBufferLocked(useNextTransaction);
+ acquireNextBufferLocked(transaction);
return;
}
@@ -455,6 +529,7 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
+ incStrong((void*)transactionCommittedCallbackThunk);
const bool sizeHasChanged = mRequestedSize != mSize;
mSize = mRequestedSize;
@@ -475,6 +550,7 @@
t->setAcquireFence(mSurfaceControl,
bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
+ t->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast<void*>(this));
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
if (updateDestinationFrame) {
@@ -527,7 +603,7 @@
t->setApplyToken(mApplyToken).apply();
}
- BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
+ BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
" applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
" graphicBufferId=%" PRIu64 "%s transform=%d",
mSize.width, mSize.height, bufferItem.mFrameNumber, boolToString(applyTransaction),
@@ -543,17 +619,44 @@
return item.mCrop;
}
+void BLASTBufferQueue::acquireAndReleaseBuffer() {
+ BufferItem bufferItem;
+ mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
+ mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ mNumFrameAvailable--;
+}
+
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
const bool nextTransactionSet = mNextTransaction != nullptr;
+ BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet));
if (nextTransactionSet) {
- while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGV("waiting in onFrameAvailable...");
+ if (mWaitForTransactionCallback) {
+ // We are waiting on a previous sync's transaction callback so allow another sync
+ // transaction to proceed.
+ //
+ // We need to first flush out the transactions that were in between the two syncs.
+ // We do this by merging them into mNextTransaction so any buffer merging will get
+ // a release callback invoked. The release callback will be async so we need to wait
+ // on max acquired to make sure we have the capacity to acquire another buffer.
+ if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGD("waiting to flush shadow queue...");
+ mCallbackCV.wait(_lock);
+ }
+ while (mNumFrameAvailable > 0) {
+ // flush out the shadow queue
+ acquireAndReleaseBuffer();
+ }
+ }
+
+ while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGD("waiting for free buffer.");
mCallbackCV.wait(_lock);
}
}
+
// add to shadow queue
mNumFrameAvailable++;
ATRACE_INT(mQueuedBufferTrace.c_str(),
@@ -561,7 +664,14 @@
BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
boolToString(nextTransactionSet));
- processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
+
+ if (nextTransactionSet) {
+ acquireNextBufferLocked(std::move(mNextTransaction));
+ mNextTransaction = nullptr;
+ mWaitForTransactionCallback = true;
+ } else if (!mWaitForTransactionCallback) {
+ acquireNextBufferLocked(std::nullopt);
+ }
}
void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 6c5b2aa..615f284 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -88,6 +88,8 @@
void onFrameDequeued(const uint64_t) override;
void onFrameCancelled(const uint64_t) override;
+ void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats);
void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
@@ -99,7 +101,6 @@
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format,
SurfaceComposerClient::Transaction* outTransaction = nullptr);
- void flushShadowQueue() {}
status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
@@ -107,6 +108,7 @@
void setSidebandStream(const sp<NativeHandle>& stream);
uint32_t getLastTransformHint() const;
+ void flushShadowQueue();
virtual ~BLASTBufferQueue();
@@ -119,13 +121,17 @@
void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer);
- void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
+ void acquireNextBufferLocked(
+ const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
// Return true if we need to reject the buffer based on the scaling mode and the buffer size.
bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
static PixelFormat convertBufferFormat(PixelFormat& format);
+ void flushShadowQueueLocked() REQUIRES(mMutex);
+ void acquireAndReleaseBuffer() REQUIRES(mMutex);
+
std::string mName;
// Represents the queued buffer count from buffer queue,
// pre-BLAST. This is mNumFrameAvailable (buffers that queued to blast) +
@@ -233,6 +239,9 @@
// Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a
// callback for them.
std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
+
+ uint32_t mCurrentMaxAcquiredBufferCount;
+ bool mWaitForTransactionCallback = false;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
similarity index 96%
rename from services/surfaceflinger/tests/utils/CallbackUtils.h
rename to libs/gui/include/gui/test/CallbackUtils.h
index f4a3425..6403208 100644
--- a/services/surfaceflinger/tests/utils/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -134,10 +134,8 @@
void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
nsecs_t latchTime) const {
- const auto&
- [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
- transformHint,
- frameEvents] = surfaceControlStats;
+ const auto& [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
+ transformHint, frameEvents] = surfaceControlStats;
ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
<< "bad acquire time";
@@ -199,7 +197,7 @@
std::this_thread::sleep_for(500ms);
std::lock_guard lock(mMutex);
- EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+ EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received";
mCallbackDataQueue = {};
}
@@ -209,5 +207,5 @@
std::condition_variable mConditionVariable;
std::queue<CallbackData> mCallbackDataQueue;
};
-}
+} // namespace
} // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 99777e9..fc75485 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -27,6 +27,7 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
+#include <gui/test/CallbackUtils.h>
#include <private/gui/ComposerService.h>
#include <ui/DisplayMode.h>
#include <ui/GraphicBuffer.h>
@@ -42,6 +43,29 @@
using Transaction = SurfaceComposerClient::Transaction;
using android::hardware::graphics::common::V1_2::BufferUsage;
+class CountProducerListener : public BnProducerListener {
+public:
+ void onBufferReleased() override {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mNumReleased++;
+ mReleaseCallback.notify_one();
+ }
+
+ void waitOnNumberReleased(int32_t expectedNumReleased) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (mNumReleased < expectedNumReleased) {
+ ASSERT_NE(mReleaseCallback.wait_for(lock, std::chrono::seconds(3)),
+ std::cv_status::timeout)
+ << "did not receive release";
+ }
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mReleaseCallback;
+ int32_t mNumReleased GUARDED_BY(mMutex) = 0;
+};
+
class BLASTBufferQueueHelper {
public:
BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
@@ -152,18 +176,19 @@
mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
}
- void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer) {
+ void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer,
+ int32_t maxBufferCount = 2) {
producer = adapter.getIGraphicBufferProducer();
- setUpProducer(producer);
+ setUpProducer(producer, maxBufferCount);
}
- void setUpProducer(sp<IGraphicBufferProducer>& igbProducer) {
+ void setUpProducer(sp<IGraphicBufferProducer>& igbProducer, int32_t maxBufferCount) {
ASSERT_NE(nullptr, igbProducer.get());
- ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
+ ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(maxBufferCount));
IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ mProducerListener = new CountProducerListener();
ASSERT_EQ(NO_ERROR,
- igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
- &qbOutput));
+ igbProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput));
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
}
@@ -287,6 +312,7 @@
DisplayCaptureArgs mCaptureArgs;
ScreenCaptureResults mCaptureResults;
+ sp<CountProducerListener> mProducerListener;
};
TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
@@ -749,6 +775,240 @@
{0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
}
+TEST_F(BLASTBufferQueueTest, SyncThenNoSync) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction next;
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ // queue non sync buffer, so this one should get blocked
+ // Add a present delay to allow the first screenshot to get taken.
+ nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+ queueBuffer(igbProducer, r, g, b, presentTimeDelay);
+
+ CallbackHelper transactionCallback;
+ next.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+
+ mProducerListener->waitOnNumberReleased(1);
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction mainTransaction;
+
+ Transaction next;
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ mainTransaction.merge(std::move(next));
+
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, r, g, b, 0);
+
+ mainTransaction.merge(std::move(next));
+ // Expect 1 buffer to be released even before sending to SurfaceFlinger
+ mProducerListener->waitOnNumberReleased(1);
+
+ CallbackHelper transactionCallback;
+ mainTransaction
+ .addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction mainTransaction;
+
+ Transaction next;
+ // queue a sync transaction
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ mainTransaction.merge(std::move(next));
+
+ // queue another buffer without setting next transaction
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+
+ // queue another sync transaction
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, r, g, b, 0);
+ // Expect 1 buffer to be released because the non sync transaction should merge
+ // with the sync
+ mProducerListener->waitOnNumberReleased(1);
+
+ mainTransaction.merge(std::move(next));
+ // Expect 2 buffers to be released due to merging the two syncs.
+ mProducerListener->waitOnNumberReleased(2);
+
+ CallbackHelper transactionCallback;
+ mainTransaction
+ .addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer, 3);
+
+ Transaction mainTransaction;
+
+ Transaction next;
+ // queue a sync transaction
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ mainTransaction.merge(std::move(next));
+
+ // queue a few buffers without setting next transaction
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+
+ // queue another sync transaction
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, r, g, b, 0);
+ // Expect 3 buffers to be released because the non sync transactions should merge
+ // with the sync
+ mProducerListener->waitOnNumberReleased(3);
+
+ mainTransaction.merge(std::move(next));
+ // Expect 4 buffers to be released due to merging the two syncs.
+ mProducerListener->waitOnNumberReleased(4);
+
+ CallbackHelper transactionCallback;
+ mainTransaction
+ .addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+// Tests BBQ with a sync transaction when the buffers acquired reaches max and the only way to
+// continue processing is for a release callback from SurfaceFlinger.
+// This is done by sending a buffer to SF so it can release the previous one and allow BBQ to
+// continue acquiring buffers.
+TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer, 4);
+
+ Transaction mainTransaction;
+
+ // Send a buffer to SF
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ Transaction next;
+ // queue a sync transaction
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ mainTransaction.merge(std::move(next));
+
+ // queue a few buffers without setting next transaction
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+
+ // apply the first synced buffer to ensure we have to wait on SF
+ mainTransaction.apply();
+
+ // queue another sync transaction
+ adapter.setNextTransaction(&next);
+ queueBuffer(igbProducer, r, g, b, 0);
+ // Expect 2 buffers to be released because the non sync transactions should merge
+ // with the sync
+ mProducerListener->waitOnNumberReleased(3);
+
+ mainTransaction.merge(std::move(next));
+
+ CallbackHelper transactionCallback;
+ mainTransaction
+ .addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
class TestProducerListener : public BnProducerListener {
public:
sp<IGraphicBufferProducer> mIgbp;
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 9fa3d4c..84fea6c 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -28,8 +28,8 @@
#include <limits>
+#include <gui/test/CallbackUtils.h>
#include "BufferGenerator.h"
-#include "utils/CallbackUtils.h"
#include "utils/ColorUtils.h"
#include "utils/TransactionUtils.h"
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 965aac3..7ff041e 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -18,8 +18,8 @@
#include <gui/DisplayEventReceiver.h>
+#include <gui/test/CallbackUtils.h>
#include "LayerTransactionTest.h"
-#include "utils/CallbackUtils.h"
using namespace std::chrono_literals;
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index c4d42fa..309ab2f 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
+#include <gui/test/CallbackUtils.h>
#include "LayerTransactionTest.h"
-#include "utils/CallbackUtils.h"
using namespace std::chrono_literals;
@@ -61,7 +61,7 @@
std::this_thread::sleep_for(300ms);
std::lock_guard lock(mMutex);
- EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+ EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received";
mCallbackDataQueue = {};
}