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 = getRenderThreadInfo(); 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.glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, 192 0, 0, m_width, m_height, 0); 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.glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, 200 0, 0, m_width, m_height, 0); 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 = getRenderThreadInfo(); 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 = getRenderThreadInfo(); 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