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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user