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
This commit is contained in:
Jamie Gennis 2013-09-23 17:22:10 -07:00
parent 93573e91c2
commit dbe9245e2e
5 changed files with 143 additions and 66 deletions

View File

@ -273,7 +273,7 @@ protected:
private: private:
// createImage creates a new EGLImage from a GraphicBuffer. // createImage creates a new EGLImage from a GraphicBuffer.
EGLImageKHR createImage(EGLDisplay dpy, EGLImageKHR createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer); const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
// freeBufferLocked frees up the given buffer slot. If the slot has been // freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in that // initialized this will release the reference to the GraphicBuffer in that
@ -386,6 +386,10 @@ private:
// mEglImage is the EGLImage created from mGraphicBuffer. // mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage; 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 // mFence is the EGL sync object that must signal before the buffer
// associated with this buffer slot may be dequeued. It is initialized // associated with this buffer slot may be dequeued. It is initialized
// to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based

View File

@ -41,6 +41,9 @@
#include <utils/String8.h> #include <utils/String8.h>
#include <utils/Trace.h> #include <utils/Trace.h>
EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
#define CROP_EXT_STR "EGL_ANDROID_image_crop"
namespace android { namespace android {
// Macros for including the GLConsumer name in log messages // 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; Mutex GLConsumer::sStaticInitLock;
sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer; sp<GraphicBuffer> 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<IGraphicBufferConsumer>& bq, uint32_t tex, GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
ConsumerBase(bq, isControlledByApp), ConsumerBase(bq, isControlledByApp),
@ -279,10 +306,22 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
} }
int slot = item->mBuf; int slot = item->mBuf;
bool destroyEglImage = false;
if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
if (item->mGraphicBuffer != NULL) { if (item->mGraphicBuffer != NULL) {
// This buffer has not been acquired before, so we must assume // This buffer has not been acquired before, so we must assume
// that any EGLImage in mEglSlots is stale. // that any EGLImage in mEglSlots is stale.
if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { 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)) { if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
slot); slot);
@ -290,7 +329,6 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
} }
mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
} }
}
return NO_ERROR; 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 // EGLImage when detaching from a context but the buffer has not been
// re-allocated. // re-allocated.
if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) { 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) { if (image == EGL_NO_IMAGE_KHR) {
ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
mEglDisplay, buf); mEglDisplay, buf);
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
mEglSlots[buf].mEglImage = image; mEglSlots[buf].mEglImage = image;
mEglSlots[buf].mCropRect = item.mCrop;
} }
// Do whatever sync ops we need to do before releasing the old slot. // 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()); mCurrentTexture, mCurrentTextureBuf.get());
// Create a temporary EGLImageKHR. // Create a temporary EGLImageKHR.
EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); Rect crop;
EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop);
if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) {
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
@ -753,6 +794,8 @@ void GLConsumer::computeCurrentTransformMatrixLocked() {
ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL"); ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
} }
float mtxBeforeFlipV[16];
if (!isEglImageCroppable(mCurrentCrop)) {
Rect cropRect = mCurrentCrop; Rect cropRect = mCurrentCrop;
float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
float bufferWidth = buf->getWidth(); float bufferWidth = buf->getWidth();
@ -805,8 +848,12 @@ void GLConsumer::computeCurrentTransformMatrixLocked() {
tx, ty, 0, 1, tx, ty, 0, 1,
}; };
float mtxBeforeFlipV[16];
mtxMul(mtxBeforeFlipV, crop, xform); mtxMul(mtxBeforeFlipV, crop, xform);
} else {
for (int i = 0; i < 16; i++) {
mtxBeforeFlipV[i] = xform[i];
}
}
// SurfaceFlinger expects the top of its window textures to be at a Y // 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 // 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, EGLImageKHR GLConsumer::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer) { const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
EGLint attrs[] = { 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, 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, EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) {

View File

@ -494,6 +494,14 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYNATIVEPIXMAPNVPROC)(EGLDisplay dpy,
#define EGL_FRAMEBUFFER_TARGET_ANDROID 0x3147 #define EGL_FRAMEBUFFER_TARGET_ANDROID 0x3147
#endif #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 #ifndef EGL_ANDROID_blob_cache
#define EGL_ANDROID_blob_cache 1 #define EGL_ANDROID_blob_cache 1
typedef khronos_ssize_t EGLsizeiANDROID; typedef khronos_ssize_t EGLsizeiANDROID;

View File

@ -99,6 +99,7 @@ extern char const * const gExtensionString =
"EGL_NV_system_time " "EGL_NV_system_time "
"EGL_ANDROID_image_native_buffer " // mandatory "EGL_ANDROID_image_native_buffer " // mandatory
"EGL_KHR_wait_sync " // strongly recommended "EGL_KHR_wait_sync " // strongly recommended
"EGL_ANDROID_recordable " // mandatory
; ;
// extensions not exposed to applications but used by the ANDROID system // 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_IMG_hibernate_process " // optional
// "EGL_ANDROID_native_fence_sync " // strongly recommended // "EGL_ANDROID_native_fence_sync " // strongly recommended
// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 // "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 * EGL Extensions entry-points exposed to 3rd party applications

View File

@ -14,4 +14,8 @@ for use by Android extensions.
0x3145 EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync) 0x3145 EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync)
0x3146 EGL_SYNC_NATIVE_FENCE_SIGNALED_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) 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)