Loading libs/hwui/HardwareBitmapUploader.cpp +267 −111 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include "hwui/Bitmap.h" #include "renderthread/EglManager.h" #include "renderthread/VulkanManager.h" #include "thread/ThreadBase.h" #include "utils/TimeUtils.h" Loading @@ -25,7 +26,9 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <GLES3/gl3.h> #include <GrContext.h> #include <SkCanvas.h> #include <SkImage.h> #include <utils/GLUtils.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> Loading @@ -33,59 +36,248 @@ namespace android::uirenderer { static std::mutex sLock{}; static sp<ThreadBase> 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<AHBUploader> sUploader = nullptr; static bool shouldTimeOutLocked() { nsecs_t durationSince = systemTime() - sLastUpload; struct FormatInfo { PixelFormat pixelFormat; GLint format, type; VkFormat vkFormat; bool isSupported = false; bool valid = true; }; 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(); } 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> graphicBuffer) { ATRACE_CALL(); beginUpload(); bool result = onUploadHardwareBitmap(bitmap, format, graphicBuffer); endUpload(); return result; } void postIdleTimeoutCheck() { mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); }); } protected: std::mutex mLock; sp<ThreadBase> 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> graphicBuffer) = 0; virtual void onBeginUpload() = 0; bool shouldTimeOutLocked() { nsecs_t durationSince = systemTime() - mLastUpload; return durationSince > 2000_ms; } static void checkIdleTimeout() { std::lock_guard _lock{sLock}; if (sPendingUploads == 0 && shouldTimeOutLocked()) { sEglManager.destroy(); void idleTimeoutCheck() { std::lock_guard _lock{mLock}; if (mPendingUploads == 0 && shouldTimeOutLocked()) { onIdle(); } else { sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout); this->postIdleTimeoutCheck(); } } static void beginUpload() { std::lock_guard _lock{sLock}; sPendingUploads++; void beginUpload() { std::lock_guard _lock{mLock}; mPendingUploads++; if (!sUploadThread) { sUploadThread = new ThreadBase{}; 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 if (!sUploadThread->isRunning()) { sUploadThread->start("GrallocUploadThread"); class EGLUploader : public AHBUploader { private: void onInitialize() override {} void onDestroy() override { mEglManager.destroy(); } void onIdle() override { mEglManager.destroy(); } if (!sEglManager.hasEglContext()) { sUploadThread->queue().runSync([]() { sEglManager.initialize(); void onBeginUpload() override { if (!mEglManager.hasEglContext()) { mUploadThread->queue().runSync([this]() { this->mEglManager.initialize(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); }); sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout); this->postIdleTimeoutCheck(); } } static void endUpload() { std::lock_guard _lock{sLock}; sPendingUploads--; sLastUpload = systemTime(); 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> 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; } static EGLDisplay getUploadEglDisplay() { std::lock_guard _lock{sLock}; LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?"); return sEglManager.eglDisplay(); { 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(); } }); } void onDestroy() override { mGrContext.reset(); mVulkanManager.destroy(); } void onIdle() override { mGrContext.reset(); } 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(); } } bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, sp<GraphicBuffer> graphicBuffer) override { ATRACE_CALL(); std::lock_guard _lock{mLock}; sk_sp<SkImage> image = SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get())); return (image.get() != nullptr); } sk_sp<GrContext> mGrContext; renderthread::VulkanManager mVulkanManager; std::mutex mVkLock; }; bool HardwareBitmapUploader::hasFP16Support() { static std::once_flag sOnce; static bool hasFP16Support = false; Loading @@ -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: Loading @@ -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; Loading @@ -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()); Loading @@ -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<Bitmap> 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<GraphicBuffer> buffer = new GraphicBuffer( static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(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(); Loading @@ -203,64 +399,24 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou return nullptr; } EGLDisplay display = getUploadEglDisplay(); createUploader(usingGL); 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()); if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) { 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()); eglDestroySyncKHR(display, fence); return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(), bitmap.alphaType(), Bitmap::computePalette(bitmap)); } return Bitmap::createFrom(buffer.get(), 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 libs/hwui/HardwareBitmapUploader.h +3 −1 Original line number Diff line number Diff line Loading @@ -22,9 +22,11 @@ namespace android::uirenderer { class ANDROID_API HardwareBitmapUploader { public: static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap); static void initialize(); static void terminate(); static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap); static bool hasFP16Support(); }; Loading libs/hwui/renderthread/RenderThread.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -415,6 +416,7 @@ void RenderThread::preload() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { requireVkContext(); } HardwareBitmapUploader::initialize(); } } /* namespace renderthread */ Loading Loading
libs/hwui/HardwareBitmapUploader.cpp +267 −111 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include "hwui/Bitmap.h" #include "renderthread/EglManager.h" #include "renderthread/VulkanManager.h" #include "thread/ThreadBase.h" #include "utils/TimeUtils.h" Loading @@ -25,7 +26,9 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <GLES3/gl3.h> #include <GrContext.h> #include <SkCanvas.h> #include <SkImage.h> #include <utils/GLUtils.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> Loading @@ -33,59 +36,248 @@ namespace android::uirenderer { static std::mutex sLock{}; static sp<ThreadBase> 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<AHBUploader> sUploader = nullptr; static bool shouldTimeOutLocked() { nsecs_t durationSince = systemTime() - sLastUpload; struct FormatInfo { PixelFormat pixelFormat; GLint format, type; VkFormat vkFormat; bool isSupported = false; bool valid = true; }; 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(); } 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> graphicBuffer) { ATRACE_CALL(); beginUpload(); bool result = onUploadHardwareBitmap(bitmap, format, graphicBuffer); endUpload(); return result; } void postIdleTimeoutCheck() { mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); }); } protected: std::mutex mLock; sp<ThreadBase> 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> graphicBuffer) = 0; virtual void onBeginUpload() = 0; bool shouldTimeOutLocked() { nsecs_t durationSince = systemTime() - mLastUpload; return durationSince > 2000_ms; } static void checkIdleTimeout() { std::lock_guard _lock{sLock}; if (sPendingUploads == 0 && shouldTimeOutLocked()) { sEglManager.destroy(); void idleTimeoutCheck() { std::lock_guard _lock{mLock}; if (mPendingUploads == 0 && shouldTimeOutLocked()) { onIdle(); } else { sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout); this->postIdleTimeoutCheck(); } } static void beginUpload() { std::lock_guard _lock{sLock}; sPendingUploads++; void beginUpload() { std::lock_guard _lock{mLock}; mPendingUploads++; if (!sUploadThread) { sUploadThread = new ThreadBase{}; 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 if (!sUploadThread->isRunning()) { sUploadThread->start("GrallocUploadThread"); class EGLUploader : public AHBUploader { private: void onInitialize() override {} void onDestroy() override { mEglManager.destroy(); } void onIdle() override { mEglManager.destroy(); } if (!sEglManager.hasEglContext()) { sUploadThread->queue().runSync([]() { sEglManager.initialize(); void onBeginUpload() override { if (!mEglManager.hasEglContext()) { mUploadThread->queue().runSync([this]() { this->mEglManager.initialize(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); }); sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout); this->postIdleTimeoutCheck(); } } static void endUpload() { std::lock_guard _lock{sLock}; sPendingUploads--; sLastUpload = systemTime(); 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> 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; } static EGLDisplay getUploadEglDisplay() { std::lock_guard _lock{sLock}; LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?"); return sEglManager.eglDisplay(); { 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(); } }); } void onDestroy() override { mGrContext.reset(); mVulkanManager.destroy(); } void onIdle() override { mGrContext.reset(); } 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(); } } bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, sp<GraphicBuffer> graphicBuffer) override { ATRACE_CALL(); std::lock_guard _lock{mLock}; sk_sp<SkImage> image = SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get())); return (image.get() != nullptr); } sk_sp<GrContext> mGrContext; renderthread::VulkanManager mVulkanManager; std::mutex mVkLock; }; bool HardwareBitmapUploader::hasFP16Support() { static std::once_flag sOnce; static bool hasFP16Support = false; Loading @@ -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: Loading @@ -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; Loading @@ -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()); Loading @@ -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<Bitmap> 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<GraphicBuffer> buffer = new GraphicBuffer( static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(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(); Loading @@ -203,64 +399,24 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou return nullptr; } EGLDisplay display = getUploadEglDisplay(); createUploader(usingGL); 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()); if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) { 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()); eglDestroySyncKHR(display, fence); return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(), bitmap.alphaType(), Bitmap::computePalette(bitmap)); } return Bitmap::createFrom(buffer.get(), 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
libs/hwui/HardwareBitmapUploader.h +3 −1 Original line number Diff line number Diff line Loading @@ -22,9 +22,11 @@ namespace android::uirenderer { class ANDROID_API HardwareBitmapUploader { public: static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap); static void initialize(); static void terminate(); static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap); static bool hasFP16Support(); }; Loading
libs/hwui/renderthread/RenderThread.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -415,6 +416,7 @@ void RenderThread::preload() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { requireVkContext(); } HardwareBitmapUploader::initialize(); } } /* namespace renderthread */ Loading