/* * Copyright (C) 2020 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 #include #include #include "cb_handle_30.h" #include "host_connection_session.h" #include "FormatConversions.h" #include "debug.h" #include "android/base/Tracing.h" #define ATRACE_TAG ATRACE_TAG_GRAPHICS const int kOMX_COLOR_FormatYUV420Planar = 19; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_vec; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::hardware::graphics::common::V1_2::PixelFormat; using ::android::hardware::graphics::common::V1_0::BufferUsage; namespace MapperV3 = ::android::hardware::graphics::mapper::V3_0; using IMapper3 = MapperV3::IMapper; using Error3 = MapperV3::Error; using YCbCrLayout3 = MapperV3::YCbCrLayout; namespace { size_t align(const size_t v, const size_t a) { return (v + a - 1) & ~(a - 1); } static int waitFenceFd(const int fd, const char* logname) { const int warningTimeout = 5000; if (sync_wait(fd, warningTimeout) < 0) { if (errno == ETIME) { ALOGW("%s: fence %d didn't signal in %d ms", logname, fd, warningTimeout); if (sync_wait(fd, -1) < 0) { RETURN_ERROR(errno); } else { RETURN(0); } } else { RETURN_ERROR(errno); } } else { RETURN(0); } } int waitHidlFence(const hidl_handle& hidlHandle, const char* logname) { const native_handle_t* nativeHandle = hidlHandle.getNativeHandle(); if (!nativeHandle) { RETURN(0); } if (nativeHandle->numFds > 1) { RETURN_ERROR(-EINVAL); } if (nativeHandle->numInts != 0) { RETURN_ERROR(-EINVAL); } return waitFenceFd(nativeHandle->data[0], logname); } constexpr uint64_t one64 = 1; constexpr uint64_t ones(int from, int to) { return ((one64 << (to - from + 1)) - 1) << from; } class GoldfishMapper : public IMapper3 { public: GoldfishMapper() : m_hostConn(HostConnection::createUnique()) { GoldfishAddressSpaceHostMemoryAllocator host_memory_allocator(false); CRASH_IF(!host_memory_allocator.is_opened(), "GoldfishAddressSpaceHostMemoryAllocator failed to open"); GoldfishAddressSpaceBlock bufferBits; CRASH_IF(host_memory_allocator.hostMalloc(&bufferBits, 256), "hostMalloc failed"); m_physAddrToOffset = bufferBits.physAddr() - bufferBits.offset(); host_memory_allocator.hostFree(&bufferBits); } Return importBuffer(const hidl_handle& hh, importBuffer_cb hidl_cb) { native_handle_t* imported = nullptr; const Error3 e = importBufferImpl(hh.getNativeHandle(), &imported); if (e == Error3::NONE) { hidl_cb(Error3::NONE, imported); } else { hidl_cb(e, nullptr); } return {}; } Return freeBuffer(void* raw) { if (!raw) { RETURN_ERROR(Error3::BAD_BUFFER); } cb_handle_30_t* cb = cb_handle_30_t::from(raw); if (!cb) { RETURN_ERROR(Error3::BAD_BUFFER); } if (cb->hostHandle) { const HostConnectionSession conn = getHostConnectionSession(); ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); rcEnc->rcCloseColorBuffer(rcEnc, cb->hostHandle); } if (cb->mmapedSize > 0) { GoldfishAddressSpaceBlock::memoryUnmap(cb->getBufferPtr(), cb->mmapedSize); } native_handle_close(cb); native_handle_delete(cb); RETURN(Error3::NONE); } Return lock(void* raw, uint64_t cpuUsage, const Rect& accessRegion, const hidl_handle& acquireFence, lock_cb hidl_cb) { void* ptr = nullptr; int32_t bytesPerPixel = 0; int32_t bytesPerStride = 0; const Error3 e = lockImpl(raw, cpuUsage, accessRegion, acquireFence, &ptr, &bytesPerPixel, &bytesPerStride); if (e == Error3::NONE) { hidl_cb(Error3::NONE, ptr, bytesPerPixel, bytesPerStride); } else { hidl_cb(e, nullptr, 0, 0); } return {}; } Return lockYCbCr(void* raw, uint64_t cpuUsage, const Rect& accessRegion, const hidl_handle& acquireFence, lockYCbCr_cb hidl_cb) { YCbCrLayout3 ycbcr = {}; const Error3 e = lockYCbCrImpl(raw, cpuUsage, accessRegion, acquireFence, &ycbcr); if (e == Error3::NONE) { hidl_cb(Error3::NONE, ycbcr); } else { hidl_cb(e, {}); } return {}; } Return unlock(void* raw, unlock_cb hidl_cb) { hidl_cb(unlockImpl(raw), {}); return {}; } Return createDescriptor(const BufferDescriptorInfo& description, createDescriptor_cb hidl_cb) { hidl_vec raw; encodeBufferDescriptorInfo(description, &raw); hidl_cb(Error3::NONE, raw); return {}; } Return isSupported(const IMapper::BufferDescriptorInfo& description, isSupported_cb hidl_cb) { hidl_cb(Error3::NONE, isSupportedImpl(description)); return {}; } Return validateBufferSize(void* buffer, const BufferDescriptorInfo& descriptor, uint32_t stride) { const cb_handle_30_t* cb = cb_handle_30_t::from(buffer); if (cb) { return validateBufferSizeImpl(*cb, descriptor, stride); } else { RETURN_ERROR(Error3::BAD_BUFFER); } } Return getTransportSize(void* buffer, getTransportSize_cb hidl_cb) { const cb_handle_30_t* cb = cb_handle_30_t::from(buffer); if (cb) { hidl_cb(Error3::NONE, cb->numFds, cb->numInts); } else { hidl_cb(Error3::BAD_BUFFER, 0, 0); } return {}; } private: // **** impl **** Error3 importBufferImpl(const native_handle_t* nh, native_handle_t** phandle) { if (!nh) { RETURN_ERROR(Error3::BAD_BUFFER); } native_handle_t* imported = native_handle_clone(nh); if (!imported) { RETURN_ERROR(Error3::BAD_BUFFER); } cb_handle_30_t* cb = cb_handle_30_t::from(imported); if (!cb) { native_handle_close(imported); native_handle_delete(imported); RETURN_ERROR(Error3::BAD_BUFFER); } if (cb->mmapedSize > 0) { void* ptr; const int res = GoldfishAddressSpaceBlock::memoryMap( cb->getBufferPtr(), cb->mmapedSize, cb->bufferFd, cb->getMmapedOffset(), &ptr); if (res) { native_handle_close(imported); native_handle_delete(imported); RETURN_ERROR(Error3::NO_RESOURCES); } cb->setBufferPtr(ptr); } if (cb->hostHandle) { const HostConnectionSession conn = getHostConnectionSession(); ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); rcEnc->rcOpenColorBuffer2(rcEnc, cb->hostHandle); } *phandle = imported; RETURN(Error3::NONE); } Error3 lockImpl(void* raw, uint64_t cpuUsage, const Rect& accessRegion, const hidl_handle& acquireFence, void** pptr, int32_t* pBytesPerPixel, int32_t* pBytesPerStride) { if (!raw) { RETURN_ERROR(Error3::BAD_BUFFER); } cb_handle_30_t* cb = cb_handle_30_t::from(raw); if (!cb) { RETURN_ERROR(Error3::BAD_BUFFER); } if (!cb->bufferSize) { RETURN_ERROR(Error3::BAD_BUFFER); } char* const bufferBits = static_cast(cb->getBufferPtr()); if (!bufferBits) { RETURN_ERROR(Error3::BAD_BUFFER); } if (waitHidlFence(acquireFence, __func__)) { RETURN_ERROR(Error3::BAD_VALUE); } if (cb->hostHandle) { const Error3 e = lockHostImpl(*cb, cpuUsage, accessRegion, bufferBits); if (e != Error3::NONE) { return e; } } *pptr = bufferBits; *pBytesPerPixel = cb->bytesPerPixel; *pBytesPerStride = cb->bytesPerPixel * cb->stride; RETURN(Error3::NONE); } Error3 lockYCbCrImpl(void* raw, uint64_t cpuUsage, const Rect& accessRegion, const hidl_handle& acquireFence, YCbCrLayout3* pYcbcr) { if (!raw) { RETURN_ERROR(Error3::BAD_BUFFER); } cb_handle_30_t* cb = cb_handle_30_t::from(raw); if (!cb) { RETURN_ERROR(Error3::BAD_BUFFER); } if (!cb->bufferSize) { RETURN_ERROR(Error3::BAD_BUFFER); } char* const bufferBits = static_cast(cb->getBufferPtr()); if (!bufferBits) { RETURN_ERROR(Error3::BAD_BUFFER); } if (waitHidlFence(acquireFence, __func__)) { RETURN_ERROR(Error3::BAD_VALUE); } size_t uOffset; size_t vOffset; size_t yStride; size_t cStride; size_t cStep; switch (static_cast(cb->format)) { case PixelFormat::YCRCB_420_SP: yStride = cb->width; cStride = yStride; vOffset = yStride * cb->height; uOffset = vOffset + 1; cStep = 2; break; case PixelFormat::YV12: // https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12 yStride = align(cb->width, 16); cStride = align(yStride / 2, 16); vOffset = yStride * cb->height; uOffset = vOffset + (cStride * cb->height / 2); cStep = 1; break; case PixelFormat::YCBCR_420_888: yStride = cb->width; cStride = yStride / 2; uOffset = cb->height * yStride; vOffset = uOffset + cStride * cb->height / 2; cStep = 1; break; default: if (static_cast(cb->format) == android::hardware::graphics::common::V1_1::PixelFormat::YCBCR_P010) { yStride = cb->width * 2; cStride = yStride; uOffset = cb->height * yStride; vOffset = uOffset + 2; cStep = 4; break; } ALOGE("%s:%d unexpected format (%d)", __func__, __LINE__, cb->format); RETURN_ERROR(Error3::BAD_BUFFER); } if (cb->hostHandle) { const Error3 e = lockHostImpl(*cb, cpuUsage, accessRegion, bufferBits); if (e != Error3::NONE) { return e; } } pYcbcr->y = bufferBits; pYcbcr->cb = bufferBits + uOffset; pYcbcr->cr = bufferBits + vOffset; pYcbcr->yStride = yStride; pYcbcr->cStride = cStride; pYcbcr->chromaStep = cStep; RETURN(Error3::NONE); } Error3 lockHostImpl(cb_handle_30_t& cb, const uint32_t usage, const Rect& accessRegion, char* const bufferBits) { const bool usageSwRead = usage & BufferUsage::CPU_READ_MASK; const bool usageSwWrite = usage & BufferUsage::CPU_WRITE_MASK; const bool usageHwCamera = usage & (BufferUsage::CAMERA_INPUT | BufferUsage::CAMERA_OUTPUT); const bool usageHwCameraWrite = usage & BufferUsage::CAMERA_OUTPUT; const HostConnectionSession conn = getHostConnectionSession(); ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); const int res = rcEnc->rcColorBufferCacheFlush( rcEnc, cb.hostHandle, 0, usageSwRead); if (res < 0) { RETURN_ERROR(Error3::NO_RESOURCES); } // camera delivers bits to the buffer directly and does not require // an explicit read. const bool cbReadable = cb.usage & BufferUsage::CPU_READ_MASK; if (usageSwRead && !usageHwCamera && cbReadable) { if (gralloc_is_yuv_format(cb.format)) { if (rcEnc->hasYUVCache()) { uint32_t bufferSize; switch (static_cast(cb.format)) { case PixelFormat::YV12: get_yv12_offsets(cb.width, cb.height, nullptr, nullptr, &bufferSize); break; case PixelFormat::YCBCR_420_888: get_yuv420p_offsets(cb.width, cb.height, nullptr, nullptr, &bufferSize); break; default: CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); break; } rcEnc->rcReadColorBufferYUV(rcEnc, cb.hostHandle, 0, 0, cb.width, cb.height, bufferBits, bufferSize); } else { // We are using RGB888 std::vector tmpBuf(cb.width * cb.height * 3); rcEnc->rcReadColorBuffer(rcEnc, cb.hostHandle, 0, 0, cb.width, cb.height, cb.glFormat, cb.glType, tmpBuf.data()); switch (static_cast(cb.format)) { case PixelFormat::YV12: rgb888_to_yv12(bufferBits, tmpBuf.data(), cb.width, cb.height, accessRegion.left, accessRegion.top, accessRegion.left + accessRegion.width - 1, accessRegion.top + accessRegion.height - 1); break; case PixelFormat::YCBCR_420_888: rgb888_to_yuv420p(bufferBits, tmpBuf.data(), cb.width, cb.height, accessRegion.left, accessRegion.top, accessRegion.left + accessRegion.width - 1, accessRegion.top + accessRegion.height - 1); break; default: CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); break; } } } else { if (rcEnc->featureInfo()->hasReadColorBufferDma) { { AEMU_SCOPED_TRACE("bindDmaDirectly"); rcEnc->bindDmaDirectly(bufferBits, getMmapedPhysAddr(cb.getMmapedOffset())); } rcEnc->rcReadColorBufferDMA(rcEnc, cb.hostHandle, 0, 0, cb.width, cb.height, cb.glFormat, cb.glType, bufferBits, cb.width * cb.height * cb.bytesPerPixel); } else { rcEnc->rcReadColorBuffer(rcEnc, cb.hostHandle, 0, 0, cb.width, cb.height, cb.glFormat, cb.glType, bufferBits); } } } if (usageSwWrite || usageHwCameraWrite) { cb.lockedLeft = accessRegion.left; cb.lockedTop = accessRegion.top; cb.lockedWidth = accessRegion.width; cb.lockedHeight = accessRegion.height; } else { cb.lockedLeft = 0; cb.lockedTop = 0; cb.lockedWidth = cb.width; cb.lockedHeight = cb.height; } cb.locked = 1; cb.lockedUsage = usage; RETURN(Error3::NONE); } Error3 unlockImpl(void* raw) { AEMU_SCOPED_TRACE("unlockImpl body"); if (!raw) { RETURN_ERROR(Error3::BAD_BUFFER); } cb_handle_30_t* cb = cb_handle_30_t::from(raw); if (!cb) { RETURN_ERROR(Error3::BAD_BUFFER); } if (!cb->bufferSize) { RETURN_ERROR(Error3::BAD_BUFFER); } char* const bufferBits = static_cast(cb->getBufferPtr()); if (!bufferBits) { RETURN_ERROR(Error3::BAD_BUFFER); } if (cb->hostHandle) { unlockHostImpl(*cb, bufferBits); } RETURN(Error3::NONE); } void unlockHostImpl(cb_handle_30_t& cb, char* const bufferBits) { AEMU_SCOPED_TRACE("unlockHostImpl body"); const int bpp = glUtilsPixelBitSize(cb.glFormat, cb.glType) >> 3; const uint32_t lockedUsage = cb.lockedUsage; const uint32_t rgbSize = cb.width * cb.height * bpp; const char* bitsToSend; uint32_t sizeToSend; const uint32_t usageSwWrite = (uint32_t)BufferUsage::CPU_WRITE_MASK; uint32_t readOnly = (!(lockedUsage & usageSwWrite)); if (!readOnly) { if (gralloc_is_yuv_format(cb.format)) { bitsToSend = bufferBits; switch (static_cast(cb.format)) { case PixelFormat::YV12: get_yv12_offsets(cb.width, cb.height, nullptr, nullptr, &sizeToSend); break; case PixelFormat::YCBCR_420_888: get_yuv420p_offsets(cb.width, cb.height, nullptr, nullptr, &sizeToSend); break; default: CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); break; } } else { bitsToSend = bufferBits; sizeToSend = rgbSize; } { const HostConnectionSession conn = getHostConnectionSession(); ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); { AEMU_SCOPED_TRACE("bindDmaDirectly"); rcEnc->bindDmaDirectly(bufferBits, getMmapedPhysAddr(cb.getMmapedOffset())); } { AEMU_SCOPED_TRACE("updateColorBuffer"); rcEnc->rcUpdateColorBufferDMA(rcEnc, cb.hostHandle, 0, 0, cb.width, cb.height, cb.glFormat, cb.glType, const_cast(bitsToSend), sizeToSend); } } } cb.lockedLeft = 0; cb.lockedTop = 0; cb.lockedWidth = 0; cb.lockedHeight = 0; cb.locked = 0; cb.lockedUsage = 0; } /* BufferUsage bits that must be zero */ static constexpr uint64_t kReservedUsage = (one64 << 10) | (one64 << 13) | (one64 << 19) | (one64 << 21) | ones(25, 27) /* bits 25-27 must be zero and are reserved for future versions */ | ones(32, 47); /* bits 32-47 must be zero and are reserved for future versions */ bool isSupportedImpl(const IMapper::BufferDescriptorInfo& descriptor) const { if (!descriptor.width) { RETURN(false); } if (!descriptor.height) { RETURN(false); } if (descriptor.layerCount != 1) { RETURN(false); } const uint64_t usage64 = descriptor.usage; if (usage64 & kReservedUsage) { RETURN(false); } const uint32_t usage = usage64; const bool usageSwWrite = usage & BufferUsage::CPU_WRITE_MASK; const bool usageSwRead = usage & BufferUsage::CPU_READ_MASK; const bool usageHwCamWrite = usage & BufferUsage::CAMERA_OUTPUT; const bool usageHwCamRead = usage & BufferUsage::CAMERA_INPUT; switch (descriptor.format) { case PixelFormat::RGBA_8888: case PixelFormat::RGBX_8888: case PixelFormat::BGRA_8888: case PixelFormat::RGB_565: case PixelFormat::RGBA_FP16: case PixelFormat::RGBA_1010102: case PixelFormat::YCRCB_420_SP: case PixelFormat::YV12: case PixelFormat::YCBCR_420_888: RETURN(true); case PixelFormat::IMPLEMENTATION_DEFINED: if (usage & BufferUsage::CAMERA_OUTPUT) { RETURN(true); } RETURN(false); case PixelFormat::RGB_888: RETURN(0 == (usage & (BufferUsage::GPU_TEXTURE | BufferUsage::GPU_RENDER_TARGET | BufferUsage::COMPOSER_OVERLAY | BufferUsage::COMPOSER_CLIENT_TARGET))); case PixelFormat::RAW16: case PixelFormat::Y16: RETURN((usageSwRead || usageHwCamRead) && (usageSwWrite || usageHwCamWrite)); case PixelFormat::BLOB: RETURN(usageSwRead); default: if (static_cast(descriptor.format) == kOMX_COLOR_FormatYUV420Planar) { return (usage & BufferUsage::VIDEO_DECODER) != 0; } RETURN(false); } } Error3 validateBufferSizeImpl(const cb_handle_t& /*cb*/, const BufferDescriptorInfo& /*descriptor*/, uint32_t /*stride*/) { RETURN(Error3::NONE); } HostConnectionSession getHostConnectionSession() const { return HostConnectionSession(m_hostConn.get()); } static void encodeBufferDescriptorInfo(const BufferDescriptorInfo& d, hidl_vec* raw) { raw->resize(5); (*raw)[0] = d.width; (*raw)[1] = d.height; (*raw)[2] = d.layerCount; (*raw)[3] = static_cast(d.format); (*raw)[4] = d.usage & UINT32_MAX; } uint64_t getMmapedPhysAddr(uint64_t offset) const { return m_physAddrToOffset + offset; } std::unique_ptr m_hostConn; uint64_t m_physAddrToOffset; }; } // namespace extern "C" IMapper3* HIDL_FETCH_IMapper(const char* /*name*/) { return new GoldfishMapper; }