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 "WindowSurface.h"
     17 #include "FBConfig.h"
     18 #include "FrameBuffer.h"
     19 #include <GLES/glext.h>
     20 #include "EGLDispatch.h"
     21 #include "GLDispatch.h"
     22 #include "GL2Dispatch.h"
     23 #include <stdio.h>
     24 #include <string.h>
     25 #include "GLErrorLog.h"
     26 
     27 WindowSurface::WindowSurface() :
     28     m_fbObj(0),
     29     m_depthRB(0),
     30     m_stencilRB(0),
     31     m_eglSurface(NULL),
     32     m_attachedColorBuffer(NULL),
     33     m_readContext(NULL),
     34     m_drawContext(NULL),
     35     m_width(0),
     36     m_height(0),
     37     m_pbufWidth(0),
     38     m_pbufHeight(0)
     39 {
     40 }
     41 
     42 WindowSurface::~WindowSurface()
     43 {
     44     s_egl.eglDestroySurface(FrameBuffer::getFB()->getDisplay(), m_eglSurface);
     45 }
     46 
     47 WindowSurface *WindowSurface::create(int p_config, int p_width, int p_height)
     48 {
     49     const FBConfig *fbconf = FBConfig::get(p_config);
     50     if (!fbconf) {
     51         return NULL;
     52     }
     53 
     54     // allocate space for the WindowSurface object
     55     WindowSurface *win = new WindowSurface();
     56     if (!win) {
     57         return NULL;
     58     }
     59     win->m_fbconf = fbconf;
     60 
     61     FrameBuffer *fb = FrameBuffer::getFB();
     62     const FrameBufferCaps &caps = fb->getCaps();
     63 
     64     //
     65     // Create a pbuffer to be used as the egl surface
     66     // for that window.
     67     //
     68     if (!win->resizePbuffer(p_width, p_height)) {
     69         delete win;
     70         return NULL;
     71     }
     72 
     73     win->m_width = p_width;
     74     win->m_height = p_height;
     75 
     76     return win;
     77 }
     78 
     79 //
     80 // flushColorBuffer - The function makes sure that the
     81 //    previous attached color buffer is updated, if copy or blit should be done
     82 //    in order to update it - it is being done here.
     83 //
     84 void WindowSurface::flushColorBuffer()
     85 {
     86     if (m_attachedColorBuffer.Ptr() != NULL) {
     87 
     88         //copyToColorBuffer();
     89         blitToColorBuffer();
     90     }
     91 }
     92 
     93 //
     94 // setColorBuffer - this function is called when a new color buffer needs to
     95 //    be attached to the surface. The function doesn't make sure that the
     96 //    previous attached color buffer is updated, this is done by flushColorBuffer
     97 //
     98 void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer)
     99 {
    100     m_attachedColorBuffer = p_colorBuffer;
    101 
    102     //
    103     // resize the window if the attached color buffer is of different
    104     // size
    105     //
    106     unsigned int cbWidth = m_attachedColorBuffer->getWidth();
    107     unsigned int cbHeight = m_attachedColorBuffer->getHeight();
    108 
    109     if (cbWidth != m_width || cbHeight != m_height) {
    110 
    111         if (m_pbufWidth && m_pbufHeight) {
    112             // if we use pbuffer, need to resize it
    113             resizePbuffer(cbWidth, cbHeight);
    114         }
    115 
    116         m_width = cbWidth;
    117         m_height = cbHeight;
    118     }
    119 }
    120 
    121 //
    122 // This function is called after the context and eglSurface is already
    123 // bound in the current thread (eglMakeCurrent has been called).
    124 // This function should take actions required on the other surface objects
    125 // when being bind/unbound
    126 //
    127 void WindowSurface::bind(RenderContextPtr p_ctx, SurfaceBindType p_bindType)
    128 {
    129     if (p_bindType == SURFACE_BIND_READ) {
    130         m_readContext = p_ctx;
    131     }
    132     else if (p_bindType == SURFACE_BIND_DRAW) {
    133         m_drawContext = p_ctx;
    134     }
    135     else if (p_bindType == SURFACE_BIND_READDRAW) {
    136         m_readContext = p_ctx;
    137         m_drawContext = p_ctx;
    138     }
    139     else {
    140         return;  // bad param
    141     }
    142 
    143 }
    144 
    145 void WindowSurface::copyToColorBuffer()
    146 {
    147     if (!m_width && !m_height) return;
    148 
    149     if (m_attachedColorBuffer->getWidth() != m_width ||
    150         m_attachedColorBuffer->getHeight() != m_height) {
    151         // XXX: should never happen - how this needs to be handled?
    152         return;
    153     }
    154 
    155     void *data = m_xferBuffer.alloc(m_width * m_height * 4);
    156     if (!data) {
    157         fprintf(stderr,"WARNING: Failed to copy buffer data - OutOfMemory\n");
    158         return;
    159     }
    160 
    161     //
    162     // Make the surface current
    163     //
    164     EGLContext prevContext = s_egl.eglGetCurrentContext();
    165     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
    166     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
    167     FrameBuffer *fb = FrameBuffer::getFB();
    168     if (!s_egl.eglMakeCurrent(fb->getDisplay(), m_eglSurface,
    169                               m_eglSurface, m_drawContext->getEGLContext())) {
    170         return;
    171     }
    172 
    173     if (m_drawContext->isGL2()) {
    174 #ifdef WITH_GLES2
    175         s_gl2.glPixelStorei(GL_PACK_ALIGNMENT, 1);
    176         s_gl2.glReadPixels(0, 0, m_width, m_height,
    177                           GL_RGBA, GL_UNSIGNED_BYTE, data);
    178 #else
    179         return; // should never happen, context cannot be GL2 in this case.
    180 #endif
    181     }
    182     else {
    183         s_gl.glPixelStorei(GL_PACK_ALIGNMENT, 1);
    184         s_gl.glReadPixels(0, 0, m_width, m_height,
    185                           GL_RGBA, GL_UNSIGNED_BYTE, data);
    186     }
    187 
    188 //
    189 // XXX: for some reason flipping the image is not required on
    190 //      Mac. Need to find the reason, currently unkbown.
    191 //
    192 #ifndef __APPLE__
    193 #define FLIP_BUFFER 1
    194 #endif
    195 
    196 #if FLIP_BUFFER
    197     //We need to flip the pixels
    198     int bpp = 4;
    199     void *tmpBuf = m_xUpdateBuf.alloc(m_width * m_height * bpp);
    200 
    201     int dst_line_len = m_width * bpp;
    202     int src_line_len = m_width * bpp;
    203     char *src = (char *)data;
    204     char *dst = (char*)tmpBuf + (m_height-1)*dst_line_len;
    205     for (uint32_t  y=0; y<m_height; y++) {
    206         memcpy(dst, src, dst_line_len);
    207         src += src_line_len;
    208         dst -= dst_line_len;
    209     }
    210     // update the attached color buffer with the fliped readback pixels
    211     m_attachedColorBuffer->update(GL_RGBA, GL_UNSIGNED_BYTE, tmpBuf);
    212 #else
    213     // update the attached color buffer with the readback pixels
    214     m_attachedColorBuffer->update(GL_RGBA, GL_UNSIGNED_BYTE, data);
    215 #endif
    216 
    217     // restore current context/surface
    218     s_egl.eglMakeCurrent(fb->getDisplay(), prevDrawSurf,
    219                          prevReadSurf, prevContext);
    220 
    221 }
    222 
    223 void WindowSurface::blitToColorBuffer()
    224 {
    225     if (!m_width && !m_height) return;
    226 
    227     if (m_attachedColorBuffer->getWidth() != m_width ||
    228         m_attachedColorBuffer->getHeight() != m_height) {
    229         // XXX: should never happen - how this needs to be handled?
    230         return;
    231     }
    232 
    233     //
    234     // Make the surface current
    235     //
    236     EGLContext prevContext = s_egl.eglGetCurrentContext();
    237     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
    238     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
    239     FrameBuffer *fb = FrameBuffer::getFB();
    240     if (!s_egl.eglMakeCurrent(fb->getDisplay(), m_eglSurface,
    241                               m_eglSurface, m_drawContext->getEGLContext())) {
    242         return;
    243     }
    244 
    245     m_attachedColorBuffer->blitFromCurrentReadBuffer();
    246 
    247     // restore current context/surface
    248     s_egl.eglMakeCurrent(fb->getDisplay(), prevDrawSurf,
    249                          prevReadSurf, prevContext);
    250 
    251 }
    252 
    253 bool WindowSurface::resizePbuffer(unsigned int p_width, unsigned int p_height)
    254 {
    255     if (m_eglSurface &&
    256         m_pbufWidth == p_width &&
    257         m_pbufHeight == p_height) {
    258         // no need to resize
    259         return true;
    260     }
    261 
    262     FrameBuffer *fb = FrameBuffer::getFB();
    263 
    264     EGLContext prevContext = s_egl.eglGetCurrentContext();
    265     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
    266     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
    267     EGLSurface prevPbuf = m_eglSurface;
    268     bool needRebindContext = m_eglSurface &&
    269                              (prevReadSurf == m_eglSurface ||
    270                               prevDrawSurf == m_eglSurface);
    271 
    272     if (needRebindContext) {
    273         s_egl.eglMakeCurrent(fb->getDisplay(), EGL_NO_SURFACE,
    274                               EGL_NO_SURFACE, EGL_NO_CONTEXT);
    275     }
    276 
    277     //
    278     // Destroy previous surface
    279     //
    280     if (m_eglSurface) {
    281         s_egl.eglDestroySurface(fb->getDisplay(), m_eglSurface);
    282         m_eglSurface = NULL;
    283     }
    284 
    285     const FrameBufferCaps &caps = fb->getCaps();
    286 
    287     //
    288     // Create pbuffer surface.
    289     //
    290     EGLint pbufAttribs[5];
    291     pbufAttribs[0] = EGL_WIDTH;
    292     pbufAttribs[1] = p_width;
    293     pbufAttribs[2] = EGL_HEIGHT;
    294     pbufAttribs[3] = p_height;
    295     pbufAttribs[4] = EGL_NONE;
    296 
    297     m_eglSurface = s_egl.eglCreatePbufferSurface(fb->getDisplay(),
    298                                                  m_fbconf->getEGLConfig(),
    299                                                  pbufAttribs);
    300     if (m_eglSurface == EGL_NO_SURFACE) {
    301         fprintf(stderr, "Renderer error: failed to create/resize pbuffer!!\n");
    302         return false;
    303     }
    304 
    305     m_pbufWidth = p_width;
    306     m_pbufHeight = p_height;
    307 
    308     if (needRebindContext) {
    309         s_egl.eglMakeCurrent(fb->getDisplay(),
    310                      (prevDrawSurf==prevPbuf) ? m_eglSurface : prevDrawSurf,
    311                      (prevReadSurf==prevPbuf) ? m_eglSurface : prevReadSurf,
    312                      prevContext);
    313     }
    314 
    315     return true;
    316 }
    317