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 "FrameBuffer.h"
17 #include "NativeSubWindow.h"
18 #include "FBConfig.h"
19 #include "EGLDispatch.h"
20 #include "GLDispatch.h"
21 #include "GL2Dispatch.h"
22 #include "ThreadInfo.h"
23 #include <stdio.h>
24 #include "TimeUtils.h"
25
26 FrameBuffer *FrameBuffer::s_theFrameBuffer = NULL;
27 HandleType FrameBuffer::s_nextHandle = 0;
28
29 #ifdef WITH_GLES2
getGLES2ExtensionString(EGLDisplay p_dpy)30 static const char *getGLES2ExtensionString(EGLDisplay p_dpy)
31 {
32 EGLConfig config;
33 EGLSurface surface;
34
35 GLint configAttribs[] = {
36 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
37 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
38 EGL_NONE
39 };
40
41 int n;
42 if (!s_egl.eglChooseConfig(p_dpy, configAttribs,
43 &config, 1, &n)) {
44 return NULL;
45 }
46
47 EGLint pbufAttribs[] = {
48 EGL_WIDTH, 1,
49 EGL_HEIGHT, 1,
50 EGL_NONE
51 };
52
53 surface = s_egl.eglCreatePbufferSurface(p_dpy, config, pbufAttribs);
54 if (surface == EGL_NO_SURFACE) {
55 return NULL;
56 }
57
58 GLint gl2ContextAttribs[] = {
59 EGL_CONTEXT_CLIENT_VERSION, 2,
60 EGL_NONE
61 };
62
63 EGLContext ctx = s_egl.eglCreateContext(p_dpy, config,
64 EGL_NO_CONTEXT,
65 gl2ContextAttribs);
66 if (ctx == EGL_NO_CONTEXT) {
67 s_egl.eglDestroySurface(p_dpy, surface);
68 return NULL;
69 }
70
71 if (!s_egl.eglMakeCurrent(p_dpy, surface, surface, ctx)) {
72 s_egl.eglDestroySurface(p_dpy, surface);
73 s_egl.eglDestroyContext(p_dpy, ctx);
74 return NULL;
75 }
76
77 const char *extString = (const char *)s_gl2.glGetString(GL_EXTENSIONS);
78 if (!extString) {
79 extString = "";
80 }
81
82 s_egl.eglMakeCurrent(p_dpy, NULL, NULL, NULL);
83 s_egl.eglDestroyContext(p_dpy, ctx);
84 s_egl.eglDestroySurface(p_dpy, surface);
85
86 return extString;
87 }
88 #endif
89
finalize()90 void FrameBuffer::finalize(){
91 if(s_theFrameBuffer){
92 s_theFrameBuffer->removeSubWindow();
93 s_theFrameBuffer->m_colorbuffers.clear();
94 s_theFrameBuffer->m_windows.clear();
95 s_theFrameBuffer->m_contexts.clear();
96 s_egl.eglMakeCurrent(s_theFrameBuffer->m_eglDisplay, NULL, NULL, NULL);
97 s_egl.eglDestroyContext(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_eglContext);
98 s_egl.eglDestroyContext(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_pbufContext);
99 s_egl.eglDestroySurface(s_theFrameBuffer->m_eglDisplay,s_theFrameBuffer->m_pbufSurface);
100 s_theFrameBuffer = NULL;
101 }
102 }
103
initialize(int width,int height,OnPostFn onPost,void * onPostContext)104 bool FrameBuffer::initialize(int width, int height, OnPostFn onPost, void* onPostContext)
105 {
106 if (s_theFrameBuffer != NULL) {
107 return true;
108 }
109
110 //
111 // allocate space for the FrameBuffer object
112 //
113 FrameBuffer *fb = new FrameBuffer(width, height, onPost, onPostContext);
114 if (!fb) {
115 ERR("Failed to create fb\n");
116 return false;
117 }
118
119 #ifdef WITH_GLES2
120 //
121 // Try to load GLES2 Plugin, not mandatory
122 //
123 if (getenv("ANDROID_NO_GLES2")) {
124 fb->m_caps.hasGL2 = false;
125 }
126 else {
127 fb->m_caps.hasGL2 = s_gl2_enabled;
128 }
129 #else
130 fb->m_caps.hasGL2 = false;
131 #endif
132
133 //
134 // Initialize backend EGL display
135 //
136 fb->m_eglDisplay = s_egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
137 if (fb->m_eglDisplay == EGL_NO_DISPLAY) {
138 ERR("Failed to Initialize backend EGL display\n");
139 delete fb;
140 return false;
141 }
142
143 if (!s_egl.eglInitialize(fb->m_eglDisplay, &fb->m_caps.eglMajor, &fb->m_caps.eglMinor)) {
144 ERR("Failed to eglInitialize\n");
145 delete fb;
146 return false;
147 }
148
149 DBG("egl: %d %d\n", fb->m_caps.eglMajor, fb->m_caps.eglMinor);
150 s_egl.eglBindAPI(EGL_OPENGL_ES_API);
151
152 //
153 // if GLES2 plugin has loaded - try to make GLES2 context and
154 // get GLES2 extension string
155 //
156 const char *gl2Extensions = NULL;
157 #ifdef WITH_GLES2
158 if (fb->m_caps.hasGL2) {
159 gl2Extensions = getGLES2ExtensionString(fb->m_eglDisplay);
160 if (!gl2Extensions) {
161 // Could not create GLES2 context - drop GL2 capability
162 fb->m_caps.hasGL2 = false;
163 }
164 }
165 #endif
166
167 //
168 // Create EGL context for framebuffer post rendering.
169 //
170 #if 0
171 GLint configAttribs[] = {
172 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
173 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
174 EGL_NONE
175 };
176 #else
177 GLint configAttribs[] = {
178 EGL_RED_SIZE, 1,
179 EGL_GREEN_SIZE, 1,
180 EGL_BLUE_SIZE, 1,
181 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
182 EGL_NONE
183 };
184 #endif
185
186 int n;
187 if (!s_egl.eglChooseConfig(fb->m_eglDisplay, configAttribs,
188 &fb->m_eglConfig, 1, &n)) {
189 ERR("Failed on eglChooseConfig\n");
190 delete fb;
191 return false;
192 }
193
194 GLint glContextAttribs[] = {
195 EGL_CONTEXT_CLIENT_VERSION, 1,
196 EGL_NONE
197 };
198
199 fb->m_eglContext = s_egl.eglCreateContext(fb->m_eglDisplay, fb->m_eglConfig,
200 EGL_NO_CONTEXT,
201 glContextAttribs);
202 if (fb->m_eglContext == EGL_NO_CONTEXT) {
203 printf("Failed to create Context 0x%x\n", s_egl.eglGetError());
204 delete fb;
205 return false;
206 }
207
208 //
209 // Create another context which shares with the eglContext to be used
210 // when we bind the pbuffer. That prevent switching drawable binding
211 // back and forth on framebuffer context.
212 // The main purpose of it is to solve a "blanking" behaviour we see on
213 // on Mac platform when switching binded drawable for a context however
214 // it is more efficient on other platforms as well.
215 //
216 fb->m_pbufContext = s_egl.eglCreateContext(fb->m_eglDisplay, fb->m_eglConfig,
217 fb->m_eglContext,
218 glContextAttribs);
219 if (fb->m_pbufContext == EGL_NO_CONTEXT) {
220 printf("Failed to create Pbuffer Context 0x%x\n", s_egl.eglGetError());
221 delete fb;
222 return false;
223 }
224
225 //
226 // create a 1x1 pbuffer surface which will be used for binding
227 // the FB context.
228 // The FB output will go to a subwindow, if one exist.
229 //
230 EGLint pbufAttribs[] = {
231 EGL_WIDTH, 1,
232 EGL_HEIGHT, 1,
233 EGL_NONE
234 };
235
236 fb->m_pbufSurface = s_egl.eglCreatePbufferSurface(fb->m_eglDisplay,
237 fb->m_eglConfig,
238 pbufAttribs);
239 if (fb->m_pbufSurface == EGL_NO_SURFACE) {
240 printf("Failed to create pbuf surface for FB 0x%x\n", s_egl.eglGetError());
241 delete fb;
242 return false;
243 }
244
245 // Make the context current
246 if (!fb->bind_locked()) {
247 ERR("Failed to make current\n");
248 delete fb;
249 return false;
250 }
251
252 //
253 // Initilize framebuffer capabilities
254 //
255 const char *glExtensions = (const char *)s_gl.glGetString(GL_EXTENSIONS);
256 bool has_gl_oes_image = false;
257 if (glExtensions) {
258 has_gl_oes_image = strstr(glExtensions, "GL_OES_EGL_image") != NULL;
259 }
260
261 if (fb->m_caps.hasGL2 && has_gl_oes_image) {
262 has_gl_oes_image &= (strstr(gl2Extensions, "GL_OES_EGL_image") != NULL);
263 }
264
265 const char *eglExtensions = s_egl.eglQueryString(fb->m_eglDisplay,
266 EGL_EXTENSIONS);
267
268 if (eglExtensions && has_gl_oes_image) {
269 fb->m_caps.has_eglimage_texture_2d =
270 strstr(eglExtensions, "EGL_KHR_gl_texture_2D_image") != NULL;
271 fb->m_caps.has_eglimage_renderbuffer =
272 strstr(eglExtensions, "EGL_KHR_gl_renderbuffer_image") != NULL;
273 }
274 else {
275 fb->m_caps.has_eglimage_texture_2d = false;
276 fb->m_caps.has_eglimage_renderbuffer = false;
277 }
278
279 //
280 // Fail initialization if not all of the following extensions
281 // exist:
282 // EGL_KHR_gl_texture_2d_image
283 // GL_OES_EGL_IMAGE (by both GLES implementations [1 and 2])
284 //
285 if (!fb->m_caps.has_eglimage_texture_2d) {
286 ERR("Failed: Missing egl_image related extension(s)\n");
287 delete fb;
288 return false;
289 }
290
291 //
292 // Initialize set of configs
293 //
294 InitConfigStatus configStatus = FBConfig::initConfigList(fb);
295 if (configStatus == INIT_CONFIG_FAILED) {
296 ERR("Failed: Initialize set of configs\n");
297 delete fb;
298 return false;
299 }
300
301 //
302 // Check that we have config for each GLES and GLES2
303 //
304 int nConfigs = FBConfig::getNumConfigs();
305 int nGLConfigs = 0;
306 int nGL2Configs = 0;
307 for (int i=0; i<nConfigs; i++) {
308 GLint rtype = FBConfig::get(i)->getRenderableType();
309 if (0 != (rtype & EGL_OPENGL_ES_BIT)) {
310 nGLConfigs++;
311 }
312 if (0 != (rtype & EGL_OPENGL_ES2_BIT)) {
313 nGL2Configs++;
314 }
315 }
316
317 //
318 // Fail initialization if no GLES configs exist
319 //
320 if (nGLConfigs == 0) {
321 delete fb;
322 return false;
323 }
324
325 //
326 // If no GLES2 configs exist - not GLES2 capability
327 //
328 if (nGL2Configs == 0) {
329 fb->m_caps.hasGL2 = false;
330 }
331
332 //
333 // Initialize some GL state in the pbuffer context
334 //
335 fb->initGLState();
336
337 //
338 // Allocate space for the onPost framebuffer image
339 //
340 if (onPost) {
341 fb->m_fbImage = (unsigned char*)malloc(4 * width * height);
342 if (!fb->m_fbImage) {
343 delete fb;
344 return false;
345 }
346 }
347
348 // release the FB context
349 fb->unbind_locked();
350
351 //
352 // Keep the singleton framebuffer pointer
353 //
354 s_theFrameBuffer = fb;
355 return true;
356 }
357
FrameBuffer(int p_width,int p_height,OnPostFn onPost,void * onPostContext)358 FrameBuffer::FrameBuffer(int p_width, int p_height,
359 OnPostFn onPost, void* onPostContext) :
360 m_width(p_width),
361 m_height(p_height),
362 m_eglDisplay(EGL_NO_DISPLAY),
363 m_eglSurface(EGL_NO_SURFACE),
364 m_eglContext(EGL_NO_CONTEXT),
365 m_pbufContext(EGL_NO_CONTEXT),
366 m_prevContext(EGL_NO_CONTEXT),
367 m_prevReadSurf(EGL_NO_SURFACE),
368 m_prevDrawSurf(EGL_NO_SURFACE),
369 m_subWin(NULL),
370 m_subWinDisplay(NULL),
371 m_lastPostedColorBuffer(0),
372 m_zRot(0.0f),
373 m_eglContextInitialized(false),
374 m_statsNumFrames(0),
375 m_statsStartTime(0LL),
376 m_onPost(onPost),
377 m_onPostContext(onPostContext),
378 m_fbImage(NULL)
379 {
380 m_fpsStats = getenv("SHOW_FPS_STATS") != NULL;
381 }
382
~FrameBuffer()383 FrameBuffer::~FrameBuffer()
384 {
385 free(m_fbImage);
386 }
387
setupSubWindow(FBNativeWindowType p_window,int p_x,int p_y,int p_width,int p_height,float zRot)388 bool FrameBuffer::setupSubWindow(FBNativeWindowType p_window,
389 int p_x, int p_y,
390 int p_width, int p_height, float zRot)
391 {
392 bool success = false;
393
394 if (s_theFrameBuffer) {
395 s_theFrameBuffer->m_lock.lock();
396 FrameBuffer *fb = s_theFrameBuffer;
397 if (!fb->m_subWin) {
398
399 // create native subwindow for FB display output
400 fb->m_subWin = createSubWindow(p_window,
401 &fb->m_subWinDisplay,
402 p_x,p_y,p_width,p_height);
403 if (fb->m_subWin) {
404 fb->m_nativeWindow = p_window;
405
406 // create EGLSurface from the generated subwindow
407 fb->m_eglSurface = s_egl.eglCreateWindowSurface(fb->m_eglDisplay,
408 fb->m_eglConfig,
409 fb->m_subWin,
410 NULL);
411
412 if (fb->m_eglSurface == EGL_NO_SURFACE) {
413 ERR("Failed to create surface\n");
414 destroySubWindow(fb->m_subWinDisplay, fb->m_subWin);
415 fb->m_subWin = NULL;
416 }
417 else if (fb->bindSubwin_locked()) {
418 // Subwin creation was successfull,
419 // update viewport and z rotation and draw
420 // the last posted color buffer.
421 s_gl.glViewport(0, 0, p_width, p_height);
422 fb->m_zRot = zRot;
423 fb->post( fb->m_lastPostedColorBuffer, false );
424 fb->unbind_locked();
425 success = true;
426 }
427 }
428 }
429 s_theFrameBuffer->m_lock.unlock();
430 }
431
432 return success;
433 }
434
removeSubWindow()435 bool FrameBuffer::removeSubWindow()
436 {
437 bool removed = false;
438 if (s_theFrameBuffer) {
439 s_theFrameBuffer->m_lock.lock();
440 if (s_theFrameBuffer->m_subWin) {
441 s_egl.eglMakeCurrent(s_theFrameBuffer->m_eglDisplay, NULL, NULL, NULL);
442 s_egl.eglDestroySurface(s_theFrameBuffer->m_eglDisplay,
443 s_theFrameBuffer->m_eglSurface);
444 destroySubWindow(s_theFrameBuffer->m_subWinDisplay,
445 s_theFrameBuffer->m_subWin);
446
447 s_theFrameBuffer->m_eglSurface = EGL_NO_SURFACE;
448 s_theFrameBuffer->m_subWin = NULL;
449 removed = true;
450 }
451 s_theFrameBuffer->m_lock.unlock();
452 }
453 return removed;
454 }
455
genHandle()456 HandleType FrameBuffer::genHandle()
457 {
458 HandleType id;
459 do {
460 id = ++s_nextHandle;
461 } while( id == 0 ||
462 m_contexts.find(id) != m_contexts.end() ||
463 m_windows.find(id) != m_windows.end() );
464
465 return id;
466 }
467
createColorBuffer(int p_width,int p_height,GLenum p_internalFormat)468 HandleType FrameBuffer::createColorBuffer(int p_width, int p_height,
469 GLenum p_internalFormat)
470 {
471 android::Mutex::Autolock mutex(m_lock);
472 HandleType ret = 0;
473
474 ColorBufferPtr cb( ColorBuffer::create(p_width, p_height, p_internalFormat) );
475 if (cb.Ptr() != NULL) {
476 ret = genHandle();
477 m_colorbuffers[ret].cb = cb;
478 m_colorbuffers[ret].refcount = 1;
479 }
480 return ret;
481 }
482
createRenderContext(int p_config,HandleType p_share,bool p_isGL2)483 HandleType FrameBuffer::createRenderContext(int p_config, HandleType p_share,
484 bool p_isGL2)
485 {
486 android::Mutex::Autolock mutex(m_lock);
487 HandleType ret = 0;
488
489 RenderContextPtr share(NULL);
490 if (p_share != 0) {
491 RenderContextMap::iterator s( m_contexts.find(p_share) );
492 if (s == m_contexts.end()) {
493 return 0;
494 }
495 share = (*s).second;
496 }
497
498 RenderContextPtr rctx( RenderContext::create(p_config, share, p_isGL2) );
499 if (rctx.Ptr() != NULL) {
500 ret = genHandle();
501 m_contexts[ret] = rctx;
502 }
503 return ret;
504 }
505
createWindowSurface(int p_config,int p_width,int p_height)506 HandleType FrameBuffer::createWindowSurface(int p_config, int p_width, int p_height)
507 {
508 android::Mutex::Autolock mutex(m_lock);
509
510 HandleType ret = 0;
511 WindowSurfacePtr win( WindowSurface::create(p_config, p_width, p_height) );
512 if (win.Ptr() != NULL) {
513 ret = genHandle();
514 m_windows[ret] = win;
515 }
516
517 return ret;
518 }
519
DestroyRenderContext(HandleType p_context)520 void FrameBuffer::DestroyRenderContext(HandleType p_context)
521 {
522 android::Mutex::Autolock mutex(m_lock);
523 m_contexts.erase(p_context);
524 }
525
DestroyWindowSurface(HandleType p_surface)526 void FrameBuffer::DestroyWindowSurface(HandleType p_surface)
527 {
528 android::Mutex::Autolock mutex(m_lock);
529 m_windows.erase(p_surface);
530 }
531
openColorBuffer(HandleType p_colorbuffer)532 void FrameBuffer::openColorBuffer(HandleType p_colorbuffer)
533 {
534 android::Mutex::Autolock mutex(m_lock);
535 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
536 if (c == m_colorbuffers.end()) {
537 // bad colorbuffer handle
538 return;
539 }
540 (*c).second.refcount++;
541 }
542
closeColorBuffer(HandleType p_colorbuffer)543 void FrameBuffer::closeColorBuffer(HandleType p_colorbuffer)
544 {
545 android::Mutex::Autolock mutex(m_lock);
546 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
547 if (c == m_colorbuffers.end()) {
548 // bad colorbuffer handle
549 return;
550 }
551 if (--(*c).second.refcount == 0) {
552 m_colorbuffers.erase(c);
553 }
554 }
555
flushWindowSurfaceColorBuffer(HandleType p_surface)556 bool FrameBuffer::flushWindowSurfaceColorBuffer(HandleType p_surface)
557 {
558 android::Mutex::Autolock mutex(m_lock);
559
560 WindowSurfaceMap::iterator w( m_windows.find(p_surface) );
561 if (w == m_windows.end()) {
562 // bad surface handle
563 return false;
564 }
565
566 (*w).second->flushColorBuffer();
567
568 return true;
569 }
570
setWindowSurfaceColorBuffer(HandleType p_surface,HandleType p_colorbuffer)571 bool FrameBuffer::setWindowSurfaceColorBuffer(HandleType p_surface,
572 HandleType p_colorbuffer)
573 {
574 android::Mutex::Autolock mutex(m_lock);
575
576 WindowSurfaceMap::iterator w( m_windows.find(p_surface) );
577 if (w == m_windows.end()) {
578 // bad surface handle
579 return false;
580 }
581
582 ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
583 if (c == m_colorbuffers.end()) {
584 // bad colorbuffer handle
585 return false;
586 }
587
588 (*w).second->setColorBuffer( (*c).second.cb );
589
590 return true;
591 }
592
updateColorBuffer(HandleType p_colorbuffer,int x,int y,int width,int height,GLenum format,GLenum type,void * pixels)593 bool FrameBuffer::updateColorBuffer(HandleType p_colorbuffer,
594 int x, int y, int width, int height,
595 GLenum format, GLenum type, void *pixels)
596 {
597 android::Mutex::Autolock mutex(m_lock);
598
599 ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
600 if (c == m_colorbuffers.end()) {
601 // bad colorbuffer handle
602 return false;
603 }
604
605 (*c).second.cb->subUpdate(x, y, width, height, format, type, pixels);
606
607 return true;
608 }
609
bindColorBufferToTexture(HandleType p_colorbuffer)610 bool FrameBuffer::bindColorBufferToTexture(HandleType p_colorbuffer)
611 {
612 android::Mutex::Autolock mutex(m_lock);
613
614 ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
615 if (c == m_colorbuffers.end()) {
616 // bad colorbuffer handle
617 return false;
618 }
619
620 return (*c).second.cb->bindToTexture();
621 }
622
bindColorBufferToRenderbuffer(HandleType p_colorbuffer)623 bool FrameBuffer::bindColorBufferToRenderbuffer(HandleType p_colorbuffer)
624 {
625 android::Mutex::Autolock mutex(m_lock);
626
627 ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
628 if (c == m_colorbuffers.end()) {
629 // bad colorbuffer handle
630 return false;
631 }
632
633 return (*c).second.cb->bindToRenderbuffer();
634 }
635
bindContext(HandleType p_context,HandleType p_drawSurface,HandleType p_readSurface)636 bool FrameBuffer::bindContext(HandleType p_context,
637 HandleType p_drawSurface,
638 HandleType p_readSurface)
639 {
640 android::Mutex::Autolock mutex(m_lock);
641
642 WindowSurfacePtr draw(NULL), read(NULL);
643 RenderContextPtr ctx(NULL);
644
645 //
646 // if this is not an unbind operation - make sure all handles are good
647 //
648 if (p_context || p_drawSurface || p_readSurface) {
649 RenderContextMap::iterator r( m_contexts.find(p_context) );
650 if (r == m_contexts.end()) {
651 // bad context handle
652 return false;
653 }
654
655 ctx = (*r).second;
656 WindowSurfaceMap::iterator w( m_windows.find(p_drawSurface) );
657 if (w == m_windows.end()) {
658 // bad surface handle
659 return false;
660 }
661 draw = (*w).second;
662
663 if (p_readSurface != p_drawSurface) {
664 WindowSurfaceMap::iterator w( m_windows.find(p_readSurface) );
665 if (w == m_windows.end()) {
666 // bad surface handle
667 return false;
668 }
669 read = (*w).second;
670 }
671 else {
672 read = draw;
673 }
674 }
675
676 if (!s_egl.eglMakeCurrent(m_eglDisplay,
677 draw ? draw->getEGLSurface() : EGL_NO_SURFACE,
678 read ? read->getEGLSurface() : EGL_NO_SURFACE,
679 ctx ? ctx->getEGLContext() : EGL_NO_CONTEXT)) {
680 // MakeCurrent failed
681 return false;
682 }
683
684 //
685 // Bind the surface(s) to the context
686 //
687 RenderThreadInfo *tinfo = getRenderThreadInfo();
688 if (draw.Ptr() == NULL && read.Ptr() == NULL) {
689 // if this is an unbind operation - make sure the current bound
690 // surfaces get unbound from the context.
691 draw = tinfo->currDrawSurf;
692 read = tinfo->currReadSurf;
693 }
694
695 if (draw.Ptr() != NULL && read.Ptr() != NULL) {
696 if (p_readSurface != p_drawSurface) {
697 draw->bind( ctx, SURFACE_BIND_DRAW );
698 read->bind( ctx, SURFACE_BIND_READ );
699 }
700 else {
701 draw->bind( ctx, SURFACE_BIND_READDRAW );
702 }
703 }
704
705 //
706 // update thread info with current bound context
707 //
708 tinfo->currContext = ctx;
709 tinfo->currDrawSurf = draw;
710 tinfo->currReadSurf = read;
711 if (ctx) {
712 if (ctx->isGL2()) tinfo->m_gl2Dec.setContextData(&ctx->decoderContextData());
713 else tinfo->m_glDec.setContextData(&ctx->decoderContextData());
714 }
715 else {
716 tinfo->m_glDec.setContextData(NULL);
717 tinfo->m_gl2Dec.setContextData(NULL);
718 }
719 return true;
720 }
721
722 //
723 // The framebuffer lock should be held when calling this function !
724 //
bind_locked()725 bool FrameBuffer::bind_locked()
726 {
727 EGLContext prevContext = s_egl.eglGetCurrentContext();
728 EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
729 EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
730
731 if (!s_egl.eglMakeCurrent(m_eglDisplay, m_pbufSurface,
732 m_pbufSurface, m_pbufContext)) {
733 ERR("eglMakeCurrent failed\n");
734 return false;
735 }
736
737 m_prevContext = prevContext;
738 m_prevReadSurf = prevReadSurf;
739 m_prevDrawSurf = prevDrawSurf;
740 return true;
741 }
742
bindSubwin_locked()743 bool FrameBuffer::bindSubwin_locked()
744 {
745 EGLContext prevContext = s_egl.eglGetCurrentContext();
746 EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
747 EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
748
749 if (!s_egl.eglMakeCurrent(m_eglDisplay, m_eglSurface,
750 m_eglSurface, m_eglContext)) {
751 ERR("eglMakeCurrent failed\n");
752 return false;
753 }
754
755 //
756 // initialize GL state in eglContext if not yet initilaized
757 //
758 if (!m_eglContextInitialized) {
759 initGLState();
760 m_eglContextInitialized = true;
761 }
762
763 m_prevContext = prevContext;
764 m_prevReadSurf = prevReadSurf;
765 m_prevDrawSurf = prevDrawSurf;
766 return true;
767 }
768
unbind_locked()769 bool FrameBuffer::unbind_locked()
770 {
771 if (!s_egl.eglMakeCurrent(m_eglDisplay, m_prevDrawSurf,
772 m_prevReadSurf, m_prevContext)) {
773 return false;
774 }
775
776 m_prevContext = EGL_NO_CONTEXT;
777 m_prevReadSurf = EGL_NO_SURFACE;
778 m_prevDrawSurf = EGL_NO_SURFACE;
779 return true;
780 }
781
post(HandleType p_colorbuffer,bool needLock)782 bool FrameBuffer::post(HandleType p_colorbuffer, bool needLock)
783 {
784 if (needLock) m_lock.lock();
785 bool ret = false;
786
787 ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
788 if (c != m_colorbuffers.end()) {
789
790 m_lastPostedColorBuffer = p_colorbuffer;
791 if (!m_subWin) {
792 // no subwindow created for the FB output
793 // cannot post the colorbuffer
794 if (needLock) m_lock.unlock();
795 return ret;
796 }
797
798
799 // bind the subwindow eglSurface
800 if (!bindSubwin_locked()) {
801 ERR("FrameBuffer::post eglMakeCurrent failed\n");
802 if (needLock) m_lock.unlock();
803 return false;
804 }
805
806 //
807 // render the color buffer to the window
808 //
809 s_gl.glPushMatrix();
810 s_gl.glRotatef(m_zRot, 0.0f, 0.0f, 1.0f);
811 if (m_zRot != 0.0f) {
812 s_gl.glClear(GL_COLOR_BUFFER_BIT);
813 }
814 ret = (*c).second.cb->post();
815 s_gl.glPopMatrix();
816
817 if (ret) {
818 //
819 // Send framebuffer (without FPS overlay) to callback
820 //
821 if (m_onPost) {
822 s_gl.glReadPixels(0, 0, m_width, m_height,
823 GL_RGBA, GL_UNSIGNED_BYTE, m_fbImage);
824 m_onPost(m_onPostContext, m_width, m_height, -1,
825 GL_RGBA, GL_UNSIGNED_BYTE, m_fbImage);
826 }
827
828 //
829 // output FPS statistics
830 //
831 if (m_fpsStats) {
832 long long currTime = GetCurrentTimeMS();
833 m_statsNumFrames++;
834 if (currTime - m_statsStartTime >= 1000) {
835 float dt = (float)(currTime - m_statsStartTime) / 1000.0f;
836 printf("FPS: %5.3f\n", (float)m_statsNumFrames / dt);
837 m_statsStartTime = currTime;
838 m_statsNumFrames = 0;
839 }
840 }
841
842 s_egl.eglSwapBuffers(m_eglDisplay, m_eglSurface);
843 }
844
845 // restore previous binding
846 unbind_locked();
847 }
848
849 if (needLock) m_lock.unlock();
850 return ret;
851 }
852
repost()853 bool FrameBuffer::repost()
854 {
855 if (m_lastPostedColorBuffer) {
856 return post( m_lastPostedColorBuffer );
857 }
858 return false;
859 }
860
initGLState()861 void FrameBuffer::initGLState()
862 {
863 s_gl.glMatrixMode(GL_PROJECTION);
864 s_gl.glLoadIdentity();
865 s_gl.glOrthof(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
866 s_gl.glMatrixMode(GL_MODELVIEW);
867 s_gl.glLoadIdentity();
868 }
869