Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "HardwareBitmapUploader.h"
     18 
     19 #include "hwui/Bitmap.h"
     20 #include "renderthread/EglManager.h"
     21 #include "renderthread/VulkanManager.h"
     22 #include "thread/ThreadBase.h"
     23 #include "utils/TimeUtils.h"
     24 
     25 #include <EGL/eglext.h>
     26 #include <GLES2/gl2.h>
     27 #include <GLES2/gl2ext.h>
     28 #include <GLES3/gl3.h>
     29 #include <GrContext.h>
     30 #include <SkCanvas.h>
     31 #include <SkImage.h>
     32 #include <utils/GLUtils.h>
     33 #include <utils/Trace.h>
     34 #include <utils/TraceUtils.h>
     35 #include <thread>
     36 
     37 namespace android::uirenderer {
     38 
     39 class AHBUploader;
     40 // This helper uploader classes allows us to upload using either EGL or Vulkan using the same
     41 // interface.
     42 static sp<AHBUploader> sUploader = nullptr;
     43 
     44 struct FormatInfo {
     45     PixelFormat pixelFormat;
     46     GLint format, type;
     47     VkFormat vkFormat;
     48     bool isSupported = false;
     49     bool valid = true;
     50 };
     51 
     52 class AHBUploader : public RefBase {
     53 public:
     54     virtual ~AHBUploader() {}
     55 
     56     // Called to start creation of the Vulkan and EGL contexts on another thread before we actually
     57     // need to do an upload.
     58     void initialize() {
     59         onInitialize();
     60     }
     61 
     62     void destroy() {
     63         std::lock_guard _lock{mLock};
     64         LOG_ALWAYS_FATAL_IF(mPendingUploads, "terminate called while uploads in progress");
     65         if (mUploadThread) {
     66             mUploadThread->requestExit();
     67             mUploadThread->join();
     68             mUploadThread = nullptr;
     69         }
     70         onDestroy();
     71     }
     72 
     73     bool uploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
     74                               sp<GraphicBuffer> graphicBuffer) {
     75         ATRACE_CALL();
     76         beginUpload();
     77         bool result = onUploadHardwareBitmap(bitmap, format, graphicBuffer);
     78         endUpload();
     79         return result;
     80     }
     81 
     82     void postIdleTimeoutCheck() {
     83         mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
     84     }
     85 
     86 protected:
     87     std::mutex mLock;
     88     sp<ThreadBase> mUploadThread = nullptr;
     89 
     90 private:
     91     virtual void onInitialize() = 0;
     92     virtual void onIdle() = 0;
     93     virtual void onDestroy() = 0;
     94 
     95     virtual bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
     96                                         sp<GraphicBuffer> graphicBuffer) = 0;
     97     virtual void onBeginUpload() = 0;
     98 
     99     bool shouldTimeOutLocked() {
    100         nsecs_t durationSince = systemTime() - mLastUpload;
    101         return durationSince > 2000_ms;
    102     }
    103 
    104     void idleTimeoutCheck() {
    105         std::lock_guard _lock{mLock};
    106         if (mPendingUploads == 0 && shouldTimeOutLocked()) {
    107             onIdle();
    108         } else {
    109             this->postIdleTimeoutCheck();
    110         }
    111     }
    112 
    113     void beginUpload() {
    114         std::lock_guard _lock{mLock};
    115         mPendingUploads++;
    116 
    117         if (!mUploadThread) {
    118             mUploadThread = new ThreadBase{};
    119         }
    120         if (!mUploadThread->isRunning()) {
    121             mUploadThread->start("GrallocUploadThread");
    122         }
    123 
    124         onBeginUpload();
    125     }
    126 
    127     void endUpload() {
    128         std::lock_guard _lock{mLock};
    129         mPendingUploads--;
    130         mLastUpload = systemTime();
    131     }
    132 
    133     int mPendingUploads = 0;
    134     nsecs_t mLastUpload = 0;
    135 };
    136 
    137 #define FENCE_TIMEOUT 2000000000
    138 
    139 class EGLUploader : public AHBUploader {
    140 private:
    141     void onInitialize() override {}
    142     void onDestroy() override {
    143         mEglManager.destroy();
    144     }
    145     void onIdle() override {
    146         mEglManager.destroy();
    147     }
    148 
    149     void onBeginUpload() override {
    150         if (!mEglManager.hasEglContext()) {
    151             mUploadThread->queue().runSync([this]() {
    152                 this->mEglManager.initialize();
    153                 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    154             });
    155 
    156             this->postIdleTimeoutCheck();
    157         }
    158     }
    159 
    160 
    161     EGLDisplay getUploadEglDisplay() {
    162         std::lock_guard _lock{mLock};
    163         LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(), "Forgot to begin an upload?");
    164         return mEglManager.eglDisplay();
    165     }
    166 
    167     bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
    168                                 sp<GraphicBuffer> graphicBuffer) override {
    169         ATRACE_CALL();
    170 
    171         EGLDisplay display = getUploadEglDisplay();
    172 
    173         LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
    174                             uirenderer::renderthread::EglManager::eglErrorString());
    175         // We use an EGLImage to access the content of the GraphicBuffer
    176         // The EGL image is later bound to a 2D texture
    177         EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
    178         AutoEglImage autoImage(display, clientBuffer);
    179         if (autoImage.image == EGL_NO_IMAGE_KHR) {
    180             ALOGW("Could not create EGL image, err =%s",
    181                   uirenderer::renderthread::EglManager::eglErrorString());
    182             return false;
    183         }
    184 
    185         {
    186             ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
    187             EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR {
    188                 AutoSkiaGlTexture glTexture;
    189                 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
    190                 GL_CHECKPOINT(MODERATE);
    191 
    192                 // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
    193                 // provide.
    194                 // But asynchronous in sense that driver may upload texture onto hardware buffer
    195                 // when we first use it in drawing
    196                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
    197                                 format.format, format.type, bitmap.getPixels());
    198                 GL_CHECKPOINT(MODERATE);
    199 
    200                 EGLSyncKHR uploadFence =
    201                         eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
    202                 LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR,
    203                                     "Could not create sync fence %#x", eglGetError());
    204                 glFlush();
    205                 return uploadFence;
    206             });
    207 
    208             EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
    209             LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
    210                                 "Failed to wait for the fence %#x", eglGetError());
    211 
    212             eglDestroySyncKHR(display, fence);
    213         }
    214         return true;
    215     }
    216 
    217     renderthread::EglManager mEglManager;
    218 };
    219 
    220 class VkUploader : public AHBUploader {
    221 private:
    222     void onInitialize() override {
    223         std::lock_guard _lock{mLock};
    224         if (!mUploadThread) {
    225             mUploadThread = new ThreadBase{};
    226         }
    227         if (!mUploadThread->isRunning()) {
    228             mUploadThread->start("GrallocUploadThread");
    229         }
    230 
    231         mUploadThread->queue().post([this]() {
    232             std::lock_guard _lock{mVkLock};
    233             if (!mVulkanManager.hasVkContext()) {
    234                 mVulkanManager.initialize();
    235             }
    236         });
    237     }
    238     void onDestroy() override {
    239         mGrContext.reset();
    240         mVulkanManager.destroy();
    241     }
    242     void onIdle() override {
    243         mGrContext.reset();
    244     }
    245 
    246     void onBeginUpload() override {
    247         {
    248             std::lock_guard _lock{mVkLock};
    249             if (!mVulkanManager.hasVkContext()) {
    250                 LOG_ALWAYS_FATAL_IF(mGrContext,
    251                     "GrContext exists with no VulkanManager for vulkan uploads");
    252                 mUploadThread->queue().runSync([this]() {
    253                     mVulkanManager.initialize();
    254                 });
    255             }
    256         }
    257         if (!mGrContext) {
    258             GrContextOptions options;
    259             mGrContext = mVulkanManager.createContext(options);
    260             LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads");
    261             this->postIdleTimeoutCheck();
    262         }
    263     }
    264 
    265     bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
    266                                 sp<GraphicBuffer> graphicBuffer) override {
    267         ATRACE_CALL();
    268 
    269         std::lock_guard _lock{mLock};
    270 
    271         sk_sp<SkImage> image = SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(),
    272             bitmap.pixmap(), reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()));
    273         return (image.get() != nullptr);
    274     }
    275 
    276     sk_sp<GrContext> mGrContext;
    277     renderthread::VulkanManager mVulkanManager;
    278     std::mutex mVkLock;
    279 };
    280 
    281 bool HardwareBitmapUploader::hasFP16Support() {
    282     static std::once_flag sOnce;
    283     static bool hasFP16Support = false;
    284 
    285     // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so
    286     // we don't need to double-check the GLES version/extension.
    287     std::call_once(sOnce, []() {
    288         sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16,
    289                                                      GraphicBuffer::USAGE_HW_TEXTURE |
    290                                                              GraphicBuffer::USAGE_SW_WRITE_NEVER |
    291                                                              GraphicBuffer::USAGE_SW_READ_NEVER,
    292                                                      "tempFp16Buffer");
    293         status_t error = buffer->initCheck();
    294         hasFP16Support = !error;
    295     });
    296 
    297     return hasFP16Support;
    298 }
    299 
    300 static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
    301     FormatInfo formatInfo;
    302     switch (skBitmap.info().colorType()) {
    303         case kRGBA_8888_SkColorType:
    304             formatInfo.isSupported = true;
    305         // ARGB_4444 is upconverted to RGBA_8888
    306         case kARGB_4444_SkColorType:
    307             formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
    308             formatInfo.format = GL_RGBA;
    309             formatInfo.type = GL_UNSIGNED_BYTE;
    310             formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
    311             break;
    312         case kRGBA_F16_SkColorType:
    313             formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support();
    314             if (formatInfo.isSupported) {
    315                 formatInfo.type = GL_HALF_FLOAT;
    316                 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
    317                 formatInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
    318             } else {
    319                 formatInfo.type = GL_UNSIGNED_BYTE;
    320                 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
    321                 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
    322             }
    323             formatInfo.format = GL_RGBA;
    324             break;
    325         case kRGB_565_SkColorType:
    326             formatInfo.isSupported = true;
    327             formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
    328             formatInfo.format = GL_RGB;
    329             formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
    330             formatInfo.vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
    331             break;
    332         case kGray_8_SkColorType:
    333             formatInfo.isSupported = usingGL;
    334             formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
    335             formatInfo.format = GL_LUMINANCE;
    336             formatInfo.type = GL_UNSIGNED_BYTE;
    337             formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
    338             break;
    339         default:
    340             ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
    341             formatInfo.valid = false;
    342     }
    343     return formatInfo;
    344 }
    345 
    346 static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
    347     if (format.isSupported) {
    348         return source;
    349     } else {
    350         SkBitmap bitmap;
    351         const SkImageInfo& info = source.info();
    352         bitmap.allocPixels(info.makeColorType(kN32_SkColorType));
    353 
    354         SkCanvas canvas(bitmap);
    355         canvas.drawColor(0);
    356         canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
    357 
    358         return bitmap;
    359     }
    360 }
    361 
    362 
    363 static void createUploader(bool usingGL) {
    364     static std::mutex lock;
    365     std::lock_guard _lock{lock};
    366     if (!sUploader.get()) {
    367         if (usingGL) {
    368             sUploader = new EGLUploader();
    369         } else {
    370             sUploader = new VkUploader();
    371         }
    372     }
    373 }
    374 
    375 sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) {
    376     ATRACE_CALL();
    377 
    378     bool usingGL = uirenderer::Properties::getRenderPipelineType() ==
    379             uirenderer::RenderPipelineType::SkiaGL;
    380 
    381     FormatInfo format = determineFormat(sourceBitmap, usingGL);
    382     if (!format.valid) {
    383         return nullptr;
    384     }
    385 
    386     SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
    387     sp<GraphicBuffer> buffer = new GraphicBuffer(
    388             static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()),
    389             format.pixelFormat,
    390             GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
    391                     GraphicBuffer::USAGE_SW_READ_NEVER,
    392             std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) +
    393                     "]");
    394 
    395     status_t error = buffer->initCheck();
    396     if (error < 0) {
    397         ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
    398         return nullptr;
    399     }
    400 
    401     createUploader(usingGL);
    402 
    403     if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) {
    404         return nullptr;
    405     }
    406     return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(),
    407                               bitmap.alphaType(), Bitmap::computePalette(bitmap));
    408 }
    409 
    410 void HardwareBitmapUploader::initialize() {
    411     bool usingGL = uirenderer::Properties::getRenderPipelineType() ==
    412             uirenderer::RenderPipelineType::SkiaGL;
    413     createUploader(usingGL);
    414     sUploader->initialize();
    415 }
    416 
    417 void HardwareBitmapUploader::terminate() {
    418     if (sUploader) {
    419         sUploader->destroy();
    420     }
    421 }
    422 
    423 }  // namespace android::uirenderer
    424