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