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
This commit is contained in:
Stan Iliev
2017-05-24 13:28:36 -04:00
parent a9861756b5
commit 7bc3bc6028
18 changed files with 466 additions and 241 deletions

View File

@@ -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);

View File

@@ -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 @@ 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);
}
@@ -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() {
@@ -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<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

View File

@@ -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 @@ 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());
@@ -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<SkImage> makeImage();
private:
Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
virtual ~Bitmap();
void* getStorage() const;
@@ -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

View File

@@ -16,6 +16,7 @@
#include "SkiaOpenGLPipeline.h"
#include "hwui/Bitmap.h"
#include "DeferredLayerUpdater.h"
#include "GlLayer.h"
#include "LayerDrawable.h"
@@ -197,6 +198,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 */

View File

@@ -19,6 +19,9 @@
#include "SkiaPipeline.h"
namespace android {
class Bitmap;
namespace uirenderer {
namespace skiapipeline {
@@ -46,6 +49,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;

View File

@@ -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<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;
}
}

View File

@@ -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<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);
}
}
}

View File

@@ -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<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, 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<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::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<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());
}
}

View File

@@ -158,6 +158,25 @@ void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* func
(*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 */

View File

@@ -47,6 +47,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::VulkanManager& mVkManager;

View File

@@ -267,6 +267,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<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 */

View File

@@ -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<Bitmap> allocateHardwareBitmap(RenderThread& thread,
SkBitmap& skBitmap);
private:
EglManager& mEglManager;

View File

@@ -664,7 +664,7 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) {
}
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();
}

View File

@@ -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<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 */

View File

@@ -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 @@ public:
VulkanManager& vulkanManager() { return *mVkManager; }
sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap);
protected:
virtual bool threadLoop() override;

View File

@@ -45,10 +45,8 @@ public:
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,

View File

@@ -75,9 +75,7 @@ public:
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);
}

View File

@@ -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<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();