offscreen: Add new video driver backend Offscreen
authorBrandon Schaefer <brandon.schaefer@epicgames.com>
Tue, 24 Sep 2019 16:36:48 -0400
changeset 130899692ad570003
parent 13088 d55db8d27108
child 13090 0d7e642c5e28
offscreen: Add new video driver backend Offscreen

The Offscreen video driver is intended to be used for headless rendering
as well as allows for multiple GPUs to be used for headless rendering

Currently only supports EGL (OpenGL / ES) or Framebuffers
Adds a hint to specifiy which EGL device to use: SDL_HINT_EGL_DEVICE
Adds testoffscreen.c which can be used to test the backend out
Disabled by default for now
CMakeLists.txt
include/SDL_config.h.cmake
src/video/SDL_egl.c
src/video/SDL_egl_c.h
src/video/SDL_sysvideo.h
src/video/SDL_video.c
src/video/offscreen/SDL_offscreenevents.c
src/video/offscreen/SDL_offscreenevents_c.h
src/video/offscreen/SDL_offscreenframebuffer.c
src/video/offscreen/SDL_offscreenframebuffer_c.h
src/video/offscreen/SDL_offscreenopengl.c
src/video/offscreen/SDL_offscreenopengl.h
src/video/offscreen/SDL_offscreenvideo.c
src/video/offscreen/SDL_offscreenvideo.h
src/video/offscreen/SDL_offscreenwindow.c
src/video/offscreen/SDL_offscreenwindow.h
test/CMakeLists.txt
test/testoffscreen.c
     1.1 --- a/CMakeLists.txt	Mon Sep 23 18:30:22 2019 -0400
     1.2 +++ b/CMakeLists.txt	Tue Sep 24 16:36:48 2019 -0400
     1.3 @@ -381,6 +381,7 @@
     1.4  set_option(VIDEO_METAL         "Enable Metal support" ${APPLE})
     1.5  set_option(VIDEO_KMSDRM        "Use KMS DRM video driver" ${UNIX_SYS})
     1.6  dep_option(KMSDRM_SHARED       "Dynamically load KMS DRM support" ON "VIDEO_KMSDRM" OFF)
     1.7 +set_option(VIDEO_OFFSCREEN     "Use offscreen video driver" OFF)
     1.8  option_string(BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" "OFF")
     1.9  option_string(FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" "OFF")
    1.10  set_option(HIDAPI              "Use HIDAPI for low level joystick drivers" ${OPT_DEF_HIDAPI})
    1.11 @@ -852,6 +853,13 @@
    1.12      set(HAVE_VIDEO_DUMMY TRUE)
    1.13      set(HAVE_SDL_VIDEO TRUE)
    1.14    endif()
    1.15 +  if(VIDEO_OFFSCREEN)
    1.16 +    set(SDL_VIDEO_DRIVER_OFFSCREEN 1)
    1.17 +    file(GLOB VIDEO_OFFSCREEN_SOURCES ${SDL2_SOURCE_DIR}/src/video/offscreen/*.c)
    1.18 +    set(SOURCE_FILES ${SOURCE_FILES} ${VIDEO_OFFSCREEN_SOURCES})
    1.19 +    set(HAVE_VIDEO_OFFSCREEN TRUE)
    1.20 +    set(HAVE_SDL_VIDEO TRUE)
    1.21 +  endif()
    1.22  endif()
    1.23  
    1.24  # Platform-specific options and settings
     2.1 --- a/include/SDL_config.h.cmake	Mon Sep 23 18:30:22 2019 -0400
     2.2 +++ b/include/SDL_config.h.cmake	Tue Sep 24 16:36:48 2019 -0400
     2.3 @@ -328,6 +328,7 @@
     2.4  #cmakedefine SDL_VIDEO_DRIVER_DIRECTFB @SDL_VIDEO_DRIVER_DIRECTFB@
     2.5  #cmakedefine SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC @SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC@
     2.6  #cmakedefine SDL_VIDEO_DRIVER_DUMMY @SDL_VIDEO_DRIVER_DUMMY@
     2.7 +#cmakedefine SDL_VIDEO_DRIVER_OFFSCREEN @SDL_VIDEO_DRIVER_OFFSCREEN@
     2.8  #cmakedefine SDL_VIDEO_DRIVER_WINDOWS @SDL_VIDEO_DRIVER_WINDOWS@
     2.9  #cmakedefine SDL_VIDEO_DRIVER_WAYLAND @SDL_VIDEO_DRIVER_WAYLAND@
    2.10  #cmakedefine SDL_VIDEO_DRIVER_RPI @SDL_VIDEO_DRIVER_RPI@
     3.1 --- a/src/video/SDL_egl.c	Mon Sep 23 18:30:22 2019 -0400
     3.2 +++ b/src/video/SDL_egl.c	Tue Sep 24 16:36:48 2019 -0400
     3.3 @@ -94,6 +94,11 @@
     3.4  }
     3.5  #endif
     3.6  
     3.7 +/* it is allowed to not have some of the EGL extensions on start - attempts to use them will fail later. */
     3.8 +#define LOAD_FUNC_EGLEXT(NAME) \
     3.9 +    _this->egl_data->NAME = _this->egl_data->eglGetProcAddress(#NAME);
    3.10 +
    3.11 +
    3.12  static const char * SDL_EGL_GetErrorName(EGLint eglErrorCode)
    3.13  {
    3.14  #define SDL_EGL_ERROR_TRANSLATE(e) case e: return #e;
    3.15 @@ -256,11 +261,10 @@
    3.16  }
    3.17  
    3.18  int
    3.19 -SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display, EGLenum platform)
    3.20 +SDL_EGL_LoadLibraryOnly(_THIS, const char *egl_path)
    3.21  {
    3.22      void *dll_handle = NULL, *egl_dll_handle = NULL; /* The naming is counter intuitive, but hey, I just work here -- Gabriel */
    3.23      const char *path = NULL;
    3.24 -    int egl_version_major = 0, egl_version_minor = 0;
    3.25  #if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
    3.26      const char *d3dcompiler;
    3.27  #endif
    3.28 @@ -406,8 +410,31 @@
    3.29      LOAD_FUNC(eglWaitNative);
    3.30      LOAD_FUNC(eglWaitGL);
    3.31      LOAD_FUNC(eglBindAPI);
    3.32 +    LOAD_FUNC(eglQueryAPI);
    3.33      LOAD_FUNC(eglQueryString);
    3.34      LOAD_FUNC(eglGetError);
    3.35 +    LOAD_FUNC_EGLEXT(eglQueryDevicesEXT);
    3.36 +    LOAD_FUNC_EGLEXT(eglGetPlatformDisplayEXT);
    3.37 +
    3.38 +    _this->gl_config.driver_loaded = 1;
    3.39 +
    3.40 +    if (path) {
    3.41 +        SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
    3.42 +    } else {
    3.43 +        *_this->gl_config.driver_path = '\0';
    3.44 +    }
    3.45 +
    3.46 +    return 0;
    3.47 +}
    3.48 +
    3.49 +int
    3.50 +SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display, EGLenum platform)
    3.51 +{
    3.52 +    int egl_version_major = 0, egl_version_minor = 0;
    3.53 +    int library_load_retcode = SDL_EGL_LoadLibraryOnly(_this, egl_path);
    3.54 +    if (library_load_retcode != 0) {
    3.55 +        return library_load_retcode;
    3.56 +    }
    3.57  
    3.58      if (_this->egl_data->eglQueryString) {
    3.59          /* EGL 1.5 allows querying for client version */
    3.60 @@ -447,20 +474,105 @@
    3.61          _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
    3.62      }
    3.63      if (_this->egl_data->egl_display == EGL_NO_DISPLAY) {
    3.64 +        _this->gl_config.driver_loaded = 0;
    3.65 +        *_this->gl_config.driver_path = '\0';
    3.66          return SDL_SetError("Could not get EGL display");
    3.67      }
    3.68      
    3.69      if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
    3.70 +        _this->gl_config.driver_loaded = 0;
    3.71 +        *_this->gl_config.driver_path = '\0';
    3.72          return SDL_SetError("Could not initialize EGL");
    3.73      }
    3.74  #endif
    3.75  
    3.76 -    if (path) {
    3.77 -        SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
    3.78 -    } else {
    3.79 -        *_this->gl_config.driver_path = '\0';
    3.80 +    _this->egl_data->is_offscreen = 0;
    3.81 +
    3.82 +    return 0;
    3.83 +}
    3.84 +
    3.85 +/**
    3.86 +   On multi GPU machines EGL device 0 is not always the first valid GPU.
    3.87 +   Container environments can restrict access to some GPUs that are still listed in the EGL
    3.88 +   device list. If the requested device is a restricted GPU and cannot be used
    3.89 +   (eglInitialize() will fail) then attempt to automatically and silently select the next
    3.90 +   valid available GPU for EGL to use.
    3.91 +*/
    3.92 +
    3.93 +int
    3.94 +SDL_EGL_InitializeOffscreen(_THIS, int device)
    3.95 +{
    3.96 +    EGLDeviceEXT egl_devices[SDL_EGL_MAX_DEVICES];
    3.97 +    EGLint num_egl_devices = 0;
    3.98 +    const char *egl_device_hint;
    3.99 +
   3.100 +    if (_this->gl_config.driver_loaded != 1) {
   3.101 +        return SDL_SetError("SDL_EGL_LoadLibraryOnly() has not been called or has failed.");
   3.102      }
   3.103 -    
   3.104 +
   3.105 +    /* Check for all extensions that are optional until used and fail if any is missing */
   3.106 +    if (_this->egl_data->eglQueryDevicesEXT == NULL) {
   3.107 +        return SDL_SetError("eglQueryDevicesEXT is missing (EXT_device_enumeration not supported by the drivers?)");
   3.108 +    }
   3.109 +
   3.110 +    if (_this->egl_data->eglGetPlatformDisplayEXT == NULL) {
   3.111 +        return SDL_SetError("eglGetPlatformDisplayEXT is missing (EXT_platform_base not supported by the drivers?)");
   3.112 +    }
   3.113 +
   3.114 +    if (_this->egl_data->eglQueryDevicesEXT(SDL_EGL_MAX_DEVICES, egl_devices, &num_egl_devices) != EGL_TRUE) {
   3.115 +        return SDL_SetError("eglQueryDevicesEXT() failed");
   3.116 +    }
   3.117 +
   3.118 +    egl_device_hint = SDL_GetHint("SDL_HINT_EGL_DEVICE");
   3.119 +    if (egl_device_hint) {
   3.120 +        device = SDL_atoi(egl_device_hint);
   3.121 +
   3.122 +        if (device >= num_egl_devices) {
   3.123 +            return SDL_SetError("Invalid EGL device is requested.");
   3.124 +        }
   3.125 +
   3.126 +        _this->egl_data->egl_display = _this->egl_data->eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, egl_devices[device], NULL);
   3.127 +
   3.128 +        if (_this->egl_data->egl_display == EGL_NO_DISPLAY) {
   3.129 +            return SDL_SetError("eglGetPlatformDisplayEXT() failed.");
   3.130 +        }
   3.131 +
   3.132 +        if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
   3.133 +            return SDL_SetError("Could not initialize EGL");
   3.134 +        }
   3.135 +    }
   3.136 +    else {
   3.137 +        int i;
   3.138 +        SDL_bool found = SDL_FALSE;
   3.139 +        EGLDisplay attempted_egl_display;
   3.140 +
   3.141 +        /* If no hint is provided lets look for the first device/display that will allow us to eglInit */
   3.142 +        for (i = 0; i < num_egl_devices; i++) {
   3.143 +            attempted_egl_display = _this->egl_data->eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, egl_devices[i], NULL);
   3.144 +
   3.145 +            if (attempted_egl_display == EGL_NO_DISPLAY) {
   3.146 +                continue;
   3.147 +            }
   3.148 +
   3.149 +            if (_this->egl_data->eglInitialize(attempted_egl_display, NULL, NULL) != EGL_TRUE) {
   3.150 +                _this->egl_data->eglTerminate(attempted_egl_display);
   3.151 +                continue;
   3.152 +            }
   3.153 +
   3.154 +            /* We did not fail, we'll pick this one! */
   3.155 +            _this->egl_data->egl_display = attempted_egl_display;
   3.156 +            found = SDL_TRUE;
   3.157 +
   3.158 +            break;
   3.159 +        }
   3.160 +
   3.161 +        if (!found) {
   3.162 +            return SDL_SetError("Could not find a valid EGL device to initialize");
   3.163 +        }
   3.164 +    }
   3.165 +
   3.166 +    _this->egl_data->is_offscreen = 1;
   3.167 +
   3.168      return 0;
   3.169  }
   3.170  
   3.171 @@ -580,6 +692,11 @@
   3.172          attribs[i++] = _this->gl_config.multisamplesamples;
   3.173      }
   3.174  
   3.175 +    if (_this->egl_data->is_offscreen) {
   3.176 +        attribs[i++] = EGL_SURFACE_TYPE;
   3.177 +        attribs[i++] = EGL_PBUFFER_BIT;
   3.178 +    }
   3.179 +
   3.180      attribs[i++] = EGL_RENDERABLE_TYPE;
   3.181      if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   3.182  #ifdef EGL_KHR_create_context
   3.183 @@ -931,6 +1048,25 @@
   3.184      return surface;
   3.185  }
   3.186  
   3.187 +EGLSurface
   3.188 +SDL_EGL_CreateOffscreenSurface(_THIS, int width, int height)
   3.189 +{
   3.190 +    EGLint attributes[] = {
   3.191 +        EGL_WIDTH, width,
   3.192 +        EGL_HEIGHT, height,
   3.193 +        EGL_NONE
   3.194 +    };
   3.195 +
   3.196 +    if (SDL_EGL_ChooseConfig(_this) != 0) {
   3.197 +        return EGL_NO_SURFACE;
   3.198 +    }
   3.199 +
   3.200 +    return _this->egl_data->eglCreatePbufferSurface(
   3.201 +        _this->egl_data->egl_display,
   3.202 +        _this->egl_data->egl_config,
   3.203 +        attributes);
   3.204 +}
   3.205 +
   3.206  void
   3.207  SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) 
   3.208  {
     4.1 --- a/src/video/SDL_egl_c.h	Mon Sep 23 18:30:22 2019 -0400
     4.2 +++ b/src/video/SDL_egl_c.h	Tue Sep 24 16:36:48 2019 -0400
     4.3 @@ -29,6 +29,8 @@
     4.4  
     4.5  #include "SDL_sysvideo.h"
     4.6  
     4.7 +#define SDL_EGL_MAX_DEVICES     8
     4.8 +
     4.9  typedef struct SDL_EGL_VideoData
    4.10  {
    4.11      void *egl_dll_handle, *dll_handle;
    4.12 @@ -81,6 +83,8 @@
    4.13      EGLBoolean(EGLAPIENTRY *eglSwapInterval) (EGLDisplay dpy, EGLint interval);
    4.14      
    4.15      const char *(EGLAPIENTRY *eglQueryString) (EGLDisplay dpy, EGLint name);
    4.16 +
    4.17 +    EGLenum(EGLAPIENTRY *eglQueryAPI)(void);
    4.18      
    4.19      EGLBoolean(EGLAPIENTRY  *eglGetConfigAttrib) (EGLDisplay dpy, EGLConfig config,
    4.20                                       EGLint attribute, EGLint * value);
    4.21 @@ -93,6 +97,13 @@
    4.22  
    4.23      EGLint(EGLAPIENTRY *eglGetError)(void);
    4.24  
    4.25 +    EGLBoolean(EGLAPIENTRY *eglQueryDevicesEXT)(EGLint max_devices,
    4.26 +                                            EGLDeviceEXT* devices,
    4.27 +                                            EGLint* num_devices);
    4.28 +
    4.29 +    /* whether EGL display was offscreen */
    4.30 +    int is_offscreen;
    4.31 +
    4.32  } SDL_EGL_VideoData;
    4.33  
    4.34  /* OpenGLES functions */
    4.35 @@ -100,6 +111,7 @@
    4.36  /* SDL_EGL_LoadLibrary can get a display for a specific platform (EGL_PLATFORM_*)
    4.37   * or, if 0 is passed, let the implementation decide.
    4.38   */
    4.39 +extern int SDL_EGL_LoadLibraryOnly(_THIS, const char *path);
    4.40  extern int SDL_EGL_LoadLibrary(_THIS, const char *path, NativeDisplayType native_display, EGLenum platform);
    4.41  extern void *SDL_EGL_GetProcAddress(_THIS, const char *proc);
    4.42  extern void SDL_EGL_UnloadLibrary(_THIS);
    4.43 @@ -111,6 +123,10 @@
    4.44  extern EGLSurface *SDL_EGL_CreateSurface(_THIS, NativeWindowType nw);
    4.45  extern void SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface);
    4.46  
    4.47 +extern EGLSurface SDL_EGL_CreateOffscreenSurface(_THIS, int width, int height);
    4.48 +/* Assumes that LoadLibraryOnly() has succeeded */
    4.49 +extern int SDL_EGL_InitializeOffscreen(_THIS, int device);
    4.50 +
    4.51  /* These need to be wrapped to get the surface for the window by the platform GLES implementation */
    4.52  extern SDL_GLContext SDL_EGL_CreateContext(_THIS, EGLSurface egl_surface);
    4.53  extern int SDL_EGL_MakeCurrent(_THIS, EGLSurface egl_surface, SDL_GLContext context);
     5.1 --- a/src/video/SDL_sysvideo.h	Mon Sep 23 18:30:22 2019 -0400
     5.2 +++ b/src/video/SDL_sysvideo.h	Tue Sep 24 16:36:48 2019 -0400
     5.3 @@ -429,6 +429,7 @@
     5.4  extern VideoBootStrap VIVANTE_bootstrap;
     5.5  extern VideoBootStrap Emscripten_bootstrap;
     5.6  extern VideoBootStrap QNX_bootstrap;
     5.7 +extern VideoBootStrap OFFSCREEN_bootstrap;
     5.8  
     5.9  extern SDL_VideoDevice *SDL_GetVideoDevice(void);
    5.10  extern int SDL_AddBasicVideoDisplay(const SDL_DisplayMode * desktop_mode);
     6.1 --- a/src/video/SDL_video.c	Mon Sep 23 18:30:22 2019 -0400
     6.2 +++ b/src/video/SDL_video.c	Tue Sep 24 16:36:48 2019 -0400
     6.3 @@ -109,6 +109,9 @@
     6.4  #if SDL_VIDEO_DRIVER_QNX
     6.5      &QNX_bootstrap,
     6.6  #endif
     6.7 +#if SDL_VIDEO_DRIVER_OFFSCREEN
     6.8 +    &OFFSCREEN_bootstrap,
     6.9 +#endif
    6.10  #if SDL_VIDEO_DRIVER_DUMMY
    6.11      &DUMMY_bootstrap,
    6.12  #endif
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/video/offscreen/SDL_offscreenevents.c	Tue Sep 24 16:36:48 2019 -0400
     7.3 @@ -0,0 +1,42 @@
     7.4 +/*
     7.5 +  Simple DirectMedia Layer
     7.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     7.7 +
     7.8 +  This software is provided 'as-is', without any express or implied
     7.9 +  warranty.  In no event will the authors be held liable for any damages
    7.10 +  arising from the use of this software.
    7.11 +
    7.12 +  Permission is granted to anyone to use this software for any purpose,
    7.13 +  including commercial applications, and to alter it and redistribute it
    7.14 +  freely, subject to the following restrictions:
    7.15 +
    7.16 +  1. The origin of this software must not be misrepresented; you must not
    7.17 +     claim that you wrote the original software. If you use this software
    7.18 +     in a product, an acknowledgment in the product documentation would be
    7.19 +     appreciated but is not required.
    7.20 +  2. Altered source versions must be plainly marked as such, and must not be
    7.21 +     misrepresented as being the original software.
    7.22 +  3. This notice may not be removed or altered from any source distribution.
    7.23 +*/
    7.24 +
    7.25 +#include "../../SDL_internal.h"
    7.26 +
    7.27 +#if SDL_VIDEO_DRIVER_OFFSCREEN
    7.28 +
    7.29 +/* Being a offscreen driver, there's no event stream. We just define stubs for
    7.30 +   most of the API. */
    7.31 +
    7.32 +#include "../../events/SDL_events_c.h"
    7.33 +
    7.34 +#include "SDL_offscreenvideo.h"
    7.35 +#include "SDL_offscreenevents_c.h"
    7.36 +
    7.37 +void
    7.38 +OFFSCREEN_PumpEvents(_THIS)
    7.39 +{
    7.40 +    /* do nothing. */
    7.41 +}
    7.42 +
    7.43 +#endif /* SDL_VIDEO_DRIVER_OFFSCREEN */
    7.44 +
    7.45 +/* vi: set ts=4 sw=4 expandtab: */
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/video/offscreen/SDL_offscreenevents_c.h	Tue Sep 24 16:36:48 2019 -0400
     8.3 @@ -0,0 +1,28 @@
     8.4 +/*
     8.5 +  Simple DirectMedia Layer
     8.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     8.7 +
     8.8 +  This software is provided 'as-is', without any express or implied
     8.9 +  warranty.  In no event will the authors be held liable for any damages
    8.10 +  arising from the use of this software.
    8.11 +
    8.12 +  Permission is granted to anyone to use this software for any purpose,
    8.13 +  including commercial applications, and to alter it and redistribute it
    8.14 +  freely, subject to the following restrictions:
    8.15 +
    8.16 +  1. The origin of this software must not be misrepresented; you must not
    8.17 +     claim that you wrote the original software. If you use this software
    8.18 +     in a product, an acknowledgment in the product documentation would be
    8.19 +     appreciated but is not required.
    8.20 +  2. Altered source versions must be plainly marked as such, and must not be
    8.21 +     misrepresented as being the original software.
    8.22 +  3. This notice may not be removed or altered from any source distribution.
    8.23 +*/
    8.24 +
    8.25 +#include "../../SDL_internal.h"
    8.26 +
    8.27 +#include "SDL_offscreenvideo.h"
    8.28 +
    8.29 +extern void OFFSCREEN_PumpEvents(_THIS);
    8.30 +
    8.31 +/* vi: set ts=4 sw=4 expandtab: */
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/video/offscreen/SDL_offscreenframebuffer.c	Tue Sep 24 16:36:48 2019 -0400
     9.3 @@ -0,0 +1,90 @@
     9.4 +/*
     9.5 +  Simple DirectMedia Layer
     9.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     9.7 +
     9.8 +  This software is provided 'as-is', without any express or implied
     9.9 +  warranty.  In no event will the authors be held liable for any damages
    9.10 +  arising from the use of this software.
    9.11 +
    9.12 +  Permission is granted to anyone to use this software for any purpose,
    9.13 +  including commercial applications, and to alter it and redistribute it
    9.14 +  freely, subject to the following restrictions:
    9.15 +
    9.16 +  1. The origin of this software must not be misrepresented; you must not
    9.17 +     claim that you wrote the original software. If you use this software
    9.18 +     in a product, an acknowledgment in the product documentation would be
    9.19 +     appreciated but is not required.
    9.20 +  2. Altered source versions must be plainly marked as such, and must not be
    9.21 +     misrepresented as being the original software.
    9.22 +  3. This notice may not be removed or altered from any source distribution.
    9.23 +*/
    9.24 +
    9.25 +#include "../../SDL_internal.h"
    9.26 +
    9.27 +#if SDL_VIDEO_DRIVER_OFFSCREEN
    9.28 +
    9.29 +#include "../SDL_sysvideo.h"
    9.30 +#include "SDL_offscreenframebuffer_c.h"
    9.31 +
    9.32 +
    9.33 +#define OFFSCREEN_SURFACE   "_SDL_DummySurface"
    9.34 +
    9.35 +int SDL_OFFSCREEN_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch)
    9.36 +{
    9.37 +    SDL_Surface *surface;
    9.38 +    const Uint32 surface_format = SDL_PIXELFORMAT_RGB888;
    9.39 +    int w, h;
    9.40 +    int bpp;
    9.41 +    Uint32 Rmask, Gmask, Bmask, Amask;
    9.42 +
    9.43 +    /* Free the old framebuffer surface */
    9.44 +    surface = (SDL_Surface *) SDL_GetWindowData(window, OFFSCREEN_SURFACE);
    9.45 +    SDL_FreeSurface(surface);
    9.46 +
    9.47 +    /* Create a new one */
    9.48 +    SDL_PixelFormatEnumToMasks(surface_format, &bpp, &Rmask, &Gmask, &Bmask, &Amask);
    9.49 +    SDL_GetWindowSize(window, &w, &h);
    9.50 +    surface = SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask);
    9.51 +    if (!surface) {
    9.52 +        return -1;
    9.53 +    }
    9.54 +
    9.55 +    /* Save the info and return! */
    9.56 +    SDL_SetWindowData(window, OFFSCREEN_SURFACE, surface);
    9.57 +    *format = surface_format;
    9.58 +    *pixels = surface->pixels;
    9.59 +    *pitch = surface->pitch;
    9.60 +    return 0;
    9.61 +}
    9.62 +
    9.63 +int SDL_OFFSCREEN_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects)
    9.64 +{
    9.65 +    static int frame_number;
    9.66 +    SDL_Surface *surface;
    9.67 +
    9.68 +    surface = (SDL_Surface *) SDL_GetWindowData(window, OFFSCREEN_SURFACE);
    9.69 +    if (!surface) {
    9.70 +        return SDL_SetError("Couldn't find offscreen surface for window");
    9.71 +    }
    9.72 +
    9.73 +    /* Send the data to the display */
    9.74 +    if (SDL_getenv("SDL_VIDEO_OFFSCREEN_SAVE_FRAMES")) {
    9.75 +        char file[128];
    9.76 +        SDL_snprintf(file, sizeof(file), "SDL_window%d-%8.8d.bmp",
    9.77 +                     SDL_GetWindowID(window), ++frame_number);
    9.78 +        SDL_SaveBMP(surface, file);
    9.79 +    }
    9.80 +    return 0;
    9.81 +}
    9.82 +
    9.83 +void SDL_OFFSCREEN_DestroyWindowFramebuffer(_THIS, SDL_Window * window)
    9.84 +{
    9.85 +    SDL_Surface *surface;
    9.86 +
    9.87 +    surface = (SDL_Surface *) SDL_SetWindowData(window, OFFSCREEN_SURFACE, NULL);
    9.88 +    SDL_FreeSurface(surface);
    9.89 +}
    9.90 +
    9.91 +#endif /* SDL_VIDEO_DRIVER_OFFSCREEN */
    9.92 +
    9.93 +/* vi: set ts=4 sw=4 expandtab: */
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/src/video/offscreen/SDL_offscreenframebuffer_c.h	Tue Sep 24 16:36:48 2019 -0400
    10.3 @@ -0,0 +1,28 @@
    10.4 +/*
    10.5 +  Simple DirectMedia Layer
    10.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
    10.7 +
    10.8 +  This software is provided 'as-is', without any express or implied
    10.9 +  warranty.  In no event will the authors be held liable for any damages
   10.10 +  arising from the use of this software.
   10.11 +
   10.12 +  Permission is granted to anyone to use this software for any purpose,
   10.13 +  including commercial applications, and to alter it and redistribute it
   10.14 +  freely, subject to the following restrictions:
   10.15 +
   10.16 +  1. The origin of this software must not be misrepresented; you must not
   10.17 +     claim that you wrote the original software. If you use this software
   10.18 +     in a product, an acknowledgment in the product documentation would be
   10.19 +     appreciated but is not required.
   10.20 +  2. Altered source versions must be plainly marked as such, and must not be
   10.21 +     misrepresented as being the original software.
   10.22 +  3. This notice may not be removed or altered from any source distribution.
   10.23 +*/
   10.24 +
   10.25 +#include "../../SDL_internal.h"
   10.26 +
   10.27 +extern int SDL_OFFSCREEN_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);
   10.28 +extern int SDL_OFFSCREEN_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);
   10.29 +extern void SDL_OFFSCREEN_DestroyWindowFramebuffer(_THIS, SDL_Window * window);
   10.30 +
   10.31 +/* vi: set ts=4 sw=4 expandtab: */
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/src/video/offscreen/SDL_offscreenopengl.c	Tue Sep 24 16:36:48 2019 -0400
    11.3 @@ -0,0 +1,102 @@
    11.4 +/*
    11.5 +  Simple DirectMedia Layer
    11.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
    11.7 +
    11.8 +  This software is provided 'as-is', without any express or implied
    11.9 +  warranty.  In no event will the authors be held liable for any damages
   11.10 +  arising from the use of this software.
   11.11 +
   11.12 +  Permission is granted to anyone to use this software for any purpose,
   11.13 +  including commercial applications, and to alter it and redistribute it
   11.14 +  freely, subject to the following restrictions:
   11.15 +
   11.16 +  1. The origin of this software must not be misrepresented; you must not
   11.17 +     claim that you wrote the original software. If you use this software
   11.18 +     in a product, an acknowledgment in the product documentation would be
   11.19 +     appreciated but is not required.
   11.20 +  2. Altered source versions must be plainly marked as such, and must not be
   11.21 +     misrepresented as being the original software.
   11.22 +  3. This notice may not be removed or altered from any source distribution.
   11.23 +*/
   11.24 +
   11.25 +#include "../../SDL_internal.h"
   11.26 +
   11.27 +#if SDL_VIDEO_DRIVER_OFFSCREEN
   11.28 +
   11.29 +#include "SDL_offscreenopengl.h"
   11.30 +
   11.31 +#include "SDL_opengl.h"
   11.32 +
   11.33 +int
   11.34 +OFFSCREEN_GL_SwapWindow(_THIS, SDL_Window* window)
   11.35 +{
   11.36 +    OFFSCREEN_Window* offscreen_wind = window->driverdata;
   11.37 +
   11.38 +    SDL_EGL_SwapBuffers(_this, offscreen_wind->egl_surface);
   11.39 +    return 0;
   11.40 +}
   11.41 +
   11.42 +int
   11.43 +OFFSCREEN_GL_MakeCurrent(_THIS, SDL_Window* window, SDL_GLContext context)
   11.44 +{
   11.45 +  if (window) {
   11.46 +      EGLSurface egl_surface = ((OFFSCREEN_Window*)window->driverdata)->egl_surface;
   11.47 +      return SDL_EGL_MakeCurrent(_this, egl_surface, context);
   11.48 +  }
   11.49 +
   11.50 +  return SDL_EGL_MakeCurrent(_this, NULL, NULL);
   11.51 +}
   11.52 +
   11.53 +SDL_GLContext
   11.54 +OFFSCREEN_GL_CreateContext(_THIS, SDL_Window* window)
   11.55 +{
   11.56 +    OFFSCREEN_Window* offscreen_window = window->driverdata;
   11.57 +
   11.58 +    SDL_GLContext context;
   11.59 +    context = SDL_EGL_CreateContext(_this, offscreen_window->egl_surface);
   11.60 +
   11.61 +    return context;
   11.62 +}
   11.63 +
   11.64 +int
   11.65 +OFFSCREEN_GL_LoadLibrary(_THIS, const char* path)
   11.66 +{
   11.67 +    int ret = SDL_EGL_LoadLibraryOnly(_this, path);
   11.68 +    if (ret != 0) {
   11.69 +        return ret;
   11.70 +    }
   11.71 +
   11.72 +    ret = SDL_EGL_InitializeOffscreen(_this, 0);
   11.73 +    if (ret != 0) {
   11.74 +        return ret;
   11.75 +    }
   11.76 +
   11.77 +    ret = SDL_EGL_ChooseConfig(_this);
   11.78 +    if (ret != 0) {
   11.79 +        return ret;
   11.80 +    }
   11.81 +
   11.82 +    return 0;
   11.83 +}
   11.84 +
   11.85 +void
   11.86 +OFFSCREEN_GL_UnloadLibrary(_THIS)
   11.87 +{
   11.88 +    SDL_EGL_UnloadLibrary(_this);
   11.89 +}
   11.90 +
   11.91 +void*
   11.92 +OFFSCREEN_GL_GetProcAddress(_THIS, const char* proc)
   11.93 +{
   11.94 +    void* proc_addr = SDL_EGL_GetProcAddress(_this, proc);
   11.95 +
   11.96 +    if (!proc_addr) {
   11.97 +        SDL_SetError("Failed to find proc address!");
   11.98 +    }
   11.99 +
  11.100 +    return proc_addr;
  11.101 +}
  11.102 +
  11.103 +#endif /* SDL_VIDEO_DRIVER_OFFSCREEN */
  11.104 +
  11.105 +/* vi: set ts=4 sw=4 expandtab: */
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/src/video/offscreen/SDL_offscreenopengl.h	Tue Sep 24 16:36:48 2019 -0400
    12.3 @@ -0,0 +1,54 @@
    12.4 +/*
    12.5 +  Simple DirectMedia Layer
    12.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
    12.7 +
    12.8 +  This software is provided 'as-is', without any express or implied
    12.9 +  warranty.  In no event will the authors be held liable for any damages
   12.10 +  arising from the use of this software.
   12.11 +
   12.12 +  Permission is granted to anyone to use this software for any purpose,
   12.13 +  including commercial applications, and to alter it and redistribute it
   12.14 +  freely, subject to the following restrictions:
   12.15 +
   12.16 +  1. The origin of this software must not be misrepresented; you must not
   12.17 +     claim that you wrote the original software. If you use this software
   12.18 +     in a product, an acknowledgment in the product documentation would be
   12.19 +     appreciated but is not required.
   12.20 +  2. Altered source versions must be plainly marked as such, and must not be
   12.21 +     misrepresented as being the original software.
   12.22 +  3. This notice may not be removed or altered from any source distribution.
   12.23 +*/
   12.24 +
   12.25 +#ifndef _SDL_offscreenopengl_h
   12.26 +#define _SDL_offscreenopengl_h
   12.27 +
   12.28 +#include "SDL_offscreenwindow.h"
   12.29 +
   12.30 +#include "../SDL_egl_c.h"
   12.31 +
   12.32 +#define OFFSCREEN_GL_DeleteContext   SDL_EGL_DeleteContext
   12.33 +#define OFFSCREEN_GL_GetSwapInterval SDL_EGL_GetSwapInterval
   12.34 +#define OFFSCREEN_GL_SetSwapInterval SDL_EGL_SetSwapInterval
   12.35 +
   12.36 +extern int
   12.37 +OFFSCREEN_GL_SwapWindow(_THIS, SDL_Window* window);
   12.38 +
   12.39 +extern int
   12.40 +OFFSCREEN_GL_MakeCurrent(_THIS, SDL_Window* window, SDL_GLContext context);
   12.41 +
   12.42 +extern SDL_GLContext
   12.43 +OFFSCREEN_GL_CreateContext(_THIS, SDL_Window* window);
   12.44 +
   12.45 +extern int
   12.46 +OFFSCREEN_GL_LoadLibrary(_THIS, const char* path);
   12.47 +
   12.48 +extern void
   12.49 +OFFSCREEN_GL_UnloadLibrary(_THIS);
   12.50 +
   12.51 +extern void*
   12.52 +OFFSCREEN_GL_GetProcAddress(_THIS, const char* proc);
   12.53 +
   12.54 +#endif /* _SDL_offscreenopengl_h */
   12.55 +
   12.56 +/* vi: set ts=4 sw=4 expandtab: */
   12.57 +
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/src/video/offscreen/SDL_offscreenvideo.c	Tue Sep 24 16:36:48 2019 -0400
    13.3 @@ -0,0 +1,166 @@
    13.4 +/*
    13.5 +  Simple DirectMedia Layer
    13.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
    13.7 +
    13.8 +  This software is provided 'as-is', without any express or implied
    13.9 +  warranty.  In no event will the authors be held liable for any damages
   13.10 +  arising from the use of this software.
   13.11 +
   13.12 +  Permission is granted to anyone to use this software for any purpose,
   13.13 +  including commercial applications, and to alter it and redistribute it
   13.14 +  freely, subject to the following restrictions:
   13.15 +
   13.16 +  1. The origin of this software must not be misrepresented; you must not
   13.17 +     claim that you wrote the original software. If you use this software
   13.18 +     in a product, an acknowledgment in the product documentation would be
   13.19 +     appreciated but is not required.
   13.20 +  2. Altered source versions must be plainly marked as such, and must not be
   13.21 +     misrepresented as being the original software.
   13.22 +  3. This notice may not be removed or altered from any source distribution.
   13.23 +*/
   13.24 +
   13.25 +#include "../../SDL_internal.h"
   13.26 +
   13.27 +#if SDL_VIDEO_DRIVER_OFFSCREEN
   13.28 +
   13.29 +/* Offscreen video driver is similar to dummy driver, however its purpose
   13.30 + * is enabling applications to use some of the SDL video functionality
   13.31 + * (notably context creation) while not requiring a display output.
   13.32 + *
   13.33 + * An example would be running a graphical program on a headless box
   13.34 + * for automated testing.
   13.35 + */
   13.36 +
   13.37 +#include "SDL_video.h"
   13.38 +#include "SDL_mouse.h"
   13.39 +#include "../SDL_sysvideo.h"
   13.40 +#include "../SDL_pixels_c.h"
   13.41 +#include "../../events/SDL_events_c.h"
   13.42 +
   13.43 +#include "SDL_offscreenvideo.h"
   13.44 +#include "SDL_offscreenevents_c.h"
   13.45 +#include "SDL_offscreenframebuffer_c.h"
   13.46 +#include "SDL_offscreenopengl.h"
   13.47 +
   13.48 +#define OFFSCREENVID_DRIVER_NAME "offscreen"
   13.49 +
   13.50 +/* Initialization/Query functions */
   13.51 +static int OFFSCREEN_VideoInit(_THIS);
   13.52 +static int OFFSCREEN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
   13.53 +static void OFFSCREEN_VideoQuit(_THIS);
   13.54 +
   13.55 +/* OFFSCREEN driver bootstrap functions */
   13.56 +
   13.57 +static int
   13.58 +OFFSCREEN_Available(void)
   13.59 +{
   13.60 +    /* Consider it always available */
   13.61 +    return (1);
   13.62 +}
   13.63 +
   13.64 +static void
   13.65 +OFFSCREEN_DeleteDevice(SDL_VideoDevice * device)
   13.66 +{
   13.67 +    SDL_free(device);
   13.68 +}
   13.69 +
   13.70 +static SDL_VideoDevice *
   13.71 +OFFSCREEN_CreateDevice(int devindex)
   13.72 +{
   13.73 +    SDL_VideoDevice *device;
   13.74 +
   13.75 +    /* Initialize all variables that we clean on shutdown */
   13.76 +    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
   13.77 +    if (!device) {
   13.78 +        SDL_OutOfMemory();
   13.79 +        return (0);
   13.80 +    }
   13.81 +
   13.82 +    /* General video */
   13.83 +    device->VideoInit = OFFSCREEN_VideoInit;
   13.84 +    device->VideoQuit = OFFSCREEN_VideoQuit;
   13.85 +    device->SetDisplayMode = OFFSCREEN_SetDisplayMode;
   13.86 +    device->PumpEvents = OFFSCREEN_PumpEvents;
   13.87 +    device->CreateWindowFramebuffer = SDL_OFFSCREEN_CreateWindowFramebuffer;
   13.88 +    device->UpdateWindowFramebuffer = SDL_OFFSCREEN_UpdateWindowFramebuffer;
   13.89 +    device->DestroyWindowFramebuffer = SDL_OFFSCREEN_DestroyWindowFramebuffer;
   13.90 +    device->free = OFFSCREEN_DeleteDevice;
   13.91 +
   13.92 +    /* GL context */
   13.93 +    device->GL_SwapWindow = OFFSCREEN_GL_SwapWindow;
   13.94 +    device->GL_MakeCurrent = OFFSCREEN_GL_MakeCurrent;
   13.95 +    device->GL_CreateContext = OFFSCREEN_GL_CreateContext;
   13.96 +    device->GL_DeleteContext = OFFSCREEN_GL_DeleteContext;
   13.97 +    device->GL_LoadLibrary = OFFSCREEN_GL_LoadLibrary;
   13.98 +    device->GL_UnloadLibrary = OFFSCREEN_GL_UnloadLibrary;
   13.99 +    device->GL_GetProcAddress = OFFSCREEN_GL_GetProcAddress;
  13.100 +    device->GL_GetSwapInterval = OFFSCREEN_GL_GetSwapInterval;
  13.101 +    device->GL_SetSwapInterval = OFFSCREEN_GL_SetSwapInterval;
  13.102 +
  13.103 +    /* "Window" */
  13.104 +    device->CreateSDLWindow = OFFSCREEN_CreateWindow;
  13.105 +    device->DestroyWindow = OFFSCREEN_DestroyWindow;
  13.106 +
  13.107 +    return device;
  13.108 +}
  13.109 +
  13.110 +VideoBootStrap OFFSCREEN_bootstrap = {
  13.111 +    OFFSCREENVID_DRIVER_NAME, "SDL offscreen video driver",
  13.112 +    OFFSCREEN_Available, OFFSCREEN_CreateDevice
  13.113 +};
  13.114 +
  13.115 +static Uint32
  13.116 +OFFSCREEN_GetGlobalMouseState(int *x, int *y)
  13.117 +{
  13.118 +    if (x) {
  13.119 +        *x = 0;
  13.120 +    }
  13.121 +
  13.122 +    if (y) {
  13.123 +        *y = 0;
  13.124 +    }
  13.125 +    return 0;
  13.126 +}
  13.127 +
  13.128 +int
  13.129 +OFFSCREEN_VideoInit(_THIS)
  13.130 +{
  13.131 +    SDL_DisplayMode mode;
  13.132 +    SDL_Mouse *mouse = NULL;
  13.133 +
  13.134 +    /* Use a fake 32-bpp desktop mode */
  13.135 +    mode.format = SDL_PIXELFORMAT_RGB888;
  13.136 +    mode.w = 1024;
  13.137 +    mode.h = 768;
  13.138 +    mode.refresh_rate = 0;
  13.139 +    mode.driverdata = NULL;
  13.140 +    if (SDL_AddBasicVideoDisplay(&mode) < 0) {
  13.141 +        return -1;
  13.142 +    }
  13.143 +
  13.144 +    SDL_zero(mode);
  13.145 +    SDL_AddDisplayMode(&_this->displays[0], &mode);
  13.146 +
  13.147 +    /* Init mouse */
  13.148 +    mouse = SDL_GetMouse();
  13.149 +    /* This function needs to be implemented by every driver */
  13.150 +    mouse->GetGlobalMouseState = OFFSCREEN_GetGlobalMouseState;
  13.151 +    
  13.152 +    /* We're done! */
  13.153 +    return 0;
  13.154 +}
  13.155 +
  13.156 +static int
  13.157 +OFFSCREEN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
  13.158 +{
  13.159 +    return 0;
  13.160 +}
  13.161 +
  13.162 +void
  13.163 +OFFSCREEN_VideoQuit(_THIS)
  13.164 +{
  13.165 +}
  13.166 +
  13.167 +#endif /* SDL_VIDEO_DRIVER_OFFSCREEN */
  13.168 +
  13.169 +/* vi: set ts=4 sw=4 expandtab: */
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/src/video/offscreen/SDL_offscreenvideo.h	Tue Sep 24 16:36:48 2019 -0400
    14.3 @@ -0,0 +1,32 @@
    14.4 +/*
    14.5 +  Simple DirectMedia Layer
    14.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
    14.7 +
    14.8 +  This software is provided 'as-is', without any express or implied
    14.9 +  warranty.  In no event will the authors be held liable for any damages
   14.10 +  arising from the use of this software.
   14.11 +
   14.12 +  Permission is granted to anyone to use this software for any purpose,
   14.13 +  including commercial applications, and to alter it and redistribute it
   14.14 +  freely, subject to the following restrictions:
   14.15 +
   14.16 +  1. The origin of this software must not be misrepresented; you must not
   14.17 +     claim that you wrote the original software. If you use this software
   14.18 +     in a product, an acknowledgment in the product documentation would be
   14.19 +     appreciated but is not required.
   14.20 +  2. Altered source versions must be plainly marked as such, and must not be
   14.21 +     misrepresented as being the original software.
   14.22 +  3. This notice may not be removed or altered from any source distribution.
   14.23 +*/
   14.24 +
   14.25 +#include "../../SDL_internal.h"
   14.26 +
   14.27 +#ifndef _SDL_offscreenvideo_h
   14.28 +#define _SDL_offscreenvideo_h
   14.29 +
   14.30 +#include "../SDL_sysvideo.h"
   14.31 +#include "../SDL_egl_c.h"
   14.32 +
   14.33 +#endif /* _SDL_offscreenvideo_h */
   14.34 +
   14.35 +/* vi: set ts=4 sw=4 expandtab: */
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/src/video/offscreen/SDL_offscreenwindow.c	Tue Sep 24 16:36:48 2019 -0400
    15.3 @@ -0,0 +1,87 @@
    15.4 +/*
    15.5 +  Simple DirectMedia Layer
    15.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
    15.7 +
    15.8 +  This software is provided 'as-is', without any express or implied
    15.9 +  warranty.  In no event will the authors be held liable for any damages
   15.10 +  arising from the use of this software.
   15.11 +
   15.12 +  Permission is granted to anyone to use this software for any purpose,
   15.13 +  including commercial applications, and to alter it and redistribute it
   15.14 +  freely, subject to the following restrictions:
   15.15 +
   15.16 +  1. The origin of this software must not be misrepresented; you must not
   15.17 +     claim that you wrote the original software. If you use this software
   15.18 +     in a product, an acknowledgment in the product documentation would be
   15.19 +     appreciated but is not required.
   15.20 +  2. Altered source versions must be plainly marked as such, and must not be
   15.21 +     misrepresented as being the original software.
   15.22 +  3. This notice may not be removed or altered from any source distribution.
   15.23 +*/
   15.24 +
   15.25 +#include "../../SDL_internal.h"
   15.26 +
   15.27 +#if SDL_VIDEO_DRIVER_OFFSCREEN
   15.28 +
   15.29 +#include "../SDL_egl_c.h"
   15.30 +#include "../SDL_sysvideo.h"
   15.31 +
   15.32 +#include "SDL_offscreenwindow.h"
   15.33 +
   15.34 +int
   15.35 +OFFSCREEN_CreateWindow(_THIS, SDL_Window* window)
   15.36 +{
   15.37 +    OFFSCREEN_Window* offscreen_window = SDL_calloc(1, sizeof(OFFSCREEN_Window));
   15.38 +
   15.39 +    if (!offscreen_window) {
   15.40 +        return SDL_OutOfMemory();
   15.41 +    }
   15.42 +
   15.43 +    window->driverdata = offscreen_window;
   15.44 +
   15.45 +    if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   15.46 +        window->x = 0;
   15.47 +    }
   15.48 +
   15.49 +    if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   15.50 +        window->y = 0;
   15.51 +    }
   15.52 +
   15.53 +    offscreen_window->sdl_window = window;
   15.54 +
   15.55 +    if (window->flags & SDL_WINDOW_OPENGL) {
   15.56 +
   15.57 +        if (!_this->egl_data) {
   15.58 +            return SDL_SetError("Cannot create an OPENGL window invalid egl_data");
   15.59 +        }
   15.60 +
   15.61 +        offscreen_window->egl_surface = SDL_EGL_CreateOffscreenSurface(_this, window->w, window->h);
   15.62 +
   15.63 +        if (offscreen_window->egl_surface == EGL_NO_SURFACE) {
   15.64 +            return SDL_SetError("Failed to created an offscreen surface (EGL display: %p)",
   15.65 +                                _this->egl_data->egl_display);
   15.66 +        }
   15.67 +    }
   15.68 +    else {
   15.69 +        offscreen_window->egl_surface = EGL_NO_SURFACE;
   15.70 +    }
   15.71 +
   15.72 +    return 0;
   15.73 +}
   15.74 +
   15.75 +void
   15.76 +OFFSCREEN_DestroyWindow(_THIS, SDL_Window* window)
   15.77 +{
   15.78 +    OFFSCREEN_Window* offscreen_window = window->driverdata;
   15.79 +
   15.80 +    if (offscreen_window) {
   15.81 +        SDL_EGL_DestroySurface(_this, offscreen_window->egl_surface);
   15.82 +        SDL_free(offscreen_window);
   15.83 +    }
   15.84 +
   15.85 +    window->driverdata = NULL;
   15.86 +}
   15.87 +
   15.88 +#endif /* SDL_VIDEO_DRIVER_OFFSCREEN */
   15.89 +
   15.90 +/* vi: set ts=4 sw=4 expandtab: */
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/src/video/offscreen/SDL_offscreenwindow.h	Tue Sep 24 16:36:48 2019 -0400
    16.3 @@ -0,0 +1,46 @@
    16.4 +/*
    16.5 +  Simple DirectMedia Layer
    16.6 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
    16.7 +
    16.8 +  This software is provided 'as-is', without any express or implied
    16.9 +  warranty.  In no event will the authors be held liable for any damages
   16.10 +  arising from the use of this software.
   16.11 +
   16.12 +  Permission is granted to anyone to use this software for any purpose,
   16.13 +  including commercial applications, and to alter it and redistribute it
   16.14 +  freely, subject to the following restrictions:
   16.15 +
   16.16 +  1. The origin of this software must not be misrepresented; you must not
   16.17 +     claim that you wrote the original software. If you use this software
   16.18 +     in a product, an acknowledgment in the product documentation would be
   16.19 +     appreciated but is not required.
   16.20 +  2. Altered source versions must be plainly marked as such, and must not be
   16.21 +     misrepresented as being the original software.
   16.22 +  3. This notice may not be removed or altered from any source distribution.
   16.23 +*/
   16.24 +
   16.25 +#ifndef _SDL_offscreenwindow_h
   16.26 +#define _SDL_offscreenwindow_h
   16.27 +
   16.28 +#include "../SDL_sysvideo.h"
   16.29 +#include "SDL_syswm.h"
   16.30 +
   16.31 +#include "SDL_offscreenvideo.h"
   16.32 +
   16.33 +typedef struct {
   16.34 +    SDL_Window* sdl_window;
   16.35 +    EGLSurface egl_surface;
   16.36 +
   16.37 +} OFFSCREEN_Window;
   16.38 +
   16.39 +
   16.40 +extern int
   16.41 +OFFSCREEN_CreateWindow(_THIS, SDL_Window* window);
   16.42 +
   16.43 +extern void
   16.44 +OFFSCREEN_DestroyWindow(_THIS, SDL_Window* window);
   16.45 +
   16.46 +#endif /* _SDL_offscreenwindow */
   16.47 +
   16.48 +/* vi: set ts=4 sw=4 expandtab: */
   16.49 +
    17.1 --- a/test/CMakeLists.txt	Mon Sep 23 18:30:22 2019 -0400
    17.2 +++ b/test/CMakeLists.txt	Tue Sep 24 16:36:48 2019 -0400
    17.3 @@ -86,6 +86,7 @@
    17.4  add_executable(testcustomcursor testcustomcursor.c)
    17.5  add_executable(controllermap controllermap.c)
    17.6  add_executable(testvulkan testvulkan.c)
    17.7 +add_executable(testoffscreen testoffscreen.c)
    17.8  
    17.9  # HACK: Dummy target to cause the resource files to be copied to the build directory.
   17.10  # Need to make it an executable so we can use the TARGET_FILE_DIR generator expression.
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/test/testoffscreen.c	Tue Sep 24 16:36:48 2019 -0400
    18.3 @@ -0,0 +1,170 @@
    18.4 +/*
    18.5 +  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
    18.6 +
    18.7 +  This software is provided 'as-is', without any express or implied
    18.8 +  warranty.  In no event will the authors be held liable for any damages
    18.9 +  arising from the use of this software.
   18.10 +
   18.11 +  Permission is granted to anyone to use this software for any purpose,
   18.12 +  including commercial applications, and to alter it and redistribute it
   18.13 +  freely.
   18.14 +*/
   18.15 +
   18.16 +/* Simple program: picks the offscreen backend and renders each frame to a bmp */
   18.17 +
   18.18 +#include <stdlib.h>
   18.19 +#include <stdio.h>
   18.20 +#include <time.h>
   18.21 +
   18.22 +#ifdef __EMSCRIPTEN__
   18.23 +#include <emscripten/emscripten.h>
   18.24 +#endif
   18.25 +
   18.26 +#include "SDL.h"
   18.27 +#include "SDL_stdinc.h"
   18.28 +#include "SDL_opengl.h"
   18.29 +
   18.30 +static SDL_Renderer *renderer = NULL;
   18.31 +static SDL_Window *window = NULL;
   18.32 +static int done = SDL_FALSE;
   18.33 +static int frame_number = 0;
   18.34 +static int width = 640;
   18.35 +static int height = 480;
   18.36 +static int max_frames = 200;
   18.37 +
   18.38 +void
   18.39 +draw()
   18.40 +{
   18.41 +    SDL_Rect Rect;
   18.42 +
   18.43 +    SDL_SetRenderDrawColor(renderer, 0x10, 0x9A, 0xCE, 0xFF);
   18.44 +    SDL_RenderClear(renderer);
   18.45 +
   18.46 +    /* Grow based on the frame just to show a difference per frame of the region */
   18.47 +    Rect.x = 0;
   18.48 +    Rect.y = 0;
   18.49 +    Rect.w = (frame_number * 2) % width;
   18.50 +    Rect.h = (frame_number * 2) % height;
   18.51 +    SDL_SetRenderDrawColor(renderer, 0xFF, 0x10, 0x21, 0xFF);
   18.52 +    SDL_RenderFillRect(renderer, &Rect);
   18.53 +
   18.54 +    SDL_RenderPresent(renderer);
   18.55 +}
   18.56 +
   18.57 +void
   18.58 +save_surface_to_bmp()
   18.59 +{
   18.60 +    SDL_Surface* surface;
   18.61 +    Uint32 r_mask, g_mask, b_mask, a_mask;
   18.62 +    Uint32 pixel_format;
   18.63 +    char file[128];
   18.64 +    int bbp;
   18.65 +
   18.66 +    pixel_format = SDL_GetWindowPixelFormat(window);
   18.67 +    SDL_PixelFormatEnumToMasks(pixel_format, &bbp, &r_mask, &g_mask, &b_mask, &a_mask);
   18.68 +
   18.69 +    surface = SDL_CreateRGBSurface(0, width, height, bbp, r_mask, g_mask, b_mask, a_mask);
   18.70 +    SDL_RenderReadPixels(renderer, NULL, pixel_format, (void*)surface->pixels, surface->pitch);
   18.71 +
   18.72 +    SDL_snprintf(file, sizeof(file), "SDL_window%d-%8.8d.bmp",
   18.73 +        SDL_GetWindowID(window), ++frame_number);
   18.74 +
   18.75 +    SDL_SaveBMP(surface, file);
   18.76 +    SDL_FreeSurface(surface);
   18.77 +}
   18.78 +
   18.79 +void
   18.80 +loop()
   18.81 +{
   18.82 +    SDL_Event event;
   18.83 +
   18.84 +    /* Check for events */
   18.85 +    while (SDL_PollEvent(&event)) {
   18.86 +        switch (event.type) {
   18.87 +            case SDL_QUIT:
   18.88 +            done = SDL_TRUE;
   18.89 +            break;
   18.90 +        }
   18.91 +    }
   18.92 +
   18.93 +    draw();
   18.94 +    save_surface_to_bmp();
   18.95 +
   18.96 +#ifdef __EMSCRIPTEN__
   18.97 +    if (done) {
   18.98 +        emscripten_cancel_main_loop();
   18.99 +    }
  18.100 +#endif
  18.101 +}
  18.102 +
  18.103 +int
  18.104 +main(int argc, char *argv[])
  18.105 +{
  18.106 +    Uint32 then, now, frames;
  18.107 +
  18.108 +    /* Enable standard application logging */
  18.109 +    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
  18.110 +
  18.111 +    /* Force the offscreen renderer, if it cannot be created then fail out */
  18.112 +    if (SDL_VideoInit("offscreen") < 0) {
  18.113 +        SDL_Log("Couldn't initialize the offscreen video driver: %s\n",
  18.114 +            SDL_GetError());
  18.115 +        return SDL_FALSE;
  18.116 +    }
  18.117 +
  18.118 +	/* If OPENGL fails to init it will fallback to using a framebuffer for rendering */
  18.119 +    window = SDL_CreateWindow("Offscreen Test",
  18.120 +                 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
  18.121 +                 width, height, 0);
  18.122 +
  18.123 +    if (!window) {
  18.124 +        SDL_Log("Couldn't create window: %s\n",
  18.125 +            SDL_GetError());
  18.126 +        return SDL_FALSE;
  18.127 +    }
  18.128 +
  18.129 +    renderer = SDL_CreateRenderer(window, -1, 0);
  18.130 +
  18.131 +    if (!renderer) {
  18.132 +        SDL_Log("Couldn't create renderer: %s\n",
  18.133 +            SDL_GetError());
  18.134 +        return SDL_FALSE;
  18.135 +    }
  18.136 +
  18.137 +    SDL_RenderClear(renderer);
  18.138 +
  18.139 +    srand((unsigned int)time(NULL));
  18.140 +
  18.141 +    /* Main render loop */
  18.142 +    frames = 0;
  18.143 +    then = SDL_GetTicks();
  18.144 +    done = 0;
  18.145 +
  18.146 +    SDL_Log("Rendering %i frames offscreen\n", max_frames);
  18.147 +
  18.148 +#ifdef __EMSCRIPTEN__
  18.149 +    emscripten_set_main_loop(loop, 0, 1);
  18.150 +#else
  18.151 +    while (!done && frames < max_frames) {
  18.152 +        ++frames;
  18.153 +        loop();
  18.154 +
  18.155 +        /* Print out some timing information, along with remaining frames */
  18.156 +        if (frames % (max_frames / 10) == 0) {
  18.157 +            now = SDL_GetTicks();
  18.158 +            if (now > then) {
  18.159 +                double fps = ((double) frames * 1000) / (now - then);
  18.160 +                SDL_Log("Frames remaining: %i rendering at %2.2f frames per second\n", max_frames - frames, fps);
  18.161 +            }
  18.162 +        }
  18.163 +    }
  18.164 +#endif
  18.165 +
  18.166 +    SDL_DestroyRenderer(renderer);
  18.167 +    SDL_DestroyWindow(window);
  18.168 +    SDL_Quit();
  18.169 +
  18.170 +    return 0;
  18.171 +}
  18.172 +
  18.173 +/* vi: set ts=4 sw=4 expandtab: */