Migrate buffers during surface change
Migrate graphic buffers during surface change in order to avoid
BufferQueue handling complexity later on.
Test: Manually using Chrome and google photo app
Bug: 132302078
Bug: 130862880
Change-Id: Ifb348b5d6a8f5a89dcc10a9f0be075057a5d3a6d
diff --git a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
index 1786c69..50790bc 100644
--- a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
+++ b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
@@ -82,21 +82,14 @@
template <typename BlockProcessor>
void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
- BlockProcessor process,
- bool processInput, bool processOutput) {
+ BlockProcessor process) {
for (const std::unique_ptr<C2Work>& work : workList) {
if (!work) {
continue;
}
- if (processInput) {
- forEachBlock(work->input, process);
- }
- if (processOutput) {
- for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
- if (worklet) {
- forEachBlock(worklet->output,
- process);
- }
+ for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
+ if (worklet) {
+ forEachBlock(worklet->output, process);
}
}
}
@@ -109,8 +102,6 @@
new B2HGraphicBufferProducer(igbp);
}
-} // unnamed namespace
-
status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
const sp<IGraphicBufferProducer>& igbp,
uint32_t generation,
@@ -154,73 +145,221 @@
_C2BlockFactory::GetGraphicBlockPoolData(block),
generation, bqId, bqSlot);
}
+} // unnamed namespace
-bool holdBufferQueueBlock(const C2ConstGraphicBlock& block,
- const sp<IGraphicBufferProducer>& igbp,
- uint64_t bqId,
- uint32_t generation) {
- std::shared_ptr<_C2BlockPoolData> data =
- _C2BlockFactory::GetGraphicBlockPoolData(block);
- if (!data) {
- return false;
- }
+class OutputBufferQueue::Impl {
+ std::mutex mMutex;
+ sp<IGraphicBufferProducer> mIgbp;
+ uint32_t mGeneration;
+ uint64_t mBqId;
+ std::shared_ptr<int> mOwner;
+ // To migrate existing buffers
+ sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; // find a better way
+ std::weak_ptr<_C2BlockPoolData>
+ mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
- uint32_t oldGeneration;
- uint64_t oldId;
- int32_t oldSlot;
- // If the block is not bufferqueue-based, do nothing.
- if (!_C2BlockFactory::GetBufferQueueData(
- data, &oldGeneration, &oldId, &oldSlot) ||
- (oldId == 0)) {
- return false;
- }
+public:
+ Impl(): mGeneration(0), mBqId(0) {}
- // If the block's bqId is the same as the desired bqId, just hold.
- if ((oldId == bqId) && (oldGeneration == generation)) {
- LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
- << " bqId " << oldId
- << ", bqSlot " << oldSlot
- << ", generation " << generation
- << ".";
- _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp));
+ bool configure(const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId) {
+ size_t tryNum = 0;
+ size_t success = 0;
+ sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ std::weak_ptr<_C2BlockPoolData>
+ poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ {
+ std::scoped_lock<std::mutex> l(mMutex);
+ if (generation == mGeneration) {
+ return false;
+ }
+ mIgbp = igbp;
+ mGeneration = generation;
+ mBqId = bqId;
+ mOwner = std::make_shared<int>(0);
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+ if (mBqId == 0 || !mBuffers[i]) {
+ continue;
+ }
+ std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock();
+ if (!data ||
+ !_C2BlockFactory::BeginAttachBlockToBufferQueue(data)) {
+ continue;
+ }
+ ++tryNum;
+ int bqSlot;
+ mBuffers[i]->setGenerationNumber(generation);
+ status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]);
+ if (result != OK) {
+ continue;
+ }
+ bool attach =
+ _C2BlockFactory::EndAttachBlockToBufferQueue(
+ data, mOwner, getHgbp(mIgbp),
+ generation, bqId, bqSlot);
+ if (!attach) {
+ igbp->cancelBuffer(bqSlot, Fence::NO_FENCE);
+ continue;
+ }
+ buffers[bqSlot] = mBuffers[i];
+ poolDatas[bqSlot] = data;
+ ++success;
+ }
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+ mBuffers[i] = buffers[i];
+ mPoolDatas[i] = poolDatas[i];
+ }
+ }
+ ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum);
return true;
}
- // Otherwise, attach to the given igbp, which must not be null.
- if (!igbp) {
+ bool registerBuffer(const C2ConstGraphicBlock& block) {
+ std::shared_ptr<_C2BlockPoolData> data =
+ _C2BlockFactory::GetGraphicBlockPoolData(block);
+ if (!data) {
+ return false;
+ }
+ std::scoped_lock<std::mutex> l(mMutex);
+
+ if (!mIgbp) {
+ return false;
+ }
+
+ uint32_t oldGeneration;
+ uint64_t oldId;
+ int32_t oldSlot;
+ // If the block is not bufferqueue-based, do nothing.
+ if (!_C2BlockFactory::GetBufferQueueData(
+ data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) {
+ return false;
+ }
+ // If the block's bqId is the same as the desired bqId, just hold.
+ if ((oldId == mBqId) && (oldGeneration == mGeneration)) {
+ LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
+ << " bqId " << oldId
+ << ", bqSlot " << oldSlot
+ << ", generation " << mGeneration
+ << ".";
+ _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp));
+ mPoolDatas[oldSlot] = data;
+ mBuffers[oldSlot] = createGraphicBuffer(block);
+ mBuffers[oldSlot]->setGenerationNumber(mGeneration);
+ return true;
+ }
+ int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration;
+ LOG(WARNING) << "receiving stale buffer: generation "
+ << mGeneration << " , diff " << d << " : slot "
+ << oldSlot;
return false;
}
- int32_t bqSlot;
- status_t result = attachToBufferQueue(block, igbp, generation, &bqSlot);
+ status_t outputBuffer(
+ const C2ConstGraphicBlock& block,
+ const BnGraphicBufferProducer::QueueBufferInput& input,
+ BnGraphicBufferProducer::QueueBufferOutput* output) {
+ uint32_t generation;
+ uint64_t bqId;
+ int32_t bqSlot;
+ bool display = displayBufferQueueBlock(block);
+ if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
+ bqId == 0) {
+ // Block not from bufferqueue -- it must be attached before queuing.
- if (result != OK) {
- LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:"
- << " target bqId " << bqId
- << ", generation " << generation
- << ".";
- return false;
+ mMutex.lock();
+ sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+ uint32_t outputGeneration = mGeneration;
+ mMutex.unlock();
+
+ status_t status = attachToBufferQueue(
+ block, outputIgbp, outputGeneration, &bqSlot);
+ if (status != OK) {
+ LOG(WARNING) << "outputBuffer -- attaching failed.";
+ return INVALID_OPERATION;
+ }
+
+ status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+ input, output);
+ if (status != OK) {
+ LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+ "on non-bufferqueue-based block. "
+ "Error = " << status << ".";
+ return status;
+ }
+ return OK;
+ }
+
+ mMutex.lock();
+ sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+ uint32_t outputGeneration = mGeneration;
+ uint64_t outputBqId = mBqId;
+ mMutex.unlock();
+
+ if (!outputIgbp) {
+ LOG(VERBOSE) << "outputBuffer -- output surface is null.";
+ return NO_INIT;
+ }
+
+ if (!display) {
+ LOG(WARNING) << "outputBuffer -- cannot display "
+ "bufferqueue-based block to the bufferqueue.";
+ return UNKNOWN_ERROR;
+ }
+ if (bqId != outputBqId || generation != outputGeneration) {
+ int32_t diff = (int32_t) outputGeneration - (int32_t) generation;
+ LOG(WARNING) << "outputBuffer -- buffers from old generation to "
+ << outputGeneration << " , diff: " << diff
+ << " , slot: " << bqSlot;
+ return DEAD_OBJECT;
+ }
+
+ status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+ input, output);
+ if (status != OK) {
+ LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+ "on bufferqueue-based block. "
+ "Error = " << status << ".";
+ return status;
+ }
+ return OK;
}
- LOG(VERBOSE) << "holdBufferQueueBlock -- attached:"
- << " bqId " << bqId
- << ", bqSlot " << bqSlot
- << ", generation " << generation
- << ".";
- _C2BlockFactory::AssignBlockToBufferQueue(
- data, getHgbp(igbp), generation, bqId, bqSlot, true);
- return true;
+ Impl *getPtr() {
+ return this;
+ }
+
+ ~Impl() {}
+};
+
+OutputBufferQueue::OutputBufferQueue(): mImpl(new Impl()) {}
+
+OutputBufferQueue::~OutputBufferQueue() {}
+
+bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId) {
+ return mImpl && mImpl->configure(igbp, generation, bqId);
}
-void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
- const sp<IGraphicBufferProducer>& igbp,
- uint64_t bqId,
- uint32_t generation,
- bool forInput) {
+status_t OutputBufferQueue::outputBuffer(
+ const C2ConstGraphicBlock& block,
+ const BnGraphicBufferProducer::QueueBufferInput& input,
+ BnGraphicBufferProducer::QueueBufferOutput* output) {
+ if (mImpl) {
+ return mImpl->outputBuffer(block, input, output);
+ }
+ return DEAD_OBJECT;
+}
+
+void OutputBufferQueue::holdBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList) {
+ if (!mImpl) {
+ return;
+ }
forEachBlock(workList,
- std::bind(holdBufferQueueBlock,
- std::placeholders::_1, igbp, bqId, generation),
- forInput, !forInput);
+ std::bind(&OutputBufferQueue::Impl::registerBuffer,
+ mImpl->getPtr(), std::placeholders::_1));
}
} // namespace utils
diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp
index 5897dce..a9f20a4 100644
--- a/media/codec2/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hidl/1.0/utils/Component.cpp
@@ -107,19 +107,22 @@
WorkBundle workBundle;
sp<Component> strongComponent = mComponent.promote();
+ beginTransferBufferQueueBlocks(c2workItems, true);
if (!objcpy(&workBundle, c2workItems, strongComponent ?
&strongComponent->mBufferPoolSender : nullptr)) {
LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
<< "received corrupted work items.";
+ endTransferBufferQueueBlocks(c2workItems, false, true);
return;
}
Return<void> transStatus = listener->onWorkDone(workBundle);
if (!transStatus.isOk()) {
LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
<< "transaction failed.";
+ endTransferBufferQueueBlocks(c2workItems, false, true);
return;
}
- yieldBufferQueueBlocks(c2workItems, true);
+ endTransferBufferQueueBlocks(c2workItems, true, true);
}
}
@@ -254,13 +257,14 @@
WorkBundle flushedWorkBundle;
Status res = static_cast<Status>(c2res);
+ beginTransferBufferQueueBlocks(c2flushedWorks, true);
if (c2res == C2_OK) {
if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) {
res = Status::CORRUPTED;
}
}
_hidl_cb(res, flushedWorkBundle);
- yieldBufferQueueBlocks(c2flushedWorks, true);
+ endTransferBufferQueueBlocks(c2flushedWorks, true, true);
return Void();
}
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
index be429ac..0a2298c 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
@@ -28,62 +28,42 @@
namespace V1_0 {
namespace utils {
-
// BufferQueue-Based Block Operations
// ==================================
-// Create a GraphicBuffer object from a graphic block and attach it to an
-// IGraphicBufferProducer.
-status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
- const sp<IGraphicBufferProducer>& igbp,
- uint32_t generation,
- int32_t* bqSlot);
+// Manage BufferQueue and graphic blocks for both component and codec.
+// Manage graphic blocks ownership consistently during surface change.
+struct OutputBufferQueue {
-// Return false if block does not come from a bufferqueue-based blockpool.
-// Otherwise, extract generation, bqId and bqSlot and return true.
-bool getBufferQueueAssignment(const C2ConstGraphicBlock& block,
- uint32_t* generation,
- uint64_t* bqId,
- int32_t* bqSlot);
+ OutputBufferQueue();
-// Assign the given block to a bufferqueue so that when the block is destroyed,
-// cancelBuffer() will be called.
-//
-// If the block does not come from a bufferqueue-based blockpool, this function
-// returns false.
-//
-// If the block already has a bufferqueue assignment that matches the given one,
-// the function returns true.
-//
-// If the block already has a bufferqueue assignment that does not match the
-// given one, the block will be reassigned to the given bufferqueue. This
-// will call attachBuffer() on the given igbp. The function then returns true on
-// success or false on any failure during the operation.
-//
-// Note: This function should be called after detachBuffer() or dequeueBuffer()
-// is called manually.
-bool holdBufferQueueBlock(const C2ConstGraphicBlock& block,
- const sp<IGraphicBufferProducer>& igbp,
- uint64_t bqId,
- uint32_t generation);
+ ~OutputBufferQueue();
-// Call holdBufferQueueBlock() on input or output blocks in the given workList.
-// Since the bufferqueue assignment for input and output buffers can be
-// different, this function takes forInput to determine whether the given
-// bufferqueue is for input buffers or output buffers. (The default value of
-// forInput is false.)
-//
-// In the (rare) case that both input and output buffers are bufferqueue-based,
-// this function must be called twice, once for the input buffers and once for
-// the output buffers.
-//
-// Note: This function should be called after WorkBundle has been received from
-// another process.
-void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
- const sp<IGraphicBufferProducer>& igbp,
- uint64_t bqId,
- uint32_t generation,
- bool forInput = false);
+ // Configure a new surface to render graphic blocks.
+ // Graphic blocks from older surface will be migrated to new surface.
+ bool configure(const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId);
+
+ // Render a graphic block to current surface.
+ status_t outputBuffer(
+ const C2ConstGraphicBlock& block,
+ const BnGraphicBufferProducer::QueueBufferInput& input,
+ BnGraphicBufferProducer::QueueBufferOutput* output);
+
+ // Call holdBufferQueueBlock() on output blocks in the given workList.
+ // The OutputBufferQueue will take the ownership of output blocks.
+ //
+ // Note: This function should be called after WorkBundle has been received
+ // from another process.
+ void holdBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList);
+
+private:
+
+ class Impl;
+ std::unique_ptr<Impl> mImpl;
+};
} // namespace utils
} // namespace V1_0
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
index 0ddfec5..a9928b3 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
@@ -299,26 +299,46 @@
// BufferQueue-Based Block Operations
// ==================================
-// Disassociate the given block with its designated bufferqueue so that
-// cancelBuffer() will not be called when the block is destroyed. If the block
-// does not have a designated bufferqueue, the function returns false.
-// Otherwise, it returns true.
+// Call before transferring block to other processes.
//
-// Note: This function should be called after attachBuffer() or queueBuffer() is
-// called manually.
-bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block);
+// The given block is ready to transfer to other processes. This will guarantee
+// the given block data is not mutated by bufferqueue migration.
+bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block);
-// Call yieldBufferQueueBlock() on blocks in the given workList. processInput
-// determines whether input blocks are yielded. processOutput works similarly on
-// output blocks. (The default value of processInput is false while the default
-// value of processOutput is true. This implies that in most cases, only output
-// buffers contain bufferqueue-based blocks.)
+// Call beginTransferBufferQueueBlock() on blocks in the given workList.
+// processInput determines whether input blocks are yielded. processOutput
+// works similarly on output blocks. (The default value of processInput is
+// false while the default value of processOutput is true. This implies that in
+// most cases, only output buffers contain bufferqueue-based blocks.)
+void beginTransferBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList,
+ bool processInput = false,
+ bool processOutput = true);
+
+// Call after transferring block is finished and make sure that
+// beginTransferBufferQueueBlock() is called before.
//
-// Note: This function should be called after WorkBundle has been successfully
-// sent over the Treble boundary to another process.
-void yieldBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
- bool processInput = false,
- bool processOutput = true);
+// The transfer of given block is finished. If transfer is successful the given
+// block is not owned by process anymore. Since transfer is finished the given
+// block data is OK to mutate by bufferqueue migration after this call.
+bool endTransferBufferQueueBlock(const C2ConstGraphicBlock& block,
+ bool transfer);
+
+// Call endTransferBufferQueueBlock() on blocks in the given workList.
+// processInput determines whether input blocks are yielded. processOutput
+// works similarly on output blocks. (The default value of processInput is
+// false while the default value of processOutput is true. This implies that in
+// most cases, only output buffers contain bufferqueue-based blocks.)
+void endTransferBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList,
+ bool transfer,
+ bool processInput = false,
+ bool processOutput = true);
+
+// The given block is ready to be rendered. the given block is not owned by
+// process anymore. If migration is in progress, this returns false in order
+// not to render.
+bool displayBufferQueueBlock(const C2ConstGraphicBlock& block);
} // namespace utils
} // namespace V1_0
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index 3fd2f92..07dbf67 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -737,7 +737,15 @@
bufferPoolSender, baseBlocks, baseBlockIndices);
}
case _C2BlockPoolData::TYPE_BUFFERQUEUE:
- // Do the same thing as a NATIVE block.
+ uint32_t gen;
+ uint64_t bqId;
+ int32_t bqSlot;
+ // Update handle if migration happened.
+ if (_C2BlockFactory::GetBufferQueueData(
+ blockPoolData, &gen, &bqId, &bqSlot)) {
+ android::MigrateNativeCodec2GrallocHandle(
+ const_cast<native_handle_t*>(handle), gen, bqId, bqSlot);
+ }
return _addBaseBlock(
index, handle,
baseBlocks, baseBlockIndices);
@@ -1772,20 +1780,53 @@
} // unnamed namespace
-bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block) {
+bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block) {
std::shared_ptr<_C2BlockPoolData> data =
_C2BlockFactory::GetGraphicBlockPoolData(block);
if (data && _C2BlockFactory::GetBufferQueueData(data)) {
- _C2BlockFactory::YieldBlockToBufferQueue(data);
+ _C2BlockFactory::BeginTransferBlockToClient(data);
return true;
}
return false;
}
-void yieldBufferQueueBlocks(
+void beginTransferBufferQueueBlocks(
const std::list<std::unique_ptr<C2Work>>& workList,
bool processInput, bool processOutput) {
- forEachBlock(workList, yieldBufferQueueBlock, processInput, processOutput);
+ forEachBlock(workList, beginTransferBufferQueueBlock,
+ processInput, processOutput);
+}
+
+bool endTransferBufferQueueBlock(
+ const C2ConstGraphicBlock& block,
+ bool transfer) {
+ std::shared_ptr<_C2BlockPoolData> data =
+ _C2BlockFactory::GetGraphicBlockPoolData(block);
+ if (data && _C2BlockFactory::GetBufferQueueData(data)) {
+ _C2BlockFactory::EndTransferBlockToClient(data, transfer);
+ return true;
+ }
+ return false;
+}
+
+void endTransferBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList,
+ bool transfer,
+ bool processInput, bool processOutput) {
+ forEachBlock(workList,
+ std::bind(endTransferBufferQueueBlock,
+ std::placeholders::_1, transfer),
+ processInput, processOutput);
+}
+
+bool displayBufferQueueBlock(const C2ConstGraphicBlock& block) {
+ std::shared_ptr<_C2BlockPoolData> data =
+ _C2BlockFactory::GetGraphicBlockPoolData(block);
+ if (data && _C2BlockFactory::GetBufferQueueData(data)) {
+ _C2BlockFactory::DisplayBlockToBufferQueue(data);
+ return true;
+ }
+ return false;
}
} // namespace utils
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index 6aca4a3..2b417a6 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -1080,15 +1080,7 @@
void Codec2Client::Component::handleOnWorkDone(
const std::list<std::unique_ptr<C2Work>> &workItems) {
// Output bufferqueue-based blocks' lifetime management
- mOutputBufferQueueMutex.lock();
- sp<IGraphicBufferProducer> igbp = mOutputIgbp;
- uint64_t bqId = mOutputBqId;
- uint32_t generation = mOutputGeneration;
- mOutputBufferQueueMutex.unlock();
-
- if (igbp) {
- holdBufferQueueBlocks(workItems, igbp, bqId, generation);
- }
+ mOutputBufferQueue.holdBufferQueueBlocks(workItems);
}
c2_status_t Codec2Client::Component::queue(
@@ -1151,15 +1143,7 @@
}
// Output bufferqueue-based blocks' lifetime management
- mOutputBufferQueueMutex.lock();
- sp<IGraphicBufferProducer> igbp = mOutputIgbp;
- uint64_t bqId = mOutputBqId;
- uint32_t generation = mOutputGeneration;
- mOutputBufferQueueMutex.unlock();
-
- if (igbp) {
- holdBufferQueueBlocks(*flushedWork, igbp, bqId, generation);
- }
+ mOutputBufferQueue.holdBufferQueueBlocks(*flushedWork);
return status;
}
@@ -1239,15 +1223,31 @@
C2BlockPool::local_id_t blockPoolId,
const sp<IGraphicBufferProducer>& surface,
uint32_t generation) {
- sp<HGraphicBufferProducer2> igbp =
- surface->getHalInterface<HGraphicBufferProducer2>();
+ uint64_t bqId = 0;
+ sp<IGraphicBufferProducer> nullIgbp;
+ sp<HGraphicBufferProducer2> nullHgbp;
- if (!igbp) {
+ sp<HGraphicBufferProducer2> igbp = surface ?
+ surface->getHalInterface<HGraphicBufferProducer2>() : nullHgbp;
+ if (surface && !igbp) {
igbp = new B2HGraphicBufferProducer2(surface);
}
+ if (!surface) {
+ mOutputBufferQueue.configure(nullIgbp, generation, 0);
+ } else if (surface->getUniqueId(&bqId) != OK) {
+ LOG(ERROR) << "setOutputSurface -- "
+ "cannot obtain bufferqueue id.";
+ bqId = 0;
+ mOutputBufferQueue.configure(nullIgbp, generation, 0);
+ } else {
+ mOutputBufferQueue.configure(surface, generation, bqId);
+ }
+ ALOGD("generation remote change %u", generation);
+
Return<Status> transStatus = mBase->setOutputSurface(
- static_cast<uint64_t>(blockPoolId), igbp);
+ static_cast<uint64_t>(blockPoolId),
+ bqId == 0 ? nullHgbp : igbp);
if (!transStatus.isOk()) {
LOG(ERROR) << "setOutputSurface -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1256,18 +1256,6 @@
static_cast<c2_status_t>(static_cast<Status>(transStatus));
if (status != C2_OK) {
LOG(DEBUG) << "setOutputSurface -- call failed: " << status << ".";
- } else {
- std::lock_guard<std::mutex> lock(mOutputBufferQueueMutex);
- if (mOutputIgbp != surface) {
- mOutputIgbp = surface;
- if (!surface) {
- mOutputBqId = 0;
- } else if (surface->getUniqueId(&mOutputBqId) != OK) {
- LOG(ERROR) << "setOutputSurface -- "
- "cannot obtain bufferqueue id.";
- }
- }
- mOutputGeneration = generation;
}
return status;
}
@@ -1276,74 +1264,7 @@
const C2ConstGraphicBlock& block,
const QueueBufferInput& input,
QueueBufferOutput* output) {
- uint32_t generation;
- uint64_t bqId;
- int32_t bqSlot;
- if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
- bqId == 0) {
- // Block not from bufferqueue -- it must be attached before queuing.
-
- mOutputBufferQueueMutex.lock();
- sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
- uint32_t outputGeneration = mOutputGeneration;
- mOutputBufferQueueMutex.unlock();
-
- status_t status = attachToBufferQueue(block,
- outputIgbp,
- outputGeneration,
- &bqSlot);
- if (status != OK) {
- LOG(WARNING) << "queueToOutputSurface -- attaching failed.";
- return INVALID_OPERATION;
- }
-
- status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
- input, output);
- if (status != OK) {
- LOG(ERROR) << "queueToOutputSurface -- queueBuffer() failed "
- "on non-bufferqueue-based block. "
- "Error = " << status << ".";
- return status;
- }
- return OK;
- }
-
- mOutputBufferQueueMutex.lock();
- sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
- uint64_t outputBqId = mOutputBqId;
- uint32_t outputGeneration = mOutputGeneration;
- mOutputBufferQueueMutex.unlock();
-
- if (!outputIgbp) {
- LOG(VERBOSE) << "queueToOutputSurface -- output surface is null.";
- return NO_INIT;
- }
-
- if (bqId != outputBqId || generation != outputGeneration) {
- if (!holdBufferQueueBlock(block, mOutputIgbp, mOutputBqId, mOutputGeneration)) {
- LOG(ERROR) << "queueToOutputSurface -- migration failed.";
- return DEAD_OBJECT;
- }
- if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot)) {
- LOG(ERROR) << "queueToOutputSurface -- corrupted bufferqueue assignment.";
- return UNKNOWN_ERROR;
- }
- }
-
- status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
- input, output);
- if (status != OK) {
- LOG(DEBUG) << "queueToOutputSurface -- queueBuffer() failed "
- "on bufferqueue-based block. "
- "Error = " << status << ".";
- return status;
- }
- if (!yieldBufferQueueBlock(block)) {
- LOG(DEBUG) << "queueToOutputSurface -- cannot yield "
- "bufferqueue-based block to the bufferqueue.";
- return UNKNOWN_ERROR;
- }
- return OK;
+ return mOutputBufferQueue.outputBuffer(block, input, output);
}
c2_status_t Codec2Client::Component::connectToInputSurface(
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index 03db515..b8a7fb5 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -369,10 +369,8 @@
::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender
mBufferPoolSender;
- std::mutex mOutputBufferQueueMutex;
- sp<IGraphicBufferProducer> mOutputIgbp;
- uint64_t mOutputBqId;
- uint32_t mOutputGeneration;
+ ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue
+ mOutputBufferQueue;
static c2_status_t setDeathListener(
const std::shared_ptr<Component>& component,
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index eb20b20..8be9a1d 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1581,6 +1581,7 @@
if (newSurface) {
newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
newSurface->setDequeueTimeout(kDequeueTimeoutNs);
+ newSurface->setMaxDequeuedBufferCount(mOutputSurface.lock()->maxDequeueBuffers);
producer = newSurface->getIGraphicBufferProducer();
producer->setGenerationNumber(generation);
} else {
@@ -1608,7 +1609,6 @@
{
Mutexed<OutputSurface>::Locked output(mOutputSurface);
- newSurface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
output->surface = newSurface;
output->generation = generation;
}
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index e698bf4..286c48a 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -201,6 +201,22 @@
return res;
}
+ static bool MigrateNativeHandle(
+ native_handle_t *handle,
+ uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) {
+ if (handle == nullptr || !isValid(handle)) {
+ return false;
+ }
+ ExtraData *ed = getExtraData(handle);
+ if (!ed) return false;
+ ed->generation = generation;
+ ed->igbp_id_lo = uint32_t(igbp_id & 0xFFFFFFFF);
+ ed->igbp_id_hi = uint32_t(igbp_id >> 32);
+ ed->igbp_slot = igbp_slot;
+ return true;
+ }
+
+
static native_handle_t* UnwrapNativeHandle(
const C2Handle *const handle) {
const ExtraData *xd = getExtraData(handle);
@@ -270,6 +286,13 @@
generation, igbp_id, igbp_slot);
}
+bool MigrateNativeCodec2GrallocHandle(
+ native_handle_t *handle,
+ uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) {
+ return C2HandleGralloc::MigrateNativeHandle(handle, generation, igbp_id, igbp_slot);
+}
+
+
class C2AllocationGralloc : public C2GraphicAllocation {
public:
virtual ~C2AllocationGralloc() override;
diff --git a/media/codec2/vndk/include/C2AllocatorGralloc.h b/media/codec2/vndk/include/C2AllocatorGralloc.h
index 05d989e..ee7524e 100644
--- a/media/codec2/vndk/include/C2AllocatorGralloc.h
+++ b/media/codec2/vndk/include/C2AllocatorGralloc.h
@@ -45,6 +45,16 @@
uint32_t generation = 0, uint64_t igbp_id = 0, uint32_t igbp_slot = 0);
/**
+ * When the gralloc handle is migrated to another bufferqueue, update
+ * bufferqueue information.
+ *
+ * @return {@code true} when native_handle is a wrapped codec2 handle.
+ */
+bool MigrateNativeCodec2GrallocHandle(
+ native_handle_t *handle,
+ uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot);
+
+/**
* \todo Get this from the buffer
*/
void _UnwrapNativeCodec2GrallocMetadata(
diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h
index 84ce70a..4ae946a 100644
--- a/media/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/codec2/vndk/internal/C2BlockInternal.h
@@ -206,23 +206,19 @@
*
* - GetBufferQueueData(): Returns generation, bqId and bqSlot.
* - HoldBlockFromBufferQueue(): Sets "held" status to true.
- * - YieldBlockToBufferQueue(): Sets "held" status to false.
- * - AssignBlockToBufferQueue(): Sets the bufferqueue assignment and
- * "held" status.
+ * - BeginTransferBlockToClient()/EndTransferBlockToClient():
+ * Clear "held" status to false if transfer was successful,
+ * otherwise "held" status remains true.
+ * - BeginAttachBlockToBufferQueue()/EndAttachBlockToBufferQueue():
+ * The will keep "held" status true if attach was eligible.
+ * Otherwise, "held" status is cleared to false. In that case,
+ * ownership of buffer should be transferred to bufferqueue.
+ * - DisplayBlockToBufferQueue()
+ * This will clear "held" status to false.
*
* All these functions operate on _C2BlockPoolData, which can be obtained by
* calling GetGraphicBlockPoolData().
*
- * HoldBlockFromBufferQueue() will mark the block as held, while
- * YieldBlockToBufferQueue() will do the opposite. These two functions do
- * not modify the bufferqueue assignment, so it is not wrong to call
- * HoldBlockFromBufferQueue() after YieldBlockToBufferQueue() if it can be
- * guaranteed that the block is not destroyed during the period between the
- * two calls.
- *
- * AssingBlockToBufferQueue() has a "held" status as an optional argument.
- * The default value is true.
- *
* Maintaining Consistency with IGraphicBufferProducer Operations
* ==============================================================
*
@@ -232,16 +228,20 @@
* information for _C2BlockPoolData, with "held" status set to true.
*
* queueBuffer()
- * - After queueBuffer() is called, YieldBlockToBufferQueue() should be
- * called.
+ * - Before queueBuffer() is called, DisplayBlockToBufferQueue() should be
+ * called to test eligibility. If it's not eligible, do not call
+ * queueBuffer().
*
- * attachBuffer()
- * - After attachBuffer() is called, AssignBlockToBufferQueue() should be
- * called with "held" status set to true.
+ * attachBuffer() - remote migration only.
+ * - Local migration on blockpool side will be done automatically by
+ * blockpool.
+ * - Before attachBuffer(), BeginAttachBlockToBufferQueue() should be called
+ * to test eligiblity.
+ * - After attachBuffer() is called, EndAttachBlockToBufferQueue() should
+ * be called. This will set "held" status to true. If it returned
+ * false, cancelBuffer() should be called.
*
- * detachBuffer()
- * - After detachBuffer() is called, HoldBlockFromBufferQueue() should be
- * called.
+ * detachBuffer() - no-op.
*/
/**
@@ -261,43 +261,12 @@
*/
static
bool GetBufferQueueData(
- const std::shared_ptr<_C2BlockPoolData>& poolData,
+ const std::shared_ptr<const _C2BlockPoolData>& poolData,
uint32_t* generation = nullptr,
uint64_t* bqId = nullptr,
int32_t* bqSlot = nullptr);
/**
- * Set bufferqueue assignment and "held" status to a block created by a
- * bufferqueue-based blockpool.
- *
- * \param poolData blockpool data associated to the block.
- * \param igbp \c IGraphicBufferProducer instance from the designated
- * bufferqueue.
- * \param generation Generation number that the buffer belongs to.
- * \param bqId Id of the bufferqueue that will own the buffer (block).
- * \param bqSlot Slot number of the buffer.
- * \param held Whether the block is held. This "held" status can be
- * changed later by calling YieldBlockToBufferQueue() or
- * HoldBlockFromBufferQueue().
- *
- * \return \c true if \p poolData is valid bufferqueue data;
- * \c false otherwise.
- *
- * Note: \p generation should match the latest generation number set on the
- * bufferqueue, and \p bqId should match the unique id for the bufferqueue
- * (obtainable by calling igbp->getUniqueId()).
- */
- static
- bool AssignBlockToBufferQueue(
- const std::shared_ptr<_C2BlockPoolData>& poolData,
- const ::android::sp<::android::hardware::graphics::bufferqueue::
- V2_0::IGraphicBufferProducer>& igbp,
- uint32_t generation,
- uint64_t bqId,
- int32_t bqSlot,
- bool held = true);
-
- /**
* Hold a block from the designated bufferqueue. This causes the destruction
* of the block to trigger a call to cancelBuffer().
*
@@ -305,6 +274,9 @@
* block. It does not check if that is the case.
*
* \param poolData blockpool data associated to the block.
+ * \param owner block owner from client bufferqueue manager.
+ * If this is expired, the block is not owned by client
+ * anymore.
* \param igbp \c IGraphicBufferProducer instance to be assigned to the
* block. This is not needed when the block is local.
*
@@ -313,24 +285,96 @@
static
bool HoldBlockFromBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& poolData,
+ const std::shared_ptr<int>& owner,
const ::android::sp<::android::hardware::graphics::bufferqueue::
V2_0::IGraphicBufferProducer>& igbp = nullptr);
/**
- * Yield a block to the designated bufferqueue. This causes the destruction
- * of the block not to trigger a call to cancelBuffer();
+ * Prepare a block to be transferred to other process. This blocks
+ * bufferqueue migration from happening. The block should be in held.
*
* This function assumes that \p poolData comes from a bufferqueue-based
* block. It does not check if that is the case.
*
* \param poolData blockpool data associated to the block.
*
- * \return The previous held status.
+ * \return true if transfer is eligible, false otherwise.
*/
static
- bool YieldBlockToBufferQueue(
+ bool BeginTransferBlockToClient(
const std::shared_ptr<_C2BlockPoolData>& poolData);
+ /**
+ * Called after transferring the specified block is finished. Make sure
+ * that BeginTransferBlockToClient() was called before this call.
+ *
+ * This will unblock bufferqueue migration. If transfer result was
+ * successful, this causes the destruction of the block not to trigger a
+ * call to cancelBuffer().
+ * This function assumes that \p poolData comes from a bufferqueue-based
+ * block. It does not check if that is the case.
+ *
+ * \param poolData blockpool data associated to the block.
+ *
+ * \return true if transfer began before, false otherwise.
+ */
+ static
+ bool EndTransferBlockToClient(
+ const std::shared_ptr<_C2BlockPoolData>& poolData,
+ bool transferred);
+
+ /**
+ * Prepare a block to be migrated to another bufferqueue. This blocks
+ * rendering until migration has been finished. The block should be in
+ * held.
+ *
+ * This function assumes that \p poolData comes from a bufferqueue-based
+ * block. It does not check if that is the case.
+ *
+ * \param poolData blockpool data associated to the block.
+ *
+ * \return true if migration is eligible, false otherwise.
+ */
+ static
+ bool BeginAttachBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& poolData);
+
+ /**
+ * Called after migration of the specified block is finished. Make sure
+ * that BeginAttachBlockToBufferQueue() was called before this call.
+ *
+ * This will unblock rendering. if redering is tried during migration,
+ * this returns false. In that case, cancelBuffer() should be called.
+ * This function assumes that \p poolData comes from a bufferqueue-based
+ * block. It does not check if that is the case.
+ *
+ * \param poolData blockpool data associated to the block.
+ *
+ * \return true if migration is eligible, false otherwise.
+ */
+ static
+ bool EndAttachBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& poolData,
+ const std::shared_ptr<int>& owner,
+ const ::android::sp<::android::hardware::graphics::bufferqueue::
+ V2_0::IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId,
+ int32_t bqSlot);
+
+ /**
+ * Indicates a block to be rendered very soon.
+ *
+ * This function assumes that \p poolData comes from a bufferqueue-based
+ * block. It does not check if that is the case.
+ *
+ * \param poolData blockpool data associated to the block.
+ *
+ * \return true if migration is eligible, false otherwise.
+ */
+ static
+ bool DisplayBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& poolData);
};
#endif // ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 3f40e56..5fa48a8 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -61,8 +61,13 @@
uint32_t generation;
uint64_t bqId;
int32_t bqSlot;
+ bool transfer; // local transfer to remote
+ bool attach; // attach on remote
+ bool display; // display on remote;
+ std::weak_ptr<int> owner;
sp<HGraphicBufferProducer> igbp;
std::shared_ptr<C2BufferQueueBlockPool::Impl> localPool;
+ mutable std::mutex lock;
virtual type_t getType() const override {
return TYPE_BUFFERQUEUE;
@@ -71,7 +76,8 @@
// Create a remote BlockPoolData.
C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
- const sp<HGraphicBufferProducer>& producer = nullptr);
+ const std::shared_ptr<int> &owner,
+ const sp<HGraphicBufferProducer>& producer);
// Create a local BlockPoolData.
C2BufferQueueBlockPoolData(
@@ -80,15 +86,19 @@
virtual ~C2BufferQueueBlockPoolData() override;
+ int migrate(const sp<HGraphicBufferProducer>& producer,
+ uint32_t toGeneration, uint64_t toBqId,
+ sp<GraphicBuffer> *buffers, uint32_t oldGeneration);
};
bool _C2BlockFactory::GetBufferQueueData(
- const std::shared_ptr<_C2BlockPoolData>& data,
+ const std::shared_ptr<const _C2BlockPoolData>& data,
uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) {
if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
if (generation) {
- const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
- std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ const std::shared_ptr<const C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<const C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
*generation = poolData->generation;
if (bqId) {
*bqId = poolData->bqId;
@@ -102,32 +112,15 @@
return false;
}
-bool _C2BlockFactory::AssignBlockToBufferQueue(
- const std::shared_ptr<_C2BlockPoolData>& data,
- const sp<HGraphicBufferProducer>& igbp,
- uint32_t generation,
- uint64_t bqId,
- int32_t bqSlot,
- bool held) {
- if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
- const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
- std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- poolData->igbp = igbp;
- poolData->generation = generation;
- poolData->bqId = bqId;
- poolData->bqSlot = bqSlot;
- poolData->held = held;
- return true;
- }
- return false;
-}
-
bool _C2BlockFactory::HoldBlockFromBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data,
+ const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& igbp) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
if (!poolData->local) {
+ poolData->owner = owner;
poolData->igbp = igbp;
}
if (poolData->held) {
@@ -138,12 +131,86 @@
return true;
}
-bool _C2BlockFactory::YieldBlockToBufferQueue(
+bool _C2BlockFactory::BeginTransferBlockToClient(
const std::shared_ptr<_C2BlockPoolData>& data) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- if (!poolData->held) {
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ poolData->transfer = true;
+ return true;
+}
+
+bool _C2BlockFactory::EndTransferBlockToClient(
+ const std::shared_ptr<_C2BlockPoolData>& data,
+ bool transfer) {
+ const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ poolData->transfer = false;
+ if (transfer) {
poolData->held = false;
+ }
+ return true;
+}
+
+bool _C2BlockFactory::BeginAttachBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& data) {
+ const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ if (poolData->local || poolData->display ||
+ poolData->attach || !poolData->held) {
+ return false;
+ }
+ if (poolData->bqId == 0) {
+ return false;
+ }
+ poolData->attach = true;
+ return true;
+}
+
+// if display was tried during attach, buffer should be retired ASAP.
+bool _C2BlockFactory::EndAttachBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& data,
+ const std::shared_ptr<int>& owner,
+ const sp<HGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId,
+ int32_t bqSlot) {
+ const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ if (poolData->local || !poolData->attach ) {
+ return false;
+ }
+ if (poolData->display) {
+ poolData->attach = false;
+ poolData->held = false;
+ return false;
+ }
+ poolData->attach = false;
+ poolData->held = true;
+ poolData->owner = owner;
+ poolData->igbp = igbp;
+ poolData->generation = generation;
+ poolData->bqId = bqId;
+ poolData->bqSlot = bqSlot;
+ return true;
+}
+
+bool _C2BlockFactory::DisplayBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& data) {
+ const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ if (poolData->local || poolData->display || !poolData->held) {
+ return false;
+ }
+ if (poolData->bqId == 0) {
+ return false;
+ }
+ poolData->display = true;
+ if (poolData->attach) {
return false;
}
poolData->held = false;
@@ -175,7 +242,9 @@
std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::make_shared<C2BufferQueueBlockPoolData>(generation,
bqId,
- (int32_t)bqSlot);
+ (int32_t)bqSlot,
+ nullptr,
+ nullptr);
block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
} else {
block = _C2BlockFactory::CreateGraphicBlock(alloc);
@@ -186,6 +255,78 @@
return nullptr;
}
+namespace {
+
+int64_t getTimestampNow() {
+ int64_t stamp;
+ struct timespec ts;
+ // TODO: CLOCK_MONOTONIC_COARSE?
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ stamp = ts.tv_nsec / 1000;
+ stamp += (ts.tv_sec * 1000000LL);
+ return stamp;
+}
+
+bool getGenerationNumber(const sp<HGraphicBufferProducer> &producer,
+ uint32_t *generation) {
+ status_t status{};
+ int slot{};
+ bool bufferNeedsReallocation{};
+ sp<Fence> fence = new Fence();
+
+ using Input = HGraphicBufferProducer::DequeueBufferInput;
+ using Output = HGraphicBufferProducer::DequeueBufferOutput;
+ Return<void> transResult = producer->dequeueBuffer(
+ Input{640, 480, HAL_PIXEL_FORMAT_YCBCR_420_888, 0},
+ [&status, &slot, &bufferNeedsReallocation, &fence]
+ (HStatus hStatus, int32_t hSlot, Output const& hOutput) {
+ slot = static_cast<int>(hSlot);
+ if (!h2b(hStatus, &status) || !h2b(hOutput.fence, &fence)) {
+ status = ::android::BAD_VALUE;
+ } else {
+ bufferNeedsReallocation =
+ hOutput.bufferNeedsReallocation;
+ }
+ });
+ if (!transResult.isOk() || status != android::OK) {
+ return false;
+ }
+ HFenceWrapper hFenceWrapper{};
+ if (!b2h(fence, &hFenceWrapper)) {
+ (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+ ALOGE("Invalid fence received from dequeueBuffer.");
+ return false;
+ }
+ sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
+ // N.B. This assumes requestBuffer# returns an existing allocation
+ // instead of a new allocation.
+ transResult = producer->requestBuffer(
+ slot,
+ [&status, &slotBuffer, &generation](
+ HStatus hStatus,
+ HBuffer const& hBuffer,
+ uint32_t generationNumber){
+ if (h2b(hStatus, &status) &&
+ h2b(hBuffer, &slotBuffer) &&
+ slotBuffer) {
+ *generation = generationNumber;
+ slotBuffer->setGenerationNumber(generationNumber);
+ } else {
+ status = android::BAD_VALUE;
+ }
+ });
+ if (!transResult.isOk()) {
+ return false;
+ } else if (status != android::NO_ERROR) {
+ (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+ return false;
+ }
+ (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+ return true;
+}
+
+};
+
class C2BufferQueueBlockPool::Impl
: public std::enable_shared_from_this<C2BufferQueueBlockPool::Impl> {
private:
@@ -227,6 +368,7 @@
});
if (!transResult.isOk() || status != android::OK) {
if (transResult.isOk()) {
+ ++mDqFailure;
if (status == android::INVALID_OPERATION ||
status == android::TIMED_OUT ||
status == android::WOULD_BLOCK) {
@@ -238,6 +380,8 @@
ALOGD("cannot dequeue buffer %d", status);
return C2_BAD_VALUE;
}
+ mDqFailure = 0;
+ mLastDqTs = getTimestampNow();
}
HFenceWrapper hFenceWrapper{};
if (!b2h(fence, &hFenceWrapper)) {
@@ -319,6 +463,7 @@
slotBuffer->getGenerationNumber(),
mProducerId, slot,
shared_from_this());
+ mPoolDatas[slot] = poolData;
*block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
return C2_OK;
}
@@ -331,7 +476,9 @@
public:
Impl(const std::shared_ptr<C2Allocator> &allocator)
- : mInit(C2_OK), mProducerId(0), mAllocator(allocator) {
+ : mInit(C2_OK), mProducerId(0), mGeneration(0),
+ mDqFailure(0), mLastDqTs(0), mLastDqLogTs(0),
+ mAllocator(allocator) {
}
~Impl() {
@@ -361,6 +508,19 @@
static int kMaxIgbpRetryDelayUs = 10000;
std::unique_lock<std::mutex> lock(mMutex);
+ if (mLastDqLogTs == 0) {
+ mLastDqLogTs = getTimestampNow();
+ } else {
+ int64_t now = getTimestampNow();
+ if (now >= mLastDqLogTs + 5000000) {
+ if (now >= mLastDqTs + 1000000 || mDqFailure > 5) {
+ ALOGW("last successful dequeue was %lld us ago, "
+ "%zu consecutive failures",
+ (long long)(now - mLastDqTs), mDqFailure);
+ }
+ mLastDqLogTs = now;
+ }
+ }
if (mProducerId == 0) {
std::shared_ptr<C2GraphicAllocation> alloc;
c2_status_t err = mAllocator->newGraphicAllocation(
@@ -386,12 +546,14 @@
}
void setRenderCallback(const OnRenderCallback &renderCallback) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::scoped_lock<std::mutex> lock(mMutex);
mRenderCallback = renderCallback;
}
void configureProducer(const sp<HGraphicBufferProducer> &producer) {
uint64_t producerId = 0;
+ uint32_t generation = 0;
+ bool haveGeneration = false;
if (producer) {
Return<uint64_t> transResult = producer->getUniqueId();
if (!transResult.isOk()) {
@@ -399,9 +561,15 @@
return;
}
producerId = static_cast<uint64_t>(transResult);
+ // TODO: provide gneration number from parameter.
+ haveGeneration = getGenerationNumber(producer, &generation);
}
+ int migrated = 0;
{
- std::lock_guard<std::mutex> lock(mMutex);
+ sp<GraphicBuffer> buffers[NUM_BUFFER_SLOTS];
+ std::weak_ptr<C2BufferQueueBlockPoolData>
+ poolDatas[NUM_BUFFER_SLOTS];
+ std::scoped_lock<std::mutex> lock(mMutex);
bool noInit = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
if (!noInit && mProducer) {
@@ -410,46 +578,88 @@
noInit = !transResult.isOk() ||
static_cast<HStatus>(transResult) == HStatus::NO_INIT;
}
- mBuffers[i].clear();
}
- if (producer) {
+ int32_t oldGeneration = mGeneration;
+ if (producer && haveGeneration) {
mProducer = producer;
mProducerId = producerId;
+ mGeneration = generation;
} else {
mProducer = nullptr;
mProducerId = 0;
+ mGeneration = 0;
+ ALOGW("invalid producer producer(%d), generation(%d)",
+ (bool)producer, haveGeneration);
}
+ if (mProducer) { // migrate buffers
+ for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
+ std::shared_ptr<C2BufferQueueBlockPoolData> data =
+ mPoolDatas[i].lock();
+ if (data) {
+ int slot = data->migrate(
+ mProducer, generation,
+ producerId, mBuffers, oldGeneration);
+ if (slot >= 0) {
+ buffers[slot] = mBuffers[i];
+ poolDatas[slot] = data;
+ ++migrated;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
+ mBuffers[i] = buffers[i];
+ mPoolDatas[i] = poolDatas[i];
+ }
+ }
+ if (producer && haveGeneration) {
+ ALOGD("local generation change %u , "
+ "bqId: %llu migrated buffers # %d",
+ generation, (unsigned long long)producerId, migrated);
}
}
private:
friend struct C2BufferQueueBlockPoolData;
- void cancel(uint64_t igbp_id, int32_t igbp_slot) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (igbp_id == mProducerId && mProducer) {
+ void cancel(uint32_t generation, uint64_t igbp_id, int32_t igbp_slot) {
+ bool cancelled = false;
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (generation == mGeneration && igbp_id == mProducerId && mProducer) {
(void)mProducer->cancelBuffer(igbp_slot, hidl_handle{}).isOk();
+ cancelled = true;
+ }
}
}
c2_status_t mInit;
uint64_t mProducerId;
+ uint32_t mGeneration;
OnRenderCallback mRenderCallback;
+ size_t mDqFailure;
+ int64_t mLastDqTs;
+ int64_t mLastDqLogTs;
+
const std::shared_ptr<C2Allocator> mAllocator;
std::mutex mMutex;
sp<HGraphicBufferProducer> mProducer;
+ sp<HGraphicBufferProducer> mSavedProducer;
sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
+ std::weak_ptr<C2BufferQueueBlockPoolData> mPoolDatas[NUM_BUFFER_SLOTS];
};
C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
+ const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& producer) :
held(producer && bqId != 0), local(false),
generation(generation), bqId(bqId), bqSlot(bqSlot),
- igbp(producer),
+ transfer(false), attach(false), display(false),
+ owner(owner), igbp(producer),
localPool() {
}
@@ -458,6 +668,7 @@
const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool) :
held(true), local(true),
generation(generation), bqId(bqId), bqSlot(bqSlot),
+ transfer(false), attach(false), display(false),
igbp(pool ? pool->mProducer : nullptr),
localPool(pool) {
}
@@ -466,12 +677,78 @@
if (!held || bqId == 0) {
return;
}
- if (local && localPool) {
- localPool->cancel(bqId, bqSlot);
- } else if (igbp) {
+ if (local) {
+ if (localPool) {
+ localPool->cancel(generation, bqId, bqSlot);
+ }
+ } else if (igbp && !owner.expired()) {
igbp->cancelBuffer(bqSlot, hidl_handle{}).isOk();
}
}
+int C2BufferQueueBlockPoolData::migrate(
+ const sp<HGraphicBufferProducer>& producer,
+ uint32_t toGeneration, uint64_t toBqId,
+ sp<GraphicBuffer> *buffers, uint32_t oldGeneration) {
+ std::scoped_lock<std::mutex> l(lock);
+ if (!held || bqId == 0) {
+ ALOGV("buffer is not owned");
+ return -1;
+ }
+ if (!local || !localPool) {
+ ALOGV("pool is not local");
+ return -1;
+ }
+ if (bqSlot < 0 || bqSlot >= NUM_BUFFER_SLOTS || !buffers[bqSlot]) {
+ ALOGV("slot is not in effect");
+ return -1;
+ }
+ if (toGeneration == generation && bqId == toBqId) {
+ ALOGV("cannot migrate to same bufferqueue");
+ return -1;
+ }
+ if (oldGeneration != generation) {
+ ALOGV("cannot migrate stale buffer");
+ }
+ if (transfer) {
+ // either transferred or detached.
+ ALOGV("buffer is in transfer");
+ return -1;
+ }
+ sp<GraphicBuffer> const& graphicBuffer = buffers[bqSlot];
+ graphicBuffer->setGenerationNumber(toGeneration);
+
+ HBuffer hBuffer{};
+ uint32_t hGenerationNumber{};
+ if (!b2h(graphicBuffer, &hBuffer, &hGenerationNumber)) {
+ ALOGD("I to O conversion failed");
+ return -1;
+ }
+
+ bool converted{};
+ status_t bStatus{};
+ int slot;
+ int *outSlot = &slot;
+ Return<void> transResult =
+ producer->attachBuffer(hBuffer, hGenerationNumber,
+ [&converted, &bStatus, outSlot](
+ HStatus hStatus, int32_t hSlot, bool releaseAll) {
+ converted = h2b(hStatus, &bStatus);
+ *outSlot = static_cast<int>(hSlot);
+ if (converted && releaseAll && bStatus == android::OK) {
+ bStatus = android::INVALID_OPERATION;
+ }
+ });
+ if (!transResult.isOk() || !converted || bStatus != android::OK) {
+ ALOGD("attach failed %d", static_cast<int>(bStatus));
+ return -1;
+ }
+ ALOGV("local migration from gen %u : %u slot %d : %d",
+ generation, toGeneration, bqSlot, slot);
+ generation = toGeneration;
+ bqId = toBqId;
+ bqSlot = slot;
+ return slot;
+}
C2BufferQueueBlockPool::C2BufferQueueBlockPool(
const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)