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 "C2VdaBqBlockPool"
19 
20 #include <errno.h>
21 
22 #include <mutex>
23 
24 #include <gui/BufferQueueDefs.h>
25 #include <utils/Log.h>
26 
27 #include <C2AllocatorGralloc.h>
28 #include <C2BlockInternal.h>
29 #include <v4l2/C2VdaBqBlockPool.h>
30 
31 using ::android::AnwBuffer;
32 using ::android::C2AndroidMemoryUsage;
33 using ::android::Fence;
34 using ::android::GraphicBuffer;
35 using ::android::HGraphicBufferProducer;
36 using ::android::hidl_handle;
37 using ::android::IGraphicBufferProducer;
38 using ::android::sp;
39 using ::android::status_t;
40 using ::android::hardware::graphics::common::V1_0::PixelFormat;
41 
42 namespace {
43 
44 // The wait time for acquire fence in milliseconds.
45 const int kFenceWaitTimeMs = 10;
46 // The timeout delay for dequeuing buffer from producer in nanoseconds.
47 const int64_t kDequeueTimeoutNs = 10 * 1000 * 1000;
48 
49 }  // namespace
50 
asC2Error(int32_t err)51 static c2_status_t asC2Error(int32_t err) {
52     switch (err) {
53     case android::NO_ERROR:
54         return C2_OK;
55     case android::NO_INIT:
56         return C2_NO_INIT;
57     case android::BAD_VALUE:
58         return C2_BAD_VALUE;
59     case android::TIMED_OUT:
60         return C2_TIMED_OUT;
61     case android::WOULD_BLOCK:
62         return C2_BLOCKING;
63     case android::NO_MEMORY:
64         return C2_NO_MEMORY;
65     case -ETIME:
66         return C2_TIMED_OUT;  // for fence wait
67     }
68     return C2_CORRUPTED;
69 }
70 
C2VdaBqBlockPool(const std::shared_ptr<C2Allocator> & allocator,const local_id_t localId)71 C2VdaBqBlockPool::C2VdaBqBlockPool(const std::shared_ptr<C2Allocator>& allocator,
72                                    const local_id_t localId)
73       : C2BufferQueueBlockPool(allocator, localId),
74         mAllocator(allocator),
75         mLocalId(localId),
76         mMaxDequeuedBuffers(0u) {}
77 
~C2VdaBqBlockPool()78 C2VdaBqBlockPool::~C2VdaBqBlockPool() {
79     std::lock_guard<std::mutex> lock(mMutex);
80     cancelAllBuffers();
81 }
82 
fetchGraphicBlock(uint32_t width,uint32_t height,uint32_t format,C2MemoryUsage usage,std::shared_ptr<C2GraphicBlock> * block)83 c2_status_t C2VdaBqBlockPool::fetchGraphicBlock(
84         uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
85         std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
86     std::lock_guard<std::mutex> lock(mMutex);
87 
88     if (!mProducer) {
89         // Producer will not be configured in byte-buffer mode. Allocate buffers from allocator
90         // directly as a basic graphic block pool.
91         std::shared_ptr<C2GraphicAllocation> alloc;
92         c2_status_t err = mAllocator->newGraphicAllocation(width, height, format, usage, &alloc);
93         if (err != C2_OK) {
94             return err;
95         }
96         *block = _C2BlockFactory::CreateGraphicBlock(alloc);
97         return C2_OK;
98     }
99 
100     sp<Fence> fence = new Fence();
101     C2AndroidMemoryUsage androidUsage = usage;
102     int32_t status;
103     PixelFormat pixelFormat = static_cast<PixelFormat>(format);
104     int32_t slot;
105 
106     mProducer->dequeueBuffer(
107             width, height, pixelFormat, androidUsage.asGrallocUsage(), true,
108             [&status, &slot, &fence](int32_t tStatus, int32_t tSlot, hidl_handle const& tFence,
109                                      HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
110                 status = tStatus;
111                 slot = tSlot;
112                 if (!android::conversion::convertTo(fence.get(), tFence) &&
113                     status == android::NO_ERROR) {
114                     status = android::BAD_VALUE;
115                 }
116                 (void)tTs;
117             });
118 
119     // check dequeueBuffer return flag
120     if (status != android::NO_ERROR &&
121         status != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
122         if (status == android::TIMED_OUT) {
123             // no buffer is available now, wait for another retry.
124             ALOGV("dequeueBuffer timed out, wait for retry...");
125             return C2_TIMED_OUT;
126         }
127         ALOGE("dequeueBuffer failed: %d", status);
128         return asC2Error(status);
129     }
130 
131     // wait for acquire fence if we get one.
132     native_handle_t* nh = nullptr;
133     hidl_handle fenceHandle;
134     if (fence) {
135         android::conversion::wrapAs(&fenceHandle, &nh, *fence);
136         status_t fenceStatus = fence->wait(kFenceWaitTimeMs);
137         if (fenceStatus != android::NO_ERROR) {
138             ALOGE("buffer fence wait error: %d", fenceStatus);
139             mProducer->cancelBuffer(slot, fenceHandle);
140             native_handle_delete(nh);
141             return asC2Error(fenceStatus);
142         }
143     }
144 
145     auto iter = mSlotAllocations.find(slot);
146     if (iter == mSlotAllocations.end()) {
147         // it's a new slot index, request for a new buffer.
148         if (mSlotAllocations.size() >= mMaxDequeuedBuffers) {
149             ALOGE("still get a new slot index but already allocated enough buffers.");
150             mProducer->cancelBuffer(slot, fenceHandle);
151             native_handle_delete(nh);
152             return C2_CORRUPTED;
153         }
154         if (status != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
155             ALOGE("expect BUFFER_NEEDS_REALLOCATION flag but didn't get one.");
156             mProducer->cancelBuffer(slot, fenceHandle);
157             native_handle_delete(nh);
158             return C2_CORRUPTED;
159         }
160         sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
161         mProducer->requestBuffer(
162                 slot, [&status, &slotBuffer](int32_t tStatus, AnwBuffer const& tBuffer) {
163                     status = tStatus;
164                     if (!android::conversion::convertTo(slotBuffer.get(), tBuffer) &&
165                         status == android::NO_ERROR) {
166                         status = android::BAD_VALUE;
167                     }
168                 });
169 
170         // check requestBuffer return flag
171         if (status != android::NO_ERROR) {
172             ALOGE("requestBuffer failed: %d", status);
173             mProducer->cancelBuffer(slot, fenceHandle);
174             native_handle_delete(nh);
175             return asC2Error(status);
176         }
177         native_handle_delete(nh);
178 
179         // convert GraphicBuffer to C2GraphicAllocation and wrap producer id and slot index
180         native_handle_t* grallocHandle = native_handle_clone(slotBuffer->handle);
181         ALOGV("buffer wraps { producer id: %" PRIu64 ", slot: %d }", mProducerId, slot);
182         C2Handle* c2Handle = android::WrapNativeCodec2GrallocHandle(
183                 grallocHandle, slotBuffer->width, slotBuffer->height, slotBuffer->format,
184                 slotBuffer->usage, slotBuffer->stride, mProducerId, slot);
185         native_handle_delete(grallocHandle);
186         if (!c2Handle) {
187             ALOGE("WrapNativeCodec2GrallocHandle failed");
188             return C2_NO_MEMORY;
189         }
190 
191         std::shared_ptr<C2GraphicAllocation> alloc;
192         c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
193         if (err != C2_OK) {
194             ALOGE("priorGraphicAllocation failed: %d", err);
195             return err;
196         }
197 
198         mSlotAllocations[slot] = std::move(alloc);
199         if (mSlotAllocations.size() == mMaxDequeuedBuffers) {
200             // already allocated enough buffers, set allowAllocation to false to restrict the
201             // eligible slots to allocated ones for future dequeue.
202             status = mProducer->allowAllocation(false);
203             if (status != android::NO_ERROR) {
204                 ALOGE("allowAllocation(false) failed");
205                 return asC2Error(status);
206             }
207         }
208     } else if (mSlotAllocations.size() < mMaxDequeuedBuffers) {
209         ALOGE("failed to allocate enough buffers");
210         return C2_BAD_STATE;
211     }
212 
213     *block = _C2BlockFactory::CreateGraphicBlock(mSlotAllocations[slot]);
214     return C2_OK;
215 }
216 
requestNewBufferSet(int32_t bufferCount)217 c2_status_t C2VdaBqBlockPool::requestNewBufferSet(int32_t bufferCount) {
218     if (bufferCount <= 0) {
219         ALOGE("Invalid requested buffer count = %d", bufferCount);
220         return C2_BAD_VALUE;
221     }
222 
223     std::lock_guard<std::mutex> lock(mMutex);
224     if (!mProducer) {
225         ALOGD("No HGraphicBufferProducer is configured...");
226         return C2_NO_INIT;
227     }
228 
229     // For dynamic resolution change, cancel all mapping buffers and discard references.
230     // Client needs to make sure all buffers are dequeued and owned by client before calling.
231     auto cancelStatus = cancelAllBuffers();
232     if (cancelStatus != C2_OK) {
233         ALOGE("cancelBuffer failed while requesting new buffer set...");
234         return C2_CORRUPTED;
235     }
236 
237     // TODO: should we query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) and add it on?
238     int32_t status = mProducer->setMaxDequeuedBufferCount(bufferCount);
239     if (status != android::NO_ERROR) {
240         ALOGE("setMaxDequeuedBufferCount failed");
241         return asC2Error(status);
242     }
243     mMaxDequeuedBuffers = static_cast<size_t>(bufferCount);
244 
245     status = mProducer->allowAllocation(true);
246     if (status != android::NO_ERROR) {
247         ALOGE("allowAllocation(true) failed");
248         return asC2Error(status);
249     }
250     return C2_OK;
251 }
252 
configureProducer(const sp<HGraphicBufferProducer> & producer)253 void C2VdaBqBlockPool::configureProducer(const sp<HGraphicBufferProducer>& producer) {
254     ALOGV("configureProducer");
255     std::lock_guard<std::mutex> lock(mMutex);
256     // TODO: handle producer change request (client changes surface) while codec is running.
257     if (mProducer) {
258         ALOGE("Not allowed to reset HGraphicBufferProducer");
259         return;
260     }
261 
262     mProducer = producer;
263     if (producer) {
264         int32_t status;
265         producer->getUniqueId(
266                 [&status, &producerId = mProducerId](int32_t tStatus, int64_t tProducerId) {
267                     status = tStatus;
268                     producerId = tProducerId;
269                 });
270         if (status != android::NO_ERROR) {
271             ALOGE("getUniqueId failed");
272             mProducer = nullptr;
273             mProducerId = 0;
274             return;
275         }
276         status = producer->setDequeueTimeout(kDequeueTimeoutNs);
277         if (status != android::NO_ERROR) {
278             ALOGE("setDequeueTimeout failed");
279             mProducer = nullptr;
280             mProducerId = 0;
281             return;
282         }
283     } else {
284         mProducerId = 0;
285     }
286     mSlotAllocations.clear();
287 }
288 
cancelAllBuffers()289 c2_status_t C2VdaBqBlockPool::cancelAllBuffers() {
290     int32_t lastError = android::NO_ERROR;
291     for (const auto& elem : mSlotAllocations) {
292         int32_t slot = elem.first;
293         sp<Fence> fence(Fence::NO_FENCE);
294         native_handle_t* nh = nullptr;
295         hidl_handle fenceHandle;
296         android::conversion::wrapAs(&fenceHandle, &nh, *fence);
297         int32_t status = mProducer->cancelBuffer(slot, fenceHandle);
298         native_handle_delete(nh);
299         if (status == android::NO_INIT) {
300             // Producer may be disconnected during the loop of cancelBuffer (especially in the
301             // destruction state). Break the loop and return immediately.
302             mSlotAllocations.clear();
303             return C2_NO_INIT;
304         }
305         if (status != android::NO_ERROR) {
306             ALOGE("cancelBuffer failed at slot = %d, err: %d", slot, status);
307             lastError = status;
308         }
309     }
310     mSlotAllocations.clear();
311     return asC2Error(lastError);
312 }
313