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