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 
create(int p_width,int p_height,GLenum p_internalFormat)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 
ColorBuffer()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 
~ColorBuffer()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 
subUpdate(int x,int y,int width,int height,GLenum p_format,GLenum p_type,void * pixels)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 
blitFromCurrentReadBuffer()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 
bindToTexture()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 
bindToRenderbuffer()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 
bind_fbo()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 
post()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 
drawTexQuad()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 
readback(unsigned char * img)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