diff --git a/Makefile b/Makefile index 0eaaa2fa2f..2980bc8b1d 100644 --- a/Makefile +++ b/Makefile @@ -124,7 +124,7 @@ ifeq ($(HAVE_COREAUDIO), 1) endif ifeq ($(HAVE_SDL), 1) - OBJ += gfx/sdl_gfx.o gfx/context/sdl_ctx.o input/sdl_input.o audio/sdl_audio.o fifo_buffer.o + OBJ += gfx/sdl_gfx.o input/sdl_input.o audio/sdl_audio.o fifo_buffer.o OBJ += gfx/scaler/scaler.o gfx/scaler/pixconv.o gfx/scaler/scaler_int.o gfx/scaler/filter.o DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC) LIBS += $(SDL_LIBS) @@ -143,10 +143,18 @@ endif ifeq ($(HAVE_OPENGL), 1) OBJ += gfx/gl.o gfx/fonts/freetype.o gfx/math/matrix.o + ifeq ($(OSX),1) LIBS += -framework OpenGL +else +ifeq ($(HAVE_GLES), 1) + LIBS += -lGL -lGLESv2 -lEGL + DEFINES += -DHAVE_OPENGLES -DHAVE_OPENGLES2 + OBJ += gfx/context/xegl_ctx.o else LIBS += -lGL + OBJ += gfx/context/sdl_ctx.o +endif endif endif endif diff --git a/gfx/context/sdl_ctx.c b/gfx/context/sdl_ctx.c index 8c57331442..90d519e645 100644 --- a/gfx/context/sdl_ctx.c +++ b/gfx/context/sdl_ctx.c @@ -185,16 +185,6 @@ void gfx_ctx_swap_buffers(void) SDL_GL_SwapBuffers(); } -bool gfx_ctx_key_pressed(int key) -{ - int num_keys; - Uint8 *keymap = SDL_GetKeyState(&num_keys); - if (key >= num_keys) - return false; - - return keymap[key]; -} - // 1.2 specific workaround for tiling WMs. In 1.3 we call GetSize directly, so we don't need to rely on // proper event handling (I hope). #if !defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_X11) diff --git a/gfx/context/xegl_ctx.c b/gfx/context/xegl_ctx.c new file mode 100644 index 0000000000..e10d162fa9 --- /dev/null +++ b/gfx/context/xegl_ctx.c @@ -0,0 +1,400 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * Copyright (C) 2011-2012 - Daniel De Matteis + * + * 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 . + */ + +// X/EGL context. Mostly used for testing GLES code paths. +// Should be its own file as it has lots of X11 stuff baked into it as well. + +#include "../../driver.h" +#include "../gfx_context.h" +#include "../gl_common.h" +#include "../gfx_common.h" + +#include +#include +#include +#include + +static Display *g_dpy; +static Window g_win; +static Colormap g_cmap; +static Atom g_quit_atom; +static bool g_has_focus; + +static EGLContext g_egl_ctx; +static EGLSurface g_egl_surf; +static EGLDisplay g_egl_dpy; +static EGLConfig g_config; + +static volatile sig_atomic_t g_quit; +static bool g_inited; +static unsigned g_interval; + +static void sighandler(int sig) +{ + (void)sig; + g_quit = 1; +} + +static void hide_mouse(void) +{ + Cursor no_ptr; + Pixmap bm_no; + XColor black, dummy; + Colormap colormap; + + static char bm_no_data[] = {0, 0, 0, 0, 0, 0, 0, 0}; + + colormap = DefaultColormap(g_dpy, DefaultScreen(g_dpy)); + if (!XAllocNamedColor(g_dpy, colormap, "black", &black, &dummy)) + return; + + bm_no = XCreateBitmapFromData(g_dpy, g_win, bm_no_data, 8, 8); + no_ptr = XCreatePixmapCursor(g_dpy, bm_no, bm_no, &black, &black, 0, 0); + + XDefineCursor(g_dpy, g_win, no_ptr); + XFreeCursor(g_dpy, no_ptr); + + if (bm_no != None) + XFreePixmap(g_dpy, bm_no); + + XFreeColors(g_dpy, colormap, &black.pixel, 1, 0); +} + +static Atom XA_NET_WM_STATE; +static Atom XA_NET_WM_STATE_FULLSCREEN; +#define XA_INIT(x) XA##x = XInternAtom(g_dpy, #x, False) +#define _NET_WM_STATE_ADD 1 +static void set_windowed_fullscreen(void) +{ + XA_INIT(_NET_WM_STATE); + XA_INIT(_NET_WM_STATE_FULLSCREEN); + + if (!XA_NET_WM_STATE || !XA_NET_WM_STATE_FULLSCREEN) + { + RARCH_ERR("[X/EGL]: Cannot set windowed fullscreen.\n"); + return; + } + + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.message_type = XA_NET_WM_STATE; + xev.xclient.window = g_win; + xev.xclient.format = 32; + xev.xclient.data.l[0] = _NET_WM_STATE_ADD; + xev.xclient.data.l[1] = XA_NET_WM_STATE_FULLSCREEN; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + XSendEvent(g_dpy, DefaultRootWindow(g_dpy), False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); +} + +void gfx_ctx_set_swap_interval(unsigned interval, bool inited) +{ + g_interval = interval; + if (inited) + eglSwapInterval(g_egl_dpy, g_interval); +} + +void gfx_ctx_check_window(bool *quit, + bool *resize, unsigned *width, unsigned *height, unsigned frame_count) +{ + (void)frame_count; + + unsigned new_width = *width, new_height = *height; + gfx_ctx_get_video_size(&new_width, &new_height); + + if (new_width != *width || new_height != *height) + { + *resize = true; + *width = new_width; + *height = new_height; + } + + XEvent event; + while (XPending(g_dpy)) + { + XNextEvent(g_dpy, &event); + switch (event.type) + { + case ClientMessage: + if ((Atom)event.xclient.data.l[0] == g_quit_atom) + g_quit = true; + break; + + case DestroyNotify: + g_quit = true; + break; + + case MapNotify: + g_has_focus = false; + break; + + case UnmapNotify: + g_has_focus = false; + break; + } + } + + *quit = g_quit; +} + +void gfx_ctx_swap_buffers(void) +{ + eglSwapBuffers(g_egl_dpy, g_egl_surf); +} + +void gfx_ctx_set_resize(unsigned width, unsigned height) +{ + (void)width; + (void)height; +} + +void gfx_ctx_update_window_title(bool reset) +{ + if (reset) + gfx_window_title_reset(); + + char buf[128]; + if (gfx_window_title(buf, sizeof(buf))) + XStoreName(g_dpy, g_win, buf); +} + +void gfx_ctx_get_video_size(unsigned *width, unsigned *height) +{ + if (!g_dpy || g_win == None) + { + *width = 0; + *height = 0; + } + else + { + XWindowAttributes target; + XGetWindowAttributes(g_dpy, g_win, &target); + + *width = target.width; + *height = target.height; + } +} + +bool gfx_ctx_init(void) +{ + if (g_inited) + return false; + + g_quit = 0; + + g_dpy = XOpenDisplay(NULL); + if (!g_dpy) + goto error; + + g_egl_dpy = eglGetDisplay(g_dpy); + if (!g_egl_dpy) + goto error; + + EGLint egl_major, egl_minor; + if (!eglInitialize(g_egl_dpy, &egl_major, &egl_minor)) + goto error; + + RARCH_LOG("[X/EGL]: EGL version: %d.%d\n", egl_major, egl_minor); + + const EGLint egl_attribs[] = { + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_DEPTH_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE, + }; + + EGLint num_configs; + if (!eglChooseConfig(g_egl_dpy, egl_attribs, &g_config, 1, &num_configs) + || num_configs == 0 || !g_config) + goto error; + + return true; + +error: + gfx_ctx_destroy(); + return false; +} + +bool gfx_ctx_set_video_mode( + unsigned width, unsigned height, + unsigned bits, bool fullscreen) +{ + (void)bits; + const EGLint egl_ctx_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE, + }; + + XVisualInfo *vi = NULL; + + EGLint vid; + if (!eglGetConfigAttrib(g_egl_dpy, g_config, EGL_NATIVE_VISUAL_ID, &vid)) + goto error; + + XVisualInfo template = {0}; + template.visualid = vid; + + EGLint num_visuals; + vi = XGetVisualInfo(g_dpy, VisualIDMask, &template, &num_visuals); + if (!vi) + goto error; + + XSetWindowAttributes swa = {0}; + swa.colormap = g_cmap = XCreateColormap(g_dpy, RootWindow(g_dpy, vi->screen), + vi->visual, AllocNone); + swa.event_mask = StructureNotifyMask; + + g_win = XCreateWindow(g_dpy, RootWindow(g_dpy, vi->screen), + 0, 0, width ? width : 200, height ? height : 200, 0, + vi->depth, InputOutput, vi->visual, + CWBorderPixel | CWColormap | CWEventMask, &swa); + XSetWindowBackground(g_dpy, g_win, 0); + + eglBindAPI(EGL_OPENGL_ES2_BIT); + + g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, EGL_NO_CONTEXT, egl_ctx_attribs); + if (!g_egl_ctx) + goto error; + + g_egl_surf = eglCreateWindowSurface(g_egl_dpy, g_config, g_win, NULL); + if (!g_egl_surf) + goto error; + + if (!eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx)) + goto error; + + gfx_ctx_update_window_title(true); + hide_mouse(); + XMapWindow(g_dpy, g_win); + + if (fullscreen) + set_windowed_fullscreen(); + + g_quit_atom = XInternAtom(g_dpy, "WM_DELETE_WINDOW", False); + if (g_quit_atom) + XSetWMProtocols(g_dpy, g_win, &g_quit_atom, 1); + + // Catch signals. + struct sigaction sa = { + .sa_handler = sighandler, + .sa_flags = SA_RESTART, + }; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + XFree(vi); + g_has_focus = true; + g_inited = true; + + return true; + +error: + if (vi) + XFree(vi); + + gfx_ctx_destroy(); + return false; +} + +void gfx_ctx_destroy(void) +{ + if (g_egl_dpy) + { + if (g_egl_ctx) + eglDestroyContext(g_egl_dpy, g_egl_ctx); + if (g_egl_surf) + eglDestroySurface(g_egl_dpy, g_egl_surf); + eglTerminate(g_egl_dpy); + } + + g_egl_ctx = NULL; + g_egl_surf = NULL; + g_egl_dpy = NULL; + + if (g_win) + { + XDestroyWindow(g_dpy, g_win); + g_win = None; + } + + if (g_cmap) + { + XFreeColormap(g_dpy, g_cmap); + g_cmap = None; + } + + if (g_dpy) + { + XCloseDisplay(g_dpy); + g_dpy = NULL; + } + + g_inited = false; +} + +void gfx_ctx_input_driver(const input_driver_t **input, void **input_data) +{ + void *xinput = input_x.init(); + *input = xinput ? &input_x : NULL; + *input_data = xinput; +} + +void gfx_ctx_set_projection(gl_t *gl, const struct gl_ortho *ortho, bool allow_rotate) +{ + // Calculate projection. + math_matrix proj; + matrix_ortho(&proj, ortho->left, ortho->right, + ortho->bottom, ortho->top, ortho->znear, ortho->zfar); + + if (allow_rotate) + { + math_matrix rot; + matrix_rotate_z(&rot, M_PI * gl->rotation / 180.0f); + matrix_multiply(&proj, &rot, &proj); + } + + gl->mvp = proj; +} + +bool gfx_ctx_window_has_focus(void) +{ + if (!g_inited) + return false; + + Window win; + int rev; + XGetInputFocus(g_dpy, &win, &rev); + + return win == g_win && g_has_focus; +} + +// Enforce void (*)(void) as it's not really legal to cast void* to fn-pointer. +// POSIX allows this, but strict C99 doesn't. +gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol) +{ + return eglGetProcAddress(symbol); +} + diff --git a/gfx/fonts/freetype.c b/gfx/fonts/freetype.c index e70d57a6cc..2ead4709b1 100644 --- a/gfx/fonts/freetype.c +++ b/gfx/fonts/freetype.c @@ -33,8 +33,8 @@ void gl_init_font(gl_t *gl, const char *font_path, unsigned font_size) { glGenTextures(1, &gl->font_tex); glBindTexture(GL_TEXTURE_2D, gl->font_tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); @@ -127,6 +127,12 @@ static void calculate_msg_geometry(const struct font_output *head, struct font_r rect->height = y_max - y_min; } +#ifdef HAVE_OPENGLES2 +#define INTENSITY_FORMAT GL_LUMINANCE +#else +#define INTENSITY_FORMAT GL_INTENSITY8 +#endif + static void adjust_power_of_two(gl_t *gl, struct font_rect *geom) { // Some systems really hate NPOT textures. @@ -139,15 +145,12 @@ static void adjust_power_of_two(gl_t *gl, struct font_rect *geom) memset(gl->font_tex_empty_buf, 0, geom->pot_width * geom->pot_height); glPixelStorei(GL_UNPACK_ALIGNMENT, 8); - glPixelStorei(GL_UNPACK_ROW_LENGTH, geom->pot_width); - glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY8, geom->pot_width, geom->pot_height, + glTexImage2D(GL_TEXTURE_2D, 0, INTENSITY_FORMAT, geom->pot_width, geom->pot_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, gl->font_tex_empty_buf); gl->font_tex_w = geom->pot_width; gl->font_tex_h = geom->pot_height; } - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } // Old style "blitting", so we can render all the fonts in one go. @@ -156,7 +159,6 @@ static void blit_fonts(gl_t *gl, const struct font_output *head, const struct fo { // Clear out earlier fonts. glPixelStorei(GL_UNPACK_ALIGNMENT, 8); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, gl->font_tex_w, gl->font_tex_h, GL_LUMINANCE, GL_UNSIGNED_BYTE, gl->font_tex_empty_buf); @@ -168,16 +170,37 @@ static void blit_fonts(gl_t *gl, const struct font_output *head, const struct fo int y = head->off_y - geom->y; y = gl->font_tex_h - head->height - y - 1; - glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(head->pitch)); + glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(head->width)); + +#ifdef GL_UNPACK_ROW_LENGTH glPixelStorei(GL_UNPACK_ROW_LENGTH, head->pitch); glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, head->width, head->height, GL_LUMINANCE, GL_UNSIGNED_BYTE, head->output); +#else + if (head->width == head->pitch) + { + glTexSubImage2D(GL_TEXTURE_2D, + 0, x, y, head->width, head->height, + GL_LUMINANCE, GL_UNSIGNED_BYTE, head->output); + } + else // Slower path + { + const uint8_t *src = head->output; + for (int i = 0; i < head->height; src += head->pitch, y++) + { + glTexSubImage2D(GL_TEXTURE_2D, + 0, x, y, head->width, 1, GL_LUMINANCE, GL_UNSIGNED_BYTE, src); + } + } +#endif head = head->next; } +#ifdef GL_UNPACK_ROW_LENGTH glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif } static void calculate_font_coords(gl_t *gl, diff --git a/gfx/gfx_context.h b/gfx/gfx_context.h index 5fe4016d6e..564863e571 100644 --- a/gfx/gfx_context.h +++ b/gfx/gfx_context.h @@ -49,8 +49,6 @@ void gfx_ctx_destroy(void); void gfx_ctx_get_video_size(unsigned *width, unsigned *height); void gfx_ctx_update_window_title(bool reset); -bool gfx_ctx_key_pressed(int key); - void gfx_ctx_check_window(bool *quit, bool *resize, unsigned *width, unsigned *height, unsigned frame_count); diff --git a/gfx/gl.c b/gfx/gl.c index ab5082fda8..5ba0fa7a22 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -44,23 +44,6 @@ #include "shader_glsl.h" #endif -// Platform specific workarounds/hacks. -#if defined(__CELLOS_LV2__) || defined(HAVE_OPENGLES) -#define NO_GL_READ_VIEWPORT -#endif - -#if defined(HAVE_OPENGL_MODERN) || defined(HAVE_OPENGLES2) -#define NO_GL_FF_VERTEX -#endif - -#if defined(HAVE_OPENGL_MODERN) || defined(HAVE_OPENGLES2) || defined(HAVE_PSGL) -#define NO_GL_FF_MATRIX -#endif - -#if defined(ANDROID) // TODO: Figure out exactly what. -#define NO_GL_CLAMP_TO_BORDER -#endif - // Used for the last pass when rendering to the back buffer. const GLfloat vertexes_flipped[] = { 0, 1, @@ -127,6 +110,13 @@ static bool load_fbo_proc(void) return pglGenFramebuffers && pglBindFramebuffer && pglFramebufferTexture2D && pglCheckFramebufferStatus && pglDeleteFramebuffers; } +#elif defined(HAVE_OPENGLES2) +#define pglGenFramebuffers glGenFramebuffers +#define pglBindFramebuffer glBindFramebuffer +#define pglFramebufferTexture2D glFramebufferTexture2D +#define pglCheckFramebufferStatus glCheckFramebufferStatus +#define pglDeleteFramebuffers glDeleteFramebuffers +static bool load_fbo_proc(void) { return true; } #elif defined(HAVE_OPENGLES) #define pglGenFramebuffers glGenFramebuffersOES #define pglBindFramebuffer glBindFramebufferOES @@ -136,7 +126,6 @@ static bool load_fbo_proc(void) #define GL_FRAMEBUFFER GL_FRAMEBUFFER_OES #define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT #define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES -#define glOrtho glOrthof static bool load_fbo_proc(void) { return true; } #else #define pglGenFramebuffers glGenFramebuffers @@ -830,9 +819,6 @@ static void gl_update_input_size(gl_t *gl, unsigned width, unsigned height, unsi #else glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * gl->base_size)); -#ifdef GL_UNPACK_ROW_LENGTH - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); -#endif glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type, gl->texture_fmt, gl->empty_buf); @@ -949,6 +935,40 @@ static inline void gl_copy_frame(gl_t *gl, const void *frame, unsigned width, un { glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * gl->base_size)); +#ifdef HAVE_OPENGLES2 // Have to perform pixel format conversions as well. (ARGB1555 => RGBA5551), (ARGB8888 => RGBA8888) :( + if (gl->base_size == 4) // ARGB8888 => RGBA8888 + { + const uint32_t *src = (const uint32_t*)frame; + uint32_t *dst = (uint32_t*)gl->conv_buffer; + unsigned pitch_width = pitch >> 2; + + // GL_RGBA + GL_UNSIGNED_BYTE apparently means in byte order, so go with little endian for now (ABGR). + for (unsigned h = 0; h < height; h++, dst += width, src += pitch_width) + { + for (unsigned w = 0; w < width; w++) + { + uint32_t col = src[w]; + dst[w] = ((col << 16) & 0x00ff0000) | ((col >> 16) & 0x000000ff) | (col & 0xff00ff00); + } + } + } + else // ARGB1555 => RGBA1555 + { + // Go 32-bit at once. + unsigned half_width = width >> 1; + const uint32_t *src = (const uint32_t*)frame; + uint32_t *dst = (uint32_t*)gl->conv_buffer; + unsigned pitch_width = pitch >> 2; + + for (unsigned h = 0; h < height; h++, dst += half_width, src += pitch_width) + for (unsigned w = 0; w < half_width; w++) + dst[w] = (src[w] << 1) & 0xfffefffe; + } + + glTexSubImage2D(GL_TEXTURE_2D, + 0, 0, 0, width, height, gl->texture_type, + gl->texture_fmt, gl->conv_buffer); +#else #ifdef GL_UNPACK_ROW_LENGTH glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / gl->base_size); glTexSubImage2D(GL_TEXTURE_2D, @@ -974,6 +994,7 @@ static inline void gl_copy_frame(gl_t *gl, const void *frame, unsigned width, un } } #endif +#endif } static void gl_init_textures(gl_t *gl) @@ -988,10 +1009,6 @@ static void gl_init_textures(gl_t *gl) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter); -#ifdef GL_UNPACK_ROW_LENGTH - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); -#endif - glTexImage2D(GL_TEXTURE_2D, 0, RARCH_GL_INTERNAL_FORMAT, gl->tex_w, gl->tex_h, 0, gl->texture_type, gl->texture_fmt, gl->empty_buf ? gl->empty_buf : NULL); @@ -1152,8 +1169,8 @@ static void gl_free(void *data) gfx_ctx_destroy(); - if (gl->empty_buf) - free(gl->empty_buf); + free(gl->empty_buf); + free(gl->conv_buffer); free(gl); } @@ -1176,18 +1193,16 @@ static bool resolve_extensions(gl_t *gl) #endif #ifdef NO_GL_CLAMP_TO_BORDER - // Doesn't support GL_CLAMP_TO_BORDER. NOTE: This will be a serious problem for some shaders. - // - // NOTE2: We still need to query if GL_CLAMP_TO_BORDER is supported even if compiling with - // OpenGL ES 1 because none of these defines are in any system headers except for what every - // Android GPU supports (which doesn't include GL_CLAMP_TO_BORDER) - move the underlying value - // for GL_CLAMP_TO_BORDER to some variable that we'll use here and query at gl_init if - // GL_CLAMP_TO_BORDER is available + // NOTE: This will be a serious problem for some shaders. gl->border_type = GL_CLAMP_TO_EDGE; #else gl->border_type = GL_CLAMP_TO_BORDER; #endif + const char *ext = (const char*)glGetString(GL_EXTENSIONS); + if (ext) + RARCH_LOG("[GL] Supported extensions: %s\n", ext); + #if defined(HAVE_PBO) RARCH_LOG("[GL]: Using PBOs.\n"); if (!gl_query_extension("GL_ARB_pixel_buffer_object")) @@ -1197,10 +1212,6 @@ static bool resolve_extensions(gl_t *gl) } #endif -#ifndef GL_UNPACK_ROW_LENGTH - RARCH_WARN("[GL]: GL_UNPACK_ROW_LENGTH is not defined. Texture uploads will possibly be slower than optimal.\n"); -#endif - return true; } @@ -1289,7 +1300,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo gl_init_fbo(gl, RARCH_SCALE_BASE * video->input_scale, RARCH_SCALE_BASE * video->input_scale); #endif - + gl->keep_aspect = video->force_aspect; // Apparently need to set viewport for passes when we aren't using FBOs. @@ -1308,7 +1319,10 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo gl->texture_fmt = video->rgb32 ? RARCH_GL_FORMAT32 : RARCH_GL_FORMAT16; gl->base_size = video->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); +#ifndef HAVE_OPENGLES glEnable(GL_TEXTURE_2D); +#endif + glDisable(GL_DEPTH_TEST); glDisable(GL_DITHER); glClearColor(0, 0, 0, 1); @@ -1337,6 +1351,17 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo // Empty buffer that we use to clear out the texture with on res change. gl->empty_buf = calloc(gl->tex_w * gl->tex_h, gl->base_size); + +#ifdef HAVE_OPENGLES2 + gl->conv_buffer = calloc(gl->tex_w * gl->tex_h, gl->base_size); + if (!gl->conv_buffer) + { + gfx_ctx_destroy(); + free(gl); + return NULL; + } +#endif + gl_init_textures(gl); for (unsigned i = 0; i < TEXTURES; i++) @@ -1347,18 +1372,17 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo for (unsigned i = 0; i < TEXTURES; i++) { - gl->prev_info[i].tex = gl->texture[(gl->tex_index - (i + 1)) & TEXTURES_MASK]; + gl->prev_info[i].tex = gl->texture[(gl->tex_index - (i + 1)) & TEXTURES_MASK]; gl->prev_info[i].input_size[0] = gl->tex_w; - gl->prev_info[i].tex_size[0] = gl->tex_w; + gl->prev_info[i].tex_size[0] = gl->tex_w; gl->prev_info[i].input_size[1] = gl->tex_h; - gl->prev_info[i].tex_size[1] = gl->tex_h; + gl->prev_info[i].tex_size[1] = gl->tex_h; memcpy(gl->prev_info[i].coord, tex_coords, sizeof(tex_coords)); } gfx_ctx_input_driver(input, input_data); gl_init_font(gl, g_settings.video.font_path, g_settings.video.font_size); - if (!gl_check_error()) { gfx_ctx_destroy(); diff --git a/gfx/gl_common.h b/gfx/gl_common.h index aaa4a41a2d..7226c37ac2 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -176,6 +176,7 @@ typedef struct gl GLuint tex_filter; void *empty_buf; + void *conv_buffer; unsigned frame_count; @@ -245,8 +246,8 @@ extern PFNGLACTIVETEXTUREPROC pglActiveTexture; #elif defined(HAVE_OPENGLES) #define RARCH_GL_INTERNAL_FORMAT GL_RGBA #define RARCH_GL_TEXTURE_TYPE GL_RGBA -#define RARCH_GL_FORMAT32 GL_UNSIGNED_INT -#define RARCH_GL_FORMAT16 GL_UNSIGNED_SHORT +#define RARCH_GL_FORMAT32 GL_UNSIGNED_BYTE +#define RARCH_GL_FORMAT16 GL_UNSIGNED_SHORT_5_5_5_1 #else #define RARCH_GL_INTERNAL_FORMAT GL_RGBA #define RARCH_GL_TEXTURE_TYPE GL_BGRA @@ -254,6 +255,27 @@ extern PFNGLACTIVETEXTUREPROC pglActiveTexture; #define RARCH_GL_FORMAT16 GL_UNSIGNED_SHORT_1_5_5_5_REV #endif +// Platform specific workarounds/hacks. +#if defined(__CELLOS_LV2__) || defined(HAVE_OPENGLES) +#define NO_GL_READ_VIEWPORT +#endif + +#if defined(HAVE_OPENGL_MODERN) || defined(HAVE_OPENGLES2) +#define NO_GL_FF_VERTEX +#endif + +#if defined(HAVE_OPENGL_MODERN) || defined(HAVE_OPENGLES2) || defined(HAVE_PSGL) +#define NO_GL_FF_MATRIX +#endif + +#if defined(HAVE_OPENGLES2) // TODO: Figure out exactly what. +#define NO_GL_CLAMP_TO_BORDER +#endif + +#if defined(HAVE_OPENGLES2) // It's an extension. Don't bother checking for it atm. +#undef GL_UNPACK_ROW_LENGTH +#endif + void gl_shader_use(unsigned index); void gl_set_projection(gl_t *gl, struct gl_ortho *ortho, bool allow_rotate); void gl_set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_full, bool allow_rotate); diff --git a/gfx/sdl_gfx.c b/gfx/sdl_gfx.c index 2543d3bc66..88c58a9959 100644 --- a/gfx/sdl_gfx.c +++ b/gfx/sdl_gfx.c @@ -292,7 +292,8 @@ static void *sdl_gfx_init(const video_info_t *video, const input_driver_t **inpu #ifdef HAVE_X11 RARCH_LOG("Suspending screensaver (X11).\n"); SDL_SysWMinfo wm_info; - if (gfx_ctx_get_wm_info(&wm_info)) + SDL_VERSION(&wm_info.version); + if (SDL_GetWMInfo(&wm_info) == 1) gfx_suspend_screensaver(wm_info.info.x11.window); else RARCH_ERR("Failed to suspend screensaver.\n"); diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 1095006f48..033f1b886c 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -60,6 +60,33 @@ #include "gl_common.h" #include "image.h" +#if defined(HAVE_OPENGLES2) || defined(HAVE_OPENGL_MODERN) +#define pglCreateProgram glCreateProgram +#define pglUseProgram glUseProgram +#define pglCreateShader glCreateShader +#define pglDeleteShader glDeleteShader +#define pglShaderSource glShaderSource +#define pglCompileShader glCompileShader +#define pglAttachShader glAttachShader +#define pglDetachShader glDetachShader +#define pglLinkProgram glLinkProgram +#define pglGetUniformLocation glGetUniformLocation +#define pglUniform1i glUniform1i +#define pglUniform1f glUniform1f +#define pglUniform2fv glUniform2fv +#define pglUniform4fv glUniform4fv +#define pglUniformMatrix4fv glUniformMatrix4fv +#define pglGetShaderiv glGetShaderiv +#define pglGetShaderInfoLog glGetShaderInfoLog +#define pglGetProgramiv glGetProgramiv +#define pglGetProgramInfoLog glGetProgramInfoLog +#define pglDeleteProgram glDeleteProgram +#define pglGetAttachedShaders glGetAttachedShaders +#define pglGetAttribLocation glGetAttribLocation +#define pglEnableVertexAttribArray glEnableVertexAttribArray +#define pglDisableVertexAttribArray glDisableVertexAttribArray +#define pglVertexAttribPointer glVertexAttribPointer +#else static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL; static PFNGLUSEPROGRAMPROC pglUseProgram = NULL; static PFNGLCREATESHADERPROC pglCreateShader = NULL; @@ -85,6 +112,13 @@ static PFNGLGETATTRIBLOCATIONPROC pglGetAttribLocation = NULL; static PFNGLENABLEVERTEXATTRIBARRAYPROC pglEnableVertexAttribArray = NULL; static PFNGLDISABLEVERTEXATTRIBARRAYPROC pglDisableVertexAttribArray = NULL; static PFNGLVERTEXATTRIBPOINTERPROC pglVertexAttribPointer = NULL; +#endif + +#ifdef HAVE_OPENGLES2 +#define BORDER_FUNC GL_CLAMP_TO_EDGE +#else +#define BORDER_FUNC GL_CLAMP_TO_BORDER +#endif #define MAX_PROGRAMS 16 #define MAX_TEXTURES 8 @@ -390,15 +424,17 @@ static bool get_texture_image(const char *shader_path, xmlNodePtr ptr) pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1); glBindTexture(GL_TEXTURE_2D, gl_teximage[gl_teximage_cnt]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, BORDER_FUNC); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, BORDER_FUNC); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexImage2D(GL_TEXTURE_2D, - 0, GL_RGBA, img.width, img.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, img.pixels); + 0, RARCH_GL_INTERNAL_FORMAT, + img.width, img.height, 0, RARCH_GL_TEXTURE_TYPE, GL_UNSIGNED_INT, img.pixels); pglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); @@ -893,17 +929,14 @@ static void gl_glsl_reset_attrib(void) // Platforms with broken get_proc_address. // Assume functions are available without proc_address. -#ifdef __PSL1GHT__ -#define LOAD_GL_SYM(SYM) pgl##SYM = gl##SYM; -#else #define LOAD_GL_SYM(SYM) if (!pgl##SYM) { \ gfx_ctx_proc_t sym = gfx_ctx_get_proc_address("gl" #SYM); \ memcpy(&(pgl##SYM), &sym, sizeof(sym)); \ } -#endif bool gl_glsl_init(const char *path) { +#if !defined(HAVE_OPENGLES2) && !defined(HAVE_OPENGL_MODERN) // Load shader functions. LOAD_GL_SYM(CreateProgram); LOAD_GL_SYM(UseProgram); @@ -946,6 +979,7 @@ bool gl_glsl_init(const char *path) RARCH_ERR("GLSL shaders aren't supported by your OpenGL driver.\n"); return false; } +#endif #ifdef HAVE_XML struct shader_program progs[MAX_PROGRAMS]; diff --git a/input/sdl_input.c b/input/sdl_input.c index 299da4a223..468af8b226 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -182,7 +182,15 @@ static void *sdl_input_init(void) static bool sdl_key_pressed(int key) { - return key < RETROK_LAST && gfx_ctx_key_pressed(keysym_lut[key]); + if (key >= RETROK_LAST) + return false; + + int num_keys; + Uint8 *keymap = SDL_GetKeyState(&num_keys); + if (key >= num_keys) + return false; + + return keymap[key]; } #ifndef HAVE_DINPUT diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 195a6ed0b2..03b6e05da2 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -145,6 +145,6 @@ check_pkgconf PYTHON python3 add_define_make OS "$OS" # Creates config.mk and config.h. -VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO PBO STRL 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 SINC FIXED_POINT BSV_MOVIE RPI" +VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL GLES DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO PBO STRL 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 SINC FIXED_POINT BSV_MOVIE RPI" 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 88271cead6..25d1e05816 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -6,6 +6,7 @@ HAVE_DYLIB=auto # Enable dynamic loading support HAVE_NETPLAY=auto # Enable netplay support HAVE_CONFIGFILE=yes # Disable support for config file HAVE_OPENGL=yes # Disable OpenGL support +HAVE_GLES=no # Use X/EGL instead of desktop GL (experimental) HAVE_CG=auto # Enable Cg shader support HAVE_XML=auto # Enable bSNES-style XML shader support HAVE_FBO=auto # Enable render-to-texture (FBO) support