SurfaceTexture: add context attach & detach
This change adds the detachFromContext and attachToContext methods to SurfaceTexture. These methods allow the SurfaceTexture to switch from one consumer GLES context to another. This change also includes a few cleanups to the error return codes in updateTexImage. Change-Id: I0df1eb599aa7b6f58f07431f242f8f09269559ed
This commit is contained in:
parent
1bb69f015c
commit
74bed55fff
|
@ -55,8 +55,7 @@ public:
|
|||
};
|
||||
|
||||
// SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
|
||||
// name of the OpenGL ES texture to which images are to be streamed. This
|
||||
// texture name cannot be changed once the SurfaceTexture is created.
|
||||
// name of the OpenGL ES texture to which images are to be streamed.
|
||||
// allowSynchronousMode specifies whether or not synchronous mode can be
|
||||
// enabled. texTarget specifies the OpenGL ES texture target to which the
|
||||
// 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
|
||||
// if behavior for queue/dequeue/connect etc needs to be customized.
|
||||
// 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,
|
||||
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
|
||||
const sp<BufferQueue> &bufferQueue = 0);
|
||||
|
@ -175,8 +189,37 @@ public:
|
|||
virtual status_t connect(int api,
|
||||
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
|
||||
|
||||
// getBufferQueue returns the BufferQueue object to which this
|
||||
// SurfaceTexture is connected.
|
||||
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
|
||||
virtual void dump(String8& result) 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.
|
||||
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
|
||||
// possible that this buffer is not associated with any buffer slot, so we
|
||||
// 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
|
||||
// be bound when updateTexImage is called. It is set at construction time
|
||||
// changed with a call to setTexName.
|
||||
const GLuint mTexName;
|
||||
// and can be changed with a call to attachToContext.
|
||||
GLuint mTexName;
|
||||
|
||||
// mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
// mEglContext is the OpenGL ES context with which this SurfaceTexture is
|
||||
// currently associated. It is initialized to EGL_NO_CONTEXT and gets set
|
||||
// to the current GL context when updateTexImage is called for the first
|
||||
// time.
|
||||
// time and when attachToContext is called.
|
||||
EGLContext mEglContext;
|
||||
|
||||
// mEGLSlots stores the buffers that have been allocated by the BufferQueue
|
||||
|
@ -323,6 +373,14 @@ private:
|
|||
// if none is supplied
|
||||
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
|
||||
// variables of SurfaceTexture objects. It must be locked whenever the
|
||||
// member variables are accessed.
|
||||
|
|
|
@ -118,7 +118,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
|||
mEglDisplay(EGL_NO_DISPLAY),
|
||||
mEglContext(EGL_NO_CONTEXT),
|
||||
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.
|
||||
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
||||
|
@ -176,21 +177,29 @@ status_t SurfaceTexture::updateTexImage() {
|
|||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
if (mAbandoned) {
|
||||
ST_LOGE("calling updateTexImage() on an abandoned SurfaceTexture");
|
||||
ST_LOGE("updateTexImage: SurfaceTexture is abandoned!");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (!mAttached) {
|
||||
ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL "
|
||||
"ES context");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||
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");
|
||||
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");
|
||||
return -EINVAL;
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
mEglDisplay = dpy;
|
||||
|
@ -216,7 +225,7 @@ status_t SurfaceTexture::updateTexImage() {
|
|||
EGLImageKHR image = mEGLSlots[buf].mEglImage;
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
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;
|
||||
}
|
||||
image = createImage(dpy, item.mGraphicBuffer);
|
||||
|
@ -224,7 +233,7 @@ status_t SurfaceTexture::updateTexImage() {
|
|||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
// NOTE: if dpy was invalid, createImage() is guaranteed to
|
||||
// fail. so we'd end up here.
|
||||
return -EINVAL;
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,31 +245,23 @@ status_t SurfaceTexture::updateTexImage() {
|
|||
glBindTexture(mTexTarget, mTexName);
|
||||
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
|
||||
|
||||
bool failed = false;
|
||||
status_t err = OK;
|
||||
while ((error = glGetError()) != GL_NO_ERROR) {
|
||||
ST_LOGE("error binding external texture image %p (slot %d): %#04x",
|
||||
image, buf, error);
|
||||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
|
||||
return -EINVAL;
|
||||
ST_LOGE("updateTexImage: error binding external texture image %p "
|
||||
"(slot %d): %#04x", image, buf, error);
|
||||
err = UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||
if (mUseFenceSync) {
|
||||
EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR,
|
||||
NULL);
|
||||
if (fence == EGL_NO_SYNC_KHR) {
|
||||
ALOGE("updateTexImage: error creating fence: %#x",
|
||||
eglGetError());
|
||||
mBufferQueue->releaseBuffer(buf, dpy,
|
||||
mEGLSlots[buf].mFence);
|
||||
return -EINVAL;
|
||||
}
|
||||
glFlush();
|
||||
mEGLSlots[mCurrentTexture].mFence = fence;
|
||||
}
|
||||
if (err == OK) {
|
||||
err = syncForReleaseLocked(dpy);
|
||||
}
|
||||
|
||||
if (err != OK) {
|
||||
// Release the buffer we just acquired. It's not safe to
|
||||
// release the old buffer, so instead we just drop the new frame.
|
||||
mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
|
||||
mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR;
|
||||
return err;
|
||||
}
|
||||
|
||||
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
||||
|
@ -268,9 +269,12 @@ status_t SurfaceTexture::updateTexImage() {
|
|||
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
|
||||
buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
|
||||
|
||||
// release old buffer
|
||||
mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
|
||||
mEGLSlots[mCurrentTexture].mFence);
|
||||
// Release the old buffer
|
||||
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||
mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
|
||||
mEGLSlots[mCurrentTexture].mFence);
|
||||
mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
|
||||
}
|
||||
|
||||
// Update the SurfaceTexture state.
|
||||
mCurrentTexture = buf;
|
||||
|
@ -280,10 +284,6 @@ status_t SurfaceTexture::updateTexImage() {
|
|||
mCurrentScalingMode = item.mScalingMode;
|
||||
mCurrentTimestamp = item.mTimestamp;
|
||||
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 {
|
||||
// We always bind the texture even if we don't update its contents.
|
||||
glBindTexture(mTexTarget, mTexName);
|
||||
|
@ -292,6 +292,168 @@ status_t SurfaceTexture::updateTexImage() {
|
|||
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)
|
||||
{
|
||||
switch (format) {
|
||||
|
|
|
@ -191,100 +191,6 @@ protected:
|
|||
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,
|
||||
int g, int b, int a, int tolerance=2) {
|
||||
GLubyte pixel[4];
|
||||
|
@ -340,6 +246,98 @@ protected:
|
|||
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
|
||||
|
||||
class SurfaceTextureGLTest : public GLTest {
|
||||
|
@ -351,43 +349,8 @@ protected:
|
|||
mST = new SurfaceTexture(TEX_ID);
|
||||
mSTC = new SurfaceTextureClient(mST);
|
||||
mANW = mSTC;
|
||||
|
||||
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");
|
||||
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);
|
||||
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
|
||||
ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp());
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
|
@ -397,51 +360,107 @@ protected:
|
|||
GLTest::TearDown();
|
||||
}
|
||||
|
||||
// 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, 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());
|
||||
mTextureRenderer->drawTexture();
|
||||
}
|
||||
|
||||
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 {
|
||||
public:
|
||||
FrameWaiter():
|
||||
|
@ -470,11 +489,7 @@ protected:
|
|||
sp<SurfaceTexture> mST;
|
||||
sp<SurfaceTextureClient> mSTC;
|
||||
sp<ANativeWindow> mANW;
|
||||
|
||||
GLuint mPgm;
|
||||
GLint mPositionHandle;
|
||||
GLint mTexSamplerHandle;
|
||||
GLint mTexMatrixHandle;
|
||||
sp<TextureRenderer> mTextureRenderer;
|
||||
};
|
||||
|
||||
// Fill a YV12 buffer with a multi-colored checkerboard pattern
|
||||
|
@ -1735,6 +1750,9 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) {
|
|||
|
||||
class SurfaceTextureMultiContextGLTest : public SurfaceTextureGLTest {
|
||||
protected:
|
||||
enum { SECOND_TEX_ID = 123 };
|
||||
enum { THIRD_TEX_ID = 456 };
|
||||
|
||||
SurfaceTextureMultiContextGLTest():
|
||||
mSecondEglContext(EGL_NO_CONTEXT) {
|
||||
}
|
||||
|
@ -1742,13 +1760,39 @@ protected:
|
|||
virtual void SetUp() {
|
||||
SurfaceTextureGLTest::SetUp();
|
||||
|
||||
// Set up the secondary context and texture renderer.
|
||||
mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig,
|
||||
EGL_NO_CONTEXT, getContextAttribs());
|
||||
ASSERT_EQ(EGL_SUCCESS, eglGetError());
|
||||
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() {
|
||||
if (mThirdEglContext != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(mEglDisplay, mThirdEglContext);
|
||||
}
|
||||
if (mSecondEglContext != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(mEglDisplay, mSecondEglContext);
|
||||
}
|
||||
|
@ -1756,6 +1800,10 @@ protected:
|
|||
}
|
||||
|
||||
EGLContext mSecondEglContext;
|
||||
sp<TextureRenderer> mSecondTextureRenderer;
|
||||
|
||||
EGLContext mThirdEglContext;
|
||||
sp<TextureRenderer> mThirdTextureRenderer;
|
||||
};
|
||||
|
||||
TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) {
|
||||
|
@ -1765,13 +1813,382 @@ TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) {
|
|||
ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
|
||||
|
||||
// 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.
|
||||
EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||
ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
|
||||
mSecondEglContext));
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue