1 /*
2 * Copyright (C) 2023 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 //#define LOG_NDEBUG 0
17 #define LOG_TAG "C2IgbaBuffer"
18 #include <android-base/logging.h>
19 #include <aidl/android/hardware/media/c2/IGraphicBufferAllocator.h>
20 #include <vndk/hardware_buffer.h>
21 #include <utils/Log.h>
22
23 #include <C2AllocatorGralloc.h>
24 #include <C2BlockInternal.h>
25 #include <C2FenceFactory.h>
26 #include <C2IgbaBufferPriv.h>
27 #include <C2PlatformSupport.h>
28
29 using ::android::C2AllocatorAhwb;
30 using C2IGBA = ::aidl::android::hardware::media::c2::IGraphicBufferAllocator;
31
32 namespace {
ToAidl(uint32_t u)33 int32_t static inline ToAidl(uint32_t u) {return static_cast<int32_t>(u);}
ToAidl(uint64_t u)34 int64_t static inline ToAidl(uint64_t u) {return static_cast<int64_t>(u);}
35
36 c2_nsecs_t static constexpr kBlockingFetchTimeoutNs = 5000000000LL; // 5 secs
37 c2_nsecs_t static constexpr kSyncFenceWaitNs = (1000000000LL / 60LL); // 60 fps frame secs
38
CreateGraphicBlockFromAhwb(AHardwareBuffer * ahwb,const std::shared_ptr<C2Allocator> & allocator,const std::shared_ptr<C2IGBA> & igba,std::shared_ptr<C2GraphicBlock> * block)39 c2_status_t static CreateGraphicBlockFromAhwb(AHardwareBuffer *ahwb,
40 const std::shared_ptr<C2Allocator> &allocator,
41 const std::shared_ptr<C2IGBA> &igba,
42 std::shared_ptr<C2GraphicBlock> *block) {
43 if (__builtin_available(android __ANDROID_API_T__, *)) {
44 uint64_t origId = 0;
45 CHECK(AHardwareBuffer_getId(ahwb, &origId) == ::android::OK);
46
47 AHardwareBuffer_Desc desc;
48 AHardwareBuffer_describe(ahwb, &desc);
49 const native_handle_t *handle = AHardwareBuffer_getNativeHandle(ahwb);
50 // cloned handle with wrapped data.(independent lifecycle with Ahwb)
51 C2Handle *c2Handle = android::WrapNativeCodec2AhwbHandle(
52 handle,
53 desc.width,
54 desc.height,
55 desc.format,
56 desc.usage,
57 desc.stride,
58 origId);
59 if (!c2Handle) {
60 return C2_NO_MEMORY;
61 }
62 std::shared_ptr<C2GraphicAllocation> alloc;
63 c2_status_t err = allocator->priorGraphicAllocation(c2Handle, &alloc);
64 if (err != C2_OK) {
65 native_handle_close(c2Handle);
66 native_handle_delete(c2Handle);
67 return err;
68 }
69 std::shared_ptr<C2IgbaBlockPoolData> poolData =
70 std::make_shared<C2IgbaBlockPoolData>(
71 ahwb, const_cast<std::shared_ptr<C2IGBA>&>(igba));
72 *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
73 return C2_OK;
74 } else {
75 return C2_OMITTED;
76 }
77 }
78
79 } // anonymous namespace
80
C2IgbaBlockPoolData(const AHardwareBuffer * buffer,std::shared_ptr<C2IGBA> & igba)81 C2IgbaBlockPoolData::C2IgbaBlockPoolData(
82 const AHardwareBuffer *buffer,
83 std::shared_ptr<C2IGBA> &igba) : mOwned(true), mBuffer(buffer), mIgba(igba) {
84 CHECK(mBuffer);
85 AHardwareBuffer_acquire(const_cast<AHardwareBuffer *>(mBuffer));
86 }
87
~C2IgbaBlockPoolData()88 C2IgbaBlockPoolData::~C2IgbaBlockPoolData() {
89 CHECK(mBuffer);
90 if (mOwned) {
91 if (__builtin_available(android __ANDROID_API_T__, *)) {
92 auto igba = mIgba.lock();
93 if (igba) {
94 uint64_t origId;
95 CHECK(AHardwareBuffer_getId(mBuffer, &origId) == ::android::OK);
96 bool aidlRet = true;
97 ::ndk::ScopedAStatus status = igba->deallocate(origId, &aidlRet);
98 if (!status.isOk() || !aidlRet) {
99 ALOGW("AHwb destruction notifying failure %d(%d)", status.isOk(), aidlRet);
100 }
101 }
102 }
103 }
104 AHardwareBuffer_release(const_cast<AHardwareBuffer *>(mBuffer));
105 }
106
getType() const107 C2IgbaBlockPoolData::type_t C2IgbaBlockPoolData::getType() const {
108 return TYPE_AHWBUFFER;
109 }
110
getAHardwareBuffer(AHardwareBuffer ** pBuf) const111 void C2IgbaBlockPoolData::getAHardwareBuffer(AHardwareBuffer **pBuf) const {
112 *pBuf = const_cast<AHardwareBuffer *>(mBuffer);
113 }
114
disown()115 void C2IgbaBlockPoolData::disown() {
116 mOwned = false;
117 }
118
registerIgba(std::shared_ptr<C2IGBA> & igba)119 void C2IgbaBlockPoolData::registerIgba(std::shared_ptr<C2IGBA> &igba) {
120 mIgba = igba;
121 }
122
CreateGraphicBlock(AHardwareBuffer * ahwb)123 std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(AHardwareBuffer *ahwb) {
124 // TODO: get proper allocator? and synchronization? or allocator-less?
125 static std::shared_ptr<C2AllocatorAhwb> sAllocator = std::make_shared<C2AllocatorAhwb>(0);
126 std::shared_ptr<C2GraphicBlock> block;
127 c2_status_t res = CreateGraphicBlockFromAhwb(
128 ahwb, std::static_pointer_cast<C2Allocator>(sAllocator), nullptr, &block);
129 if (res != C2_OK) {
130 return nullptr;
131 }
132 return block;
133 }
134
GetAHardwareBuffer(const std::shared_ptr<const _C2BlockPoolData> & data,AHardwareBuffer ** pBuf)135 bool _C2BlockFactory::GetAHardwareBuffer(
136 const std::shared_ptr<const _C2BlockPoolData>& data,
137 AHardwareBuffer **pBuf) {
138 if (data && data->getType() == _C2BlockPoolData::TYPE_AHWBUFFER) {
139 const std::shared_ptr<const C2IgbaBlockPoolData> poolData =
140 std::static_pointer_cast<const C2IgbaBlockPoolData>(data);
141 poolData->getAHardwareBuffer(pBuf);
142 return true;
143 }
144 return false;
145 }
146
DisownIgbaBlock(const std::shared_ptr<_C2BlockPoolData> & data)147 void _C2BlockFactory::DisownIgbaBlock(
148 const std::shared_ptr<_C2BlockPoolData>& data) {
149 if (data && data->getType() == _C2BlockPoolData::TYPE_AHWBUFFER) {
150 const std::shared_ptr<C2IgbaBlockPoolData> poolData =
151 std::static_pointer_cast<C2IgbaBlockPoolData>(data);
152 poolData->disown();
153 }
154 }
155
RegisterIgba(const std::shared_ptr<_C2BlockPoolData> & data,std::shared_ptr<C2IGBA> & igba)156 void _C2BlockFactory::RegisterIgba(
157 const std::shared_ptr<_C2BlockPoolData>& data,
158 std::shared_ptr<C2IGBA> &igba) {
159 if (data && data->getType() == _C2BlockPoolData::TYPE_AHWBUFFER) {
160 const std::shared_ptr<C2IgbaBlockPoolData> poolData =
161 std::static_pointer_cast<C2IgbaBlockPoolData>(data);
162 poolData->registerIgba(igba);
163 }
164 }
165
C2IgbaBlockPool(const std::shared_ptr<C2Allocator> & allocator,const std::shared_ptr<C2IGBA> & igba,::android::base::unique_fd && ufd,const local_id_t localId)166 C2IgbaBlockPool::C2IgbaBlockPool(
167 const std::shared_ptr<C2Allocator> &allocator,
168 const std::shared_ptr<C2IGBA> &igba,
169 ::android::base::unique_fd &&ufd,
170 const local_id_t localId) : mAllocator(allocator), mIgba(igba), mLocalId(localId) {
171 if (!mIgba) {
172 mValid = false;
173 return;
174 }
175 if (ufd.get() < 0) {
176 mValid = false;
177 return;
178 }
179 mWaitFence = _C2FenceFactory::CreatePipeFence(std::move(ufd));
180 if (!mWaitFence.valid()) {
181 mValid = false;
182 return;
183 }
184 mValid = true;
185 }
186
fetchGraphicBlock(uint32_t width,uint32_t height,uint32_t format,C2MemoryUsage usage,std::shared_ptr<C2GraphicBlock> * block)187 c2_status_t C2IgbaBlockPool::fetchGraphicBlock(
188 uint32_t width, uint32_t height, uint32_t format,
189 C2MemoryUsage usage, std::shared_ptr<C2GraphicBlock> *block) {
190 uint64_t origId;
191 C2Fence fence;
192 c2_status_t res = _fetchGraphicBlock(
193 width, height, format, usage, kBlockingFetchTimeoutNs, &origId, block, &fence);
194
195 if (res == C2_TIMED_OUT) {
196 // SyncFence waiting timeout.
197 // Usually HAL treats C2_TIMED_OUT as an irrecoverable error.
198 // We want HAL to re-try.
199 return C2_BLOCKING;
200 }
201 return res;
202 }
203
fetchGraphicBlock(uint32_t width,uint32_t height,uint32_t format,C2MemoryUsage usage,std::shared_ptr<C2GraphicBlock> * block,C2Fence * fence)204 c2_status_t C2IgbaBlockPool::fetchGraphicBlock(
205 uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
206 std::shared_ptr<C2GraphicBlock> *block, C2Fence *fence) {
207 uint64_t origId;
208 c2_status_t res = _fetchGraphicBlock(width, height, format, usage, 0LL, &origId, block, fence);
209 if (res == C2_TIMED_OUT) {
210 *fence = C2Fence();
211 return C2_BLOCKING;
212 }
213 return res;
214 }
215
_fetchGraphicBlock(uint32_t width,uint32_t height,uint32_t format,C2MemoryUsage usage,c2_nsecs_t timeoutNs,uint64_t * origId,std::shared_ptr<C2GraphicBlock> * block,C2Fence * fence)216 c2_status_t C2IgbaBlockPool::_fetchGraphicBlock(
217 uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
218 c2_nsecs_t timeoutNs,
219 uint64_t *origId,
220 std::shared_ptr<C2GraphicBlock> *block,
221 C2Fence *fence) {
222 if (!mValid) {
223 return C2_BAD_STATE;
224 }
225 if (__builtin_available(android __ANDROID_API_T__, *)) {
226 c2_status_t waitRes = mWaitFence.wait(timeoutNs);
227 switch (waitRes) {
228 case C2_CANCELED: {
229 *fence = mWaitFence;
230 return C2_BLOCKING;
231 }
232 case C2_TIMED_OUT: {
233 *fence = mWaitFence;
234 return C2_BLOCKING;
235 }
236 case C2_OK:
237 break;
238 default: { // C2_BAD_STATE
239 mValid = false;
240 return C2_BAD_STATE;
241 }
242 }
243
244 ::android::C2AndroidMemoryUsage memUsage{usage};
245 C2IGBA::Description desc{
246 ToAidl(width), ToAidl(height), ToAidl(format), ToAidl(memUsage.asGrallocUsage())};
247 C2IGBA::Allocation allocation;
248 ::ndk::ScopedAStatus status = mIgba->allocate(desc, &allocation);
249 if (!status.isOk()) {
250 binder_exception_t ex = status.getExceptionCode();
251 if (ex == EX_SERVICE_SPECIFIC) {
252 c2_status_t err = static_cast<c2_status_t>(status.getServiceSpecificError());
253 if (err == C2_BLOCKING) {
254 *fence = mWaitFence;
255 }
256 return err;
257 } else {
258 ALOGW("igba::allocate transaction failed: %d", ex);
259 return C2_CORRUPTED;
260 }
261 }
262
263 C2Fence syncFence = _C2FenceFactory::CreateSyncFence(allocation.fence.release());
264 AHardwareBuffer *ahwb = allocation.buffer.release(); // This is acquired.
265 CHECK(AHardwareBuffer_getId(ahwb, origId) == ::android::OK);
266
267 // We are waiting for SyncFence here for backward compatibility.
268 // H/W based Sync Fence could be returned to improve pipeline latency.
269 //
270 // TODO: Add a component configuration for returning sync fence
271 // from fetchGraphicBlock() as the C2Fence output param(b/322283520).
272 // In the case C2_OK along with GraphicBlock must be returned together.
273 c2_status_t res = syncFence.wait(kSyncFenceWaitNs);
274 if (res != C2_OK) {
275 AHardwareBuffer_release(ahwb);
276 bool aidlRet = true;
277 ::ndk::ScopedAStatus status = mIgba->deallocate(*origId, &aidlRet);
278 ALOGE("Waiting a sync fence failed %d aidl(%d: %d)",
279 res, status.isOk(), aidlRet);
280 return C2_TIMED_OUT;
281 }
282
283 res = CreateGraphicBlockFromAhwb(ahwb, mAllocator, mIgba, block);
284 AHardwareBuffer_release(ahwb);
285 if (res != C2_OK) {
286 bool aidlRet = true;
287 ::ndk::ScopedAStatus status = mIgba->deallocate(*origId, &aidlRet);
288 ALOGE("We got AHWB via AIDL but failed to created C2GraphicBlock err(%d) aidl(%d, %d)",
289 res, status.isOk(), aidlRet);
290 }
291 return res;
292 } else {
293 return C2_OMITTED;
294 }
295 }
296
invalidate()297 void C2IgbaBlockPool::invalidate() {
298 mValid = false;
299 }
300