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