diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 5f803da4b23fd..f04abecade20f 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -63,14 +63,17 @@ static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) { static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jobject jbitmap, jint tileModeX, jint tileModeY) { const SkMatrix* matrix = reinterpret_cast(matrixPtr); - SkBitmap bitmap; + sk_sp image; if (jbitmap) { // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise, // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility. - android::bitmap::toBitmap(env, jbitmap).getSkBitmapForShaders(&bitmap); + image = android::bitmap::toBitmap(env, jbitmap).makeImage(); } - sk_sp image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + if (!image.get()) { + SkBitmap bitmap; + image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + } sk_sp baseShader = image->makeShader( (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY); diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 4e6a7db45fef2..6dde005c2fc42 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -26,16 +26,12 @@ #include #include -#include -#include -#include -#include - #include #include #include #include +#include namespace android { @@ -89,171 +85,6 @@ static sk_sp allocateHeapBitmap(size_t size, const SkImageInfo& info, si return sk_sp(new Bitmap(addr, size, info, rowBytes, std::move(ctable))); } -#define FENCE_TIMEOUT 2000000000 - -// TODO: handle SRGB sanely -static PixelFormat internalFormatToPixelFormat(GLint internalFormat) { - switch (internalFormat) { - case GL_LUMINANCE: - return PIXEL_FORMAT_RGBA_8888; - case GL_SRGB8_ALPHA8: - return PIXEL_FORMAT_RGBA_8888; - case GL_RGBA: - return PIXEL_FORMAT_RGBA_8888; - case GL_RGB: - return PIXEL_FORMAT_RGB_565; - case GL_RGBA16F: - return PIXEL_FORMAT_RGBA_FP16; - default: - LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat); - return PIXEL_FORMAT_UNKNOWN; - } -} - -class AutoEglFence { -public: - AutoEglFence(EGLDisplay display) - : mDisplay(display) { - fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL); - } - - ~AutoEglFence() { - if (fence != EGL_NO_SYNC_KHR) { - eglDestroySyncKHR(mDisplay, fence); - } - } - - EGLSyncKHR fence = EGL_NO_SYNC_KHR; -private: - EGLDisplay mDisplay = EGL_NO_DISPLAY; -}; - -class AutoEglImage { -public: - AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) - : mDisplay(display) { - EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; - image = eglCreateImageKHR(display, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs); - } - - ~AutoEglImage() { - if (image != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mDisplay, image); - } - } - - EGLImageKHR image = EGL_NO_IMAGE_KHR; -private: - EGLDisplay mDisplay = EGL_NO_DISPLAY; -}; - -class AutoGlTexture { -public: - AutoGlTexture(uirenderer::Caches& caches) - : mCaches(caches) { - glGenTextures(1, &mTexture); - caches.textureState().bindTexture(mTexture); - } - - ~AutoGlTexture() { - mCaches.textureState().deleteTexture(mTexture); - } - -private: - uirenderer::Caches& mCaches; - GLuint mTexture = 0; -}; - -static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap, - GraphicBuffer& buffer, GLint format, GLint type) { - EGLDisplay display = eglGetCurrentDisplay(); - 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 false; - } - AutoGlTexture glTexture(caches); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); - - GL_CHECKPOINT(MODERATE); - - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), - format, type, bitmap.getPixels()); - - GL_CHECKPOINT(MODERATE); - - // The fence is used to wait for the texture upload to finish - // properly. We cannot rely on glFlush() and glFinish() as - // some drivers completely ignore these API calls - AutoEglFence autoFence(display); - if (autoFence.fence == EGL_NO_SYNC_KHR) { - LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError()); - return false; - } - // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a - // pipeline flush (similar to what a glFlush() would do.) - EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence, - EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); - if (waitStatus != EGL_CONDITION_SATISFIED_KHR) { - LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError()); - return false; - } - return true; -} - -sk_sp Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThread& renderThread, - SkBitmap& skBitmap) { - renderThread.eglManager().initialize(); - uirenderer::Caches& caches = uirenderer::Caches::getInstance(); - - const SkImageInfo& info = skBitmap.info(); - if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) { - ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType()); - return nullptr; - } - - bool needSRGB = uirenderer::transferFunctionCloseToSRGB(skBitmap.info().colorSpace()); - bool hasLinearBlending = caches.extensions().hasLinearBlending(); - GLint format, type, internalFormat; - uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(), - needSRGB && hasLinearBlending, &internalFormat, &format, &type); - - PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat); - sp buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat, - GraphicBuffer::USAGE_HW_TEXTURE | - GraphicBuffer::USAGE_SW_WRITE_NEVER | - GraphicBuffer::USAGE_SW_READ_NEVER, - std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + "]"); - - status_t error = buffer->initCheck(); - if (error < 0) { - ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()"); - return nullptr; - } - - SkBitmap bitmap; - if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(), - hasLinearBlending))) { - sk_sp sRGB = SkColorSpace::MakeSRGB(); - bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); - } else { - bitmap = skBitmap; - } - - if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) { - return nullptr; - } - return sk_sp(new Bitmap(buffer.get(), bitmap.info())); -} - sk_sp Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) { return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap); } @@ -392,6 +223,12 @@ Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info) , mPixelStorageType(PixelStorageType::Hardware) { mPixelStorage.hardware.buffer = buffer; buffer->incStrong(buffer); + setImmutable(); // HW bitmaps are always immutable + if (uirenderer::Properties::isSkiaEnabled()) { + // TODO: add color correctness for Skia pipeline - pass null color space for now + mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast(buffer), + mInfo.alphaType(), nullptr); + } } Bitmap::~Bitmap() { @@ -486,16 +323,6 @@ void Bitmap::getSkBitmap(SkBitmap* outBitmap) { outBitmap->setPixelRef(sk_ref_sp(this), 0, 0); } -void Bitmap::getSkBitmapForShaders(SkBitmap* outBitmap) { - if (isHardware() && uirenderer::Properties::isSkiaEnabled()) { - getSkBitmap(outBitmap); - } else { - outBitmap->setInfo(info(), rowBytes()); - outBitmap->setPixelRef(sk_ref_sp(this), 0, 0); - outBitmap->setHasHardwareMipMap(mHasHardwareMipMap); - } -} - void Bitmap::getBounds(SkRect* bounds) const { SkASSERT(bounds); bounds->set(0, 0, SkIntToScalar(width()), SkIntToScalar(height())); @@ -508,4 +335,20 @@ GraphicBuffer* Bitmap::graphicBuffer() { return nullptr; } +sk_sp Bitmap::makeImage() { + sk_sp image = mImage; + if (!image) { + SkASSERT(!(isHardware() && uirenderer::Properties::isSkiaEnabled())); + SkBitmap skiaBitmap; + skiaBitmap.setInfo(info(), rowBytes()); + skiaBitmap.setPixelRef(sk_ref_sp(this), 0, 0); + skiaBitmap.setHasHardwareMipMap(mHasHardwareMipMap); + // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap + // internally and ~Bitmap won't be invoked. + // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here. + image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode); + } + return image; +} + } // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 9a767152ea4e4..3642406709f9f 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -18,10 +18,12 @@ #include #include #include +#include #include #include #include #include +#include namespace android { @@ -57,15 +59,13 @@ public: static sk_sp createFrom(const SkImageInfo&, SkPixelRef&); - static sk_sp allocateHardwareBitmap(uirenderer::renderthread::RenderThread&, - SkBitmap& bitmap); - Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes, sk_sp ctable); Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, size_t rowBytes, sk_sp ctable); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes, sk_sp ctable); + Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); int rowBytesAsPixels() const { return rowBytes() >> SkColorTypeShiftPerPixel(mInfo.colorType()); @@ -78,10 +78,6 @@ public: void getSkBitmap(SkBitmap* outBitmap); - // Ugly hack: in case of hardware bitmaps, it sets nullptr as pixels pointer - // so it would crash if anyone tries to render this bitmap. - void getSkBitmapForShaders(SkBitmap* outBitmap); - int getAshmemFd() const; size_t getAllocationByteCount() const; @@ -105,8 +101,11 @@ public: } GraphicBuffer* graphicBuffer(); + + // makeImage creates or returns a cached SkImage. Can be invoked from UI or render thread. + // Caching is supported only for HW Bitmaps with skia pipeline. + sk_sp makeImage(); private: - Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); virtual ~Bitmap(); void* getStorage() const; @@ -135,6 +134,8 @@ private: GraphicBuffer* buffer; } hardware; } mPixelStorage; + + sk_sp mImage; // Cache is used only for HW Bitmaps with Skia pipeline. }; } //namespace android diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 46cee67ff89a6..095d32e75911c 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -16,6 +16,7 @@ #include "SkiaOpenGLPipeline.h" +#include "hwui/Bitmap.h" #include "DeferredLayerUpdater.h" #include "GlLayer.h" #include "LayerDrawable.h" @@ -199,6 +200,186 @@ void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* func } } +#define FENCE_TIMEOUT 2000000000 + +class AutoEglFence { +public: + AutoEglFence(EGLDisplay display) + : mDisplay(display) { + fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL); + } + + ~AutoEglFence() { + if (fence != EGL_NO_SYNC_KHR) { + eglDestroySyncKHR(mDisplay, fence); + } + } + + EGLSyncKHR fence = EGL_NO_SYNC_KHR; +private: + EGLDisplay mDisplay = EGL_NO_DISPLAY; +}; + +class AutoEglImage { +public: + AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) + : mDisplay(display) { + EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; + image = eglCreateImageKHR(display, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs); + } + + ~AutoEglImage() { + if (image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mDisplay, image); + } + } + + EGLImageKHR image = EGL_NO_IMAGE_KHR; +private: + EGLDisplay mDisplay = EGL_NO_DISPLAY; +}; + +class AutoSkiaGlTexture { +public: + AutoSkiaGlTexture() { + glGenTextures(1, &mTexture); + glBindTexture(GL_TEXTURE_2D, mTexture); + } + + ~AutoSkiaGlTexture() { + glDeleteTextures(1, &mTexture); + } + +private: + GLuint mTexture = 0; +}; + +sk_sp SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread, + SkBitmap& skBitmap) { + renderThread.eglManager().initialize(); + + sk_sp grContext = sk_ref_sp(renderThread.getGrContext()); + const SkImageInfo& info = skBitmap.info(); + PixelFormat pixelFormat; + GLint format, type; + bool isSupported = false; + + //TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined) + switch (info.colorType()) { + case kRGBA_8888_SkColorType: + isSupported = true; + // ARGB_4444 and Index_8 are both upconverted to RGBA_8888 + case kIndex_8_SkColorType: + case kARGB_4444_SkColorType: + pixelFormat = PIXEL_FORMAT_RGBA_8888; + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + case kRGBA_F16_SkColorType: + isSupported = grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig); + if (isSupported) { + type = GL_HALF_FLOAT; + pixelFormat = PIXEL_FORMAT_RGBA_FP16; + } else { + type = GL_UNSIGNED_BYTE; + pixelFormat = PIXEL_FORMAT_RGBA_8888; + } + format = GL_RGBA; + break; + case kRGB_565_SkColorType: + isSupported = true; + pixelFormat = PIXEL_FORMAT_RGB_565; + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case kGray_8_SkColorType: + isSupported = true; + pixelFormat = PIXEL_FORMAT_RGBA_8888; + format = GL_LUMINANCE; + type = GL_UNSIGNED_BYTE; + break; + default: + ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType()); + return nullptr; + } + + SkBitmap bitmap; + if (isSupported) { + bitmap = skBitmap; + } else { + bitmap.allocPixels(SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), + nullptr)); + bitmap.eraseColor(0); + if (info.colorType() == kRGBA_F16_SkColorType) { + // Drawing RGBA_F16 onto ARGB_8888 is not supported + skBitmap.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), + bitmap.getPixels(), bitmap.rowBytes(), 0, 0); + } else { + SkCanvas canvas(bitmap); + canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr); + } + } + + sp buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat, + GraphicBuffer::USAGE_HW_TEXTURE | + GraphicBuffer::USAGE_SW_WRITE_NEVER | + GraphicBuffer::USAGE_SW_READ_NEVER, + std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) + "]"); + + status_t error = buffer->initCheck(); + if (error < 0) { + ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()"); + return nullptr; + } + + //upload the bitmap into a texture + EGLDisplay display = eglGetCurrentDisplay(); + 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; + } + 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, info.width(), info.height(), format, type, + bitmap.getPixels()); + GL_CHECKPOINT(MODERATE); + + // The fence is used to wait for the texture upload to finish + // properly. We cannot rely on glFlush() and glFinish() as + // some drivers completely ignore these API calls + AutoEglFence autoFence(display); + if (autoFence.fence == EGL_NO_SYNC_KHR) { + LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError()); + return nullptr; + } + // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a + // pipeline flush (similar to what a glFlush() would do.) + EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); + if (waitStatus != EGL_CONDITION_SATISFIED_KHR) { + LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError()); + return nullptr; + } + + grContext->resetContext(kTextureBinding_GrGLBackendState); + + return sk_sp(new Bitmap(buffer.get(), bitmap.info())); +} + } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index caf9467598779..d5471de06aa34 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -19,6 +19,9 @@ #include "SkiaPipeline.h" namespace android { + +class Bitmap; + namespace uirenderer { namespace skiapipeline { @@ -47,6 +50,8 @@ public: bool isContextReady() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); + static sk_sp allocateHardwareBitmap(renderthread::RenderThread& thread, + SkBitmap& skBitmap); private: renderthread::EglManager& mEglManager; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp index 04aeb7c18c959..89697d7445c69 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp @@ -86,23 +86,17 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4 textureMatrix.mapRect(&skiaSrcRect); if (skiaSrcRect.intersect(bufferRect)) { - SkPoint srcOrigin = SkPoint::Make(skiaSrcRect.fLeft, skiaSrcRect.fTop); + // we render in an offscreen buffer to scale and to avoid an issue b/62262733 + // with reading incorrect data from EGLImage backed SkImage (likely a driver bug) + sk_sp scaledSurface = SkSurface::MakeRenderTarget( + grContext.get(), SkBudgeted::kYes, bitmap->info()); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, + SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint); + image = scaledSurface->makeImageSnapshot(); - // if we need to scale the result we must render to an offscreen buffer - if (bitmap->width() != skiaSrcRect.width() - || bitmap->height() != skiaSrcRect.height()) { - sk_sp scaledSurface = SkSurface::MakeRenderTarget( - grContext.get(), SkBudgeted::kYes, bitmap->info()); - SkPaint paint; - paint.setBlendMode(SkBlendMode::kSrc); - scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, - SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint); - image = scaledSurface->makeImageSnapshot(); - srcOrigin.set(0,0); - } - - if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), - srcOrigin.fX, srcOrigin.fY)) { + if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) { copyResult = CopyResult::Success; } } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 75f1adc7755cb..88293db193f88 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -159,11 +159,11 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { GrContext* context = thread.getGrContext(); if (context) { ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height()); - SkBitmap skiaBitmap; - bitmap->getSkBitmap(&skiaBitmap); - sk_sp image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode); - SkImage_pinAsTexture(image.get(), context); - SkImage_unpinAsTexture(image.get(), context); + auto image = bitmap->makeImage(); + if (image.get() && !bitmap->isHardware()) { + SkImage_pinAsTexture(image.get(), context); + SkImage_unpinAsTexture(image.get(), context); + } } } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index cf8b4dd6cf96f..a0cce98c8d576 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -156,30 +156,25 @@ inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPa } void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - SkBitmap skBitmap; - bitmap.getSkBitmap(&skBitmap); - - sk_sp image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode); + sk_sp image = bitmap.makeImage(); SkPaint tmpPaint; mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint)); // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed // when this function ends. - if (!skBitmap.isImmutable() && image.get() && !image->unique()) { + if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } } void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, const SkPaint* paint) { - SkBitmap bitmap; - hwuiBitmap.getSkBitmap(&bitmap); SkAutoCanvasRestore acr(&mRecorder, true); concat(matrix); - sk_sp image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + sk_sp image = hwuiBitmap.makeImage(); SkPaint tmpPaint; mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint)); - if (!bitmap.isImmutable() && image.get() && !image->unique()) { + if (!hwuiBitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } } @@ -187,14 +182,12 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { - SkBitmap bitmap; - hwuiBitmap.getSkBitmap(&bitmap); SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - sk_sp image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + sk_sp image = hwuiBitmap.makeImage(); SkPaint tmpPaint; mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint)); - if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() + if (!hwuiBitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); } @@ -202,11 +195,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float sr void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { - SkBitmap bitmap; - hwuiBitmap.getSkBitmap(&bitmap); - SkCanvas::Lattice lattice; - NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); + NinePatchUtils::SetLatticeDivs(&lattice, chunk, hwuiBitmap.width(), hwuiBitmap.height()); lattice.fFlags = nullptr; int numFlags = 0; @@ -223,11 +213,11 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - sk_sp image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + sk_sp image = hwuiBitmap.makeImage(); SkPaint tmpPaint; mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint)); - if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { + if (!hwuiBitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); } } diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index e8e8ccd96654f..f1ef9e60f9970 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -160,6 +160,25 @@ void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* func (*functor)(mode, nullptr); } +sk_sp SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread, + SkBitmap& skBitmap) { + //TODO: implement this function for Vulkan pipeline + //code below is a hack to avoid crashing because of missing HW Bitmap support + sp buffer = new GraphicBuffer(skBitmap.info().width(), skBitmap.info().height(), + PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_HW_TEXTURE | + GraphicBuffer::USAGE_SW_WRITE_NEVER | + GraphicBuffer::USAGE_SW_READ_NEVER, + std::string("SkiaVulkanPipeline::allocateHardwareBitmap pid [") + + std::to_string(getpid()) + "]"); + status_t error = buffer->initCheck(); + if (error < 0) { + ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()"); + return nullptr; + } + return sk_sp(new Bitmap(buffer.get(), skBitmap.info())); +} + } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 12935a604ffb8..3481312d3b215 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -48,6 +48,8 @@ public: bool isContextReady() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); + static sk_sp allocateHardwareBitmap(renderthread::RenderThread& thread, + SkBitmap& skBitmap); private: renderthread::VulkanManager& mVkManager; diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp index 9631fd6510162..e15d0eb334037 100644 --- a/libs/hwui/renderthread/OpenGLPipeline.cpp +++ b/libs/hwui/renderthread/OpenGLPipeline.cpp @@ -268,6 +268,171 @@ void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) thread.renderState().invokeFunctor(functor, mode, nullptr); } +#define FENCE_TIMEOUT 2000000000 + +class AutoEglFence { +public: + AutoEglFence(EGLDisplay display) + : mDisplay(display) { + fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL); + } + + ~AutoEglFence() { + if (fence != EGL_NO_SYNC_KHR) { + eglDestroySyncKHR(mDisplay, fence); + } + } + + EGLSyncKHR fence = EGL_NO_SYNC_KHR; +private: + EGLDisplay mDisplay = EGL_NO_DISPLAY; +}; + +class AutoEglImage { +public: + AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) + : mDisplay(display) { + EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; + image = eglCreateImageKHR(display, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs); + } + + ~AutoEglImage() { + if (image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mDisplay, image); + } + } + + EGLImageKHR image = EGL_NO_IMAGE_KHR; +private: + EGLDisplay mDisplay = EGL_NO_DISPLAY; +}; + +class AutoGlTexture { +public: + AutoGlTexture(uirenderer::Caches& caches) + : mCaches(caches) { + glGenTextures(1, &mTexture); + caches.textureState().bindTexture(mTexture); + } + + ~AutoGlTexture() { + mCaches.textureState().deleteTexture(mTexture); + } + +private: + uirenderer::Caches& mCaches; + GLuint mTexture = 0; +}; + +static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap, + GraphicBuffer& buffer, GLint format, GLint type) { + EGLDisplay display = eglGetCurrentDisplay(); + 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 false; + } + AutoGlTexture glTexture(caches); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); + + GL_CHECKPOINT(MODERATE); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), + format, type, bitmap.getPixels()); + + GL_CHECKPOINT(MODERATE); + + // The fence is used to wait for the texture upload to finish + // properly. We cannot rely on glFlush() and glFinish() as + // some drivers completely ignore these API calls + AutoEglFence autoFence(display); + if (autoFence.fence == EGL_NO_SYNC_KHR) { + LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError()); + return false; + } + // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a + // pipeline flush (similar to what a glFlush() would do.) + EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); + if (waitStatus != EGL_CONDITION_SATISFIED_KHR) { + LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError()); + return false; + } + return true; +} + +// TODO: handle SRGB sanely +static PixelFormat internalFormatToPixelFormat(GLint internalFormat) { + switch (internalFormat) { + case GL_LUMINANCE: + return PIXEL_FORMAT_RGBA_8888; + case GL_SRGB8_ALPHA8: + return PIXEL_FORMAT_RGBA_8888; + case GL_RGBA: + return PIXEL_FORMAT_RGBA_8888; + case GL_RGB: + return PIXEL_FORMAT_RGB_565; + case GL_RGBA16F: + return PIXEL_FORMAT_RGBA_FP16; + default: + LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat); + return PIXEL_FORMAT_UNKNOWN; + } +} + +sk_sp OpenGLPipeline::allocateHardwareBitmap(RenderThread& renderThread, + SkBitmap& skBitmap) { + renderThread.eglManager().initialize(); + uirenderer::Caches& caches = uirenderer::Caches::getInstance(); + + const SkImageInfo& info = skBitmap.info(); + if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) { + ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType()); + return nullptr; + } + + bool needSRGB = uirenderer::transferFunctionCloseToSRGB(skBitmap.info().colorSpace()); + bool hasLinearBlending = caches.extensions().hasLinearBlending(); + GLint format, type, internalFormat; + uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(), + needSRGB && hasLinearBlending, &internalFormat, &format, &type); + + PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat); + sp buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat, + GraphicBuffer::USAGE_HW_TEXTURE | + GraphicBuffer::USAGE_SW_WRITE_NEVER | + GraphicBuffer::USAGE_SW_READ_NEVER, + std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + "]"); + + status_t error = buffer->initCheck(); + if (error < 0) { + ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()"); + return nullptr; + } + + SkBitmap bitmap; + if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(), + hasLinearBlending))) { + sk_sp sRGB = SkColorSpace::MakeSRGB(); + bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); + } else { + bitmap = skBitmap; + } + + if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) { + return nullptr; + } + return sk_sp(new Bitmap(buffer.get(), bitmap.info())); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h index c3cf80369bb05..0e8c3f553bc7a 100644 --- a/libs/hwui/renderthread/OpenGLPipeline.h +++ b/libs/hwui/renderthread/OpenGLPipeline.h @@ -61,6 +61,8 @@ public: static void destroyLayer(RenderNode* node); static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap); static void invokeFunctor(const RenderThread& thread, Functor* functor); + static sk_sp allocateHardwareBitmap(RenderThread& thread, + SkBitmap& skBitmap); private: EglManager& mEglManager; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 83b3e3a3ab1e8..5a4695f1315ed 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -676,7 +676,7 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) { } CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) { - sk_sp hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap); + sk_sp hardwareBitmap = args->thread->allocateHardwareBitmap(*args->bitmap); return hardwareBitmap.release(); } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 1450ec98dabf0..0554583970235 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -16,8 +16,12 @@ #include "RenderThread.h" -#include "../renderstate/RenderState.h" -#include "../pipeline/skia/SkiaOpenGLReadback.h" +#include "hwui/Bitmap.h" +#include "renderstate/RenderState.h" +#include "renderthread/OpenGLPipeline.h" +#include "pipeline/skia/SkiaOpenGLReadback.h" +#include "pipeline/skia/SkiaOpenGLPipeline.h" +#include "pipeline/skia/SkiaVulkanPipeline.h" #include "CanvasContext.h" #include "EglManager.h" #include "OpenGLReadback.h" @@ -433,6 +437,22 @@ RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) { return next; } +sk_sp RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) { + auto renderType = Properties::getRenderPipelineType(); + switch (renderType) { + case RenderPipelineType::OpenGL: + return OpenGLPipeline::allocateHardwareBitmap(*this, skBitmap); + case RenderPipelineType::SkiaGL: + return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(*this, skBitmap); + case RenderPipelineType::SkiaVulkan: + return skiapipeline::SkiaVulkanPipeline::allocateHardwareBitmap(*this, skBitmap); + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; + } + return nullptr; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 9bc5985e5b162..4b5601c5abc43 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ namespace android { +class Bitmap; class DisplayEventReceiver; namespace uirenderer { @@ -104,6 +106,8 @@ public: VulkanManager& vulkanManager() { return *mVkManager; } + sk_sp allocateHardwareBitmap(SkBitmap& skBitmap); + protected: virtual bool threadLoop() override; diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index a7ebb68d9d1f5..4797dec8e89ea 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -45,10 +45,8 @@ public: skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint); }); - SkBitmap bitmap; SkPaint paint; - hwuiBitmap->getSkBitmapForShaders(&bitmap); - sk_sp image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + sk_sp image = hwuiBitmap->makeImage(); sk_sp repeatShader = image->makeShader( SkShader::TileMode::kRepeat_TileMode, SkShader::TileMode::kRepeat_TileMode, diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index a46142642ed43..c246ebaddcadd 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -75,9 +75,7 @@ public: void doFrame(int frameNr) override { } sk_sp createBitmapShader(Bitmap& bitmap) { - SkBitmap skBitmap; - bitmap.getSkBitmapForShaders(&skBitmap); - sk_sp image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode); + sk_sp image = bitmap.makeImage(); return image->makeShader(SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode); } diff --git a/libs/hwui/tests/unit/TextureCacheTests.cpp b/libs/hwui/tests/unit/TextureCacheTests.cpp index 72384bfa87d8c..ab740dd8330e9 100644 --- a/libs/hwui/tests/unit/TextureCacheTests.cpp +++ b/libs/hwui/tests/unit/TextureCacheTests.cpp @@ -31,7 +31,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(TextureCache, clear) { SkBitmap skBitmap; SkImageInfo info = SkImageInfo::Make(100, 100, kN32_SkColorType, kPremul_SkAlphaType); skBitmap.setInfo(info); - sk_sp hwBitmap(Bitmap::allocateHardwareBitmap(renderThread, skBitmap)); + sk_sp hwBitmap(renderThread.allocateHardwareBitmap(skBitmap)); cache.get(hwBitmap.get()); ASSERT_EQ(GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture), initialCount + 1); cache.clear();