mirror of
https://github.com/glfw/glfw.git
synced 2025-10-03 13:20:58 +00:00
egl headless
This commit is contained in:
parent
7dbdd2e6a5
commit
0408ee93f1
@ -28,6 +28,8 @@ option(GLFW_VULKAN_STATIC "Assume the Vulkan loader is linked with the applicati
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
cmake_dependent_option(GLFW_USE_EGLHEADLESS "Use EGL for offscreen context creation" OFF
|
||||
"UNIX" OFF)
|
||||
cmake_dependent_option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF
|
||||
"UNIX" OFF)
|
||||
cmake_dependent_option(GLFW_USE_HYBRID_HPG "Force use of high-performance GPU on hybrid systems" OFF
|
||||
@ -143,6 +145,9 @@ if (GLFW_USE_WAYLAND)
|
||||
elseif (GLFW_USE_OSMESA)
|
||||
set(_GLFW_OSMESA 1)
|
||||
message(STATUS "Using OSMesa for headless context creation")
|
||||
elseif (GLFW_USE_EGLHEADLESS)
|
||||
set(_GLFW_EGLHEADLESS 1)
|
||||
message(STATUS "Using EGL for headless context creation")
|
||||
elseif (WIN32)
|
||||
set(_GLFW_WIN32 1)
|
||||
message(STATUS "Using Win32 for window creation")
|
||||
@ -278,6 +283,13 @@ if (_GLFW_OSMESA)
|
||||
list(APPEND glfw_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
|
||||
endif()
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Use EGL for offscreen context creation
|
||||
#--------------------------------------------------------------------
|
||||
if (_GLFW_EGLHEADLESS)
|
||||
list(APPEND glfw_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
|
||||
endif()
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Use Cocoa for window creation and NSOpenGL for context creation
|
||||
#--------------------------------------------------------------------
|
||||
|
@ -60,6 +60,11 @@ elseif (_GLFW_OSMESA)
|
||||
posix_time.h posix_thread.h osmesa_context.h)
|
||||
set(glfw_SOURCES ${common_SOURCES} null_init.c null_monitor.c null_window.c
|
||||
null_joystick.c posix_time.c posix_thread.c osmesa_context.c)
|
||||
elseif (_GLFW_EGLHEADLESS)
|
||||
set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h
|
||||
posix_time.h posix_thread.h egl_context.h)
|
||||
set(glfw_SOURCES ${common_SOURCES} null_init.c null_monitor.c null_window.c
|
||||
null_joystick.c posix_time.c posix_thread.c egl_context.c)
|
||||
endif()
|
||||
|
||||
if (_GLFW_X11 OR _GLFW_WAYLAND)
|
||||
|
@ -34,6 +34,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define setAttrib(a, v) \
|
||||
{ \
|
||||
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
attribs[index++] = a; \
|
||||
attribs[index++] = v; \
|
||||
}
|
||||
|
||||
|
||||
// Return a description of the specified EGL error
|
||||
//
|
||||
@ -91,6 +98,33 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* desired,
|
||||
EGLConfig* result)
|
||||
{
|
||||
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
{
|
||||
EGLint attribs[40];
|
||||
int index = 0;
|
||||
setAttrib(EGL_SURFACE_TYPE, EGL_PBUFFER_BIT);
|
||||
setAttrib(EGL_BLUE_SIZE, 8);
|
||||
setAttrib(EGL_GREEN_SIZE, 8);
|
||||
setAttrib(EGL_RED_SIZE, 8);
|
||||
setAttrib(EGL_DEPTH_SIZE, 8);
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
setAttrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAttrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT);
|
||||
}
|
||||
setAttrib(EGL_NONE, EGL_NONE);
|
||||
|
||||
int num_configs;
|
||||
if (!eglChooseConfig(_glfw.egl.display, attribs, result, 1, &num_configs) || num_configs == 0) {
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
#else
|
||||
EGLConfig* nativeConfigs;
|
||||
_GLFWfbconfig* usableConfigs;
|
||||
const _GLFWfbconfig* closest;
|
||||
@ -186,6 +220,7 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
|
||||
free(usableConfigs);
|
||||
|
||||
return closest != NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void makeContextCurrentEGL(_GLFWwindow* window)
|
||||
@ -239,13 +274,14 @@ static void swapIntervalEGL(int interval)
|
||||
|
||||
static int extensionSupportedEGL(const char* extension)
|
||||
{
|
||||
#if !defined(_GLFW_EGLHEADLESS)
|
||||
const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS);
|
||||
if (extensions)
|
||||
{
|
||||
if (_glfwStringInExtensionString(extension, extensions))
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
@ -359,6 +395,12 @@ GLFWbool _glfwInitEGL(void)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglDestroyContext");
|
||||
_glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface");
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
_glfw.egl.CreatePbufferSurface = (PFN_eglCreatePbufferSurface)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglCreatePbufferSurface");
|
||||
_glfw.egl.ChooseConfig = (PFN_eglChooseConfig)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglChooseConfig");
|
||||
#endif
|
||||
_glfw.egl.MakeCurrent = (PFN_eglMakeCurrent)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent");
|
||||
_glfw.egl.SwapBuffers = (PFN_eglSwapBuffers)
|
||||
@ -381,6 +423,10 @@ GLFWbool _glfwInitEGL(void)
|
||||
!_glfw.egl.DestroySurface ||
|
||||
!_glfw.egl.DestroyContext ||
|
||||
!_glfw.egl.CreateWindowSurface ||
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
!_glfw.egl.CreatePbufferSurface ||
|
||||
!_glfw.egl.ChooseConfig ||
|
||||
#endif
|
||||
!_glfw.egl.MakeCurrent ||
|
||||
!_glfw.egl.SwapBuffers ||
|
||||
!_glfw.egl.SwapInterval ||
|
||||
@ -394,7 +440,11 @@ GLFWbool _glfwInitEGL(void)
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
_glfw.egl.display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
#else
|
||||
_glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY);
|
||||
#endif
|
||||
if (_glfw.egl.display == EGL_NO_DISPLAY)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
@ -446,13 +496,6 @@ void _glfwTerminateEGL(void)
|
||||
}
|
||||
}
|
||||
|
||||
#define setAttrib(a, v) \
|
||||
{ \
|
||||
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
attribs[index++] = a; \
|
||||
attribs[index++] = v; \
|
||||
}
|
||||
|
||||
// Create the OpenGL or OpenGL ES context
|
||||
//
|
||||
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
@ -462,7 +505,6 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
EGLint attribs[40];
|
||||
EGLConfig config;
|
||||
EGLContext share = NULL;
|
||||
int index = 0;
|
||||
|
||||
if (!_glfw.egl.display)
|
||||
{
|
||||
@ -501,6 +543,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
if (_glfw.egl.KHR_create_context)
|
||||
{
|
||||
int mask = 0, flags = 0;
|
||||
@ -573,10 +616,21 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
setAttrib(EGL_CONTEXT_MAJOR_VERSION, ctxconfig->major);
|
||||
setAttrib(EGL_CONTEXT_MINOR_VERSION, ctxconfig->minor);
|
||||
setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
|
||||
#endif
|
||||
|
||||
setAttrib(EGL_NONE, EGL_NONE);
|
||||
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
window->context.egl.handle = eglCreateContext(_glfw.egl.display,
|
||||
config, share, NULL);
|
||||
#else
|
||||
window->context.egl.handle = eglCreateContext(_glfw.egl.display,
|
||||
config, share, attribs);
|
||||
#endif
|
||||
|
||||
if (window->context.egl.handle == EGL_NO_CONTEXT)
|
||||
{
|
||||
@ -586,6 +640,18 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
// Set up attributes for surface creation
|
||||
{
|
||||
int width, height;
|
||||
_glfwPlatformGetFramebufferSize(window, &width, &height);
|
||||
int index = 0;
|
||||
setAttrib(EGL_WIDTH, width);
|
||||
setAttrib(EGL_HEIGHT, height);
|
||||
setAttrib(EGL_NONE, EGL_NONE);
|
||||
}
|
||||
window->context.egl.surface = eglCreatePbufferSurface(_glfw.egl.display, config, attribs);
|
||||
#else
|
||||
// Set up attributes for surface creation
|
||||
{
|
||||
int index = 0;
|
||||
@ -598,12 +664,12 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
|
||||
setAttrib(EGL_NONE, EGL_NONE);
|
||||
}
|
||||
|
||||
window->context.egl.surface =
|
||||
eglCreateWindowSurface(_glfw.egl.display,
|
||||
config,
|
||||
_GLFW_EGL_NATIVE_WINDOW,
|
||||
attribs);
|
||||
#endif
|
||||
if (window->context.egl.surface == EGL_NO_SURFACE)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
@ -656,8 +722,12 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
_GLFW_OPENGL_LIBRARY,
|
||||
#elif defined(_GLFW_WIN32)
|
||||
#elif defined(_GLFW_COCOA)
|
||||
#else
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
"libOpenGL.so.0",
|
||||
#else
|
||||
"libGL.so.1",
|
||||
#endif
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
@ -43,6 +43,10 @@ typedef Window EGLNativeWindowType;
|
||||
#define EGLAPIENTRY
|
||||
typedef struct wl_display* EGLNativeDisplayType;
|
||||
typedef struct wl_egl_window* EGLNativeWindowType;
|
||||
#elif defined(_GLFW_EGLHEADLESS)
|
||||
#define EGLAPIENTRY
|
||||
typedef void* EGLNativeDisplayType;
|
||||
typedef void* EGLNativeWindowType;
|
||||
#else
|
||||
#error "No supported EGL platform selected"
|
||||
#endif
|
||||
@ -67,6 +71,7 @@ typedef struct wl_egl_window* EGLNativeWindowType;
|
||||
#define EGL_SURFACE_TYPE 0x3033
|
||||
#define EGL_WINDOW_BIT 0x0004
|
||||
#define EGL_RENDERABLE_TYPE 0x3040
|
||||
#define EGL_PBUFFER_BIT 0x0001
|
||||
#define EGL_OPENGL_ES_BIT 0x0001
|
||||
#define EGL_OPENGL_ES2_BIT 0x0004
|
||||
#define EGL_OPENGL_BIT 0x0008
|
||||
@ -87,9 +92,12 @@ typedef struct wl_egl_window* EGLNativeWindowType;
|
||||
#define EGL_NO_DISPLAY ((EGLDisplay) 0)
|
||||
#define EGL_NO_CONTEXT ((EGLContext) 0)
|
||||
#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0)
|
||||
#define EGL_HEIGHT 0x3056
|
||||
#define EGL_WIDTH 0x3057
|
||||
|
||||
#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
|
||||
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
|
||||
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
|
||||
#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
|
||||
#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
|
||||
#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd
|
||||
@ -97,8 +105,11 @@ typedef struct wl_egl_window* EGLNativeWindowType;
|
||||
#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf
|
||||
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
|
||||
#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098
|
||||
#define EGL_CONTEXT_MAJOR_VERSION 0x3098
|
||||
#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb
|
||||
#define EGL_CONTEXT_MINOR_VERSION 0x30FB
|
||||
#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd
|
||||
#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD
|
||||
#define EGL_CONTEXT_FLAGS_KHR 0x30fc
|
||||
#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3
|
||||
#define EGL_GL_COLORSPACE_KHR 0x309d
|
||||
@ -127,6 +138,10 @@ typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGL
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext);
|
||||
typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*);
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
typedef EGLSurface (EGLAPIENTRY * PFN_eglCreatePbufferSurface)(EGLDisplay,EGLConfig,const EGLint*);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglChooseConfig)(EGLDisplay,const EGLint*,EGLConfig,EGLint,EGLint*);
|
||||
#endif
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint);
|
||||
@ -143,6 +158,10 @@ typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*);
|
||||
#define eglDestroySurface _glfw.egl.DestroySurface
|
||||
#define eglDestroyContext _glfw.egl.DestroyContext
|
||||
#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
#define eglCreatePbufferSurface _glfw.egl.CreatePbufferSurface
|
||||
#define eglChooseConfig _glfw.egl.ChooseConfig
|
||||
#endif
|
||||
#define eglMakeCurrent _glfw.egl.MakeCurrent
|
||||
#define eglSwapBuffers _glfw.egl.SwapBuffers
|
||||
#define eglSwapInterval _glfw.egl.SwapInterval
|
||||
@ -192,6 +211,10 @@ typedef struct _GLFWlibraryEGL
|
||||
PFN_eglDestroySurface DestroySurface;
|
||||
PFN_eglDestroyContext DestroyContext;
|
||||
PFN_eglCreateWindowSurface CreateWindowSurface;
|
||||
#if defined(_GLFW_EGLHEADLESS)
|
||||
PFN_eglCreatePbufferSurface CreatePbufferSurface;
|
||||
PFN_eglChooseConfig ChooseConfig;
|
||||
#endif
|
||||
PFN_eglMakeCurrent MakeCurrent;
|
||||
PFN_eglSwapBuffers SwapBuffers;
|
||||
PFN_eglSwapInterval SwapInterval;
|
||||
|
@ -44,6 +44,8 @@
|
||||
#cmakedefine _GLFW_WAYLAND
|
||||
// Define this to 1 if building GLFW for OSMesa
|
||||
#cmakedefine _GLFW_OSMESA
|
||||
// Define this to 1 if building GLFW for EGLHeadless
|
||||
#cmakedefine _GLFW_EGLHEADLESS
|
||||
|
||||
// Define this to 1 if building as a shared library / dynamic library / DLL
|
||||
#cmakedefine _GLFW_BUILD_DLL
|
||||
|
@ -189,6 +189,8 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
|
||||
#include "wl_platform.h"
|
||||
#elif defined(_GLFW_OSMESA)
|
||||
#include "null_platform.h"
|
||||
#elif defined(_GLFW_EGLHEADLESS)
|
||||
#include "null_platform.h"
|
||||
#else
|
||||
#error "No supported window creation API selected"
|
||||
#endif
|
||||
|
@ -42,11 +42,23 @@ int _glfwPlatformInit(void)
|
||||
|
||||
void _glfwPlatformTerminate(void)
|
||||
{
|
||||
#if defined(_GLFW_OSMESA)
|
||||
_glfwTerminateOSMesa();
|
||||
#elif defined(_GLFW_EGLHEADLESS)
|
||||
_glfwTerminateEGL();
|
||||
#else
|
||||
#error "No supported context selected"
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetVersionString(void)
|
||||
{
|
||||
#if defined(_GLFW_OSMESA)
|
||||
return _GLFW_VERSION_NUMBER " null OSMesa";
|
||||
#elif defined(_GLFW_EGLHEADLESS)
|
||||
return _GLFW_VERSION_NUMBER " null EGL Headless";
|
||||
#else
|
||||
#error "No supported context selected"
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -34,10 +34,22 @@
|
||||
#define _GLFW_PLATFORM_CURSOR_STATE struct { int dummyCursor; }
|
||||
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE struct { int dummyLibraryWindow; }
|
||||
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; }
|
||||
#if !defined(_GLFW_EGLHEADLESS)
|
||||
#define _GLFW_EGL_CONTEXT_STATE struct { int dummyEGLContext; }
|
||||
#define _GLFW_EGL_LIBRARY_CONTEXT_STATE struct { int dummyEGLLibraryContext; }
|
||||
#endif
|
||||
#if !defined(_GLFW_OSMESA)
|
||||
#define _GLFW_OSMESA_CONTEXT_STATE struct { int dummyOSMesaContext; }
|
||||
#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE struct { int dummyOSMesaLibraryContext; }
|
||||
#endif
|
||||
|
||||
#if defined(_GLFW_OSMESA)
|
||||
#include "osmesa_context.h"
|
||||
#elif defined(_GLFW_EGLHEADLESS)
|
||||
#include "egl_context.h"
|
||||
#else
|
||||
#error "No supported context selected"
|
||||
#endif
|
||||
#include "posix_time.h"
|
||||
#include "posix_thread.h"
|
||||
#include "null_joystick.h"
|
||||
|
@ -54,6 +54,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
|
||||
if (ctxconfig->client != GLFW_NO_API)
|
||||
{
|
||||
#if defined(_GLFW_OSMESA)
|
||||
if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API ||
|
||||
ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
|
||||
{
|
||||
@ -64,9 +65,25 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "Null: EGL not available");
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "Null: context not available");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
#elif defined(_GLFW_EGLHEADLESS)
|
||||
if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
|
||||
{
|
||||
if (!_glfwInitEGL())
|
||||
return GLFW_FALSE;
|
||||
if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "Null: context not available");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
#else
|
||||
#error "No supported context selected"
|
||||
#endif
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
|
Loading…
Reference in New Issue
Block a user