| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // #define LOG_NDEBUG 0 |
| #define LOG_TAG "C2AllocatorBlob" |
| |
| #include <set> |
| |
| #include <C2AllocatorBlob.h> |
| #include <C2PlatformSupport.h> |
| |
| #include <android/hardware/graphics/common/1.2/types.h> |
| #include <utils/Log.h> |
| |
| namespace android { |
| |
| using ::android::hardware::graphics::common::V1_2::PixelFormat; |
| |
| constexpr uint32_t kLinearBufferHeight = 1u; |
| constexpr uint32_t kLinearBufferFormat = static_cast<uint32_t>(PixelFormat::BLOB); |
| |
| namespace { |
| |
| c2_status_t GetCapacityFromHandle(const C2Handle* const grallocHandle, size_t* capacity) { |
| uint32_t width, height, format, stride, generation, igbp_slot; |
| uint64_t usage, igbp_id; |
| _UnwrapNativeCodec2GrallocMetadata(grallocHandle, &width, &height, &format, &usage, &stride, |
| &generation, &igbp_id, &igbp_slot); |
| |
| if (height != kLinearBufferHeight || format != kLinearBufferFormat) { |
| return C2_BAD_VALUE; |
| } |
| *capacity = width; |
| return C2_OK; |
| } |
| |
| } // namespace |
| |
| // C2AllocationBlob is a wrapper for C2AllocationGralloc allocated by C2AllocatorGralloc. |
| // C2AllocationBlob::handle() delegates to the backed C2AllocationGralloc::handle(). |
| class C2AllocationBlob : public C2LinearAllocation { |
| public: |
| C2AllocationBlob(std::shared_ptr<C2GraphicAllocation> graphicAllocation, size_t capacity, |
| C2Allocator::id_t allocatorId); |
| ~C2AllocationBlob() override; |
| c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence* fence, |
| void** addr /* nonnull */) override; |
| c2_status_t unmap(void* addr, size_t size, C2Fence* fenceFd) override; |
| |
| id_t getAllocatorId() const override { return mAllocatorId; } |
| const C2Handle* handle() const override { return mGraphicAllocation->handle(); } |
| bool equals(const std::shared_ptr<C2LinearAllocation>& other) const override { |
| return other && other->handle() == handle(); |
| } |
| |
| private: |
| const std::shared_ptr<C2GraphicAllocation> mGraphicAllocation; |
| const C2Allocator::id_t mAllocatorId; |
| |
| std::mutex mMapLock; |
| std::multiset<std::pair<size_t, size_t>> mMappedOffsetSize; |
| uint8_t *mMappedAddr; |
| }; |
| |
| C2AllocationBlob::C2AllocationBlob( |
| std::shared_ptr<C2GraphicAllocation> graphicAllocation, size_t capacity, |
| C2Allocator::id_t allocatorId) |
| : C2LinearAllocation(capacity), |
| mGraphicAllocation(std::move(graphicAllocation)), |
| mAllocatorId(allocatorId), |
| mMappedAddr(nullptr) {} |
| |
| C2AllocationBlob::~C2AllocationBlob() { |
| if (mMappedAddr) { |
| C2Rect rect(capacity(), kLinearBufferHeight); |
| mGraphicAllocation->unmap(&mMappedAddr, rect, nullptr); |
| } |
| } |
| |
| c2_status_t C2AllocationBlob::map(size_t offset, size_t size, C2MemoryUsage usage, |
| C2Fence* fence, void** addr /* nonnull */) { |
| *addr = nullptr; |
| if (size > capacity() || offset > capacity() || offset > capacity() - size) { |
| ALOGV("C2AllocationBlob: map: bad offset / size: offset=%zu size=%zu capacity=%u", |
| offset, size, capacity()); |
| return C2_BAD_VALUE; |
| } |
| std::unique_lock<std::mutex> lock(mMapLock); |
| if (mMappedAddr) { |
| *addr = mMappedAddr + offset; |
| mMappedOffsetSize.insert({offset, size}); |
| ALOGV("C2AllocationBlob: mapped from existing mapping: offset=%zu size=%zu capacity=%u", |
| offset, size, capacity()); |
| return C2_OK; |
| } |
| C2PlanarLayout layout; |
| C2Rect rect = C2Rect(capacity(), kLinearBufferHeight); |
| c2_status_t err = mGraphicAllocation->map(rect, usage, fence, &layout, &mMappedAddr); |
| if (err != C2_OK) { |
| ALOGV("C2AllocationBlob: map failed: offset=%zu size=%zu capacity=%u err=%d", |
| offset, size, capacity(), err); |
| mMappedAddr = nullptr; |
| return err; |
| } |
| *addr = mMappedAddr + offset; |
| mMappedOffsetSize.insert({offset, size}); |
| ALOGV("C2AllocationBlob: new map succeeded: offset=%zu size=%zu capacity=%u", |
| offset, size, capacity()); |
| return C2_OK; |
| } |
| |
| c2_status_t C2AllocationBlob::unmap(void* addr, size_t size, C2Fence* fenceFd) { |
| std::unique_lock<std::mutex> lock(mMapLock); |
| uint8_t *u8Addr = static_cast<uint8_t *>(addr); |
| if (u8Addr < mMappedAddr || mMappedAddr + capacity() < u8Addr + size) { |
| ALOGV("C2AllocationBlob: unmap: Bad addr / size: addr=%p size=%zu capacity=%u", |
| addr, size, capacity()); |
| return C2_BAD_VALUE; |
| } |
| auto it = mMappedOffsetSize.find(std::make_pair(u8Addr - mMappedAddr, size)); |
| if (it == mMappedOffsetSize.end()) { |
| ALOGV("C2AllocationBlob: unrecognized map: addr=%p size=%zu capacity=%u", |
| addr, size, capacity()); |
| return C2_BAD_VALUE; |
| } |
| mMappedOffsetSize.erase(it); |
| if (!mMappedOffsetSize.empty()) { |
| ALOGV("C2AllocationBlob: still maintain mapping: addr=%p size=%zu capacity=%u", |
| addr, size, capacity()); |
| return C2_OK; |
| } |
| C2Rect rect(capacity(), kLinearBufferHeight); |
| c2_status_t err = mGraphicAllocation->unmap(&mMappedAddr, rect, fenceFd); |
| ALOGV("C2AllocationBlob: last unmap: addr=%p size=%zu capacity=%u err=%d", |
| addr, size, capacity(), err); |
| mMappedAddr = nullptr; |
| return err; |
| } |
| |
| /* ====================================== BLOB ALLOCATOR ====================================== */ |
| C2AllocatorBlob::C2AllocatorBlob(id_t id) { |
| C2MemoryUsage minUsage = {0, 0}; |
| C2MemoryUsage maxUsage = {C2MemoryUsage::CPU_READ | C2MemoryUsage::READ_PROTECTED, |
| C2MemoryUsage::CPU_WRITE}; |
| Traits traits = {"android.allocator.blob", id, LINEAR, minUsage, maxUsage}; |
| mTraits = std::make_shared<C2Allocator::Traits>(traits); |
| auto allocatorStore = GetCodec2PlatformAllocatorStore(); |
| allocatorStore->fetchAllocator(C2PlatformAllocatorStore::GRALLOC, &mC2AllocatorGralloc); |
| if (!mC2AllocatorGralloc) { |
| ALOGE("Failed to obtain C2AllocatorGralloc as backed allocator"); |
| } |
| } |
| |
| C2AllocatorBlob::~C2AllocatorBlob() {} |
| |
| c2_status_t C2AllocatorBlob::newLinearAllocation( |
| uint32_t capacity, C2MemoryUsage usage, std::shared_ptr<C2LinearAllocation>* allocation) { |
| if (allocation == nullptr) { |
| return C2_BAD_VALUE; |
| } |
| |
| allocation->reset(); |
| |
| if (!mC2AllocatorGralloc) { |
| return C2_CORRUPTED; |
| } |
| |
| // Note: the BLOB allocator does not support padding as this functionality is expected |
| // to be provided by the gralloc implementation. |
| std::shared_ptr<C2GraphicAllocation> graphicAllocation; |
| c2_status_t status = mC2AllocatorGralloc->newGraphicAllocation( |
| capacity, kLinearBufferHeight, kLinearBufferFormat, usage, &graphicAllocation); |
| if (status != C2_OK) { |
| ALOGE("Failed newGraphicAllocation"); |
| return status; |
| } |
| |
| allocation->reset(new C2AllocationBlob(std::move(graphicAllocation), |
| static_cast<size_t>(capacity), mTraits->id)); |
| return C2_OK; |
| } |
| |
| c2_status_t C2AllocatorBlob::priorLinearAllocation( |
| const C2Handle* handle, std::shared_ptr<C2LinearAllocation>* allocation) { |
| if (allocation == nullptr) { |
| return C2_BAD_VALUE; |
| } |
| |
| allocation->reset(); |
| |
| if (!mC2AllocatorGralloc) { |
| return C2_CORRUPTED; |
| } |
| |
| std::shared_ptr<C2GraphicAllocation> graphicAllocation; |
| c2_status_t status = mC2AllocatorGralloc->priorGraphicAllocation(handle, &graphicAllocation); |
| if (status != C2_OK) { |
| ALOGE("Failed priorGraphicAllocation"); |
| return status; |
| } |
| |
| const C2Handle* const grallocHandle = graphicAllocation->handle(); |
| size_t capacity = 0; |
| status = GetCapacityFromHandle(grallocHandle, &capacity); |
| if (status != C2_OK) { |
| ALOGE("Failed to extract capacity from Handle"); |
| return status; |
| } |
| |
| allocation->reset(new C2AllocationBlob(std::move(graphicAllocation), capacity, mTraits->id)); |
| return C2_OK; |
| } |
| |
| id_t C2AllocatorBlob::getId() const { |
| return mTraits->id; |
| } |
| |
| C2String C2AllocatorBlob::getName() const { |
| return mTraits->name; |
| } |
| |
| std::shared_ptr<const C2Allocator::Traits> C2AllocatorBlob::getTraits() const { |
| return mTraits; |
| } |
| |
| // static |
| bool C2AllocatorBlob::CheckHandle(const C2Handle* const o) { |
| size_t capacity; |
| // Distinguish C2Handle purely allocated by C2AllocatorGralloc, or one allocated through |
| // C2AllocatorBlob, by checking the handle's height is 1, and its format is |
| // PixelFormat::BLOB by GetCapacityFromHandle(). |
| return C2AllocatorGralloc::CheckHandle(o) && GetCapacityFromHandle(o, &capacity) == C2_OK; |
| } |
| |
| } // namespace android |