| /* |
| * 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. |
| */ |
| |
| #include <string.h> |
| #include <pthread.h> |
| #include <limits.h> |
| #include <unistd.h> |
| #include <errno.h> |
| |
| #include <set> |
| #include <map> |
| #include <vector> |
| #include <string> |
| #include <algorithm> |
| |
| #include <log/log.h> |
| #include <sys/mman.h> |
| |
| #include <hardware/hardware.h> |
| #include <hardware/gralloc.h> |
| |
| #include "../../shared/OpenglCodecCommon/gralloc_cb.h" |
| #include "gralloc_common.h" |
| #include "goldfish_address_space.h" |
| #include "HostConnection.h" |
| #include "FormatConversions.h" |
| #include "qemu_pipe.h" |
| |
| #define CRASH(MSG) \ |
| do { \ |
| ALOGE("%s:%d crashed with '%s'", __func__, __LINE__, MSG); \ |
| ::abort(); \ |
| } while (false) |
| |
| #define CRASH_IF(COND, MSG) \ |
| do { \ |
| if ((COND)) { \ |
| ALOGE("%s:%d crashed on '%s' with '%s'", __func__, __LINE__, #COND, MSG); \ |
| ::abort(); \ |
| } \ |
| } while (false) |
| |
| #define RETURN_ERROR_CODE(X) \ |
| do { \ |
| ALOGE("%s:%d failed with '%s' (%d)", \ |
| __func__, __LINE__, strerror(-(X)), -(X)); \ |
| return (X); \ |
| } while (false) |
| |
| #define RETURN_ERROR(X) \ |
| do { \ |
| ALOGE("%s:%d failed with '%s'", __func__, __LINE__, #X); \ |
| return (X); \ |
| } while (false) |
| |
| namespace { |
| |
| const char GOLDFISH_GRALLOC_MODULE_NAME[] = "Graphics Memory Allocator Module"; |
| |
| hw_device_t make_hw_device(hw_module_t* module, int (*close)(hw_device_t*)) { |
| hw_device_t result = {}; |
| |
| result.tag = HARDWARE_DEVICE_TAG; |
| result.version = 0; |
| result.module = module; |
| result.close = close; |
| |
| return result; |
| } |
| |
| size_t align(const size_t v, const size_t a) { return (v + a - 1) & ~(a - 1); } |
| |
| class HostConnectionSession { |
| public: |
| explicit HostConnectionSession(HostConnection* hc) : conn(hc) { |
| hc->lock(); |
| } |
| |
| ~HostConnectionSession() { |
| if (conn) { |
| conn->unlock(); |
| } |
| } |
| |
| HostConnectionSession(HostConnectionSession&& rhs) : conn(rhs.conn) { |
| rhs.conn = nullptr; |
| } |
| |
| HostConnectionSession& operator=(HostConnectionSession&& rhs) { |
| if (this != &rhs) { |
| std::swap(conn, rhs.conn); |
| } |
| return *this; |
| } |
| |
| HostConnectionSession(const HostConnectionSession&) = delete; |
| HostConnectionSession& operator=(const HostConnectionSession&) = delete; |
| |
| ExtendedRCEncoderContext* getRcEncoder() const { |
| return conn->rcEncoder(); |
| } |
| |
| private: |
| HostConnection* conn; |
| }; |
| |
| class goldfish_gralloc30_module_t; |
| class goldfish_gralloc30_device_t; |
| class goldfish_fb30_device_t; |
| |
| class buffer_manager_t { |
| public: |
| buffer_manager_t() = default; |
| buffer_manager_t(const buffer_manager_t&) = delete; |
| buffer_manager_t& operator=(const buffer_manager_t&) = delete; |
| buffer_manager_t(buffer_manager_t&&) = delete; |
| buffer_manager_t& operator=(buffer_manager_t&&) = delete; |
| virtual ~buffer_manager_t() {} |
| |
| virtual uint64_t getMmapedPhysAddr(uint64_t offset) const = 0; |
| |
| virtual int alloc_buffer(int usage, |
| int width, int height, int format, |
| EmulatorFrameworkFormat emulatorFrameworkFormat, |
| int glFormat, int glType, |
| size_t bufferSize, |
| buffer_handle_t* pHandle) = 0; |
| virtual int free_buffer(buffer_handle_t h) = 0; |
| virtual int register_buffer(buffer_handle_t h) = 0; |
| virtual int unregister_buffer(buffer_handle_t h) = 0; |
| }; |
| |
| std::unique_ptr<buffer_manager_t> create_buffer_manager(goldfish_gralloc30_module_t*); |
| |
| class goldfish_gralloc30_module_t { |
| public: |
| goldfish_gralloc30_module_t(): m_hostConn(HostConnection::createUnique()) { |
| CRASH_IF(!m_hostConn, "m_hostConn cannot be nullptr"); |
| m_bufferManager = create_buffer_manager(this); |
| CRASH_IF(!m_bufferManager, "m_bufferManager cannot be nullptr"); |
| } |
| |
| HostConnectionSession getHostConnectionSession() const { |
| return HostConnectionSession(m_hostConn /*.get()*/); |
| } |
| |
| int alloc_buffer(int usage, |
| int width, int height, int format, |
| EmulatorFrameworkFormat emulatorFrameworkFormat, |
| int glFormat, int glType, |
| size_t bufferSize, |
| buffer_handle_t* pHandle) { |
| return m_bufferManager->alloc_buffer(usage, |
| width, height, format, |
| emulatorFrameworkFormat, |
| glFormat, glType, |
| bufferSize, |
| pHandle); |
| } |
| |
| int free_buffer(buffer_handle_t h) { |
| return m_bufferManager->free_buffer(h); |
| } |
| |
| int register_buffer(buffer_handle_t h) { |
| return m_bufferManager->register_buffer(h); |
| } |
| |
| int unregister_buffer(buffer_handle_t h) { |
| return m_bufferManager->unregister_buffer(h); |
| } |
| |
| int lock(cb_handle_t& handle, |
| const int usage, |
| const int left, const int top, const int width, const int height, |
| void** vaddr) { |
| if (!handle.bufferSize) { RETURN_ERROR_CODE(-EINVAL); } |
| char* const bufferBits = static_cast<char*>(handle.getBufferPtr()); |
| if (!bufferBits) { RETURN_ERROR_CODE(-EINVAL); } |
| |
| if (handle.hostHandle) { |
| const int res = lock_impl(handle, |
| usage, |
| left, top, width, height, |
| bufferBits); |
| if (res) { return res; } |
| } |
| |
| *vaddr = bufferBits; |
| return 0; |
| } |
| |
| int unlock(cb_handle_t& handle) { |
| if (!handle.bufferSize) { RETURN_ERROR_CODE(-EINVAL); } |
| |
| char* const bufferBits = static_cast<char*>(handle.getBufferPtr()); |
| if (!bufferBits) { RETURN_ERROR_CODE(-EINVAL); } |
| |
| if (handle.hostHandle) { |
| unlock_impl(handle, bufferBits); |
| } |
| |
| return 0; |
| } |
| |
| int lock_ycbcr(cb_handle_t& handle, |
| const int usage, |
| const int left, const int top, const int width, const int height, |
| android_ycbcr* ycbcr) { |
| if (!ycbcr) { RETURN_ERROR_CODE(-EINVAL); } |
| if (!handle.bufferSize) { RETURN_ERROR_CODE(-EINVAL); } |
| char* const bufferBits = static_cast<char*>(handle.getBufferPtr()); |
| if (!bufferBits) { RETURN_ERROR_CODE(-EINVAL); } |
| |
| size_t uOffset; |
| size_t vOffset; |
| size_t yStride; |
| size_t cStride; |
| size_t cStep; |
| |
| switch (handle.format) { |
| case HAL_PIXEL_FORMAT_YCrCb_420_SP: |
| yStride = handle.width; |
| cStride = yStride; |
| vOffset = yStride * handle.height; |
| uOffset = vOffset + 1; |
| cStep = 2; |
| break; |
| |
| case HAL_PIXEL_FORMAT_YV12: |
| // https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12 |
| yStride = align(handle.width, 16); |
| cStride = align(yStride / 2, 16); |
| vOffset = yStride * handle.height; |
| uOffset = vOffset + (cStride * handle.height / 2); |
| cStep = 1; |
| break; |
| |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| yStride = handle.width; |
| cStride = yStride / 2; |
| uOffset = handle.height * yStride; |
| vOffset = uOffset + cStride * handle.height / 2; |
| cStep = 1; |
| break; |
| |
| default: |
| ALOGE("%s:%d unexpected format (%d)", __func__, __LINE__, handle.format); |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| if (handle.hostHandle) { |
| const int res = lock_impl(handle, |
| usage, |
| left, top, width, height, |
| bufferBits); |
| if (res) { return res; } |
| } |
| |
| memset(ycbcr->reserved, 0, sizeof(ycbcr->reserved)); |
| char* const vaddr1 = static_cast<char*>(bufferBits); |
| ycbcr->y = vaddr1; |
| ycbcr->cb = vaddr1 + uOffset; |
| ycbcr->cr = vaddr1 + vOffset; |
| ycbcr->ystride = yStride; |
| ycbcr->cstride = cStride; |
| ycbcr->chroma_step = cStep; |
| return 0; |
| } |
| |
| private: |
| int lock_impl(cb_handle_t& handle, |
| const int usage, |
| const int left, const int top, const int width, const int height, |
| char* const bufferBits) { |
| const bool usageSwRead = usage & GRALLOC_USAGE_SW_READ_MASK; |
| const bool usageSwWrite = usage & GRALLOC_USAGE_SW_WRITE_MASK; |
| const bool usageHwCamera = usage & GRALLOC_USAGE_HW_CAMERA_MASK; |
| const bool usageHwCameraWrite = usage & GRALLOC_USAGE_HW_CAMERA_WRITE; |
| |
| const HostConnectionSession conn = getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| |
| const int res = rcEnc->rcColorBufferCacheFlush( |
| rcEnc, handle.hostHandle, 0, usageSwRead); |
| if (res < 0) { |
| RETURN_ERROR_CODE(-EBUSY); |
| } |
| |
| // camera delivers bits to the buffer directly and does not require |
| // an explicit read. |
| if (usageSwRead && !usageHwCamera) { |
| if (gralloc_is_yuv_format(handle.format)) { |
| if (rcEnc->hasYUVCache()) { |
| uint32_t bufferSize; |
| |
| switch (handle.format) { |
| case HAL_PIXEL_FORMAT_YV12: |
| get_yv12_offsets(handle.width, handle.height, |
| nullptr, nullptr, &bufferSize); |
| break; |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| get_yuv420p_offsets(handle.width, handle.height, |
| nullptr, nullptr, &bufferSize); |
| break; |
| default: |
| CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); |
| break; |
| } |
| |
| rcEnc->rcReadColorBufferYUV(rcEnc, handle.hostHandle, |
| 0, 0, handle.width, handle.height, |
| bufferBits, bufferSize); |
| } else { |
| // We are using RGB888 |
| std::vector<char> tmpBuf(handle.width * handle.height * 3); |
| rcEnc->rcReadColorBuffer(rcEnc, handle.hostHandle, |
| 0, 0, handle.width, handle.height, |
| handle.glFormat, handle.glType, |
| tmpBuf.data()); |
| |
| switch (handle.format) { |
| case HAL_PIXEL_FORMAT_YV12: |
| rgb888_to_yv12(bufferBits, tmpBuf.data(), |
| handle.width, handle.height, |
| left, top, |
| left + width - 1, top + height - 1); |
| break; |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| rgb888_to_yuv420p(bufferBits, tmpBuf.data(), |
| handle.width, handle.height, |
| left, top, |
| left + width - 1, top + height - 1); |
| break; |
| default: |
| CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); |
| break; |
| } |
| } |
| } else { |
| rcEnc->rcReadColorBuffer(rcEnc, |
| handle.hostHandle, |
| 0, 0, handle.width, handle.height, |
| handle.glFormat, handle.glType, |
| bufferBits); |
| } |
| } |
| |
| if (usageSwWrite || usageHwCameraWrite) { |
| handle.lockedLeft = left; |
| handle.lockedTop = top; |
| handle.lockedWidth = width; |
| handle.lockedHeight = height; |
| } else { |
| handle.lockedLeft = 0; |
| handle.lockedTop = 0; |
| handle.lockedWidth = handle.width; |
| handle.lockedHeight = handle.height; |
| } |
| |
| return 0; |
| } |
| |
| void unlock_impl(cb_handle_t& handle, char* const bufferBits) { |
| const int bpp = glUtilsPixelBitSize(handle.glFormat, handle.glType) >> 3; |
| const int left = handle.lockedLeft; |
| const int top = handle.lockedTop; |
| const int width = handle.lockedWidth; |
| const int height = handle.lockedHeight; |
| const uint32_t rgbSize = width * height * bpp; |
| |
| std::vector<char> convertedBuf; |
| const char* bitsToSend; |
| uint32_t sizeToSend; |
| if (gralloc_is_yuv_format(handle.format)) { |
| bitsToSend = bufferBits; |
| switch (handle.format) { |
| case HAL_PIXEL_FORMAT_YV12: |
| get_yv12_offsets(width, height, nullptr, nullptr, &sizeToSend); |
| break; |
| |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| get_yuv420p_offsets(width, height, nullptr, nullptr, &sizeToSend); |
| break; |
| |
| default: |
| CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); |
| break; |
| } |
| } else { |
| convertedBuf.resize(rgbSize); |
| copy_rgb_buffer_from_unlocked( |
| convertedBuf.data(), bufferBits, |
| handle.width, |
| width, height, top, left, bpp); |
| bitsToSend = convertedBuf.data(); |
| sizeToSend = rgbSize; |
| } |
| |
| { |
| const HostConnectionSession conn = getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| |
| rcEnc->bindDmaDirectly(bufferBits, |
| m_bufferManager->getMmapedPhysAddr(handle.getMmapedOffset())); |
| rcEnc->rcUpdateColorBufferDMA(rcEnc, handle.hostHandle, |
| left, top, width, height, |
| handle.glFormat, handle.glType, |
| const_cast<char*>(bitsToSend), sizeToSend); |
| } |
| |
| handle.lockedLeft = 0; |
| handle.lockedTop = 0; |
| handle.lockedWidth = 0; |
| handle.lockedHeight = 0; |
| } |
| |
| //std::unique_ptr<HostConnection> m_hostConn; // b/142677230 |
| HostConnection* m_hostConn; |
| std::unique_ptr<buffer_manager_t> m_bufferManager; |
| }; |
| |
| // no ctor/dctor here |
| struct private_module_t { |
| goldfish_gralloc30_module_t* impl() { |
| return &gralloc30; |
| } |
| |
| hw_module_t* to_hw_module() { return &base.common; } |
| |
| static private_module_t* from_hw_module(const hw_module_t* m) { |
| if (!m) { |
| RETURN_ERROR(nullptr); |
| } |
| |
| if ((m->id == GRALLOC_HARDWARE_MODULE_ID) && (m->name == GOLDFISH_GRALLOC_MODULE_NAME)) { |
| return reinterpret_cast<private_module_t*>(const_cast<hw_module_t*>(m)); |
| } else { |
| RETURN_ERROR(nullptr); |
| } |
| } |
| |
| static private_module_t* from_gralloc_module(const gralloc_module_t* m) { |
| if (!m) { |
| RETURN_ERROR(nullptr); |
| } |
| |
| return from_hw_module(&m->common); |
| } |
| |
| gralloc_module_t base; |
| goldfish_gralloc30_module_t gralloc30; |
| }; |
| |
| class goldfish_gralloc30_device_t { |
| alloc_device_t device; |
| goldfish_gralloc30_module_t* gralloc_module; |
| |
| public: |
| goldfish_gralloc30_device_t(private_module_t* module) |
| : gralloc_module(module->impl()) { |
| memset(&device, 0, sizeof(device)); |
| device.common = make_hw_device(module->to_hw_module(), |
| &s_goldfish_gralloc30_device_close); |
| device.alloc = &s_gralloc_alloc; |
| device.free = &s_gralloc_free; |
| } |
| |
| hw_device_t* get_hw_device_ptr() { return &device.common; } |
| |
| private: |
| static int get_buffer_format(const int frameworkFormat, const int usage) { |
| if (frameworkFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { |
| if (usage & GRALLOC_USAGE_HW_CAMERA_WRITE) { |
| if (usage & GRALLOC_USAGE_HW_TEXTURE) { |
| // Camera-to-display is RGBA |
| return HAL_PIXEL_FORMAT_RGBA_8888; |
| } else if (usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) { |
| // Camera-to-encoder is NV21 |
| return HAL_PIXEL_FORMAT_YCrCb_420_SP; |
| } |
| } |
| |
| RETURN_ERROR_CODE(-EINVAL); |
| } else { |
| return frameworkFormat; |
| } |
| } |
| |
| int gralloc_alloc(const int width, const int height, |
| const int frameworkFormat, |
| const int usage, |
| buffer_handle_t* pHandle, |
| int* pStride) { |
| const bool usageSwWrite = usage & GRALLOC_USAGE_SW_WRITE_MASK; |
| const bool usageSwRead = usage & GRALLOC_USAGE_SW_READ_MASK; |
| const bool usageHwTexture = usage & GRALLOC_USAGE_HW_TEXTURE; |
| const bool usageHwRender = usage & GRALLOC_USAGE_HW_RENDER; |
| const bool usageHw2d = usage & GRALLOC_USAGE_HW_2D; |
| const bool usageHwComposer = usage & GRALLOC_USAGE_HW_COMPOSER; |
| const bool usageHwFb = usage & GRALLOC_USAGE_HW_FB; |
| const bool usageHwCamWrite = usage & GRALLOC_USAGE_HW_CAMERA_WRITE; |
| const bool usageHwCamRead = usage & GRALLOC_USAGE_HW_CAMERA_READ; |
| const bool usageRGB888Unsupported = usageHwTexture |
| || usageHwRender ||usageHw2d || usageHwComposer || usageHwFb; |
| |
| int bpp = 1; |
| int glFormat = 0; |
| int glType = 0; |
| int align = 1; |
| bool yuv_format = false; |
| EmulatorFrameworkFormat emulatorFrameworkFormat = FRAMEWORK_FORMAT_GL_COMPATIBLE; |
| |
| const int format = get_buffer_format(frameworkFormat, usage); |
| if (format < 0) { |
| ALOGE("%s:%d Unsupported format: frameworkFormat=%d, usage=%x", |
| __func__, __LINE__, frameworkFormat, usage); |
| return format; |
| } |
| |
| switch (format) { |
| case HAL_PIXEL_FORMAT_RGBA_8888: |
| case HAL_PIXEL_FORMAT_RGBX_8888: |
| case HAL_PIXEL_FORMAT_BGRA_8888: |
| bpp = 4; |
| glFormat = GL_RGBA; |
| glType = GL_UNSIGNED_BYTE; |
| break; |
| |
| case HAL_PIXEL_FORMAT_RGB_888: |
| if (usageRGB888Unsupported) { |
| RETURN_ERROR_CODE(-EINVAL); // we dont support RGB_888 for HW usage |
| } else { |
| bpp = 3; |
| glFormat = GL_RGB; |
| glType = GL_UNSIGNED_BYTE; |
| } |
| break; |
| |
| case HAL_PIXEL_FORMAT_RGB_565: |
| bpp = 2; |
| glFormat = GL_RGB565; |
| glType = GL_UNSIGNED_SHORT_5_6_5; |
| break; |
| |
| case HAL_PIXEL_FORMAT_RGBA_FP16: |
| bpp = 8; |
| glFormat = GL_RGBA16F; |
| glType = GL_HALF_FLOAT; |
| break; |
| |
| case HAL_PIXEL_FORMAT_RGBA_1010102: |
| bpp = 4; |
| glFormat = GL_RGB10_A2; |
| glType = GL_UNSIGNED_INT_2_10_10_10_REV; |
| break; |
| |
| case HAL_PIXEL_FORMAT_RAW16: |
| case HAL_PIXEL_FORMAT_Y16: |
| bpp = 2; |
| align = 16 * bpp; |
| if (!((usageSwRead || usageHwCamRead) && (usageSwWrite || usageHwCamWrite))) { |
| // Raw sensor data or Y16 only goes between camera and CPU |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| // Not expecting to actually create any GL surfaces for this |
| glFormat = GL_LUMINANCE; |
| glType = GL_UNSIGNED_SHORT; |
| break; |
| |
| case HAL_PIXEL_FORMAT_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_CODE(-EINVAL); |
| } |
| // Not expecting to actually create any GL surfaces for this |
| glFormat = GL_LUMINANCE; |
| glType = GL_UNSIGNED_BYTE; |
| break; |
| |
| case HAL_PIXEL_FORMAT_YCrCb_420_SP: |
| yuv_format = true; |
| // Not expecting to actually create any GL surfaces for this |
| break; |
| |
| case HAL_PIXEL_FORMAT_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 = FRAMEWORK_FORMAT_YV12; |
| break; |
| |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| yuv_format = true; |
| // We are going to use RGB888 on the host |
| glFormat = GL_RGB; |
| glType = GL_UNSIGNED_BYTE; |
| emulatorFrameworkFormat = FRAMEWORK_FORMAT_YUV_420_888; |
| break; |
| |
| default: |
| ALOGE("%s:%d Unsupported format: format=%d, frameworkFormat=%d, usage=%x", |
| __func__, __LINE__, format, frameworkFormat, usage); |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| const size_t align1 = align - 1; |
| int 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; |
| } |
| |
| const int res = gralloc_module->alloc_buffer( |
| usage, |
| width, height, format, |
| emulatorFrameworkFormat, |
| glFormat, glType, |
| bufferSize, |
| pHandle); |
| if (res) { |
| return res; |
| } |
| |
| *pStride = stride; |
| return 0; |
| } |
| |
| int gralloc_free(buffer_handle_t h) { |
| return gralloc_module->free_buffer(h); |
| } |
| |
| static int s_goldfish_gralloc30_device_close(hw_device_t* d) { |
| goldfish_gralloc30_device_t* gd = from_hw_device(d); |
| if (!gd) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| std::unique_ptr<goldfish_gralloc30_device_t> deleter(gd); |
| return 0; |
| } |
| |
| static int s_gralloc_alloc(alloc_device_t* ad, |
| int w, int h, int format, int usage, |
| buffer_handle_t* pHandle, int* pStride) { |
| goldfish_gralloc30_device_t* gd = from_alloc_device(ad); |
| if (!gd) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return gd->gralloc_alloc(w, h, format, usage, pHandle, pStride); |
| } |
| |
| static int s_gralloc_free(alloc_device_t* ad, buffer_handle_t h) { |
| goldfish_gralloc30_device_t* gd = from_alloc_device(ad); |
| if (!gd) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return gd->gralloc_free(h); |
| } |
| |
| static goldfish_gralloc30_device_t* from_hw_device(hw_device_t* d) { |
| if (!d) { |
| RETURN_ERROR(nullptr); |
| } |
| |
| if (d->close == &s_goldfish_gralloc30_device_close) { |
| return reinterpret_cast<goldfish_gralloc30_device_t*>(d); |
| } else { |
| RETURN_ERROR(nullptr); |
| } |
| } |
| |
| static goldfish_gralloc30_device_t* from_alloc_device(alloc_device_t* d) { |
| if (!d) { |
| RETURN_ERROR(nullptr); |
| } |
| |
| return from_hw_device(&d->common); |
| } |
| }; |
| |
| template <class T> T& unconst(const T& x) { return const_cast<T&>(x); } |
| |
| class goldfish_fb30_device_t { |
| private: |
| framebuffer_device_t device; |
| goldfish_gralloc30_module_t* gralloc_module; |
| |
| public: |
| goldfish_fb30_device_t(private_module_t* module) |
| : device({}), gralloc_module(module->impl()) { |
| device.common = make_hw_device(module->to_hw_module(), |
| &s_goldfish_fb30_device_close); |
| device.setSwapInterval = &s_setSwapInterval; |
| device.post = &s_post; |
| |
| { |
| const HostConnectionSession conn = gralloc_module->getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| |
| unconst(device.flags) = 0; |
| unconst(device.width) = rcEnc->rcGetFBParam(rcEnc, FB_WIDTH); |
| unconst(device.height) = rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT);; |
| unconst(device.stride) = device.width; |
| unconst(device.format) = HAL_PIXEL_FORMAT_RGBA_8888; |
| unconst(device.xdpi) = rcEnc->rcGetFBParam(rcEnc, FB_XDPI); |
| unconst(device.ydpi) = rcEnc->rcGetFBParam(rcEnc, FB_YDPI); |
| unconst(device.fps) = rcEnc->rcGetFBParam(rcEnc, FB_FPS); |
| unconst(device.minSwapInterval) = rcEnc->rcGetFBParam(rcEnc, FB_MIN_SWAP_INTERVAL); |
| unconst(device.maxSwapInterval) = rcEnc->rcGetFBParam(rcEnc, FB_MAX_SWAP_INTERVAL); |
| } |
| } |
| |
| hw_device_t* get_hw_device_ptr() { return &device.common; } |
| |
| private: |
| int setSwapInterval(const int interval) { |
| const HostConnectionSession conn = gralloc_module->getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| |
| rcEnc->rcFBSetSwapInterval(rcEnc, interval); |
| return 0; |
| } |
| |
| int post(cb_handle_t& handle) { |
| if (!(handle.usage & GRALLOC_USAGE_HW_FB)) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| if (handle.hostHandle) { |
| const HostConnectionSession conn = gralloc_module->getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| |
| rcEnc->rcFBPost(rcEnc, handle.hostHandle); |
| } |
| |
| return 0; |
| } |
| |
| static int s_goldfish_fb30_device_close(struct hw_device_t *d) { |
| goldfish_fb30_device_t* fbd = from_hw_device(d); |
| if (!fbd) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| std::unique_ptr<goldfish_fb30_device_t> deleter(fbd); |
| return 0; |
| } |
| |
| static int s_setSwapInterval(struct framebuffer_device_t* d, int interval) { |
| goldfish_fb30_device_t* fbd = from_framebuffer_device(d); |
| if (!fbd) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return fbd->setSwapInterval(interval); |
| } |
| |
| static int s_post(struct framebuffer_device_t* d, buffer_handle_t h) { |
| goldfish_fb30_device_t* fbd = from_framebuffer_device(d); |
| if (!fbd) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| cb_handle_t* handle = cb_handle_t::from_unconst(h); |
| if (!handle) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return fbd->post(*handle); |
| } |
| |
| static goldfish_fb30_device_t* from_hw_device(hw_device_t* d) { |
| if (!d) { |
| RETURN_ERROR(nullptr); |
| } |
| |
| if (d->close == &s_goldfish_fb30_device_close) { |
| return reinterpret_cast<goldfish_fb30_device_t*>(d); |
| } else { |
| RETURN_ERROR(nullptr); |
| } |
| } |
| |
| static goldfish_fb30_device_t* from_framebuffer_device(framebuffer_device_t* d) { |
| if (!d) { |
| RETURN_ERROR(nullptr); |
| } |
| |
| return from_hw_device(&d->common); |
| } |
| }; |
| |
| const uint32_t CB_HANDLE_MAGIC_30 = CB_HANDLE_MAGIC_BASE | 0x2; |
| |
| struct cb_handle_30_t : public cb_handle_t { |
| cb_handle_30_t(address_space_handle_t p_bufferFd, |
| QEMU_PIPE_HANDLE p_hostHandleRefCountFd, |
| uint32_t p_hostHandle, |
| int32_t p_usage, |
| int32_t p_width, |
| int32_t p_height, |
| int32_t p_format, |
| int32_t p_glFormat, |
| int32_t p_glType, |
| uint32_t p_bufSize, |
| void* p_bufPtr, |
| int32_t p_bufferPtrPid, |
| uint32_t p_mmapedSize, |
| uint64_t p_mmapedOffset) |
| : cb_handle_t(p_bufferFd, |
| p_hostHandleRefCountFd, |
| CB_HANDLE_MAGIC_30, |
| p_hostHandle, |
| p_usage, |
| p_width, |
| p_height, |
| p_format, |
| p_glFormat, |
| p_glType, |
| p_bufSize, |
| p_bufPtr, |
| p_mmapedOffset), |
| bufferFdAsInt(p_bufferFd), |
| bufferPtrPid(p_bufferPtrPid), |
| mmapedSize(p_mmapedSize) { |
| numInts = CB_HANDLE_NUM_INTS(numFds); |
| } |
| |
| bool isValid() const { return (version == sizeof(native_handle_t)) && (magic == CB_HANDLE_MAGIC_30); } |
| |
| static cb_handle_30_t* from(void* p) { |
| if (!p) { return nullptr; } |
| cb_handle_30_t* cb = static_cast<cb_handle_30_t*>(p); |
| return cb->isValid() ? cb : nullptr; |
| } |
| |
| static const cb_handle_30_t* from(const void* p) { |
| return from(const_cast<void*>(p)); |
| } |
| |
| static cb_handle_30_t* from_unconst(const void* p) { |
| return from(const_cast<void*>(p)); |
| } |
| |
| int32_t bufferFdAsInt; // int copy of bufferFd, to check if fd was duped |
| int32_t bufferPtrPid; // pid where bufferPtr belongs to |
| uint32_t mmapedSize; // real allocation side |
| }; |
| |
| // goldfish_address_space_host_malloc_handle_manager_t uses |
| // GoldfishAddressSpaceHostMemoryAllocator and GoldfishAddressSpaceBlock |
| // to allocate buffers on the host. |
| // It keeps track of usage of host handles allocated by rcCreateColorBufferDMA |
| // on the guest by qemu_pipe_open("refcount"). |
| class goldfish_address_space_host_malloc_buffer_manager_t : public buffer_manager_t { |
| public: |
| goldfish_address_space_host_malloc_buffer_manager_t(goldfish_gralloc30_module_t* gr): m_gr(gr) { |
| GoldfishAddressSpaceHostMemoryAllocator host_memory_allocator; |
| 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(); |
| } |
| |
| uint64_t getMmapedPhysAddr(uint64_t offset) const override { |
| return m_physAddrToOffset + offset; |
| } |
| |
| int alloc_buffer(int usage, |
| int width, int height, int format, |
| EmulatorFrameworkFormat emulatorFrameworkFormat, |
| int glFormat, int glType, |
| size_t bufferSize, |
| buffer_handle_t* pHandle) override { |
| GoldfishAddressSpaceHostMemoryAllocator host_memory_allocator; |
| if (!host_memory_allocator.is_opened()) { RETURN_ERROR_CODE(-EIO); } |
| |
| GoldfishAddressSpaceBlock bufferBits; |
| if (host_memory_allocator.hostMalloc(&bufferBits, bufferSize)) { RETURN_ERROR_CODE(-EIO); } |
| |
| uint32_t hostHandle = 0; |
| QEMU_PIPE_HANDLE hostHandleRefCountFd = QEMU_PIPE_INVALID_HANDLE; |
| if (need_host_cb(usage, format)) { |
| hostHandleRefCountFd = qemu_pipe_open("refcount"); |
| if (!qemu_pipe_valid(hostHandleRefCountFd)) { RETURN_ERROR_CODE(-EIO); } |
| |
| const GLenum allocFormat = |
| (HAL_PIXEL_FORMAT_RGBX_8888 == format) ? GL_RGB : glFormat; |
| |
| const HostConnectionSession conn = m_gr->getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| |
| hostHandle = rcEnc->rcCreateColorBufferDMA( |
| rcEnc, |
| width, height, |
| allocFormat, emulatorFrameworkFormat); |
| if (!hostHandle) { |
| qemu_pipe_close(hostHandleRefCountFd); |
| RETURN_ERROR_CODE(-EIO); |
| } |
| if (qemu_pipe_write(hostHandleRefCountFd, &hostHandle, sizeof(hostHandle)) != sizeof(hostHandle)) { |
| rcEnc->rcCloseColorBuffer(rcEnc, hostHandle); |
| qemu_pipe_close(hostHandleRefCountFd); |
| RETURN_ERROR_CODE(-EIO); |
| } |
| } |
| |
| std::unique_ptr<cb_handle_30_t> handle = |
| std::make_unique<cb_handle_30_t>( |
| host_memory_allocator.release(), hostHandleRefCountFd, |
| hostHandle, |
| usage, width, height, |
| format, glFormat, glType, |
| bufferSize, bufferBits.guestPtr(), getpid(), |
| bufferBits.size(), bufferBits.offset()); |
| bufferBits.release(); |
| |
| *pHandle = handle.release(); |
| return 0; |
| } |
| |
| int free_buffer(buffer_handle_t h) override { |
| std::unique_ptr<cb_handle_30_t> handle(cb_handle_30_t::from_unconst(h)); |
| if (!handle) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| if (handle->bufferPtrPid != getpid()) { RETURN_ERROR_CODE(-EACCES); } |
| if (handle->bufferFd != handle->bufferFdAsInt) { RETURN_ERROR_CODE(-EACCES); } |
| |
| if (qemu_pipe_valid(handle->hostHandleRefCountFd)) { |
| qemu_pipe_close(handle->hostHandleRefCountFd); |
| } |
| // We can't recycle the address block and host resources because this |
| // fd could be duped. The kernel will take care of it when the last fd |
| // will be closed. |
| if (handle->mmapedSize > 0) { |
| GoldfishAddressSpaceBlock::memoryUnmap(handle->getBufferPtr(), handle->mmapedSize); |
| } |
| GoldfishAddressSpaceHostMemoryAllocator::closeHandle(handle->bufferFd); |
| |
| return 0; |
| } |
| |
| int register_buffer(buffer_handle_t h) override { |
| #ifndef HOST_BUILD |
| cb_handle_30_t *handle = cb_handle_30_t::from_unconst(h); |
| if (!handle) { RETURN_ERROR_CODE(-EINVAL); } |
| |
| if (handle->mmapedSize > 0) { |
| void* ptr; |
| const int res = GoldfishAddressSpaceBlock::memoryMap( |
| handle->getBufferPtr(), |
| handle->mmapedSize, |
| handle->bufferFd, |
| handle->getMmapedOffset(), |
| &ptr); |
| if (res) { |
| RETURN_ERROR_CODE(-res); |
| } |
| |
| handle->setBufferPtr(ptr); |
| } |
| if (handle->hostHandle) { |
| const HostConnectionSession conn = m_gr->getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| rcEnc->rcOpenColorBuffer2(rcEnc, handle->hostHandle); |
| } |
| |
| handle->bufferFdAsInt = handle->bufferFd; |
| handle->bufferPtrPid = getpid(); |
| #endif // HOST_BUILD |
| |
| return 0; |
| } |
| |
| int unregister_buffer(buffer_handle_t h) override { |
| #ifndef HOST_BUILD |
| cb_handle_30_t *handle = cb_handle_30_t::from_unconst(h); |
| if (!handle) { RETURN_ERROR_CODE(-EINVAL); } |
| |
| if (handle->bufferPtrPid != getpid()) { RETURN_ERROR_CODE(-EACCES); } |
| if (handle->bufferFd != handle->bufferFdAsInt) { RETURN_ERROR_CODE(-EACCES); } |
| |
| if (handle->hostHandle) { |
| const HostConnectionSession conn = m_gr->getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| rcEnc->rcCloseColorBuffer(rcEnc, handle->hostHandle); |
| } |
| if (handle->mmapedSize > 0) { |
| GoldfishAddressSpaceBlock::memoryUnmap(handle->getBufferPtr(), handle->mmapedSize); |
| } |
| |
| handle->bufferFdAsInt = -1; |
| handle->bufferPtrPid = -1; |
| #endif // HOST_BUILD |
| return 0; |
| } |
| |
| static bool need_host_cb(const int usage, const int format) { |
| return ((usage & GOLDFISH_GRALLOC_USAGE_GPU_DATA_BUFFER) |
| || (format != HAL_PIXEL_FORMAT_BLOB)) |
| && (usage & (GRALLOC_USAGE_HW_TEXTURE |
| | GRALLOC_USAGE_HW_RENDER |
| | GRALLOC_USAGE_HW_2D |
| | GRALLOC_USAGE_HW_COMPOSER |
| | GRALLOC_USAGE_HW_VIDEO_ENCODER |
| | GRALLOC_USAGE_HW_FB |
| | GRALLOC_USAGE_SW_READ_MASK)); |
| } |
| |
| private: |
| goldfish_gralloc30_module_t* m_gr; |
| uint64_t m_physAddrToOffset; |
| }; |
| |
| std::unique_ptr<buffer_manager_t> create_buffer_manager(goldfish_gralloc30_module_t* gr) { |
| if (!gr) { |
| RETURN_ERROR(nullptr); |
| } |
| |
| // TODO: negotiate with the host the best way to allocate memory. |
| |
| return std::make_unique<goldfish_address_space_host_malloc_buffer_manager_t>(gr); |
| } |
| |
| int gralloc_register_buffer(const gralloc_module_t* gralloc_module, buffer_handle_t h) { |
| private_module_t* module = private_module_t::from_gralloc_module(gralloc_module); |
| if (!module) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return module->impl()->register_buffer(h); |
| } |
| |
| int gralloc_unregister_buffer(const gralloc_module_t* gralloc_module, buffer_handle_t h) { |
| private_module_t* module = private_module_t::from_gralloc_module(gralloc_module); |
| if (!module) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return module->impl()->unregister_buffer(h); |
| } |
| |
| int gralloc_lock(const gralloc_module_t* gralloc_module, |
| buffer_handle_t bh, int usage, |
| int l, int t, int w, int h, |
| void** vaddr) { |
| private_module_t* module = private_module_t::from_gralloc_module(gralloc_module); |
| if (!module) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| cb_handle_t* handle = cb_handle_t::from_unconst(bh); |
| if (!handle) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return module->impl()->lock(*handle, usage, l, t, w, h, vaddr); |
| } |
| |
| int gralloc_unlock(const gralloc_module_t* gralloc_module, buffer_handle_t bh) { |
| private_module_t* module = private_module_t::from_gralloc_module(gralloc_module); |
| if (!module) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| cb_handle_t* handle = cb_handle_t::from_unconst(bh); |
| if (!handle) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return module->impl()->unlock(*handle); |
| } |
| |
| int gralloc_lock_ycbcr(const gralloc_module_t* gralloc_module, |
| buffer_handle_t bh, int usage, |
| int l, int t, int w, int h, |
| android_ycbcr *ycbcr) { |
| private_module_t* module = private_module_t::from_gralloc_module(gralloc_module); |
| if (!module) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| cb_handle_t* handle = cb_handle_t::from_unconst(bh); |
| if (!handle) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| return module->impl()->lock_ycbcr(*handle, usage, l, t, w, h, ycbcr); |
| } |
| |
| int gralloc_device_open_gpu0(private_module_t* module, hw_device_t** device) { |
| std::unique_ptr<goldfish_gralloc30_device_t> gralloc_device = |
| std::make_unique<goldfish_gralloc30_device_t>(module); |
| if (!gralloc_device) { |
| RETURN_ERROR_CODE(-ENOMEM); |
| } |
| |
| *device = gralloc_device->get_hw_device_ptr(); |
| gralloc_device.release(); |
| return 0; |
| } |
| |
| int gralloc_device_open_fb0(private_module_t* module, hw_device_t** device) { |
| std::unique_ptr<goldfish_fb30_device_t> fb_device = |
| std::make_unique<goldfish_fb30_device_t>(module); |
| if (!fb_device) { |
| RETURN_ERROR_CODE(-ENOMEM); |
| } |
| |
| *device = fb_device->get_hw_device_ptr(); |
| fb_device.release(); |
| return 0; |
| } |
| |
| int gralloc_device_open(const hw_module_t* hw_module, |
| const char* name, |
| hw_device_t** device) { |
| private_module_t* module = private_module_t::from_hw_module(hw_module); |
| if (!module) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| if (!name) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| if (!device) { |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { |
| return gralloc_device_open_gpu0(module, device); |
| } |
| |
| if (!strcmp(name, GRALLOC_HARDWARE_FB0)) { |
| return gralloc_device_open_fb0(module, device); |
| } |
| |
| RETURN_ERROR_CODE(-EINVAL); |
| } |
| |
| struct hw_module_methods_t gralloc_module_methods = { |
| .open = &gralloc_device_open |
| }; |
| } // namespace |
| |
| extern "C" __attribute__((visibility("default"))) |
| struct private_module_t HAL_MODULE_INFO_SYM = { |
| .base = { |
| .common = { |
| .tag = HARDWARE_MODULE_TAG, |
| .module_api_version = GRALLOC_MODULE_API_VERSION_0_2, |
| .hal_api_version = 0, |
| .id = GRALLOC_HARDWARE_MODULE_ID, |
| .name = GOLDFISH_GRALLOC_MODULE_NAME, |
| .author = "The Android Open Source Project", |
| .methods = &gralloc_module_methods, |
| .dso = nullptr, |
| .reserved = {0} |
| }, |
| .registerBuffer = &gralloc_register_buffer, |
| .unregisterBuffer = &gralloc_unregister_buffer, |
| .lock = &gralloc_lock, |
| .unlock = &gralloc_unlock, |
| .perform = nullptr, /* reserved for future use */ |
| .lock_ycbcr = &gralloc_lock_ycbcr, |
| }, |
| }; |