Merge "Routine to upload hardware bitmaps"

This commit is contained in:
Sergei Vasilinetc
2016-11-01 19:32:15 +00:00
committed by Android (Google) Code Review
11 changed files with 467 additions and 34 deletions

View File

@@ -82,6 +82,12 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for
void Texture::deleteTexture() {
mCaches.textureState().deleteTexture(mId);
mId = 0;
mTarget = GL_NONE;
if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
mEglImageHandle = EGL_NO_IMAGE_KHR;
}
}
bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat,
@@ -129,6 +135,17 @@ void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height,
GL_CHECKPOINT(MODERATE);
}
void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) {
EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
mEglImageHandle = EGL_NO_IMAGE_KHR;
}
mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
buffer->getNativeBuffer(), 0);
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle);
}
static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) {
@@ -173,7 +190,7 @@ static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GL
}
}
static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
switch (colorType) {
case kAlpha_8_SkColorType:
@@ -214,6 +231,24 @@ static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorTy
}
}
SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) {
SkBitmap rgbaBitmap;
rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
rgbaBitmap.eraseColor(0);
SkCanvas canvas(rgbaBitmap);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
return rgbaBitmap;
}
bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) {
bool needSRGB = info.colorSpace() == sRGB;
return info.colorType() == kARGB_4444_SkColorType
|| info.colorType() == kIndex_8_SkColorType
|| (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB);
}
void Texture::upload(Bitmap& bitmap) {
if (!bitmap.readyToDraw()) {
ALOGE("Cannot generate texture from bitmap");
@@ -243,33 +278,23 @@ void Texture::upload(Bitmap& bitmap) {
GLint internalFormat, format, type;
colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
if (updateSize(bitmap.width(), bitmap.height(), internalFormat, format, GL_TEXTURE_2D)) {
needsAlloc = true;
}
GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target);
blend = !bitmap.isOpaque();
mCaches.textureState().bindTexture(mId);
mCaches.textureState().bindTexture(mTarget, mId);
// TODO: Handle sRGB gray bitmaps
bool hasSRGB = mCaches.extensions().hasSRGB();
if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
|| bitmap.colorType() == kIndex_8_SkColorType
|| (bitmap.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB))) {
SkBitmap rgbaBitmap;
rgbaBitmap.allocPixels(SkImageInfo::MakeN32(
mWidth, mHeight, bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
rgbaBitmap.eraseColor(0);
SkCanvas canvas(rgbaBitmap);
if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) {
SkBitmap skBitmap;
bitmap.getSkBitmap(&skBitmap);
canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr);
SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
rgbaBitmap.height(), rgbaBitmap.getPixels());
} else if (bitmap.isHardware()) {
uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
} else {
uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels());

View File

@@ -21,8 +21,14 @@
#include "hwui/Bitmap.h"
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <SkBitmap.h>
namespace android {
class GraphicBuffer;
namespace uirenderer {
class Caches;
@@ -34,6 +40,11 @@ class Layer;
*/
class Texture : public GpuMemoryTracker {
public:
static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB);
static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB);
static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType);
explicit Texture(Caches& caches)
: GpuMemoryTracker(GpuObjectType::Texture)
, mCaches(caches)
@@ -147,7 +158,6 @@ public:
* the current frame. This is reset at the start of a new frame.
*/
void* isInUse = nullptr;
private:
// TODO: Temporarily grant private access to Layer, remove once
// Layer can be de-tangled from being a dual-purpose render target
@@ -157,6 +167,7 @@ private:
// Returns true if the size changed, false if it was the same
bool updateSize(uint32_t width, uint32_t height, GLint internalFormat,
GLint format, GLenum target);
void uploadHardwareBitmapToTexture(GraphicBuffer* buffer);
void resetCachedParams();
GLuint mId = 0;
@@ -165,6 +176,7 @@ private:
GLint mFormat = 0;
GLint mInternalFormat = 0;
GLenum mTarget = GL_NONE;
EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR;
/* See GLES spec section 3.8.14
* "In the initial state, the value assigned to TEXTURE_MIN_FILTER is

View File

@@ -16,11 +16,27 @@
#include "Bitmap.h"
#include "Caches.h"
#include "renderthread/RenderThread.h"
#include "renderthread/RenderProxy.h"
#include <cutils/log.h>
#include <sys/mman.h>
#include <cutils/ashmem.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
#include <binder/IServiceManager.h>
#include <ui/PixelFormat.h>
#include <SkCanvas.h>
namespace android {
static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
@@ -76,6 +92,167 @@ static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, si
return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));
}
#define FENCE_TIMEOUT 2000000000
// TODO: handle SRGB sanely
static PixelFormat internalFormatToPixelFormat(GLint internalFormat) {
switch (internalFormat) {
case GL_ALPHA:
return PIXEL_FORMAT_TRANSPARENT;
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;
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;
};
static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap,
GraphicBuffer& buffer, GLint format, GLint type) {
SkAutoLockPixels alp(bitmap);
EGLDisplay display = eglGetCurrentDisplay();
LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
"Failed to get EGL_DEFAULT_DISPLAY! err=%s",
uirenderer::renderthread::EglManager::eglErrorString());
// These objects are initialized below but the default "null"
// values are used to cleanup properly at any point in the
// initialization sequenc
GLuint texture = 0;
// 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;
}
glGenTextures(1, &texture);
caches.textureState().bindTexture(texture);
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();
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
if (alloc == NULL) {
ALOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
return nullptr;
}
const SkImageInfo& info = skBitmap.info();
if (info.colorType() == kUnknown_SkColorType) {
ALOGW("unable to create hardware bitmap of configuration");
return nullptr;
}
sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
bool needSRGB = skBitmap.info().colorSpace() == sRGB.get();
bool hasSRGB = caches.extensions().hasSRGB();
GLint format, type, internalFormat;
uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
needSRGB, &internalFormat, &format, &type);
PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
status_t error;
sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(info.width(), info.height(), pixelFormat,
GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER
| GraphicBuffer::USAGE_SW_READ_NEVER , &error);
if (!buffer.get()) {
ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
return nullptr;
}
SkBitmap bitmap;
if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(),
hasSRGB, sRGB.get()))) {
bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
} else {
bitmap = skBitmap;
}
if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) {
return nullptr;
}
return sk_sp<Bitmap>(new Bitmap(std::move(buffer), info));
}
sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap);
}
sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
return allocateBitmap(bitmap, ctable, &android::allocateHeapBitmap);
}
@@ -183,6 +360,16 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize,
reconfigure(info, rowBytes, ctable);
}
Bitmap::Bitmap(sp<GraphicBuffer>&& buffer, const SkImageInfo& info)
: SkPixelRef(info)
, mPixelStorageType(PixelStorageType::Hardware) {
auto rawBuffer = buffer.get();
mPixelStorage.hardware.buffer = rawBuffer;
if (rawBuffer) {
rawBuffer->incStrong(rawBuffer);
}
mRowBytes = bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride();
}
Bitmap::~Bitmap() {
switch (mPixelStorageType) {
case PixelStorageType::External:
@@ -196,6 +383,12 @@ Bitmap::~Bitmap() {
case PixelStorageType::Heap:
free(mPixelStorage.heap.address);
break;
case PixelStorageType::Hardware:
auto buffer = mPixelStorage.hardware.buffer;
buffer->decStrong(buffer);
mPixelStorage.hardware.buffer = nullptr;
break;
}
if (android::uirenderer::Caches::hasInstance()) {
@@ -219,6 +412,9 @@ void* Bitmap::getStorage() const {
return mPixelStorage.ashmem.address;
case PixelStorageType::Heap:
return mPixelStorage.heap.address;
case PixelStorageType::Hardware:
LOG_ALWAYS_FATAL_IF("Can't get address for hardware bitmap");
return nullptr;
}
}
@@ -264,6 +460,11 @@ void Bitmap::setAlphaType(SkAlphaType alphaType) {
}
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
if (isHardware()) {
//TODO: use readback to get pixels
LOG_ALWAYS_FATAL("Not implemented");
return;
}
outBitmap->setInfo(info(), rowBytes());
outBitmap->setPixelRef(this);
outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
@@ -274,4 +475,11 @@ void Bitmap::getBounds(SkRect* bounds) const {
bounds->set(0, 0, SkIntToScalar(info().width()), SkIntToScalar(info().height()));
}
GraphicBuffer* Bitmap::graphicBuffer() {
if (isHardware()) {
return mPixelStorage.hardware.buffer;
}
return nullptr;
}
} // namespace android

View File

@@ -20,6 +20,7 @@
#include <SkImageInfo.h>
#include <SkPixelRef.h>
#include <cutils/compiler.h>
#include <ui/GraphicBuffer.h>
namespace android {
@@ -27,8 +28,17 @@ enum class PixelStorageType {
External,
Heap,
Ashmem,
Hardware,
};
namespace uirenderer {
namespace renderthread {
class RenderThread;
}
}
class PixelStorage;
typedef void (*FreeFunc)(void* addr, void* context);
class ANDROID_API Bitmap : public SkPixelRef {
@@ -36,17 +46,24 @@ public:
static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable);
static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable);
static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
size_t rowBytes, SkColorTable* ctable);
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,
SkColorTable* ctable);
Bitmap(void* address, void* context, FreeFunc freeFunc,
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
size_t rowBytes, SkColorTable* ctable);
Bitmap(sp<GraphicBuffer>&& buffer, const SkImageInfo& info);
int width() const { return info().width(); }
int height() const { return info().height(); }
@@ -81,13 +98,18 @@ public:
bool readyToDraw() const {
return this->colorType() != kIndex_8_SkColorType || mColorTable;
}
bool isHardware() const {
return mPixelStorageType == PixelStorageType::Hardware;
}
GraphicBuffer* graphicBuffer();
protected:
virtual bool onNewLockPixels(LockRec* rec) override;
virtual void onUnlockPixels() override { };
virtual size_t getAllocatedSizeInBytes() const override;
private:
virtual ~Bitmap();
void doFreePixels();
void* getStorage() const;
PixelStorageType mPixelStorageType;
@@ -111,6 +133,9 @@ private:
void* address;
size_t size;
} heap;
struct {
GraphicBuffer* buffer;
} hardware;
} mPixelStorage;
};

View File

@@ -16,6 +16,7 @@
#include "EglManager.h"
#include "Texture.h"
#include "Caches.h"
#include "DeviceInfo.h"
#include "Properties.h"
@@ -60,7 +61,7 @@ static const char* egl_error_str(EGLint error) {
return "Unknown error";
}
}
static const char* egl_error_str() {
const char* EglManager::eglErrorString() {
return egl_error_str(eglGetError());
}
@@ -103,11 +104,11 @@ void EglManager::initialize() {
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
"Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
"Failed to get EGL_DEFAULT_DISPLAY! err=%s", eglErrorString());
EGLint major, minor;
LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
"Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
"Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
@@ -186,7 +187,7 @@ void EglManager::loadConfig() {
loadConfig();
} else {
// Failed to get a valid config
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
}
}
}
@@ -198,7 +199,7 @@ void EglManager::createContext() {
};
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
"Failed to create context, error = %s", egl_error_str());
"Failed to create context, error = %s", eglErrorString());
}
void EglManager::createPBufferSurface() {
@@ -225,12 +226,12 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs);
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
"Failed to create EGLSurface for window %p, eglErr = %s",
(void*) window, egl_error_str());
(void*) window, eglErrorString());
if (mSwapBehavior != SwapBehavior::Preserved) {
LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE,
"Failed to set swap behavior to destroyed for window %p, eglErr = %s",
(void*) window, egl_error_str());
(void*) window, eglErrorString());
}
return surface;
@@ -241,7 +242,7 @@ void EglManager::destroySurface(EGLSurface surface) {
makeCurrent(EGL_NO_SURFACE);
}
if (!eglDestroySurface(mEglDisplay, surface)) {
ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, eglErrorString());
}
}
@@ -276,7 +277,7 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
(void*)surface, egl_error_str(*errOut));
} else {
LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
(void*)surface, egl_error_str());
(void*)surface, eglErrorString());
}
}
mCurrentSurface = surface;
@@ -317,7 +318,7 @@ void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) {
frame.map(dirty, rects);
if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
(void*)frame.mSurface, egl_error_str());
(void*)frame.mSurface, eglErrorString());
}
}
#endif
@@ -371,14 +372,14 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
if (!preserved) {
ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
(void*) surface, egl_error_str());
(void*) surface, eglErrorString());
// Maybe it's already set?
EGLint swapBehavior;
if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
preserved = (swapBehavior == EGL_BUFFER_PRESERVED);
} else {
ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
(void*) surface, egl_error_str());
(void*) surface, eglErrorString());
}
}

View File

@@ -55,6 +55,7 @@ private:
// and EGLConfig, which are re-used by CanvasContext
class EglManager {
public:
static const char* eglErrorString();
// Returns true on success, false on failure
void initialize();
@@ -83,7 +84,6 @@ public:
private:
friend class RenderThread;
explicit EglManager(RenderThread& thread);
// EglContext is never destroyed, method is purposely not implemented
~EglManager();

View File

@@ -650,6 +650,19 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) {
}
}
CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) {
sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap);
return hardwareBitmap.release();
}
sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
SETUP_TASK(allocateHardwareBitmap);
args->bitmap = &bitmap;
args->thread = &RenderThread::getInstance();
sk_sp<Bitmap> hardwareBitmap(reinterpret_cast<Bitmap*>(staticPostAndWait(task)));
return hardwareBitmap;
}
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}

View File

@@ -130,6 +130,7 @@ public:
int left, int top, int right, int bottom, SkBitmap* bitmap);
ANDROID_API static void prepareToDraw(Bitmap& bitmap);
static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
private:
RenderThread& mRenderThread;
CanvasContext* mContext;

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "TestScene.h"
#include <SkBitmap.h>
#include <string>
namespace android {
namespace uirenderer {
namespace test {
class BitmapAllocationTestUtils {
public:
static sk_sp<Bitmap> allocateHeapBitmap(int width, int height,
SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
sk_sp<Bitmap> bitmap = TestUtils::createBitmap(width, height, colorType);
SkBitmap skBitmap;
bitmap->getSkBitmap(&skBitmap);
setup(skBitmap);
return bitmap;
}
static sk_sp<Bitmap> allocateHardwareBitmap(int width, int height,
SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
SkBitmap skBitmap;
sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(width, height, &skBitmap));
setup(skBitmap);
return Bitmap::allocateHardwareBitmap(skBitmap);
}
typedef sk_sp<Bitmap> (*BitmapAllocator) (int, int, SkColorType,
std::function<void(SkBitmap& bitmap)> setup);
template <class T, BitmapAllocator allocator>
static test::TestScene* createBitmapAllocationScene(const TestScene::Options&) {
return new T(allocator);
}
template <class BaseScene>
static bool registerBitmapAllocationScene(std::string name, std::string description) {
TestScene::registerScene({
name + "GlTex",
description + " (GlTex version).",
createBitmapAllocationScene<BaseScene, &allocateHeapBitmap>
});
TestScene::registerScene({
name + "EglImage",
description + " (EglImage version).",
createBitmapAllocationScene<BaseScene, &allocateHardwareBitmap>
});
return true;
}
};
} // namespace test
} // namespace uirenderer
} // namespace android

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "TestSceneBase.h"
#include "tests/common/BitmapAllocationTestUtils.h"
#include "utils/Color.h"
#include <SkBitmap.h>
using namespace android;
using namespace android::uirenderer;
class BitmapFillrate;
static bool _BitmapFillrate(
BitmapAllocationTestUtils::registerBitmapAllocationScene<BitmapFillrate>(
"bitmapFillrate", "Draws multiple large half transparent bitmaps."));
class BitmapFillrate : public TestScene {
public:
BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
: TestScene()
, mAllocator(allocator) { }
void createContent(int width, int height, Canvas& canvas) override {
canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
createNode(canvas, 0x909C27B0, 0, 0, width, height);
createNode(canvas, 0xA0CDDC39, width / 3, height / 3, width, height);
createNode(canvas, 0x90009688, width / 3, 0, width, height);
createNode(canvas, 0xA0FF5722, 0, height / 3, width, height);
createNode(canvas, 0x9000796B, width / 6, height/6, width, height);
createNode(canvas, 0xA0FFC107, width / 6, 0, width, height);
}
void doFrame(int frameNr) override {
for (size_t ci = 0; ci < mNodes.size(); ci++) {
mNodes[ci]->mutateStagingProperties().setTranslationX(frameNr % 200);
mNodes[ci]->mutateStagingProperties().setTranslationY(frameNr % 200);
mNodes[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
}
private:
void createNode(Canvas& canvas, SkColor color, int left, int top,
int width, int height) {
int itemWidth = 2 * width / 3;
int itemHeight = 2 * height / 3;
auto card = TestUtils::createNode(left, top, left + itemWidth , top + itemHeight,
[this, itemWidth, itemHeight, color](RenderProperties& props, Canvas& canvas) {
sk_sp<Bitmap> bitmap = mAllocator(itemWidth, itemHeight, kRGBA_8888_SkColorType,
[color](SkBitmap& skBitmap) {
skBitmap.eraseColor(color);
});
canvas.drawBitmap(*bitmap, 0, 0, nullptr);
});
canvas.drawRenderNode(card.get());
mNodes.push_back(card);
}
BitmapAllocationTestUtils::BitmapAllocator mAllocator;
std::vector< sp<RenderNode> > mNodes;
};

View File

@@ -27,7 +27,7 @@ namespace uirenderer {
#if DEBUG_OPENGL
#define GL_CHECKPOINT(LEVEL) \
do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\
LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\
LOG_ALWAYS_FATAL_IF(android::uirenderer::GLUtils::dumpGLErrors(),\
"GL errors! %s:%d", __FILE__, __LINE__);\
} } while (0)
#else