• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
create(int p_width,int p_height,GLenum p_internalFormat)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 
ColorBuffer()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 
~ColorBuffer()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 
subUpdate(int x,int y,int width,int height,GLenum p_format,GLenum p_type,void * pixels)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 
blitFromCurrentReadBuffer()171 bool ColorBuffer::blitFromCurrentReadBuffer()
172 {
173     RenderThreadInfo *tInfo = RenderThreadInfo::get();
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.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
192                                   m_width, m_height);
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.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
200                                  m_width, m_height);
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 
bindToTexture()257 bool ColorBuffer::bindToTexture()
258 {
259     if (m_eglImage) {
260         RenderThreadInfo *tInfo = RenderThreadInfo::get();
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 
bindToRenderbuffer()278 bool ColorBuffer::bindToRenderbuffer()
279 {
280     if (m_eglImage) {
281         RenderThreadInfo *tInfo = RenderThreadInfo::get();
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 
bind_fbo()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 
post()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 
drawTexQuad(bool flipy)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 
readback(unsigned char * img)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