From dbe9245e2e362b12e184cd33b9a27b0901f81244 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Mon, 23 Sep 2013 17:22:10 -0700 Subject: [PATCH] GLConsumer: start using EGL_ANDROID_image_crop This change makes GLConsumer use the EGL_ANDROID_image_crop extension when available on a device. The crop rectangle is passed to the EGL driver when creating EGLImages, allowing the crop to be performed by the driver rather than using the texture transform matrix. Bug: 10897141 Change-Id: I63e9a5d5c85067376abc420e3639154468346311 --- include/gui/GLConsumer.h | 6 +- libs/gui/GLConsumer.cpp | 185 ++++++++++++++++++++++++------------ opengl/include/EGL/eglext.h | 8 ++ opengl/libs/EGL/eglApi.cpp | 4 +- opengl/specs/README | 6 +- 5 files changed, 143 insertions(+), 66 deletions(-) diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h index 75ee30c63..a5fdfb9d2 100644 --- a/include/gui/GLConsumer.h +++ b/include/gui/GLConsumer.h @@ -273,7 +273,7 @@ protected: private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, - const sp& graphicBuffer); + const sp& graphicBuffer, const Rect& crop); // freeBufferLocked frees up the given buffer slot. If the slot has been // initialized this will release the reference to the GraphicBuffer in that @@ -386,6 +386,10 @@ private: // mEglImage is the EGLImage created from mGraphicBuffer. EGLImageKHR mEglImage; + // mCropRect is the crop rectangle passed to EGL when mEglImage was + // created. + Rect mCropRect; + // mFence is the EGL sync object that must signal before the buffer // associated with this buffer slot may be dequeued. It is initialized // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index cf3f12a0c..7ee3081c8 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -41,6 +41,9 @@ #include #include +EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); +#define CROP_EXT_STR "EGL_ANDROID_image_crop" + namespace android { // Macros for including the GLConsumer name in log messages @@ -89,6 +92,30 @@ static void mtxMul(float out[16], const float a[16], const float b[16]); Mutex GLConsumer::sStaticInitLock; sp GLConsumer::sReleasedTexImageBuffer; +static bool hasEglAndroidImageCropImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(CROP_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(CROP_EXT_STR, exts); + bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); + bool atEnd = (cropExtLen+1) < extsLen && + !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); + bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} + +static bool hasEglAndroidImageCrop() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglAndroidImageCropImpl(); + return hasIt; +} + +static bool isEglImageCroppable(const Rect& crop) { + return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0); +} + GLConsumer::GLConsumer(const sp& bq, uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(bq, isControlledByApp), @@ -279,19 +306,30 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item, } int slot = item->mBuf; - if (item->mGraphicBuffer != NULL) { - // This buffer has not been acquired before, so we must assume - // that any EGLImage in mEglSlots is stale. - if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { - if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { - ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", - slot); - // keep going - } - mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; + bool destroyEglImage = false; + + if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { + if (item->mGraphicBuffer != NULL) { + // This buffer has not been acquired before, so we must assume + // that any EGLImage in mEglSlots is stale. + destroyEglImage = true; + } else if (mEglSlots[slot].mCropRect != item->mCrop) { + // We've already seen this buffer before, but it now has a + // different crop rect, so we'll need to recreate the EGLImage if + // we're using the EGL_ANDROID_image_crop extension. + destroyEglImage = hasEglAndroidImageCrop(); } } + if (destroyEglImage) { + if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { + ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", + slot); + // keep going + } + mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; + } + return NO_ERROR; } @@ -334,13 +372,15 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) // EGLImage when detaching from a context but the buffer has not been // re-allocated. if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) { - EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer); + EGLImageKHR image = createImage(mEglDisplay, + mSlots[buf].mGraphicBuffer, item.mCrop); if (image == EGL_NO_IMAGE_KHR) { ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, buf); return UNKNOWN_ERROR; } mEglSlots[buf].mEglImage = image; + mEglSlots[buf].mCropRect = item.mCrop; } // Do whatever sync ops we need to do before releasing the old slot. @@ -581,7 +621,8 @@ status_t GLConsumer::bindUnslottedBufferLocked(EGLDisplay dpy) { mCurrentTexture, mCurrentTextureBuf.get()); // Create a temporary EGLImageKHR. - EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); + Rect crop; + EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; } @@ -753,60 +794,66 @@ void GLConsumer::computeCurrentTransformMatrixLocked() { ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL"); } - Rect cropRect = mCurrentCrop; - float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); - if (!cropRect.isEmpty()) { - float shrinkAmount = 0.0f; - if (mFilteringEnabled) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - // We know there's no subsampling of any channels, so we - // only need to shrink by a half a pixel. - shrinkAmount = 0.5; - break; + float mtxBeforeFlipV[16]; + if (!isEglImageCroppable(mCurrentCrop)) { + Rect cropRect = mCurrentCrop; + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + if (!cropRect.isEmpty()) { + float shrinkAmount = 0.0f; + if (mFilteringEnabled) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // We know there's no subsampling of any channels, so we + // only need to shrink by a half a pixel. + shrinkAmount = 0.5; + break; - default: - // If we don't recognize the format, we must assume the - // worst case (that we care about), which is YUV420. - shrinkAmount = 1.0; - break; + default: + // If we don't recognize the format, we must assume the + // worst case (that we care about), which is YUV420. + shrinkAmount = 1.0; + break; + } + } + + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / + bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / + bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / + bufferHeight; } } + float crop[16] = { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, 1, 0, + tx, ty, 0, 1, + }; - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / - bufferWidth; - } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / - bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / - bufferHeight; + mtxMul(mtxBeforeFlipV, crop, xform); + } else { + for (int i = 0; i < 16; i++) { + mtxBeforeFlipV[i] = xform[i]; } } - float crop[16] = { - sx, 0, 0, 0, - 0, sy, 0, 0, - 0, 0, 1, 0, - tx, ty, 0, 1, - }; - - float mtxBeforeFlipV[16]; - mtxMul(mtxBeforeFlipV, crop, xform); // SurfaceFlinger expects the top of its window textures to be at a Y // coordinate of 0, so GLConsumer must behave the same way. We don't @@ -828,12 +875,26 @@ nsecs_t GLConsumer::getFrameNumber() { } EGLImageKHR GLConsumer::createImage(EGLDisplay dpy, - const sp& graphicBuffer) { + const sp& graphicBuffer, const Rect& crop) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, + EGL_IMAGE_CROP_TOP_ANDROID, crop.top, + EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, + EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, EGL_NONE, }; + if (!crop.isValid()) { + // No crop rect to set, so terminate the attrib array before the crop. + attrs[2] = EGL_NONE; + } else if (!isEglImageCroppable(crop)) { + // The crop rect is not at the origin, so we can't set the crop on the + // EGLImage because that's not allowed by the EGL_ANDROID_image_crop + // extension. In the future we can add a layered extension that + // removes this restriction if there is hardware that can support it. + attrs[2] = EGL_NONE; + } EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index 6c505ed0d..3b2984a8e 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -494,6 +494,14 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYNATIVEPIXMAPNVPROC)(EGLDisplay dpy, #define EGL_FRAMEBUFFER_TARGET_ANDROID 0x3147 #endif +#ifndef EGL_ANDROID_image_crop +#define EGL_ANDROID_image_crop 1 +#define EGL_IMAGE_CROP_LEFT_ANDROID 0x3148 +#define EGL_IMAGE_CROP_TOP_ANDROID 0x3149 +#define EGL_IMAGE_CROP_RIGHT_ANDROID 0x314A +#define EGL_IMAGE_CROP_BOTTOM_ANDROID 0x314B +#endif + #ifndef EGL_ANDROID_blob_cache #define EGL_ANDROID_blob_cache 1 typedef khronos_ssize_t EGLsizeiANDROID; diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 04d5f4598..0cc5265af 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -99,6 +99,7 @@ extern char const * const gExtensionString = "EGL_NV_system_time " "EGL_ANDROID_image_native_buffer " // mandatory "EGL_KHR_wait_sync " // strongly recommended + "EGL_ANDROID_recordable " // mandatory ; // extensions not exposed to applications but used by the ANDROID system @@ -106,8 +107,7 @@ extern char const * const gExtensionString = // "EGL_IMG_hibernate_process " // optional // "EGL_ANDROID_native_fence_sync " // strongly recommended // "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 -// "EGL_ANDROID_recordable " // mandatory - +// "EGL_ANDROID_image_crop " // optional /* * EGL Extensions entry-points exposed to 3rd party applications diff --git a/opengl/specs/README b/opengl/specs/README index eb8686926..f4de1b3d6 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -14,4 +14,8 @@ for use by Android extensions. 0x3145 EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync) 0x3146 EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync) 0x3147 EGL_FRAMEBUFFER_TARGET_ANDROID (EGL_ANDROID_framebuffer_target) -0x3148 - 0x314F (unused) +0x3148 EGL_IMAGE_CROP_LEFT_ANDROID (EGL_ANDROID_image_crop) +0x3149 EGL_IMAGE_CROP_TOP_ANDROID (EGL_ANDROID_image_crop) +0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop) +0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop) +0x314C - 0x314F (unused)