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 #include "GLcommon/GLutils.h" 22 #ifdef WITH_GLES2 23 #include "GL2Dispatch.h" 24 #endif 25 #include <stdio.h> 26 27 ColorBuffer *ColorBuffer::create(int p_width, int p_height, 28 GLenum p_internalFormat) 29 { 30 FrameBuffer *fb = FrameBuffer::getFB(); 31 32 GLenum texInternalFormat = 0; 33 34 switch(p_internalFormat) { 35 case GL_RGB: 36 case GL_RGB565_OES: 37 texInternalFormat = GL_RGB; 38 break; 39 40 case GL_RGBA: 41 case GL_RGB5_A1_OES: 42 case GL_RGBA4_OES: 43 texInternalFormat = GL_RGBA; 44 break; 45 46 default: 47 return NULL; 48 break; 49 } 50 51 if (!fb->bind_locked()) { 52 return NULL; 53 } 54 55 ColorBuffer *cb = new ColorBuffer(); 56 57 58 s_gl.glGenTextures(1, &cb->m_tex); 59 s_gl.glBindTexture(GL_TEXTURE_2D, cb->m_tex); 60 int nComp = (texInternalFormat == GL_RGB ? 3 : 4); 61 char *zBuff = new char[nComp*p_width*p_height]; 62 if (zBuff) { 63 memset(zBuff, 0, nComp*p_width*p_height); 64 } 65 s_gl.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, 66 p_width, p_height, 0, 67 texInternalFormat, 68 GL_UNSIGNED_BYTE, zBuff); 69 delete [] zBuff; 70 s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 71 s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 72 s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 73 s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 74 s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 75 76 // 77 // create another texture for that colorbuffer for blit 78 // 79 s_gl.glGenTextures(1, &cb->m_blitTex); 80 s_gl.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex); 81 s_gl.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, 82 p_width, p_height, 0, 83 texInternalFormat, 84 GL_UNSIGNED_BYTE, NULL); 85 s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 86 s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 87 s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 88 s_gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 89 s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 90 91 cb->m_width = p_width; 92 cb->m_height = p_height; 93 cb->m_internalFormat = texInternalFormat; 94 95 if (fb->getCaps().has_eglimage_texture_2d) { 96 cb->m_eglImage = s_egl.eglCreateImageKHR( 97 fb->getDisplay(), 98 s_egl.eglGetCurrentContext(), 99 EGL_GL_TEXTURE_2D_KHR, 100 (EGLClientBuffer)SafePointerFromUInt(cb->m_tex), 101 NULL); 102 103 cb->m_blitEGLImage = s_egl.eglCreateImageKHR( 104 fb->getDisplay(), 105 s_egl.eglGetCurrentContext(), 106 EGL_GL_TEXTURE_2D_KHR, 107 (EGLClientBuffer)SafePointerFromUInt(cb->m_blitTex), 108 NULL); 109 } 110 111 fb->unbind_locked(); 112 return cb; 113 } 114 115 ColorBuffer::ColorBuffer() : 116 m_tex(0), 117 m_blitTex(0), 118 m_eglImage(NULL), 119 m_blitEGLImage(NULL), 120 m_fbo(0), 121 m_internalFormat(0) 122 { 123 } 124 125 ColorBuffer::~ColorBuffer() 126 { 127 FrameBuffer *fb = FrameBuffer::getFB(); 128 fb->bind_locked(); 129 130 if (m_blitEGLImage) { 131 s_egl.eglDestroyImageKHR(fb->getDisplay(), m_blitEGLImage); 132 } 133 if (m_eglImage) { 134 s_egl.eglDestroyImageKHR(fb->getDisplay(), m_eglImage); 135 } 136 137 if (m_fbo) { 138 s_gl.glDeleteFramebuffersOES(1, &m_fbo); 139 } 140 141 GLuint tex[2] = {m_tex, m_blitTex}; 142 s_gl.glDeleteTextures(2, tex); 143 144 fb->unbind_locked(); 145 } 146 147 void ColorBuffer::subUpdate(int x, int y, int width, int height, GLenum p_format, GLenum p_type, void *pixels) 148 { 149 FrameBuffer *fb = FrameBuffer::getFB(); 150 if (!fb->bind_locked()) return; 151 s_gl.glBindTexture(GL_TEXTURE_2D, m_tex); 152 s_gl.glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 153 s_gl.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 154 width, height, p_format, p_type, pixels); 155 fb->unbind_locked(); 156 } 157 158 bool ColorBuffer::blitFromCurrentReadBuffer() 159 { 160 RenderThreadInfo *tInfo = RenderThreadInfo::get(); 161 if (!tInfo->currContext.Ptr()) { 162 // no Current context 163 return false; 164 } 165 166 // 167 // Create a temporary texture inside the current context 168 // from the blit_texture EGLImage and copy the pixels 169 // from the current read buffer to that texture 170 // 171 GLuint tmpTex; 172 GLint currTexBind; 173 if (tInfo->currContext->isGL2()) { 174 s_gl2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind); 175 s_gl2.glGenTextures(1,&tmpTex); 176 s_gl2.glBindTexture(GL_TEXTURE_2D, tmpTex); 177 s_gl2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage); 178 s_gl2.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 179 m_width, m_height); 180 } 181 else { 182 s_gl.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind); 183 s_gl.glGenTextures(1,&tmpTex); 184 s_gl.glBindTexture(GL_TEXTURE_2D, tmpTex); 185 s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage); 186 s_gl.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 187 m_width, m_height); 188 } 189 190 191 // 192 // Now bind the frame buffer context and blit from 193 // m_blitTex into m_tex 194 // 195 FrameBuffer *fb = FrameBuffer::getFB(); 196 if (fb->bind_locked()) { 197 198 // 199 // bind FBO object which has this colorbuffer as render target 200 // 201 if (bind_fbo()) { 202 203 // 204 // save current viewport and match it to the current 205 // colorbuffer size 206 // 207 GLint vport[4] = {}; 208 s_gl.glGetIntegerv(GL_VIEWPORT, vport); 209 s_gl.glViewport(0, 0, m_width, m_height); 210 211 // render m_blitTex 212 s_gl.glBindTexture(GL_TEXTURE_2D, m_blitTex); 213 s_gl.glEnable(GL_TEXTURE_2D); 214 s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 215 drawTexQuad(); // this will render the texture flipped 216 217 // unbind the fbo 218 s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); 219 220 // restrore previous viewport 221 s_gl.glViewport(vport[0], vport[1], vport[2], vport[3]); 222 } 223 224 // unbind from the FrameBuffer context 225 fb->unbind_locked(); 226 } 227 228 // 229 // delete the temporary texture and restore the texture binding 230 // inside the current context 231 // 232 if (tInfo->currContext->isGL2()) { 233 s_gl2.glDeleteTextures(1, &tmpTex); 234 s_gl2.glBindTexture(GL_TEXTURE_2D, currTexBind); 235 } 236 else { 237 s_gl.glDeleteTextures(1, &tmpTex); 238 s_gl.glBindTexture(GL_TEXTURE_2D, currTexBind); 239 } 240 241 return true; 242 } 243 244 bool ColorBuffer::bindToTexture() 245 { 246 if (m_eglImage) { 247 RenderThreadInfo *tInfo = RenderThreadInfo::get(); 248 if (tInfo->currContext.Ptr()) { 249 #ifdef WITH_GLES2 250 if (tInfo->currContext->isGL2()) { 251 s_gl2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); 252 } 253 else { 254 s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); 255 } 256 #else 257 s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); 258 #endif 259 return true; 260 } 261 } 262 return false; 263 } 264 265 bool ColorBuffer::bindToRenderbuffer() 266 { 267 if (m_eglImage) { 268 RenderThreadInfo *tInfo = RenderThreadInfo::get(); 269 if (tInfo->currContext.Ptr()) { 270 #ifdef WITH_GLES2 271 if (tInfo->currContext->isGL2()) { 272 s_gl2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage); 273 } 274 else { 275 s_gl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage); 276 } 277 #else 278 s_gl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage); 279 #endif 280 return true; 281 } 282 } 283 return false; 284 } 285 286 bool ColorBuffer::bind_fbo() 287 { 288 if (m_fbo) { 289 // fbo already exist - just bind 290 s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_fbo); 291 return true; 292 } 293 294 s_gl.glGenFramebuffersOES(1, &m_fbo); 295 s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_fbo); 296 s_gl.glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, 297 GL_COLOR_ATTACHMENT0_OES, 298 GL_TEXTURE_2D, m_tex, 0); 299 GLenum status = s_gl.glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); 300 if (status != GL_FRAMEBUFFER_COMPLETE_OES) { 301 ERR("ColorBuffer::bind_fbo: FBO not complete: %#x\n", status); 302 s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); 303 s_gl.glDeleteFramebuffersOES(1, &m_fbo); 304 m_fbo = 0; 305 return false; 306 } 307 308 return true; 309 } 310 311 bool ColorBuffer::post() 312 { 313 s_gl.glBindTexture(GL_TEXTURE_2D, m_tex); 314 s_gl.glEnable(GL_TEXTURE_2D); 315 s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 316 drawTexQuad(); 317 318 return true; 319 } 320 321 void ColorBuffer::drawTexQuad() 322 { 323 GLfloat verts[] = { -1.0f, -1.0f, 0.0f, 324 -1.0f, +1.0f, 0.0f, 325 +1.0f, -1.0f, 0.0f, 326 +1.0f, +1.0f, 0.0f }; 327 328 GLfloat tcoords[] = { 0.0f, 1.0f, 329 0.0f, 0.0f, 330 1.0f, 1.0f, 331 1.0f, 0.0f }; 332 333 s_gl.glClientActiveTexture(GL_TEXTURE0); 334 s_gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY); 335 s_gl.glTexCoordPointer(2, GL_FLOAT, 0, tcoords); 336 337 s_gl.glEnableClientState(GL_VERTEX_ARRAY); 338 s_gl.glVertexPointer(3, GL_FLOAT, 0, verts); 339 s_gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 340 } 341 342 void ColorBuffer::readback(unsigned char* img) 343 { 344 FrameBuffer *fb = FrameBuffer::getFB(); 345 if (fb->bind_locked()) { 346 if (bind_fbo()) { 347 s_gl.glReadPixels(0, 0, m_width, m_height, 348 GL_RGBA, GL_UNSIGNED_BYTE, img); 349 } 350 fb->unbind_locked(); 351 } 352 } 353