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:
parent
93573e91c2
commit
dbe9245e2e
@ -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
|
||||||
|
@ -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,19 +306,30 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int slot = item->mBuf;
|
int slot = item->mBuf;
|
||||||
if (item->mGraphicBuffer != NULL) {
|
bool destroyEglImage = false;
|
||||||
// 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 (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
|
if (item->mGraphicBuffer != NULL) {
|
||||||
if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
|
// This buffer has not been acquired before, so we must assume
|
||||||
ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
|
// that any EGLImage in mEglSlots is stale.
|
||||||
slot);
|
destroyEglImage = true;
|
||||||
// keep going
|
} else if (mEglSlots[slot].mCropRect != item->mCrop) {
|
||||||
}
|
// We've already seen this buffer before, but it now has a
|
||||||
mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
|
// 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;
|
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,60 +794,66 @@ void GLConsumer::computeCurrentTransformMatrixLocked() {
|
|||||||
ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
|
ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect cropRect = mCurrentCrop;
|
float mtxBeforeFlipV[16];
|
||||||
float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
|
if (!isEglImageCroppable(mCurrentCrop)) {
|
||||||
float bufferWidth = buf->getWidth();
|
Rect cropRect = mCurrentCrop;
|
||||||
float bufferHeight = buf->getHeight();
|
float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
|
||||||
if (!cropRect.isEmpty()) {
|
float bufferWidth = buf->getWidth();
|
||||||
float shrinkAmount = 0.0f;
|
float bufferHeight = buf->getHeight();
|
||||||
if (mFilteringEnabled) {
|
if (!cropRect.isEmpty()) {
|
||||||
// In order to prevent bilinear sampling beyond the edge of the
|
float shrinkAmount = 0.0f;
|
||||||
// crop rectangle we may need to shrink it by 2 texels in each
|
if (mFilteringEnabled) {
|
||||||
// dimension. Normally this would just need to take 1/2 a texel
|
// In order to prevent bilinear sampling beyond the edge of the
|
||||||
// off each end, but because the chroma channels of YUV420 images
|
// crop rectangle we may need to shrink it by 2 texels in each
|
||||||
// are subsampled we may need to shrink the crop region by a whole
|
// dimension. Normally this would just need to take 1/2 a texel
|
||||||
// texel on each side.
|
// off each end, but because the chroma channels of YUV420 images
|
||||||
switch (buf->getPixelFormat()) {
|
// are subsampled we may need to shrink the crop region by a whole
|
||||||
case PIXEL_FORMAT_RGBA_8888:
|
// texel on each side.
|
||||||
case PIXEL_FORMAT_RGBX_8888:
|
switch (buf->getPixelFormat()) {
|
||||||
case PIXEL_FORMAT_RGB_888:
|
case PIXEL_FORMAT_RGBA_8888:
|
||||||
case PIXEL_FORMAT_RGB_565:
|
case PIXEL_FORMAT_RGBX_8888:
|
||||||
case PIXEL_FORMAT_BGRA_8888:
|
case PIXEL_FORMAT_RGB_888:
|
||||||
// We know there's no subsampling of any channels, so we
|
case PIXEL_FORMAT_RGB_565:
|
||||||
// only need to shrink by a half a pixel.
|
case PIXEL_FORMAT_BGRA_8888:
|
||||||
shrinkAmount = 0.5;
|
// We know there's no subsampling of any channels, so we
|
||||||
break;
|
// only need to shrink by a half a pixel.
|
||||||
|
shrinkAmount = 0.5;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// If we don't recognize the format, we must assume the
|
// If we don't recognize the format, we must assume the
|
||||||
// worst case (that we care about), which is YUV420.
|
// worst case (that we care about), which is YUV420.
|
||||||
shrinkAmount = 1.0;
|
shrinkAmount = 1.0;
|
||||||
break;
|
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.
|
mtxMul(mtxBeforeFlipV, crop, xform);
|
||||||
if (cropRect.width() < bufferWidth) {
|
} else {
|
||||||
tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
|
for (int i = 0; i < 16; i++) {
|
||||||
sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
|
mtxBeforeFlipV[i] = xform[i];
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
float mtxBeforeFlipV[16];
|
|
||||||
mtxMul(mtxBeforeFlipV, crop, xform);
|
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user