1 /*
2 * Copyright (C) 2019 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 "C2AllocatorBlob"
19
20 #include <memory>
21 #include <mutex>
22 #include <set>
23
24 #include <C2AllocatorBlob.h>
25 #include <C2PlatformSupport.h>
26
27 #include <android/hardware/graphics/common/1.2/types.h>
28 #include <utils/Log.h>
29
30 namespace android {
31
32 using ::android::hardware::graphics::common::V1_2::PixelFormat;
33
34 constexpr uint32_t kLinearBufferHeight = 1u;
35 constexpr uint32_t kLinearBufferFormat = static_cast<uint32_t>(PixelFormat::BLOB);
36
37 namespace {
38
GetCapacityFromHandle(const C2Handle * const grallocHandle,size_t * capacity)39 c2_status_t GetCapacityFromHandle(const C2Handle* const grallocHandle, size_t* capacity) {
40 uint32_t width, height, format, stride, generation, igbp_slot;
41 uint64_t usage, igbp_id;
42 _UnwrapNativeCodec2GrallocMetadata(grallocHandle, &width, &height, &format, &usage, &stride,
43 &generation, &igbp_id, &igbp_slot);
44
45 if (height != kLinearBufferHeight || format != kLinearBufferFormat) {
46 return C2_BAD_VALUE;
47 }
48 *capacity = width;
49 return C2_OK;
50 }
51
52 } // namespace
53
54 // C2AllocationBlob is a wrapper for C2AllocationGralloc allocated by C2AllocatorGralloc.
55 // C2AllocationBlob::handle() delegates to the backed C2AllocationGralloc::handle().
56 class C2AllocationBlob : public C2LinearAllocation {
57 public:
58 C2AllocationBlob(std::shared_ptr<C2GraphicAllocation> graphicAllocation, size_t capacity,
59 C2Allocator::id_t allocatorId);
60 ~C2AllocationBlob() override;
61 c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence* fence,
62 void** addr /* nonnull */) override;
63 c2_status_t unmap(void* addr, size_t size, C2Fence* fenceFd) override;
64
getAllocatorId() const65 id_t getAllocatorId() const override { return mAllocatorId; }
handle() const66 const C2Handle* handle() const override { return mGraphicAllocation->handle(); }
equals(const std::shared_ptr<C2LinearAllocation> & other) const67 bool equals(const std::shared_ptr<C2LinearAllocation>& other) const override {
68 return other && other->handle() == handle();
69 }
70
71 private:
72 const std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
73 const C2Allocator::id_t mAllocatorId;
74
75 std::mutex mMapLock;
76 std::multiset<std::pair<size_t, size_t>> mMappedOffsetSize;
77 uint8_t *mMappedAddr;
78 };
79
C2AllocationBlob(std::shared_ptr<C2GraphicAllocation> graphicAllocation,size_t capacity,C2Allocator::id_t allocatorId)80 C2AllocationBlob::C2AllocationBlob(
81 std::shared_ptr<C2GraphicAllocation> graphicAllocation, size_t capacity,
82 C2Allocator::id_t allocatorId)
83 : C2LinearAllocation(capacity),
84 mGraphicAllocation(std::move(graphicAllocation)),
85 mAllocatorId(allocatorId),
86 mMappedAddr(nullptr) {}
87
~C2AllocationBlob()88 C2AllocationBlob::~C2AllocationBlob() {
89 if (mMappedAddr) {
90 C2Rect rect(capacity(), kLinearBufferHeight);
91 mGraphicAllocation->unmap(&mMappedAddr, rect, nullptr);
92 }
93 }
94
map(size_t offset,size_t size,C2MemoryUsage usage,C2Fence * fence,void ** addr)95 c2_status_t C2AllocationBlob::map(size_t offset, size_t size, C2MemoryUsage usage,
96 C2Fence* fence, void** addr /* nonnull */) {
97 *addr = nullptr;
98 if (size > capacity() || offset > capacity() || offset > capacity() - size) {
99 ALOGV("C2AllocationBlob: map: bad offset / size: offset=%zu size=%zu capacity=%u",
100 offset, size, capacity());
101 return C2_BAD_VALUE;
102 }
103 std::unique_lock<std::mutex> lock(mMapLock);
104 if (mMappedAddr) {
105 *addr = mMappedAddr + offset;
106 mMappedOffsetSize.insert({offset, size});
107 ALOGV("C2AllocationBlob: mapped from existing mapping: offset=%zu size=%zu capacity=%u",
108 offset, size, capacity());
109 return C2_OK;
110 }
111 C2PlanarLayout layout;
112 C2Rect rect = C2Rect(capacity(), kLinearBufferHeight);
113 c2_status_t err = mGraphicAllocation->map(rect, usage, fence, &layout, &mMappedAddr);
114 if (err != C2_OK) {
115 ALOGV("C2AllocationBlob: map failed: offset=%zu size=%zu capacity=%u err=%d",
116 offset, size, capacity(), err);
117 mMappedAddr = nullptr;
118 return err;
119 }
120 *addr = mMappedAddr + offset;
121 mMappedOffsetSize.insert({offset, size});
122 ALOGV("C2AllocationBlob: new map succeeded: offset=%zu size=%zu capacity=%u",
123 offset, size, capacity());
124 return C2_OK;
125 }
126
unmap(void * addr,size_t size,C2Fence * fenceFd)127 c2_status_t C2AllocationBlob::unmap(void* addr, size_t size, C2Fence* fenceFd) {
128 std::unique_lock<std::mutex> lock(mMapLock);
129 uint8_t *u8Addr = static_cast<uint8_t *>(addr);
130 if (u8Addr < mMappedAddr || mMappedAddr + capacity() < u8Addr + size) {
131 ALOGV("C2AllocationBlob: unmap: Bad addr / size: addr=%p size=%zu capacity=%u",
132 addr, size, capacity());
133 return C2_BAD_VALUE;
134 }
135 auto it = mMappedOffsetSize.find(std::make_pair(u8Addr - mMappedAddr, size));
136 if (it == mMappedOffsetSize.end()) {
137 ALOGV("C2AllocationBlob: unrecognized map: addr=%p size=%zu capacity=%u",
138 addr, size, capacity());
139 return C2_BAD_VALUE;
140 }
141 mMappedOffsetSize.erase(it);
142 if (!mMappedOffsetSize.empty()) {
143 ALOGV("C2AllocationBlob: still maintain mapping: addr=%p size=%zu capacity=%u",
144 addr, size, capacity());
145 return C2_OK;
146 }
147 C2Rect rect(capacity(), kLinearBufferHeight);
148 c2_status_t err = mGraphicAllocation->unmap(&mMappedAddr, rect, fenceFd);
149 ALOGV("C2AllocationBlob: last unmap: addr=%p size=%zu capacity=%u err=%d",
150 addr, size, capacity(), err);
151 mMappedAddr = nullptr;
152 return err;
153 }
154
155 /* ====================================== BLOB ALLOCATOR ====================================== */
C2AllocatorBlob(id_t id)156 C2AllocatorBlob::C2AllocatorBlob(id_t id) {
157 C2MemoryUsage minUsage = {0, 0};
158 C2MemoryUsage maxUsage = {C2MemoryUsage::CPU_READ | C2MemoryUsage::READ_PROTECTED,
159 C2MemoryUsage::CPU_WRITE};
160 Traits traits = {"android.allocator.blob", id, LINEAR, minUsage, maxUsage};
161 mTraits = std::make_shared<C2Allocator::Traits>(traits);
162 auto allocatorStore = GetCodec2PlatformAllocatorStore();
163 allocatorStore->fetchAllocator(C2PlatformAllocatorStore::GRALLOC, &mC2AllocatorGralloc);
164 if (!mC2AllocatorGralloc) {
165 ALOGE("Failed to obtain C2AllocatorGralloc as backed allocator");
166 }
167 }
168
~C2AllocatorBlob()169 C2AllocatorBlob::~C2AllocatorBlob() {}
170
newLinearAllocation(uint32_t capacity,C2MemoryUsage usage,std::shared_ptr<C2LinearAllocation> * allocation)171 c2_status_t C2AllocatorBlob::newLinearAllocation(
172 uint32_t capacity, C2MemoryUsage usage, std::shared_ptr<C2LinearAllocation>* allocation) {
173 if (allocation == nullptr) {
174 return C2_BAD_VALUE;
175 }
176
177 allocation->reset();
178
179 if (!mC2AllocatorGralloc) {
180 return C2_CORRUPTED;
181 }
182
183 // Note: the BLOB allocator does not support padding as this functionality is expected
184 // to be provided by the gralloc implementation.
185 std::shared_ptr<C2GraphicAllocation> graphicAllocation;
186 c2_status_t status = mC2AllocatorGralloc->newGraphicAllocation(
187 capacity, kLinearBufferHeight, kLinearBufferFormat, usage, &graphicAllocation);
188 if (status != C2_OK) {
189 ALOGE("Failed newGraphicAllocation");
190 return status;
191 }
192
193 allocation->reset(new C2AllocationBlob(std::move(graphicAllocation),
194 static_cast<size_t>(capacity), mTraits->id));
195 return C2_OK;
196 }
197
priorLinearAllocation(const C2Handle * handle,std::shared_ptr<C2LinearAllocation> * allocation)198 c2_status_t C2AllocatorBlob::priorLinearAllocation(
199 const C2Handle* handle, std::shared_ptr<C2LinearAllocation>* allocation) {
200 if (allocation == nullptr) {
201 return C2_BAD_VALUE;
202 }
203
204 allocation->reset();
205
206 if (!mC2AllocatorGralloc) {
207 return C2_CORRUPTED;
208 }
209
210 std::shared_ptr<C2GraphicAllocation> graphicAllocation;
211 c2_status_t status = mC2AllocatorGralloc->priorGraphicAllocation(handle, &graphicAllocation);
212 if (status != C2_OK) {
213 ALOGE("Failed priorGraphicAllocation");
214 return status;
215 }
216
217 const C2Handle* const grallocHandle = graphicAllocation->handle();
218 size_t capacity = 0;
219 status = GetCapacityFromHandle(grallocHandle, &capacity);
220 if (status != C2_OK) {
221 ALOGE("Failed to extract capacity from Handle");
222 return status;
223 }
224
225 allocation->reset(new C2AllocationBlob(std::move(graphicAllocation), capacity, mTraits->id));
226 return C2_OK;
227 }
228
getId() const229 id_t C2AllocatorBlob::getId() const {
230 return mTraits->id;
231 }
232
getName() const233 C2String C2AllocatorBlob::getName() const {
234 return mTraits->name;
235 }
236
getTraits() const237 std::shared_ptr<const C2Allocator::Traits> C2AllocatorBlob::getTraits() const {
238 return mTraits;
239 }
240
241 // static
CheckHandle(const C2Handle * const o)242 bool C2AllocatorBlob::CheckHandle(const C2Handle* const o) {
243 size_t capacity;
244 // Distinguish C2Handle purely allocated by C2AllocatorGralloc, or one allocated through
245 // C2AllocatorBlob, by checking the handle's height is 1, and its format is
246 // PixelFormat::BLOB by GetCapacityFromHandle().
247 return C2AllocatorGralloc::CheckHandle(o) && GetCapacityFromHandle(o, &capacity) == C2_OK;
248 }
249
250 } // namespace android
251