nkeynes@1245: /** nkeynes@1245: * $Id$ nkeynes@1245: * nkeynes@1245: * Window management using EGL. nkeynes@1245: * nkeynes@1245: * Copyright (c) 2012 Nathan Keynes. nkeynes@1245: * nkeynes@1245: * This program is free software; you can redistribute it and/or modify nkeynes@1245: * it under the terms of the GNU General Public License as published by nkeynes@1245: * the Free Software Foundation; either version 2 of the License, or nkeynes@1245: * (at your option) any later version. nkeynes@1245: * nkeynes@1245: * This program is distributed in the hope that it will be useful, nkeynes@1245: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1245: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1245: * GNU General Public License for more details. nkeynes@1245: */ nkeynes@1245: nkeynes@1245: #include "lxdream.h" nkeynes@1245: #include "display.h" nkeynes@1245: #include "video_egl.h" nkeynes@1245: #include "video_gl.h" nkeynes@1245: #include "pvr2/pvr2.h" nkeynes@1245: #include "pvr2/glutil.h" nkeynes@1245: nkeynes@1245: static const char *getEGLErrorString( EGLint code ) nkeynes@1245: { nkeynes@1245: switch( code ) { nkeynes@1245: case EGL_SUCCESS: return "OK"; nkeynes@1245: case EGL_NOT_INITIALIZED: return "EGL not initialized"; nkeynes@1245: case EGL_BAD_ACCESS: return "Bad access"; nkeynes@1245: case EGL_BAD_ALLOC: return "Allocation failed"; nkeynes@1245: case EGL_BAD_ATTRIBUTE: return "Bad attribute"; nkeynes@1245: case EGL_BAD_CONTEXT: return "Bad context"; nkeynes@1245: case EGL_BAD_CONFIG: return "Bad config"; nkeynes@1245: case EGL_BAD_CURRENT_SURFACE: return "Bad current surface"; nkeynes@1245: case EGL_BAD_DISPLAY: return "Bad display"; nkeynes@1245: case EGL_BAD_MATCH: return "Bad match"; nkeynes@1245: case EGL_BAD_PARAMETER: return "Bad parameter"; nkeynes@1245: case EGL_BAD_NATIVE_PIXMAP: return "Bad native pixmap"; nkeynes@1245: case EGL_BAD_NATIVE_WINDOW: return "Bad native window"; nkeynes@1245: default: return "Unknown error"; nkeynes@1245: } nkeynes@1245: } nkeynes@1245: nkeynes@1245: nkeynes@1245: static void logEGLError(const char *msg) nkeynes@1245: { nkeynes@1245: EGLint error = eglGetError(); nkeynes@1245: const char *errorStr = getEGLErrorString(error); nkeynes@1245: nkeynes@1245: ERROR( "%s: %s (%x)", msg, errorStr, error ); nkeynes@1245: } nkeynes@1245: nkeynes@1245: static const EGLint RGB888_attributes[] = { nkeynes@1245: EGL_RED_SIZE, 8, nkeynes@1245: EGL_GREEN_SIZE, 8, nkeynes@1245: EGL_BLUE_SIZE, 8, nkeynes@1245: EGL_DEPTH_SIZE, 16, nkeynes@1245: EGL_STENCIL_SIZE, 8, nkeynes@1245: EGL_SURFACE_TYPE, EGL_WINDOW_BIT, nkeynes@1245: EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT, nkeynes@1245: EGL_NONE, EGL_NONE }; nkeynes@1245: nkeynes@1245: static const EGLint RGB565_attributes[] = { nkeynes@1245: EGL_RED_SIZE, 5, nkeynes@1245: EGL_GREEN_SIZE, 6, nkeynes@1245: EGL_BLUE_SIZE, 5, nkeynes@1245: EGL_DEPTH_SIZE, 16, nkeynes@1245: EGL_STENCIL_SIZE, 8, nkeynes@1245: EGL_SURFACE_TYPE, EGL_WINDOW_BIT, nkeynes@1245: EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT, nkeynes@1245: EGL_NONE, EGL_NONE }; nkeynes@1245: nkeynes@1287: static const EGLint alt_attributes[] = { nkeynes@1287: EGL_DEPTH_SIZE, 16, nkeynes@1287: EGL_STENCIL_SIZE, 8, nkeynes@1287: EGL_SURFACE_TYPE, EGL_WINDOW_BIT, nkeynes@1287: EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT, nkeynes@1287: EGL_NONE, EGL_NONE }; nkeynes@1287: nkeynes@1245: static const EGLint context_attributes[] = { nkeynes@1245: EGL_CONTEXT_CLIENT_VERSION, 2, nkeynes@1245: EGL_NONE, EGL_NONE }; nkeynes@1245: nkeynes@1275: static EGLDisplay display = EGL_NO_DISPLAY; nkeynes@1245: static EGLContext context = EGL_NO_CONTEXT; nkeynes@1245: static EGLSurface surface = EGL_NO_SURFACE; nkeynes@1245: static gboolean fbo_created = FALSE; nkeynes@1245: nkeynes@1287: static void video_egl_swap_buffers(); nkeynes@1287: static int video_egl_major = 0, video_egl_minor = 0; nkeynes@1287: nkeynes@1287: gboolean video_egl_init() nkeynes@1245: { nkeynes@1245: display = eglGetDisplay(EGL_DEFAULT_DISPLAY); nkeynes@1287: if( eglInitialize(display, &video_egl_major, &video_egl_minor) != EGL_TRUE ) { nkeynes@1245: logEGLError( "Unable to initialise EGL display" ); nkeynes@1245: return FALSE; nkeynes@1245: } nkeynes@1287: return TRUE; nkeynes@1287: } nkeynes@1287: nkeynes@1287: gboolean video_egl_init_context( EGLNativeWindowType window, int format ) nkeynes@1287: { nkeynes@1287: EGLConfig config; nkeynes@1287: EGLint num_config; nkeynes@1287: const EGLint *attribute_list; nkeynes@1245: nkeynes@1245: if( format == COLFMT_RGB565 || format == COLFMT_BGRA1555 ) { nkeynes@1245: attribute_list = RGB565_attributes; nkeynes@1245: } else { nkeynes@1245: attribute_list = RGB888_attributes; nkeynes@1245: } nkeynes@1287: eglChooseConfig(display, attribute_list, &config, 1, &num_config); nkeynes@1245: nkeynes@1287: surface = eglCreateWindowSurface(display, config, window, NULL); nkeynes@1287: if( surface == EGL_NO_SURFACE ) { nkeynes@1287: /* Try alternate config in case of failure. This provides a workaround for nkeynes@1287: * the Nokia N900 where eglCreateWindowSurface fails when color attributes nkeynes@1287: * are specified. (bug report: https://bugs.maemo.org/show_bug.cgi?id=9335) nkeynes@1287: */ nkeynes@1287: eglChooseConfig(display, alt_attributes, &config, 1, &num_config); nkeynes@1287: surface = eglCreateWindowSurface(display, config, window, NULL); nkeynes@1245: nkeynes@1287: if( surface == EGL_NO_SURFACE ) { nkeynes@1287: logEGLError( "Unable to create EGL surface" ); nkeynes@1287: video_egl_shutdown(); nkeynes@1287: return FALSE; nkeynes@1287: } nkeynes@1287: } nkeynes@1245: nkeynes@1245: context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes); nkeynes@1245: if( context == EGL_NO_CONTEXT ) { nkeynes@1245: logEGLError( "Unable to create EGL context" ); nkeynes@1287: video_egl_shutdown(); nkeynes@1245: return FALSE; nkeynes@1245: } nkeynes@1245: nkeynes@1287: nkeynes@1287: nkeynes@1287: if( eglMakeCurrent( display, surface, surface, context ) == EGL_FALSE ) { nkeynes@1287: logEGLError( "Unable to make EGL context current" ); nkeynes@1287: video_egl_shutdown(); nkeynes@1245: return FALSE; nkeynes@1245: } nkeynes@1245: nkeynes@1287: return TRUE; nkeynes@1287: } nkeynes@1245: nkeynes@1287: gboolean video_egl_init_driver( display_driver_t driver ) nkeynes@1287: { nkeynes@1287: driver->swap_buffers = video_egl_swap_buffers; nkeynes@1287: driver->capabilities.depth_bits = 16; /* TODO: get from config info */ nkeynes@1287: if( !gl_init_driver(driver, TRUE) ) { nkeynes@1287: video_egl_shutdown(); nkeynes@1245: return FALSE; nkeynes@1245: } nkeynes@1258: fbo_created = TRUE; nkeynes@1245: return TRUE; nkeynes@1245: } nkeynes@1245: nkeynes@1287: void video_egl_shutdown() nkeynes@1245: { nkeynes@1245: if( fbo_created ) { nkeynes@1275: pvr2_shutdown_gl_context(); nkeynes@1245: gl_fbo_shutdown(); nkeynes@1245: fbo_created = FALSE; nkeynes@1245: } nkeynes@1245: eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); nkeynes@1245: if( surface != EGL_NO_SURFACE ) { nkeynes@1245: eglDestroySurface(display, surface); nkeynes@1245: surface = EGL_NO_SURFACE; nkeynes@1245: } nkeynes@1245: if( context != EGL_NO_CONTEXT ) { nkeynes@1245: eglDestroyContext(display, context); nkeynes@1245: context = EGL_NO_CONTEXT; nkeynes@1245: } nkeynes@1275: if( display != EGL_NO_DISPLAY ) { nkeynes@1275: eglTerminate(display); nkeynes@1275: display = EGL_NO_DISPLAY; nkeynes@1275: } nkeynes@1287: } nkeynes@1287: nkeynes@1287: gboolean video_egl_set_window(EGLNativeWindowType window, int width, int height, int format) nkeynes@1287: { nkeynes@1287: if( ! video_egl_init() || nkeynes@1287: ! video_egl_init_context( window, format ) || nkeynes@1287: ! video_egl_init_driver( &display_egl_driver ) ) { nkeynes@1287: return FALSE; nkeynes@1287: } nkeynes@1287: gl_set_video_size(width, height, 0); nkeynes@1287: pvr2_setup_gl_context(); nkeynes@1287: INFO( "Initialised EGL %d.%d", video_egl_major, video_egl_minor ); nkeynes@1287: return TRUE; nkeynes@1287: } nkeynes@1287: nkeynes@1287: void video_egl_clear_window() nkeynes@1287: { nkeynes@1287: video_egl_shutdown(); nkeynes@1275: INFO( "Terminated EGL" ); nkeynes@1245: } nkeynes@1245: nkeynes@1251: static void video_egl_swap_buffers() nkeynes@1251: { nkeynes@1251: eglSwapBuffers(display, surface); nkeynes@1251: } nkeynes@1245: nkeynes@1245: nkeynes@1245: /** nkeynes@1245: * Minimal init and shutdown. The real work is done from set_window nkeynes@1245: */ nkeynes@1245: struct display_driver display_egl_driver = { nkeynes@1245: "egl", N_("OpenGLES driver"), NULL, NULL, nkeynes@1245: NULL, NULL, NULL, nkeynes@1245: NULL, NULL, NULL, NULL, nkeynes@1245: gl_load_frame_buffer, gl_display_render_buffer, gl_display_blank, nkeynes@1251: video_egl_swap_buffers, gl_read_render_buffer, NULL, NULL nkeynes@1245: };