Implement HW Bitmap for Skia pipeline
Implement HW Bitmap for Skia pipeline. Use new Skia
SkImage::MakeFromAHardwareBuffer API, which will enable to
record HW Bitmap into a picture. Move logic that uploads
SkBitmap into a GraphicBuffer into pipeline specific classes.
Test: All CTS and other tests pass for HWUI pipleine. For Skia
pipeline graphics CTS tests pass, 2 UIRendering CTS tests which
excise HW bitmaps with color spaces fail, bitmapShaderEglImage
macrobench fails (to be fixed by a CL in Skia), HWUI unit tests
pass, no EGL leaks found.
Change-Id: Id5926d7cccd81af8b55400f44fb524a427543d05
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 4e6a7db..6dde005 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -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 {
@@ -89,171 +85,6 @@
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);
}
@@ -392,6 +223,12 @@
, 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() {
@@ -486,16 +323,6 @@
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 @@
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
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 9a76715..364240670 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -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 {
@@ -57,15 +59,13 @@
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());
@@ -78,10 +78,6 @@
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 @@
}
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;
@@ -135,6 +134,8 @@
GraphicBuffer* buffer;
} hardware;
} mPixelStorage;
+
+ sk_sp<SkImage> 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 ae13131..a4fff97 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"
@@ -197,6 +198,186 @@
}
}
+#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 */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 36685dd..3ab03a7 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 {
@@ -46,6 +49,8 @@
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;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 04aeb7c..89697d7 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -86,23 +86,17 @@
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<SkSurface> 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<SkSurface> 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 75f1adc..88293db 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -159,11 +159,11 @@
GrContext* context = thread.getGrContext();
if (context) {
ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
- SkBitmap skiaBitmap;
- bitmap->getSkBitmap(&skiaBitmap);
- sk_sp<SkImage> 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 cf8b4dd..a0cce98 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -156,30 +156,25 @@
}
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- SkBitmap skBitmap;
- bitmap.getSkBitmap(&skBitmap);
-
- sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+ sk_sp<SkImage> 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<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ sk_sp<SkImage> 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, 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<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ sk_sp<SkImage> 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::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 @@
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ sk_sp<SkImage> 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 d28e605..9e25514 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -158,6 +158,25 @@
(*functor)(mode, nullptr);
}
+sk_sp<Bitmap> 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<GraphicBuffer> 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<Bitmap>(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 aab1d7a..1f2404b 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -47,6 +47,8 @@
bool isContextReady() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+ static sk_sp<Bitmap> 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 e1ae585..6b12012 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -267,6 +267,171 @@
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<Bitmap> 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<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()));
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index 6df8be4..c434f2e 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -61,6 +61,8 @@
static void destroyLayer(RenderNode* node);
static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
static void invokeFunctor(const RenderThread& thread, Functor* functor);
+ static sk_sp<Bitmap> allocateHardwareBitmap(RenderThread& thread,
+ SkBitmap& skBitmap);
private:
EglManager& mEglManager;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index eed5238..d5875d8 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -664,7 +664,7 @@
}
CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) {
- sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap);
+ sk_sp<Bitmap> hardwareBitmap = args->thread->allocateHardwareBitmap(*args->bitmap);
return hardwareBitmap.release();
}
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 1450ec9..0554583 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 @@
return next;
}
+sk_sp<Bitmap> 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 9bc5985..4b5601c 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -24,6 +24,7 @@
#include <GrContext.h>
#include <cutils/compiler.h>
+#include <SkBitmap.h>
#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
@@ -33,6 +34,7 @@
namespace android {
+class Bitmap;
class DisplayEventReceiver;
namespace uirenderer {
@@ -104,6 +106,8 @@
VulkanManager& vulkanManager() { return *mVkManager; }
+ sk_sp<Bitmap> 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 a7ebb68..4797dec 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -45,10 +45,8 @@
skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint);
});
- SkBitmap bitmap;
SkPaint paint;
- hwuiBitmap->getSkBitmapForShaders(&bitmap);
- sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ sk_sp<SkImage> image = hwuiBitmap->makeImage();
sk_sp<SkShader> 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 a461426..c246eba 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -75,9 +75,7 @@
void doFrame(int frameNr) override { }
sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) {
- SkBitmap skBitmap;
- bitmap.getSkBitmapForShaders(&skBitmap);
- sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+ sk_sp<SkImage> 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 72384bf..ab740dd 100644
--- a/libs/hwui/tests/unit/TextureCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextureCacheTests.cpp
@@ -31,7 +31,7 @@
SkBitmap skBitmap;
SkImageInfo info = SkImageInfo::Make(100, 100, kN32_SkColorType, kPremul_SkAlphaType);
skBitmap.setInfo(info);
- sk_sp<Bitmap> hwBitmap(Bitmap::allocateHardwareBitmap(renderThread, skBitmap));
+ sk_sp<Bitmap> hwBitmap(renderThread.allocateHardwareBitmap(skBitmap));
cache.get(hwBitmap.get());
ASSERT_EQ(GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture), initialCount + 1);
cache.clear();