nkeynes@1245 | 1 | /**
|
nkeynes@1245 | 2 | * $Id$
|
nkeynes@1245 | 3 | *
|
nkeynes@1245 | 4 | * Window management using EGL.
|
nkeynes@1245 | 5 | *
|
nkeynes@1245 | 6 | * Copyright (c) 2012 Nathan Keynes.
|
nkeynes@1245 | 7 | *
|
nkeynes@1245 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@1245 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@1245 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@1245 | 11 | * (at your option) any later version.
|
nkeynes@1245 | 12 | *
|
nkeynes@1245 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@1245 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@1245 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@1245 | 16 | * GNU General Public License for more details.
|
nkeynes@1245 | 17 | */
|
nkeynes@1245 | 18 |
|
nkeynes@1245 | 19 | #include "lxdream.h"
|
nkeynes@1245 | 20 | #include "display.h"
|
nkeynes@1245 | 21 | #include "video_egl.h"
|
nkeynes@1245 | 22 | #include "video_gl.h"
|
nkeynes@1245 | 23 | #include "pvr2/pvr2.h"
|
nkeynes@1245 | 24 | #include "pvr2/glutil.h"
|
nkeynes@1245 | 25 |
|
nkeynes@1245 | 26 | static const char *getEGLErrorString( EGLint code )
|
nkeynes@1245 | 27 | {
|
nkeynes@1245 | 28 | switch( code ) {
|
nkeynes@1245 | 29 | case EGL_SUCCESS: return "OK";
|
nkeynes@1245 | 30 | case EGL_NOT_INITIALIZED: return "EGL not initialized";
|
nkeynes@1245 | 31 | case EGL_BAD_ACCESS: return "Bad access";
|
nkeynes@1245 | 32 | case EGL_BAD_ALLOC: return "Allocation failed";
|
nkeynes@1245 | 33 | case EGL_BAD_ATTRIBUTE: return "Bad attribute";
|
nkeynes@1245 | 34 | case EGL_BAD_CONTEXT: return "Bad context";
|
nkeynes@1245 | 35 | case EGL_BAD_CONFIG: return "Bad config";
|
nkeynes@1245 | 36 | case EGL_BAD_CURRENT_SURFACE: return "Bad current surface";
|
nkeynes@1245 | 37 | case EGL_BAD_DISPLAY: return "Bad display";
|
nkeynes@1245 | 38 | case EGL_BAD_MATCH: return "Bad match";
|
nkeynes@1245 | 39 | case EGL_BAD_PARAMETER: return "Bad parameter";
|
nkeynes@1245 | 40 | case EGL_BAD_NATIVE_PIXMAP: return "Bad native pixmap";
|
nkeynes@1245 | 41 | case EGL_BAD_NATIVE_WINDOW: return "Bad native window";
|
nkeynes@1245 | 42 | default: return "Unknown error";
|
nkeynes@1245 | 43 | }
|
nkeynes@1245 | 44 | }
|
nkeynes@1245 | 45 |
|
nkeynes@1245 | 46 |
|
nkeynes@1245 | 47 | static void logEGLError(const char *msg)
|
nkeynes@1245 | 48 | {
|
nkeynes@1245 | 49 | EGLint error = eglGetError();
|
nkeynes@1245 | 50 | const char *errorStr = getEGLErrorString(error);
|
nkeynes@1245 | 51 |
|
nkeynes@1245 | 52 | ERROR( "%s: %s (%x)", msg, errorStr, error );
|
nkeynes@1245 | 53 | }
|
nkeynes@1245 | 54 |
|
nkeynes@1245 | 55 | static const EGLint RGB888_attributes[] = {
|
nkeynes@1245 | 56 | EGL_RED_SIZE, 8,
|
nkeynes@1245 | 57 | EGL_GREEN_SIZE, 8,
|
nkeynes@1245 | 58 | EGL_BLUE_SIZE, 8,
|
nkeynes@1245 | 59 | EGL_DEPTH_SIZE, 16,
|
nkeynes@1245 | 60 | EGL_STENCIL_SIZE, 8,
|
nkeynes@1245 | 61 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
nkeynes@1245 | 62 | EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,
|
nkeynes@1245 | 63 | EGL_NONE, EGL_NONE };
|
nkeynes@1245 | 64 |
|
nkeynes@1245 | 65 | static const EGLint RGB565_attributes[] = {
|
nkeynes@1245 | 66 | EGL_RED_SIZE, 5,
|
nkeynes@1245 | 67 | EGL_GREEN_SIZE, 6,
|
nkeynes@1245 | 68 | EGL_BLUE_SIZE, 5,
|
nkeynes@1245 | 69 | EGL_DEPTH_SIZE, 16,
|
nkeynes@1245 | 70 | EGL_STENCIL_SIZE, 8,
|
nkeynes@1245 | 71 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
nkeynes@1245 | 72 | EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,
|
nkeynes@1245 | 73 | EGL_NONE, EGL_NONE };
|
nkeynes@1245 | 74 |
|
nkeynes@1287 | 75 | static const EGLint alt_attributes[] = {
|
nkeynes@1287 | 76 | EGL_DEPTH_SIZE, 16,
|
nkeynes@1287 | 77 | EGL_STENCIL_SIZE, 8,
|
nkeynes@1287 | 78 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
nkeynes@1287 | 79 | EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,
|
nkeynes@1287 | 80 | EGL_NONE, EGL_NONE };
|
nkeynes@1287 | 81 |
|
nkeynes@1245 | 82 | static const EGLint context_attributes[] = {
|
nkeynes@1245 | 83 | EGL_CONTEXT_CLIENT_VERSION, 2,
|
nkeynes@1245 | 84 | EGL_NONE, EGL_NONE };
|
nkeynes@1245 | 85 |
|
nkeynes@1275 | 86 | static EGLDisplay display = EGL_NO_DISPLAY;
|
nkeynes@1245 | 87 | static EGLContext context = EGL_NO_CONTEXT;
|
nkeynes@1245 | 88 | static EGLSurface surface = EGL_NO_SURFACE;
|
nkeynes@1245 | 89 | static gboolean fbo_created = FALSE;
|
nkeynes@1245 | 90 |
|
nkeynes@1287 | 91 | static void video_egl_swap_buffers();
|
nkeynes@1287 | 92 | static int video_egl_major = 0, video_egl_minor = 0;
|
nkeynes@1287 | 93 |
|
nkeynes@1287 | 94 | gboolean video_egl_init()
|
nkeynes@1245 | 95 | {
|
nkeynes@1245 | 96 | display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
nkeynes@1287 | 97 | if( eglInitialize(display, &video_egl_major, &video_egl_minor) != EGL_TRUE ) {
|
nkeynes@1245 | 98 | logEGLError( "Unable to initialise EGL display" );
|
nkeynes@1245 | 99 | return FALSE;
|
nkeynes@1245 | 100 | }
|
nkeynes@1287 | 101 | return TRUE;
|
nkeynes@1287 | 102 | }
|
nkeynes@1287 | 103 |
|
nkeynes@1287 | 104 | gboolean video_egl_init_context( EGLNativeWindowType window, int format )
|
nkeynes@1287 | 105 | {
|
nkeynes@1287 | 106 | EGLConfig config;
|
nkeynes@1287 | 107 | EGLint num_config;
|
nkeynes@1287 | 108 | const EGLint *attribute_list;
|
nkeynes@1245 | 109 |
|
nkeynes@1245 | 110 | if( format == COLFMT_RGB565 || format == COLFMT_BGRA1555 ) {
|
nkeynes@1245 | 111 | attribute_list = RGB565_attributes;
|
nkeynes@1245 | 112 | } else {
|
nkeynes@1245 | 113 | attribute_list = RGB888_attributes;
|
nkeynes@1245 | 114 | }
|
nkeynes@1287 | 115 | eglChooseConfig(display, attribute_list, &config, 1, &num_config);
|
nkeynes@1245 | 116 |
|
nkeynes@1287 | 117 | surface = eglCreateWindowSurface(display, config, window, NULL);
|
nkeynes@1287 | 118 | if( surface == EGL_NO_SURFACE ) {
|
nkeynes@1287 | 119 | /* Try alternate config in case of failure. This provides a workaround for
|
nkeynes@1287 | 120 | * the Nokia N900 where eglCreateWindowSurface fails when color attributes
|
nkeynes@1287 | 121 | * are specified. (bug report: https://bugs.maemo.org/show_bug.cgi?id=9335)
|
nkeynes@1287 | 122 | */
|
nkeynes@1287 | 123 | eglChooseConfig(display, alt_attributes, &config, 1, &num_config);
|
nkeynes@1287 | 124 | surface = eglCreateWindowSurface(display, config, window, NULL);
|
nkeynes@1245 | 125 |
|
nkeynes@1287 | 126 | if( surface == EGL_NO_SURFACE ) {
|
nkeynes@1287 | 127 | logEGLError( "Unable to create EGL surface" );
|
nkeynes@1287 | 128 | video_egl_shutdown();
|
nkeynes@1287 | 129 | return FALSE;
|
nkeynes@1287 | 130 | }
|
nkeynes@1287 | 131 | }
|
nkeynes@1245 | 132 |
|
nkeynes@1245 | 133 | context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes);
|
nkeynes@1245 | 134 | if( context == EGL_NO_CONTEXT ) {
|
nkeynes@1245 | 135 | logEGLError( "Unable to create EGL context" );
|
nkeynes@1287 | 136 | video_egl_shutdown();
|
nkeynes@1245 | 137 | return FALSE;
|
nkeynes@1245 | 138 | }
|
nkeynes@1245 | 139 |
|
nkeynes@1287 | 140 |
|
nkeynes@1287 | 141 |
|
nkeynes@1287 | 142 | if( eglMakeCurrent( display, surface, surface, context ) == EGL_FALSE ) {
|
nkeynes@1287 | 143 | logEGLError( "Unable to make EGL context current" );
|
nkeynes@1287 | 144 | video_egl_shutdown();
|
nkeynes@1245 | 145 | return FALSE;
|
nkeynes@1245 | 146 | }
|
nkeynes@1245 | 147 |
|
nkeynes@1287 | 148 | return TRUE;
|
nkeynes@1287 | 149 | }
|
nkeynes@1245 | 150 |
|
nkeynes@1287 | 151 | gboolean video_egl_init_driver( display_driver_t driver )
|
nkeynes@1287 | 152 | {
|
nkeynes@1287 | 153 | driver->swap_buffers = video_egl_swap_buffers;
|
nkeynes@1287 | 154 | driver->capabilities.depth_bits = 16; /* TODO: get from config info */
|
nkeynes@1287 | 155 | if( !gl_init_driver(driver, TRUE) ) {
|
nkeynes@1287 | 156 | video_egl_shutdown();
|
nkeynes@1245 | 157 | return FALSE;
|
nkeynes@1245 | 158 | }
|
nkeynes@1258 | 159 | fbo_created = TRUE;
|
nkeynes@1245 | 160 | return TRUE;
|
nkeynes@1245 | 161 | }
|
nkeynes@1245 | 162 |
|
nkeynes@1287 | 163 | void video_egl_shutdown()
|
nkeynes@1245 | 164 | {
|
nkeynes@1245 | 165 | if( fbo_created ) {
|
nkeynes@1275 | 166 | pvr2_shutdown_gl_context();
|
nkeynes@1245 | 167 | gl_fbo_shutdown();
|
nkeynes@1245 | 168 | fbo_created = FALSE;
|
nkeynes@1245 | 169 | }
|
nkeynes@1245 | 170 | eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
nkeynes@1245 | 171 | if( surface != EGL_NO_SURFACE ) {
|
nkeynes@1245 | 172 | eglDestroySurface(display, surface);
|
nkeynes@1245 | 173 | surface = EGL_NO_SURFACE;
|
nkeynes@1245 | 174 | }
|
nkeynes@1245 | 175 | if( context != EGL_NO_CONTEXT ) {
|
nkeynes@1245 | 176 | eglDestroyContext(display, context);
|
nkeynes@1245 | 177 | context = EGL_NO_CONTEXT;
|
nkeynes@1245 | 178 | }
|
nkeynes@1275 | 179 | if( display != EGL_NO_DISPLAY ) {
|
nkeynes@1275 | 180 | eglTerminate(display);
|
nkeynes@1275 | 181 | display = EGL_NO_DISPLAY;
|
nkeynes@1275 | 182 | }
|
nkeynes@1287 | 183 | }
|
nkeynes@1287 | 184 |
|
nkeynes@1287 | 185 | gboolean video_egl_set_window(EGLNativeWindowType window, int width, int height, int format)
|
nkeynes@1287 | 186 | {
|
nkeynes@1287 | 187 | if( ! video_egl_init() ||
|
nkeynes@1287 | 188 | ! video_egl_init_context( window, format ) ||
|
nkeynes@1287 | 189 | ! video_egl_init_driver( &display_egl_driver ) ) {
|
nkeynes@1287 | 190 | return FALSE;
|
nkeynes@1287 | 191 | }
|
nkeynes@1287 | 192 | gl_set_video_size(width, height, 0);
|
nkeynes@1287 | 193 | pvr2_setup_gl_context();
|
nkeynes@1287 | 194 | INFO( "Initialised EGL %d.%d", video_egl_major, video_egl_minor );
|
nkeynes@1287 | 195 | return TRUE;
|
nkeynes@1287 | 196 | }
|
nkeynes@1287 | 197 |
|
nkeynes@1287 | 198 | void video_egl_clear_window()
|
nkeynes@1287 | 199 | {
|
nkeynes@1287 | 200 | video_egl_shutdown();
|
nkeynes@1275 | 201 | INFO( "Terminated EGL" );
|
nkeynes@1245 | 202 | }
|
nkeynes@1245 | 203 |
|
nkeynes@1251 | 204 | static void video_egl_swap_buffers()
|
nkeynes@1251 | 205 | {
|
nkeynes@1251 | 206 | eglSwapBuffers(display, surface);
|
nkeynes@1251 | 207 | }
|
nkeynes@1245 | 208 |
|
nkeynes@1245 | 209 |
|
nkeynes@1245 | 210 | /**
|
nkeynes@1245 | 211 | * Minimal init and shutdown. The real work is done from set_window
|
nkeynes@1245 | 212 | */
|
nkeynes@1245 | 213 | struct display_driver display_egl_driver = {
|
nkeynes@1245 | 214 | "egl", N_("OpenGLES driver"), NULL, NULL,
|
nkeynes@1245 | 215 | NULL, NULL, NULL,
|
nkeynes@1245 | 216 | NULL, NULL, NULL, NULL,
|
nkeynes@1245 | 217 | gl_load_frame_buffer, gl_display_render_buffer, gl_display_blank,
|
nkeynes@1251 | 218 | video_egl_swap_buffers, gl_read_render_buffer, NULL, NULL
|
nkeynes@1245 | 219 | };
|