1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "Codec2-OutputBufferQueue"
19 #define ATRACE_TAG  ATRACE_TAG_VIDEO
20 #include <android-base/logging.h>
21 #include <utils/Trace.h>
22 
23 #include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
24 #include <codec2/hidl/output.h>
25 #include <cutils/ashmem.h>
26 #include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
27 #include <sys/mman.h>
28 
29 #include <C2AllocatorGralloc.h>
30 #include <C2BlockInternal.h>
31 #include <C2Buffer.h>
32 #include <C2PlatformSupport.h>
33 #include <C2SurfaceSyncObj.h>
34 
35 #include <iomanip>
36 
37 namespace android {
38 namespace hardware {
39 namespace media {
40 namespace c2 {
41 
42 using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
43         V2_0::IGraphicBufferProducer;
44 using B2HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
45         V2_0::utils::B2HGraphicBufferProducer;
46 
47 namespace /* unnamed */ {
48 
49 // Create a GraphicBuffer object from a graphic block.
createGraphicBuffer(const C2ConstGraphicBlock & block)50 sp<GraphicBuffer> createGraphicBuffer(const C2ConstGraphicBlock& block) {
51     uint32_t width;
52     uint32_t height;
53     uint32_t format;
54     uint64_t usage;
55     uint32_t stride;
56     uint32_t generation;
57     uint64_t bqId;
58     int32_t bqSlot;
59     _UnwrapNativeCodec2GrallocMetadata(
60             block.handle(), &width, &height, &format, &usage,
61             &stride, &generation, &bqId, reinterpret_cast<uint32_t*>(&bqSlot));
62     native_handle_t *grallocHandle =
63             UnwrapNativeCodec2GrallocHandle(block.handle());
64     sp<GraphicBuffer> graphicBuffer =
65             new GraphicBuffer(grallocHandle,
66                               GraphicBuffer::CLONE_HANDLE,
67                               width, height, format,
68                               1, usage, stride);
69     native_handle_delete(grallocHandle);
70     return graphicBuffer;
71 }
72 
73 template <typename BlockProcessor>
forEachBlock(C2FrameData & frameData,BlockProcessor process)74 void forEachBlock(C2FrameData& frameData,
75                   BlockProcessor process) {
76     for (const std::shared_ptr<C2Buffer>& buffer : frameData.buffers) {
77         if (buffer) {
78             for (const C2ConstGraphicBlock& block :
79                     buffer->data().graphicBlocks()) {
80                 process(block);
81             }
82         }
83     }
84 }
85 
86 template <typename BlockProcessor>
forEachBlock(const std::list<std::unique_ptr<C2Work>> & workList,BlockProcessor process)87 void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
88                   BlockProcessor process) {
89     for (const std::unique_ptr<C2Work>& work : workList) {
90         if (!work) {
91             continue;
92         }
93         for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
94             if (worklet) {
95                 forEachBlock(worklet->output, process);
96             }
97         }
98     }
99 }
100 
getHgbp(const sp<IGraphicBufferProducer> & igbp)101 sp<HGraphicBufferProducer> getHgbp(const sp<IGraphicBufferProducer>& igbp) {
102     sp<HGraphicBufferProducer> hgbp =
103             igbp->getHalInterface<HGraphicBufferProducer>();
104     return hgbp ? hgbp :
105             new B2HGraphicBufferProducer(igbp);
106 }
107 
attachToBufferQueue(const C2ConstGraphicBlock & block,const sp<IGraphicBufferProducer> & igbp,uint32_t generation,int32_t * bqSlot,std::shared_ptr<C2SurfaceSyncMemory> syncMem)108 status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
109                              const sp<IGraphicBufferProducer>& igbp,
110                              uint32_t generation,
111                              int32_t* bqSlot,
112                              std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
113     if (!igbp) {
114         LOG(WARNING) << "attachToBufferQueue -- null producer.";
115         return NO_INIT;
116     }
117 
118     sp<GraphicBuffer> graphicBuffer = createGraphicBuffer(block);
119     graphicBuffer->setGenerationNumber(generation);
120 
121     LOG(VERBOSE) << "attachToBufferQueue -- attaching buffer:"
122             << " block dimension " << block.width() << "x"
123                                    << block.height()
124             << ", graphicBuffer dimension " << graphicBuffer->getWidth() << "x"
125                                            << graphicBuffer->getHeight()
126             << std::hex << std::setfill('0')
127             << ", format 0x" << std::setw(8) << graphicBuffer->getPixelFormat()
128             << ", usage 0x" << std::setw(16) << graphicBuffer->getUsage()
129             << std::dec << std::setfill(' ')
130             << ", stride " << graphicBuffer->getStride()
131             << ", generation " << graphicBuffer->getGenerationNumber();
132 
133     C2SyncVariables *syncVar = syncMem ? syncMem->mem() : nullptr;
134     status_t result = OK;
135     if (syncVar) {
136         syncVar->lock();
137         if (!syncVar->isDequeueableLocked() ||
138             syncVar->getSyncStatusLocked() == C2SyncVariables::STATUS_SWITCHING) {
139             syncVar->unlock();
140             LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
141                             "status = " << INVALID_OPERATION << ".";
142             return INVALID_OPERATION;
143         }
144         syncVar->notifyDequeuedLocked();
145         syncVar->unlock();
146         result = igbp->attachBuffer(bqSlot, graphicBuffer);
147         if (result != OK) {
148             syncVar->lock();
149             syncVar->notifyQueuedLocked();
150             syncVar->unlock();
151         }
152     } else {
153         result = igbp->attachBuffer(bqSlot, graphicBuffer);
154     }
155     if (result != OK) {
156         LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
157                         "status = " << result << ".";
158         return result;
159     }
160     LOG(VERBOSE) << "attachToBufferQueue -- attachBuffer returned slot #"
161                  << *bqSlot << ".";
162     return OK;
163 }
164 
getBufferQueueAssignment(const C2ConstGraphicBlock & block,uint32_t * generation,uint64_t * bqId,int32_t * bqSlot)165 bool getBufferQueueAssignment(const C2ConstGraphicBlock& block,
166                               uint32_t* generation,
167                               uint64_t* bqId,
168                               int32_t* bqSlot) {
169     return _C2BlockFactory::GetBufferQueueData(
170             _C2BlockFactory::GetGraphicBlockPoolData(block),
171             generation, bqId, bqSlot);
172 }
173 
174 } // unnamed namespace
175 
OutputBufferQueue()176 OutputBufferQueue::OutputBufferQueue()
177       : mGeneration{0}, mBqId{0}, mStopped{false} {
178 }
179 
~OutputBufferQueue()180 OutputBufferQueue::~OutputBufferQueue() {
181 }
182 
configure(const sp<IGraphicBufferProducer> & igbp,uint32_t generation,uint64_t bqId,int maxDequeueBufferCount,std::shared_ptr<V1_2::SurfaceSyncObj> * syncObj)183 bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp,
184                                   uint32_t generation,
185                                   uint64_t bqId,
186                                   int maxDequeueBufferCount,
187                                   std::shared_ptr<V1_2::SurfaceSyncObj> *syncObj) {
188     uint64_t consumerUsage = 0;
189     if (igbp && igbp->getConsumerUsage(&consumerUsage) != OK) {
190         ALOGW("failed to get consumer usage");
191     }
192 
193     // TODO : Abstract creation process into C2SurfaceSyncMemory class.
194     // use C2LinearBlock instead ashmem.
195     std::shared_ptr<C2SurfaceSyncMemory> syncMem;
196     if (syncObj && igbp) {
197         bool mapped = false;
198         int memFd = ashmem_create_region("C2SurfaceMem", sizeof(C2SyncVariables));
199         size_t memSize = memFd < 0 ? 0 : ashmem_get_size_region(memFd);
200         if (memSize > 0) {
201             syncMem = C2SurfaceSyncMemory::Create(memFd, memSize);
202             if (syncMem) {
203                 mapped = true;
204                 *syncObj = std::make_shared<V1_2::SurfaceSyncObj>();
205                 (*syncObj)->syncMemory = syncMem->handle();
206                 (*syncObj)->bqId = bqId;
207                 (*syncObj)->generationId = generation;
208                 (*syncObj)->consumerUsage = consumerUsage;
209                 ALOGD("C2SurfaceSyncMemory created %zu(%zu)", sizeof(C2SyncVariables), memSize);
210             }
211         }
212         if (!mapped) {
213             if (memFd >= 0) {
214                 ::close(memFd);
215             }
216             ALOGW("SurfaceSyncObj creation failure");
217         }
218     }
219 
220     size_t tryNum = 0;
221     size_t success = 0;
222     sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
223     std::weak_ptr<_C2BlockPoolData>
224             poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
225     std::shared_ptr<C2SurfaceSyncMemory> oldMem;
226     {
227         std::scoped_lock<std::mutex> l(mMutex);
228         bool stopped = mStopped;
229         mStopped = false;
230         if (generation == mGeneration) {
231             // case of old BlockPool destruction
232             C2SyncVariables *var = mSyncMem ? mSyncMem->mem() : nullptr;
233             if (syncObj && var) {
234                 *syncObj = std::make_shared<V1_2::SurfaceSyncObj>();
235                 (*syncObj)->bqId = bqId;
236                 (*syncObj)->syncMemory = mSyncMem->handle();
237                 (*syncObj)->generationId = generation;
238                 (*syncObj)->consumerUsage = consumerUsage;
239                 mMaxDequeueBufferCount = maxDequeueBufferCount;
240                 var->lock();
241                 var->setSyncStatusLocked(C2SyncVariables::STATUS_INIT);
242                 var->setInitialDequeueCountLocked(mMaxDequeueBufferCount, 0);
243                 var->unlock();
244             }
245             return false;
246         }
247         oldMem = mSyncMem;
248         C2SyncVariables *oldSync = mSyncMem ? mSyncMem->mem() : nullptr;
249         if (oldSync) {
250             oldSync->lock();
251             oldSync->setSyncStatusLocked(C2SyncVariables::STATUS_SWITCHING);
252             oldSync->unlock();
253         }
254         mSyncMem.reset();
255         if (syncMem) {
256             mSyncMem = syncMem;
257         }
258         C2SyncVariables *newSync = mSyncMem ? mSyncMem->mem() : nullptr;
259 
260         mIgbp = igbp;
261         mGeneration = generation;
262         mBqId = bqId;
263         mOwner = std::make_shared<int>(0);
264         mMaxDequeueBufferCount = maxDequeueBufferCount;
265         if (igbp == nullptr) {
266             return false;
267         }
268         for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
269             if (mBqId == 0 || !mBuffers[i] || stopped) {
270                 continue;
271             }
272             std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock();
273             if (!data ||
274                 !_C2BlockFactory::BeginAttachBlockToBufferQueue(data)) {
275                 continue;
276             }
277             ++tryNum;
278             int bqSlot;
279 
280             // Update buffer's generation and usage.
281             if ((mBuffers[i]->getUsage() & consumerUsage) != consumerUsage) {
282                 mBuffers[i] = new GraphicBuffer(
283                     mBuffers[i]->handle, GraphicBuffer::CLONE_HANDLE,
284                     mBuffers[i]->width, mBuffers[i]->height,
285                     mBuffers[i]->format, mBuffers[i]->layerCount,
286                     mBuffers[i]->getUsage() | consumerUsage,
287                     mBuffers[i]->stride);
288                 if (mBuffers[i]->initCheck() != OK) {
289                     ALOGW("%s() failed to update usage, original usage=%" PRIx64
290                           ", consumer usage=%" PRIx64,
291                           __func__, mBuffers[i]->getUsage(), consumerUsage);
292                     continue;
293                 }
294             }
295             mBuffers[i]->setGenerationNumber(generation);
296 
297             status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]);
298             if (result != OK) {
299                 continue;
300             }
301             bool attach =
302                     _C2BlockFactory::EndAttachBlockToBufferQueue(
303                             data, mOwner, getHgbp(mIgbp), mSyncMem,
304                             generation, bqId, bqSlot);
305             if (!attach) {
306                 igbp->cancelBuffer(bqSlot, Fence::NO_FENCE);
307                 continue;
308             }
309             buffers[bqSlot] = mBuffers[i];
310             poolDatas[bqSlot] = data;
311             ++success;
312         }
313         for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
314             mBuffers[i] = buffers[i];
315             mPoolDatas[i] = poolDatas[i];
316         }
317         if (newSync) {
318             newSync->lock();
319             newSync->setInitialDequeueCountLocked(mMaxDequeueBufferCount, success);
320             newSync->unlock();
321         }
322     }
323     {
324         std::scoped_lock<std::mutex> l(mOldMutex);
325         mOldMem = oldMem;
326     }
327     ALOGD("remote graphic buffer migration %zu/%zu",
328           success, tryNum);
329     return true;
330 }
331 
expireOldWaiters()332 void OutputBufferQueue::expireOldWaiters() {
333     std::scoped_lock<std::mutex> l(mOldMutex);
334     if (mOldMem) {
335         C2SyncVariables *oldSync = mOldMem->mem();
336         if (oldSync) {
337             oldSync->notifyAll();
338         }
339         mOldMem.reset();
340     }
341 }
342 
stop()343 void OutputBufferQueue::stop() {
344     std::shared_ptr<C2SurfaceSyncMemory> oldMem;
345     {
346         std::scoped_lock<std::mutex> l(mMutex);
347         if (mStopped) {
348             return;
349         }
350         mStopped = true;
351         mOwner.reset(); // destructor of the block will not trigger IGBP::cancel()
352         // basically configuring null surface
353         oldMem = mSyncMem;
354         mSyncMem.reset();
355         mIgbp.clear();
356         mGeneration = 0;
357         mBqId = 0;
358     }
359     {
360         std::scoped_lock<std::mutex> l(mOldMutex);
361         mOldMem = oldMem;
362     }
363 }
364 
registerBuffer(const C2ConstGraphicBlock & block)365 bool OutputBufferQueue::registerBuffer(const C2ConstGraphicBlock& block) {
366     std::shared_ptr<_C2BlockPoolData> data =
367             _C2BlockFactory::GetGraphicBlockPoolData(block);
368     if (!data) {
369         return false;
370     }
371     std::scoped_lock<std::mutex> l(mMutex);
372 
373     if (!mIgbp || mStopped) {
374         return false;
375     }
376 
377     uint32_t oldGeneration;
378     uint64_t oldId;
379     int32_t oldSlot;
380     // If the block is not bufferqueue-based, do nothing.
381     if (!_C2BlockFactory::GetBufferQueueData(
382             data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) {
383         return false;
384     }
385     // If the block's bqId is the same as the desired bqId, just hold.
386     if ((oldId == mBqId) && (oldGeneration == mGeneration)) {
387         LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
388                      << " bqId " << oldId
389                      << ", bqSlot " << oldSlot
390                      << ", generation " << mGeneration
391                      << ".";
392         _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp), mSyncMem);
393         mPoolDatas[oldSlot] = data;
394         mBuffers[oldSlot] = createGraphicBuffer(block);
395         mBuffers[oldSlot]->setGenerationNumber(mGeneration);
396         return true;
397     }
398     int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration;
399     LOG(WARNING) << "receiving stale buffer: generation "
400                  << mGeneration << " , diff " << d  << " : slot "
401                  << oldSlot;
402     return false;
403 }
404 
outputBuffer(const C2ConstGraphicBlock & block,const BnGraphicBufferProducer::QueueBufferInput & input,BnGraphicBufferProducer::QueueBufferOutput * output)405 status_t OutputBufferQueue::outputBuffer(
406         const C2ConstGraphicBlock& block,
407         const BnGraphicBufferProducer::QueueBufferInput& input,
408         BnGraphicBufferProducer::QueueBufferOutput* output) {
409     uint32_t generation;
410     uint64_t bqId;
411     int32_t bqSlot;
412     ScopedTrace trace(ATRACE_TAG,"Codec2-OutputBufferQueue::outputBuffer");
413     bool display = V1_0::utils::displayBufferQueueBlock(block);
414     if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
415         bqId == 0) {
416         // Block not from bufferqueue -- it must be attached before queuing.
417 
418         std::shared_ptr<C2SurfaceSyncMemory> syncMem;
419         mMutex.lock();
420         bool stopped = mStopped;
421         sp<IGraphicBufferProducer> outputIgbp = mIgbp;
422         uint32_t outputGeneration = mGeneration;
423         syncMem = mSyncMem;
424         mMutex.unlock();
425 
426         if (stopped) {
427             LOG(INFO) << "outputBuffer -- already stopped.";
428             return DEAD_OBJECT;
429         }
430 
431         status_t status = attachToBufferQueue(
432                 block, outputIgbp, outputGeneration, &bqSlot, syncMem);
433 
434         if (status != OK) {
435             LOG(WARNING) << "outputBuffer -- attaching failed.";
436             return INVALID_OPERATION;
437         }
438 
439         auto syncVar = syncMem ? syncMem->mem() : nullptr;
440         if(syncVar) {
441             status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
442                                          input, output);
443             if (status == OK) {
444                 if (output->bufferReplaced) {
445                     syncVar->lock();
446                     syncVar->notifyQueuedLocked();
447                     syncVar->unlock();
448                 }
449             }
450         } else {
451             status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
452                                          input, output);
453         }
454         if (status != OK) {
455             LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
456                        "on non-bufferqueue-based block. "
457                        "Error = " << status << ".";
458             return status;
459         }
460         return OK;
461     }
462 
463     std::shared_ptr<C2SurfaceSyncMemory> syncMem;
464     mMutex.lock();
465     bool stopped = mStopped;
466     sp<IGraphicBufferProducer> outputIgbp = mIgbp;
467     uint32_t outputGeneration = mGeneration;
468     uint64_t outputBqId = mBqId;
469     syncMem = mSyncMem;
470     mMutex.unlock();
471 
472     if (stopped) {
473         LOG(INFO) << "outputBuffer -- already stopped.";
474         return DEAD_OBJECT;
475     }
476 
477     if (!outputIgbp) {
478         LOG(VERBOSE) << "outputBuffer -- output surface is null.";
479         return NO_INIT;
480     }
481 
482     if (!display) {
483         LOG(WARNING) << "outputBuffer -- cannot display "
484                      "bufferqueue-based block to the bufferqueue.";
485         return UNKNOWN_ERROR;
486     }
487     if (bqId != outputBqId || generation != outputGeneration) {
488         int32_t diff = (int32_t) outputGeneration - (int32_t) generation;
489         LOG(WARNING) << "outputBuffer -- buffers from old generation to "
490                      << outputGeneration << " , diff: " << diff
491                      << " , slot: " << bqSlot;
492         return DEAD_OBJECT;
493     }
494 
495     auto syncVar = syncMem ? syncMem->mem() : nullptr;
496     status_t status = OK;
497     if (syncVar) {
498         status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
499                                                   input, output);
500         if (status == OK) {
501             if (output->bufferReplaced) {
502                 syncVar->lock();
503                 syncVar->notifyQueuedLocked();
504                 syncVar->unlock();
505             }
506         }
507     } else {
508         status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
509                                                   input, output);
510     }
511 
512     if (status != OK) {
513         LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
514                    "on bufferqueue-based block. "
515                    "Error = " << status << ".";
516         return status;
517     }
518     return OK;
519 }
520 
onBufferReleased(uint32_t generation)521 void OutputBufferQueue::onBufferReleased(uint32_t generation) {
522     std::shared_ptr<C2SurfaceSyncMemory> syncMem;
523     sp<IGraphicBufferProducer> outputIgbp;
524     uint32_t outputGeneration = 0;
525     {
526         std::unique_lock<std::mutex> l(mMutex);
527         if (mStopped) {
528             return;
529         }
530         outputIgbp = mIgbp;
531         outputGeneration = mGeneration;
532         syncMem = mSyncMem;
533     }
534 
535     if (outputIgbp && generation == outputGeneration) {
536         auto syncVar = syncMem ? syncMem->mem() : nullptr;
537         if (syncVar) {
538             syncVar->lock();
539             syncVar->notifyQueuedLocked();
540             syncVar->unlock();
541         }
542     }
543 }
544 
pollForRenderedFrames(FrameEventHistoryDelta * delta)545 void OutputBufferQueue::pollForRenderedFrames(FrameEventHistoryDelta* delta) {
546     if (mIgbp) {
547         mIgbp->getFrameTimestamps(delta);
548     }
549 }
550 
holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>> & workList)551 void OutputBufferQueue::holdBufferQueueBlocks(
552         const std::list<std::unique_ptr<C2Work>>& workList) {
553     forEachBlock(workList,
554                  std::bind(&OutputBufferQueue::registerBuffer,
555                            this, std::placeholders::_1));
556 }
557 
updateMaxDequeueBufferCount(int maxDequeueBufferCount)558 void OutputBufferQueue::updateMaxDequeueBufferCount(int maxDequeueBufferCount) {
559     mMutex.lock();
560     mMaxDequeueBufferCount = maxDequeueBufferCount;
561     auto syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
562     if (syncVar && !mStopped) {
563         syncVar->lock();
564         syncVar->updateMaxDequeueCountLocked(maxDequeueBufferCount);
565         syncVar->unlock();
566     }
567     mMutex.unlock();
568     ALOGD("set max dequeue count %d from update", maxDequeueBufferCount);
569 }
570 
571 }  // namespace c2
572 }  // namespace media
573 }  // namespace hardware
574 }  // namespace android
575