Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SDL2 on Raspberry Pi without X?

I'm hoping to develop some code that uses SDL2 to display graphics on the 7" RPi touchscreen, but I'd rather not install a full desktop OS. I've got Raspbian Buster Lite installed. Some simple test code gets an error when I try to run it:

user@rpi4:~/01_hello_SDL $ ./hw
Window could not be created! SDL_Error: Could not initialize EGL
user@rpi4:~/01_hello_SDL $ sudo ./hw
error: XDG_RUNTIME_DIR not set in the environment.
Window could not be created! SDL_Error: Could not initialize EGL

I'm trying to create the window with

SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL )

I found a post that referenced instructions on how to build SDL2 without X, but I was hoping someone could educate me a bit more about how SDL finds the display in various environments, and if it’s even possible to do what I want to do.

A few years ago I used SDL 1.2 to do full-screen graphics on a Beaglebone Black running a version of Debian, but I seem to have lost that installation, and don’t remember how it was set up. I vaguely recall some issues around fbdev and it being non-accelerated graphics, but that didn’t matter at the time (and while I’d like to get accelerated graphics now, it’s not critical).

Example code:

/*This source code copyrighted by Lazy Foo' Productions (2004-2019)
and may not be redistributed without written permission.*/

//Using SDL and standard IO
#include <SDL.h>
#include <stdio.h>

//Screen dimension constants
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 480;

int main( int argc, char* args[] )
{
    //The window we'll be rendering to
    SDL_Window* window = NULL;
    
    //The surface contained by the window
    SDL_Surface* screenSurface = NULL;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
    }
    else
    {
        //Create window
        window = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL );
        if( window == NULL )
        {
            printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
        }
        else
        {
            //Get window surface
            screenSurface = SDL_GetWindowSurface( window );

            //Fill the surface white
            SDL_FillRect( screenSurface, NULL, SDL_MapRGB( screenSurface->format, 0xFF, 0xFF, 0xFF ) );
            
            //Update the surface
            SDL_UpdateWindowSurface( window );

            //Wait two seconds
            SDL_Delay( 2000 );
        }
    }

    //Destroy window
    SDL_DestroyWindow( window );

    //Quit SDL subsystems
    SDL_Quit();

    return 0;
}
like image 670
Rick Avatar asked Aug 27 '19 10:08

Rick


1 Answers

Alrighty, got it working on my Raspberry Pi 3 with 2019-07-10-raspbian-buster-lite.img, both with the default Broadcom blobs & the KMS/DRM backend:

  1. Install SDL2 build dependencies:

    # install everything Debian uses to build SDL
    sudo apt build-dep libsdl2
    
    # needed for the KMSDRM backend:
    sudo apt install libdrm-dev libgbm-dev
    
  2. Grab the latest stable SDL source tarball or tag (release-2.0.10) from Git and extract it somewhere like ~/sdl-src

  3. Run SDL's configure script:

    cd ~/sdl-src
    ./configure --enable-video-kmsdrm
    

    Here's my configure summary, note the rpi and kmsdrm(dynamic) entries in the Video drivers list:

    SDL2 Configure Summary:
    Building Shared Libraries
    Building Static Libraries
    Enabled modules : atomic audio video render events joystick haptic sensor power filesystem threads timers file loadso cpuinfo assembly
    Assembly Math   :
    Audio drivers   : disk dummy oss alsa(dynamic) pulse(dynamic) sndio(dynamic)
    Video drivers   : dummy rpi x11(dynamic) kmsdrm(dynamic) opengl opengl_es1 opengl_es2 vulkan wayland(dynamic)
    X11 libraries   : xcursor xdbe xinerama xinput2 xinput2_multitouch xrandr xscrnsaver xshape xvidmode
    Input drivers   : linuxev linuxkd
    Using libsamplerate : YES
    Using libudev       : YES
    Using dbus          : YES
    Using ime           : YES
    Using ibus          : YES
    Using fcitx         : YES
    
  4. Build & install SDL; took ~4.5 minutes on my Rpi3:

    make -j4 && sudo make install
    
  5. Build test program:

    g++ main.cpp `pkg-config --cflags --libs sdl2`
    
  6. (Optional) Enable the "Full KMS" driver if you want to use the KMSDRM backend instead of the default OpenGL ES blobs:

    $ sudo raspi-config
    select '7 Advanced Options'
    select 'A7 GL Driver'
    select 'G3 GL (Full KMS)'
    reboot
    
  7. Run test program:

    $ ./a.out 
    Testing video drivers...
    The path /dev/dri/ cannot be opened or is not available
    The path /dev/dri/ cannot be opened or is not available
    SDL_VIDEODRIVER available: x11 wayland KMSDRM RPI dummy
    SDL_VIDEODRIVER usable   : RPI
    The path /dev/dri/ cannot be opened or is not available
    The path /dev/dri/ cannot be opened or is not available
    SDL_VIDEODRIVER selected : RPI
    SDL_RENDER_DRIVER available: opengl opengles2 opengles software
    SDL_RENDER_DRIVER selected : opengles2
    

    You can use environment variables to override the default video/render driver selection:

    SDL_VIDEODRIVER=KMSDRM SDL_RENDER_DRIVER=software ./a.out
    

    I had to hold SDL's hand a bit with envvars to get the KMSDRM backend to load:

    # no envvars, fails:
    $ ./a.out 
    Testing video drivers...
    SDL_VIDEODRIVER available: x11 wayland KMSDRM RPI dummy
    SDL_VIDEODRIVER usable   : KMSDRM
    SDL_VIDEODRIVER selected : KMSDRM
    SDL_CreateWindow(): Could not initialize OpenGL / GLES library
    
    # with envvars, succeeds:
    $ SDL_VIDEO_EGL_DRIVER=libEGL.so SDL_VIDEO_GL_DRIVER=libGLESv2.so ./a.out
    Testing video drivers...
    SDL_VIDEODRIVER available: x11 wayland KMSDRM RPI dummy
    SDL_VIDEODRIVER usable   : KMSDRM
    SDL_VIDEODRIVER selected : KMSDRM
    SDL_RENDER_DRIVER available: opengl opengles2 opengles software
    SDL_RENDER_DRIVER selected : opengl
    

Here's the test program I've been using:

// g++ main.cpp `pkg-config --cflags --libs sdl2`
#include <SDL.h>
#include <iostream>
#include <vector>

int main( int argc, char** argv )
{
    SDL_Init( 0 );

    std::cout << "Testing video drivers..." << '\n';
    std::vector< bool > drivers( SDL_GetNumVideoDrivers() );
    for( int i = 0; i < drivers.size(); ++i )
    {
        drivers[ i ] = ( 0 == SDL_VideoInit( SDL_GetVideoDriver( i ) ) );
        SDL_VideoQuit();
    }

    std::cout << "SDL_VIDEODRIVER available:";
    for( int i = 0; i < drivers.size(); ++i )
    {
        std::cout << " " << SDL_GetVideoDriver( i );
    }
    std::cout << '\n';

    std::cout << "SDL_VIDEODRIVER usable   :";
    for( int i = 0; i < drivers.size(); ++i )
    {
        if( !drivers[ i ] ) continue;
        std::cout << " " << SDL_GetVideoDriver( i );
    }
    std::cout << '\n';

    if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
    {
        std::cerr << "SDL_Init(): " << SDL_GetError() << '\n';
        return EXIT_FAILURE;
    }
    std::cout << "SDL_VIDEODRIVER selected : " << SDL_GetCurrentVideoDriver() << '\n';

    SDL_Window* window = SDL_CreateWindow
        (
        "SDL2",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        640, 480,
        SDL_WINDOW_SHOWN
        );
    if( nullptr == window )
    {
        std::cerr << "SDL_CreateWindow(): " << SDL_GetError() << '\n';
        return EXIT_FAILURE;
    }

    std::cout << "SDL_RENDER_DRIVER available:";
    for( int i = 0; i < SDL_GetNumRenderDrivers(); ++i )
    {
        SDL_RendererInfo info;
        SDL_GetRenderDriverInfo( i, &info );
        std::cout << " " << info.name;
    }
    std::cout << '\n';

    SDL_Renderer* renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );
    if( nullptr == renderer )
    {
        std::cerr << "SDL_CreateRenderer(): " << SDL_GetError() << '\n';
        return EXIT_FAILURE;
    }
    SDL_RendererInfo info;
    SDL_GetRendererInfo( renderer, &info );
    std::cout << "SDL_RENDER_DRIVER selected : " << info.name << '\n';

    bool running = true;
    unsigned char i = 0;
    while( running )
    {
        SDL_Event ev;
        while( SDL_PollEvent( &ev ) )
        {
            if( ( ev.type == SDL_QUIT ) ||
                ( ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_ESCAPE ) )
            {
                running = false;
            }
        }

        SDL_SetRenderDrawColor( renderer, i, i, i, SDL_ALPHA_OPAQUE );
        SDL_RenderClear( renderer );
        SDL_RenderPresent( renderer );
        i++;
    }

    SDL_DestroyRenderer( renderer );
    SDL_DestroyWindow( window );
    SDL_Quit();
    return 0;
}
like image 188
genpfault Avatar answered Nov 15 '22 17:11

genpfault