irixxxx / picodrive

Fast MegaDrive/MegaCD/32X emulator
Other
55 stars 25 forks source link

Feature request: Linux OpenGL, or fully scalable SDL, support #70

Closed noabody closed 2 years ago

noabody commented 2 years ago

Would you consider supporting Linux x86_64 as a build platform with the OpenGL back-end or a fully scalable SDL implementation?

The GUI works well until the scaled aspect ratio becomes greater than 4:3? At that point the menu tilts.

Here are the patches I've been using to force OpenGL. Please keep in mind that I kludged this together because I'm NOT a programmer. I really don't know how best to implement this.

The last patch is to use an SDL Hat switch on the joystick. notaz had some concerns but I don't really know if that is an issue. See 3

diff a/picodrive/Makefile b/picodrive/Makefile
index 2903a68..bad1aa5 100644
--- a/picodrive/Makefile    1969-12-31 17:00:00.000000000 -0700
+++ b/picodrive/Makefile    1969-12-31 17:00:00.000000000 -0700
@@ -164,7 +164,8 @@ OBJS += platform/libpicofe/gl_platform.o
 USE_FRONTEND = 1
 endif
 ifeq "$(PLATFORM)" "generic"
-CFLAGS += -DSDL_OVERLAY_2X -DSDL_BUFFER_3X
+CFLAGS += -DHAVE_GLES
+LDFLAGS += -ldl -lpthread -lGL -lEGL
 OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME
 ifeq "$(use_inputmap)" "1"
 OBJS += platform/common/plat_sdl.o platform/opendingux/inputmap.o
@@ -173,6 +174,7 @@ OBJS += platform/common/plat_sdl.o platform/common/inputmap_kbd.o
 endif
 OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o
 OBJS += platform/libpicofe/plat_dummy.o platform/libpicofe/linux/plat.o
+OBJS += platform/libpicofe/gl.o platform/libpicofe/gl_platform.o
 USE_FRONTEND = 1
 endif
 ifeq "$(PLATFORM)" "pandora"
diff a/picodrive/platform/libpicofe/gl.c b/picodrive/platform/libpicofe/gl.c
--- a/picodrive/platform/libpicofe/gl.c 1969-12-31 17:00:00.000000000 -0700
+++ b/picodrive/platform/libpicofe/gl.c 1969-12-31 17:00:00.000000000 -0700
@@ -2,7 +2,11 @@
 #include <stdlib.h>

 #include <EGL/egl.h>
-#include <GLES/gl.h>
+#if defined(__arm__)
+  #include <GLES/gl.h>
+#else
+  #include <GL/gl.h>
+#endif
 #include "gl_platform.h"
 #include "gl.h"
diff a/picodrive/platform/libpicofe/gl_platform.c b/picodrive/platform/libpicofe/gl_platform.c
--- a/picodrive/platform/libpicofe/gl_platform.c    1969-12-31 17:00:00.000000000 -0700
+++ b/picodrive/platform/libpicofe/gl_platform.c    1969-12-31 17:00:00.000000000 -0700
@@ -1,7 +1,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <EGL/egl.h>
-#include <GLES/gl.h>
+#if defined(__arm__)
+  #include <GLES/gl.h>
+#else
+  #include <GL/gl.h>
+#endif

 #include "gl.h"
 #include "gl_platform.h"
diff a/picodrive/platform/libpicofe/in_sdl.c  b/picodrive/platform/libpicofe/in_sdl.c
--- a/picodrive/platform/libpicofe/in_sdl.c 1969-12-31 17:00:00.000000000 -0700
+++ b/picodrive/platform/libpicofe/in_sdl.c 1969-12-31 17:00:00.000000000 -0700
@@ -300,6 +300,34 @@
        }
        break;

+   case SDL_JOYHATMOTION:
+       if (event->jhat.which != state->joy_id)
+           return -2;
+       if (event->jhat.value == SDL_HAT_CENTERED) {
+           kc = state->axis_keydown[event->jhat.hat];
+           state->axis_keydown[event->jhat.hat] = 0;
+           ret = 1;
+       }
+       else if (event->jhat.value & SDL_HAT_UP || event->jhat.value & SDL_HAT_LEFT) {
+           kc = state->axis_keydown[event->jhat.hat];
+           if (kc)
+               update_keystate(state->keystate, kc, 0);
+           kc = (event->jhat.value & SDL_HAT_UP) ? SDLK_UP : SDLK_LEFT;
+           state->axis_keydown[event->jhat.hat] = kc;
+           down = 1;
+           ret = 1;
+       }
+       else if (event->jhat.value & SDL_HAT_DOWN || event->jhat.value & SDL_HAT_RIGHT) {
+           kc = state->axis_keydown[event->jhat.hat];
+           if (kc)
+               update_keystate(state->keystate, kc, 0);
+           kc = (event->jhat.value & SDL_HAT_DOWN) ? SDLK_DOWN : SDLK_RIGHT;
+           state->axis_keydown[event->jhat.hat] = kc;
+           down = 1;
+           ret = 1;
+       }
+       break;
+
    case SDL_JOYBUTTONDOWN:
    case SDL_JOYBUTTONUP:
        if (event->jbutton.which != state->joy_id)
noabody commented 2 years ago

picodrive

irixxxx commented 2 years ago

What exactly have you done to get this display? I can't reproduce this neither with SDL window nor with overlay.

The GL support is unfortunately quite hardcoded for Raspi. I have already tried to get this running on a more general base, but ultimately decided it would need SDL2 to do this in a more portable way. That however is some work for dark windy fall days ;-)

noabody commented 2 years ago

I hear you on the SDL2. Let me elaborate on my situation. I'm building picodrive on Arch-like Manjaro 21.2.6 x86_64 Linux, not on an Arm based micro platform. I have been able to package it using my personal, and someone unorthodox, patch.

I can build picodrive for Linux x86_64 without the patch, but it is forced into an SDL 2x window, because the Makefile considers my platform to be generic. I can modify generic to use opengl, as shown in the patches, but we really need a separate platform. Maybe the code would look something like this?

ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","__linux__"))
CFLAGS += -DHAVE_GLES
LDFLAGS += -ldl -lpthread -lGL -lEGL
OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME
OBJS += platform/common/plat_sdl.o platform/common/inputmap_kbd.o
OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o
OBJS += platform/libpicofe/plat_dummy.o platform/libpicofe/linux/plat.o
OBJS += platform/libpicofe/gl.o platform/libpicofe/gl_platform.o
USE_FRONTEND = 1
endif

Anyway, once I build it for Arch Linux and opengl, Picodrive works quite well. Without the patch, the GUI is not resizable and it isn't useful. My interest in the project is as a tinkerer. That's who noabody is.

irixxxx commented 2 years ago

I'll see if I can find that old patch. It had all the basics covered, but there were some problems left (can't remember which ones though).

irixxxx commented 2 years ago

Here's my old patch. It's ugly, and I gave up on it because I couldn't control the redraw stuff properly. The picodrive architecture just wasn't designed with supporting a proper window system in mind. gl.txt

noabody commented 2 years ago

I get the gist of what you're doing there. The target is GLES for ARM. In Arch Linux for x86_64 we don't have GLES includes, only GLES2 GLES3, so #include <GLES/gl.h> would fail.

I don't pretend to understand the rest, other than I see where you're manipulating the SDL window size. I can't comment on whether the code is ugly, but then I'm a fan of programmers so I always think code is beautiful.

My thought was to add a new PLATFORM target of _x8664. Wouldn't checking for the ARM architecture in gl.c/gl_platform.c be the easiest way to set includes?

With these suggestions I simply ./configure --platform=x86_64 then make

diff --git a/Makefile b/Makefile
index 9d0c986a..43840839 100644
--- a/Makefile
+++ b/Makefile
@@ -175,6 +175,20 @@ OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o
 OBJS += platform/libpicofe/plat_dummy.o platform/libpicofe/linux/plat.o
 USE_FRONTEND = 1
 endif
+ifeq "$(PLATFORM)" "x86_64"
+CFLAGS += -DHAVE_GLES -DSDL_OVERLAY_2X -DSDL_BUFFER_3X
+LDFLAGS += -ldl -lpthread -lGL -lEGL
+OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME
+ifeq "$(use_inputmap)" "1"
+OBJS += platform/common/plat_sdl.o platform/opendingux/inputmap.o
+else
+OBJS += platform/common/plat_sdl.o platform/common/inputmap_kbd.o
+endif
+OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o
+OBJS += platform/libpicofe/plat_dummy.o platform/libpicofe/linux/plat.o
+OBJS += platform/libpicofe/gl.o platform/libpicofe/gl_platform.o
+USE_FRONTEND = 1
+endif
 ifeq "$(PLATFORM)" "pandora"
 platform/common/menu_pico.o: CFLAGS += -DPANDORA
 platform/libpicofe/linux/plat.o: CFLAGS += -DPANDORA
diff --git a/configure b/configure
index c67b9a0c..6678f2ea 100755
--- a/configure
+++ b/configure
@@ -39,7 +39,7 @@ check_define()
 # "" means "autodetect".

 # TODO this is annoyingly messy. should have platform and device
-platform_list="generic pandora gp2x wiz caanoo dingux retrofw gcw0 rg350 opendingux miyoo rpi1 rpi2 psp"
+platform_list="generic pandora gp2x wiz caanoo dingux retrofw gcw0 rg350 opendingux miyoo rpi1 rpi2 psp x86_64"
 platform="generic"
 sound_driver_list="oss alsa sdl"
 sound_drivers=""
@@ -138,6 +138,8 @@ set_platform()
     MFLAGS="-march=allegrex"
     ARCH=mipsel
     ;;
+  x86_64)
+    ;;
   *)
     fail "unsupported platform: $platform"
     ;;
@@ -258,7 +260,7 @@ arm*)
 esac

 case "$platform" in
-rpi1 | rpi2 | generic | opendingux | miyoo)
+rpi1 | rpi2 | generic | opendingux | miyoo | x86_64)
   need_sdl="yes"
   ;;
 esac

Along with the modifications to the submodule libpicofe:

diff --git a/gl.c b/gl.c
index 0e18c45..3a08569 100644
--- a/gl.c
+++ b/gl.c
@@ -2,7 +2,11 @@
 #include <stdlib.h>

 #include <EGL/egl.h>
-#include <GLES/gl.h>
+#if defined(__arm__)
+  #include <GLES/gl.h>
+#else
+  #include <GL/gl.h>
+#endif
 #include "gl_platform.h"
 #include "gl.h"

diff --git a/gl_platform.c b/gl_platform.c
index d5f0c20..0f03dbc 100644
--- a/gl_platform.c
+++ b/gl_platform.c
@@ -1,7 +1,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <EGL/egl.h>
-#include <GLES/gl.h>
+#if defined(__arm__)
+  #include <GLES/gl.h>
+#else
+  #include <GL/gl.h>
+#endif

 #include "gl.h"
 #include "gl_platform.h"
diff --git a/in_sdl.c b/in_sdl.c
index a84c781..8cf53ac 100644
--- a/in_sdl.c
+++ b/in_sdl.c
@@ -321,6 +321,34 @@ static int handle_joy_event(struct in_sdl_state *state, SDL_Event *event,
        }
        break;

+   case SDL_JOYHATMOTION:
+       if (event->jhat.which != state->joy_id)
+           return -2;
+       if (event->jhat.value == SDL_HAT_CENTERED) {
+           kc = state->axis_keydown[event->jhat.hat];
+           state->axis_keydown[event->jhat.hat] = 0;
+           ret = 1;
+       }
+       else if (event->jhat.value & SDL_HAT_UP || event->jhat.value & SDL_HAT_LEFT) {
+           kc = state->axis_keydown[event->jhat.hat];
+           if (kc)
+               update_keystate(state->keystate, kc, 0);
+           kc = (event->jhat.value & SDL_HAT_UP) ? SDLK_UP : SDLK_LEFT;
+           state->axis_keydown[event->jhat.hat] = kc;
+           down = 1;
+           ret = 1;
+       }
+       else if (event->jhat.value & SDL_HAT_DOWN || event->jhat.value & SDL_HAT_RIGHT) {
+           kc = state->axis_keydown[event->jhat.hat];
+           if (kc)
+               update_keystate(state->keystate, kc, 0);
+           kc = (event->jhat.value & SDL_HAT_DOWN) ? SDLK_DOWN : SDLK_RIGHT;
+           state->axis_keydown[event->jhat.hat] = kc;
+           down = 1;
+           ret = 1;
+       }
+       break;
+
    case SDL_JOYBUTTONDOWN:
    case SDL_JOYBUTTONUP:
        if (event->jbutton.which != state->joy_id)
irixxxx commented 2 years ago

I was compiling this code under x86_64 xubuntu. I just had to install libgles1. It's also available under ubuntu armv8, hence I think it's rather standard fare. Is this not available on your distro?

noabody commented 2 years ago

Regrettably, Arch Linux does not have a package with "include/GLES/gl.h". The file gl.h only exists in location "include/GL/gl.h" as part of package libglvnd. Another Arch user tried to get a patch going, on a bug request, but it doesn't seem to have gotten anywhere.

I wasn't aware of that so it is an issue for me, as a packager of a sort (perl -pi -e 's|(GL)ES(/gl\.h)|\1\2|gi' platform/libpicofe/gl{,_platform}.c). Your patch compiled after I fixed the include location, set LDFLAGS += -lGL -lEGL -lGLESv2, and ./configure --with-sdl-gles=yes then make.

I can resize the window but the menu contents go blank and this is printed in the command console: GL error: glTexSubImage2D 501. A running game scales fine but the menu is just a black screen.

If you want to have me test out code, I'd be happy to. As it is, I definitely think this is a "rainy day" project and Arch Linux is an anomaly even for x86_64.

irixxxx commented 2 years ago

Please try again with current master. It works on current Xubuntu, but I guess there's more checking for specific libraries needed. I'd welcome hints which libs I need to check for in configure.

noabody commented 2 years ago

Thanks for your hard work! I'm sure Xubuntu is fine. Arch Linux is always madly updating to the latest thing. They broke the EGL code with an update in July so I only get an error in the console eglSwapBuffers failed 300d. I looked into it a bit and the older libraries in Ubuntu variants work.

I gave up on it thinking SDL is probably the way to go but, of course, it segfaults. gdb has this output:

plat_sdl: overlay: fmt 59565955, planes: 1, pitch: 1280, hw: 1

Thread 1 "PicoDrive" received signal SIGSEGV, Segmentation fault.
0x000055555556858e in plat_sdl_change_video_mode (w=640, h=480, force=force@entry=1) at platform/libpicofe/plat_sdl.c:110
110       if ((long)plat_sdl_overlay->pixels[0] & 3)

Basically, the code base in Picodrive is just a bit out of reach of newer Arch distributions but it works fine on the intended targets.

irixxxx commented 2 years ago

AFAIK the GLES code in Picodrive was specifically designed for Pi 1 and 2. SDL 1 isn't supporting GL directly, hence it's basically a kludge. Lucky me that it worked more or less out of the box under Xubuntu. The crash you observe is in the SDL overlay code. Maybe it misses locking the surface before accessing the pixels in the surface. Try adding SDL_LockYUVOverlay(plat_sdl_overlay);in the line before the if, and SDL_UnlockYUVOverlay(plat_sdl_overlay);in the line after plat_sdl_overlay_clear() and see if that works better for you.

noabody commented 2 years ago

Yeah that fixed it right up. I might fool around with the EGL code a little - which means I might track down similar examples, based on the error, and see if I can apply their fixes to this code base ;). It might be a surface locking issue also affecting the eglSwapBuffers.

That however is related to newer libraries deciding that features should be implemented in a slightly different way - in the name of progress ;P.

We should probably close this feature request. Short of a re-implementation to future proof some of the GL code, it is doing exactly what it's supposed to do.

noabody commented 2 years ago

After some research, I'm thinking Arch's Mesa EGL gets fouled up by having multiple platforms X11/DRM/Wayland. Reading c - EGLDisplay on GBM suggests a different mechanism for interacting with these than what is used in gl.c.

irixxxx commented 2 years ago

I did my best to improve handling of redraw events, but with the current design of input handling this is limited. It should basically work, but there are still some situation where window redrawing is done incorrectly after resizing. libpicofe and picodrive input handling would need a redesign to handle this correctly.

However, this has improved window resizing considerably. Last commit for this was 54f74b8. Please check this for sdl window and sdl overlay modes.

noabody commented 2 years ago

Strictly speaking, Change Options > Display Options > Video Output Mode > SDL doesn't scale but Video Overlay/[2x] both seem to work fine and are exactly what I was looking for.

Performance seems good in full-size window on monitor set to 1920x1080.

Excellent work!

irixxxx commented 2 years ago

SDL window mode is more for the lot of linux-based portable handhelds, from gp2x upto the current lot of mips based ones like gkd350h and rg350. Those normally have the sdl window scaled by hardware to the screen, but no hardware scaling based overlay modes. This reflects in the default mode, desktop has it at overlay while portable usually has it at window. I'll have more bug fixing on my backlog, but for now this is done.