| /* |
| * Copyright (C) 2022 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. |
| */ |
| |
| #include "Gralloc.h" |
| |
| #include <aidl/android/hardware/graphics/common/PlaneLayoutComponent.h> |
| #include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h> |
| #include <drm_fourcc.h> |
| #include <gralloctypes/Gralloc4.h> |
| #include <hidl/ServiceManagement.h> |
| #include <log/log.h> |
| |
| #include <algorithm> |
| |
| #include "Drm.h" |
| |
| using aidl::android::hardware::graphics::common::PlaneLayout; |
| using aidl::android::hardware::graphics::common::PlaneLayoutComponent; |
| using aidl::android::hardware::graphics::common::PlaneLayoutComponentType; |
| using android::hardware::hidl_handle; |
| using android::hardware::hidl_vec; |
| using android::hardware::graphics::common::V1_2::BufferUsage; |
| using android::hardware::graphics::mapper::V4_0::Error; |
| using android::hardware::graphics::mapper::V4_0::IMapper; |
| using MetadataType = |
| android::hardware::graphics::mapper::V4_0::IMapper::MetadataType; |
| |
| namespace aidl::android::hardware::graphics::composer3::impl { |
| |
| Gralloc::Gralloc() { |
| ::android::hardware::preloadPassthroughService<IMapper>(); |
| |
| gralloc4_ = IMapper::getService(); |
| if (gralloc4_ != nullptr) { |
| ALOGE("%s using Gralloc4.", __FUNCTION__); |
| return; |
| } |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| |
| ALOGE("%s No Grallocs available!", __FUNCTION__); |
| } |
| |
| Error Gralloc::GetMetadata(buffer_handle_t buffer, MetadataType type, |
| hidl_vec<uint8_t>* metadata) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return Error::NO_RESOURCES; |
| } |
| |
| if (metadata == nullptr) { |
| return Error::BAD_VALUE; |
| } |
| |
| Error error = Error::NONE; |
| |
| auto native_handle = const_cast<native_handle_t*>(buffer); |
| |
| auto ret = |
| gralloc4_->get(native_handle, type, |
| [&](const auto& get_error, const auto& get_metadata) { |
| error = get_error; |
| *metadata = get_metadata; |
| }); |
| |
| if (!ret.isOk()) { |
| error = Error::NO_RESOURCES; |
| } |
| |
| if (error != Error::NONE) { |
| ALOGE("%s failed to get metadata %s", __FUNCTION__, type.name.c_str()); |
| } |
| return error; |
| } |
| |
| std::optional<uint32_t> Gralloc::GetWidth(buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| hidl_vec<uint8_t> encoded_width; |
| |
| Error error = GetMetadata(buffer, ::android::gralloc4::MetadataType_Width, |
| &encoded_width); |
| if (error != Error::NONE) { |
| return std::nullopt; |
| } |
| |
| uint64_t width = 0; |
| ::android::gralloc4::decodeWidth(encoded_width, &width); |
| return static_cast<uint32_t>(width); |
| } |
| |
| std::optional<uint32_t> Gralloc::GetHeight(buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| hidl_vec<uint8_t> encoded_height; |
| |
| Error error = GetMetadata(buffer, ::android::gralloc4::MetadataType_Height, |
| &encoded_height); |
| if (error != Error::NONE) { |
| return std::nullopt; |
| } |
| |
| uint64_t height = 0; |
| ::android::gralloc4::decodeHeight(encoded_height, &height); |
| return static_cast<uint32_t>(height); |
| } |
| |
| std::optional<uint32_t> Gralloc::GetDrmFormat(buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| hidl_vec<uint8_t> encoded_format; |
| |
| Error error = |
| GetMetadata(buffer, ::android::gralloc4::MetadataType_PixelFormatFourCC, |
| &encoded_format); |
| if (error != Error::NONE) { |
| return std::nullopt; |
| } |
| |
| uint32_t format = 0; |
| ::android::gralloc4::decodePixelFormatFourCC(encoded_format, &format); |
| return static_cast<uint32_t>(format); |
| } |
| |
| std::optional<std::vector<PlaneLayout>> Gralloc::GetPlaneLayouts( |
| buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| hidl_vec<uint8_t> encoded_layouts; |
| |
| Error error = GetMetadata( |
| buffer, ::android::gralloc4::MetadataType_PlaneLayouts, &encoded_layouts); |
| if (error != Error::NONE) { |
| return std::nullopt; |
| } |
| |
| std::vector<PlaneLayout> plane_layouts; |
| ::android::gralloc4::decodePlaneLayouts(encoded_layouts, &plane_layouts); |
| return plane_layouts; |
| } |
| |
| std::optional<uint32_t> Gralloc::GetMonoPlanarStrideBytes( |
| buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| auto plane_layouts_opt = GetPlaneLayouts(buffer); |
| if (!plane_layouts_opt) { |
| return std::nullopt; |
| } |
| |
| std::vector<PlaneLayout>& plane_layouts = *plane_layouts_opt; |
| if (plane_layouts.size() != 1) { |
| return std::nullopt; |
| } |
| |
| return static_cast<uint32_t>(plane_layouts[0].strideInBytes); |
| } |
| |
| std::optional<GrallocBuffer> Gralloc::Import(buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| buffer_handle_t imported_buffer; |
| |
| Error error; |
| auto ret = |
| gralloc4_->importBuffer(buffer, [&](const auto& err, const auto& buf) { |
| error = err; |
| if (err == Error::NONE) { |
| imported_buffer = static_cast<buffer_handle_t>(buf); |
| } |
| }); |
| |
| if (!ret.isOk() || error != Error::NONE) { |
| ALOGE("%s failed to import buffer", __FUNCTION__); |
| return std::nullopt; |
| } |
| return GrallocBuffer(this, imported_buffer); |
| } |
| |
| void Gralloc::Release(buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return; |
| } |
| |
| auto native_buffer = const_cast<native_handle_t*>(buffer); |
| auto ret = gralloc4_->freeBuffer(native_buffer); |
| |
| if (!ret.isOk()) { |
| ALOGE("%s failed to release buffer", __FUNCTION__); |
| } |
| } |
| |
| std::optional<void*> Gralloc::Lock(buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| auto native_buffer = const_cast<native_handle_t*>(buffer); |
| |
| const auto buffer_usage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | |
| BufferUsage::CPU_WRITE_OFTEN); |
| |
| auto width_opt = GetWidth(buffer); |
| if (!width_opt) { |
| return std::nullopt; |
| } |
| |
| auto height_opt = GetHeight(buffer); |
| if (!height_opt) { |
| return std::nullopt; |
| } |
| |
| IMapper::Rect buffer_region; |
| buffer_region.left = 0; |
| buffer_region.top = 0; |
| buffer_region.width = *width_opt; |
| buffer_region.height = *height_opt; |
| |
| // Empty fence, lock immedietly. |
| hidl_handle fence; |
| |
| Error error = Error::NONE; |
| void* data = nullptr; |
| |
| auto ret = |
| gralloc4_->lock(native_buffer, buffer_usage, buffer_region, fence, |
| [&](const auto& lock_error, const auto& lock_data) { |
| error = lock_error; |
| if (lock_error == Error::NONE) { |
| data = lock_data; |
| } |
| }); |
| |
| if (!ret.isOk()) { |
| error = Error::NO_RESOURCES; |
| } |
| |
| if (error != Error::NONE) { |
| ALOGE("%s failed to lock buffer", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| return data; |
| } |
| |
| std::optional<android_ycbcr> Gralloc::LockYCbCr(buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| auto format_opt = GetDrmFormat(buffer); |
| if (!format_opt) { |
| ALOGE("%s failed to check format of buffer", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| if (*format_opt != DRM_FORMAT_NV12 && *format_opt != DRM_FORMAT_NV21 && |
| *format_opt != DRM_FORMAT_YVU420) { |
| ALOGE("%s called on non-ycbcr buffer", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| auto lock_opt = Lock(buffer); |
| if (!lock_opt) { |
| ALOGE("%s failed to lock buffer", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| auto plane_layouts_opt = GetPlaneLayouts(buffer); |
| if (!plane_layouts_opt) { |
| ALOGE("%s failed to get plane layouts", __FUNCTION__); |
| return std::nullopt; |
| } |
| |
| android_ycbcr buffer_ycbcr; |
| buffer_ycbcr.y = nullptr; |
| buffer_ycbcr.cb = nullptr; |
| buffer_ycbcr.cr = nullptr; |
| buffer_ycbcr.ystride = 0; |
| buffer_ycbcr.cstride = 0; |
| buffer_ycbcr.chroma_step = 0; |
| |
| for (const auto& plane_layout : *plane_layouts_opt) { |
| for (const auto& plane_layout_component : plane_layout.components) { |
| const auto& type = plane_layout_component.type; |
| |
| if (!::android::gralloc4::isStandardPlaneLayoutComponentType(type)) { |
| continue; |
| } |
| |
| auto* component_data = reinterpret_cast<uint8_t*>(*lock_opt) + |
| plane_layout.offsetInBytes + |
| plane_layout_component.offsetInBits / 8; |
| |
| switch (static_cast<PlaneLayoutComponentType>(type.value)) { |
| case PlaneLayoutComponentType::Y: |
| buffer_ycbcr.y = component_data; |
| buffer_ycbcr.ystride = plane_layout.strideInBytes; |
| break; |
| case PlaneLayoutComponentType::CB: |
| buffer_ycbcr.cb = component_data; |
| buffer_ycbcr.cstride = plane_layout.strideInBytes; |
| buffer_ycbcr.chroma_step = plane_layout.sampleIncrementInBits / 8; |
| break; |
| case PlaneLayoutComponentType::CR: |
| buffer_ycbcr.cr = component_data; |
| buffer_ycbcr.cstride = plane_layout.strideInBytes; |
| buffer_ycbcr.chroma_step = plane_layout.sampleIncrementInBits / 8; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| return buffer_ycbcr; |
| } |
| |
| void Gralloc::Unlock(buffer_handle_t buffer) { |
| if (gralloc4_ == nullptr) { |
| ALOGE("%s Gralloc4 not available.", __FUNCTION__); |
| return; |
| } |
| |
| auto native_handle = const_cast<native_handle_t*>(buffer); |
| |
| Error error = Error::NONE; |
| auto ret = gralloc4_->unlock( |
| native_handle, |
| [&](const auto& unlock_error, const auto&) { error = unlock_error; }); |
| |
| if (!ret.isOk()) { |
| error = Error::NO_RESOURCES; |
| } |
| |
| if (error != Error::NONE) { |
| ALOGE("%s failed to unlock buffer", __FUNCTION__); |
| } |
| } |
| |
| GrallocBuffer::GrallocBuffer(Gralloc* gralloc, buffer_handle_t buffer) |
| : gralloc_(gralloc), buffer_(buffer) {} |
| |
| GrallocBuffer::~GrallocBuffer() { Release(); } |
| |
| GrallocBuffer::GrallocBuffer(GrallocBuffer&& rhs) { *this = std::move(rhs); } |
| |
| GrallocBuffer& GrallocBuffer::operator=(GrallocBuffer&& rhs) { |
| gralloc_ = rhs.gralloc_; |
| buffer_ = rhs.buffer_; |
| rhs.gralloc_ = nullptr; |
| rhs.buffer_ = nullptr; |
| return *this; |
| } |
| |
| void GrallocBuffer::Release() { |
| if (gralloc_ && buffer_) { |
| gralloc_->Release(buffer_); |
| gralloc_ = nullptr; |
| buffer_ = nullptr; |
| } |
| } |
| |
| std::optional<GrallocBufferView> GrallocBuffer::Lock() { |
| if (gralloc_ && buffer_) { |
| auto format_opt = GetDrmFormat(); |
| if (!format_opt) { |
| ALOGE("%s failed to check format of buffer", __FUNCTION__); |
| return std::nullopt; |
| } |
| if (*format_opt != DRM_FORMAT_NV12 && *format_opt != DRM_FORMAT_NV21 && |
| *format_opt != DRM_FORMAT_YVU420) { |
| auto locked_opt = gralloc_->Lock(buffer_); |
| if (!locked_opt) { |
| return std::nullopt; |
| } |
| return GrallocBufferView(this, *locked_opt); |
| } else { |
| auto locked_ycbcr_opt = gralloc_->LockYCbCr(buffer_); |
| if (!locked_ycbcr_opt) { |
| ALOGE("%s failed to lock ycbcr buffer", __FUNCTION__); |
| return std::nullopt; |
| } |
| return GrallocBufferView(this, *locked_ycbcr_opt); |
| } |
| } |
| return std::nullopt; |
| } |
| |
| void GrallocBuffer::Unlock() { |
| if (gralloc_ && buffer_) { |
| gralloc_->Unlock(buffer_); |
| } |
| } |
| |
| std::optional<uint32_t> GrallocBuffer::GetWidth() { |
| if (gralloc_ && buffer_) { |
| return gralloc_->GetWidth(buffer_); |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<uint32_t> GrallocBuffer::GetHeight() { |
| if (gralloc_ && buffer_) { |
| return gralloc_->GetHeight(buffer_); |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<uint32_t> GrallocBuffer::GetDrmFormat() { |
| if (gralloc_ && buffer_) { |
| return gralloc_->GetDrmFormat(buffer_); |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<std::vector<PlaneLayout>> GrallocBuffer::GetPlaneLayouts() { |
| if (gralloc_ && buffer_) { |
| return gralloc_->GetPlaneLayouts(buffer_); |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<uint32_t> GrallocBuffer::GetMonoPlanarStrideBytes() { |
| if (gralloc_ && buffer_) { |
| return gralloc_->GetMonoPlanarStrideBytes(buffer_); |
| } |
| return std::nullopt; |
| } |
| |
| GrallocBufferView::GrallocBufferView(GrallocBuffer* buffer, void* raw) |
| : gralloc_buffer_(buffer), locked_(raw) {} |
| |
| GrallocBufferView::GrallocBufferView(GrallocBuffer* buffer, android_ycbcr raw) |
| : gralloc_buffer_(buffer), locked_ycbcr_(raw) {} |
| |
| GrallocBufferView::~GrallocBufferView() { |
| if (gralloc_buffer_) { |
| gralloc_buffer_->Unlock(); |
| } |
| } |
| |
| GrallocBufferView::GrallocBufferView(GrallocBufferView&& rhs) { |
| *this = std::move(rhs); |
| } |
| |
| GrallocBufferView& GrallocBufferView::operator=(GrallocBufferView&& rhs) { |
| std::swap(gralloc_buffer_, rhs.gralloc_buffer_); |
| std::swap(locked_, rhs.locked_); |
| std::swap(locked_ycbcr_, rhs.locked_ycbcr_); |
| return *this; |
| } |
| |
| const std::optional<void*> GrallocBufferView::Get() const { return locked_; } |
| |
| const std::optional<android_ycbcr>& GrallocBufferView::GetYCbCr() const { |
| return locked_ycbcr_; |
| } |
| |
| } // namespace aidl::android::hardware::graphics::composer3::impl |