Home | History | Annotate | Download | only in libOpenglRender
      1 /*
      2 * Copyright (C) 2011 The Android Open Source Project
      3 *
      4 * Licensed under the Apache License, Version 2.0 (the "License");
      5 * you may not use this file except in compliance with the License.
      6 * You may obtain a copy of the License at
      7 *
      8 * http://www.apache.org/licenses/LICENSE-2.0
      9 *
     10 * Unless required by applicable law or agreed to in writing, software
     11 * distributed under the License is distributed on an "AS IS" BASIS,
     12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 * See the License for the specific language governing permissions and
     14 * limitations under the License.
     15 */
     16 #include "ColorBuffer.h"
     17 #include "FrameBuffer.h"
     18 #include "EGLDispatch.h"
     19 #include "GLDispatch.h"
     20 #include "ThreadInfo.h"
     21 #ifdef WITH_GLES2
     22 #include "GL2Dispatch.h"
     23 #endif
     24 #include <stdio.h>
     25 
     26 ColorBuffer *ColorBuffer::create(int p_width, int p_height,
     27                                  GLenum p_internalFormat)
     28 {
     29     FrameBuffer *fb = FrameBuffer::getFB();
     30 
     31     GLenum texInternalFormat = 0;
     32 
     33     switch(p_internalFormat) {
     34         case GL_RGB:
     35         case GL_RGB565_OES:
     36             texInternalFormat = GL_RGB;
     37             break;
     38 
     39         case GL_RGBA:
     40         case GL_RGB5_A1_OES:
     41         case GL_RGBA4_OES:
     42             texInternalFormat = GL_RGBA;
     43             break;
     44 
     45         default:
     46             return NULL;
     47             break;
     48     }
     49 
     50     if (!fb->bind_locked()) {
     51         return NULL;
     52     }
     53 
     54     ColorBuffer *cb = new ColorBuffer();
     55 
     56 
     57     s_gl.glGenTextures(1, &cb->m_tex);
     58     s_gl.glBindTexture(GL_TEXTURE_2D, cb->m_tex);
     59     int nComp = (texInternalFormat == GL_RGB ? 3 : 4);
     60     char *zBuff = new char[nComp*p_width*p_height];
     61     if (zBuff) {
     62         memset(zBuff, 0, nComp*p_width*p_height);
     63     }
     64     s_gl.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat,
     65                       p_width, p_height, 0,
     66                       texInternalFormat,
     67                       GL_UNSIGNED_BYTE, zBuff);
     68     delete [] zBuff;
     69     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     70     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     71     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     72     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     73     s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
     74 
     75     //
     76     // create another texture for that colorbuffer for blit
     77     //
     78     s_gl.glGenTextures(1, &cb->m_blitTex);
     79     s_gl.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
     80     s_gl.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat,
     81                       p_width, p_height, 0,
     82                       texInternalFormat,
     83                       GL_UNSIGNED_BYTE, NULL);
     84     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     85     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     86     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     87     s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     88     s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
     89 
     90     cb->m_width = p_width;
     91     cb->m_height = p_height;
     92     cb->m_internalFormat = texInternalFormat;
     93 
     94     if (fb->getCaps().has_eglimage_texture_2d) {
     95         cb->m_eglImage = s_egl.eglCreateImageKHR(fb->getDisplay(),
     96                                                  s_egl.eglGetCurrentContext(),
     97                                                  EGL_GL_TEXTURE_2D_KHR,
     98                                                  (EGLClientBuffer)cb->m_tex,
     99                                                  NULL);
    100 
    101         cb->m_blitEGLImage = s_egl.eglCreateImageKHR(fb->getDisplay(),
    102                                                  s_egl.eglGetCurrentContext(),
    103                                                  EGL_GL_TEXTURE_2D_KHR,
    104                                                  (EGLClientBuffer)cb->m_blitTex,
    105                                                  NULL);
    106     }
    107 
    108     fb->unbind_locked();
    109     return cb;
    110 }
    111 
    112 ColorBuffer::ColorBuffer() :
    113     m_tex(0),
    114     m_blitTex(0),
    115     m_eglImage(NULL),
    116     m_blitEGLImage(NULL),
    117     m_fbo(0),
    118     m_internalFormat(0),
    119     m_warYInvertBug(false)
    120 {
    121 #if __APPLE__
    122     // On Macs running OS X 10.6 and 10.7 with Intel HD Graphics 3000 or 4000,
    123     // some screens or parts of the screen are displayed upside down. The exact
    124     // conditions/sequence that triggers this aren't known yet; I haven't been
    125     // able to reproduce it in a standalone test. This way of enabling the
    126     // workaround will break if it is a driver bug (rather than a bug in this
    127     // code which works by accident elsewhere) and Apple/Intel release a fix
    128     // for it. Running a standalone test to detect the problem at runtime would
    129     // be more robust.
    130     const char* renderer = (const char*)s_gl.glGetString(GL_RENDERER);
    131     if (strstr(renderer, "Intel HD Graphics 3000") ||
    132         strstr(renderer, "Intel HD Graphics 4000")) {
    133         m_warYInvertBug = true;
    134     }
    135 #endif
    136 }
    137 
    138 ColorBuffer::~ColorBuffer()
    139 {
    140     FrameBuffer *fb = FrameBuffer::getFB();
    141     fb->bind_locked();
    142 
    143     if (m_blitEGLImage) {
    144         s_egl.eglDestroyImageKHR(fb->getDisplay(), m_blitEGLImage);
    145     }
    146     if (m_eglImage) {
    147         s_egl.eglDestroyImageKHR(fb->getDisplay(), m_eglImage);
    148     }
    149 
    150     if (m_fbo) {
    151         s_gl.glDeleteFramebuffersOES(1, &m_fbo);
    152     }
    153 
    154     GLuint tex[2] = {m_tex, m_blitTex};
    155     s_gl.glDeleteTextures(2, tex);
    156 
    157     fb->unbind_locked();
    158 }
    159 
    160 void ColorBuffer::subUpdate(int x, int y, int width, int height, GLenum p_format, GLenum p_type, void *pixels)
    161 {
    162     FrameBuffer *fb = FrameBuffer::getFB();
    163     if (!fb->bind_locked()) return;
    164     s_gl.glBindTexture(GL_TEXTURE_2D, m_tex);
    165     s_gl.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    166     s_gl.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y,
    167                          width, height, p_format, p_type, pixels);
    168     fb->unbind_locked();
    169 }
    170 
    171 bool ColorBuffer::blitFromCurrentReadBuffer()
    172 {
    173     RenderThreadInfo *tInfo = RenderThreadInfo::get();
    174     if (!tInfo->currContext.Ptr()) {
    175         // no Current context
    176         return false;
    177     }
    178 
    179     //
    180     // Create a temporary texture inside the current context
    181     // from the blit_texture EGLImage and copy the pixels
    182     // from the current read buffer to that texture
    183     //
    184     GLuint tmpTex;
    185     GLint currTexBind;
    186     if (tInfo->currContext->isGL2()) {
    187         s_gl2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
    188         s_gl2.glGenTextures(1,&tmpTex);
    189         s_gl2.glBindTexture(GL_TEXTURE_2D, tmpTex);
    190         s_gl2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
    191         s_gl2.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
    192                                   m_width, m_height);
    193     }
    194     else {
    195         s_gl.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
    196         s_gl.glGenTextures(1,&tmpTex);
    197         s_gl.glBindTexture(GL_TEXTURE_2D, tmpTex);
    198         s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
    199         s_gl.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
    200                                  m_width, m_height);
    201     }
    202 
    203 
    204     //
    205     // Now bind the frame buffer context and blit from
    206     // m_blitTex into m_tex
    207     //
    208     FrameBuffer *fb = FrameBuffer::getFB();
    209     if (fb->bind_locked()) {
    210 
    211         //
    212         // bind FBO object which has this colorbuffer as render target
    213         //
    214         if (bind_fbo()) {
    215 
    216             //
    217             // save current viewport and match it to the current
    218             // colorbuffer size
    219             //
    220             GLint vport[4];
    221             s_gl.glGetIntegerv(GL_VIEWPORT, vport);
    222             s_gl.glViewport(0, 0, m_width, m_height);
    223 
    224             // render m_blitTex
    225             s_gl.glBindTexture(GL_TEXTURE_2D, m_blitTex);
    226             s_gl.glEnable(GL_TEXTURE_2D);
    227             s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    228             drawTexQuad(!m_warYInvertBug);
    229 
    230             // unbind the fbo
    231             s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
    232 
    233             // restrore previous viewport
    234             s_gl.glViewport(vport[0], vport[1], vport[2], vport[3]);
    235         }
    236 
    237         // unbind from the FrameBuffer context
    238         fb->unbind_locked();
    239     }
    240 
    241     //
    242     // delete the temporary texture and restore the texture binding
    243     // inside the current context
    244     //
    245     if (tInfo->currContext->isGL2()) {
    246         s_gl2.glDeleteTextures(1, &tmpTex);
    247         s_gl2.glBindTexture(GL_TEXTURE_2D, currTexBind);
    248     }
    249     else {
    250         s_gl.glDeleteTextures(1, &tmpTex);
    251         s_gl.glBindTexture(GL_TEXTURE_2D, currTexBind);
    252     }
    253 
    254     return true;
    255 }
    256 
    257 bool ColorBuffer::bindToTexture()
    258 {
    259     if (m_eglImage) {
    260         RenderThreadInfo *tInfo = RenderThreadInfo::get();
    261         if (tInfo->currContext.Ptr()) {
    262 #ifdef WITH_GLES2
    263             if (tInfo->currContext->isGL2()) {
    264                 s_gl2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
    265             }
    266             else {
    267                 s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
    268             }
    269 #else
    270             s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
    271 #endif
    272             return true;
    273         }
    274     }
    275     return false;
    276 }
    277 
    278 bool ColorBuffer::bindToRenderbuffer()
    279 {
    280     if (m_eglImage) {
    281         RenderThreadInfo *tInfo = RenderThreadInfo::get();
    282         if (tInfo->currContext.Ptr()) {
    283 #ifdef WITH_GLES2
    284             if (tInfo->currContext->isGL2()) {
    285                 s_gl2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
    286             }
    287             else {
    288                 s_gl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
    289             }
    290 #else
    291             s_gl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
    292 #endif
    293             return true;
    294         }
    295     }
    296     return false;
    297 }
    298 
    299 bool ColorBuffer::bind_fbo()
    300 {
    301     if (m_fbo) {
    302         // fbo already exist - just bind
    303         s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_fbo);
    304         return true;
    305     }
    306 
    307     s_gl.glGenFramebuffersOES(1, &m_fbo);
    308     s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_fbo);
    309     s_gl.glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
    310                                    GL_COLOR_ATTACHMENT0_OES,
    311                                    GL_TEXTURE_2D, m_tex, 0);
    312     GLenum status = s_gl.glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
    313     if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
    314         s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
    315         s_gl.glDeleteFramebuffersOES(1, &m_fbo);
    316         m_fbo = 0;
    317         return false;
    318     }
    319 
    320     return true;
    321 }
    322 
    323 bool ColorBuffer::post()
    324 {
    325     s_gl.glBindTexture(GL_TEXTURE_2D, m_tex);
    326     s_gl.glEnable(GL_TEXTURE_2D);
    327     s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    328     drawTexQuad(true);
    329 
    330     return true;
    331 }
    332 
    333 void ColorBuffer::drawTexQuad(bool flipy)
    334 {
    335     GLfloat verts[] = { -1.0f, -1.0f, 0.0f,
    336                          -1.0f, +1.0f, 0.0f,
    337                          +1.0f, -1.0f, 0.0f,
    338                          +1.0f, +1.0f, 0.0f };
    339 
    340     GLfloat tcoords[] = { 0.0f, 1.0f,
    341                            0.0f, 0.0f,
    342                            1.0f, 1.0f,
    343                            1.0f, 0.0f };
    344 
    345     if (!flipy) {
    346         for (int i = 0; i < 4; i++) {
    347             // swap 0.0/1.0 in second element of each tcoord vector
    348             tcoords[2*i + 1] = tcoords[2*i + 1] == 0.0f ? 1.0f : 0.0f;
    349         }
    350     }
    351 
    352     s_gl.glClientActiveTexture(GL_TEXTURE0);
    353     s_gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    354     s_gl.glTexCoordPointer(2, GL_FLOAT, 0, tcoords);
    355 
    356     s_gl.glEnableClientState(GL_VERTEX_ARRAY);
    357     s_gl.glVertexPointer(3, GL_FLOAT, 0, verts);
    358     s_gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    359 }
    360 
    361 void ColorBuffer::readback(unsigned char* img)
    362 {
    363     FrameBuffer *fb = FrameBuffer::getFB();
    364     if (fb->bind_locked()) {
    365         if (bind_fbo()) {
    366             s_gl.glReadPixels(0, 0, m_width, m_height,
    367                     GL_RGBA, GL_UNSIGNED_BYTE, img);
    368         }
    369         fb->unbind_locked();
    370     }
    371 }
    372