Loading media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp +204 −65 Original line number Original line Diff line number Diff line Loading @@ -82,21 +82,14 @@ void forEachBlock(C2FrameData& frameData, template <typename BlockProcessor> template <typename BlockProcessor> void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList, void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList, BlockProcessor process, BlockProcessor process) { bool processInput, bool processOutput) { for (const std::unique_ptr<C2Work>& work : workList) { for (const std::unique_ptr<C2Work>& work : workList) { if (!work) { if (!work) { continue; continue; } } if (processInput) { forEachBlock(work->input, process); } if (processOutput) { for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) { for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) { if (worklet) { if (worklet) { forEachBlock(worklet->output, forEachBlock(worklet->output, process); process); } } } } } } } Loading @@ -109,8 +102,6 @@ sp<HGraphicBufferProducer> getHgbp(const sp<IGraphicBufferProducer>& igbp) { new B2HGraphicBufferProducer(igbp); new B2HGraphicBufferProducer(igbp); } } } // unnamed namespace status_t attachToBufferQueue(const C2ConstGraphicBlock& block, status_t attachToBufferQueue(const C2ConstGraphicBlock& block, const sp<IGraphicBufferProducer>& igbp, const sp<IGraphicBufferProducer>& igbp, uint32_t generation, uint32_t generation, Loading Loading @@ -154,73 +145,221 @@ bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, _C2BlockFactory::GetGraphicBlockPoolData(block), _C2BlockFactory::GetGraphicBlockPoolData(block), generation, bqId, bqSlot); generation, bqId, bqSlot); } } } // unnamed namespace bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, class OutputBufferQueue::Impl { const sp<IGraphicBufferProducer>& igbp, std::mutex mMutex; uint64_t bqId, sp<IGraphicBufferProducer> mIgbp; uint32_t generation) { 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]; public: Impl(): mGeneration(0), mBqId(0) {} 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; } bool registerBuffer(const C2ConstGraphicBlock& block) { std::shared_ptr<_C2BlockPoolData> data = std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(block); _C2BlockFactory::GetGraphicBlockPoolData(block); if (!data) { if (!data) { return false; return false; } } std::scoped_lock<std::mutex> l(mMutex); if (!mIgbp) { return false; } uint32_t oldGeneration; uint32_t oldGeneration; uint64_t oldId; uint64_t oldId; int32_t oldSlot; int32_t oldSlot; // If the block is not bufferqueue-based, do nothing. // If the block is not bufferqueue-based, do nothing. if (!_C2BlockFactory::GetBufferQueueData( if (!_C2BlockFactory::GetBufferQueueData( data, &oldGeneration, &oldId, &oldSlot) || data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) { (oldId == 0)) { return false; return false; } } // If the block's bqId is the same as the desired bqId, just hold. // If the block's bqId is the same as the desired bqId, just hold. if ((oldId == bqId) && (oldGeneration == generation)) { if ((oldId == mBqId) && (oldGeneration == mGeneration)) { LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" << " bqId " << oldId << " bqId " << oldId << ", bqSlot " << oldSlot << ", bqSlot " << oldSlot << ", generation " << generation << ", generation " << mGeneration << "."; << "."; _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp)); _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp)); mPoolDatas[oldSlot] = data; mBuffers[oldSlot] = createGraphicBuffer(block); mBuffers[oldSlot]->setGenerationNumber(mGeneration); return true; return true; } } int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration; // Otherwise, attach to the given igbp, which must not be null. LOG(WARNING) << "receiving stale buffer: generation " if (!igbp) { << mGeneration << " , diff " << d << " : slot " << oldSlot; return false; return false; } } status_t outputBuffer( const C2ConstGraphicBlock& block, const BnGraphicBufferProducer::QueueBufferInput& input, BnGraphicBufferProducer::QueueBufferOutput* output) { uint32_t generation; uint64_t bqId; int32_t bqSlot; int32_t bqSlot; status_t result = attachToBufferQueue(block, igbp, generation, &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) { mMutex.lock(); LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:" sp<IGraphicBufferProducer> outputIgbp = mIgbp; << " target bqId " << bqId uint32_t outputGeneration = mGeneration; << ", generation " << generation mMutex.unlock(); << "."; return false; status_t status = attachToBufferQueue( block, outputIgbp, outputGeneration, &bqSlot); if (status != OK) { LOG(WARNING) << "outputBuffer -- attaching failed."; return INVALID_OPERATION; } } LOG(VERBOSE) << "holdBufferQueueBlock -- attached:" status = outputIgbp->queueBuffer(static_cast<int>(bqSlot), << " bqId " << bqId input, output); << ", bqSlot " << bqSlot if (status != OK) { << ", generation " << generation LOG(ERROR) << "outputBuffer -- queueBuffer() failed " << "."; "on non-bufferqueue-based block. " _C2BlockFactory::AssignBlockToBufferQueue( "Error = " << status << "."; data, getHgbp(igbp), generation, bqId, bqSlot, true); return status; return true; } return OK; } } void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList, mMutex.lock(); const sp<IGraphicBufferProducer>& igbp, sp<IGraphicBufferProducer> outputIgbp = mIgbp; uint64_t bqId, 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; } Impl *getPtr() { return this; } ~Impl() {} }; OutputBufferQueue::OutputBufferQueue(): mImpl(new Impl()) {} OutputBufferQueue::~OutputBufferQueue() {} bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp, uint32_t generation, uint32_t generation, bool forInput) { uint64_t bqId) { return mImpl && mImpl->configure(igbp, generation, bqId); } 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, forEachBlock(workList, std::bind(holdBufferQueueBlock, std::bind(&OutputBufferQueue::Impl::registerBuffer, std::placeholders::_1, igbp, bqId, generation), mImpl->getPtr(), std::placeholders::_1)); forInput, !forInput); } } } // namespace utils } // namespace utils Loading media/codec2/hidl/1.0/utils/Component.cpp +6 −2 Original line number Original line Diff line number Diff line Loading @@ -107,19 +107,22 @@ struct Component::Listener : public C2Component::Listener { WorkBundle workBundle; WorkBundle workBundle; sp<Component> strongComponent = mComponent.promote(); sp<Component> strongComponent = mComponent.promote(); beginTransferBufferQueueBlocks(c2workItems, true); if (!objcpy(&workBundle, c2workItems, strongComponent ? if (!objcpy(&workBundle, c2workItems, strongComponent ? &strongComponent->mBufferPoolSender : nullptr)) { &strongComponent->mBufferPoolSender : nullptr)) { LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " << "received corrupted work items."; << "received corrupted work items."; endTransferBufferQueueBlocks(c2workItems, false, true); return; return; } } Return<void> transStatus = listener->onWorkDone(workBundle); Return<void> transStatus = listener->onWorkDone(workBundle); if (!transStatus.isOk()) { if (!transStatus.isOk()) { LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " << "transaction failed."; << "transaction failed."; endTransferBufferQueueBlocks(c2workItems, false, true); return; return; } } yieldBufferQueueBlocks(c2workItems, true); endTransferBufferQueueBlocks(c2workItems, true, true); } } } } Loading Loading @@ -254,13 +257,14 @@ Return<void> Component::flush(flush_cb _hidl_cb) { WorkBundle flushedWorkBundle; WorkBundle flushedWorkBundle; Status res = static_cast<Status>(c2res); Status res = static_cast<Status>(c2res); beginTransferBufferQueueBlocks(c2flushedWorks, true); if (c2res == C2_OK) { if (c2res == C2_OK) { if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) { if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) { res = Status::CORRUPTED; res = Status::CORRUPTED; } } } } _hidl_cb(res, flushedWorkBundle); _hidl_cb(res, flushedWorkBundle); yieldBufferQueueBlocks(c2flushedWorks, true); endTransferBufferQueueBlocks(c2flushedWorks, true, true); return Void(); return Void(); } } Loading media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h +30 −50 Original line number Original line Diff line number Diff line Loading @@ -28,62 +28,42 @@ namespace c2 { namespace V1_0 { namespace V1_0 { namespace utils { namespace utils { // BufferQueue-Based Block Operations // BufferQueue-Based Block Operations // ================================== // ================================== // Create a GraphicBuffer object from a graphic block and attach it to an // Manage BufferQueue and graphic blocks for both component and codec. // IGraphicBufferProducer. // Manage graphic blocks ownership consistently during surface change. status_t attachToBufferQueue(const C2ConstGraphicBlock& block, struct OutputBufferQueue { const sp<IGraphicBufferProducer>& igbp, OutputBufferQueue(); ~OutputBufferQueue(); // 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, uint32_t generation, int32_t* bqSlot); uint64_t bqId); // Return false if block does not come from a bufferqueue-based blockpool. // Render a graphic block to current surface. // Otherwise, extract generation, bqId and bqSlot and return true. status_t outputBuffer( bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, const C2ConstGraphicBlock& block, uint32_t* generation, const BnGraphicBufferProducer::QueueBufferInput& input, uint64_t* bqId, BnGraphicBufferProducer::QueueBufferOutput* output); int32_t* bqSlot); // Assign the given block to a bufferqueue so that when the block is destroyed, // Call holdBufferQueueBlock() on output blocks in the given workList. // cancelBuffer() will be called. // The OutputBufferQueue will take the ownership of output blocks. // // 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 // Note: This function should be called after WorkBundle has been received // given one, the block will be reassigned to the given bufferqueue. This // from another process. // will call attachBuffer() on the given igbp. The function then returns true on void holdBufferQueueBlocks( // success or false on any failure during the operation. const std::list<std::unique_ptr<C2Work>>& workList); // // 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); // Call holdBufferQueueBlock() on input or output blocks in the given workList. private: // Since the bufferqueue assignment for input and output buffers can be // different, this function takes forInput to determine whether the given class Impl; // bufferqueue is for input buffers or output buffers. (The default value of std::unique_ptr<Impl> mImpl; // 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); } // namespace utils } // namespace utils } // namespace V1_0 } // namespace V1_0 Loading media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h +38 −18 Original line number Original line Diff line number Diff line Loading @@ -299,27 +299,47 @@ c2_status_t toC2Status(::android::hardware::media::bufferpool::V2_0:: // BufferQueue-Based Block Operations // BufferQueue-Based Block Operations // ================================== // ================================== // Disassociate the given block with its designated bufferqueue so that // Call before transferring block to other processes. // 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. // // // Note: This function should be called after attachBuffer() or queueBuffer() is // The given block is ready to transfer to other processes. This will guarantee // called manually. // the given block data is not mutated by bufferqueue migration. bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block); bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block); // Call yieldBufferQueueBlock() on blocks in the given workList. processInput // Call beginTransferBufferQueueBlock() on blocks in the given workList. // determines whether input blocks are yielded. processOutput works similarly on // processInput determines whether input blocks are yielded. processOutput // output blocks. (The default value of processInput is false while the default // works similarly on output blocks. (The default value of processInput is // value of processOutput is true. This implies that in most cases, only output // false while the default value of processOutput is true. This implies that in // buffers contain bufferqueue-based blocks.) // 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 // The transfer of given block is finished. If transfer is successful the given // sent over the Treble boundary to another process. // block is not owned by process anymore. Since transfer is finished the given void yieldBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList, // 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 processInput = false, bool processOutput = true); 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 utils } // namespace V1_0 } // namespace V1_0 } // namespace c2 } // namespace c2 Loading media/codec2/hidl/1.0/utils/types.cpp +46 −5 Original line number Original line Diff line number Diff line Loading @@ -737,7 +737,15 @@ bool addBaseBlock( bufferPoolSender, baseBlocks, baseBlockIndices); bufferPoolSender, baseBlocks, baseBlockIndices); } } case _C2BlockPoolData::TYPE_BUFFERQUEUE: 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( return _addBaseBlock( index, handle, index, handle, baseBlocks, baseBlockIndices); baseBlocks, baseBlockIndices); Loading Loading @@ -1772,20 +1780,53 @@ void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList, } // unnamed namespace } // unnamed namespace bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block) { bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block) { std::shared_ptr<_C2BlockPoolData> data = std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(block); _C2BlockFactory::GetGraphicBlockPoolData(block); if (data && _C2BlockFactory::GetBufferQueueData(data)) { if (data && _C2BlockFactory::GetBufferQueueData(data)) { _C2BlockFactory::YieldBlockToBufferQueue(data); _C2BlockFactory::BeginTransferBlockToClient(data); return true; return true; } } return false; return false; } } void yieldBufferQueueBlocks( void beginTransferBufferQueueBlocks( const std::list<std::unique_ptr<C2Work>>& workList, const std::list<std::unique_ptr<C2Work>>& workList, bool processInput, bool processOutput) { 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 } // namespace utils Loading Loading
media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp +204 −65 Original line number Original line Diff line number Diff line Loading @@ -82,21 +82,14 @@ void forEachBlock(C2FrameData& frameData, template <typename BlockProcessor> template <typename BlockProcessor> void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList, void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList, BlockProcessor process, BlockProcessor process) { bool processInput, bool processOutput) { for (const std::unique_ptr<C2Work>& work : workList) { for (const std::unique_ptr<C2Work>& work : workList) { if (!work) { if (!work) { continue; continue; } } if (processInput) { forEachBlock(work->input, process); } if (processOutput) { for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) { for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) { if (worklet) { if (worklet) { forEachBlock(worklet->output, forEachBlock(worklet->output, process); process); } } } } } } } Loading @@ -109,8 +102,6 @@ sp<HGraphicBufferProducer> getHgbp(const sp<IGraphicBufferProducer>& igbp) { new B2HGraphicBufferProducer(igbp); new B2HGraphicBufferProducer(igbp); } } } // unnamed namespace status_t attachToBufferQueue(const C2ConstGraphicBlock& block, status_t attachToBufferQueue(const C2ConstGraphicBlock& block, const sp<IGraphicBufferProducer>& igbp, const sp<IGraphicBufferProducer>& igbp, uint32_t generation, uint32_t generation, Loading Loading @@ -154,73 +145,221 @@ bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, _C2BlockFactory::GetGraphicBlockPoolData(block), _C2BlockFactory::GetGraphicBlockPoolData(block), generation, bqId, bqSlot); generation, bqId, bqSlot); } } } // unnamed namespace bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, class OutputBufferQueue::Impl { const sp<IGraphicBufferProducer>& igbp, std::mutex mMutex; uint64_t bqId, sp<IGraphicBufferProducer> mIgbp; uint32_t generation) { 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]; public: Impl(): mGeneration(0), mBqId(0) {} 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; } bool registerBuffer(const C2ConstGraphicBlock& block) { std::shared_ptr<_C2BlockPoolData> data = std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(block); _C2BlockFactory::GetGraphicBlockPoolData(block); if (!data) { if (!data) { return false; return false; } } std::scoped_lock<std::mutex> l(mMutex); if (!mIgbp) { return false; } uint32_t oldGeneration; uint32_t oldGeneration; uint64_t oldId; uint64_t oldId; int32_t oldSlot; int32_t oldSlot; // If the block is not bufferqueue-based, do nothing. // If the block is not bufferqueue-based, do nothing. if (!_C2BlockFactory::GetBufferQueueData( if (!_C2BlockFactory::GetBufferQueueData( data, &oldGeneration, &oldId, &oldSlot) || data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) { (oldId == 0)) { return false; return false; } } // If the block's bqId is the same as the desired bqId, just hold. // If the block's bqId is the same as the desired bqId, just hold. if ((oldId == bqId) && (oldGeneration == generation)) { if ((oldId == mBqId) && (oldGeneration == mGeneration)) { LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" << " bqId " << oldId << " bqId " << oldId << ", bqSlot " << oldSlot << ", bqSlot " << oldSlot << ", generation " << generation << ", generation " << mGeneration << "."; << "."; _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp)); _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp)); mPoolDatas[oldSlot] = data; mBuffers[oldSlot] = createGraphicBuffer(block); mBuffers[oldSlot]->setGenerationNumber(mGeneration); return true; return true; } } int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration; // Otherwise, attach to the given igbp, which must not be null. LOG(WARNING) << "receiving stale buffer: generation " if (!igbp) { << mGeneration << " , diff " << d << " : slot " << oldSlot; return false; return false; } } status_t outputBuffer( const C2ConstGraphicBlock& block, const BnGraphicBufferProducer::QueueBufferInput& input, BnGraphicBufferProducer::QueueBufferOutput* output) { uint32_t generation; uint64_t bqId; int32_t bqSlot; int32_t bqSlot; status_t result = attachToBufferQueue(block, igbp, generation, &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) { mMutex.lock(); LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:" sp<IGraphicBufferProducer> outputIgbp = mIgbp; << " target bqId " << bqId uint32_t outputGeneration = mGeneration; << ", generation " << generation mMutex.unlock(); << "."; return false; status_t status = attachToBufferQueue( block, outputIgbp, outputGeneration, &bqSlot); if (status != OK) { LOG(WARNING) << "outputBuffer -- attaching failed."; return INVALID_OPERATION; } } LOG(VERBOSE) << "holdBufferQueueBlock -- attached:" status = outputIgbp->queueBuffer(static_cast<int>(bqSlot), << " bqId " << bqId input, output); << ", bqSlot " << bqSlot if (status != OK) { << ", generation " << generation LOG(ERROR) << "outputBuffer -- queueBuffer() failed " << "."; "on non-bufferqueue-based block. " _C2BlockFactory::AssignBlockToBufferQueue( "Error = " << status << "."; data, getHgbp(igbp), generation, bqId, bqSlot, true); return status; return true; } return OK; } } void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList, mMutex.lock(); const sp<IGraphicBufferProducer>& igbp, sp<IGraphicBufferProducer> outputIgbp = mIgbp; uint64_t bqId, 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; } Impl *getPtr() { return this; } ~Impl() {} }; OutputBufferQueue::OutputBufferQueue(): mImpl(new Impl()) {} OutputBufferQueue::~OutputBufferQueue() {} bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp, uint32_t generation, uint32_t generation, bool forInput) { uint64_t bqId) { return mImpl && mImpl->configure(igbp, generation, bqId); } 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, forEachBlock(workList, std::bind(holdBufferQueueBlock, std::bind(&OutputBufferQueue::Impl::registerBuffer, std::placeholders::_1, igbp, bqId, generation), mImpl->getPtr(), std::placeholders::_1)); forInput, !forInput); } } } // namespace utils } // namespace utils Loading
media/codec2/hidl/1.0/utils/Component.cpp +6 −2 Original line number Original line Diff line number Diff line Loading @@ -107,19 +107,22 @@ struct Component::Listener : public C2Component::Listener { WorkBundle workBundle; WorkBundle workBundle; sp<Component> strongComponent = mComponent.promote(); sp<Component> strongComponent = mComponent.promote(); beginTransferBufferQueueBlocks(c2workItems, true); if (!objcpy(&workBundle, c2workItems, strongComponent ? if (!objcpy(&workBundle, c2workItems, strongComponent ? &strongComponent->mBufferPoolSender : nullptr)) { &strongComponent->mBufferPoolSender : nullptr)) { LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " << "received corrupted work items."; << "received corrupted work items."; endTransferBufferQueueBlocks(c2workItems, false, true); return; return; } } Return<void> transStatus = listener->onWorkDone(workBundle); Return<void> transStatus = listener->onWorkDone(workBundle); if (!transStatus.isOk()) { if (!transStatus.isOk()) { LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " << "transaction failed."; << "transaction failed."; endTransferBufferQueueBlocks(c2workItems, false, true); return; return; } } yieldBufferQueueBlocks(c2workItems, true); endTransferBufferQueueBlocks(c2workItems, true, true); } } } } Loading Loading @@ -254,13 +257,14 @@ Return<void> Component::flush(flush_cb _hidl_cb) { WorkBundle flushedWorkBundle; WorkBundle flushedWorkBundle; Status res = static_cast<Status>(c2res); Status res = static_cast<Status>(c2res); beginTransferBufferQueueBlocks(c2flushedWorks, true); if (c2res == C2_OK) { if (c2res == C2_OK) { if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) { if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) { res = Status::CORRUPTED; res = Status::CORRUPTED; } } } } _hidl_cb(res, flushedWorkBundle); _hidl_cb(res, flushedWorkBundle); yieldBufferQueueBlocks(c2flushedWorks, true); endTransferBufferQueueBlocks(c2flushedWorks, true, true); return Void(); return Void(); } } Loading
media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h +30 −50 Original line number Original line Diff line number Diff line Loading @@ -28,62 +28,42 @@ namespace c2 { namespace V1_0 { namespace V1_0 { namespace utils { namespace utils { // BufferQueue-Based Block Operations // BufferQueue-Based Block Operations // ================================== // ================================== // Create a GraphicBuffer object from a graphic block and attach it to an // Manage BufferQueue and graphic blocks for both component and codec. // IGraphicBufferProducer. // Manage graphic blocks ownership consistently during surface change. status_t attachToBufferQueue(const C2ConstGraphicBlock& block, struct OutputBufferQueue { const sp<IGraphicBufferProducer>& igbp, OutputBufferQueue(); ~OutputBufferQueue(); // 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, uint32_t generation, int32_t* bqSlot); uint64_t bqId); // Return false if block does not come from a bufferqueue-based blockpool. // Render a graphic block to current surface. // Otherwise, extract generation, bqId and bqSlot and return true. status_t outputBuffer( bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, const C2ConstGraphicBlock& block, uint32_t* generation, const BnGraphicBufferProducer::QueueBufferInput& input, uint64_t* bqId, BnGraphicBufferProducer::QueueBufferOutput* output); int32_t* bqSlot); // Assign the given block to a bufferqueue so that when the block is destroyed, // Call holdBufferQueueBlock() on output blocks in the given workList. // cancelBuffer() will be called. // The OutputBufferQueue will take the ownership of output blocks. // // 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 // Note: This function should be called after WorkBundle has been received // given one, the block will be reassigned to the given bufferqueue. This // from another process. // will call attachBuffer() on the given igbp. The function then returns true on void holdBufferQueueBlocks( // success or false on any failure during the operation. const std::list<std::unique_ptr<C2Work>>& workList); // // 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); // Call holdBufferQueueBlock() on input or output blocks in the given workList. private: // Since the bufferqueue assignment for input and output buffers can be // different, this function takes forInput to determine whether the given class Impl; // bufferqueue is for input buffers or output buffers. (The default value of std::unique_ptr<Impl> mImpl; // 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); } // namespace utils } // namespace utils } // namespace V1_0 } // namespace V1_0 Loading
media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h +38 −18 Original line number Original line Diff line number Diff line Loading @@ -299,27 +299,47 @@ c2_status_t toC2Status(::android::hardware::media::bufferpool::V2_0:: // BufferQueue-Based Block Operations // BufferQueue-Based Block Operations // ================================== // ================================== // Disassociate the given block with its designated bufferqueue so that // Call before transferring block to other processes. // 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. // // // Note: This function should be called after attachBuffer() or queueBuffer() is // The given block is ready to transfer to other processes. This will guarantee // called manually. // the given block data is not mutated by bufferqueue migration. bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block); bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block); // Call yieldBufferQueueBlock() on blocks in the given workList. processInput // Call beginTransferBufferQueueBlock() on blocks in the given workList. // determines whether input blocks are yielded. processOutput works similarly on // processInput determines whether input blocks are yielded. processOutput // output blocks. (The default value of processInput is false while the default // works similarly on output blocks. (The default value of processInput is // value of processOutput is true. This implies that in most cases, only output // false while the default value of processOutput is true. This implies that in // buffers contain bufferqueue-based blocks.) // 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 // The transfer of given block is finished. If transfer is successful the given // sent over the Treble boundary to another process. // block is not owned by process anymore. Since transfer is finished the given void yieldBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList, // 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 processInput = false, bool processOutput = true); 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 utils } // namespace V1_0 } // namespace V1_0 } // namespace c2 } // namespace c2 Loading
media/codec2/hidl/1.0/utils/types.cpp +46 −5 Original line number Original line Diff line number Diff line Loading @@ -737,7 +737,15 @@ bool addBaseBlock( bufferPoolSender, baseBlocks, baseBlockIndices); bufferPoolSender, baseBlocks, baseBlockIndices); } } case _C2BlockPoolData::TYPE_BUFFERQUEUE: 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( return _addBaseBlock( index, handle, index, handle, baseBlocks, baseBlockIndices); baseBlocks, baseBlockIndices); Loading Loading @@ -1772,20 +1780,53 @@ void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList, } // unnamed namespace } // unnamed namespace bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block) { bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block) { std::shared_ptr<_C2BlockPoolData> data = std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(block); _C2BlockFactory::GetGraphicBlockPoolData(block); if (data && _C2BlockFactory::GetBufferQueueData(data)) { if (data && _C2BlockFactory::GetBufferQueueData(data)) { _C2BlockFactory::YieldBlockToBufferQueue(data); _C2BlockFactory::BeginTransferBlockToClient(data); return true; return true; } } return false; return false; } } void yieldBufferQueueBlocks( void beginTransferBufferQueueBlocks( const std::list<std::unique_ptr<C2Work>>& workList, const std::list<std::unique_ptr<C2Work>>& workList, bool processInput, bool processOutput) { 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 } // namespace utils Loading