diff --git a/Makefile b/Makefile index 3feed6b7a5..89399186c1 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,7 @@ endif ifneq ($(findstring Linux,$(OS)),) LIBS += -lrt + JOYCONFIG_LIBS += -lrt OBJ += input/linuxraw_input.o input/linuxraw_joypad.o JOYCONFIG_OBJ += tools/linuxraw_joypad.o endif @@ -237,6 +238,10 @@ ifeq ($(HAVE_OPENGL), 1) OBJ += gfx/context/vc_egl_ctx.o endif + ifeq ($(HAVE_MALI_FBDEV), 1) + OBJ += gfx/context/mali_fbdev_ctx.o + endif + ifeq ($(HAVE_X11), 1) ifeq ($(HAVE_GLES), 0) OBJ += gfx/context/glx_ctx.o diff --git a/gfx/context/mali_fbdev_ctx.c b/gfx/context/mali_fbdev_ctx.c new file mode 100644 index 0000000000..841948fa82 --- /dev/null +++ b/gfx/context/mali_fbdev_ctx.c @@ -0,0 +1,281 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "../../driver.h" +#include "../../general.h" +#include "../gfx_common.h" +#include "../gl_common.h" + +#include +#include + +static EGLContext g_egl_ctx; +static EGLSurface g_egl_surf; +static EGLDisplay g_egl_dpy; +static EGLConfig g_config; +static bool g_resize; +static unsigned g_width, g_height; + +static volatile sig_atomic_t g_quit; +static void sighandler(int sig) +{ + (void)sig; + g_quit = 1; +} + +static void gfx_ctx_set_swap_interval(void *data, unsigned interval) +{ + (void)data; + if (g_egl_dpy) + eglSwapInterval(g_egl_dpy, interval); +} + +static void gfx_ctx_destroy(void *data) +{ + (void)data; + + if (g_egl_dpy != EGL_NO_DISPLAY) + { + if (g_egl_ctx != EGL_NO_CONTEXT) + { + glFlush(); + glFinish(); + } + + eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (g_egl_ctx != EGL_NO_CONTEXT) + eglDestroyContext(g_egl_dpy, g_egl_ctx); + if (g_egl_surf != EGL_NO_SURFACE) + eglDestroySurface(g_egl_dpy, g_egl_surf); + eglTerminate(g_egl_dpy); + } + + g_egl_dpy = EGL_NO_DISPLAY; + g_egl_surf = EGL_NO_SURFACE; + g_egl_ctx = EGL_NO_CONTEXT; + g_config = 0; + g_quit = 0; + g_resize = false; +} + +static void gfx_ctx_get_video_size(void *data, unsigned *width, unsigned *height) +{ + (void)data; + if (g_egl_dpy != EGL_NO_DISPLAY && g_egl_surf != EGL_NO_SURFACE) + { + *width = g_width; + *height = g_height; + } + else + { + *width = 0; + *height = 0; + } +} + +static bool gfx_ctx_init(void *data) +{ + (void)data; + + struct sigaction sa = {{0}}; + sa.sa_handler = sighandler; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + static const EGLint attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE + }; + EGLint num_config; + EGLint egl_version_major, egl_version_minor; + EGLint format; + + RARCH_LOG("[Mali fbdev]: Initializing context\n"); + + if ((g_egl_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) + { + RARCH_ERR("[Mali fbdev]: eglGetDisplay failed.\n"); + goto error; + } + + if (!eglInitialize(g_egl_dpy, &egl_version_major, &egl_version_minor)) + { + RARCH_ERR("[Mali fbdev]: eglInitialize failed.\n"); + goto error; + } + + RARCH_LOG("[Mali fbdev]: EGL version: %d.%d\n", egl_version_major, egl_version_minor); + + + if (!eglChooseConfig(g_egl_dpy, attribs, &g_config, 1, &num_config)) + { + RARCH_ERR("[Mali fbdev]: eglChooseConfig failed.\n"); + goto error; + } + + return true; + +error: + RARCH_ERR("[Mali fbdev]: EGL error: %d.\n", eglGetError()); + gfx_ctx_destroy(data); + return false; +} + +static void gfx_ctx_swap_buffers(void *data) +{ + (void)data; + eglSwapBuffers(g_egl_dpy, g_egl_surf); +} + +static void gfx_ctx_check_window(void *data, bool *quit, + bool *resize, unsigned *width, unsigned *height, unsigned frame_count) +{ + (void)frame_count; + + unsigned new_width, new_height; + gfx_ctx_get_video_size(data, &new_width, &new_height); + if (new_width != *width || new_height != *height) + { + *width = new_width; + *height = new_height; + *resize = true; + } + + *quit = g_quit; +} + +static void gfx_ctx_set_resize(void *data, unsigned width, unsigned height) +{ + (void)data; + (void)width; + (void)height; +} + +static void gfx_ctx_update_window_title(void *data) +{ + (void)data; + char buf[128], buf_fps[128]; + bool fps_draw = g_settings.fps_show; + gfx_get_fps(buf, sizeof(buf), fps_draw ? buf_fps : NULL, sizeof(buf_fps)); + + if (fps_draw) + msg_queue_push(g_extern.msg_queue, buf_fps, 1, 1); +} + +static bool gfx_ctx_set_video_mode(void *data, + unsigned width, unsigned height, + bool fullscreen) +{ + // Pick some arbitrary default. + if (!width || !fullscreen) + width = 1280; + if (!height || !fullscreen) + height = 720; + + g_width = width; + g_height = height; + + static const EGLint attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, // Use version 2, even for GLES3. + EGL_NONE + }; + + struct fbdev_window window = { width, height }; + if ((g_egl_surf = eglCreateWindowSurface(g_egl_dpy, g_config, &window, 0)) == EGL_NO_SURFACE) + { + RARCH_ERR("eglCreateWindowSurface failed.\n"); + goto error; + } + + if ((g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, 0, attribs)) == EGL_NO_CONTEXT) + { + RARCH_ERR("eglCreateContext failed.\n"); + goto error; + } + + if (!eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx)) + { + RARCH_ERR("eglMakeCurrent failed.\n"); + goto error; + } + + return true; + +error: + RARCH_ERR("[Mali fbdev]: EGL error: %d.\n", eglGetError()); + gfx_ctx_destroy(data); + return false; +} + +static void gfx_ctx_input_driver(void *data, const input_driver_t **input, void **input_data) +{ + (void)data; + *input = NULL; + *input_data = NULL; +} + +static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol) +{ + rarch_assert(sizeof(void*) == sizeof(void (*)(void))); + gfx_ctx_proc_t ret; + + void *sym__ = eglGetProcAddress(symbol); + memcpy(&ret, &sym__, sizeof(void*)); + + return ret; +} + +static bool gfx_ctx_bind_api(void *data, enum gfx_ctx_api api, unsigned major, unsigned minor) +{ + (void)data; + return api == GFX_CTX_OPENGL_ES_API; +} + +static bool gfx_ctx_has_focus(void *data) +{ + (void)data; + return true; +} + +const gfx_ctx_driver_t gfx_ctx_mali_fbdev = { + gfx_ctx_init, + gfx_ctx_destroy, + gfx_ctx_bind_api, + gfx_ctx_set_swap_interval, + gfx_ctx_set_video_mode, + gfx_ctx_get_video_size, + NULL, + gfx_ctx_update_window_title, + gfx_ctx_check_window, + gfx_ctx_set_resize, + gfx_ctx_has_focus, + gfx_ctx_swap_buffers, + gfx_ctx_input_driver, + gfx_ctx_get_proc_address, +#ifdef HAVE_EGL + NULL, + NULL, +#endif + NULL, + "mali-fbdev", +}; + diff --git a/gfx/gfx_context.c b/gfx/gfx_context.c index f917950772..5c586c6ea2 100644 --- a/gfx/gfx_context.c +++ b/gfx/gfx_context.c @@ -32,6 +32,9 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = { #if defined(HAVE_VIDEOCORE) &gfx_ctx_videocore, #endif +#if defined(HAVE_MALI_FBDEV) + &gfx_ctx_mali_fbdev, +#endif #if defined(_WIN32) && defined(HAVE_OPENGL) &gfx_ctx_wgl, #endif diff --git a/gfx/gfx_context.h b/gfx/gfx_context.h index 80cc01d94f..7005bb830b 100644 --- a/gfx/gfx_context.h +++ b/gfx/gfx_context.h @@ -113,6 +113,7 @@ extern const gfx_ctx_driver_t gfx_ctx_x_egl; extern const gfx_ctx_driver_t gfx_ctx_glx; extern const gfx_ctx_driver_t gfx_ctx_d3d9; extern const gfx_ctx_driver_t gfx_ctx_drm_egl; +extern const gfx_ctx_driver_t gfx_ctx_mali_fbdev; extern const gfx_ctx_driver_t gfx_ctx_android; extern const gfx_ctx_driver_t gfx_ctx_ps3; extern const gfx_ctx_driver_t gfx_ctx_wgl; diff --git a/gfx/gl.c b/gfx/gl.c index ba54a3eb3b..ac3a775e24 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -539,10 +539,13 @@ static void gl_create_fbo_textures(void *data) { RARCH_LOG("[GL]: FBO pass #%d is sRGB.\n", i); #ifdef HAVE_OPENGLES2 + // EXT defines are same as core GLES3 defines, but GLES3 variant requires different + // arguments ... glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, - GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, NULL); + gl->has_srgb_fbo_gles3 ? GL_RGBA : GL_SRGB_ALPHA_EXT, + GL_UNSIGNED_BYTE, NULL); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, @@ -1858,14 +1861,27 @@ static bool resolve_extensions(gl_t *gl) "32-bit path will require conversion.\n"); } - gl->support_unpack_row_length = false; - if (gl_query_extension(gl, "GL_EXT_unpack_subimage")) + bool gles3 = false; + const char *version = (const char*)glGetString(GL_VERSION); + unsigned gles_major = 0, gles_minor = 0; + // This format is mandated by GLES. + if (version && sscanf(version, "OpenGL ES %u.%u", &gles_major, &gles_minor) == 2 && gles_major >= 3) + { + RARCH_LOG("[GL]: GLES3 or newer detected. Auto-enabling some extensions.\n"); + gles3 = true; + } + + // GLES3 has unpack_subimage and sRGB in core. + + gl->support_unpack_row_length = gles3; + if (!gles3 && gl_query_extension(gl, "GL_EXT_unpack_subimage")) { RARCH_LOG("[GL]: Extension GL_EXT_unpack_subimage, can copy textures faster using UNPACK_ROW_LENGTH.\n"); gl->support_unpack_row_length = true; } // No extensions for float FBO currently. - gl->has_srgb_fbo = gl_query_extension(gl, "EXT_sRGB"); + gl->has_srgb_fbo = gles3 || gl_query_extension(gl, "EXT_sRGB"); + gl->has_srgb_fbo_gles3 = gles3; #else #ifdef HAVE_FBO // Float FBO is core in 3.2. diff --git a/gfx/gl_common.h b/gfx/gl_common.h index b67dc8d5c1..fa40a083a8 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -240,6 +240,7 @@ typedef struct gl bool hw_render_depth_init; bool has_fp_fbo; bool has_srgb_fbo; + bool has_srgb_fbo_gles3; #endif bool hw_render_use; bool shared_context_use; diff --git a/libretro-test-gl/Makefile b/libretro-test-gl/Makefile index 0389b42afe..4c4b175d1b 100644 --- a/libretro-test-gl/Makefile +++ b/libretro-test-gl/Makefile @@ -120,7 +120,7 @@ endif all: $(TARGET) $(TARGET): $(OBJECTS) - $(CC) $(fpic) $(SHARED) $(INCLUDES) -o $@ $(OBJECTS) $(LIBS) -lm + $(CC) $(fpic) $(SHARED) $(INCLUDES) -o $@ $(OBJECTS) $(LIBS) -lm $(EXTRA_GL_LIBS) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< diff --git a/qb/config.libs.sh b/qb/config.libs.sh index b95f917dba..a7aa4243ab 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -255,6 +255,10 @@ else fi check_pkgconf UDEV libudev +if [ "$HAVE_UDEV" = "no" ]; then + HAVE_UDEV=auto && check_lib UDEV "-ludev" + [ "$HAVE_UDEV" = "yes" ] && UDEV_LIBS=-ludev +fi check_lib STRL -lc strlcpy check_lib STRCASESTR -lc strcasestr @@ -268,6 +272,6 @@ add_define_make OS "$OS" # Creates config.mk and config.h. add_define_make GLOBAL_CONFIG_DIR "$GLOBAL_CONFIG_DIR" -VARS="RGUI LAKKA ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL LIMA OMAP GLES GLES3 VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG LIBXML2 ZLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE FREETYPE XKBCOMMON XVIDEO X11 XEXT XF86VM XINERAMA NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL STRCASESTR MMAP PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 BSV_MOVIE VIDEOCORE NEON FLOATHARD FLOATSOFTFP UDEV V4L2 AV_CHANNEL_LAYOUT" +VARS="RGUI LAKKA ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL LIMA OMAP GLES GLES3 VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG LIBXML2 ZLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE FREETYPE XKBCOMMON XVIDEO X11 XEXT XF86VM XINERAMA MALI_FBDEV NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL STRCASESTR MMAP PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 BSV_MOVIE VIDEOCORE NEON FLOATHARD FLOATSOFTFP UDEV V4L2 AV_CHANNEL_LAYOUT" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/qb/config.params.sh b/qb/config.params.sh index eea2b4ecc3..88d50a3cb7 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -13,6 +13,7 @@ HAVE_DYLIB=auto # Enable dynamic loading support HAVE_NETPLAY=auto # Enable netplay support HAVE_OPENGL=yes # Disable OpenGL support HAVE_GLES=no # Use GLESv2 instead of desktop GL +HAVE_MALI_FBDEV=no # Enable Mali fbdev context support HAVE_GLES3=no # Enable OpenGLES3 support HAVE_X11=auto # Disable everything X11. HAVE_LIMA=no # Enable Lima video support