/* * 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 #include "glUtils.h" #include "cb_handle_30.h" #include "host_connection_session.h" #include "types.h" #include "debug.h" const int kOMX_COLOR_FormatYUV420Planar = 19; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_vec; using ::android::hardware::hidl_bitfield; 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 AllocatorV3 = ::android::hardware::graphics::allocator::V3_0; namespace MapperV3 = ::android::hardware::graphics::mapper::V3_0; using IAllocator3 = AllocatorV3::IAllocator; using IMapper3 = MapperV3::IMapper; using Error3 = MapperV3::Error; using BufferDescriptorInfo = IMapper3::BufferDescriptorInfo; class GoldfishAllocator : public IAllocator3 { public: GoldfishAllocator() : m_hostConn(HostConnection::createUnique()) {} Return dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { hidl_cb("GoldfishAllocator::dumpDebugInfo is not implemented"); return {}; } Return allocate(const hidl_vec& rawDescriptor, uint32_t count, allocate_cb hidl_cb) { uint32_t stride = 0; std::vector cbs; cbs.reserve(count); const Error3 e = allocateImpl(rawDescriptor, count, &stride, &cbs); if (e == Error3::NONE) { hidl_vec handles(cbs.cbegin(), cbs.cend()); hidl_cb(Error3::NONE, stride, handles); } else { hidl_cb(e, 0, {}); } for (cb_handle_30_t* cb : cbs) { freeCb(std::unique_ptr(cb)); } return {}; } private: // this function should be in sync with GoldfishMapper::isSupportedImpl Error3 allocateImpl(const hidl_vec& rawDescriptor, uint32_t count, uint32_t* pStride, std::vector* cbs) { BufferDescriptorInfo descriptor; if (!decodeBufferDescriptorInfo(rawDescriptor, &descriptor)) { RETURN_ERROR(Error3::BAD_DESCRIPTOR); } if (!descriptor.width) { RETURN_ERROR(Error3::UNSUPPORTED); } if (!descriptor.height) { RETURN_ERROR(Error3::UNSUPPORTED); } if (descriptor.layerCount != 1) { RETURN_ERROR(Error3::UNSUPPORTED); } const uint32_t usage = descriptor.usage; 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; int bpp = 1; int glFormat = 0; int glType = 0; int align = 1; bool yuv_format = false; EmulatorFrameworkFormat emulatorFrameworkFormat = EmulatorFrameworkFormat::GL_COMPATIBLE; PixelFormat format; Error3 e = getBufferFormat(descriptor.format, usage, &format); if (e != Error3::NONE) { ALOGE("%s:%d Unsupported format: frameworkFormat=%d, usage=%x", __func__, __LINE__, descriptor.format, usage); return e; } switch (format) { case PixelFormat::RGBA_8888: case PixelFormat::RGBX_8888: case PixelFormat::BGRA_8888: bpp = 4; glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; break; case PixelFormat::RGB_888: if (usage & (BufferUsage::GPU_TEXTURE | BufferUsage::GPU_RENDER_TARGET | BufferUsage::COMPOSER_OVERLAY | BufferUsage::COMPOSER_CLIENT_TARGET)) { RETURN_ERROR(Error3::UNSUPPORTED); } else { bpp = 3; glFormat = GL_RGB; glType = GL_UNSIGNED_BYTE; } break; case PixelFormat::RGB_565: bpp = 2; glFormat = GL_RGB565; glType = GL_UNSIGNED_SHORT_5_6_5; break; case PixelFormat::RGBA_FP16: bpp = 8; glFormat = GL_RGBA16F; glType = GL_HALF_FLOAT; break; case PixelFormat::RGBA_1010102: bpp = 4; glFormat = GL_RGB10_A2; glType = GL_UNSIGNED_INT_2_10_10_10_REV; break; case PixelFormat::RAW16: case PixelFormat::Y16: bpp = 2; align = 16 * bpp; if (!((usageSwRead || usageHwCamRead) && (usageSwWrite || usageHwCamWrite))) { // Raw sensor data or Y16 only goes between camera and CPU RETURN_ERROR(Error3::UNSUPPORTED); } // Not expecting to actually create any GL surfaces for this glFormat = GL_LUMINANCE; glType = GL_UNSIGNED_SHORT; break; case PixelFormat::BLOB: if (!usageSwRead) { // Blob data cannot be used by HW other than camera emulator // But there is a CTS test trying to have access to it // BUG: https://buganizer.corp.google.com/issues/37719518 RETURN_ERROR(Error3::UNSUPPORTED); } // Not expecting to actually create any GL surfaces for this glFormat = GL_LUMINANCE; glType = GL_UNSIGNED_BYTE; break; case PixelFormat::YCRCB_420_SP: yuv_format = true; // Not expecting to actually create any GL surfaces for this break; case PixelFormat::YV12: align = 16; yuv_format = true; // We are going to use RGB8888 on the host for Vulkan glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; emulatorFrameworkFormat = EmulatorFrameworkFormat::YV12; break; case PixelFormat::YCBCR_420_888: yuv_format = true; // We are going to use RGBA 8888 on the host glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; emulatorFrameworkFormat = EmulatorFrameworkFormat::YUV_420_888; break; default: if (static_cast(format) == android::hardware::graphics::common::V1_1::PixelFormat::YCBCR_P010) { yuv_format = true; glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; bpp = 2; break; } ALOGE("%s:%d Unsupported format: format=%d, frameworkFormat=%d, usage=%x", __func__, __LINE__, format, descriptor.format, usage); RETURN_ERROR(Error3::UNSUPPORTED); } const size_t align1 = align - 1; const uint32_t width = descriptor.width; const uint32_t height = descriptor.height; uint32_t stride; size_t bufferSize; if (yuv_format) { const size_t yStride = (width * bpp + align1) & ~align1; const size_t uvStride = (yStride / 2 + align1) & ~align1; const size_t uvHeight = height / 2; bufferSize = yStride * height + 2 * (uvHeight * uvStride); stride = yStride / bpp; } else { const size_t bpr = (width * bpp + align1) & ~align1; bufferSize = bpr * height; stride = bpr / bpp; } *pStride = stride; return allocateImpl2(usage, width, height, format, emulatorFrameworkFormat, glFormat, glType, bufferSize, bpp, stride, count, cbs); } Error3 allocateImpl2(const uint32_t usage, const uint32_t width, const uint32_t height, const PixelFormat format, const EmulatorFrameworkFormat emulatorFrameworkFormat, const int glFormat, const int glType, const size_t bufferSize, const uint32_t bytesPerPixel, const uint32_t stride, const uint32_t count, std::vector* cbs) { for (uint32_t i = 0; i < count; ++i) { cb_handle_30_t* cb; Error3 e = allocateCb(usage, width, height, format, emulatorFrameworkFormat, glFormat, glType, bufferSize, bytesPerPixel, stride, &cb); if (e == Error3::NONE) { cbs->push_back(cb); } else { return e; } } RETURN(Error3::NONE); } // see GoldfishMapper::encodeBufferDescriptorInfo static bool decodeBufferDescriptorInfo(const hidl_vec& raw, BufferDescriptorInfo* d) { if (raw.size() == 5) { d->width = raw[0]; d->height = raw[1]; d->layerCount = raw[2]; d->format = static_cast(raw[3]); d->usage = raw[4]; RETURN(true); } else { RETURN_ERROR(false); } } static Error3 getBufferFormat(const PixelFormat frameworkFormat, const uint32_t usage, PixelFormat* format) { if (frameworkFormat == PixelFormat::IMPLEMENTATION_DEFINED) { if (usage & BufferUsage::CAMERA_OUTPUT) { if (usage & BufferUsage::GPU_TEXTURE) { // Camera-to-display is RGBA *format = PixelFormat::RGBA_8888; RETURN(Error3::NONE); } else if (usage & BufferUsage::VIDEO_ENCODER) { // Camera-to-encoder is NV21 *format = PixelFormat::YCRCB_420_SP; RETURN(Error3::NONE); } else { // b/189957071 *format = PixelFormat::YCBCR_420_888; RETURN(Error3::NONE); } } RETURN_ERROR(Error3::UNSUPPORTED); } else if (static_cast(frameworkFormat) == kOMX_COLOR_FormatYUV420Planar && (usage & BufferUsage::VIDEO_DECODER)) { ALOGW("gralloc_alloc: Requested OMX_COLOR_FormatYUV420Planar, given " "YCbCr_420_888, taking experimental path. " "usage=%x", usage); *format = PixelFormat::YCBCR_420_888; RETURN(Error3::NONE); } else { *format = frameworkFormat; RETURN(Error3::NONE); } } static bool needHostCb(const uint32_t usage, const PixelFormat format) { if (static_cast(format) == android::hardware::graphics::common::V1_1::PixelFormat::YCBCR_P010) { return false; } // b/186585177 if ((usage & (BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK)) && (0 == (usage & ~(BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK)))) { return false; } return ((usage & BufferUsage::GPU_DATA_BUFFER) || (format != PixelFormat::BLOB && format != PixelFormat::RAW16 && format != PixelFormat::Y16)) && (usage & (BufferUsage::GPU_TEXTURE | BufferUsage::GPU_RENDER_TARGET | BufferUsage::COMPOSER_OVERLAY | BufferUsage::VIDEO_ENCODER | BufferUsage::VIDEO_DECODER | BufferUsage::COMPOSER_CLIENT_TARGET | BufferUsage::CPU_READ_MASK)); } Error3 allocateCb(const uint32_t usage, const uint32_t width, const uint32_t height, const PixelFormat format, const EmulatorFrameworkFormat emulatorFrameworkFormat, const int glFormat, const int glType, const size_t bufferSize, const int32_t bytesPerPixel, const int32_t stride, cb_handle_30_t** cb) { const HostConnectionSession conn = getHostConnectionSession(); ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); CRASH_IF(!rcEnc, "conn.getRcEncoder() failed"); GoldfishAddressSpaceHostMemoryAllocator host_memory_allocator( rcEnc->featureInfo_const()->hasSharedSlotsHostMemoryAllocator); if (!host_memory_allocator.is_opened()) { RETURN_ERROR(Error3::NO_RESOURCES); } GoldfishAddressSpaceBlock bufferBits; if (host_memory_allocator.hostMalloc(&bufferBits, bufferSize)) { RETURN_ERROR(Error3::NO_RESOURCES); } uint32_t hostHandle = 0; QEMU_PIPE_HANDLE hostHandleRefCountFd = QEMU_PIPE_INVALID_HANDLE; if (needHostCb(usage, format)) { hostHandleRefCountFd = qemu_pipe_open("refcount"); if (!qemu_pipe_valid(hostHandleRefCountFd)) { RETURN_ERROR(Error3::NO_RESOURCES); } const GLenum allocFormat = (PixelFormat::RGBX_8888 == format) ? GL_RGB : glFormat; hostHandle = rcEnc->rcCreateColorBufferDMA( rcEnc, width, height, allocFormat, static_cast(emulatorFrameworkFormat)); if (!hostHandle) { qemu_pipe_close(hostHandleRefCountFd); RETURN_ERROR(Error3::NO_RESOURCES); } if (qemu_pipe_write(hostHandleRefCountFd, &hostHandle, sizeof(hostHandle)) != sizeof(hostHandle)) { rcEnc->rcCloseColorBuffer(rcEnc, hostHandle); qemu_pipe_close(hostHandleRefCountFd); RETURN_ERROR(Error3::NO_RESOURCES); } } std::unique_ptr handle = std::make_unique( host_memory_allocator.release(), hostHandleRefCountFd, hostHandle, usage, width, height, static_cast(format), glFormat, glType, bufferSize, bufferBits.guestPtr(), bufferBits.size(), bufferBits.offset(), bytesPerPixel, stride); bufferBits.release(); *cb = handle.release(); RETURN(Error3::NONE); } void freeCb(std::unique_ptr cb) { // no need to undo .hostMalloc: the kernel will take care of it once the // last bufferFd (duped) is closed. if (qemu_pipe_valid(cb->hostHandleRefCountFd)) { qemu_pipe_close(cb->hostHandleRefCountFd); } GoldfishAddressSpaceBlock::memoryUnmap(cb->getBufferPtr(), cb->mmapedSize); GoldfishAddressSpaceHostMemoryAllocator::closeHandle(cb->bufferFd); } HostConnectionSession getHostConnectionSession() const { return HostConnectionSession(m_hostConn.get()); } std::unique_ptr m_hostConn; }; int main(int, char**) { using ::android::sp; ::android::hardware::configureRpcThreadpool(4, true /* callerWillJoin */); sp allocator(new GoldfishAllocator()); if (allocator->registerAsService() != ::android::NO_ERROR) { ALOGE("failed to register graphics IAllocator@3.0 service"); return -EINVAL; } ALOGI("graphics IAllocator@3.0 service is initialized"); ::android::hardware::joinRpcThreadpool(); ALOGI("graphics IAllocator@3.0 service is terminating"); return 0; }