diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index c4a65f6fd5456..0a60a8ea4c995 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -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 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()); diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index d73789a28ead8..c75e88f5dd6ed 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -21,8 +21,14 @@ #include "hwui/Bitmap.h" #include +#include +#include +#include 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 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 diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 31fbe68078c09..be0b22ee8d318 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -16,11 +16,27 @@ #include "Bitmap.h" #include "Caches.h" +#include "renderthread/RenderThread.h" +#include "renderthread/RenderProxy.h" #include #include #include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include + namespace android { static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) { @@ -76,6 +92,167 @@ static sk_sp allocateHeapBitmap(size_t size, const SkImageInfo& info, si return sk_sp(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::allocateHardwareBitmap(uirenderer::renderthread::RenderThread& renderThread, + SkBitmap& skBitmap) { + renderThread.eglManager().initialize(); + uirenderer::Caches& caches = uirenderer::Caches::getInstance(); + + sp composer(ComposerService::getComposerService()); + sp 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 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 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(new Bitmap(std::move(buffer), info)); +} + +sk_sp Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) { + return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap); +} + sk_sp 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&& 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 \ No newline at end of file diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index c175690265761..3940381edf629 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -20,6 +20,7 @@ #include #include #include +#include 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 allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable); static sk_sp allocateHeapBitmap(const SkImageInfo& info); + static sk_sp allocateHardwareBitmap(SkBitmap& bitmap); + static sk_sp allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable); static sk_sp allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); static sk_sp createFrom(const SkImageInfo&, SkPixelRef&); + + static sk_sp 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&& 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; }; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index beda0455c1453..cf0af6f4fda17 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -16,6 +16,7 @@ #include "EglManager.h" +#include "Texture.h" #include "Caches.h" #include "DeviceInfo.h" #include "Properties.h" @@ -58,7 +59,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()); } @@ -101,11 +102,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); @@ -173,7 +174,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()); } } } @@ -185,7 +186,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() { @@ -212,12 +213,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; @@ -228,7 +229,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()); } } @@ -262,7 +263,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; @@ -303,7 +304,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 @@ -357,14 +358,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()); } } diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index ba4a3e1c5192c..7349dcc9f0417 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -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(); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 42da293021b9c..f747a5127923a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -653,6 +653,19 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) { } } +CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) { + sk_sp hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap); + return hardwareBitmap.release(); +} + +sk_sp RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) { + SETUP_TASK(allocateHardwareBitmap); + args->bitmap = &bitmap; + args->thread = &RenderThread::getInstance(); + sk_sp hardwareBitmap(reinterpret_cast(staticPostAndWait(task))); + return hardwareBitmap; +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index ae9330ddccf25..e559142c1acec 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -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 allocateHardwareBitmap(SkBitmap& bitmap); private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/tests/common/BitmapAllocationTestUtils.h b/libs/hwui/tests/common/BitmapAllocationTestUtils.h new file mode 100644 index 0000000000000..6dadd3e364cfa --- /dev/null +++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h @@ -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 +#include + +namespace android { +namespace uirenderer { +namespace test { + +class BitmapAllocationTestUtils { +public: + static sk_sp allocateHeapBitmap(int width, int height, + SkColorType colorType, std::function setup) { + sk_sp bitmap = TestUtils::createBitmap(width, height, colorType); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + setup(skBitmap); + return bitmap; + } + + static sk_sp allocateHardwareBitmap(int width, int height, + SkColorType colorType, std::function setup) { + SkBitmap skBitmap; + sk_sp heapBitmap(TestUtils::createBitmap(width, height, &skBitmap)); + setup(skBitmap); + return Bitmap::allocateHardwareBitmap(skBitmap); + } + + typedef sk_sp (*BitmapAllocator) (int, int, SkColorType, + std::function setup); + + template + static test::TestScene* createBitmapAllocationScene(const TestScene::Options&) { + return new T(allocator); + } + + template + static bool registerBitmapAllocationScene(std::string name, std::string description) { + TestScene::registerScene({ + name + "GlTex", + description + " (GlTex version).", + createBitmapAllocationScene + }); + + TestScene::registerScene({ + name + "EglImage", + description + " (EglImage version).", + createBitmapAllocationScene + }); + return true; + } +}; + +} // namespace test +} // namespace uirenderer +} // namespace android \ No newline at end of file diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp new file mode 100644 index 0000000000000..be58d09b7f4d1 --- /dev/null +++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp @@ -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 + +using namespace android; +using namespace android::uirenderer; + +class BitmapFillrate; + +static bool _BitmapFillrate( + BitmapAllocationTestUtils::registerBitmapAllocationScene( + "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 = 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 > mNodes; +}; \ No newline at end of file diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h index b49c1eb1dc059..94818b2dc44f2 100644 --- a/libs/hwui/utils/GLUtils.h +++ b/libs/hwui/utils/GLUtils.h @@ -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