opengles emulator: replace the readback at end of frame with blit

That replaces the readback to host we do at the end of each frame
with two blits on the GPU, one to copy the pixels into a texture
and another to render the texture to another in order to flip
the image.

Change-Id: I7e0e10493d38944d0b613e245023f34236d3dfc4
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.cpp
index b0a1ba2..c39dac3 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.cpp
@@ -72,8 +72,24 @@
     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 
+    //
+    // create another texture for that colorbuffer for blit
+    //
+    s_gl.glGenTextures(1, &cb->m_blitTex);
+    s_gl.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
+    s_gl.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat,
+                      p_width, p_height, 0,
+                      texInternalFormat,
+                      GL_UNSIGNED_BYTE, NULL);
+    s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
     cb->m_width = p_width;
     cb->m_height = p_height;
+    cb->m_internalFormat = texInternalFormat;
 
     if (fb->getCaps().has_eglimage_texture_2d) {
         cb->m_eglImage = s_egl.eglCreateImageKHR(fb->getDisplay(),
@@ -81,6 +97,12 @@
                                                  EGL_GL_TEXTURE_2D_KHR,
                                                  (EGLClientBuffer)cb->m_tex,
                                                  NULL);
+
+        cb->m_blitEGLImage = s_egl.eglCreateImageKHR(fb->getDisplay(),
+                                                 fb->getContext(),
+                                                 EGL_GL_TEXTURE_2D_KHR,
+                                                 (EGLClientBuffer)cb->m_blitTex,
+                                                 NULL);
     }
 
     fb->unbind_locked();
@@ -90,7 +112,8 @@
 ColorBuffer::ColorBuffer() :
     m_tex(0),
     m_eglImage(NULL),
-    m_fbo(0)
+    m_fbo(0),
+    m_internalFormat(0)
 {
 }
 
@@ -176,6 +199,90 @@
     return true;
 }
 
+bool ColorBuffer::blitFromCurrentReadBuffer()
+{
+    RenderThreadInfo *tInfo = getRenderThreadInfo();
+    if (!tInfo->currContext.Ptr()) {
+        // no Current context
+        return false;
+    }
+
+    //
+    // Create a temporary texture inside the current context
+    // from the blit_texture EGLImage and copy the pixels
+    // from the current read buffer to that texture
+    //
+    GLuint tmpTex;
+    GLint currTexBind;
+    if (tInfo->currContext->isGL2()) {
+        s_gl2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
+        s_gl2.glGenTextures(1,&tmpTex);
+        s_gl2.glBindTexture(GL_TEXTURE_2D, tmpTex);
+        s_gl2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
+        s_gl2.glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat,
+                               0, 0, m_width, m_height, 0);
+    }
+    else {
+        s_gl.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
+        s_gl.glGenTextures(1,&tmpTex);
+        s_gl.glBindTexture(GL_TEXTURE_2D, tmpTex);
+        s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
+        s_gl.glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat,
+                              0, 0, m_width, m_height, 0);
+    }
+
+
+    //
+    // Now bind the frame buffer context and blit from
+    // m_blitTex into m_tex
+    //
+    FrameBuffer *fb = FrameBuffer::getFB();
+    if (fb->bind_locked()) {
+
+        //
+        // bind FBO object which has this colorbuffer as render target
+        //
+        if (bind_fbo()) {
+
+            //
+            // save current viewport and match it to the current
+            // colorbuffer size
+            //
+            GLint vport[4];
+            s_gl.glGetIntegerv(GL_VIEWPORT, vport);
+            s_gl.glViewport(0, 0, m_width, m_height);
+
+            // render m_blitTex
+            s_gl.glBindTexture(GL_TEXTURE_2D, m_blitTex);
+            drawTexQuad();  // this will render the texture flipped
+
+            // unbind the fbo
+            s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+
+            // restrore previous viewport
+            s_gl.glViewport(vport[0], vport[1], vport[2], vport[3]);
+        }
+
+        // unbind from the FrameBuffer context
+        fb->unbind_locked();
+    }
+
+    //
+    // delete the temporary texture and restore the texture binding
+    // inside the current context
+    //
+    if (tInfo->currContext->isGL2()) {
+        s_gl2.glDeleteTextures(1, &tmpTex);
+        s_gl2.glBindTexture(GL_TEXTURE_2D, currTexBind);
+    }
+    else {
+        s_gl.glDeleteTextures(1, &tmpTex);
+        s_gl.glBindTexture(GL_TEXTURE_2D, currTexBind);
+    }
+
+    return true;
+}
+
 bool ColorBuffer::bindToTexture()
 {
     if (m_eglImage) {
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.h b/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.h
index 4bc495a..5e01dcc 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.h
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/ColorBuffer.h
@@ -38,6 +38,7 @@
     bool post();
     bool bindToTexture();
     bool bindToRenderbuffer();
+    bool blitFromCurrentReadBuffer();
 
 private:
     ColorBuffer();
@@ -46,10 +47,13 @@
 
 private:
     GLuint m_tex;
+    GLuint m_blitTex;
     EGLImageKHR m_eglImage;
+    EGLImageKHR m_blitEGLImage;
     GLuint m_width;
     GLuint m_height;
     GLuint m_fbo;
+    GLenum m_internalFormat;
 };
 
 typedef SmartPtr<ColorBuffer> ColorBufferPtr;
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.cpp b/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.cpp
index 36fb704..14f38be 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.cpp
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.cpp
@@ -115,7 +115,8 @@
             }
 
             if (!copied) {
-                copyToColorBuffer();
+                //copyToColorBuffer();
+                blitToColorBuffer();
             }
         }
         else {
@@ -257,6 +258,36 @@
 
 }
 
+void WindowSurface::blitToColorBuffer()
+{
+    if (!m_width && !m_height) return;
+
+    if (m_attachedColorBuffer->getWidth() != m_width ||
+        m_attachedColorBuffer->getHeight() != m_height) {
+        // XXX: should never happen - how this needs to be handled?
+        return;
+    }
+
+    //
+    // Make the surface current
+    //
+    EGLContext prevContext = s_egl.eglGetCurrentContext();
+    EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
+    EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
+    FrameBuffer *fb = FrameBuffer::getFB();
+    if (!s_egl.eglMakeCurrent(fb->getDisplay(), m_eglSurface,
+                              m_eglSurface, m_drawContext->getEGLContext())) {
+        return;
+    }
+
+    m_attachedColorBuffer->blitFromCurrentReadBuffer();
+
+    // restore current context/surface
+    s_egl.eglMakeCurrent(fb->getDisplay(), prevDrawSurf,
+                         prevReadSurf, prevContext);
+
+}
+
 bool WindowSurface::resizePbuffer(unsigned int p_width, unsigned int p_height)
 {
     if (m_eglSurface && 
diff --git a/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.h b/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.h
index de9bc7b..6c08954 100644
--- a/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.h
+++ b/tools/emulator/opengl/host/libs/libOpenglRender/WindowSurface.h
@@ -45,6 +45,7 @@
     WindowSurface();
 
     void copyToColorBuffer();  // copy pbuffer content with readback+download
+    void blitToColorBuffer();  // copy pbuffer content with texload and blit
     bool resizePbuffer(unsigned int p_width, unsigned int p_height);
 
 private: