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