From 41e087433af2239963b9d0e42e52ad222cca7ca7 Mon Sep 17 00:00:00 2001 From: Carlos Aragones <> Date: Fri, 18 Sep 2020 15:05:39 +0200 Subject: [PATCH] use OpenGL legacy API for X11 (faster than draw XImages) --- CMakeLists.txt | 13 ++++ src/gl/MiniFB_GL.c | 159 ++++++++++++++++++++++++++++++++------- src/gl/MiniFB_GL.h | 4 +- src/windows/WinMiniFB.c | 6 +- src/x11/WindowData_X11.h | 10 ++- src/x11/X11MiniFB.c | 39 +++++++++- 6 files changed, 192 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94ee4f0..baf9985 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,9 @@ if(APPLE AND NOT IOS) option(USE_METAL_API "Build the project using metal API code" ON) elseif(UNIX) option(USE_WAYLAND_API "Build the project using wayland API code" OFF) + if(NOT USE_WAYLAND_API) + option(USE_OPENGL_API "Build the project using OpenGL API code" ON) + endif() elseif(WIN32) option(USE_OPENGL_API "Build the project using OpenGL API code" ON) endif() @@ -174,6 +177,11 @@ elseif(UNIX) if(USE_WAYLAND_API) list(APPEND SrcLib ${SrcWayland}) else() + if(USE_OPENGL_API) + list(APPEND SrcLib ${SrcGL}) + + add_definitions(-DUSE_OPENGL_API) + endif() list(APPEND SrcLib ${SrcX11}) endif() @@ -216,6 +224,11 @@ elseif(UNIX) target_link_libraries(minifb "-lX11" ) + if(USE_OPENGL_API) + target_link_libraries(minifb + "-lGL" + ) + endif() endif() elseif(WIN32) diff --git a/src/gl/MiniFB_GL.c b/src/gl/MiniFB_GL.c index f8e0f76..1456a5d 100644 --- a/src/gl/MiniFB_GL.c +++ b/src/gl/MiniFB_GL.c @@ -3,8 +3,13 @@ #include "MiniFB_GL.h" #if defined(_WIN32) || defined(WIN32) #include + #include +#elif defined(linux) + #include + #include + #include #endif -#include +#include #include //#define kUse_Clean_UP @@ -15,22 +20,8 @@ #endif //------------------------------------- -void setup_pixel_format(HDC hDC); - -//------------------------------------- -void -create_GL_context(SWindowData *window_data) { - SWindowData_Win *window_data_win; - - window_data_win = (SWindowData_Win *) window_data->specific; - setup_pixel_format(window_data_win->hdc); - window_data_win->hGLRC = wglCreateContext(window_data_win->hdc); - wglMakeCurrent(window_data_win->hdc, window_data_win->hGLRC); - init_GL(window_data); -} - -//------------------------------------- -void +#if defined(_WIN32) || defined(WIN32) +bool setup_pixel_format(HDC hDC) { int pixelFormat; @@ -58,23 +49,122 @@ setup_pixel_format(HDC hDC) { pixelFormat = ChoosePixelFormat(hDC, &pfd); if (pixelFormat == 0) { MessageBox(WindowFromDC(hDC), "ChoosePixelFormat failed.", "Error", MB_ICONERROR | MB_OK); - exit(1); + return false; } if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) { MessageBox(WindowFromDC(hDC), "SetPixelFormat failed.", "Error", MB_ICONERROR | MB_OK); - exit(1); + return false; } + + return true; +} +#endif + +//------------------------------------- +bool +create_GL_context(SWindowData *window_data) { +#if defined(_WIN32) || defined(WIN32) + SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific; + + if(setup_pixel_format(window_data_win->hdc) == false) + return false; + window_data_win->hGLRC = wglCreateContext(window_data_win->hdc); + wglMakeCurrent(window_data_win->hdc, window_data_win->hGLRC); + init_GL(window_data); + + return true; + +#elif defined(linux) + SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific; + + GLint majorGLX, minorGLX = 0; + glXQueryVersion(window_data_x11->display, &majorGLX, &minorGLX); + if (majorGLX <= 1 && minorGLX < 2) { + fprintf(stderr, "GLX 1.2 or greater is required.\n"); + XCloseDisplay(window_data_x11->display); + return false; + } + else { + //fprintf(stdout, "GLX version: %d.%d\n", majorGLX, minorGLX); + } + + GLint glxAttribs[] = { + GLX_RGBA, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_SAMPLE_BUFFERS, 0, + GLX_SAMPLES, 0, + None + }; + XVisualInfo* visualInfo = glXChooseVisual(window_data_x11->display, window_data_x11->screen, glxAttribs); + if (visualInfo == 0) { + fprintf(stderr, "Could not create correct visual window.\n"); + XCloseDisplay(window_data_x11->display); + return false; + } + + window_data_x11->context = glXCreateContext(window_data_x11->display, visualInfo, NULL, GL_TRUE); + glXMakeCurrent(window_data_x11->display, window_data_x11->window, window_data_x11->context); + + //fprintf(stdout, "GL Vendor: %s\n", glGetString(GL_VENDOR)); + //fprintf(stdout, "GL Renderer: %s\n", glGetString(GL_RENDERER)); + //fprintf(stdout, "GL Version: %s\n", glGetString(GL_VERSION)); + //fprintf(stdout, "GL Shading Language: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); + + init_GL(window_data); + + return true; +#endif } -#define TEXTURE0 0x84C0 +//------------------------------------- +void +destroy_GL_context(SWindowData *window_data) { +#if defined(_WIN32) || defined(WIN32) + + SWindowData_Win *window_data_win = (SWindowData_Win *) window_data->specific; + if (window_data_win->hGLRC) { + wglMakeCurrent(NULL, NULL); + wglDeleteContext(window_data_win->hGLRC); + window_data_win->hGLRC = 0; + } + +#elif defined(linux) + + SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific; + glXDestroyContext(window_data_x11->display, window_data_x11->context); + +#endif +} + +//------------------------------------- +#define TEXTURE0 0x84C0 // [ Core in gl 1.3, gles1 1.0, gles2 2.0, glsc2 2.0, Provided by GL_ARB_multitexture (gl) ] +#define RGB 0x1907 // [ Core in gl 1.0, gles1 1.0, gles2 2.0, glsc2 2.0 ] +#define RGBA 0x1908 // [ Core in gl 1.0, gles1 1.0, gles2 2.0, glsc2 2.0 ] +#define BGR 0x80E0 // [ Core in gl 1.2 ] +#define BGRA 0x80E1 // [ Core in gl 1.2, Provided by GL_ARB_vertex_array_bgra (gl|glcore) ] + //------------------------------------- void init_GL(SWindowData *window_data) { - SWindowData_Win *window_data_win; +#if defined(_WIN32) || defined(WIN32) + + SWindowData_Win *window_data_ex = (SWindowData_Win *) window_data->specific; + +#elif defined(linux) + + SWindowData_X11 *window_data_ex = (SWindowData_X11 *) window_data->specific; + +#endif - window_data_win = (SWindowData_Win *) window_data->specific; glViewport(0, 0, window_data->window_width, window_data->window_height); @@ -90,9 +180,9 @@ init_GL(SWindowData *window_data) { glEnable(GL_TEXTURE_2D); - glGenTextures(1, &window_data_win->text_id); + glGenTextures(1, &window_data_ex->text_id); //glActiveTexture(TEXTURE0); - glBindTexture(GL_TEXTURE_2D, window_data_win->text_id); + glBindTexture(GL_TEXTURE_2D, window_data_ex->text_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -123,10 +213,19 @@ resize_GL(SWindowData *window_data) { //------------------------------------- void redraw_GL(SWindowData *window_data, const void *pixels) { - float x, y, w, h; - SWindowData_Win *window_data_ex; +#if defined(_WIN32) || defined(WIN32) - window_data_ex = (SWindowData_Win *) window_data->specific; + SWindowData_Win *window_data_ex = (SWindowData_Win *) window_data->specific; + GLenum format = GL_RGBA; + +#elif defined(linux) + + SWindowData_X11 *window_data_ex = (SWindowData_X11 *) window_data->specific; + GLenum format = GL_BGRA; // for some reason the colors are inverted on X11 + +#endif + + float x, y, w, h; x = (float) window_data->dst_offset_x; y = (float) window_data->dst_offset_y; @@ -150,7 +249,7 @@ redraw_GL(SWindowData *window_data, const void *pixels) { glClear(GL_COLOR_BUFFER_BIT); UseCleanUp(glBindTexture(GL_TEXTURE_2D, window_data_ex->text_id)); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_data->buffer_width, window_data->buffer_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_data->buffer_width, window_data->buffer_height, 0, format, GL_UNSIGNED_BYTE, pixels); //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, window_data->buffer_width, window_data->buffer_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); UseCleanUp(glEnableClientState(GL_VERTEX_ARRAY)); @@ -164,7 +263,11 @@ redraw_GL(SWindowData *window_data, const void *pixels) { UseCleanUp(glDisableClientState(GL_VERTEX_ARRAY)); UseCleanUp(glBindTexture(GL_TEXTURE_2D, 0)); +#if defined(_WIN32) || defined(WIN32) SwapBuffers(window_data_ex->hdc); +#elif defined(linux) + glXSwapBuffers(window_data_ex->display, window_data_ex->window); +#endif } #endif \ No newline at end of file diff --git a/src/gl/MiniFB_GL.h b/src/gl/MiniFB_GL.h index 2526aed..77d11b6 100644 --- a/src/gl/MiniFB_GL.h +++ b/src/gl/MiniFB_GL.h @@ -3,8 +3,10 @@ #if defined(USE_OPENGL_API) #include "WindowData.h" + #include - void create_GL_context(SWindowData *window_data); + bool create_GL_context(SWindowData *window_data); + void destroy_GL_context(SWindowData *window_data); void init_GL(SWindowData *window_data); void redraw_GL(SWindowData *window_data, const void *pixels); void resize_GL(SWindowData *window_data); diff --git a/src/windows/WinMiniFB.c b/src/windows/WinMiniFB.c index 834aa96..1517c04 100644 --- a/src/windows/WinMiniFB.c +++ b/src/windows/WinMiniFB.c @@ -508,11 +508,7 @@ destroy_window_data(SWindowData *window_data) { window_data_win->bitmapInfo = 0x0; } #else - if (window_data_win->hGLRC) { - wglMakeCurrent(NULL, NULL); - wglDeleteContext(window_data_win->hGLRC); - window_data_win->hGLRC = 0; - } + destroy_GL_context(window_data); #endif if (window_data_win->window != 0 && window_data_win->hdc != 0) { diff --git a/src/x11/WindowData_X11.h b/src/x11/WindowData_X11.h index 7e7d926..646ec35 100644 --- a/src/x11/WindowData_X11.h +++ b/src/x11/WindowData_X11.h @@ -3,7 +3,9 @@ #include #include #include - +#if defined(USE_OPENGL_API) +#include +#endif typedef struct { Window window; @@ -11,12 +13,16 @@ typedef struct { Display *display; int screen; GC gc; +#if defined(USE_OPENGL_API) + GLXContext context; + uint32_t text_id; +#else XImage *image; - void *image_buffer; XImage *image_scaler; uint32_t image_scaler_width; uint32_t image_scaler_height; +#endif struct mfb_timer *timer; } SWindowData_X11; diff --git a/src/x11/X11MiniFB.c b/src/x11/X11MiniFB.c index 7265c30..a7c4f8c 100644 --- a/src/x11/X11MiniFB.c +++ b/src/x11/X11MiniFB.c @@ -13,6 +13,10 @@ #include "WindowData.h" #include "WindowData_X11.h" +#if defined(USE_OPENGL_API) + #include +#endif + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void init_keycodes(SWindowData_X11 *window_data_x11); @@ -176,6 +180,15 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) sizeHints.max_height = height; } +#if defined(USE_OPENGL_API) + if(create_GL_context(window_data) == false) { + return 0x0; + } + +#else + window_data_x11->image = XCreateImage(window_data_x11->display, CopyFromParent, depth, ZPixmap, 0, 0x0, width, height, 32, width * 4); +#endif + XSetWMNormalHints(window_data_x11->display, window_data_x11->window, &sizeHints); XClearWindow(window_data_x11->display, window_data_x11->window); XMapRaised(window_data_x11->display, window_data_x11->window); @@ -183,8 +196,6 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) window_data_x11->gc = DefaultGC(window_data_x11->display, window_data_x11->screen); - window_data_x11->image = XCreateImage(window_data_x11->display, CopyFromParent, depth, ZPixmap, 0, 0x0, width, height, 32, width * 4); - window_data_x11->timer = mfb_timer_create(); mfb_set_keyboard_callback((struct mfb_window *) window_data, keyboard_default); @@ -264,6 +275,9 @@ processEvent(SWindowData *window_data, XEvent *event) { window_data->window_height = event->xconfigure.height; resize_dst(window_data, event->xconfigure.width, event->xconfigure.height); +#if defined(USE_OPENGL_API) + resize_GL(window_data); +#else SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific; if(window_data_x11->image_scaler != 0x0) { window_data_x11->image_scaler->data = 0x0; @@ -272,8 +286,8 @@ processEvent(SWindowData *window_data, XEvent *event) { window_data_x11->image_scaler_width = 0; window_data_x11->image_scaler_height = 0; } - XClearWindow(window_data_x11->display, window_data_x11->window); +#endif kCall(resize_func, window_data->window_width, window_data->window_height); } break; @@ -330,16 +344,22 @@ mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned return STATE_INVALID_BUFFER; } +#if !defined(USE_OPENGL_API) SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific; bool different_size = false; +#endif if(window_data->buffer_width != width || window_data->buffer_height != height) { window_data->buffer_width = width; window_data->buffer_stride = width * 4; window_data->buffer_height = height; +#if !defined(USE_OPENGL_API) different_size = true; +#endif } +#if !defined(USE_OPENGL_API) + if (different_size || window_data->buffer_width != window_data->dst_width || window_data->buffer_height != window_data->dst_height) { if (window_data_x11->image_scaler_width != window_data->dst_width || window_data_x11->image_scaler_height != window_data->dst_height) { if (window_data_x11->image_scaler != 0x0) { @@ -372,6 +392,13 @@ mfb_update_ex(struct mfb_window *window, void *buffer, unsigned width, unsigned XPutImage(window_data_x11->display, window_data_x11->window, window_data_x11->gc, window_data_x11->image, 0, 0, window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height); } XFlush(window_data_x11->display); + +#else + + redraw_GL(window_data, buffer); + +#endif + processEvents(window_data); return STATE_OK; @@ -453,12 +480,18 @@ destroy_window_data(SWindowData *window_data) { if (window_data != 0x0) { if (window_data->specific != 0x0) { SWindowData_X11 *window_data_x11 = (SWindowData_X11 *) window_data->specific; + +#if defined(USE_OPENGL_API) + destroy_GL_context(window_data); +#else if (window_data_x11->image != 0x0) { window_data_x11->image->data = 0x0; XDestroyImage(window_data_x11->image); XDestroyWindow(window_data_x11->display, window_data_x11->window); XCloseDisplay(window_data_x11->display); } +#endif + mfb_timer_destroy(window_data_x11->timer); memset(window_data_x11, 0, sizeof(SWindowData_X11)); free(window_data_x11);