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     //
     62     // Create a pbuffer to be used as the egl surface
     63     // for that window.
     64     //
     65     if (!win->resizePbuffer(p_width, p_height)) {
     66         delete win;
     67         return NULL;
     68     }
     69 
     70     win->m_width = p_width;
     71     win->m_height = p_height;
     72 
     73     return win;
     74 }
     75 
     76 //
     77 // flushColorBuffer - The function makes sure that the
     78 //    previous attached color buffer is updated, if copy or blit should be done
     79 //    in order to update it - it is being done here.
     80 //
     81 bool WindowSurface::flushColorBuffer()
     82 {
     83     if (m_attachedColorBuffer.Ptr() != NULL) {
     84         return blitToColorBuffer();
     85     }
     86     return true;
     87 }
     88 
     89 //
     90 // setColorBuffer - this function is called when a new color buffer needs to
     91 //    be attached to the surface. The function doesn't make sure that the
     92 //    previous attached color buffer is updated, this is done by flushColorBuffer
     93 //
     94 void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer)
     95 {
     96     m_attachedColorBuffer = p_colorBuffer;
     97 
     98     //
     99     // resize the window if the attached color buffer is of different
    100     // size
    101     //
    102     unsigned int cbWidth = m_attachedColorBuffer->getWidth();
    103     unsigned int cbHeight = m_attachedColorBuffer->getHeight();
    104 
    105     if (cbWidth != m_width || cbHeight != m_height) {
    106 
    107         if (m_pbufWidth && m_pbufHeight) {
    108             // if we use pbuffer, need to resize it
    109             resizePbuffer(cbWidth, cbHeight);
    110         }
    111 
    112         m_width = cbWidth;
    113         m_height = cbHeight;
    114     }
    115 }
    116 
    117 //
    118 // This function is called after the context and eglSurface is already
    119 // bound in the current thread (eglMakeCurrent has been called).
    120 // This function should take actions required on the other surface objects
    121 // when being bind/unbound
    122 //
    123 void WindowSurface::bind(RenderContextPtr p_ctx, SurfaceBindType p_bindType)
    124 {
    125     if (p_bindType == SURFACE_BIND_READ) {
    126         m_readContext = p_ctx;
    127     }
    128     else if (p_bindType == SURFACE_BIND_DRAW) {
    129         m_drawContext = p_ctx;
    130     }
    131     else if (p_bindType == SURFACE_BIND_READDRAW) {
    132         m_readContext = p_ctx;
    133         m_drawContext = p_ctx;
    134     }
    135     else {
    136         return;  // bad param
    137     }
    138 
    139 }
    140 
    141 bool WindowSurface::blitToColorBuffer()
    142 {
    143     if (!m_width && !m_height) return false;
    144 
    145     if (m_attachedColorBuffer->getWidth() != m_width ||
    146         m_attachedColorBuffer->getHeight() != m_height) {
    147         // XXX: should never happen - how this needs to be handled?
    148         fprintf(stderr, "Dimensions do not match\n");
    149         return false;
    150     }
    151 
    152     //
    153     // Make the surface current
    154     //
    155     EGLContext prevContext = s_egl.eglGetCurrentContext();
    156     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
    157     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
    158     FrameBuffer *fb = FrameBuffer::getFB();
    159     if (!m_drawContext.Ptr()) {
    160         fprintf(stderr, "Draw context is NULL\n");
    161         return false;
    162     }
    163     if (!s_egl.eglMakeCurrent(fb->getDisplay(), m_eglSurface,
    164                               m_eglSurface, m_drawContext->getEGLContext())) {
    165         fprintf(stderr, "Error making draw context current\n");
    166         return false;
    167     }
    168 
    169     m_attachedColorBuffer->blitFromCurrentReadBuffer();
    170 
    171     // restore current context/surface
    172     s_egl.eglMakeCurrent(fb->getDisplay(), prevDrawSurf,
    173                          prevReadSurf, prevContext);
    174     return true;
    175 }
    176 
    177 bool WindowSurface::resizePbuffer(unsigned int p_width, unsigned int p_height)
    178 {
    179     if (m_eglSurface &&
    180         m_pbufWidth == p_width &&
    181         m_pbufHeight == p_height) {
    182         // no need to resize
    183         return true;
    184     }
    185 
    186     FrameBuffer *fb = FrameBuffer::getFB();
    187 
    188     EGLContext prevContext = s_egl.eglGetCurrentContext();
    189     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
    190     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
    191     EGLSurface prevPbuf = m_eglSurface;
    192     bool needRebindContext = m_eglSurface &&
    193                              (prevReadSurf == m_eglSurface ||
    194                               prevDrawSurf == m_eglSurface);
    195 
    196     if (needRebindContext) {
    197         s_egl.eglMakeCurrent(fb->getDisplay(), EGL_NO_SURFACE,
    198                               EGL_NO_SURFACE, EGL_NO_CONTEXT);
    199     }
    200 
    201     //
    202     // Destroy previous surface
    203     //
    204     if (m_eglSurface) {
    205         s_egl.eglDestroySurface(fb->getDisplay(), m_eglSurface);
    206         m_eglSurface = NULL;
    207     }
    208 
    209     //
    210     // Create pbuffer surface.
    211     //
    212     EGLint pbufAttribs[5];
    213     pbufAttribs[0] = EGL_WIDTH;
    214     pbufAttribs[1] = p_width;
    215     pbufAttribs[2] = EGL_HEIGHT;
    216     pbufAttribs[3] = p_height;
    217     pbufAttribs[4] = EGL_NONE;
    218 
    219     m_eglSurface = s_egl.eglCreatePbufferSurface(fb->getDisplay(),
    220                                                  m_fbconf->getEGLConfig(),
    221                                                  pbufAttribs);
    222     if (m_eglSurface == EGL_NO_SURFACE) {
    223         fprintf(stderr, "Renderer error: failed to create/resize pbuffer!!\n");
    224         return false;
    225     }
    226 
    227     m_pbufWidth = p_width;
    228     m_pbufHeight = p_height;
    229 
    230     if (needRebindContext) {
    231         s_egl.eglMakeCurrent(fb->getDisplay(),
    232                      (prevDrawSurf==prevPbuf) ? m_eglSurface : prevDrawSurf,
    233                      (prevReadSurf==prevPbuf) ? m_eglSurface : prevReadSurf,
    234                      prevContext);
    235     }
    236 
    237     return true;
    238 }
    239