Merge "SurfaceTexture: add context attach & detach"
This commit is contained in:
commit
0e1080f887
|
@ -55,8 +55,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
|
// SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
|
||||||
// name of the OpenGL ES texture to which images are to be streamed. This
|
// name of the OpenGL ES texture to which images are to be streamed.
|
||||||
// texture name cannot be changed once the SurfaceTexture is created.
|
|
||||||
// allowSynchronousMode specifies whether or not synchronous mode can be
|
// allowSynchronousMode specifies whether or not synchronous mode can be
|
||||||
// enabled. texTarget specifies the OpenGL ES texture target to which the
|
// enabled. texTarget specifies the OpenGL ES texture target to which the
|
||||||
// texture will be bound in updateTexImage. useFenceSync specifies whether
|
// texture will be bound in updateTexImage. useFenceSync specifies whether
|
||||||
|
@ -64,6 +63,21 @@ public:
|
||||||
// is enabled at compile-time. A custom bufferQueue can be specified
|
// is enabled at compile-time. A custom bufferQueue can be specified
|
||||||
// if behavior for queue/dequeue/connect etc needs to be customized.
|
// if behavior for queue/dequeue/connect etc needs to be customized.
|
||||||
// Otherwise a default BufferQueue will be created and used.
|
// Otherwise a default BufferQueue will be created and used.
|
||||||
|
//
|
||||||
|
// For legacy reasons, the SurfaceTexture is created in a state where it is
|
||||||
|
// considered attached to an OpenGL ES context for the purposes of the
|
||||||
|
// attachToContext and detachFromContext methods. However, despite being
|
||||||
|
// considered "attached" to a context, the specific OpenGL ES context
|
||||||
|
// doesn't get latched until the first call to updateTexImage. After that
|
||||||
|
// point, all calls to updateTexImage must be made with the same OpenGL ES
|
||||||
|
// context current.
|
||||||
|
//
|
||||||
|
// A SurfaceTexture may be detached from one OpenGL ES context and then
|
||||||
|
// attached to a different context using the detachFromContext and
|
||||||
|
// attachToContext methods, respectively. The intention of these methods is
|
||||||
|
// purely to allow a SurfaceTexture to be transferred from one consumer
|
||||||
|
// context to another. If such a transfer is not needed there is no
|
||||||
|
// requirement that either of these methods be called.
|
||||||
SurfaceTexture(GLuint tex, bool allowSynchronousMode = true,
|
SurfaceTexture(GLuint tex, bool allowSynchronousMode = true,
|
||||||
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
|
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
|
||||||
const sp<BufferQueue> &bufferQueue = 0);
|
const sp<BufferQueue> &bufferQueue = 0);
|
||||||
|
@ -175,8 +189,37 @@ public:
|
||||||
virtual status_t connect(int api,
|
virtual status_t connect(int api,
|
||||||
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
|
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
|
||||||
|
|
||||||
|
// getBufferQueue returns the BufferQueue object to which this
|
||||||
|
// SurfaceTexture is connected.
|
||||||
sp<BufferQueue> getBufferQueue() const;
|
sp<BufferQueue> getBufferQueue() const;
|
||||||
|
|
||||||
|
// detachFromContext detaches the SurfaceTexture from the calling thread's
|
||||||
|
// current OpenGL ES context. This context must be the same as the context
|
||||||
|
// that was current for previous calls to updateTexImage.
|
||||||
|
//
|
||||||
|
// Detaching a SurfaceTexture from an OpenGL ES context will result in the
|
||||||
|
// deletion of the OpenGL ES texture object into which the images were being
|
||||||
|
// streamed. After a SurfaceTexture has been detached from the OpenGL ES
|
||||||
|
// context calls to updateTexImage will fail returning INVALID_OPERATION
|
||||||
|
// until the SurfaceTexture is attached to a new OpenGL ES context using the
|
||||||
|
// attachToContext method.
|
||||||
|
status_t detachFromContext();
|
||||||
|
|
||||||
|
// attachToContext attaches a SurfaceTexture that is currently in the
|
||||||
|
// 'detached' state to the current OpenGL ES context. A SurfaceTexture is
|
||||||
|
// in the 'detached' state iff detachFromContext has successfully been
|
||||||
|
// called and no calls to attachToContext have succeeded since the last
|
||||||
|
// detachFromContext call. Calls to attachToContext made on a
|
||||||
|
// SurfaceTexture that is not in the 'detached' state will result in an
|
||||||
|
// INVALID_OPERATION error.
|
||||||
|
//
|
||||||
|
// The tex argument specifies the OpenGL ES texture object name in the
|
||||||
|
// new context into which the image contents will be streamed. A successful
|
||||||
|
// call to attachToContext will result in this texture object being bound to
|
||||||
|
// the texture target and populated with the image contents that were
|
||||||
|
// current at the time of the last call to detachFromContext.
|
||||||
|
status_t attachToContext(GLuint tex);
|
||||||
|
|
||||||
// dump our state in a String
|
// dump our state in a String
|
||||||
virtual void dump(String8& result) const;
|
virtual void dump(String8& result) const;
|
||||||
virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
|
virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
|
||||||
|
@ -209,6 +252,12 @@ private:
|
||||||
// to compute this matrix and stores it in mCurrentTransformMatrix.
|
// to compute this matrix and stores it in mCurrentTransformMatrix.
|
||||||
void computeCurrentTransformMatrix();
|
void computeCurrentTransformMatrix();
|
||||||
|
|
||||||
|
// syncForReleaseLocked performs the synchronization needed to release the
|
||||||
|
// current slot from an OpenGL ES context. If needed it will set the
|
||||||
|
// current slot's fence to guard against a producer accessing the buffer
|
||||||
|
// before the outstanding accesses have completed.
|
||||||
|
status_t syncForReleaseLocked(EGLDisplay dpy);
|
||||||
|
|
||||||
// mCurrentTextureBuf is the graphic buffer of the current texture. It's
|
// mCurrentTextureBuf is the graphic 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.
|
||||||
|
@ -237,8 +286,8 @@ private:
|
||||||
|
|
||||||
// mTexName is the name of the OpenGL texture to which streamed images will
|
// mTexName is the name of the OpenGL texture to which streamed images will
|
||||||
// be bound when updateTexImage is called. It is set at construction time
|
// be bound when updateTexImage is called. It is set at construction time
|
||||||
// changed with a call to setTexName.
|
// and can be changed with a call to attachToContext.
|
||||||
const GLuint mTexName;
|
GLuint mTexName;
|
||||||
|
|
||||||
// mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
|
// mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
|
||||||
// extension should be used to prevent buffers from being dequeued before
|
// extension should be used to prevent buffers from being dequeued before
|
||||||
|
@ -277,13 +326,14 @@ private:
|
||||||
|
|
||||||
// mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
|
// mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
|
||||||
// associated. It is intialized to EGL_NO_DISPLAY and gets set to the
|
// associated. It is intialized to EGL_NO_DISPLAY and gets set to the
|
||||||
// current display when updateTexImage is called for the first time.
|
// current display when updateTexImage is called for the first time and when
|
||||||
|
// attachToContext is called.
|
||||||
EGLDisplay mEglDisplay;
|
EGLDisplay mEglDisplay;
|
||||||
|
|
||||||
// mEglContext is the OpenGL ES context with which this SurfaceTexture is
|
// mEglContext is the OpenGL ES context with which this SurfaceTexture is
|
||||||
// currently associated. It is initialized to EGL_NO_CONTEXT and gets set
|
// currently associated. It is initialized to EGL_NO_CONTEXT and gets set
|
||||||
// to the current GL context when updateTexImage is called for the first
|
// to the current GL context when updateTexImage is called for the first
|
||||||
// time.
|
// time and when attachToContext is called.
|
||||||
EGLContext mEglContext;
|
EGLContext mEglContext;
|
||||||
|
|
||||||
// mEGLSlots stores the buffers that have been allocated by the BufferQueue
|
// mEGLSlots stores the buffers that have been allocated by the BufferQueue
|
||||||
|
@ -323,6 +373,14 @@ private:
|
||||||
// if none is supplied
|
// if none is supplied
|
||||||
sp<BufferQueue> mBufferQueue;
|
sp<BufferQueue> mBufferQueue;
|
||||||
|
|
||||||
|
// mAttached indicates whether the SurfaceTexture is currently attached to
|
||||||
|
// an OpenGL ES context. For legacy reasons, this is initialized to true,
|
||||||
|
// indicating that the SurfaceTexture is considered to be attached to
|
||||||
|
// whatever context is current at the time of the first updateTexImage call.
|
||||||
|
// It is set to false by detachFromContext, and then set to true again by
|
||||||
|
// attachToContext.
|
||||||
|
bool mAttached;
|
||||||
|
|
||||||
// mMutex is the mutex used to prevent concurrent access to the member
|
// mMutex is the mutex used to prevent concurrent access to the member
|
||||||
// variables of SurfaceTexture objects. It must be locked whenever the
|
// variables of SurfaceTexture objects. It must be locked whenever the
|
||||||
// member variables are accessed.
|
// member variables are accessed.
|
||||||
|
|
|
@ -118,7 +118,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
||||||
mEglDisplay(EGL_NO_DISPLAY),
|
mEglDisplay(EGL_NO_DISPLAY),
|
||||||
mEglContext(EGL_NO_CONTEXT),
|
mEglContext(EGL_NO_CONTEXT),
|
||||||
mAbandoned(false),
|
mAbandoned(false),
|
||||||
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
|
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
|
||||||
|
mAttached(true)
|
||||||
{
|
{
|
||||||
// Choose a name using the PID and a process-unique ID.
|
// Choose a name using the PID and a process-unique ID.
|
||||||
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
||||||
|
@ -176,21 +177,29 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
Mutex::Autolock lock(mMutex);
|
Mutex::Autolock lock(mMutex);
|
||||||
|
|
||||||
if (mAbandoned) {
|
if (mAbandoned) {
|
||||||
ST_LOGE("calling updateTexImage() on an abandoned SurfaceTexture");
|
ST_LOGE("updateTexImage: SurfaceTexture is abandoned!");
|
||||||
return NO_INIT;
|
return NO_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mAttached) {
|
||||||
|
ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL "
|
||||||
|
"ES context");
|
||||||
|
return INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
EGLDisplay dpy = eglGetCurrentDisplay();
|
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||||
EGLContext ctx = eglGetCurrentContext();
|
EGLContext ctx = eglGetCurrentContext();
|
||||||
|
|
||||||
if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
|
if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) ||
|
||||||
|
dpy == EGL_NO_DISPLAY) {
|
||||||
ST_LOGE("updateTexImage: invalid current EGLDisplay");
|
ST_LOGE("updateTexImage: invalid current EGLDisplay");
|
||||||
return -EINVAL;
|
return INVALID_OPERATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
|
if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
|
||||||
|
ctx == EGL_NO_CONTEXT) {
|
||||||
ST_LOGE("updateTexImage: invalid current EGLContext");
|
ST_LOGE("updateTexImage: invalid current EGLContext");
|
||||||
return -EINVAL;
|
return INVALID_OPERATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
mEglDisplay = dpy;
|
mEglDisplay = dpy;
|
||||||
|
@ -216,7 +225,7 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
EGLImageKHR image = mEGLSlots[buf].mEglImage;
|
EGLImageKHR image = mEGLSlots[buf].mEglImage;
|
||||||
if (image == EGL_NO_IMAGE_KHR) {
|
if (image == EGL_NO_IMAGE_KHR) {
|
||||||
if (item.mGraphicBuffer == 0) {
|
if (item.mGraphicBuffer == 0) {
|
||||||
ST_LOGE("buffer at slot %d is null", buf);
|
ST_LOGE("updateTexImage: buffer at slot %d is null", buf);
|
||||||
return BAD_VALUE;
|
return BAD_VALUE;
|
||||||
}
|
}
|
||||||
image = createImage(dpy, item.mGraphicBuffer);
|
image = createImage(dpy, item.mGraphicBuffer);
|
||||||
|
@ -224,7 +233,7 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
if (image == EGL_NO_IMAGE_KHR) {
|
if (image == EGL_NO_IMAGE_KHR) {
|
||||||
// NOTE: if dpy was invalid, createImage() is guaranteed to
|
// NOTE: if dpy was invalid, createImage() is guaranteed to
|
||||||
// fail. so we'd end up here.
|
// fail. so we'd end up here.
|
||||||
return -EINVAL;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,31 +245,23 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
glBindTexture(mTexTarget, mTexName);
|
glBindTexture(mTexTarget, mTexName);
|
||||||
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
|
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
|
||||||
|
|
||||||
bool failed = false;
|
status_t err = OK;
|
||||||
while ((error = glGetError()) != GL_NO_ERROR) {
|
while ((error = glGetError()) != GL_NO_ERROR) {
|
||||||
ST_LOGE("error binding external texture image %p (slot %d): %#04x",
|
ST_LOGE("updateTexImage: error binding external texture image %p "
|
||||||
image, buf, error);
|
"(slot %d): %#04x", image, buf, error);
|
||||||
failed = true;
|
err = UNKNOWN_ERROR;
|
||||||
}
|
|
||||||
if (failed) {
|
|
||||||
mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
if (err == OK) {
|
||||||
if (mUseFenceSync) {
|
err = syncForReleaseLocked(dpy);
|
||||||
EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR,
|
}
|
||||||
NULL);
|
|
||||||
if (fence == EGL_NO_SYNC_KHR) {
|
if (err != OK) {
|
||||||
ALOGE("updateTexImage: error creating fence: %#x",
|
// Release the buffer we just acquired. It's not safe to
|
||||||
eglGetError());
|
// release the old buffer, so instead we just drop the new frame.
|
||||||
mBufferQueue->releaseBuffer(buf, dpy,
|
mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
|
||||||
mEGLSlots[buf].mFence);
|
mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR;
|
||||||
return -EINVAL;
|
return err;
|
||||||
}
|
|
||||||
glFlush();
|
|
||||||
mEGLSlots[mCurrentTexture].mFence = fence;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
||||||
|
@ -268,9 +269,12 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
|
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
|
||||||
buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
|
buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
|
||||||
|
|
||||||
// release old buffer
|
// Release the old buffer
|
||||||
mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
|
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||||
mEGLSlots[mCurrentTexture].mFence);
|
mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
|
||||||
|
mEGLSlots[mCurrentTexture].mFence);
|
||||||
|
mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the SurfaceTexture state.
|
// Update the SurfaceTexture state.
|
||||||
mCurrentTexture = buf;
|
mCurrentTexture = buf;
|
||||||
|
@ -280,10 +284,6 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
mCurrentScalingMode = item.mScalingMode;
|
mCurrentScalingMode = item.mScalingMode;
|
||||||
mCurrentTimestamp = item.mTimestamp;
|
mCurrentTimestamp = item.mTimestamp;
|
||||||
computeCurrentTransformMatrix();
|
computeCurrentTransformMatrix();
|
||||||
|
|
||||||
// Now that we've passed the point at which failures can happen,
|
|
||||||
// it's safe to remove the buffer from the front of the queue.
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// We always bind the texture even if we don't update its contents.
|
// We always bind the texture even if we don't update its contents.
|
||||||
glBindTexture(mTexTarget, mTexName);
|
glBindTexture(mTexTarget, mTexName);
|
||||||
|
@ -292,6 +292,168 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status_t SurfaceTexture::detachFromContext() {
|
||||||
|
ATRACE_CALL();
|
||||||
|
ST_LOGV("detachFromContext");
|
||||||
|
Mutex::Autolock lock(mMutex);
|
||||||
|
|
||||||
|
if (mAbandoned) {
|
||||||
|
ST_LOGE("detachFromContext: abandoned SurfaceTexture");
|
||||||
|
return NO_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mAttached) {
|
||||||
|
ST_LOGE("detachFromContext: SurfaceTexture is not attached to a "
|
||||||
|
"context");
|
||||||
|
return INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||||
|
EGLContext ctx = eglGetCurrentContext();
|
||||||
|
|
||||||
|
if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
|
||||||
|
ST_LOGE("detachFromContext: invalid current EGLDisplay");
|
||||||
|
return INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
|
||||||
|
ST_LOGE("detachFromContext: invalid current EGLContext");
|
||||||
|
return INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) {
|
||||||
|
status_t err = syncForReleaseLocked(dpy);
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteTextures(1, &mTexName);
|
||||||
|
}
|
||||||
|
|
||||||
|
mEglDisplay = EGL_NO_DISPLAY;
|
||||||
|
mEglContext = EGL_NO_CONTEXT;
|
||||||
|
mAttached = false;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t SurfaceTexture::attachToContext(GLuint tex) {
|
||||||
|
ATRACE_CALL();
|
||||||
|
ST_LOGV("attachToContext");
|
||||||
|
Mutex::Autolock lock(mMutex);
|
||||||
|
|
||||||
|
if (mAbandoned) {
|
||||||
|
ST_LOGE("attachToContext: abandoned SurfaceTexture");
|
||||||
|
return NO_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAttached) {
|
||||||
|
ST_LOGE("attachToContext: SurfaceTexture is already attached to a "
|
||||||
|
"context");
|
||||||
|
return INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||||
|
EGLContext ctx = eglGetCurrentContext();
|
||||||
|
|
||||||
|
if (dpy == EGL_NO_DISPLAY) {
|
||||||
|
ST_LOGE("attachToContext: invalid current EGLDisplay");
|
||||||
|
return INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx == EGL_NO_CONTEXT) {
|
||||||
|
ST_LOGE("attachToContext: invalid current EGLContext");
|
||||||
|
return INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to bind the texture regardless of whether there's a current
|
||||||
|
// buffer.
|
||||||
|
glBindTexture(mTexTarget, tex);
|
||||||
|
|
||||||
|
if (mCurrentTextureBuf != NULL) {
|
||||||
|
// If the current buffer is no longer associated with a slot, then it
|
||||||
|
// doesn't have an EGLImage. In that case we create one now, but we also
|
||||||
|
// destroy it once we've used it to attach the buffer to the OpenGL ES
|
||||||
|
// texture.
|
||||||
|
bool imageNeedsDestroy = false;
|
||||||
|
EGLImageKHR image = EGL_NO_IMAGE_KHR;
|
||||||
|
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||||
|
image = mEGLSlots[mCurrentTexture].mEglImage;
|
||||||
|
imageNeedsDestroy = false;
|
||||||
|
} else {
|
||||||
|
image = createImage(dpy, mCurrentTextureBuf);
|
||||||
|
if (image == EGL_NO_IMAGE_KHR) {
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
imageNeedsDestroy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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("attachToContext: error binding external texture image %p "
|
||||||
|
"(slot %d): %#04x", image, mCurrentTexture, error);
|
||||||
|
err = UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageNeedsDestroy) {
|
||||||
|
eglDestroyImageKHR(dpy, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mEglDisplay = dpy;
|
||||||
|
mEglContext = ctx;
|
||||||
|
mTexName = tex;
|
||||||
|
mAttached = true;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
|
||||||
|
ST_LOGV("syncForReleaseLocked");
|
||||||
|
|
||||||
|
if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||||
|
EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence;
|
||||||
|
if (fence != EGL_NO_SYNC_KHR) {
|
||||||
|
// There is already a fence for the current slot. We need to wait
|
||||||
|
// on that before replacing it with another fence to ensure that all
|
||||||
|
// outstanding buffer accesses have completed before the producer
|
||||||
|
// accesses it.
|
||||||
|
EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
|
||||||
|
if (result == EGL_FALSE) {
|
||||||
|
ST_LOGE("syncForReleaseLocked: error waiting for previous "
|
||||||
|
"fence: %#x", eglGetError());
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
} else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
|
||||||
|
ST_LOGE("syncForReleaseLocked: timeout waiting for previous "
|
||||||
|
"fence");
|
||||||
|
return TIMED_OUT;
|
||||||
|
}
|
||||||
|
eglDestroySyncKHR(dpy, fence);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a fence for the outstanding accesses in the current OpenGL ES
|
||||||
|
// context.
|
||||||
|
fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
|
||||||
|
if (fence == EGL_NO_SYNC_KHR) {
|
||||||
|
ST_LOGE("syncForReleaseLocked: error creating fence: %#x",
|
||||||
|
eglGetError());
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
glFlush();
|
||||||
|
mEGLSlots[mCurrentTexture].mFence = fence;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
bool SurfaceTexture::isExternalFormat(uint32_t format)
|
bool SurfaceTexture::isExternalFormat(uint32_t format)
|
||||||
{
|
{
|
||||||
switch (format) {
|
switch (format) {
|
||||||
|
|
|
@ -191,100 +191,6 @@ protected:
|
||||||
return 512;
|
return 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
|
|
||||||
GLuint shader = glCreateShader(shaderType);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
if (shader) {
|
|
||||||
glShaderSource(shader, 1, &pSource, NULL);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
glCompileShader(shader);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
GLint compiled = 0;
|
|
||||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
if (!compiled) {
|
|
||||||
GLint infoLen = 0;
|
|
||||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
if (infoLen) {
|
|
||||||
char* buf = (char*) malloc(infoLen);
|
|
||||||
if (buf) {
|
|
||||||
glGetShaderInfoLog(shader, infoLen, NULL, buf);
|
|
||||||
printf("Shader compile log:\n%s\n", buf);
|
|
||||||
free(buf);
|
|
||||||
FAIL();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
char* buf = (char*) malloc(0x1000);
|
|
||||||
if (buf) {
|
|
||||||
glGetShaderInfoLog(shader, 0x1000, NULL, buf);
|
|
||||||
printf("Shader compile log:\n%s\n", buf);
|
|
||||||
free(buf);
|
|
||||||
FAIL();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glDeleteShader(shader);
|
|
||||||
shader = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ASSERT_TRUE(shader != 0);
|
|
||||||
*outShader = shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
void createProgram(const char* pVertexSource, const char* pFragmentSource,
|
|
||||||
GLuint* outPgm) {
|
|
||||||
GLuint vertexShader, fragmentShader;
|
|
||||||
{
|
|
||||||
SCOPED_TRACE("compiling vertex shader");
|
|
||||||
loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
|
|
||||||
if (HasFatalFailure()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
SCOPED_TRACE("compiling fragment shader");
|
|
||||||
loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
|
|
||||||
if (HasFatalFailure()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GLuint program = glCreateProgram();
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
if (program) {
|
|
||||||
glAttachShader(program, vertexShader);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
glAttachShader(program, fragmentShader);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
glLinkProgram(program);
|
|
||||||
GLint linkStatus = GL_FALSE;
|
|
||||||
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
|
||||||
if (linkStatus != GL_TRUE) {
|
|
||||||
GLint bufLength = 0;
|
|
||||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
|
|
||||||
if (bufLength) {
|
|
||||||
char* buf = (char*) malloc(bufLength);
|
|
||||||
if (buf) {
|
|
||||||
glGetProgramInfoLog(program, bufLength, NULL, buf);
|
|
||||||
printf("Program link log:\n%s\n", buf);
|
|
||||||
free(buf);
|
|
||||||
FAIL();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glDeleteProgram(program);
|
|
||||||
program = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glDeleteShader(vertexShader);
|
|
||||||
glDeleteShader(fragmentShader);
|
|
||||||
ASSERT_TRUE(program != 0);
|
|
||||||
*outPgm = program;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int abs(int value) {
|
|
||||||
return value > 0 ? value : -value;
|
|
||||||
}
|
|
||||||
|
|
||||||
::testing::AssertionResult checkPixel(int x, int y, int r,
|
::testing::AssertionResult checkPixel(int x, int y, int r,
|
||||||
int g, int b, int a, int tolerance=2) {
|
int g, int b, int a, int tolerance=2) {
|
||||||
GLubyte pixel[4];
|
GLubyte pixel[4];
|
||||||
|
@ -340,6 +246,98 @@ protected:
|
||||||
EGLConfig mGlConfig;
|
EGLConfig mGlConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void loadShader(GLenum shaderType, const char* pSource,
|
||||||
|
GLuint* outShader) {
|
||||||
|
GLuint shader = glCreateShader(shaderType);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
if (shader) {
|
||||||
|
glShaderSource(shader, 1, &pSource, NULL);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
glCompileShader(shader);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
GLint compiled = 0;
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
if (!compiled) {
|
||||||
|
GLint infoLen = 0;
|
||||||
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
if (infoLen) {
|
||||||
|
char* buf = (char*) malloc(infoLen);
|
||||||
|
if (buf) {
|
||||||
|
glGetShaderInfoLog(shader, infoLen, NULL, buf);
|
||||||
|
printf("Shader compile log:\n%s\n", buf);
|
||||||
|
free(buf);
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char* buf = (char*) malloc(0x1000);
|
||||||
|
if (buf) {
|
||||||
|
glGetShaderInfoLog(shader, 0x1000, NULL, buf);
|
||||||
|
printf("Shader compile log:\n%s\n", buf);
|
||||||
|
free(buf);
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glDeleteShader(shader);
|
||||||
|
shader = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(shader != 0);
|
||||||
|
*outShader = shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createProgram(const char* pVertexSource,
|
||||||
|
const char* pFragmentSource, GLuint* outPgm) {
|
||||||
|
GLuint vertexShader, fragmentShader;
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("compiling vertex shader");
|
||||||
|
ASSERT_NO_FATAL_FAILURE(loadShader(GL_VERTEX_SHADER, pVertexSource,
|
||||||
|
&vertexShader));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("compiling fragment shader");
|
||||||
|
ASSERT_NO_FATAL_FAILURE(loadShader(GL_FRAGMENT_SHADER, pFragmentSource,
|
||||||
|
&fragmentShader));
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint program = glCreateProgram();
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
if (program) {
|
||||||
|
glAttachShader(program, vertexShader);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
glAttachShader(program, fragmentShader);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
glLinkProgram(program);
|
||||||
|
GLint linkStatus = GL_FALSE;
|
||||||
|
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
||||||
|
if (linkStatus != GL_TRUE) {
|
||||||
|
GLint bufLength = 0;
|
||||||
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
|
||||||
|
if (bufLength) {
|
||||||
|
char* buf = (char*) malloc(bufLength);
|
||||||
|
if (buf) {
|
||||||
|
glGetProgramInfoLog(program, bufLength, NULL, buf);
|
||||||
|
printf("Program link log:\n%s\n", buf);
|
||||||
|
free(buf);
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glDeleteProgram(program);
|
||||||
|
program = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glDeleteShader(vertexShader);
|
||||||
|
glDeleteShader(fragmentShader);
|
||||||
|
ASSERT_TRUE(program != 0);
|
||||||
|
*outPgm = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int abs(int value) {
|
||||||
|
return value > 0 ? value : -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// XXX: Code above this point should live elsewhere
|
// XXX: Code above this point should live elsewhere
|
||||||
|
|
||||||
class SurfaceTextureGLTest : public GLTest {
|
class SurfaceTextureGLTest : public GLTest {
|
||||||
|
@ -351,43 +349,8 @@ protected:
|
||||||
mST = new SurfaceTexture(TEX_ID);
|
mST = new SurfaceTexture(TEX_ID);
|
||||||
mSTC = new SurfaceTextureClient(mST);
|
mSTC = new SurfaceTextureClient(mST);
|
||||||
mANW = mSTC;
|
mANW = mSTC;
|
||||||
|
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
|
||||||
const char vsrc[] =
|
ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp());
|
||||||
"attribute vec4 vPosition;\n"
|
|
||||||
"varying vec2 texCoords;\n"
|
|
||||||
"uniform mat4 texMatrix;\n"
|
|
||||||
"void main() {\n"
|
|
||||||
" vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
|
|
||||||
" texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n"
|
|
||||||
" gl_Position = vPosition;\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const char fsrc[] =
|
|
||||||
"#extension GL_OES_EGL_image_external : require\n"
|
|
||||||
"precision mediump float;\n"
|
|
||||||
"uniform samplerExternalOES texSampler;\n"
|
|
||||||
"varying vec2 texCoords;\n"
|
|
||||||
"void main() {\n"
|
|
||||||
" gl_FragColor = texture2D(texSampler, texCoords);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
{
|
|
||||||
SCOPED_TRACE("creating shader program");
|
|
||||||
createProgram(vsrc, fsrc, &mPgm);
|
|
||||||
if (HasFatalFailure()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mPositionHandle = glGetAttribLocation(mPgm, "vPosition");
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
ASSERT_NE(-1, mPositionHandle);
|
|
||||||
mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler");
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
ASSERT_NE(-1, mTexSamplerHandle);
|
|
||||||
mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix");
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
ASSERT_NE(-1, mTexMatrixHandle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void TearDown() {
|
virtual void TearDown() {
|
||||||
|
@ -397,51 +360,107 @@ protected:
|
||||||
GLTest::TearDown();
|
GLTest::TearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawTexture draws the SurfaceTexture over the entire GL viewport.
|
|
||||||
void drawTexture() {
|
void drawTexture() {
|
||||||
const GLfloat triangleVertices[] = {
|
mTextureRenderer->drawTexture();
|
||||||
-1.0f, 1.0f,
|
|
||||||
-1.0f, -1.0f,
|
|
||||||
1.0f, -1.0f,
|
|
||||||
1.0f, 1.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0,
|
|
||||||
triangleVertices);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
glEnableVertexAttribArray(mPositionHandle);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
|
|
||||||
glUseProgram(mPgm);
|
|
||||||
glUniform1i(mTexSamplerHandle, 0);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
|
|
||||||
// XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as
|
|
||||||
// they're setting the defautls for that target, but when hacking things
|
|
||||||
// to use GL_TEXTURE_2D they are needed to achieve the same behavior.
|
|
||||||
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
|
|
||||||
GL_LINEAR);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER,
|
|
||||||
GL_LINEAR);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
|
|
||||||
GL_CLAMP_TO_EDGE);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
|
|
||||||
GL_CLAMP_TO_EDGE);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
|
|
||||||
GLfloat texMatrix[16];
|
|
||||||
mST->getTransformMatrix(texMatrix);
|
|
||||||
glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix);
|
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
||||||
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TextureRenderer: public RefBase {
|
||||||
|
public:
|
||||||
|
TextureRenderer(GLuint texName, const sp<SurfaceTexture>& st):
|
||||||
|
mTexName(texName),
|
||||||
|
mST(st) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() {
|
||||||
|
const char vsrc[] =
|
||||||
|
"attribute vec4 vPosition;\n"
|
||||||
|
"varying vec2 texCoords;\n"
|
||||||
|
"uniform mat4 texMatrix;\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
|
||||||
|
" texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n"
|
||||||
|
" gl_Position = vPosition;\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const char fsrc[] =
|
||||||
|
"#extension GL_OES_EGL_image_external : require\n"
|
||||||
|
"precision mediump float;\n"
|
||||||
|
"uniform samplerExternalOES texSampler;\n"
|
||||||
|
"varying vec2 texCoords;\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" gl_FragColor = texture2D(texSampler, texCoords);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("creating shader program");
|
||||||
|
ASSERT_NO_FATAL_FAILURE(createProgram(vsrc, fsrc, &mPgm));
|
||||||
|
}
|
||||||
|
|
||||||
|
mPositionHandle = glGetAttribLocation(mPgm, "vPosition");
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
ASSERT_NE(-1, mPositionHandle);
|
||||||
|
mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler");
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
ASSERT_NE(-1, mTexSamplerHandle);
|
||||||
|
mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix");
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
ASSERT_NE(-1, mTexMatrixHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawTexture draws the SurfaceTexture over the entire GL viewport.
|
||||||
|
void drawTexture() {
|
||||||
|
const GLfloat triangleVertices[] = {
|
||||||
|
-1.0f, 1.0f,
|
||||||
|
-1.0f, -1.0f,
|
||||||
|
1.0f, -1.0f,
|
||||||
|
1.0f, 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0,
|
||||||
|
triangleVertices);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
glEnableVertexAttribArray(mPositionHandle);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
|
||||||
|
glUseProgram(mPgm);
|
||||||
|
glUniform1i(mTexSamplerHandle, 0);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
|
||||||
|
// XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as
|
||||||
|
// they're setting the defautls for that target, but when hacking
|
||||||
|
// things to use GL_TEXTURE_2D they are needed to achieve the same
|
||||||
|
// behavior.
|
||||||
|
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
|
||||||
|
GL_LINEAR);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER,
|
||||||
|
GL_LINEAR);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
|
||||||
|
GL_CLAMP_TO_EDGE);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
|
||||||
|
GL_CLAMP_TO_EDGE);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
|
||||||
|
GLfloat texMatrix[16];
|
||||||
|
mST->getTransformMatrix(texMatrix);
|
||||||
|
glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint mTexName;
|
||||||
|
sp<SurfaceTexture> mST;
|
||||||
|
GLuint mPgm;
|
||||||
|
GLint mPositionHandle;
|
||||||
|
GLint mTexSamplerHandle;
|
||||||
|
GLint mTexMatrixHandle;
|
||||||
|
};
|
||||||
|
|
||||||
class FrameWaiter : public SurfaceTexture::FrameAvailableListener {
|
class FrameWaiter : public SurfaceTexture::FrameAvailableListener {
|
||||||
public:
|
public:
|
||||||
FrameWaiter():
|
FrameWaiter():
|
||||||
|
@ -470,11 +489,7 @@ protected:
|
||||||
sp<SurfaceTexture> mST;
|
sp<SurfaceTexture> mST;
|
||||||
sp<SurfaceTextureClient> mSTC;
|
sp<SurfaceTextureClient> mSTC;
|
||||||
sp<ANativeWindow> mANW;
|
sp<ANativeWindow> mANW;
|
||||||
|
sp<TextureRenderer> mTextureRenderer;
|
||||||
GLuint mPgm;
|
|
||||||
GLint mPositionHandle;
|
|
||||||
GLint mTexSamplerHandle;
|
|
||||||
GLint mTexMatrixHandle;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fill a YV12 buffer with a multi-colored checkerboard pattern
|
// Fill a YV12 buffer with a multi-colored checkerboard pattern
|
||||||
|
@ -1735,6 +1750,9 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) {
|
||||||
|
|
||||||
class SurfaceTextureMultiContextGLTest : public SurfaceTextureGLTest {
|
class SurfaceTextureMultiContextGLTest : public SurfaceTextureGLTest {
|
||||||
protected:
|
protected:
|
||||||
|
enum { SECOND_TEX_ID = 123 };
|
||||||
|
enum { THIRD_TEX_ID = 456 };
|
||||||
|
|
||||||
SurfaceTextureMultiContextGLTest():
|
SurfaceTextureMultiContextGLTest():
|
||||||
mSecondEglContext(EGL_NO_CONTEXT) {
|
mSecondEglContext(EGL_NO_CONTEXT) {
|
||||||
}
|
}
|
||||||
|
@ -1742,13 +1760,39 @@ protected:
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
SurfaceTextureGLTest::SetUp();
|
SurfaceTextureGLTest::SetUp();
|
||||||
|
|
||||||
|
// Set up the secondary context and texture renderer.
|
||||||
mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig,
|
mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig,
|
||||||
EGL_NO_CONTEXT, getContextAttribs());
|
EGL_NO_CONTEXT, getContextAttribs());
|
||||||
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||||
ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext);
|
ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext);
|
||||||
|
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mSecondEglContext));
|
||||||
|
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||||
|
mSecondTextureRenderer = new TextureRenderer(SECOND_TEX_ID, mST);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(mSecondTextureRenderer->SetUp());
|
||||||
|
|
||||||
|
// Set up the tertiary context and texture renderer.
|
||||||
|
mThirdEglContext = eglCreateContext(mEglDisplay, mGlConfig,
|
||||||
|
EGL_NO_CONTEXT, getContextAttribs());
|
||||||
|
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||||
|
ASSERT_NE(EGL_NO_CONTEXT, mThirdEglContext);
|
||||||
|
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mThirdEglContext));
|
||||||
|
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||||
|
mThirdTextureRenderer = new TextureRenderer(THIRD_TEX_ID, mST);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(mThirdTextureRenderer->SetUp());
|
||||||
|
|
||||||
|
// Switch back to the primary context to start the tests.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mEglContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void TearDown() {
|
virtual void TearDown() {
|
||||||
|
if (mThirdEglContext != EGL_NO_CONTEXT) {
|
||||||
|
eglDestroyContext(mEglDisplay, mThirdEglContext);
|
||||||
|
}
|
||||||
if (mSecondEglContext != EGL_NO_CONTEXT) {
|
if (mSecondEglContext != EGL_NO_CONTEXT) {
|
||||||
eglDestroyContext(mEglDisplay, mSecondEglContext);
|
eglDestroyContext(mEglDisplay, mSecondEglContext);
|
||||||
}
|
}
|
||||||
|
@ -1756,6 +1800,10 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLContext mSecondEglContext;
|
EGLContext mSecondEglContext;
|
||||||
|
sp<TextureRenderer> mSecondTextureRenderer;
|
||||||
|
|
||||||
|
EGLContext mThirdEglContext;
|
||||||
|
sp<TextureRenderer> mThirdTextureRenderer;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) {
|
TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) {
|
||||||
|
@ -1765,13 +1813,382 @@ TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) {
|
||||||
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
// Latch the texture contents on the primary context.
|
// Latch the texture contents on the primary context.
|
||||||
mST->updateTexImage();
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
// Attempt to latch the texture on the secondary context.
|
// Attempt to latch the texture on the secondary context.
|
||||||
EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
mSecondEglContext));
|
mSecondEglContext));
|
||||||
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||||
ASSERT_EQ(-EINVAL, mST->updateTexImage());
|
ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextSucceeds) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Check that the GL texture was deleted.
|
||||||
|
EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest,
|
||||||
|
DetachFromContextSucceedsAfterProducerDisconnect) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU);
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Check that the GL texture was deleted.
|
||||||
|
EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenAbandoned) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Attempt to detach from the primary context.
|
||||||
|
mST->abandon();
|
||||||
|
ASSERT_EQ(NO_INIT, mST->detachFromContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenDetached) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attempt to detach from the primary context again.
|
||||||
|
ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoDisplay) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Make there be no current display.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
|
EGL_NO_CONTEXT));
|
||||||
|
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||||
|
|
||||||
|
// Attempt to detach from the primary context.
|
||||||
|
ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoContext) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Make current context be incorrect.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mSecondEglContext));
|
||||||
|
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||||
|
|
||||||
|
// Attempt to detach from the primary context.
|
||||||
|
ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageFailsWhenDetached) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attempt to latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceeds) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attach to the secondary context.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mSecondEglContext));
|
||||||
|
ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
|
||||||
|
// Verify that the texture object was created and bound.
|
||||||
|
GLint texBinding = -1;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding);
|
||||||
|
EXPECT_EQ(SECOND_TEX_ID, texBinding);
|
||||||
|
|
||||||
|
// Try to use the texture from the secondary context.
|
||||||
|
glClearColor(0.2, 0.2, 0.2, 0.2);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glViewport(0, 0, 1, 1);
|
||||||
|
mSecondTextureRenderer->drawTexture();
|
||||||
|
ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35));
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest,
|
||||||
|
AttachToContextSucceedsAfterProducerDisconnect) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU);
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attach to the secondary context.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mSecondEglContext));
|
||||||
|
ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
|
||||||
|
// Verify that the texture object was created and bound.
|
||||||
|
GLint texBinding = -1;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding);
|
||||||
|
EXPECT_EQ(SECOND_TEX_ID, texBinding);
|
||||||
|
|
||||||
|
// Try to use the texture from the secondary context.
|
||||||
|
glClearColor(0.2, 0.2, 0.2, 0.2);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glViewport(0, 0, 1, 1);
|
||||||
|
mSecondTextureRenderer->drawTexture();
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest,
|
||||||
|
AttachToContextSucceedsBeforeUpdateTexImage) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU);
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attach to the secondary context.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mSecondEglContext));
|
||||||
|
ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
|
||||||
|
// Verify that the texture object was created and bound.
|
||||||
|
GLint texBinding = -1;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding);
|
||||||
|
EXPECT_EQ(SECOND_TEX_ID, texBinding);
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Try to use the texture from the secondary context.
|
||||||
|
glClearColor(0.2, 0.2, 0.2, 0.2);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glViewport(0, 0, 1, 1);
|
||||||
|
mSecondTextureRenderer->drawTexture();
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAbandoned) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attempt to attach to the secondary context.
|
||||||
|
mST->abandon();
|
||||||
|
|
||||||
|
// Attempt to attach to the primary context.
|
||||||
|
ASSERT_EQ(NO_INIT, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAttached) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Attempt to attach to the primary context.
|
||||||
|
ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest,
|
||||||
|
AttachToContextFailsWhenAttachedBeforeUpdateTexImage) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Attempt to attach to the primary context.
|
||||||
|
ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWithNoDisplay) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Make there be no current display.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
|
EGL_NO_CONTEXT));
|
||||||
|
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||||
|
|
||||||
|
// Attempt to attach with no context current.
|
||||||
|
ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsTwice) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Latch the texture contents on the primary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attach to the secondary context.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mSecondEglContext));
|
||||||
|
ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
|
||||||
|
// Detach from the secondary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attach to the tertiary context.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mThirdEglContext));
|
||||||
|
ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID));
|
||||||
|
|
||||||
|
// Verify that the texture object was created and bound.
|
||||||
|
GLint texBinding = -1;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding);
|
||||||
|
EXPECT_EQ(THIRD_TEX_ID, texBinding);
|
||||||
|
|
||||||
|
// Try to use the texture from the tertiary context.
|
||||||
|
glClearColor(0.2, 0.2, 0.2, 0.2);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glViewport(0, 0, 1, 1);
|
||||||
|
mThirdTextureRenderer->drawTexture();
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SurfaceTextureMultiContextGLTest,
|
||||||
|
AttachToContextSucceedsTwiceBeforeUpdateTexImage) {
|
||||||
|
sp<FrameWaiter> fw(new FrameWaiter);
|
||||||
|
mST->setFrameAvailableListener(fw);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||||
|
|
||||||
|
// Detach from the primary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attach to the secondary context.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mSecondEglContext));
|
||||||
|
ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID));
|
||||||
|
|
||||||
|
// Detach from the secondary context.
|
||||||
|
ASSERT_EQ(OK, mST->detachFromContext());
|
||||||
|
|
||||||
|
// Attach to the tertiary context.
|
||||||
|
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||||
|
mThirdEglContext));
|
||||||
|
ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID));
|
||||||
|
|
||||||
|
// Verify that the texture object was created and bound.
|
||||||
|
GLint texBinding = -1;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding);
|
||||||
|
EXPECT_EQ(THIRD_TEX_ID, texBinding);
|
||||||
|
|
||||||
|
// Latch the texture contents on the tertiary context.
|
||||||
|
fw->waitForFrame();
|
||||||
|
ASSERT_EQ(OK, mST->updateTexImage());
|
||||||
|
|
||||||
|
// Try to use the texture from the tertiary context.
|
||||||
|
glClearColor(0.2, 0.2, 0.2, 0.2);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glViewport(0, 0, 1, 1);
|
||||||
|
mThirdTextureRenderer->drawTexture();
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
Loading…
Reference in New Issue