Commit 368fa947 authored by Jason Daly's avatar Jason Daly Committed by Camilla Berglund
Browse files

Add headless OSMesa backend

Allows creation and drawing to in-memory OpenGL contexts.

This backend does not provide input.

Related to #850.
parent a90ee65f
# Try to find OSMesa on a Unix system
#
# This will define:
#
# OSMESA_LIBRARIES - Link these to use OSMesa
# OSMESA_INCLUDE_DIR - Include directory for OSMesa
#
# Copyright (c) 2014 Brandon Schaefer <brandon.schaefer@canonical.com>
if (NOT WIN32)
find_package (PkgConfig)
pkg_check_modules (PKG_OSMESA QUIET osmesa)
set (OSMESA_INCLUDE_DIR ${PKG_OSMESA_INCLUDE_DIRS})
set (OSMESA_LIBRARIES ${PKG_OSMESA_LIBRARIES})
endif ()
......@@ -41,6 +41,7 @@ endif()
if (UNIX AND NOT APPLE)
option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF)
option(GLFW_USE_MIR "Use Mir for window creation" OFF)
option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF)
endif()
if (MSVC)
......@@ -154,6 +155,9 @@ elseif (UNIX)
elseif (GLFW_USE_MIR)
set(_GLFW_MIR 1)
message(STATUS "Using Mir for window creation")
elseif (GLFW_USE_OSMESA)
set(_GLFW_OSMESA 1)
message(STATUS "Using OSMesa for windowless context creation")
else()
set(_GLFW_X11 1)
message(STATUS "Using X11 for window creation")
......@@ -318,6 +322,18 @@ if (_GLFW_MIR)
list(APPEND glfw_LIBRARIES "${XKBCOMMON_LIBRARY}")
endif()
#--------------------------------------------------------------------
# Use OSMesa for offscreen context creation
#--------------------------------------------------------------------
if (_GLFW_OSMESA)
find_package(osmesa REQUIRED)
list(APPEND glfw_PKG_DEPS "osmesa")
list(APPEND glfw_INCLUDE_DIRS "${OSMESA_INCLUDE_DIR}")
list(APPEND glfw_LIBRARIES "${OSMESA_LIBRARIES}"
"${CMAKE_THREAD_LIBS_INIT}")
endif()
#--------------------------------------------------------------------
# Use Cocoa for window creation and NSOpenGL for context creation
#--------------------------------------------------------------------
......
......@@ -448,6 +448,30 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
/*! @brief Returns the color buffer associated with the specified window.
*
* @param[out] width The width of the color buffer.
* @param[out] height The height of the color buffer.
* @param[out] format The pixel format of the color buffer (OSMESA_FORMAT_*).
* @param[out] buffer The buffer data.
* @return 1 if successful, or 0 if not.
*/
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width,
int* height, int* format, void** buffer);
/*! @brief Returns the depth buffer associated with the specified window.
*
* @param[out] width The width of the depth buffer.
* @param[out] height The height of the depth buffer.
* @param[out] bytesPerValue The number of bytes per depth buffer element.
* @param[out] buffer The buffer data.
* @return 1 if successful, or 0 if not.
*/
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width,
int* height, int* bytesPerValue, void** buffer);
#endif
#ifdef __cplusplus
}
#endif
......
......@@ -45,6 +45,12 @@ elseif (_GLFW_MIR)
set(glfw_SOURCES ${common_SOURCES} mir_init.c mir_monitor.c mir_window.c
linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c
egl_context.c)
elseif (_GLFW_OSMESA)
set(glfw_HEADERS ${common_HEADERS} osmesa_platform.h
posix_time.h posix_tls.h osmesa_context.h)
set(glfw_SOURCES ${common_SOURCES} osmesa_init.c osmesa_monitor.c
osmesa_window.c posix_time.c posix_tls.c
osmesa_context.c)
endif()
if (APPLE)
......
......@@ -44,6 +44,8 @@
#cmakedefine _GLFW_WAYLAND
// Define this to 1 if building GLFW for Mir
#cmakedefine _GLFW_MIR
// Define this to 1 if building GLFW for OSMesa
#cmakedefine _GLFW_OSMESA
// Define this to 1 if building as a shared library / dynamic library / DLL
#cmakedefine _GLFW_BUILD_DLL
......
......@@ -172,6 +172,8 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
#include "wl_platform.h"
#elif defined(_GLFW_MIR)
#include "mir_platform.h"
#elif defined(_GLFW_OSMESA)
#include "osmesa_platform.h"
#else
#error "No supported window creation API selected"
#endif
......
#include <stdlib.h>
#include <string.h>
#include "internal.h"
#include "osmesa_context.h"
static void makeContextCurrentOSMesa(_GLFWwindow* window)
{
if (window)
{
// Check to see if we need to allocate a new buffer.
if ((window->context.osmesa.buffer == NULL) ||
(window->osmesa.width != window->context.osmesa.width) ||
(window->osmesa.height != window->context.osmesa.height))
{
// Free the current buffer, if necessary.
if (window->context.osmesa.buffer != NULL)
{
free(window->context.osmesa.buffer);
}
// Allocate the new buffer (width * height * 1 byte per RGBA
// channel).
window->context.osmesa.buffer = (unsigned char *) malloc(
window->osmesa.width * window->osmesa.height * 4);
// Update the context size.
window->context.osmesa.width = window->osmesa.width;
window->context.osmesa.height = window->osmesa.height;
}
// Make the context current.
if (!OSMesaMakeCurrent(window->context.osmesa.handle,
window->context.osmesa.buffer,
0x1401, // GL_UNSIGNED_BYTE
window->osmesa.width, window->osmesa.height))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"OSMesa: Failed to make context current.");
return;
}
}
else
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"OSMesa: Releasing the current context is not supported.");
}
_glfwPlatformSetCurrentContext(window);
}
static GLFWglproc getProcAddressOSMesa(const char* procname)
{
_GLFWwindow* window = _glfwPlatformGetCurrentContext();
if (window->context.osmesa.handle)
{
return (GLFWglproc) OSMesaGetProcAddress(procname);
}
return NULL;
}
static void destroyContextOSMesa(_GLFWwindow* window)
{
if (window->context.osmesa.handle != NULL)
{
OSMesaDestroyContext(window->context.osmesa.handle);
window->context.osmesa.handle = NULL;
}
if (window->context.osmesa.buffer != NULL)
{
free(window->context.osmesa.buffer);
window->context.osmesa.width = 0;
window->context.osmesa.height = 0;
}
}
static void swapBuffersOSMesa(_GLFWwindow* window) {}
static void swapIntervalOSMesa(int interval) {}
static int extensionSupportedOSMesa()
{
return GLFW_FALSE;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
GLFWbool _glfwInitOSMesa(void)
{
int i;
const char* sonames[] =
{
#if defined(_GLFW_WIN32)
"libOSMesa.dll",
"OSMesa.dll",
#elif defined(_GLFW_COCOA)
"libOSMesa.dylib",
#else
"libOSMesa.so.6",
#endif
NULL
};
if (_glfw.osmesa.handle)
return GLFW_TRUE;
for (i = 0; sonames[i]; i++)
{
_glfw.osmesa.handle = _glfw_dlopen(sonames[i]);
if (_glfw.osmesa.handle)
break;
}
if (!_glfw.osmesa.handle)
{
_glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found");
return GLFW_FALSE;
}
_glfw.osmesa.prefix = (strncmp(sonames[i], "lib", 3) == 0);
_glfw.osmesa.CreateContext = (PFNOSMESACREATECONTEXTPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContext");
_glfw.osmesa.DestroyContext = (PFNOSMESADESTROYCONTEXTPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext");
_glfw.osmesa.MakeCurrent = (PFNOSMESAMAKECURRENTPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent");
_glfw.osmesa.GetCurrentContext = (PFNOSMESAGETCURRENTCONTEXTPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetCurrentContext");
_glfw.osmesa.PixelStore = (PFNOSMESAPIXELSTOREPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaPixelStore");
_glfw.osmesa.GetIntegerv = (PFNOSMESAGETINTEGERVPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetIntegerv");
_glfw.osmesa.GetColorBuffer = (PFNOSMESAGETCOLORBUFFERPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer");
_glfw.osmesa.GetDepthBuffer = (PFNOSMESAGETDEPTHBUFFERPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer");
_glfw.osmesa.GetProcAddress = (PFNOSMESAGETPROCADDRESSPROC)
_glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress");
if (!_glfw.osmesa.CreateContext ||
!_glfw.osmesa.DestroyContext ||
!_glfw.osmesa.MakeCurrent ||
!_glfw.osmesa.GetCurrentContext ||
!_glfw.osmesa.PixelStore ||
!_glfw.osmesa.GetIntegerv ||
!_glfw.osmesa.GetColorBuffer ||
!_glfw.osmesa.GetDepthBuffer ||
!_glfw.osmesa.GetProcAddress)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"OSMesa: Failed to load required entry points");
_glfwTerminateOSMesa();
return GLFW_FALSE;
}
return GLFW_TRUE;
}
void _glfwTerminateOSMesa(void)
{
if (_glfw.osmesa.handle)
{
_glfw_dlclose(_glfw.osmesa.handle);
_glfw.osmesa.handle = NULL;
}
}
GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
OSMesaContext share;
if (ctxconfig->share)
share = ctxconfig->share->context.osmesa.handle;
// Initialize the context.
window->context.osmesa.buffer = NULL;
window->context.osmesa.width = 0;
window->context.osmesa.height = 0;
// Create to create an OSMesa context.
window->context.osmesa.handle = OSMesaCreateContext(OSMESA_RGBA, share);
if (window->context.osmesa.handle == 0)
{
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
"OSMesa: Failed to create context.");
return GLFW_FALSE;
}
// Set up the context API.
window->context.makeCurrent = makeContextCurrentOSMesa;
window->context.swapBuffers = swapBuffersOSMesa;
window->context.swapInterval = swapIntervalOSMesa;
window->context.extensionSupported = extensionSupportedOSMesa;
window->context.getProcAddress = getProcAddressOSMesa;
window->context.destroy = destroyContextOSMesa;
return GLFW_TRUE;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
//////////////////////////////////////////////////////////////////////////
#ifndef _glfw3_osmesa_context_h_
#define _glfw3_osmesa_context_h_
#define OSMESA_COLOR_INDEX GL_COLOR_INDEX
#define OSMESA_RGBA 0x1908
#define OSMESA_BGRA 0x1
#define OSMESA_ARGB 0x2
#define OSMESA_RGB 0x1907
#define OSMESA_BGR 0x4
#define OSMESA_RGB_565 0x5
#define OSMESA_ROW_LENGTH 0x10
#define OSMESA_Y_UP 0x11
#define OSMESA_WIDTH 0x20
#define OSMESA_HEIGHT 0x21
#define OSMESA_FORMAT 0x22
#define OSMESA_TYPE 0x23
#define OSMESA_MAX_WIDTH 0x24
#define OSMESA_MAX_HEIGHT 0x25
typedef void* OSMesaContext;
typedef void (*OSMESAproc)();
typedef OSMesaContext (* PFNOSMESACREATECONTEXTPROC)(GLenum, OSMesaContext);
typedef void (* PFNOSMESADESTROYCONTEXTPROC)(OSMesaContext);
typedef int (* PFNOSMESAMAKECURRENTPROC)(OSMesaContext, void*, int, int, int);
typedef OSMesaContext (* PFNOSMESAGETCURRENTCONTEXTPROC)();
typedef void (* PFNOSMESAPIXELSTOREPROC)(int, int);
typedef void (* PFNOSMESAGETINTEGERVPROC)(int, int*);
typedef int (* PFNOSMESAGETCOLORBUFFERPROC)(OSMesaContext, int*, int*, int*, void**);
typedef int (* PFNOSMESAGETDEPTHBUFFERPROC)(OSMesaContext, int*, int*, int*, void**);
typedef GLFWglproc (* PFNOSMESAGETPROCADDRESSPROC)(const char*);
#define OSMesaCreateContext _glfw.osmesa.CreateContext
#define OSMesaDestroyContext _glfw.osmesa.DestroyContext
#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent
#define OSMesaGetCurrentContext _glfw.osmesa.GetCurrentContext
#define OSMesaPixelStore _glfw.osmesa.PixelStore
#define OSMesaGetIntegerv _glfw.osmesa.GetIntegerv
#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer
#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer
#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextOSMesa osmesa
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWctxlibraryOSMesa osmesa
// OSMesa-specific per-context data
//
typedef struct _GLFWcontextOSMesa
{
OSMesaContext handle;
int width;
int height;
void * buffer;
} _GLFWcontextOSMesa;
// OSMesa-specific global data
//
typedef struct _GLFWctxlibraryOSMesa
{
GLFWbool prefix;
void* handle;
PFNOSMESACREATECONTEXTPROC CreateContext;
PFNOSMESADESTROYCONTEXTPROC DestroyContext;
PFNOSMESAMAKECURRENTPROC MakeCurrent;
PFNOSMESAGETCURRENTCONTEXTPROC GetCurrentContext;
PFNOSMESAPIXELSTOREPROC PixelStore;
PFNOSMESAGETINTEGERVPROC GetIntegerv;
PFNOSMESAGETCOLORBUFFERPROC GetColorBuffer;
PFNOSMESAGETDEPTHBUFFERPROC GetDepthBuffer;
PFNOSMESAGETPROCADDRESSPROC GetProcAddress;
} _GLFWctxlibraryOSMesa;
GLFWbool _glfwInitOSMesa(void);
void _glfwTerminateOSMesa(void);
GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
#endif // _glfw3_osmesa_context_h_
#include "internal.h"
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
int _glfwPlatformInit(void)
{
if (!_glfwInitThreadLocalStoragePOSIX()) {
return GLFW_FALSE;
}
_glfwInitTimerPOSIX();
return GLFW_TRUE;
}
void _glfwPlatformTerminate(void)
{
_glfwTerminateOSMesa();
_glfwTerminateThreadLocalStoragePOSIX();
}
const char* _glfwPlatformGetVersionString(void)
{
const char* version = _GLFW_VERSION_NUMBER " OSMESA";
return version;
}
#include "internal.h"
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
_GLFWmonitor** _glfwPlatformGetMonitors(int* count)
{
// OSMesa is headless, so no monitors.
_GLFWmonitor** monitors = NULL;
if (count != NULL) {
*count = 0;
}
return monitors;
}
int _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second)
{
return GLFW_FALSE;
}
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) {}
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)
{
return NULL;
}
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {}
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) {}
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor,
const GLFWgammaramp* ramp) {}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
//////////////////////////////////////////////////////////////////////////
#ifndef _osmesa_platform_h_
#define _osmesa_platform_h_
#include <dlfcn.h>
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowOSMesa osmesa
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorOSMesa osmesa
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorOSMesa osmesa
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWwinlibraryOSMesa osmesawin
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE
#define _GLFW_EGL_CONTEXT_STATE
#define _GLFW_EGL_LIBRARY_CONTEXT_STATE
#include "osmesa_context.h"
#include "posix_time.h"
#include "posix_tls.h"
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
#define _glfw_dlclose(handle) dlclose(handle)
#define _glfw_dlsym(handle, name) dlsym(handle, name)
// TODO(dalyj) "PLACEHOLDER" variables are there to silence "empty struct"
// warnings
// OSMesa-specific per-window data
//
typedef struct _GLFWwindowOSMesa
{
int width;
int height;
} _GLFWwindowOSMesa;
// OSMesa-specific global data
//
typedef struct _GLFWwinlibraryOSMesa
{
int PLACEHOLDER;
} _GLFWwinlibraryOSMesa;
// OSMesa-specific per-monitor data
//
typedef struct _GLFWmonitorOSMesa
{
int PLACEHOLDER;
} _GLFWmonitorOSMesa;
// OSMesa-specific per-cursor data
//
typedef struct _GLFWcursorOSMesa
{
int PLACEHOLDER;
} _GLFWcursorOSMesa;
#endif // _osmesa_platform_h_
#include "internal.h"
#include <assert.h>
int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig)
{
window->osmesa.width = wndconfig->width;
window->osmesa.height = wndconfig->height;
return GLFW_TRUE;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
int _glfwPlatformCreateWindow(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
if (!_glfwInitOSMesa())
return GLFW_FALSE;
if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
return GLFW_FALSE;
if (!createWindow(window, wndconfig))
return GLFW_FALSE;
return GLFW_TRUE;
}
void _glfwPlatformDestroyWindow(_GLFWwindow* window)
{
if (window->context.destroy)
window->context.destroy(window);
}
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) {}
void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count,
const GLFWimage* images) {}
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
_GLFWmonitor* monitor,
int xpos, int ypos,
int width, int height,
int refreshRate) {}
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{
if (xpos != NULL) *xpos = 0;
if (ypos != NULL) *ypos = 0;
}
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) {}
void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)