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, some
123 // 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
125 // been 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 for
128 // it. Running a standalone test to detect the problem at runtime would be
129 // more robust.
130 if (strstr((const char*)s_gl.glGetString(GL_RENDERER), "Intel HD Graphics 3000"))
131 m_warYInvertBug = true;
132 #endif
133 }
134
~ColorBuffer()135 ColorBuffer::~ColorBuffer()
136 {
137 FrameBuffer *fb = FrameBuffer::getFB();
138 fb->bind_locked();
139
140 if (m_blitEGLImage) {
141 s_egl.eglDestroyImageKHR(fb->getDisplay(), m_blitEGLImage);
142 }
143 if (m_eglImage) {
144 s_egl.eglDestroyImageKHR(fb->getDisplay(), m_eglImage);
145 }
146
147 if (m_fbo) {
148 s_gl.glDeleteFramebuffersOES(1, &m_fbo);
149 }
150
151 GLuint tex[2] = {m_tex, m_blitTex};
152 s_gl.glDeleteTextures(2, tex);
153
154 fb->unbind_locked();
155 }
156
subUpdate(int x,int y,int width,int height,GLenum p_format,GLenum p_type,void * pixels)157 void ColorBuffer::subUpdate(int x, int y, int width, int height, GLenum p_format, GLenum p_type, void *pixels)
158 {
159 FrameBuffer *fb = FrameBuffer::getFB();
160 if (!fb->bind_locked()) return;
161 s_gl.glBindTexture(GL_TEXTURE_2D, m_tex);
162 s_gl.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
163 s_gl.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y,
164 width, height, p_format, p_type, pixels);
165 fb->unbind_locked();
166 }
167
blitFromCurrentReadBuffer()168 bool ColorBuffer::blitFromCurrentReadBuffer()
169 {
170 RenderThreadInfo *tInfo = getRenderThreadInfo();
171 if (!tInfo->currContext.Ptr()) {
172 // no Current context
173 return false;
174 }
175
176 //
177 // Create a temporary texture inside the current context
178 // from the blit_texture EGLImage and copy the pixels
179 // from the current read buffer to that texture
180 //
181 GLuint tmpTex;
182 GLint currTexBind;
183 if (tInfo->currContext->isGL2()) {
184 s_gl2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
185 s_gl2.glGenTextures(1,&tmpTex);
186 s_gl2.glBindTexture(GL_TEXTURE_2D, tmpTex);
187 s_gl2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
188 s_gl2.glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat,
189 0, 0, m_width, m_height, 0);
190 }
191 else {
192 s_gl.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
193 s_gl.glGenTextures(1,&tmpTex);
194 s_gl.glBindTexture(GL_TEXTURE_2D, tmpTex);
195 s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
196 s_gl.glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat,
197 0, 0, m_width, m_height, 0);
198 }
199
200
201 //
202 // Now bind the frame buffer context and blit from
203 // m_blitTex into m_tex
204 //
205 FrameBuffer *fb = FrameBuffer::getFB();
206 if (fb->bind_locked()) {
207
208 //
209 // bind FBO object which has this colorbuffer as render target
210 //
211 if (bind_fbo()) {
212
213 //
214 // save current viewport and match it to the current
215 // colorbuffer size
216 //
217 GLint vport[4];
218 s_gl.glGetIntegerv(GL_VIEWPORT, vport);
219 s_gl.glViewport(0, 0, m_width, m_height);
220
221 // render m_blitTex
222 s_gl.glBindTexture(GL_TEXTURE_2D, m_blitTex);
223 s_gl.glEnable(GL_TEXTURE_2D);
224 s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
225 drawTexQuad(!m_warYInvertBug);
226
227 // unbind the fbo
228 s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
229
230 // restrore previous viewport
231 s_gl.glViewport(vport[0], vport[1], vport[2], vport[3]);
232 }
233
234 // unbind from the FrameBuffer context
235 fb->unbind_locked();
236 }
237
238 //
239 // delete the temporary texture and restore the texture binding
240 // inside the current context
241 //
242 if (tInfo->currContext->isGL2()) {
243 s_gl2.glDeleteTextures(1, &tmpTex);
244 s_gl2.glBindTexture(GL_TEXTURE_2D, currTexBind);
245 }
246 else {
247 s_gl.glDeleteTextures(1, &tmpTex);
248 s_gl.glBindTexture(GL_TEXTURE_2D, currTexBind);
249 }
250
251 return true;
252 }
253
bindToTexture()254 bool ColorBuffer::bindToTexture()
255 {
256 if (m_eglImage) {
257 RenderThreadInfo *tInfo = getRenderThreadInfo();
258 if (tInfo->currContext.Ptr()) {
259 #ifdef WITH_GLES2
260 if (tInfo->currContext->isGL2()) {
261 s_gl2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
262 }
263 else {
264 s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
265 }
266 #else
267 s_gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
268 #endif
269 return true;
270 }
271 }
272 return false;
273 }
274
bindToRenderbuffer()275 bool ColorBuffer::bindToRenderbuffer()
276 {
277 if (m_eglImage) {
278 RenderThreadInfo *tInfo = getRenderThreadInfo();
279 if (tInfo->currContext.Ptr()) {
280 #ifdef WITH_GLES2
281 if (tInfo->currContext->isGL2()) {
282 s_gl2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
283 }
284 else {
285 s_gl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
286 }
287 #else
288 s_gl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
289 #endif
290 return true;
291 }
292 }
293 return false;
294 }
295
bind_fbo()296 bool ColorBuffer::bind_fbo()
297 {
298 if (m_fbo) {
299 // fbo already exist - just bind
300 s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_fbo);
301 return true;
302 }
303
304 s_gl.glGenFramebuffersOES(1, &m_fbo);
305 s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_fbo);
306 s_gl.glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
307 GL_COLOR_ATTACHMENT0_OES,
308 GL_TEXTURE_2D, m_tex, 0);
309 GLenum status = s_gl.glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
310 if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
311 s_gl.glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
312 s_gl.glDeleteFramebuffersOES(1, &m_fbo);
313 m_fbo = 0;
314 return false;
315 }
316
317 return true;
318 }
319
post()320 bool ColorBuffer::post()
321 {
322 s_gl.glBindTexture(GL_TEXTURE_2D, m_tex);
323 s_gl.glEnable(GL_TEXTURE_2D);
324 s_gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
325 drawTexQuad(true);
326
327 return true;
328 }
329
drawTexQuad(bool flipy)330 void ColorBuffer::drawTexQuad(bool flipy)
331 {
332 GLfloat verts[] = { -1.0f, -1.0f, 0.0f,
333 -1.0f, +1.0f, 0.0f,
334 +1.0f, -1.0f, 0.0f,
335 +1.0f, +1.0f, 0.0f };
336
337 GLfloat tcoords[] = { 0.0f, 1.0f,
338 0.0f, 0.0f,
339 1.0f, 1.0f,
340 1.0f, 0.0f };
341
342 if (!flipy) {
343 for (int i = 0; i < 4; i++) {
344 // swap 0.0/1.0 in second element of each tcoord vector
345 tcoords[2*i + 1] = tcoords[2*i + 1] == 0.0f ? 1.0f : 0.0f;
346 }
347 }
348
349 s_gl.glClientActiveTexture(GL_TEXTURE0);
350 s_gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
351 s_gl.glTexCoordPointer(2, GL_FLOAT, 0, tcoords);
352
353 s_gl.glEnableClientState(GL_VERTEX_ARRAY);
354 s_gl.glVertexPointer(3, GL_FLOAT, 0, verts);
355 s_gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
356 }
357
readback(unsigned char * img)358 void ColorBuffer::readback(unsigned char* img)
359 {
360 FrameBuffer *fb = FrameBuffer::getFB();
361 if (fb->bind_locked()) {
362 if (bind_fbo()) {
363 s_gl.glReadPixels(0, 0, m_width, m_height,
364 GL_RGBA, GL_UNSIGNED_BYTE, img);
365 }
366 fb->unbind_locked();
367 }
368 }
369