Loading libs/hwui/Texture.cpp +43 −18 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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) { Loading Loading @@ -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: Loading Loading @@ -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"); Loading Loading @@ -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()); Loading libs/hwui/Texture.h +13 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) Loading Loading @@ -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 Loading @@ -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; Loading @@ -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 Loading libs/hwui/hwui/Bitmap.cpp +208 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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); } Loading Loading @@ -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: Loading @@ -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()) { Loading @@ -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; } } Loading Loading @@ -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); Loading @@ -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 libs/hwui/hwui/Bitmap.h +26 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <SkImageInfo.h> #include <SkPixelRef.h> #include <cutils/compiler.h> #include <ui/GraphicBuffer.h> namespace android { Loading @@ -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 { Loading @@ -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(); } Loading Loading @@ -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; Loading @@ -111,6 +133,9 @@ private: void* address; size_t size; } heap; struct { GraphicBuffer* buffer; } hardware; } mPixelStorage; }; Loading libs/hwui/renderthread/EglManager.cpp +13 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "EglManager.h" #include "Texture.h" #include "Caches.h" #include "DeviceInfo.h" #include "Properties.h" Loading Loading @@ -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()); } Loading Loading @@ -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); Loading Loading @@ -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()); } } } Loading @@ -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() { Loading @@ -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; Loading @@ -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()); } } Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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()); } } Loading Loading
libs/hwui/Texture.cpp +43 −18 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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) { Loading Loading @@ -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: Loading Loading @@ -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"); Loading Loading @@ -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()); Loading
libs/hwui/Texture.h +13 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) Loading Loading @@ -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 Loading @@ -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; Loading @@ -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 Loading
libs/hwui/hwui/Bitmap.cpp +208 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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); } Loading Loading @@ -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: Loading @@ -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()) { Loading @@ -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; } } Loading Loading @@ -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); Loading @@ -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
libs/hwui/hwui/Bitmap.h +26 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <SkImageInfo.h> #include <SkPixelRef.h> #include <cutils/compiler.h> #include <ui/GraphicBuffer.h> namespace android { Loading @@ -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 { Loading @@ -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(); } Loading Loading @@ -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; Loading @@ -111,6 +133,9 @@ private: void* address; size_t size; } heap; struct { GraphicBuffer* buffer; } hardware; } mPixelStorage; }; Loading
libs/hwui/renderthread/EglManager.cpp +13 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "EglManager.h" #include "Texture.h" #include "Caches.h" #include "DeviceInfo.h" #include "Properties.h" Loading Loading @@ -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()); } Loading Loading @@ -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); Loading Loading @@ -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()); } } } Loading @@ -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() { Loading @@ -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; Loading @@ -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()); } } Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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()); } } Loading