am 22956874
: Merge "GLProducer: Reference count images rather than buffers." into lmp-dev
* commit '2295687487a0f2cc3e77915d5b0fe794d3af4d20': GLProducer: Reference count images rather than buffers.
This commit is contained in:
commit
0c92547d87
@ -231,7 +231,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
|
|
||||||
// abandonLocked overrides the ConsumerBase method to clear
|
// abandonLocked overrides the ConsumerBase method to clear
|
||||||
// mCurrentTextureBuf in addition to the ConsumerBase behavior.
|
// mCurrentTextureImage in addition to the ConsumerBase behavior.
|
||||||
virtual void abandonLocked();
|
virtual void abandonLocked();
|
||||||
|
|
||||||
// dumpLocked overrides the ConsumerBase method to dump GLConsumer-
|
// dumpLocked overrides the ConsumerBase method to dump GLConsumer-
|
||||||
@ -262,7 +262,7 @@ protected:
|
|||||||
status_t updateAndReleaseLocked(const BufferQueue::BufferItem& item);
|
status_t updateAndReleaseLocked(const BufferQueue::BufferItem& item);
|
||||||
|
|
||||||
// Binds mTexName and the current buffer to mTexTarget. Uses
|
// 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.
|
// bind succeeds, this calls doGLFenceWait.
|
||||||
status_t bindTextureImageLocked();
|
status_t bindTextureImageLocked();
|
||||||
|
|
||||||
@ -275,11 +275,57 @@ protected:
|
|||||||
status_t checkAndUpdateEglStateLocked(bool contextCheck = false);
|
status_t checkAndUpdateEglStateLocked(bool contextCheck = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// createImage creates a new EGLImage from a GraphicBuffer.
|
// EglImage is a utility class for tracking and creating EGLImageKHRs. There
|
||||||
EGLImageKHR createImage(EGLDisplay dpy,
|
// is primarily just one image per slot, but there is also special cases:
|
||||||
const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
|
// - 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
|
// initialized this will release the reference to the GraphicBuffer in that
|
||||||
// slot and destroy the EGLImage in that slot. Otherwise it has no effect.
|
// 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
|
// computeCurrentTransformMatrixLocked computes the transform matrix for the
|
||||||
// current texture. It uses mCurrentTransform and the current GraphicBuffer
|
// current texture. It uses mCurrentTransform and the current GraphicBuffer
|
||||||
// to compute this matrix and stores it in mCurrentTransformMatrix.
|
// to compute this matrix and stores it in mCurrentTransformMatrix.
|
||||||
// mCurrentTextureBuf must not be NULL.
|
// mCurrentTextureImage must not be NULL.
|
||||||
void computeCurrentTransformMatrixLocked();
|
void computeCurrentTransformMatrixLocked();
|
||||||
|
|
||||||
// doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
|
// doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
|
||||||
@ -303,13 +349,6 @@ private:
|
|||||||
// before the outstanding accesses have completed.
|
// before the outstanding accesses have completed.
|
||||||
status_t syncForReleaseLocked(EGLDisplay dpy);
|
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
|
// returns a graphic buffer used when the texture image has been released
|
||||||
static sp<GraphicBuffer> getDebugTexImageBuffer();
|
static sp<GraphicBuffer> getDebugTexImageBuffer();
|
||||||
|
|
||||||
@ -319,10 +358,10 @@ private:
|
|||||||
// consume buffers as hardware textures.
|
// consume buffers as hardware textures.
|
||||||
static const uint32_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
|
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
|
// possible that this buffer is not associated with any buffer slot, so we
|
||||||
// must track it separately in order to support the getCurrentBuffer method.
|
// 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.
|
// mCurrentCrop is the crop rectangle that applies to the current texture.
|
||||||
// It gets set each time updateTexImage is called.
|
// It gets set each time updateTexImage is called.
|
||||||
@ -382,17 +421,10 @@ private:
|
|||||||
// EGLSlot contains the information and object references that
|
// EGLSlot contains the information and object references that
|
||||||
// GLConsumer maintains about a BufferQueue buffer slot.
|
// GLConsumer maintains about a BufferQueue buffer slot.
|
||||||
struct EglSlot {
|
struct EglSlot {
|
||||||
EglSlot()
|
EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
|
||||||
: mEglImage(EGL_NO_IMAGE_KHR),
|
|
||||||
mEglFence(EGL_NO_SYNC_KHR) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// mEglImage is the EGLImage created from mGraphicBuffer.
|
// mEglImage is the EGLImage created from mGraphicBuffer.
|
||||||
EGLImageKHR mEglImage;
|
sp<EglImage> 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
|
||||||
@ -444,6 +476,7 @@ private:
|
|||||||
// mReleasedTexImageBuffer is a dummy buffer used when in single buffer
|
// mReleasedTexImageBuffer is a dummy buffer used when in single buffer
|
||||||
// mode and releaseTexImage() has been called
|
// mode and releaseTexImage() has been called
|
||||||
static sp<GraphicBuffer> sReleasedTexImageBuffer;
|
static sp<GraphicBuffer> sReleasedTexImageBuffer;
|
||||||
|
sp<EglImage> mReleasedTexImage;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -279,8 +279,12 @@ status_t GLConsumer::releaseTexImage() {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mReleasedTexImage == NULL) {
|
||||||
|
mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
|
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
|
||||||
mCurrentTextureBuf = getDebugTexImageBuffer();
|
mCurrentTextureImage = mReleasedTexImage;
|
||||||
mCurrentCrop.makeInvalid();
|
mCurrentCrop.makeInvalid();
|
||||||
mCurrentTransform = 0;
|
mCurrentTransform = 0;
|
||||||
mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
|
mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
|
||||||
@ -288,9 +292,11 @@ status_t GLConsumer::releaseTexImage() {
|
|||||||
mCurrentFence = Fence::NO_FENCE;
|
mCurrentFence = Fence::NO_FENCE;
|
||||||
|
|
||||||
if (mAttached) {
|
if (mAttached) {
|
||||||
// bind a dummy texture
|
// This binds a dummy buffer (mReleasedTexImage).
|
||||||
glBindTexture(mTexTarget, mTexName);
|
status_t err = bindTextureImageLocked();
|
||||||
bindUnslottedBufferLocked(mEglDisplay);
|
if (err != NO_ERROR) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// detached, don't touch the texture (and we may not even have an
|
// detached, don't touch the texture (and we may not even have an
|
||||||
// EGLDisplay here.
|
// EGLDisplay here.
|
||||||
@ -332,29 +338,12 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int slot = item->mBuf;
|
// If item->mGraphicBuffer is not null, this buffer has not been acquired
|
||||||
bool destroyEglImage = false;
|
// 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 (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
|
if (item->mGraphicBuffer != NULL) {
|
||||||
if (item->mGraphicBuffer != NULL) {
|
int slot = item->mBuf;
|
||||||
// This buffer has not been acquired before, so we must assume
|
mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
|
||||||
// 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;
|
return NO_ERROR;
|
||||||
@ -395,29 +384,18 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the mEglSlot entry is empty, create an EGLImage for the gralloc
|
// Ensure we have a valid EglImageKHR for the slot, creating an EglImage
|
||||||
// buffer currently in the slot in ConsumerBase.
|
// if nessessary, for the gralloc buffer currently in the slot in
|
||||||
//
|
// ConsumerBase.
|
||||||
// We may have to do this even when item.mGraphicBuffer == NULL (which
|
// We may have to do this even when item.mGraphicBuffer == NULL (which
|
||||||
// means the buffer was previously acquired), if we destroyed the
|
// means the buffer was previously acquired).
|
||||||
// EGLImage when detaching from a context but the buffer has not been
|
err = mEglSlots[buf].mEglImage->createIfNeeded(mEglDisplay, item.mCrop);
|
||||||
// re-allocated.
|
if (err != NO_ERROR) {
|
||||||
if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
|
ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
|
||||||
EGLImageKHR image = createImage(mEglDisplay,
|
mEglDisplay, buf);
|
||||||
mSlots[buf].mGraphicBuffer, item.mCrop);
|
releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
|
||||||
if (image == EGL_NO_IMAGE_KHR) {
|
mEglDisplay, EGL_NO_SYNC_KHR);
|
||||||
ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
|
return UNKNOWN_ERROR;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@ -433,15 +411,15 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
||||||
mCurrentTexture,
|
mCurrentTexture, mCurrentTextureImage != NULL ?
|
||||||
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
|
mCurrentTextureImage->graphicBufferHandle() : 0,
|
||||||
buf, mSlots[buf].mGraphicBuffer->handle);
|
buf, mSlots[buf].mGraphicBuffer->handle);
|
||||||
|
|
||||||
// release old buffer
|
// release old buffer
|
||||||
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||||
status_t status = releaseBufferLocked(
|
status_t status = releaseBufferLocked(
|
||||||
mCurrentTexture, mCurrentTextureBuf, mEglDisplay,
|
mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
|
||||||
mEglSlots[mCurrentTexture].mEglFence);
|
mEglDisplay, mEglSlots[mCurrentTexture].mEglFence);
|
||||||
if (status < NO_ERROR) {
|
if (status < NO_ERROR) {
|
||||||
ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
|
ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
|
||||||
strerror(-status), status);
|
strerror(-status), status);
|
||||||
@ -452,7 +430,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
|
|||||||
|
|
||||||
// Update the GLConsumer state.
|
// Update the GLConsumer state.
|
||||||
mCurrentTexture = buf;
|
mCurrentTexture = buf;
|
||||||
mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
|
mCurrentTextureImage = mEglSlots[buf].mEglImage;
|
||||||
mCurrentCrop = item.mCrop;
|
mCurrentCrop = item.mCrop;
|
||||||
mCurrentTransform = item.mTransform;
|
mCurrentTransform = item.mTransform;
|
||||||
mCurrentScalingMode = item.mScalingMode;
|
mCurrentScalingMode = item.mScalingMode;
|
||||||
@ -477,25 +455,26 @@ status_t GLConsumer::bindTextureImageLocked() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
glBindTexture(mTexTarget, mTexName);
|
glBindTexture(mTexTarget, mTexName);
|
||||||
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
|
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT &&
|
||||||
if (mCurrentTextureBuf == NULL) {
|
mCurrentTextureImage == NULL) {
|
||||||
ST_LOGE("bindTextureImage: no currently-bound texture");
|
ST_LOGE("bindTextureImage: no currently-bound texture");
|
||||||
return NO_INIT;
|
return NO_INIT;
|
||||||
}
|
}
|
||||||
status_t err = bindUnslottedBufferLocked(mEglDisplay);
|
|
||||||
if (err != NO_ERROR) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage;
|
|
||||||
|
|
||||||
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
|
status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
|
||||||
|
mCurrentCrop);
|
||||||
|
|
||||||
while ((error = glGetError()) != GL_NO_ERROR) {
|
if (err != NO_ERROR) {
|
||||||
ST_LOGE("bindTextureImage: error binding external texture image %p"
|
ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
|
||||||
": %#04x", image, error);
|
mEglDisplay, mCurrentTexture);
|
||||||
return UNKNOWN_ERROR;
|
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.
|
// Wait for the new buffer to be ready.
|
||||||
@ -537,7 +516,7 @@ void GLConsumer::setReleaseFence(const sp<Fence>& fence) {
|
|||||||
if (fence->isValid() &&
|
if (fence->isValid() &&
|
||||||
mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||||
status_t err = addReleaseFence(mCurrentTexture,
|
status_t err = addReleaseFence(mCurrentTexture,
|
||||||
mCurrentTextureBuf, fence);
|
mCurrentTextureImage->graphicBuffer(), fence);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)",
|
ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)",
|
||||||
strerror(-err), err);
|
strerror(-err), err);
|
||||||
@ -583,18 +562,6 @@ status_t GLConsumer::detachFromContext() {
|
|||||||
glDeleteTextures(1, &mTexName);
|
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;
|
mEglDisplay = EGL_NO_DISPLAY;
|
||||||
mEglContext = EGL_NO_CONTEXT;
|
mEglContext = EGL_NO_CONTEXT;
|
||||||
mAttached = false;
|
mAttached = false;
|
||||||
@ -635,56 +602,25 @@ status_t GLConsumer::attachToContext(uint32_t tex) {
|
|||||||
// buffer.
|
// buffer.
|
||||||
glBindTexture(mTexTarget, GLuint(tex));
|
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;
|
mEglDisplay = dpy;
|
||||||
mEglContext = ctx;
|
mEglContext = ctx;
|
||||||
mTexName = tex;
|
mTexName = tex;
|
||||||
mAttached = true;
|
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;
|
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) {
|
status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) {
|
||||||
ST_LOGV("syncForReleaseLocked");
|
ST_LOGV("syncForReleaseLocked");
|
||||||
@ -708,7 +644,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) {
|
|||||||
}
|
}
|
||||||
sp<Fence> fence(new Fence(fenceFd));
|
sp<Fence> fence(new Fence(fenceFd));
|
||||||
status_t err = addReleaseFenceLocked(mCurrentTexture,
|
status_t err = addReleaseFenceLocked(mCurrentTexture,
|
||||||
mCurrentTextureBuf, fence);
|
mCurrentTextureImage->graphicBuffer(), fence);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ST_LOGE("syncForReleaseLocked: error adding release fence: "
|
ST_LOGE("syncForReleaseLocked: error adding release fence: "
|
||||||
"%s (%d)", strerror(-err), err);
|
"%s (%d)", strerror(-err), err);
|
||||||
@ -787,11 +723,11 @@ void GLConsumer::setFilteringEnabled(bool enabled) {
|
|||||||
bool needsRecompute = mFilteringEnabled != enabled;
|
bool needsRecompute = mFilteringEnabled != enabled;
|
||||||
mFilteringEnabled = enabled;
|
mFilteringEnabled = enabled;
|
||||||
|
|
||||||
if (needsRecompute && mCurrentTextureBuf==NULL) {
|
if (needsRecompute && mCurrentTextureImage==NULL) {
|
||||||
ST_LOGD("setFilteringEnabled called with mCurrentTextureBuf == NULL");
|
ST_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsRecompute && mCurrentTextureBuf != NULL) {
|
if (needsRecompute && mCurrentTextureImage != NULL) {
|
||||||
computeCurrentTransformMatrixLocked();
|
computeCurrentTransformMatrixLocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -825,10 +761,11 @@ void GLConsumer::computeCurrentTransformMatrixLocked() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sp<GraphicBuffer>& buf(mCurrentTextureBuf);
|
sp<GraphicBuffer> buf = (mCurrentTextureImage == NULL) ?
|
||||||
|
NULL : mCurrentTextureImage->graphicBuffer();
|
||||||
|
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
|
ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureImage is NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
float mtxBeforeFlipV[16];
|
float mtxBeforeFlipV[16];
|
||||||
@ -911,39 +848,10 @@ nsecs_t GLConsumer::getFrameNumber() {
|
|||||||
return mCurrentFrameNumber;
|
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 {
|
sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
|
||||||
Mutex::Autolock lock(mMutex);
|
Mutex::Autolock lock(mMutex);
|
||||||
return mCurrentTextureBuf;
|
return (mCurrentTextureImage == NULL) ?
|
||||||
|
NULL : mCurrentTextureImage->graphicBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect GLConsumer::getCurrentCrop() const {
|
Rect GLConsumer::getCurrentCrop() const {
|
||||||
@ -1067,18 +975,13 @@ void GLConsumer::freeBufferLocked(int slotIndex) {
|
|||||||
if (slotIndex == mCurrentTexture) {
|
if (slotIndex == mCurrentTexture) {
|
||||||
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
|
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
|
||||||
}
|
}
|
||||||
EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
|
mEglSlots[slotIndex].mEglImage.clear();
|
||||||
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;
|
|
||||||
ConsumerBase::freeBufferLocked(slotIndex);
|
ConsumerBase::freeBufferLocked(slotIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLConsumer::abandonLocked() {
|
void GLConsumer::abandonLocked() {
|
||||||
ST_LOGV("abandonLocked");
|
ST_LOGV("abandonLocked");
|
||||||
mCurrentTextureBuf.clear();
|
mCurrentTextureImage.clear();
|
||||||
ConsumerBase::abandonLocked();
|
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];
|
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
|
}; // namespace android
|
||||||
|
Loading…
Reference in New Issue
Block a user