Loading core/jni/android/graphics/Shader.cpp +6 −3 Original line number Diff line number Diff line Loading @@ -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<const SkMatrix*>(matrixPtr); SkBitmap bitmap; sk_sp<SkImage> 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<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); if (!image.get()) { SkBitmap bitmap; image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); } sk_sp<SkShader> baseShader = image->makeShader( (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY); Loading libs/hwui/hwui/Bitmap.cpp +23 −180 Original line number Diff line number Diff line Loading @@ -26,16 +26,12 @@ #include <log/log.h> #include <cutils/ashmem.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <EGL/egl.h> #include <EGL/eglext.h> #include <private/gui/ComposerService.h> #include <binder/IServiceManager.h> #include <ui/PixelFormat.h> #include <SkCanvas.h> #include <SkImagePriv.h> namespace android { Loading Loading @@ -89,171 +85,6 @@ static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, si return sk_sp<Bitmap>(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> 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<GraphicBuffer> 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<SkColorSpace> 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<Bitmap>(new Bitmap(buffer.get(), bitmap.info())); } sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) { return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap); } Loading Loading @@ -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<AHardwareBuffer*>(buffer), mInfo.alphaType(), nullptr); } } Bitmap::~Bitmap() { Loading Loading @@ -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())); Loading @@ -508,4 +335,20 @@ GraphicBuffer* Bitmap::graphicBuffer() { return nullptr; } sk_sp<SkImage> Bitmap::makeImage() { sk_sp<SkImage> 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 libs/hwui/hwui/Bitmap.h +9 −8 Original line number Diff line number Diff line Loading @@ -18,10 +18,12 @@ #include <SkBitmap.h> #include <SkColorSpace.h> #include <SkColorTable.h> #include <SkImage.h> #include <SkImageInfo.h> #include <SkPixelRef.h> #include <cutils/compiler.h> #include <ui/GraphicBuffer.h> #include <SkImage.h> namespace android { Loading Loading @@ -57,15 +59,13 @@ public: static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&); static sk_sp<Bitmap> allocateHardwareBitmap(uirenderer::renderthread::RenderThread&, SkBitmap& bitmap); Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable); Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable); Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); int rowBytesAsPixels() const { return rowBytes() >> SkColorTypeShiftPerPixel(mInfo.colorType()); Loading @@ -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; Loading @@ -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<SkImage> makeImage(); private: Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); virtual ~Bitmap(); void* getStorage() const; Loading Loading @@ -135,6 +134,8 @@ private: GraphicBuffer* buffer; } hardware; } mPixelStorage; sk_sp<SkImage> mImage; // Cache is used only for HW Bitmaps with Skia pipeline. }; } //namespace android libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +181 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "SkiaOpenGLPipeline.h" #include "hwui/Bitmap.h" #include "DeferredLayerUpdater.h" #include "GlLayer.h" #include "LayerDrawable.h" Loading Loading @@ -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<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread, SkBitmap& skBitmap) { renderThread.eglManager().initialize(); sk_sp<GrContext> 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<GraphicBuffer> 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<Bitmap>(new Bitmap(buffer.get(), bitmap.info())); } } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +5 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ #include "SkiaPipeline.h" namespace android { class Bitmap; namespace uirenderer { namespace skiapipeline { Loading Loading @@ -47,6 +50,8 @@ public: bool isContextReady() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, SkBitmap& skBitmap); private: renderthread::EglManager& mEglManager; Loading Loading
core/jni/android/graphics/Shader.cpp +6 −3 Original line number Diff line number Diff line Loading @@ -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<const SkMatrix*>(matrixPtr); SkBitmap bitmap; sk_sp<SkImage> 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<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); if (!image.get()) { SkBitmap bitmap; image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); } sk_sp<SkShader> baseShader = image->makeShader( (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY); Loading
libs/hwui/hwui/Bitmap.cpp +23 −180 Original line number Diff line number Diff line Loading @@ -26,16 +26,12 @@ #include <log/log.h> #include <cutils/ashmem.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <EGL/egl.h> #include <EGL/eglext.h> #include <private/gui/ComposerService.h> #include <binder/IServiceManager.h> #include <ui/PixelFormat.h> #include <SkCanvas.h> #include <SkImagePriv.h> namespace android { Loading Loading @@ -89,171 +85,6 @@ static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, si return sk_sp<Bitmap>(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> 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<GraphicBuffer> 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<SkColorSpace> 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<Bitmap>(new Bitmap(buffer.get(), bitmap.info())); } sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) { return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap); } Loading Loading @@ -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<AHardwareBuffer*>(buffer), mInfo.alphaType(), nullptr); } } Bitmap::~Bitmap() { Loading Loading @@ -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())); Loading @@ -508,4 +335,20 @@ GraphicBuffer* Bitmap::graphicBuffer() { return nullptr; } sk_sp<SkImage> Bitmap::makeImage() { sk_sp<SkImage> 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
libs/hwui/hwui/Bitmap.h +9 −8 Original line number Diff line number Diff line Loading @@ -18,10 +18,12 @@ #include <SkBitmap.h> #include <SkColorSpace.h> #include <SkColorTable.h> #include <SkImage.h> #include <SkImageInfo.h> #include <SkPixelRef.h> #include <cutils/compiler.h> #include <ui/GraphicBuffer.h> #include <SkImage.h> namespace android { Loading Loading @@ -57,15 +59,13 @@ public: static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&); static sk_sp<Bitmap> allocateHardwareBitmap(uirenderer::renderthread::RenderThread&, SkBitmap& bitmap); Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable); Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable); Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); int rowBytesAsPixels() const { return rowBytes() >> SkColorTypeShiftPerPixel(mInfo.colorType()); Loading @@ -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; Loading @@ -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<SkImage> makeImage(); private: Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); virtual ~Bitmap(); void* getStorage() const; Loading Loading @@ -135,6 +134,8 @@ private: GraphicBuffer* buffer; } hardware; } mPixelStorage; sk_sp<SkImage> mImage; // Cache is used only for HW Bitmaps with Skia pipeline. }; } //namespace android
libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +181 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "SkiaOpenGLPipeline.h" #include "hwui/Bitmap.h" #include "DeferredLayerUpdater.h" #include "GlLayer.h" #include "LayerDrawable.h" Loading Loading @@ -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<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread, SkBitmap& skBitmap) { renderThread.eglManager().initialize(); sk_sp<GrContext> 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<GraphicBuffer> 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<Bitmap>(new Bitmap(buffer.get(), bitmap.info())); } } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */
libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +5 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ #include "SkiaPipeline.h" namespace android { class Bitmap; namespace uirenderer { namespace skiapipeline { Loading Loading @@ -47,6 +50,8 @@ public: bool isContextReady() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, SkBitmap& skBitmap); private: renderthread::EglManager& mEglManager; Loading