Skip to content

Commit 9d04fe2

Browse files
Sungtak LeeGerrit Code Review
authored andcommitted
Merge "Add IProducerListener::onBufferAttached" into main
2 parents a0bbe3d + 6d223cb commit 9d04fe2

7 files changed

Lines changed: 243 additions & 65 deletions

File tree

libs/gui/BufferQueueConsumer.cpp

Lines changed: 82 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242

4343
#include <system/window.h>
4444

45+
#include <com_android_graphics_libgui_flags.h>
46+
4547
namespace android {
4648

4749
// Macros for include BufferQueueCore information in log messages
@@ -370,79 +372,94 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot,
370372
return BAD_VALUE;
371373
}
372374

373-
std::lock_guard<std::mutex> lock(mCore->mMutex);
375+
sp<IProducerListener> listener;
376+
{
377+
std::lock_guard<std::mutex> lock(mCore->mMutex);
374378

375-
if (mCore->mSharedBufferMode) {
376-
BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode");
377-
return BAD_VALUE;
378-
}
379+
if (mCore->mSharedBufferMode) {
380+
BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode");
381+
return BAD_VALUE;
382+
}
379383

380-
// Make sure we don't have too many acquired buffers
381-
int numAcquiredBuffers = 0;
382-
for (int s : mCore->mActiveBuffers) {
383-
if (mSlots[s].mBufferState.isAcquired()) {
384-
++numAcquiredBuffers;
384+
// Make sure we don't have too many acquired buffers
385+
int numAcquiredBuffers = 0;
386+
for (int s : mCore->mActiveBuffers) {
387+
if (mSlots[s].mBufferState.isAcquired()) {
388+
++numAcquiredBuffers;
389+
}
385390
}
386-
}
387391

388-
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
389-
BQ_LOGE("attachBuffer: max acquired buffer count reached: %d "
390-
"(max %d)", numAcquiredBuffers,
391-
mCore->mMaxAcquiredBufferCount);
392-
return INVALID_OPERATION;
393-
}
392+
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
393+
BQ_LOGE("attachBuffer: max acquired buffer count reached: %d "
394+
"(max %d)", numAcquiredBuffers,
395+
mCore->mMaxAcquiredBufferCount);
396+
return INVALID_OPERATION;
397+
}
394398

395-
if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
396-
BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
397-
"[queue %u]", buffer->getGenerationNumber(),
398-
mCore->mGenerationNumber);
399-
return BAD_VALUE;
400-
}
399+
if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
400+
BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
401+
"[queue %u]", buffer->getGenerationNumber(),
402+
mCore->mGenerationNumber);
403+
return BAD_VALUE;
404+
}
401405

402-
// Find a free slot to put the buffer into
403-
int found = BufferQueueCore::INVALID_BUFFER_SLOT;
404-
if (!mCore->mFreeSlots.empty()) {
405-
auto slot = mCore->mFreeSlots.begin();
406-
found = *slot;
407-
mCore->mFreeSlots.erase(slot);
408-
} else if (!mCore->mFreeBuffers.empty()) {
409-
found = mCore->mFreeBuffers.front();
410-
mCore->mFreeBuffers.remove(found);
411-
}
412-
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
413-
BQ_LOGE("attachBuffer: could not find free buffer slot");
414-
return NO_MEMORY;
406+
// Find a free slot to put the buffer into
407+
int found = BufferQueueCore::INVALID_BUFFER_SLOT;
408+
if (!mCore->mFreeSlots.empty()) {
409+
auto slot = mCore->mFreeSlots.begin();
410+
found = *slot;
411+
mCore->mFreeSlots.erase(slot);
412+
} else if (!mCore->mFreeBuffers.empty()) {
413+
found = mCore->mFreeBuffers.front();
414+
mCore->mFreeBuffers.remove(found);
415+
}
416+
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
417+
BQ_LOGE("attachBuffer: could not find free buffer slot");
418+
return NO_MEMORY;
419+
}
420+
421+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
422+
if (mCore->mBufferAttachedCbEnabled) {
423+
listener = mCore->mConnectedProducerListener;
424+
}
425+
#endif
426+
427+
mCore->mActiveBuffers.insert(found);
428+
*outSlot = found;
429+
ATRACE_BUFFER_INDEX(*outSlot);
430+
BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
431+
432+
mSlots[*outSlot].mGraphicBuffer = buffer;
433+
mSlots[*outSlot].mBufferState.attachConsumer();
434+
mSlots[*outSlot].mNeedsReallocation = true;
435+
mSlots[*outSlot].mFence = Fence::NO_FENCE;
436+
mSlots[*outSlot].mFrameNumber = 0;
437+
438+
// mAcquireCalled tells BufferQueue that it doesn't need to send a valid
439+
// GraphicBuffer pointer on the next acquireBuffer call, which decreases
440+
// Binder traffic by not un/flattening the GraphicBuffer. However, it
441+
// requires that the consumer maintain a cached copy of the slot <--> buffer
442+
// mappings, which is why the consumer doesn't need the valid pointer on
443+
// acquire.
444+
//
445+
// The StreamSplitter is one of the primary users of the attach/detach
446+
// logic, and while it is running, all buffers it acquires are immediately
447+
// detached, and all buffers it eventually releases are ones that were
448+
// attached (as opposed to having been obtained from acquireBuffer), so it
449+
// doesn't make sense to maintain the slot/buffer mappings, which would
450+
// become invalid for every buffer during detach/attach. By setting this to
451+
// false, the valid GraphicBuffer pointer will always be sent with acquire
452+
// for attached buffers.
453+
mSlots[*outSlot].mAcquireCalled = false;
454+
455+
VALIDATE_CONSISTENCY();
415456
}
416457

417-
mCore->mActiveBuffers.insert(found);
418-
*outSlot = found;
419-
ATRACE_BUFFER_INDEX(*outSlot);
420-
BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
421-
422-
mSlots[*outSlot].mGraphicBuffer = buffer;
423-
mSlots[*outSlot].mBufferState.attachConsumer();
424-
mSlots[*outSlot].mNeedsReallocation = true;
425-
mSlots[*outSlot].mFence = Fence::NO_FENCE;
426-
mSlots[*outSlot].mFrameNumber = 0;
427-
428-
// mAcquireCalled tells BufferQueue that it doesn't need to send a valid
429-
// GraphicBuffer pointer on the next acquireBuffer call, which decreases
430-
// Binder traffic by not un/flattening the GraphicBuffer. However, it
431-
// requires that the consumer maintain a cached copy of the slot <--> buffer
432-
// mappings, which is why the consumer doesn't need the valid pointer on
433-
// acquire.
434-
//
435-
// The StreamSplitter is one of the primary users of the attach/detach
436-
// logic, and while it is running, all buffers it acquires are immediately
437-
// detached, and all buffers it eventually releases are ones that were
438-
// attached (as opposed to having been obtained from acquireBuffer), so it
439-
// doesn't make sense to maintain the slot/buffer mappings, which would
440-
// become invalid for every buffer during detach/attach. By setting this to
441-
// false, the valid GraphicBuffer pointer will always be sent with acquire
442-
// for attached buffers.
443-
mSlots[*outSlot].mAcquireCalled = false;
444-
445-
VALIDATE_CONSISTENCY();
458+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
459+
if (listener != nullptr) {
460+
listener->onBufferAttached();
461+
}
462+
#endif
446463

447464
return NO_ERROR;
448465
}

libs/gui/BufferQueueCore.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ BufferQueueCore::BufferQueueCore()
9696
mLinkedToDeath(),
9797
mConnectedProducerListener(),
9898
mBufferReleasedCbEnabled(false),
99+
mBufferAttachedCbEnabled(false),
99100
mSlots(),
100101
mQueue(),
101102
mFreeSlots(),

libs/gui/BufferQueueProducer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,9 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
13241324
#endif
13251325
mCore->mConnectedProducerListener = listener;
13261326
mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
1327+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
1328+
mCore->mBufferAttachedCbEnabled = listener->needsAttachNotify();
1329+
#endif
13271330
}
13281331
break;
13291332
default:

libs/gui/IProducerListener.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ enum {
2525
ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
2626
NEEDS_RELEASE_NOTIFY,
2727
ON_BUFFERS_DISCARDED,
28+
ON_BUFFER_DETACHED,
29+
ON_BUFFER_ATTACHED,
30+
NEEDS_ATTACH_NOTIFY,
2831
};
2932

3033
class BpProducerListener : public BpInterface<IProducerListener>
@@ -64,6 +67,38 @@ class BpProducerListener : public BpInterface<IProducerListener>
6467
data.writeInt32Vector(discardedSlots);
6568
remote()->transact(ON_BUFFERS_DISCARDED, data, &reply, IBinder::FLAG_ONEWAY);
6669
}
70+
71+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
72+
virtual void onBufferDetached(int slot) {
73+
Parcel data, reply;
74+
data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
75+
data.writeInt32(slot);
76+
remote()->transact(ON_BUFFER_DETACHED, data, &reply, IBinder::FLAG_ONEWAY);
77+
}
78+
79+
virtual void onBufferAttached() {
80+
Parcel data, reply;
81+
data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
82+
remote()->transact(ON_BUFFER_ATTACHED, data, &reply, IBinder::FLAG_ONEWAY);
83+
}
84+
85+
virtual bool needsAttachNotify() {
86+
bool result;
87+
Parcel data, reply;
88+
data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
89+
status_t err = remote()->transact(NEEDS_ATTACH_NOTIFY, data, &reply);
90+
if (err != NO_ERROR) {
91+
ALOGE("IProducerListener: binder call \'needsAttachNotify\' failed");
92+
return true;
93+
}
94+
err = reply.readBool(&result);
95+
if (err != NO_ERROR) {
96+
ALOGE("IProducerListener: malformed binder reply");
97+
return true;
98+
}
99+
return result;
100+
}
101+
#endif
67102
};
68103

69104
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -115,6 +150,27 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data,
115150
onBuffersDiscarded(discardedSlots);
116151
return NO_ERROR;
117152
}
153+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
154+
case ON_BUFFER_DETACHED: {
155+
CHECK_INTERFACE(IProducerListener, data, reply);
156+
int slot;
157+
status_t result = data.readInt32(&slot);
158+
if (result != NO_ERROR) {
159+
ALOGE("ON_BUFFER_DETACHED failed to read slot: %d", result);
160+
return result;
161+
}
162+
onBufferDetached(slot);
163+
return NO_ERROR;
164+
}
165+
case ON_BUFFER_ATTACHED:
166+
CHECK_INTERFACE(IProducerListener, data, reply);
167+
onBufferAttached();
168+
return NO_ERROR;
169+
case NEEDS_ATTACH_NOTIFY:
170+
CHECK_INTERFACE(IProducerListener, data, reply);
171+
reply->writeBool(needsAttachNotify());
172+
return NO_ERROR;
173+
#endif
118174
}
119175
return BBinder::onTransact(code, data, reply, flags);
120176
}

libs/gui/include/gui/BufferQueueCore.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ class BufferQueueCore : public virtual RefBase {
189189
// callback is registered by the listener. When set to false,
190190
// mConnectedProducerListener will not trigger onBufferReleased() callback.
191191
bool mBufferReleasedCbEnabled;
192+
// mBufferAttachedCbEnabled is used to indicate whether onBufferAttached()
193+
// callback is registered by the listener. When set to false,
194+
// mConnectedProducerListener will not trigger onBufferAttached() callback.
195+
bool mBufferAttachedCbEnabled;
192196

193197
// mSlots is an array of buffer slots that must be mirrored on the producer
194198
// side. This allows buffer ownership to be transferred between the producer

libs/gui/include/gui/IProducerListener.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <hidl/HybridInterface.h>
2626
#include <utils/RefBase.h>
2727

28+
#include <com_android_graphics_libgui_flags.h>
29+
2830
namespace android {
2931

3032
// ProducerListener is the interface through which the BufferQueue notifies the
@@ -55,6 +57,16 @@ class ProducerListener : public virtual RefBase
5557
// This is called without any lock held and can be called concurrently by
5658
// multiple threads.
5759
virtual void onBufferDetached(int /*slot*/) {} // Asynchronous
60+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
61+
// onBufferAttached is called from IGraphicBufferConsumer::attachBuffer to
62+
// notify the producer that a buffer is attached.
63+
//
64+
// This is called without any lock held and can be called concurrently by
65+
// multiple threads. This callback is enabled only when needsAttachNotify()
66+
// returns {@code true}.
67+
virtual void onBufferAttached() {} // Asynchronous
68+
virtual bool needsAttachNotify() { return false; }
69+
#endif
5870
};
5971

6072
#ifndef NO_BINDER

libs/gui/tests/BufferQueue_test.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,91 @@ TEST_F(BufferQueueTest, TestConsumerDetachProducerListener) {
12571257
ASSERT_TRUE(result == WOULD_BLOCK || result == TIMED_OUT || result == INVALID_OPERATION);
12581258
}
12591259

1260+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
1261+
struct BufferAttachedListener : public BnProducerListener {
1262+
public:
1263+
BufferAttachedListener(bool enable) : mEnabled(enable), mAttached(0) {}
1264+
virtual ~BufferAttachedListener() = default;
1265+
1266+
virtual void onBufferReleased() {}
1267+
virtual bool needsReleaseNotify() { return true; }
1268+
virtual void onBufferAttached() {
1269+
++mAttached;
1270+
}
1271+
virtual bool needsAttachNotify() { return mEnabled; }
1272+
1273+
int getNumAttached() const { return mAttached; }
1274+
private:
1275+
const bool mEnabled;
1276+
int mAttached;
1277+
};
1278+
1279+
TEST_F(BufferQueueTest, TestConsumerAttachProducerListener) {
1280+
createBufferQueue();
1281+
sp<MockConsumer> mc1(new MockConsumer);
1282+
ASSERT_EQ(OK, mConsumer->consumerConnect(mc1, true));
1283+
IGraphicBufferProducer::QueueBufferOutput output;
1284+
// Do not enable attach callback.
1285+
sp<BufferAttachedListener> pl1(new BufferAttachedListener(false));
1286+
ASSERT_EQ(OK, mProducer->connect(pl1, NATIVE_WINDOW_API_CPU, true, &output));
1287+
ASSERT_EQ(OK, mProducer->setDequeueTimeout(0));
1288+
ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
1289+
1290+
sp<Fence> fence = Fence::NO_FENCE;
1291+
sp<GraphicBuffer> buffer = nullptr;
1292+
1293+
int slot;
1294+
status_t result = OK;
1295+
1296+
ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(1));
1297+
1298+
result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
1299+
GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
1300+
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
1301+
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
1302+
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
1303+
1304+
// Check # of attach is zero.
1305+
ASSERT_EQ(0, pl1->getNumAttached());
1306+
1307+
// Attach a buffer and check the callback was not called.
1308+
ASSERT_EQ(OK, mConsumer->attachBuffer(&slot, buffer));
1309+
ASSERT_EQ(0, pl1->getNumAttached());
1310+
1311+
mProducer = nullptr;
1312+
mConsumer = nullptr;
1313+
createBufferQueue();
1314+
1315+
sp<MockConsumer> mc2(new MockConsumer);
1316+
ASSERT_EQ(OK, mConsumer->consumerConnect(mc2, true));
1317+
// Enable attach callback.
1318+
sp<BufferAttachedListener> pl2(new BufferAttachedListener(true));
1319+
ASSERT_EQ(OK, mProducer->connect(pl2, NATIVE_WINDOW_API_CPU, true, &output));
1320+
ASSERT_EQ(OK, mProducer->setDequeueTimeout(0));
1321+
ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
1322+
1323+
fence = Fence::NO_FENCE;
1324+
buffer = nullptr;
1325+
1326+
result = OK;
1327+
1328+
ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(1));
1329+
1330+
result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
1331+
GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
1332+
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
1333+
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
1334+
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
1335+
1336+
// Check # of attach is zero.
1337+
ASSERT_EQ(0, pl2->getNumAttached());
1338+
1339+
// Attach a buffer and check the callback was called.
1340+
ASSERT_EQ(OK, mConsumer->attachBuffer(&slot, buffer));
1341+
ASSERT_EQ(1, pl2->getNumAttached());
1342+
}
1343+
#endif
1344+
12601345
TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
12611346
createBufferQueue();
12621347
sp<MockConsumer> mc(new MockConsumer);

0 commit comments

Comments
 (0)