raysan5 / raylib

A simple and easy-to-use library to enjoy videogames programming
http://www.raylib.com
zlib License
22.63k stars 2.27k forks source link

[core] RPI4 without X11 #1096

Closed dginu85 closed 4 years ago

dginu85 commented 4 years ago

In init device core.c

define RPI4

Initrpi4();

else

Normal init rpi 2 3 bla bla

endif

////////////////////// RPI4InitDevice.h // gcc -o drm-gbm drm-gbm-mod.c -ldrm -lgbm -lEGL -lGL -I/usr/include/libdrm

//---------------------------------------------------------------------- //-------- Trying to get OpenGL ES screen on RPi4 without X //-------- based on drm-gbm https://github.com/eyelash/tutorials/blob/master/drm-gbm.c //-------- and kmscube https://github.com/robclark/kmscube //-------- pik33@o2.pl //----------------------------------------------------------------------

include

include

include

include <EGL/egl.h>

include <GL/gl.h>

include

include

include

include

define EXIT(msg) { fputs (msg, stderr); exit (EXIT_FAILURE); }

// global variables declarations

static int device; static drmModeRes resources; static drmModeConnector connector; static uint32_t connector_id; static drmModeEncoder encoder; static drmModeModeInfo mode_info; static drmModeCrtc crtc; static struct gbm_device gbm_device; static EGLDisplay display; static EGLContext context; static struct gbm_surface gbm_surface; static EGLSurface egl_surface; EGLConfig config; EGLint num_config; EGLint count=0; EGLConfig *configs; int config_index; int i;

static struct gbm_bo *previous_bo = NULL; static uint32_t previous_fb;

static EGLint attributes[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 0, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE };

static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };

struct gbm_bo *bo;
uint32_t handle; uint32_t pitch; int32_t fb; uint64_t modifier;

static drmModeConnector find_connector (drmModeRes resources) {

for (i=0; icount_connectors; i++) { drmModeConnector *connector = drmModeGetConnector (device, resources->connectors[i]); if (connector->connection == DRM_MODE_CONNECTED) {return connector;} drmModeFreeConnector (connector); } return NULL; // if no connector found }

static drmModeEncoder find_encoder (drmModeRes resources, drmModeConnector *connector) {

if (connector->encoder_id) {return drmModeGetEncoder (device, connector->encoder_id);} return NULL; // if no encoder found }

static void swap_buffers () {

eglSwapBuffers (display, egl_surface); bo = gbm_surface_lock_front_buffer (gbm_surface); handle = gbm_bo_get_handle (bo).u32; pitch = gbm_bo_get_stride (bo); drmModeAddFB (device, mode_info.hdisplay, mode_info.vdisplay, 24, 32, pitch, handle, &fb); drmModeSetCrtc (device, crtc->crtc_id, fb, 0, 0, &connector_id, 1, &mode_info); if (previous_bo) { drmModeRmFB (device, previous_fb); gbm_surface_release_buffer (gbm_surface, previous_bo); } previous_bo = bo; previous_fb = fb; }

static void draw (float progress) {

glClearColor (1.0f-progress, progress, 0.0, 1.0); glClear (GL_COLOR_BUFFER_BIT); swap_buffers (); }

static int match_config_to_visual(EGLDisplay egl_display, EGLint visual_id, EGLConfig *configs, int count) {

EGLint id; for (i = 0; i < count; ++i) { if (!eglGetConfigAttrib(egl_display, configs[i], EGL_NATIVE_VISUAL_ID,&id)) continue; if (id == visual_id) return i; } return -1; }

Bool initrpi4 () {

device = open ("/dev/dri/card1", O_RDWR); resources = drmModeGetResources (device); connector = find_connector (resources); connector_id = connector->connector_id; mode_info = connector->modes[0]; encoder = find_encoder (resources, connector); crtc = drmModeGetCrtc (device, encoder->crtc_id); drmModeFreeEncoder (encoder); drmModeFreeConnector (connector); drmModeFreeResources (resources); gbm_device = gbm_create_device (device); gbm_surface = gbm_surface_create (gbm_device, mode_info.hdisplay, mode_info.vdisplay, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT|GBM_BO_USE_RENDERING); display = eglGetDisplay (gbm_device); eglInitialize (display, NULL ,NULL); eglBindAPI (EGL_OPENGL_API); eglGetConfigs(display, NULL, 0, &count); configs = malloc(count sizeof configs); eglChooseConfig (display, attributes, configs, count, &num_config); config_index = match_config_to_visual(display,GBM_FORMAT_XRGB8888,configs,num_config); context = eglCreateContext (display, configs[config_index], EGL_NO_CONTEXT, context_attribs); egl_surface = eglCreateWindowSurface (display, configs[config_index], gbm_surface, NULL); free(configs); eglMakeCurrent (display, egl_surface, egl_surface, context);

Return here...

drmModeSetCrtc (device, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, &connector_id, 1, &crtc->mode); drmModeFreeCrtc (crtc); if (previous_bo) { drmModeRmFB (device, previous_fb); gbm_surface_release_buffer (gbm_surface, previous_bo); } eglDestroySurface (display, egl_surface); gbm_surface_destroy (gbm_surface); eglDestroyContext (display, context); eglTerminate (display); gbm_device_destroy (gbm_device);

close (device); return 0; }

dginu85 commented 4 years ago

image

raysan5 commented 4 years ago

@dginu85 sorry, what is the issue about? Maybe related to https://github.com/raysan5/raylib/issues/1041

dginu85 commented 4 years ago

image

dginu85 commented 4 years ago

i build with "make PLATFORM=PLATFORM_DESKTOP GRAPHICS=GRAPHICS_API_OPENGL_21"

raysan5 commented 4 years ago

Ok, last time I tested, about 2 weeks ago, it worked ok RPI4 desktop mode with OpenGL 2.1... I'll try to review it at some point, I have no RPI4 for testing.

dginu85 commented 4 years ago

I try this for test I make file "EGL_FIX." ////////////////////////////////////////////////////////////

#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#include <EGL/egl.h>
#include <GL/gl.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define EXIT(msg) { fputs (msg, stderr); exit (EXIT_FAILURE); }

// global variables declarations

static int device;
static drmModeRes *resources;
static drmModeConnector *connector;
static uint32_t connector_id;
static drmModeEncoder *encoder;
static drmModeModeInfo mode_info;
static drmModeCrtc *crtc;
static struct gbm_device *gbm_device;
static EGLDisplay display;
static EGLContext context;
static struct gbm_surface *gbm_surface;
static EGLSurface egl_surface;
EGLConfig config;
EGLint num_config;
EGLint count = 0;
EGLConfig *configs;
int config_index;
int i;

static struct gbm_bo *previous_bo = NULL;
static uint32_t previous_fb;       

static EGLint attributes[] = {
    EGL_SURFACE_TYPE,
    EGL_WINDOW_BIT,
    EGL_RED_SIZE,
    8,
    EGL_GREEN_SIZE,
    8,
    EGL_BLUE_SIZE,
    8,
    EGL_ALPHA_SIZE,
    0,
    EGL_RENDERABLE_TYPE,
    EGL_OPENGL_ES2_BIT,
    EGL_NONE
};

static const EGLint context_attribs[] = {
    EGL_CONTEXT_CLIENT_VERSION,
    2,
    EGL_NONE
};

struct gbm_bo *bo;  
uint32_t handle;
uint32_t pitch;
int32_t fb;
uint64_t modifier;

static drmModeConnector *find_connector(drmModeRes *resources) {

    for (i = 0; i < resources->count_connectors; i++) {
        drmModeConnector *connector = drmModeGetConnector(device, resources->connectors[i]);
        if (connector->connection == DRM_MODE_CONNECTED) {return connector;}
        drmModeFreeConnector(connector);
    }
    return NULL; // if no connector found
}

static drmModeEncoder *find_encoder(drmModeRes *resources, drmModeConnector *connector) {

    if (connector->encoder_id) {return drmModeGetEncoder(device, connector->encoder_id);}
    return NULL; // if no encoder found
}

static void swap_buffers() {

    eglSwapBuffers(display, egl_surface);
    bo = gbm_surface_lock_front_buffer(gbm_surface);
    handle = gbm_bo_get_handle(bo).u32;
    pitch = gbm_bo_get_stride(bo);
    drmModeAddFB(device, mode_info.hdisplay, mode_info.vdisplay, 24, 32, pitch, handle, &fb);
    drmModeSetCrtc(device, crtc->crtc_id, fb, 0, 0, &connector_id, 1, &mode_info);
    if (previous_bo) {
        drmModeRmFB(device, previous_fb);
        gbm_surface_release_buffer(gbm_surface, previous_bo);
    }
    previous_bo = bo;
    previous_fb = fb;
}

static void draw(float progress) {

    glClearColor(1.0f - progress, progress, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    swap_buffers();
}

static int match_config_to_visual(EGLDisplay egl_display, EGLint visual_id, EGLConfig *configs, int count) {

    EGLint id;
    for (i = 0; i < count; ++i) {
        if (!eglGetConfigAttrib(egl_display, configs[i], EGL_NATIVE_VISUAL_ID, &id)) continue;
        if (id == visual_id) return i;
    }
    return -1;
}

static int IniTRpi4() {

    device = open("/dev/dri/card1", O_RDWR);
    resources = drmModeGetResources(device);
    connector = find_connector(resources);
    connector_id = connector->connector_id;
    mode_info = connector->modes[0];
    encoder = find_encoder(resources, connector);
    crtc = drmModeGetCrtc(device, encoder->crtc_id);
    drmModeFreeEncoder(encoder);
    drmModeFreeConnector(connector);
    drmModeFreeResources(resources);
    gbm_device = gbm_create_device(device);
    gbm_surface = gbm_surface_create(gbm_device, mode_info.hdisplay, mode_info.vdisplay, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
    display = eglGetDisplay(gbm_device);
    eglInitialize(display, NULL, NULL);
    eglBindAPI(EGL_OPENGL_API);
    eglGetConfigs(display, NULL, 0, &count);
    configs = malloc(count * sizeof *configs);
    eglChooseConfig(display, attributes, configs, count, &num_config);
    config_index = match_config_to_visual(display, GBM_FORMAT_XRGB8888, configs, num_config);
    context = eglCreateContext(display, configs[config_index], EGL_NO_CONTEXT, context_attribs);
    egl_surface = eglCreateWindowSurface(display, configs[config_index], gbm_surface, NULL);
    free(configs);
    eglMakeCurrent(display, egl_surface, egl_surface, context);
    swap_buffers();
    return 1;

}

static void CloseSurface()
{
    close(device);
    drmModeSetCrtc(device, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, &connector_id, 1, &crtc->mode);
    drmModeFreeCrtc(crtc);
    if (previous_bo) {
        drmModeRmFB(device, previous_fb);
        gbm_surface_release_buffer(gbm_surface, previous_bo);
    }
    eglDestroySurface(display, egl_surface);
    gbm_surface_destroy(gbm_surface);
    eglDestroyContext(display, context);
    eglTerminate(display);
    gbm_device_destroy(gbm_device);

}

In core.c ////////////////////////////////////////////////////////////////////////////////////////

static bool InitGraphicsDevice(int width, int height)
{
    CORE.Window.screen.width = width;            // User desired width
    CORE.Window.screen.height = height;          // User desired height
    CORE.Window.display.width = width;            // User desired width
    CORE.Window.display.height = height;          // User desired height
    CORE.Window.render.width = width;            // User desired width
    CORE.Window.render.height = height;          // User desired height
    CORE.Window.screenScale = MatrixIdentity();  // No draw scaling required by default
    CORE.Window.fullscreen = true;

#pragma region EGLFIX

    IniTRpi4();

    CORE.Window.config = configs;
    CORE.Window.device = display;
    CORE.Window.surface = egl_surface;
    CORE.Window.context = context;
    SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);

#pragma endregion EGLFIX

        TRACELOG(LOG_INFO, "Display device initialized successfully");
        TRACELOG(LOG_INFO, "Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
        TRACELOG(LOG_INFO, "Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
        TRACELOG(LOG_INFO, "Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
        TRACELOG(LOG_INFO, "Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);

    rlglInit(CORE.Window.screen.width, CORE.Window.screen.height);
    SetupViewport(CORE.Window.screen.width, CORE.Window.screen.height);
    CORE.Window.currentFbo.width = CORE.Window.screen.width;
    CORE.Window.currentFbo.height = CORE.Window.screen.height;
    ClearBackground(RAYWHITE);      
    CORE.Window.ready = true;
    return true;
}

Result work but paint only BLACK why?

dginu85 commented 4 years ago

OK FIX void BeginDrawing(void) { //add this swap_buffers(); work perfect :-) }

raysan5 commented 4 years ago

Just for reference, here another RPI4-native implementation for raylib: https://github.com/jonlidgard/raylib/tree/rpi4

alanbork commented 4 years ago

Is there any chance of a single binary that would work on all versions of the PI? My understanding is to make a native CLI binary that works on the 4 you need to

"make PLATFORM=PLATFORM_RPI RPI_VERSION=RPI_VERSION_4"

precluding use on earlier PIs.

raysan5 commented 4 years ago

@alanbork Actually those changes have not been integrated on raylib, at the moment raylib does not support RaspberryPi 4 officially.

Unfortunately, it's not possible to have a single binary for all the RPi, RPI4 dratically changed the required libraries for native GPU access and old libraries do not work anymore.

alanbork commented 4 years ago

I see. The main website does say you can compile to support the pi 4 - am I misunderstanding this? From the webpage it sure looks like it's CLI not x11:

On Raspberry Pi platform (native mode), Videocore API and EGL libraries are used for window/context management. Inputs are processed using evdev Linux libraries. To build for the Raspberry Pi 4:

cd src make PLATFORM=PLATFORM_RPI RPI_VERSION=RPI_VERSION_4

On Fri, Jun 19, 2020 at 3:13 AM Ray notifications@github.com wrote:

@alanbork https://github.com/alanbork Actually those changes have not been integrated on raylib, at the moment raylib does not support RaspberryPi 4 officially.

Unfortunately, it's not possible to have a single binary for all the RPi, RPI4 dratically changed the required libraries for native GPU access and old libraries do not work anymore.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/raysan5/raylib/issues/1096#issuecomment-646555493, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANPGZ5PSP7V7BOBIQHEHFWDRXM24LANCNFSM4KTHYJMA .

alanbork commented 4 years ago

never-mind, I see that the website in question is a fork of the current raylib for supporting rpi4.

alanbork commented 4 years ago

Has anybody else working on getting raylib for the pi4 gotten this error?

FATAL: kms: Unable to access GPU via DRM

(this is using the rpi4 branch contributed by jonlidgard). I'd file an issue with his branch but it doesn't seem possible?!

alanbork commented 4 years ago

ok, I figured that out on my own: I need to enable DRM in the boot/config.txt overlay:

dtoverlay=vc4-fkms-v3d

there's still other issues, but at least it runs now.

TSnake41 commented 4 years ago

You can also try with :

and run executables using cage

cage ./core_basic_window

There are some issues with renderer size (as cage forces fullscreen) but aside that, it works perfectly.

alanbork commented 4 years ago

After a lot of work I have gotten the unofficial port (https://github.com/jonlidgard/raylib/tree/rpi4) to work on my pi4. It is, however fragile. it's sensitive to how the resolution is configured; using tvservice + fbset doesn't work right or at least it doesn't work like it did in the pie zero. But if you set the resolution using rasp-config the result looks good - so long as your code isn't very demanding. As soon as you try to do anything very complex inside your render loop, however, it seems to act very strange, with frames getting rendered out of order. I think the issue is that it seems to be double buffered instead of triple buffered, and sometimes it doesn't flip the buffer when it's supposed to? Or maybe there's only 1 single buffer? It's very hard to diagnose. In any case, other people have remarked on the vsync being broken, and although I didn't see any torn frames the weirdness I saw may well be due to incorrect measurement of sync, causing buffer flips to occur at odd times. Just FYI.

alanbork commented 4 years ago

You can also try with :

  • PLATFORM_DESKTOP
  • USE_WAYLAND_DISPLAY = TRUE
  • GRAPHICS_API_OPENGL_21 if OpenGL 3.3 is not supported (GRAPHICS_API_OPENGL_ES2 may be interesting but it's currently not supported with PLATFORM_DESKTOP)

and run executables using cage

cage ./core_basic_window

There are some issues with renderer size (as cage forces fullscreen) but aside that, it works perfectly.

FYI this can be done with X11 too:

https://linuxconfig.org/how-to-run-x-applications-without-a-desktop-or-a-wm

(tested, and working with version 3.0 of raylib).

Admittedly, this thread is about running without X11, but I'm not sure wayland is going to be more lightweight? I have determined that using xinit/no wm does give better latency than using whatever the default Rpi4 desktop X11 environment is that you get with startx.

ghost commented 4 years ago

I modified the Raspberry Pi portions of core.c for VideoCore VI (which is supported by the V3D driver and which is in hardware in the Raspberry Pi 4) when a preprocessor definition V3D is set and I added a build-rpi4.sh script. Other makefiles, CMake files or project files aren't modified because I currently don't know the best way to integrate this new option into the current build system of PLATFORM etc. You can see my changes at https://github.com/kernelkinetic/raylib, actually only core.c and build-rpi4.sh are changed. My intension was to get raylib working without X but hardware acceleration on Raspberry Pi 4, I currently didn't test how this works with X11, but because I used the "proper linux way" to initialize the DRM/GBM/OpenGL ES stuff (as it is to be done for the new V3D driver as stated by the Raspberry Pi Foundation), I think it also may work using X if added appropriately.

ghost commented 4 years ago

From the raylib code I assume that raylib on VideoCore IV doesn't support the V3D drivers (kms and fake kms) but only the legacy graphics using the bcm libs. If this is right, my code could also be used on Raspberry Pi 1-3 with the kms/fkms drivers with just one line changed. Is it possible then to build one binary running on all Raspberry Pis without X using the kms or fkms V3D driver (again, I didn't test how it works with X). But all of this needs testing, I only tested the successfull initialization of the OpenGL ES context and some basic examples like core_basic_window and core_2d_camera but not all raylib graphics possibilities.

alanbork commented 4 years ago

I modified the Raspberry Pi portions of core.c for VideoCore VI (which is supported by the V3D driver and which is in hardware in the Raspberry Pi 4) when a preprocessor definition V3D is set and I added a build-rpi4.sh script. Other makefiles, CMake files or project files aren't modified because I currently don't know the best way to integrate this new option into the current build system of PLATFORM etc. You can see my changes at https://github.com/kernelkinetic/raylib, actually only core.c and build-rpi4.sh are changed. My intension was to get raylib working without X but hardware acceleration on Raspberry Pi 4, I currently didn't test how this works with X11, but because I used the "proper linux way" to initialize the DRM/GBM/OpenGL ES stuff (as it is to be done for the new V3D driver as stated by the Raspberry Pi Foundation), I think it also may work using X if added appropriately.

So this is interesting and I'd be happy to test it out some but you've left out a lot of details here. Which drivers (kms/fkms, or nothing) should be loaded? what's the proper command line call to make that would build the raylib examples?

ghost commented 4 years ago

@alanbork You need to use the V3D driver which you can setup using raspi-config and selecting GL (Fake KMS) or by adding dtoverlay=vc4-fkms-v3d to your/boot/config.txt (that's the same what raspi-config is doing). I didn't commit the Makefiles for lib and examples because I don't know how it is "the raylib way" to support all of the Raspberry Pi versions. Anyways, I pushed to my repository these modified versions of the two Makefiles: https://github.com/kernelkinetic/raylib/commit/ea9360c68efef1df4d7f8741aff3c34d01522f09

raysan5 commented 4 years ago

@kernelkinetic current V3D driver replaces the old Broadcom driver? Does it work for RPI 0,1,2,3 in native mode? In that case, what is the performance of V3D in comparison to the old Broadcom?

ghost commented 4 years ago

@raysan5 V3D is since Raspberry Pi 4 the current driver (see https://www.raspberrypi.org/blog/raspberry-pi-4-on-sale-now-from-35/), the previous non-GL VideoCore IV "bcm..." stuff is considered "legacy" on Raspberry Pi 4. V3D is public available since 02/2016 as experimental driver and the announcement says it's only available from Pi 2 because of memory requirements (https://www.raspberrypi.org/blog/another-new-raspbian-release/). and as far as I tested the only difference to get my code running on a Raspberry Pi 3 (beneath enabling the V3D kms or fkms driver) is to open "card0" instead of "card1" as dri device (https://github.com/kernelkinetic/raylib/blob/ea9360c68efef1df4d7f8741aff3c34d01522f09/src/core.c#L2975). V3D is in active development (https://www.raspberrypi.org/blog/vc4-and-v3d-opengl-drivers-for-raspberry-pi-an-update/) and supports hardware acceleration as well as software rendering (if you don't have permissions the software renderer is selected automatically, for example). In my few tests I came to the assesment that V3D and Broadcom drivers doesn't have noticeable performance differences, but I didn't measure anything. The previous link shows also many already reached and further planned enhancements of the V3D driver. As far as I understood, V3D also supports hardware accelerated rendering in X but I didn't investigated this further. Maybe I also need to state that I wrote this code to get raylib to work on a Pi 4 just for fun, I didn't used this raylib version currently for more than testing and all informations I used are taken from different public sources and express my understanding of these.

alanbork commented 4 years ago

@kernelkinetic I'm not sure what I was doing wrong but your code is working fine now. perhaps the firmware I had installed was causing intermittent issues. I've deleted my earlier comments since they were in essence wrong.

So here's the only issue I do see: in regular raylib accepts InitWindow(0,0, "name") to make a full-screen window. your code instead makes a 0,0 window. Which is why I was having problems earlier.

if I give it a smaller window I get something strange:

INFO: DISPLAY: Device initialized successfully INFO: > Display size: 1920 x 1080 INFO: > Render size: 1280 x 720 INFO: > Screen size: 1280 x 720

with the Render window in the lower right corner, and horizontal lines everywhere that's not inside the render window. I don't get 1280x720 resolution, just 1920 x 1080 no matter what I send to initwindow.

if I pass the actual resolution, I get a properly functioning full screen window.

performance seems good - buffer flips happen when I expect them to, and there are no dropped frames as measured with a photodiode.

alanbork commented 4 years ago

ps it is definitely possible to get a lower resolution properly - using the code at

https://www.raspberrypi.org/forums/viewtopic.php?f=68&t=243707&hilit=pi4#p1499181

I can select different modes from the mode array and they come up just fine. So this is a fixable problem in your code.

ghost commented 4 years ago

@alanbork Wow, thanks for your obviously deep analysis. I'm happy that it works so far and I'll look into my code and your link the next days to fix this. I'll notice here when it's finished.

ghost commented 4 years ago

@alanbork Different resolutions should now work as expected. https://github.com/kernelkinetic/raylib/commit/25a687b275ecfe9416b52380c1c5818ceede74d4

alanbork commented 4 years ago

@alanbork Different resolutions should now work as expected. kernelkinetic@25a687b

Excellent work! 720x480, 1280x720 and 1920x1080 result in the desired resolution. still some more to go, however.

Calling InitWindow(0,0, "name") gives a segfault, rather than using whatever the current video mode is (set at boot with config.txt or via tvservice at runtime). Raylib on pi0..3 get this right, including matching the interlacing of the current mode, which can be important if used with a TV.

ghost commented 4 years ago

@alanbork Thanks, and thanks again for testing, get ready for the next session :) https://github.com/kernelkinetic/raylib/commit/b1f3d8ab27cdef6a5fc6cdb0cc61c6ac5e2d4888 contains the screen size security check from PLATFORM_DESKTOP and PLATFORM_RPI to support also InitWindow(0, 0, ...). I'm not sure if this uses the current video mode because I don't know if I can get this information via DRM. At the moment I use the first available mode like any example I found is doing. Maybe you are so kind to check if this works as expected. If it doesn't work I'll look deeper into DRM.

By the way, I didn't implement this before because I didn't find documentation about the InitWindow function. Is there any I can use and overlooked so far (maybe @raysan5)?

alanbork commented 4 years ago

@kernelkinetic

not sure if this is relevant, but the 0,0 thing is officially supported:

https://github.com/raysan5/raylib/issues/1186

ghost commented 4 years ago

@alanbork Thanks, so I expect my already mentioned commit to work the same way as mentioned in this issue.

alanbork commented 4 years ago

current raylib on pi0.3 it uses the current mode set by config.txt/tvservice , which makes sense i think.

user chose that mode for a reason. for instance on my tv 0th mode is an interlaced mode that doesn't actually match tv's native resolution. not a great solution :-(

On Sat, Sep 5, 2020 at 12:03 PM kernelkinetic notifications@github.com wrote:

@alanbork Thanks, so I expect my already mentioned commit to work the same way as mentioned in this issue.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

alanbork commented 4 years ago

@kernelkinetic , see if this works. for me it shows 1280x720, as expected

cat /sys/kernel/debug/dri/1/*

alanbork commented 4 years ago

@kernelkinetic , see if this works. for me it shows 1280x720, as expected

cat /sys/kernel/debug/dri/1/*

sorry I should have checked - that seems to be constant independent of calls to tvservice. maybe it's the boot resolution?

ghost commented 4 years ago

@alanbork You lead me to an idea: I think I could use the same function to get the current display size as for Raspberry Pi 0..3. I'll check that tomorrow 👍🏼

alanbork commented 4 years ago

@kernelkinetic there's this option:

https://blog.weghos.com/userland/PiUserland/host_applications/linux/apps/tvservice/tvservice.c.html#451

but that seems to use non-drm methods to get the current mode. Since we are talking about supporting he the pi4 that wouldn't be the end of the world, but certainly not elegant.

ghost commented 4 years ago

@alanbork Thanks for your hints, I also thought to use graphics_get_display_size() from bcm, but I think we should stick to DRM because that's what should be available on every Linux system and so we may build a base for other embedded linux boards already. I was going a little bit deeper into DRM: According to https://manpages.debian.org/testing/libdrm-dev/drmModeGetResources.3.en.html the correct way seems to be to find the connector of the display we want to use and then get the current display setting from the connector's crt controller and mode. That leads to another question: Raspberry Pi 4 has two HDMI connectors and the LCD connector as third option, how to say to raylib which one to use?

ghost commented 4 years ago

@alanbork Beside we should also answer the previous question, I added that the current screen settings are used if InitWindow() isn't called with two positive size values in https://github.com/kernelkinetic/raylib/commit/926f6f7b1f8eb3deaaa434a18b3e62a652a545c4

TSnake41 commented 4 years ago

@kernelkinetic you shouldn't name this support "V3D", the API you use is supported on most (if not all) DRI supported platforms. So nouveau, r200/r300/r600, radeonsi, iris, i965, lima, panfrost, and many others are supported with your code.

Maybe name it PLATFORM_DRM ?

ghost commented 4 years ago

Thanks for the hint, @TSnake41. This touches the (still not answered) question how to integrate this into the current raylib build system. Since I'm currently not aware wether and how DRM and X11 works together and I'm concentrating on raylib's "native mode" only, I'm not able to answer this question. Of course I'm open to suggestions or decisions how to integrate this into raylib the proper way and then it will get the name whatever it should have. But to be honest I'm not sure if its a new platform and so I didn't build it as PLATFORM_DRM so far. I think someone with more knowledge of raylib should answer this.

TSnake41 commented 4 years ago

I can run your fork with -DPLATFORM_RPI and -DV3D fine on my Linux laptop (i5-5300U, HD 5500) and should work as long as you have "Open Source drivers" (got the same idea working on a Orange Pi One and my desktop (i5-4670k, GTX 750-Ti (nouveau driver)). Just compile it, and it runs (almost) properly, only having display issues with big screens or multi screen (probably a issue of actual size vs requested size).

About project integration, making a PLATFORM_DRM platform looks fine with shared parts between PLATFORM_RPI and PLATFORM_DRM, there is already EGL stuff shared between all these platforms.

OpenGL ES is needed, but it's already provided by Mesa, even for desktop/laptops (I have OpenGL ES 3.2 on HD 5500/GTX 750-Ti).

ghost commented 4 years ago

@TSnake41 Okay, sounds reasonable to me. So I'll change my implementation to a new PLATFORM_DRM :)

alanbork commented 4 years ago

wrt to getting the current mode, from another forum post:

The current mode is a setting on the crtc (the thing that applies the video timings), so you'll find the current mode in "drmModeCrtc" as "mode".

It gets tricky fast when you consider multiple monitors/outputs, but as long as there's only one attached it's possible to do the right thing without having to guess. IMHO if there's more than one display just punt and use the first, as raylib doesn't have any API for this anyway. A more general option would be to support a .rayrc file in the same directory that override initwindow or resolution, monitor, etc, but going that way is up to ray.

ghost commented 4 years ago

@alanbork Thanks, I already implemented this in https://github.com/kernelkinetic/raylib/commit/926f6f7b1f8eb3deaaa434a18b3e62a652a545c4 :D

ghost commented 4 years ago

I've updated my repo to https://github.com/kernelkinetic/raylib/commit/f900a4017e02f5c86fd47e3a19d05d5662d89c22 containing my implementation as new PLATFORM_DRM and I also modified the Makefiles in src and in examples for first test builds.

alanbork commented 4 years ago

@kernelkinetic Ah, ok. I just downloaded your newest release and it's working great. Even potentially tricky bits like 480p vs 480i are maintained properly! FYI things start to go south once you use tvservice to set to current display mode; but this is because tvservice is deprecated for fkms. IMHO, at least from the users side of things this is functionally ready for consideration of a merge with the official raylib. The only thing I could suggest is perhaps a more informative error message in the case that a user tries to run your code when fkms is not loaded; plus there's the question of what to with pi0...3 - the original vc4/dispmanx api's are really better suited for that hardware, so the holy grail here would be to support both in a single binary.

raysan5 commented 4 years ago

@kernelkinetic great work! I need to see how to integrate it in raylib properly... @alanbork I'm afraid RPI0-3 and RPI4 paths should be separated in some way and users need to recompile raylib with the proper libraries for every platform... I'm considering splitting PLATFORM_RPI into:

Is this implementation supported in other embedded platforms other than RPI? could it be easily adapated? I don't know what's the state of DRM driver between platforms... any info?

ghost commented 4 years ago

Thanks a lot @alanbork and @raysan5, I'm glad to read this! I didn't check input et. al., only the graphic part. Because I only updated the PLATFORM_RPI defines to also be included on PLATFORM_DRM, I didn't expect any flaws there.

I mentioned earlier that my implementation should also work with V3D (f)kms driver on Raspberry Pi 2 and above. If we want to support both APIs in one binary, the only solution I can imagine is to dynamically load the corresponding libraries - which could also be used if the (f)kms driver is not available. Let me check the necessary effort of such an implementation. The only drawback with this would be that we loose link-time checks for these dependencies.

@raysan5 As @TSnake41 already mentioned, the DRM implementation is "the Linux standard" as native base to EGL. It should also work on most Linux distributions, even on desktops. The only difference that my implementation uses OpenGL ES while desktops often use OpenGL profile.

ghost commented 4 years ago

Some useful links to DRM:

DRM in the Linux kernel documentation: https://www.kernel.org/doc/html/latest/gpu/introduction.html

DRM on Android: https://www.collabora.com/news-and-blog/blog/2018/12/17/a-dream-come-true-android-finally-using-drm/kms/

NVIDIA: https://download.nvidia.com/XFree86/Linux-x86_64/396.51/README/kms.html

alanbork commented 4 years ago

I'm glad to read this! I didn't check input et. al., only the graphic part. Because I only updated the PLATFORM_RPI defines to also be included on PLATFORM_DRM, I didn't expect any flaws there.

FYI the current raylib implementation of keyboard input works fine on the pi4. I have not tested usb gamepads but I would expect it to work.