GLProducer: Reference count images rather than buffers.

In most cases, EGLImages can be created one-to-one with graphic
buffers in slots, but that was difficult due to some special
cases:
- ReleaseTexImage binds a custom 'unslotted' debug image.
- When all slots are freed, we still need to hang on to one.

These cases were handled by keeping an additional reference to
the 'current' buffer (mCurrentTextureBuf), but we would create
new images since we can't reference count them in the same way.
This patch uses the same semantics, except that it reference
counts the image (an EglImage wrapper class) rather than just
buffer. The wrapper class also detects the cases when we need
a new EGLImage, and only creates them in those rare cases.

Change-Id: I2915761dbe49d2a9bda1f59e60f857543634636b
This commit is contained in:
Eric Penner 2014-07-11 19:08:04 -07:00
parent 7869e224aa
commit 5c3d243fcc
2 changed files with 214 additions and 195 deletions

View File

@ -231,7 +231,7 @@ public:
protected:
// abandonLocked overrides the ConsumerBase method to clear
// mCurrentTextureBuf in addition to the ConsumerBase behavior.
// mCurrentTextureImage in addition to the ConsumerBase behavior.
virtual void abandonLocked();
// dumpLocked overrides the ConsumerBase method to dump GLConsumer-
@ -262,7 +262,7 @@ protected:
status_t updateAndReleaseLocked(const BufferQueue::BufferItem& item);
// Binds mTexName and the current buffer to mTexTarget. Uses
// mCurrentTexture if it's set, mCurrentTextureBuf if not. If the
// mCurrentTexture if it's set, mCurrentTextureImage if not. If the
// bind succeeds, this calls doGLFenceWait.
status_t bindTextureImageLocked();
@ -275,11 +275,57 @@ protected:
status_t checkAndUpdateEglStateLocked(bool contextCheck = false);
private:
// createImage creates a new EGLImage from a GraphicBuffer.
EGLImageKHR createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
// EglImage is a utility class for tracking and creating EGLImageKHRs. There
// is primarily just one image per slot, but there is also special cases:
// - For releaseTexImage, we use a debug image (mReleasedTexImage)
// - After freeBuffer, we must still keep the current image/buffer
// Reference counting EGLImages lets us handle all these cases easily while
// also only creating new EGLImages from buffers when required.
class EglImage : public LightRefBase<EglImage> {
public:
EglImage(sp<GraphicBuffer> graphicBuffer);
// freeBufferLocked frees up the given buffer slot. If the slot has been
// createIfNeeded creates an EGLImage if required (we haven't created
// one yet, or the EGLDisplay or crop-rect has changed).
status_t createIfNeeded(EGLDisplay display, const Rect& cropRect);
// This calls glEGLImageTargetTexture2DOES to bind the image to the
// texture in the specified texture target.
void bindToTextureTarget(uint32_t texTarget);
const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
const native_handle* graphicBufferHandle() {
return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle;
}
private:
// Only allow instantiation using ref counting.
friend class LightRefBase<EglImage>;
virtual ~EglImage();
// createImage creates a new EGLImage from a GraphicBuffer.
EGLImageKHR createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
// Disallow copying
EglImage(const EglImage& rhs);
void operator = (const EglImage& rhs);
// mGraphicBuffer is the buffer that was used to create this image.
sp<GraphicBuffer> mGraphicBuffer;
// mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage;
// mEGLDisplay is the EGLDisplay that was used to create mEglImage.
EGLDisplay mEglDisplay;
// mCropRect is the crop rectangle passed to EGL when mEglImage
// was created.
Rect mCropRect;
};
// freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in that
// slot and destroy the EGLImage in that slot. Otherwise it has no effect.
//
@ -289,7 +335,7 @@ private:
// computeCurrentTransformMatrixLocked computes the transform matrix for the
// current texture. It uses mCurrentTransform and the current GraphicBuffer
// to compute this matrix and stores it in mCurrentTransformMatrix.
// mCurrentTextureBuf must not be NULL.
// mCurrentTextureImage must not be NULL.
void computeCurrentTransformMatrixLocked();
// doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
@ -303,13 +349,6 @@ private:
// before the outstanding accesses have completed.
status_t syncForReleaseLocked(EGLDisplay dpy);
// Normally, when we bind a buffer to a texture target, we bind a buffer
// that is referenced by an entry in mEglSlots. In some situations we
// have a buffer in mCurrentTextureBuf, but no corresponding entry for
// it in our slot array. bindUnslottedBuffer handles that situation by
// binding the buffer without touching the EglSlots.
status_t bindUnslottedBufferLocked(EGLDisplay dpy);
// returns a graphic buffer used when the texture image has been released
static sp<GraphicBuffer> getDebugTexImageBuffer();
@ -319,10 +358,10 @@ private:
// consume buffers as hardware textures.
static const uint32_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
// mCurrentTextureBuf is the graphic buffer of the current texture. It's
// mCurrentTextureImage is the EglImage/buffer of the current texture. It's
// possible that this buffer is not associated with any buffer slot, so we
// must track it separately in order to support the getCurrentBuffer method.
sp<GraphicBuffer> mCurrentTextureBuf;
sp<EglImage> mCurrentTextureImage;
// mCurrentCrop is the crop rectangle that applies to the current texture.
// It gets set each time updateTexImage is called.
@ -382,17 +421,10 @@ private:
// EGLSlot contains the information and object references that
// GLConsumer maintains about a BufferQueue buffer slot.
struct EglSlot {
EglSlot()
: mEglImage(EGL_NO_IMAGE_KHR),
mEglFence(EGL_NO_SYNC_KHR) {
}
EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
// mEglImage is the EGLImage created from mGraphicBuffer.
EGLImageKHR mEglImage;
// mCropRect is the crop rectangle passed to EGL when mEglImage was
// created.
Rect mCropRect;
sp<EglImage> mEglImage;
// mFence is the EGL sync object that must signal before the buffer
// associated with this buffer slot may be dequeued. It is initialized
@ -444,6 +476,7 @@ private:
// mReleasedTexImageBuffer is a dummy buffer used when in single buffer
// mode and releaseTexImage() has been called
static sp<GraphicBuffer> sReleasedTexImageBuffer;
sp<EglImage> mReleasedTexImage;
};
// ----------------------------------------------------------------------------

View File

@ -279,8 +279,12 @@ status_t GLConsumer::releaseTexImage() {
return err;
}
if (mReleasedTexImage == NULL) {
mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
}
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
mCurrentTextureBuf = getDebugTexImageBuffer();
mCurrentTextureImage = mReleasedTexImage;
mCurrentCrop.makeInvalid();
mCurrentTransform = 0;
mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
@ -288,9 +292,11 @@ status_t GLConsumer::releaseTexImage() {
mCurrentFence = Fence::NO_FENCE;
if (mAttached) {
// bind a dummy texture
glBindTexture(mTexTarget, mTexName);
bindUnslottedBufferLocked(mEglDisplay);
// This binds a dummy buffer (mReleasedTexImage).
status_t err = bindTextureImageLocked();
if (err != NO_ERROR) {
return err;
}
} else {
// detached, don't touch the texture (and we may not even have an
// EGLDisplay here.
@ -332,29 +338,12 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
return err;
}
int slot = item->mBuf;
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;
// If item->mGraphicBuffer is not null, this buffer has not been acquired
// before, so any prior EglImage created is using a stale buffer. This
// replaces any old EglImage with a new one (using the new buffer).
if (item->mGraphicBuffer != NULL) {
int slot = item->mBuf;
mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
}
return NO_ERROR;
@ -395,29 +384,18 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
return err;
}
// If the mEglSlot entry is empty, create an EGLImage for the gralloc
// buffer currently in the slot in ConsumerBase.
//
// Ensure we have a valid EglImageKHR for the slot, creating an EglImage
// if nessessary, for the gralloc buffer currently in the slot in
// ConsumerBase.
// We may have to do this even when item.mGraphicBuffer == NULL (which
// means the buffer was previously acquired), if we destroyed the
// 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, item.mCrop);
if (image == EGL_NO_IMAGE_KHR) {
ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
mEglDisplay, buf);
const sp<GraphicBuffer>& gb = mSlots[buf].mGraphicBuffer;
ST_LOGW("buffer size=%ux%u st=%u usage=0x%x fmt=%d",
gb->getWidth(), gb->getHeight(), gb->getStride(),
gb->getUsage(), gb->getPixelFormat());
releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
mEglDisplay, EGL_NO_SYNC_KHR);
return UNKNOWN_ERROR;
}
mEglSlots[buf].mEglImage = image;
mEglSlots[buf].mCropRect = item.mCrop;
// means the buffer was previously acquired).
err = mEglSlots[buf].mEglImage->createIfNeeded(mEglDisplay, item.mCrop);
if (err != NO_ERROR) {
ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
mEglDisplay, buf);
releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
mEglDisplay, EGL_NO_SYNC_KHR);
return UNKNOWN_ERROR;
}
// Do whatever sync ops we need to do before releasing the old slot.
@ -433,15 +411,15 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
}
ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
mCurrentTexture,
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
mCurrentTexture, mCurrentTextureImage != NULL ?
mCurrentTextureImage->graphicBufferHandle() : 0,
buf, mSlots[buf].mGraphicBuffer->handle);
// release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
status_t status = releaseBufferLocked(
mCurrentTexture, mCurrentTextureBuf, mEglDisplay,
mEglSlots[mCurrentTexture].mEglFence);
mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
mEglDisplay, mEglSlots[mCurrentTexture].mEglFence);
if (status < NO_ERROR) {
ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
strerror(-status), status);
@ -452,7 +430,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
// Update the GLConsumer state.
mCurrentTexture = buf;
mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
mCurrentTextureImage = mEglSlots[buf].mEglImage;
mCurrentCrop = item.mCrop;
mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode;
@ -477,25 +455,26 @@ status_t GLConsumer::bindTextureImageLocked() {
}
glBindTexture(mTexTarget, mTexName);
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
if (mCurrentTextureBuf == NULL) {
ST_LOGE("bindTextureImage: no currently-bound texture");
return NO_INIT;
}
status_t err = bindUnslottedBufferLocked(mEglDisplay);
if (err != NO_ERROR) {
return err;
}
} else {
EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage;
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT &&
mCurrentTextureImage == NULL) {
ST_LOGE("bindTextureImage: no currently-bound texture");
return NO_INIT;
}
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
mCurrentCrop);
while ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGE("bindTextureImage: error binding external texture image %p"
": %#04x", image, error);
return UNKNOWN_ERROR;
}
if (err != NO_ERROR) {
ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
mEglDisplay, mCurrentTexture);
return UNKNOWN_ERROR;
}
mCurrentTextureImage->bindToTextureTarget(mTexTarget);
while ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGE("bindTextureImage: error binding external image: %#04x", error);
return UNKNOWN_ERROR;
}
// Wait for the new buffer to be ready.
@ -537,7 +516,7 @@ void GLConsumer::setReleaseFence(const sp<Fence>& fence) {
if (fence->isValid() &&
mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
status_t err = addReleaseFence(mCurrentTexture,
mCurrentTextureBuf, fence);
mCurrentTextureImage->graphicBuffer(), fence);
if (err != OK) {
ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)",
strerror(-err), err);
@ -583,18 +562,6 @@ status_t GLConsumer::detachFromContext() {
glDeleteTextures(1, &mTexName);
}
// Because we're giving up the EGLDisplay we need to free all the EGLImages
// that are associated with it. They'll be recreated when the
// GLConsumer gets attached to a new OpenGL ES context (and thus gets a
// new EGLDisplay).
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
EGLImageKHR img = mEglSlots[i].mEglImage;
if (img != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEglDisplay, img);
mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
}
}
mEglDisplay = EGL_NO_DISPLAY;
mEglContext = EGL_NO_CONTEXT;
mAttached = false;
@ -635,56 +602,25 @@ status_t GLConsumer::attachToContext(uint32_t tex) {
// buffer.
glBindTexture(mTexTarget, GLuint(tex));
if (mCurrentTextureBuf != NULL) {
// The EGLImageKHR that was associated with the slot was destroyed when
// the GLConsumer was detached from the old context, so we need to
// recreate it here.
status_t err = bindUnslottedBufferLocked(dpy);
if (err != NO_ERROR) {
return err;
}
}
mEglDisplay = dpy;
mEglContext = ctx;
mTexName = tex;
mAttached = true;
if (mCurrentTextureImage != NULL) {
// This may wait for a buffer a second time. This is likely required if
// this is a different context, since otherwise the wait could be skipped
// by bouncing through another context. For the same context the extra
// wait is redundant.
status_t err = bindTextureImageLocked();
if (err != NO_ERROR) {
return err;
}
}
return OK;
}
status_t GLConsumer::bindUnslottedBufferLocked(EGLDisplay dpy) {
ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p",
mCurrentTexture, mCurrentTextureBuf.get());
// Create a temporary EGLImageKHR.
Rect crop;
EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop);
if (image == EGL_NO_IMAGE_KHR) {
return UNKNOWN_ERROR;
}
// Attach the current buffer to the GL texture.
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
GLint error;
status_t err = OK;
while ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGE("bindUnslottedBuffer: error binding external texture image %p "
"(slot %d): %#04x", image, mCurrentTexture, error);
err = UNKNOWN_ERROR;
}
// We destroy the EGLImageKHR here because the current buffer may no
// longer be associated with one of the buffer slots, so we have
// nowhere to to store it. If the buffer is still associated with a
// slot then another EGLImageKHR will be created next time that buffer
// gets acquired in updateTexImage.
eglDestroyImageKHR(dpy, image);
return err;
}
status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) {
ST_LOGV("syncForReleaseLocked");
@ -708,7 +644,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) {
}
sp<Fence> fence(new Fence(fenceFd));
status_t err = addReleaseFenceLocked(mCurrentTexture,
mCurrentTextureBuf, fence);
mCurrentTextureImage->graphicBuffer(), fence);
if (err != OK) {
ST_LOGE("syncForReleaseLocked: error adding release fence: "
"%s (%d)", strerror(-err), err);
@ -787,11 +723,11 @@ void GLConsumer::setFilteringEnabled(bool enabled) {
bool needsRecompute = mFilteringEnabled != enabled;
mFilteringEnabled = enabled;
if (needsRecompute && mCurrentTextureBuf==NULL) {
ST_LOGD("setFilteringEnabled called with mCurrentTextureBuf == NULL");
if (needsRecompute && mCurrentTextureImage==NULL) {
ST_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL");
}
if (needsRecompute && mCurrentTextureBuf != NULL) {
if (needsRecompute && mCurrentTextureImage != NULL) {
computeCurrentTransformMatrixLocked();
}
}
@ -825,10 +761,11 @@ void GLConsumer::computeCurrentTransformMatrixLocked() {
}
}
sp<GraphicBuffer>& buf(mCurrentTextureBuf);
sp<GraphicBuffer> buf = (mCurrentTextureImage == NULL) ?
NULL : mCurrentTextureImage->graphicBuffer();
if (buf == NULL) {
ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureImage is NULL");
}
float mtxBeforeFlipV[16];
@ -911,39 +848,10 @@ nsecs_t GLConsumer::getFrameNumber() {
return mCurrentFrameNumber;
}
EGLImageKHR GLConsumer::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
EGLint attrs[] = {
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) {
EGLint error = eglGetError();
ST_LOGE("error creating EGLImage: %#x", error);
}
return image;
}
sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
Mutex::Autolock lock(mMutex);
return mCurrentTextureBuf;
return (mCurrentTextureImage == NULL) ?
NULL : mCurrentTextureImage->graphicBuffer();
}
Rect GLConsumer::getCurrentCrop() const {
@ -1067,18 +975,13 @@ void GLConsumer::freeBufferLocked(int slotIndex) {
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
}
EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
if (img != EGL_NO_IMAGE_KHR) {
ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
eglDestroyImageKHR(mEglDisplay, img);
}
mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
mEglSlots[slotIndex].mEglImage.clear();
ConsumerBase::freeBufferLocked(slotIndex);
}
void GLConsumer::abandonLocked() {
ST_LOGV("abandonLocked");
mCurrentTextureBuf.clear();
mCurrentTextureImage.clear();
ConsumerBase::abandonLocked();
}
@ -1138,4 +1041,87 @@ static void mtxMul(float out[16], const float a[16], const float b[16]) {
out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];
}
GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) :
mGraphicBuffer(graphicBuffer),
mEglImage(EGL_NO_IMAGE_KHR),
mEglDisplay(EGL_NO_DISPLAY) {
}
GLConsumer::EglImage::~EglImage() {
if (mEglImage != EGL_NO_IMAGE_KHR) {
if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
ALOGE("~EglImage: eglDestroyImageKHR failed");
}
}
}
status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay,
const Rect& cropRect) {
// If there's an image and it's no longer valid, destroy it.
bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
bool displayInvalid = mEglDisplay != eglDisplay;
bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect;
if (haveImage && (displayInvalid || cropInvalid)) {
if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
ALOGE("createIfNeeded: eglDestroyImageKHR failed");
}
mEglImage = EGL_NO_IMAGE_KHR;
mEglDisplay = EGL_NO_DISPLAY;
}
// If there's no image, create one.
if (mEglImage == EGL_NO_IMAGE_KHR) {
mEglDisplay = eglDisplay;
mCropRect = cropRect;
mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect);
}
// Fail if we can't create a valid image.
if (mEglImage == EGL_NO_IMAGE_KHR) {
mEglDisplay = EGL_NO_DISPLAY;
mCropRect.makeInvalid();
const sp<GraphicBuffer>& buffer = mGraphicBuffer;
ALOGE("Failed to create image. size=%ux%u st=%u usage=0x%x fmt=%d",
buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
buffer->getUsage(), buffer->getPixelFormat());
return UNKNOWN_ERROR;
}
return OK;
}
void GLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
glEGLImageTargetTexture2DOES(texTarget, (GLeglImageOES)mEglImage);
}
EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
EGLint attrs[] = {
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) {
EGLint error = eglGetError();
ALOGE("error creating EGLImage: %#x", error);
}
return image;
}
}; // namespace android