Moving to Frame Buffer Objects.

Change-Id: I742a1e66214247a3049c1327e2dcce6799d41acf
diff --git a/suite/pts/deviceTests/opengl/jni/GLNative.cpp b/suite/pts/deviceTests/opengl/jni/GLNative.cpp
index 0192889..28e8265 100644
--- a/suite/pts/deviceTests/opengl/jni/GLNative.cpp
+++ b/suite/pts/deviceTests/opengl/jni/GLNative.cpp
@@ -44,12 +44,16 @@
     // Sets up the renderer.
     bool success = gRenderer->setUp();
 
+    // Draw to the screen. This allows debugging and also warms up the HW.
+    success = success && gRenderer->draw(false);
+
     // Records the start time.
     double start = currentTimeMillis();
 
+    // Draw off the screen.
     for (int i = 0; i < numFrames && success; i++) {
         // Draw a frame.
-        success = gRenderer->draw();
+        success = gRenderer->draw(true);
     }
 
     // Records the end time.
diff --git a/suite/pts/deviceTests/opengl/jni/GLUtils.cpp b/suite/pts/deviceTests/opengl/jni/GLUtils.cpp
index ea8279c..f62f5b8 100644
--- a/suite/pts/deviceTests/opengl/jni/GLUtils.cpp
+++ b/suite/pts/deviceTests/opengl/jni/GLUtils.cpp
@@ -69,7 +69,7 @@
 }
 
 // Rounds a number up to the smallest power of 2 that is greater than the original number.
-static inline int roundUpToSmallestPowerOf2(int x) {
+int GLUtils::roundUpToSmallestPowerOf2(int x) {
     if (x < 0) {
         return 0;
     }
@@ -82,8 +82,8 @@
     return x + 1;
 }
 
-int GLUtils::genRandTex(int texWidth, int texHeight) {
-    GLuint textureId = -1;
+GLuint GLUtils::genRandTex(int texWidth, int texHeight) {
+    GLuint textureId = 0;
     int w = roundUpToSmallestPowerOf2(texWidth);
     int h = roundUpToSmallestPowerOf2(texHeight);
     uint32_t* m = new uint32_t[w * h];
diff --git a/suite/pts/deviceTests/opengl/jni/GLUtils.h b/suite/pts/deviceTests/opengl/jni/GLUtils.h
index f874710..a0525bc 100644
--- a/suite/pts/deviceTests/opengl/jni/GLUtils.h
+++ b/suite/pts/deviceTests/opengl/jni/GLUtils.h
@@ -23,8 +23,10 @@
     // Creates a program with the given vertex and fragment shader source code.
     static GLuint createProgram(const char** vertexSource,
             const char** fragmentSource);
+    // Rounds a number up to the smallest power of 2 that is greater than the original number.
+    static int roundUpToSmallestPowerOf2(int x);
     // Generates a random texture of the given dimensions.
-    static int genRandTex(int texWidth, int texHeight);
+    static GLuint genRandTex(int texWidth, int texHeight);
 };
 
 #endif
diff --git a/suite/pts/deviceTests/opengl/jni/Renderer.cpp b/suite/pts/deviceTests/opengl/jni/Renderer.cpp
index 6d2b105..70445dc 100644
--- a/suite/pts/deviceTests/opengl/jni/Renderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/Renderer.cpp
@@ -12,6 +12,11 @@
  * the License.
  */
 #include "Renderer.h"
+#include <GLUtils.h>
+
+#define LOG_TAG "PTS_OPENGL"
+#define LOG_NDEBUG 0
+#include "utils/Log.h"
 
 static const EGLint contextAttribs[] = {
         EGL_CONTEXT_CLIENT_VERSION, 2,
@@ -29,8 +34,7 @@
         EGL_NONE };
 
 Renderer::Renderer(ANativeWindow* window, int workload) :
-        mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(
-                EGL_NO_CONTEXT) {
+        mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {
     mWindow = window;
     mWorkload = workload;
 }
@@ -43,8 +47,7 @@
 
     EGLint major;
     EGLint minor;
-    if (!eglInitialize(mEglDisplay, &major, &minor)
-            || EGL_SUCCESS != eglGetError()) {
+    if (!eglInitialize(mEglDisplay, &major, &minor) || EGL_SUCCESS != eglGetError()) {
         return false;
     }
 
@@ -59,8 +62,7 @@
         return false;
     }
 
-    mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
-            contextAttribs);
+    mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, contextAttribs);
     if (EGL_NO_CONTEXT == mEglContext || EGL_SUCCESS != eglGetError()) {
         return false;
     }
@@ -80,20 +82,71 @@
     }
 
     glViewport(0, 0, width, height);
-    return GLenum(GL_NO_ERROR) == glGetError();
+
+    int w = GLUtils::roundUpToSmallestPowerOf2(width);
+    int h = GLUtils::roundUpToSmallestPowerOf2(height);
+    if (!createFBO(mFboId, mRboId, mCboId, w, h)) {
+        return false;
+    }
+
+    GLuint err = glGetError();
+    if (err != GL_NO_ERROR) {
+        ALOGV("GLError %d", err);
+        return false;
+    }
+    return true;
+}
+
+bool Renderer::createFBO(GLuint& fboId, GLuint& rboId, GLuint& cboId, int width, int height) {
+    glGenFramebuffers(1, &fboId);
+    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
+
+    glGenRenderbuffers(1, &rboId);
+    glBindRenderbuffer(GL_RENDERBUFFER, rboId);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
+    glBindRenderbuffer(GL_RENDERBUFFER, 0);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId);
+
+    glGenRenderbuffers(1, &cboId);
+    glBindRenderbuffer(GL_RENDERBUFFER, cboId);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB565, width, height);
+    glBindRenderbuffer(GL_RENDERBUFFER, 0);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cboId);
+
+    GLuint err = glGetError();
+    if (err != GL_NO_ERROR) {
+        ALOGV("GLError %d", err);
+        return false;
+    }
+
+    return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
 }
 
 bool Renderer::tearDown() {
+    if (mFboId != 0) {
+        glDeleteFramebuffers(1, &mFboId);
+        mFboId = 0;
+    }
+    if (mRboId != 0) {
+        glDeleteRenderbuffers(1, &mRboId);
+        mRboId = 0;
+    }
+    if (mCboId != 0) {
+        glDeleteRenderbuffers(1, &mCboId);
+        mCboId = 0;
+    }
     if (mEglContext != EGL_NO_CONTEXT) {
         eglDestroyContext(mEglDisplay, mEglContext);
+        mEglContext = EGL_NO_CONTEXT;
     }
     if (mEglSurface != EGL_NO_SURFACE) {
         eglDestroySurface(mEglDisplay, mEglSurface);
+        mEglSurface = EGL_NO_SURFACE;
     }
     if (mEglDisplay != EGL_NO_DISPLAY) {
-        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
-                EGL_NO_CONTEXT);
+        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
         eglTerminate(mEglDisplay);
+        mEglDisplay = EGL_NO_DISPLAY;
     }
     return EGL_SUCCESS == eglGetError();
 }
diff --git a/suite/pts/deviceTests/opengl/jni/Renderer.h b/suite/pts/deviceTests/opengl/jni/Renderer.h
index 56303c1..10e179a 100644
--- a/suite/pts/deviceTests/opengl/jni/Renderer.h
+++ b/suite/pts/deviceTests/opengl/jni/Renderer.h
@@ -25,14 +25,18 @@
     Renderer(ANativeWindow* window, int workload);
     virtual bool setUp();
     virtual bool tearDown();
-    virtual bool draw() = 0;
+    virtual bool draw(bool offscreen) = 0;
     virtual ~Renderer() {};
 protected:
+    bool createFBO(GLuint& fboId, GLuint& rboId, GLuint& cboId, int width, int height);
     ANativeWindow* mWindow;
     EGLDisplay mEglDisplay;
     EGLSurface mEglSurface;
     EGLContext mEglContext;
     EGLConfig mGlConfig;
+    GLuint mFboId; //Frame buffer
+    GLuint mRboId; //Depth buffer
+    GLuint mCboId; //Color buffer
     GLuint mProgram;
     EGLint width;
     EGLint height;
diff --git a/suite/pts/deviceTests/opengl/jni/contextswitch/ContextSwitchRenderer.cpp b/suite/pts/deviceTests/opengl/jni/contextswitch/ContextSwitchRenderer.cpp
index d64ca3f..f69a9f5 100644
--- a/suite/pts/deviceTests/opengl/jni/contextswitch/ContextSwitchRenderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/contextswitch/ContextSwitchRenderer.cpp
@@ -23,6 +23,10 @@
 #include "ContextSwitchRenderer.h"
 #include <GLUtils.h>
 
+#define LOG_TAG "PTS_OPENGL"
+#define LOG_NDEBUG 0
+#include "utils/Log.h"
+
 static const EGLint contextAttribs[] =
         { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
 
@@ -61,8 +65,7 @@
         "  gl_FragColor = texture2D(u_Texture, v_TexCoord);"
         "}";
 
-ContextSwitchRenderer::ContextSwitchRenderer(ANativeWindow* window,
-        int workload) :
+ContextSwitchRenderer::ContextSwitchRenderer(ANativeWindow* window, int workload) :
         Renderer(window, workload), mContexts(NULL) {
 }
 
@@ -71,15 +74,24 @@
         return false;
     }
 
-    // We dont need to context created by Renderer.
+    // We don't need the context created by Renderer.
     eglDestroyContext(mEglDisplay, mEglContext);
     mEglContext = EGL_NO_CONTEXT;
 
-    mTextureIds = new GLuint[mWorkload];
+    int w = GLUtils::roundUpToSmallestPowerOf2(width);
+    int h = GLUtils::roundUpToSmallestPowerOf2(height);
+
     mContexts = new EGLContext[mWorkload];
+    mTextureIds = new GLuint[mWorkload];
+    mFboIds = new GLuint[mWorkload];
+    mRboIds = new GLuint[mWorkload];
+    mCboIds = new GLuint[mWorkload];
+    mPrograms = new GLuint[mWorkload];
+    mTextureUniformHandles = new GLuint[mWorkload];
+    mPositionHandles = new GLuint[mWorkload];
+    mTexCoordHandles = new GLuint[mWorkload];
     for (int i = 0; i < mWorkload; i++) {
-        mContexts[i] = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
-                contextAttribs);
+        mContexts[i] = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, contextAttribs);
         if (EGL_NO_CONTEXT == mContexts[i] || EGL_SUCCESS != eglGetError()) {
             return false;
         }
@@ -89,23 +101,33 @@
             return false;
         }
 
-        // Setup textures.
-        int texId = GLUtils::genRandTex(width, height);
-        if (texId < 0) {
+        // Setup FBOs.
+        if (!Renderer::createFBO(mFboIds[i], mRboIds[i], mCboIds[i], w, h)) {
             return false;
-        } else {
-            mTextureIds[i] = texId;
         }
+
+        // Setup textures.
+        mTextureIds[i] = GLUtils::genRandTex(width, height);
+        if (mTextureIds[i] == 0) {
+            return false;
+        }
+
+        // Create program.
+        mPrograms[i] = GLUtils::createProgram(&CS_VERTEX, &CS_FRAGMENT);
+        if (mPrograms[i] == 0) {
+            return false;
+        }
+        // Bind attributes.
+        mTextureUniformHandles[i] = glGetUniformLocation(mPrograms[i], "u_Texture");
+        mPositionHandles[i] = glGetAttribLocation(mPrograms[i], "a_Position");
+        mTexCoordHandles[i] = glGetAttribLocation(mPrograms[i], "a_TexCoord");
     }
 
-    // Create program.
-    mProgram = GLUtils::createProgram(&CS_VERTEX, &CS_FRAGMENT);
-    if (mProgram == 0)
+    GLuint err = glGetError();
+    if (err != GL_NO_ERROR) {
+        ALOGV("GLError %d", err);
         return false;
-    // Bind attributes.
-    mTextureUniformHandle = glGetUniformLocation(mProgram, "u_Texture");
-    mPositionHandle = glGetAttribLocation(mProgram, "a_Position");
-    mTexCoordHandle = glGetAttribLocation(mProgram, "a_TexCoord");
+    }
 
     return true;
 }
@@ -117,7 +139,20 @@
         }
         delete[] mContexts;
     }
+    if (mFboIds) {
+        glDeleteFramebuffers(mWorkload, mFboIds);
+        delete[] mFboIds;
+    }
+    if (mRboIds) {
+        glDeleteRenderbuffers(mWorkload, mRboIds);
+        delete[] mRboIds;
+    }
+    if (mCboIds) {
+        glDeleteRenderbuffers(mWorkload, mCboIds);
+        delete[] mCboIds;
+    }
     if (mTextureIds) {
+        glDeleteTextures(mWorkload, mTextureIds);
         delete[] mTextureIds;
     }
     if (!Renderer::tearDown()) {
@@ -126,30 +161,43 @@
     return true;
 }
 
-bool ContextSwitchRenderer::draw() {
+bool ContextSwitchRenderer::draw(bool offscreen) {
     for (int i = 0; i < mWorkload; i++) {
         if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mContexts[i])
                 || EGL_SUCCESS != eglGetError()) {
             return false;
         }
-        glUseProgram (mProgram);
+        glBindFramebuffer(GL_FRAMEBUFFER, (offscreen) ? mFboIds[i] : 0);
+        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+            return false;
+        }
+
+        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+        glUseProgram(mPrograms[i]);
         glActiveTexture (GL_TEXTURE0);
         // Bind the texture to this unit.
         glBindTexture(GL_TEXTURE_2D, mTextureIds[i]);
 
         // Tell the texture uniform sampler to use this texture in the shader by binding to texture
         // unit 0.
-        glUniform1i(mTextureUniformHandle, 0);
+        glUniform1i(mTextureUniformHandles[i], 0);
 
-        glEnableVertexAttribArray(mPositionHandle);
-        glEnableVertexAttribArray(mTexCoordHandle);
-        glVertexAttribPointer(mPositionHandle, 3, GL_FLOAT, false, 0,
-                CS_VERTICES);
-        glVertexAttribPointer(mTexCoordHandle, 2, GL_FLOAT, false, 0,
-                CS_TEX_COORDS);
+        glEnableVertexAttribArray(mPositionHandles[i]);
+        glEnableVertexAttribArray(mTexCoordHandles[i]);
+        glVertexAttribPointer(mPositionHandles[i], 3, GL_FLOAT, false, 0, CS_VERTICES);
+        glVertexAttribPointer(mTexCoordHandles[i], 2, GL_FLOAT, false, 0, CS_TEX_COORDS);
 
         glDrawArrays(GL_TRIANGLES, 0, CS_NUM_VERTICES);
+        glFinish();
     }
-    return eglSwapBuffers(mEglDisplay, mEglSurface);
+
+    GLuint err = glGetError();
+    if (err != GL_NO_ERROR) {
+        ALOGV("GLError %d", err);
+        return false;
+    }
+
+    return (offscreen) ? true : eglSwapBuffers(mEglDisplay, mEglSurface);
 }
diff --git a/suite/pts/deviceTests/opengl/jni/contextswitch/ContextSwitchRenderer.h b/suite/pts/deviceTests/opengl/jni/contextswitch/ContextSwitchRenderer.h
index a393d30..c2ef8e1 100644
--- a/suite/pts/deviceTests/opengl/jni/contextswitch/ContextSwitchRenderer.h
+++ b/suite/pts/deviceTests/opengl/jni/contextswitch/ContextSwitchRenderer.h
@@ -22,13 +22,20 @@
     virtual ~ContextSwitchRenderer() {};
     bool setUp();
     bool tearDown();
-    bool draw();
+    bool draw(bool offscreen);
 private:
     GLuint mTextureUniformHandle;
     GLuint mPositionHandle;
     GLuint mTexCoordHandle;
     EGLContext* mContexts;
     GLuint* mTextureIds;
+    GLuint* mFboIds;
+    GLuint* mRboIds;
+    GLuint* mCboIds;
+    GLuint* mPrograms;
+    GLuint* mTextureUniformHandles;
+    GLuint* mPositionHandles;
+    GLuint* mTexCoordHandles;
 };
 
 #endif
diff --git a/suite/pts/deviceTests/opengl/jni/fullpipeline/FullPipelineRenderer.cpp b/suite/pts/deviceTests/opengl/jni/fullpipeline/FullPipelineRenderer.cpp
index f85a173..35389d2 100644
--- a/suite/pts/deviceTests/opengl/jni/fullpipeline/FullPipelineRenderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/fullpipeline/FullPipelineRenderer.cpp
@@ -23,6 +23,10 @@
 #include <graphics/TransformationNode.h>
 #include <GLUtils.h>
 
+#define LOG_TAG "PTS_OPENGL"
+#define LOG_NDEBUG 0
+#include "utils/Log.h"
+
 static const int FP_NUM_VERTICES = 6;
 
 static const float FP_VERTICES[FP_NUM_VERTICES * 3] = {
@@ -92,25 +96,20 @@
         "}";
 
 FullPipelineRenderer::FullPipelineRenderer(ANativeWindow* window, int workload) :
-        Renderer(window, workload), mProgram(NULL), mSceneGraph(NULL), mModelMatrix(
-                NULL), mViewMatrix(NULL), mProjectionMatrix(NULL), mMesh(NULL) {
+        Renderer(window, workload), mProgram(NULL), mSceneGraph(NULL), mModelMatrix(NULL),
+        mViewMatrix(NULL), mProjectionMatrix(NULL), mMesh(NULL) {
 }
 
 bool FullPipelineRenderer::setUp() {
     if (!Renderer::setUp()) {
         return false;
     }
+
     GLuint programId = GLUtils::createProgram(&FP_VERTEX, &FP_FRAGMENT);
     if (programId == 0)
         return false;
     mProgram = new FullPipelineProgram(programId);
 
-    // Set the background clear color to black.
-    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
-    // Use culling to remove back faces.
-    glEnable (GL_CULL_FACE);
-
     mModelMatrix = new Matrix();
 
     // Position the eye in front of the origin.
@@ -129,8 +128,7 @@
     float upZ = 0.0f;
 
     // Set the view matrix. This matrix can be said to represent the camera position.
-    mViewMatrix = Matrix::newLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ,
-            upX, upY, upZ);
+    mViewMatrix = Matrix::newLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
 
     // Create a new perspective projection matrix. The height will stay the same
     // while the width will vary as per aspect ratio.
@@ -144,16 +142,17 @@
 
     mProjectionMatrix = Matrix::newFrustum(left, right, bottom, top, near, far);
 
-    int textureId = GLUtils::genRandTex(width, height);
-    if (textureId < 0) {
+    // Setup texture.
+    mTextureId = GLUtils::genRandTex(width, height);
+    if (mTextureId == 0) {
         return false;
     }
 
-    float count = pow(2, mWorkload-1);
+    float count = pow(2, mWorkload - 1);
     float middle = count / 2.0f;
     float scale = 1.0f / count;
 
-    mMesh = new Mesh(FP_VERTICES, FP_NORMALS, FP_TEX_COORDS, FP_NUM_VERTICES, textureId);
+    mMesh = new Mesh(FP_VERTICES, FP_NORMALS, FP_TEX_COORDS, FP_NUM_VERTICES, mTextureId);
     mSceneGraph = new ProgramNode();
 
     for (int i = 0; i < count; i++) {
@@ -170,6 +169,10 @@
 }
 
 bool FullPipelineRenderer::tearDown() {
+    if (mTextureId != 0) {
+        glDeleteTextures(1, &mTextureId);
+        mTextureId = 0;
+    }
     if (!Renderer::tearDown()) {
         return false;
     }
@@ -188,9 +191,28 @@
     return true;
 }
 
-bool FullPipelineRenderer::draw() {
+bool FullPipelineRenderer::draw(bool offscreen) {
+    glBindFramebuffer(GL_FRAMEBUFFER, (offscreen) ? mFboId : 0);
+    // Set the background clear color to black.
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    // Use culling to remove back faces.
+    glEnable (GL_CULL_FACE);
+    // Use depth testing.
+    glEnable (GL_DEPTH_TEST);
     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
     mModelMatrix->identity();
     mSceneGraph->draw(*mProgram, *mModelMatrix, *mViewMatrix, *mProjectionMatrix);
-    return eglSwapBuffers(mEglDisplay, mEglSurface);
+
+    GLuint err = glGetError();
+    if (err != GL_NO_ERROR) {
+        ALOGV("GLError %d", err);
+        return false;
+    }
+
+    if (offscreen) {
+        glFinish();
+        return true;
+    } else {
+        return eglSwapBuffers(mEglDisplay, mEglSurface);
+    }
 }
diff --git a/suite/pts/deviceTests/opengl/jni/fullpipeline/FullPipelineRenderer.h b/suite/pts/deviceTests/opengl/jni/fullpipeline/FullPipelineRenderer.h
index 52d4f1a..61a732262 100644
--- a/suite/pts/deviceTests/opengl/jni/fullpipeline/FullPipelineRenderer.h
+++ b/suite/pts/deviceTests/opengl/jni/fullpipeline/FullPipelineRenderer.h
@@ -27,7 +27,7 @@
     virtual ~FullPipelineRenderer() {};
     bool setUp();
     bool tearDown();
-    bool draw();
+    bool draw(bool offscreen);
 private:
     FullPipelineProgram* mProgram;
     ProgramNode* mSceneGraph;
@@ -35,5 +35,6 @@
     Matrix* mViewMatrix;
     Matrix* mProjectionMatrix;
     Mesh* mMesh;
+    GLuint mTextureId;
 };
 #endif
diff --git a/suite/pts/deviceTests/opengl/jni/pixeloutput/PixelOutputRenderer.cpp b/suite/pts/deviceTests/opengl/jni/pixeloutput/PixelOutputRenderer.cpp
index c97b860..cfddafa 100644
--- a/suite/pts/deviceTests/opengl/jni/pixeloutput/PixelOutputRenderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/pixeloutput/PixelOutputRenderer.cpp
@@ -14,6 +14,10 @@
 #include "PixelOutputRenderer.h"
 #include <GLUtils.h>
 
+#define LOG_TAG "PTS_OPENGL"
+#define LOG_NDEBUG 0
+#include "utils/Log.h"
+
 static const int PO_NUM_VERTICES = 6;
 
 static const float PO_VERTICES[PO_NUM_VERTICES * 3] = {
@@ -67,27 +71,39 @@
     mTexCoordHandle = glGetAttribLocation(mProgram, "a_TexCoord");
 
     // Setup texture.
-    int texId = GLUtils::genRandTex(width, height);
-    if (texId < 0) {
+    mTextureId = GLUtils::genRandTex(width, height);
+    if (mTextureId == 0) {
         return false;
-    } else {
-        mTextureId = texId;
     }
     return true;
 }
 
-bool PixelOutputRenderer::draw() {
+bool PixelOutputRenderer::tearDown() {
+    if (mTextureId != 0) {
+        glDeleteTextures(1, &mTextureId);
+        mTextureId = 0;
+    }
+    if (!Renderer::tearDown()) {
+        return false;
+    }
+    return true;
+}
+
+bool PixelOutputRenderer::draw(bool offscreen) {
+    glBindFramebuffer(GL_FRAMEBUFFER, (offscreen) ? mFboId : 0);
     glUseProgram (mProgram);
+    // Set the background clear color to black.
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
 
     // No culling of back faces
-    glDisable (GL_CULL_FACE);
+    glDisable(GL_CULL_FACE);
 
     // No depth testing
-    glDisable (GL_DEPTH_TEST);
+    glDisable(GL_DEPTH_TEST);
 
     // Enable blending
-    glEnable (GL_BLEND);
+    glEnable(GL_BLEND);
     glBlendFunc(GL_ONE, GL_ONE);
 
     glActiveTexture (GL_TEXTURE0);
@@ -106,5 +122,16 @@
         glDrawArrays(GL_TRIANGLES, 0, PO_NUM_VERTICES);
     }
 
-    return eglSwapBuffers(mEglDisplay, mEglSurface);
+    GLuint err = glGetError();
+    if (err != GL_NO_ERROR) {
+        ALOGV("GLError %d", err);
+        return false;
+    }
+
+    if (offscreen) {
+        glFinish();
+        return true;
+    } else {
+        return eglSwapBuffers(mEglDisplay, mEglSurface);
+    }
 }
diff --git a/suite/pts/deviceTests/opengl/jni/pixeloutput/PixelOutputRenderer.h b/suite/pts/deviceTests/opengl/jni/pixeloutput/PixelOutputRenderer.h
index bac7560..ed631e2 100644
--- a/suite/pts/deviceTests/opengl/jni/pixeloutput/PixelOutputRenderer.h
+++ b/suite/pts/deviceTests/opengl/jni/pixeloutput/PixelOutputRenderer.h
@@ -21,7 +21,8 @@
     PixelOutputRenderer(ANativeWindow* window, int workload);
     virtual ~PixelOutputRenderer() {};
     bool setUp();
-    bool draw();
+    bool tearDown();
+    bool draw(bool offscreen);
 private:
     GLuint mTextureId;
     GLuint mTextureUniformHandle;
diff --git a/suite/pts/deviceTests/opengl/jni/shaderperf/ShaderPerfRenderer.cpp b/suite/pts/deviceTests/opengl/jni/shaderperf/ShaderPerfRenderer.cpp
index 11ca0a4..bbb5f68 100644
--- a/suite/pts/deviceTests/opengl/jni/shaderperf/ShaderPerfRenderer.cpp
+++ b/suite/pts/deviceTests/opengl/jni/shaderperf/ShaderPerfRenderer.cpp
@@ -14,6 +14,10 @@
 #include "ShaderPerfRenderer.h"
 #include <GLUtils.h>
 
+#define LOG_TAG "PTS_OPENGL"
+#define LOG_NDEBUG 0
+#include "utils/Log.h"
+
 static const int SP_NUM_VERTICES = 6;
 
 static const float SP_VERTICES[SP_NUM_VERTICES * 3] = {
@@ -52,24 +56,34 @@
         return false;
     // Bind attributes.
     mPositionHandle = glGetAttribLocation(mProgram, "a_Position");
-
     return true;
 }
 
-bool ShaderPerfRenderer::draw() {
-    glUseProgram (mProgram);
+bool ShaderPerfRenderer::draw(bool offscreen) {
+    glBindFramebuffer(GL_FRAMEBUFFER, (offscreen) ? mFboId : 0);
+    glUseProgram(mProgram);
+    // Set the background clear color to black.
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
 
     // No culling of back faces
-    glDisable (GL_CULL_FACE);
-
-    // No depth testing
-    glDisable (GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
 
     glEnableVertexAttribArray(mPositionHandle);
     glVertexAttribPointer(mPositionHandle, 3, GL_FLOAT, false, 0, SP_VERTICES);
 
     glDrawArrays(GL_TRIANGLES, 0, SP_NUM_VERTICES);
 
-    return eglSwapBuffers(mEglDisplay, mEglSurface);
+    GLuint err = glGetError();
+    if (err != GL_NO_ERROR) {
+        ALOGV("GLError %d", err);
+        return false;
+    }
+
+    if (offscreen) {
+        glFinish();
+        return true;
+    } else {
+        return eglSwapBuffers(mEglDisplay, mEglSurface);
+    }
 }
diff --git a/suite/pts/deviceTests/opengl/jni/shaderperf/ShaderPerfRenderer.h b/suite/pts/deviceTests/opengl/jni/shaderperf/ShaderPerfRenderer.h
index 034b8fe..2e5cb98 100644
--- a/suite/pts/deviceTests/opengl/jni/shaderperf/ShaderPerfRenderer.h
+++ b/suite/pts/deviceTests/opengl/jni/shaderperf/ShaderPerfRenderer.h
@@ -21,7 +21,7 @@
     ShaderPerfRenderer(ANativeWindow* window, int workload);
     virtual ~ShaderPerfRenderer() {};
     bool setUp();
-    bool draw();
+    bool draw(bool offscreen);
 private:
     GLuint mPositionHandle;
 };