From c073252f764529ac7859746d81d9a4a6818037d3 Mon Sep 17 00:00:00 2001 From: Greg Daniel Date: Wed, 20 Feb 2019 08:31:03 -0500 Subject: [PATCH] Add support for uploading to AHBs using Vulkan. Test: manual build and testing on blueline. Change-Id: I240a5f1e3ba34677b03131eba36ffd8783d99041 --- libs/hwui/HardwareBitmapUploader.cpp | 378 ++++++++++++++++++++++---------- libs/hwui/HardwareBitmapUploader.h | 4 +- libs/hwui/renderthread/RenderThread.cpp | 2 + 3 files changed, 272 insertions(+), 112 deletions(-) diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index aeeb32c4620a..8f7e8142e735 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -18,6 +18,7 @@ #include "hwui/Bitmap.h" #include "renderthread/EglManager.h" +#include "renderthread/VulkanManager.h" #include "thread/ThreadBase.h" #include "utils/TimeUtils.h" @@ -25,7 +26,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -33,58 +36,247 @@ namespace android::uirenderer { -static std::mutex sLock{}; -static sp sUploadThread = nullptr; -static renderthread::EglManager sEglManager; -static int sPendingUploads = 0; -static nsecs_t sLastUpload = 0; +class AHBUploader; +// This helper uploader classes allows us to upload using either EGL or Vulkan using the same +// interface. +static sp sUploader = nullptr; -static bool shouldTimeOutLocked() { - nsecs_t durationSince = systemTime() - sLastUpload; - return durationSince > 2000_ms; -} +struct FormatInfo { + PixelFormat pixelFormat; + GLint format, type; + VkFormat vkFormat; + bool isSupported = false; + bool valid = true; +}; -static void checkIdleTimeout() { - std::lock_guard _lock{sLock}; - if (sPendingUploads == 0 && shouldTimeOutLocked()) { - sEglManager.destroy(); - } else { - sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout); +class AHBUploader : public RefBase { +public: + virtual ~AHBUploader() {} + + // Called to start creation of the Vulkan and EGL contexts on another thread before we actually + // need to do an upload. + void initialize() { + onInitialize(); } -} -static void beginUpload() { - std::lock_guard _lock{sLock}; - sPendingUploads++; + void destroy() { + std::lock_guard _lock{mLock}; + LOG_ALWAYS_FATAL_IF(mPendingUploads, "terminate called while uploads in progress"); + if (mUploadThread) { + mUploadThread->requestExit(); + mUploadThread->join(); + mUploadThread = nullptr; + } + onDestroy(); + } + + bool uploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, + sp graphicBuffer) { + ATRACE_CALL(); + beginUpload(); + bool result = onUploadHardwareBitmap(bitmap, format, graphicBuffer); + endUpload(); + return result; + } + + void postIdleTimeoutCheck() { + mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); }); + } - if (!sUploadThread) { - sUploadThread = new ThreadBase{}; +protected: + std::mutex mLock; + sp mUploadThread = nullptr; + +private: + virtual void onInitialize() = 0; + virtual void onIdle() = 0; + virtual void onDestroy() = 0; + + virtual bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, + sp graphicBuffer) = 0; + virtual void onBeginUpload() = 0; + + bool shouldTimeOutLocked() { + nsecs_t durationSince = systemTime() - mLastUpload; + return durationSince > 2000_ms; } - if (!sUploadThread->isRunning()) { - sUploadThread->start("GrallocUploadThread"); + void idleTimeoutCheck() { + std::lock_guard _lock{mLock}; + if (mPendingUploads == 0 && shouldTimeOutLocked()) { + onIdle(); + } else { + this->postIdleTimeoutCheck(); + } } - if (!sEglManager.hasEglContext()) { - sUploadThread->queue().runSync([]() { - sEglManager.initialize(); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + void beginUpload() { + std::lock_guard _lock{mLock}; + mPendingUploads++; + + if (!mUploadThread) { + mUploadThread = new ThreadBase{}; + } + if (!mUploadThread->isRunning()) { + mUploadThread->start("GrallocUploadThread"); + } + + onBeginUpload(); + } + + void endUpload() { + std::lock_guard _lock{mLock}; + mPendingUploads--; + mLastUpload = systemTime(); + } + + int mPendingUploads = 0; + nsecs_t mLastUpload = 0; +}; + +#define FENCE_TIMEOUT 2000000000 + +class EGLUploader : public AHBUploader { +private: + void onInitialize() override {} + void onDestroy() override { + mEglManager.destroy(); + } + void onIdle() override { + mEglManager.destroy(); + } + + void onBeginUpload() override { + if (!mEglManager.hasEglContext()) { + mUploadThread->queue().runSync([this]() { + this->mEglManager.initialize(); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + }); + + this->postIdleTimeoutCheck(); + } + } + + + EGLDisplay getUploadEglDisplay() { + std::lock_guard _lock{mLock}; + LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(), "Forgot to begin an upload?"); + return mEglManager.eglDisplay(); + } + + bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, + sp graphicBuffer) override { + ATRACE_CALL(); + + EGLDisplay display = getUploadEglDisplay(); + + LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", + uirenderer::renderthread::EglManager::eglErrorString()); + // We use an EGLImage to access the content of the GraphicBuffer + // The EGL image is later bound to a 2D texture + EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); + AutoEglImage autoImage(display, clientBuffer); + if (autoImage.image == EGL_NO_IMAGE_KHR) { + ALOGW("Could not create EGL image, err =%s", + uirenderer::renderthread::EglManager::eglErrorString()); + return false; + } + + { + ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height()); + EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR { + AutoSkiaGlTexture glTexture; + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); + GL_CHECKPOINT(MODERATE); + + // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we + // provide. + // But asynchronous in sense that driver may upload texture onto hardware buffer + // when we first use it in drawing + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), + format.format, format.type, bitmap.getPixels()); + GL_CHECKPOINT(MODERATE); + + EGLSyncKHR uploadFence = + eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL); + LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, + "Could not create sync fence %#x", eglGetError()); + glFlush(); + return uploadFence; + }); + + EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT); + LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, + "Failed to wait for the fence %#x", eglGetError()); + + eglDestroySyncKHR(display, fence); + } + return true; + } + + renderthread::EglManager mEglManager; +}; + +class VkUploader : public AHBUploader { +private: + void onInitialize() override { + std::lock_guard _lock{mLock}; + if (!mUploadThread) { + mUploadThread = new ThreadBase{}; + } + if (!mUploadThread->isRunning()) { + mUploadThread->start("GrallocUploadThread"); + } + + mUploadThread->queue().post([this]() { + std::lock_guard _lock{mVkLock}; + if (!mVulkanManager.hasVkContext()) { + mVulkanManager.initialize(); + } }); - sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout); } -} + void onDestroy() override { + mGrContext.reset(); + mVulkanManager.destroy(); + } + void onIdle() override { + mGrContext.reset(); + } -static void endUpload() { - std::lock_guard _lock{sLock}; - sPendingUploads--; - sLastUpload = systemTime(); -} + void onBeginUpload() override { + { + std::lock_guard _lock{mVkLock}; + if (!mVulkanManager.hasVkContext()) { + LOG_ALWAYS_FATAL_IF(mGrContext, + "GrContext exists with no VulkanManager for vulkan uploads"); + mUploadThread->queue().runSync([this]() { + mVulkanManager.initialize(); + }); + } + } + if (!mGrContext) { + GrContextOptions options; + mGrContext = mVulkanManager.createContext(options); + LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads"); + this->postIdleTimeoutCheck(); + } + } -static EGLDisplay getUploadEglDisplay() { - std::lock_guard _lock{sLock}; - LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?"); - return sEglManager.eglDisplay(); -} + bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, + sp graphicBuffer) override { + ATRACE_CALL(); + + std::lock_guard _lock{mLock}; + + sk_sp image = SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), + bitmap.pixmap(), reinterpret_cast(graphicBuffer.get())); + return (image.get() != nullptr); + } + + sk_sp mGrContext; + renderthread::VulkanManager mVulkanManager; + std::mutex mVkLock; +}; bool HardwareBitmapUploader::hasFP16Support() { static std::once_flag sOnce; @@ -105,16 +297,7 @@ bool HardwareBitmapUploader::hasFP16Support() { return hasFP16Support; } -#define FENCE_TIMEOUT 2000000000 - -struct FormatInfo { - PixelFormat pixelFormat; - GLint format, type; - bool isSupported = false; - bool valid = true; -}; - -static FormatInfo determineFormat(const SkBitmap& skBitmap) { +static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) { FormatInfo formatInfo; switch (skBitmap.info().colorType()) { case kRGBA_8888_SkColorType: @@ -124,15 +307,18 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap) { formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; formatInfo.format = GL_RGBA; formatInfo.type = GL_UNSIGNED_BYTE; + formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; break; case kRGBA_F16_SkColorType: formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support(); if (formatInfo.isSupported) { formatInfo.type = GL_HALF_FLOAT; formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16; + formatInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT; } else { formatInfo.type = GL_UNSIGNED_BYTE; formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; + formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; } formatInfo.format = GL_RGBA; break; @@ -141,12 +327,14 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap) { formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565; formatInfo.format = GL_RGB; formatInfo.type = GL_UNSIGNED_SHORT_5_6_5; + formatInfo.vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16; break; case kGray_8_SkColorType: - formatInfo.isSupported = true; + formatInfo.isSupported = usingGL; formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; formatInfo.format = GL_LUMINANCE; formatInfo.type = GL_UNSIGNED_BYTE; + formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; break; default: ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType()); @@ -172,29 +360,37 @@ static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& sourc } } -class ScopedUploadRequest { -public: - ScopedUploadRequest() { beginUpload(); } - ~ScopedUploadRequest() { endUpload(); } -}; + +static void createUploader(bool usingGL) { + static std::mutex lock; + std::lock_guard _lock{lock}; + if (!sUploader.get()) { + if (usingGL) { + sUploader = new EGLUploader(); + } else { + sUploader = new VkUploader(); + } + } +} sk_sp HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) { ATRACE_CALL(); - FormatInfo format = determineFormat(sourceBitmap); + bool usingGL = uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL; + + FormatInfo format = determineFormat(sourceBitmap, usingGL); if (!format.valid) { return nullptr; } - ScopedUploadRequest _uploadRequest{}; - SkBitmap bitmap = makeHwCompatible(format, sourceBitmap); sp buffer = new GraphicBuffer( static_cast(bitmap.width()), static_cast(bitmap.height()), format.pixelFormat, GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | GraphicBuffer::USAGE_SW_READ_NEVER, - std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) + + std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + "]"); status_t error = buffer->initCheck(); @@ -203,64 +399,24 @@ sk_sp HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou return nullptr; } - EGLDisplay display = getUploadEglDisplay(); - - LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", - uirenderer::renderthread::EglManager::eglErrorString()); - // We use an EGLImage to access the content of the GraphicBuffer - // The EGL image is later bound to a 2D texture - EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer(); - AutoEglImage autoImage(display, clientBuffer); - if (autoImage.image == EGL_NO_IMAGE_KHR) { - ALOGW("Could not create EGL image, err =%s", - uirenderer::renderthread::EglManager::eglErrorString()); - return nullptr; - } - - { - ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height()); - EGLSyncKHR fence = sUploadThread->queue().runSync([&]() -> EGLSyncKHR { - AutoSkiaGlTexture glTexture; - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); - GL_CHECKPOINT(MODERATE); - - // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we - // provide. - // But asynchronous in sense that driver may upload texture onto hardware buffer when we - // first - // use it in drawing - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format, - format.type, bitmap.getPixels()); - GL_CHECKPOINT(MODERATE); - - EGLSyncKHR uploadFence = - eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL); - LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, "Could not create sync fence %#x", - eglGetError()); - glFlush(); - return uploadFence; - }); - - EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT); - LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, - "Failed to wait for the fence %#x", eglGetError()); + createUploader(usingGL); - eglDestroySyncKHR(display, fence); + if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) { + return nullptr; } - - return Bitmap::createFrom(buffer.get(), bitmap.colorType(), bitmap.refColorSpace(), + return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(), bitmap.alphaType(), Bitmap::computePalette(bitmap)); } +void HardwareBitmapUploader::initialize() { + bool usingGL = uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL; + createUploader(usingGL); + sUploader->initialize(); +} + void HardwareBitmapUploader::terminate() { - std::lock_guard _lock{sLock}; - LOG_ALWAYS_FATAL_IF(sPendingUploads, "terminate called while uploads in progress"); - if (sUploadThread) { - sUploadThread->requestExit(); - sUploadThread->join(); - sUploadThread = nullptr; - } - sEglManager.destroy(); + sUploader->destroy(); } } // namespace android::uirenderer diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h index 6f41e6db4e32..c300593d47a1 100644 --- a/libs/hwui/HardwareBitmapUploader.h +++ b/libs/hwui/HardwareBitmapUploader.h @@ -22,9 +22,11 @@ namespace android::uirenderer { class ANDROID_API HardwareBitmapUploader { public: - static sk_sp allocateHardwareBitmap(const SkBitmap& sourceBitmap); + static void initialize(); static void terminate(); + static sk_sp allocateHardwareBitmap(const SkBitmap& sourceBitmap); + static bool hasFP16Support(); }; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index def805adeeea..6cce31943d03 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -29,6 +29,7 @@ #include "utils/FatVector.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" +#include "../HardwareBitmapUploader.h" #ifdef HWUI_GLES_WRAP_ENABLED #include "debug/GlesDriver.h" @@ -415,6 +416,7 @@ void RenderThread::preload() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { requireVkContext(); } + HardwareBitmapUploader::initialize(); } } /* namespace renderthread */ -- 2.11.0