fteqw/engine/gl/gl_videgl.c

628 lines
17 KiB
C
Raw Normal View History

#include "quakedef.h"
#if defined(GLQUAKE) && defined(USE_EGL)
#include "gl_videgl.h"
#include "vr.h"
//EGL_KHR_gl_colorspace
#ifndef EGL_GL_COLORSPACE_KHR
#define EGL_GL_COLORSPACE_KHR 0x309D
#endif
#ifndef EGL_GL_COLORSPACE_SRGB_KHR
#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089
#endif
#ifndef EGL_GL_COLORSPACE_LINEAR_KHR
#define EGL_GL_COLORSPACE_LINEAR_KHR 0x308A
#endif
extern cvar_t vid_vsync;
EGLContext eglctx = EGL_NO_CONTEXT;
EGLDisplay egldpy = EGL_NO_DISPLAY;
EGLSurface eglsurf = EGL_NO_SURFACE;
static dllhandle_t *egllibrary;
static dllhandle_t *eslibrary;
static EGLint (EGLAPIENTRY *qeglGetError)(void);
static EGLDisplay (EGLAPIENTRY *qeglGetPlatformDisplay)(EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
static EGLDisplay (EGLAPIENTRY *qeglGetDisplay)(EGLNativeDisplayType display_id);
static EGLBoolean (EGLAPIENTRY *qeglInitialize)(EGLDisplay dpy, EGLint *major, EGLint *minor);
static EGLBoolean (EGLAPIENTRY *qeglTerminate)(EGLDisplay dpy);
static EGLBoolean (EGLAPIENTRY *qeglGetConfigs)(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
static EGLBoolean (EGLAPIENTRY *qeglChooseConfig)(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
EGLBoolean (EGLAPIENTRY *qeglGetConfigAttrib)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
static EGLBoolean (EGLAPIENTRY *qeglBindAPI) (EGLenum api);
static EGLSurface (EGLAPIENTRY *qeglCreatePlatformWindowSurface)(EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
static EGLSurface (EGLAPIENTRY *qeglCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
static EGLSurface (EGLAPIENTRY *qeglCreatePbufferSurface) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
static EGLBoolean (EGLAPIENTRY *qeglDestroySurface)(EGLDisplay dpy, EGLSurface surface);
static EGLBoolean (EGLAPIENTRY *qeglQuerySurface)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
static EGLBoolean (EGLAPIENTRY *qeglSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
static EGLBoolean (EGLAPIENTRY *qeglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
static EGLContext (EGLAPIENTRY *qeglCreateContext)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
static EGLBoolean (EGLAPIENTRY *qeglDestroyContext)(EGLDisplay dpy, EGLContext ctx);
static void * (EGLAPIENTRY *qeglGetProcAddress) (const char *name);
static EGLBoolean (EGLAPIENTRY *qeglSwapInterval) (EGLDisplay display, EGLint interval);
static dllfunction_t qeglfuncs[] =
{
{(void*)&qeglGetError, "eglGetError"},
{(void*)&qeglGetDisplay, "eglGetDisplay"},
{(void*)&qeglInitialize, "eglInitialize"},
{(void*)&qeglTerminate, "eglTerminate"},
{(void*)&qeglGetConfigs, "eglGetConfigs"},
{(void*)&qeglChooseConfig, "eglChooseConfig"},
{(void*)&qeglGetConfigAttrib, "eglGetConfigAttrib"},
{(void*)&qeglCreatePbufferSurface, "eglCreatePbufferSurface"},
{(void*)&qeglCreateWindowSurface, "eglCreateWindowSurface"},
{(void*)&qeglDestroySurface, "eglDestroySurface"},
{(void*)&qeglQuerySurface, "eglQuerySurface"},
{(void*)&qeglSwapBuffers, "eglSwapBuffers"},
{(void*)&qeglMakeCurrent, "eglMakeCurrent"},
{(void*)&qeglCreateContext, "eglCreateContext"},
{(void*)&qeglDestroyContext, "eglDestroyContext"},
{(void*)&qeglGetProcAddress, "eglGetProcAddress"},
//EGL 1.1
{(void*)&qeglSwapInterval, "eglSwapInterval"},
{NULL}
};
void *EGL_Proc(char *f)
{
void *proc = NULL;
/*
char fname[512];
{
sprintf(fname, "wrap_%s", f);
f = fname;
}
*/
if (!proc)
proc = Sys_GetAddressForName(eslibrary, f);
if (!proc)
proc = Sys_GetAddressForName(egllibrary, f);
//eglGetProcAddress functions must work regardless of context...
//FIXME: this means lots of false-positives.
//as well as lots of thunks, which will result in slowdowns.
if (qeglGetProcAddress)
proc = qeglGetProcAddress(f);
return proc;
}
static const char *EGL_GetErrorString(int error)
{
switch(error)
{
case EGL_BAD_ACCESS: return "BAD_ACCESS";
case EGL_BAD_ALLOC: return "BAD_ALLOC";
case EGL_BAD_ATTRIBUTE: return "BAD_ATTRIBUTE";
case EGL_BAD_CONFIG: return "BAD_CONFIG";
case EGL_BAD_CONTEXT: return "BAD_CONEXT";
case EGL_BAD_CURRENT_SURFACE: return "BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY: return "BAD_DISPLAY";
case EGL_BAD_MATCH: return "BAD_MATCH";
case EGL_BAD_NATIVE_PIXMAP: return "BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW: return "BAD_NATIVE_WINDOW";
case EGL_BAD_PARAMETER: return "BAD_PARAMETER";
case EGL_BAD_SURFACE: return "BAD_SURFACE";
default: return va("EGL:%#x", error);
}
}
void EGL_UnloadLibrary(void)
{
if (egllibrary)
Sys_CloseLibrary(egllibrary);
if (egllibrary == eslibrary)
eslibrary = NULL;
if (eslibrary)
Sys_CloseLibrary(eslibrary);
eslibrary = egllibrary = NULL;
}
qboolean EGL_LoadLibrary(char *driver)
{
/* linux seem to load glesv2 first for dependency issues.
(most things are expected to statically link to their libs)
strictly speaking, EGL says that functions should work regardless of context.
(which of course makes portability a nightmare, especially on windows where static linking is basically impossible)
(android's EGL bugs out if you use eglGetProcAddress for core functions too, note that EGL_KHR_get_all_proc_addresses fixes that.)
*/
Sys_Printf("Attempting to dlopen libGLESv2... ");
eslibrary = Sys_LoadLibrary("libGLESv2", NULL);
if (!eslibrary)
{
Sys_Printf("failed\n");
// return false;
}
else
Sys_Printf("success\n");
#ifndef _WIN32
if (!eslibrary)
{
eslibrary = dlopen("libGL.so.1.2", RTLD_NOW|RTLD_GLOBAL);
if (eslibrary) Sys_Printf("Loaded libGL.so.1.2\n");
}
if (!eslibrary)
{
eslibrary = dlopen("libGL.so.1", RTLD_NOW|RTLD_GLOBAL);
if (eslibrary) Sys_Printf("Loaded libGL.so.1\n");
}
if (!eslibrary)
{
eslibrary = dlopen("libGL", RTLD_NOW|RTLD_GLOBAL);
if (eslibrary) Sys_Printf("Loaded libGL\n");
}
#endif
if (!eslibrary)
Sys_Printf("unable to load some libGL\n");
Sys_Printf("Attempting to dlopen libEGL... ");
egllibrary = Sys_LoadLibrary("libEGL", qeglfuncs);
if (!egllibrary)
{
Sys_Printf("failed\n");
Con_Printf("libEGL library not loadable\n");
/* TODO: some implementations combine EGL/GLESv2 into single library... */
Sys_CloseLibrary(eslibrary);
return false;
}
Sys_Printf("success\n");
//egl1.2
qeglBindAPI = EGL_Proc("eglBindAPI");
//these are from egl1.5
qeglGetPlatformDisplay = EGL_Proc("eglGetPlatformDisplay");
qeglCreatePlatformWindowSurface = EGL_Proc("eglCreatePlatformWindowSurface");
//and in case they arn't defined...
if (!qeglGetPlatformDisplay) qeglGetPlatformDisplay = EGL_Proc("eglGetPlatformDisplayEXT");
if (!qeglCreatePlatformWindowSurface) qeglCreatePlatformWindowSurface = EGL_Proc("eglCreatePlatformWindowSurfaceEXT");
return true;
}
void EGL_Shutdown(void)
{
if (eglctx == EGL_NO_CONTEXT)
return;
qeglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
qeglDestroyContext(egldpy, eglctx);
if (eglsurf != EGL_NO_SURFACE)
qeglDestroySurface(egldpy, eglsurf);
qeglTerminate(egldpy);
eglctx = EGL_NO_CONTEXT;
egldpy = EGL_NO_DISPLAY;
eglsurf = EGL_NO_SURFACE;
}
/*static void EGL_ShowConfig(EGLDisplay egldpy, EGLConfig cfg)
{
struct
{
EGLint attr;
const char *attrname;
} eglattrs[] =
{
{EGL_ALPHA_SIZE, "EGL_ALPHA_SIZE"},
{EGL_ALPHA_MASK_SIZE, "EGL_ALPHA_MASK_SIZE"},
{EGL_BIND_TO_TEXTURE_RGB, "EGL_BIND_TO_TEXTURE_RGB"},
{EGL_BIND_TO_TEXTURE_RGBA, "EGL_BIND_TO_TEXTURE_RGBA"},
{EGL_BLUE_SIZE, "EGL_BLUE_SIZE"},
{EGL_BUFFER_SIZE, "EGL_BUFFER_SIZE"},
{EGL_COLOR_BUFFER_TYPE, "EGL_COLOR_BUFFER_TYPE"},
{EGL_CONFIG_CAVEAT, "EGL_CONFIG_CAVEAT"},
{EGL_CONFIG_ID, "EGL_CONFIG_ID"},
{EGL_CONFORMANT, "EGL_CONFORMANT"},
{EGL_DEPTH_SIZE, "EGL_DEPTH_SIZE"},
{EGL_GREEN_SIZE, "EGL_GREEN_SIZE"},
{EGL_LEVEL, "EGL_LEVEL"},
{EGL_LUMINANCE_SIZE, "EGL_LUMINANCE_SIZE"},
{EGL_MAX_PBUFFER_WIDTH, "EGL_MAX_PBUFFER_WIDTH"},
{EGL_MAX_PBUFFER_HEIGHT, "EGL_MAX_PBUFFER_HEIGHT"},
{EGL_MAX_PBUFFER_PIXELS, "EGL_MAX_PBUFFER_PIXELS"},
{EGL_MAX_SWAP_INTERVAL, "EGL_MAX_SWAP_INTERVAL"},
{EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL"},
{EGL_NATIVE_RENDERABLE, "EGL_NATIVE_RENDERABLE"},
{EGL_NATIVE_VISUAL_ID, "EGL_NATIVE_VISUAL_ID"},
{EGL_NATIVE_VISUAL_TYPE, "EGL_NATIVE_VISUAL_TYPE"},
{EGL_RED_SIZE, "EGL_RED_SIZE"},
{EGL_RENDERABLE_TYPE, "EGL_RENDERABLE_TYPE"},
{EGL_SAMPLE_BUFFERS, "EGL_SAMPLE_BUFFERS"},
{EGL_SAMPLES, "EGL_SAMPLES"},
{EGL_STENCIL_SIZE, "EGL_STENCIL_SIZE"},
{EGL_SURFACE_TYPE, "EGL_SURFACE_TYPE"},
{EGL_TRANSPARENT_TYPE, "EGL_TRANSPARENT_TYPE"},
{EGL_TRANSPARENT_RED_VALUE, "EGL_TRANSPARENT_RED_VALUE"},
{EGL_TRANSPARENT_GREEN_VALUE, "EGL_TRANSPARENT_GREEN_VALUE"},
{EGL_TRANSPARENT_BLUE_VALUE, "EGL_TRANSPARENT_BLUE_VALUE"},
};
size_t i;
EGLint val;
for (i = 0; i < countof(eglattrs); i++)
{
if (qeglGetConfigAttrib(egldpy, cfg, eglattrs[i].attr, &val))
Con_DPrintf("%p.%s: %i\n", cfg, eglattrs[i].attrname, val);
else
Con_DPrintf("%p.%s: UNKNOWN\n", cfg, eglattrs[i].attrname);
}
}*/
static void EGL_UpdateSwapInterval(void)
{
int interval;
vid_vsync.modified = false;
if (*vid_vsync.string)
interval = vid_vsync.ival;
else
interval = 1; //default is to always vsync, according to EGL docs, so lets just do that.
if (qeglSwapInterval)
qeglSwapInterval(egldpy, interval);
}
void EGL_SwapBuffers (void)
{
if (vid_vsync.modified)
EGL_UpdateSwapInterval();
TRACE(("EGL_SwapBuffers\n"));
TRACE(("swapping buffers\n"));
qeglSwapBuffers(egldpy, eglsurf);
/* TODO: check result? */
TRACE(("EGL_SwapBuffers done\n"));
}
qboolean EGL_InitDisplay (rendererstate_t *info, int eglplat, void *ndpy, EGLNativeDisplayType dpyid, EGLConfig *outconfig)
{
EGLint numconfig=0;
EGLConfig cfg=0;
EGLint major=0, minor=0;
EGLint attrib[] =
{
EGL_SURFACE_TYPE, (eglplat==EGL_PLATFORM_DEVICE_EXT)?EGL_PBUFFER_BIT:EGL_WINDOW_BIT,
// EGL_BUFFER_SIZE, info->bpp,
// EGL_SAMPLES, info->multisample,
// EGL_STENCIL_SIZE, 8,
EGL_ALPHA_MASK_SIZE, 0,
makefile: attempt to fix freetype when not using makelibs (should make it slightly easier for people to compile with msys2 without needing to resort to cmake). emenu: clean up hexen2's maplist options slightly. emenu: modelviewer should now be slightly more friendly (click+wasd to move around). particles: fix up randomised s coords. csqc: try to fix issue with applycustomskin not refcounting properly. client: [s_]precache and (new) mod_precache cvars can be set to 2 to precache the resources after load, for faster loading at the expense of some early stutter, without risking later mid-game stuttering. gltf: add support for morphweights in a cpu-fallback path. don't expect good performance on surfaces with morphtargets for now. gtlf: add some support for gltf1 files. far from perfect. shaders: gltf1 semantics handling shaders: const correctness iqmtool: fix up mdl skin export. iqmtool: integrate the engine's gltf2 loader. works with animated models, but unanimated ones suffer from basepose-different-from-bindpose issues. q3bsp: hopefully fixed bih traces. still disabled for now. qc: change default value of pr_gc_threaded to 1. qcext: add the '__deprecated' keyword to various symbols in fteextensions.qc, now that fteqcc supports it. ssqc: spit out a more readable error for WriteByte(MSG_CSQC,...) outside of SendEntity. ssqc: add registercommand builtin, for consistency with menuqc and csqc (though only one can register any single command). sv: report userinfo/serverinfo sizes (some clients still have arbitrary limits, plus its nice to see how abusive things are) sv: try to optimise sv_cullentities_trace a little. movechain: relink moved ents. csqc: add spriteframe builtin, for freecs to use instead of more ugly less reliable hacks. menuqc: fopen("tls://host:port", FILE_STREAM) should now open a tls stream. tcp:// should also work. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5703 fc73d0e0-1445-4013-8a0c-d673dee63da5
2020-06-13 00:29:58 +01:00
EGL_DEPTH_SIZE, info->depthbits?info->depthbits:16,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
/* if (!EGL_LoadLibrary(""))
{
Con_Printf(CON_ERROR "EGL: unable to load library!\n");
return false;
}
*/
if (qeglGetPlatformDisplay && eglplat)
egldpy = qeglGetPlatformDisplay(eglplat, ndpy, NULL/*attribs*/);
else
{
if (eglplat == EGL_PLATFORM_WAYLAND_KHR)
Con_Printf(CON_ERROR "EGL: eglGetPlatformDisplay[EXT] not supported. Your EGL implementation is probably too old.\n");
egldpy = qeglGetDisplay(dpyid);
}
if (egldpy == EGL_NO_DISPLAY)
{
Con_Printf(CON_WARNING "EGL: creating default display\n");
egldpy = qeglGetDisplay(EGL_DEFAULT_DISPLAY);
}
if (egldpy == EGL_NO_DISPLAY)
{
Con_Printf(CON_ERROR "EGL: can't get display!\n");
return false;
}
//NOTE: mesa's egl really loves to crash on this call, and I define crash as 'anything that fails to return to caller', which fucks everything up.
if (!qeglInitialize(egldpy, &major, &minor))
{
Con_Printf(CON_ERROR "EGL: can't initialize display!\n");
return false;
}
/*
if (!qeglGetConfigs(egldpy, NULL, 0, &numconfigs) || !numconfigs)
{
Con_Printf(CON_ERROR "EGL: can't get configs!\n");
return false;
}
*/
if (!qeglChooseConfig(egldpy, attrib, &cfg, 1, &numconfig))
{
Con_Printf(CON_ERROR "EGL: can't choose config!\n");
return false;
}
if (!numconfig)
{
Con_Printf(CON_ERROR "EGL: no configs!\n");
return false;
}
// EGL_ShowConfig(egldpy, cfg);
*outconfig = cfg;
return true;
}
qboolean EGL_InitWindow (rendererstate_t *info, int eglplat, void *nwindow, EGLNativeWindowType windowid, EGLConfig cfg)
{
EGLint renderabletype;
if (eglplat == EGL_PLATFORM_DEVICE_EXT && qeglCreatePbufferSurface)
{
EGLint wndattrib[] =
{
EGL_WIDTH, vid.pixelwidth,
EGL_HEIGHT, vid.pixelheight,
// EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
EGL_NONE,EGL_NONE
};
eglsurf = qeglCreatePbufferSurface(egldpy, cfg, wndattrib);
}
else if (qeglCreatePlatformWindowSurface)
{
EGLAttrib wndattrib[] =
{
// EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
EGL_NONE,EGL_NONE
};
eglsurf = qeglCreatePlatformWindowSurface(egldpy, cfg, nwindow, info->srgb?wndattrib:NULL);
}
else
{
EGLint wndattrib[] =
{
// EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
EGL_NONE,EGL_NONE
};
eglsurf = qeglCreateWindowSurface(egldpy, cfg, windowid, info->srgb?wndattrib:NULL);
}
if (eglsurf == EGL_NO_SURFACE)
{
int err = qeglGetError();
if (eglplat == EGL_PLATFORM_WAYLAND_KHR && err == EGL_BAD_DISPLAY) //slightly more friendly error that slags off nvidia for their refusal to implement existing standards, as is apparently appropriate.
Con_Printf(CON_ERROR "EGL: eglCreateWindowSurface failed: Bad Display. Your wayland setup is probably not properly supported by your video drivers.\n");
else
Con_Printf(CON_ERROR "EGL: eglCreateWindowSurface failed: %s\n", EGL_GetErrorString(err));
return false;
}
qeglGetConfigAttrib(egldpy, cfg, EGL_RENDERABLE_TYPE, &renderabletype);
if ((renderabletype & EGL_OPENGL_BIT) && qeglBindAPI)
{
EGLint contextattr[] =
{
// EGL_CONTEXT_OPENGL_DEBUG, 1,
// EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
// EGL_CONTEXT_CLIENT_VERSION, 2, //requires EGL 1.3
// EGL_TEXTURE_TARGET,EGL_NO_TEXTURE, //just a rendertarget, not a texture.
EGL_NONE,EGL_NONE
};
qeglBindAPI(EGL_OPENGL_API);
eglctx = qeglCreateContext(egldpy, cfg, EGL_NO_SURFACE, contextattr);
}
else
{
EGLint contextattr[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2, //requires EGL 1.3
EGL_NONE, EGL_NONE
};
eglctx = qeglCreateContext(egldpy, cfg, EGL_NO_SURFACE, contextattr);
}
if (eglctx == EGL_NO_CONTEXT)
{
Con_Printf(CON_ERROR "EGL: no context!\n");
return false;
}
openxr plugin: tweaked - inputs should be working properly now, and are visible to csqc. subject to further breaking changes, however. _pext_vrinputs: added cvar to enable vr inputs protocol extension allowing vr inputs to be networked to ssqc too. defaults to 0 for now, will be renamed when deemed final. updates menu: the prompt to enable sources is now more explicit instead of expecting the user to have a clue. updates menu: added a v3 sources format, which should be more maintainable. not final. updates menu: try to give reasons why sources might be failing (to help blame ISPs if they try fucking over TTH dns again). presets menu: no longer closes the instant a preset is chosen. some presets have a couple of modifiers listed. force the demo loop in the background to serve as a preview. prompts menus: now does word wrapping. ftemaster: support importing server lists from other master servers (requested by Eukara). server: try to detect when non-reply inbound packets are blocked by firewalls/nats/etc (using ftemaster to do so). qcvm: added pointcontentsmask builtin, allowing it to probe more than just world, with fte's full contentbit range instead of just q1 legacy. qcvm: memfill8 builtin now works on createbuffer() pointers. qcvm: add missing unsigned ops. Fixed double comparison ops. fixed bug with op_store_i64. added missing OP_LOADP_I64 qcc: added '#pragma framerate RATE' for overriding implicit nextthink durations. qcc: fixed '#pragma DONT_COMPILE_THIS_FILE' to not screw up comments. qcc: added __GITURL__ __GITHASH__ __GITDATE__ __GITDATETIME__ __GITDESC__ for any mods that might want to make use of that. qcc: fix up -Fhashonly a little setrenderer: support for vulkan gpu enumeration. rulesets: reworked to support custom rulesets (using hashes to catch haxxors, though still nothing prevents just changing the client to ignore rulesets) bspx: use our BIH code for the bspx BRUSHLIST lump instead of the older less efficient code. (static)iqm+obj: these model formats can now be used for the worldmodel (with a suitable .ent file). Also using BIH for much better collision performance. pmove: tried to optimise PM_NudgePosition, should boost fps in stress tests. wayland: fix a crash on startup. mousegrabs now works better. imagetool: uses sdl for previews. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5813 fc73d0e0-1445-4013-8a0c-d673dee63da5
2021-04-14 06:21:04 +01:00
if (!qeglMakeCurrent(egldpy, eglsurf, eglsurf, eglctx))
{
Con_Printf(CON_ERROR "EGL: can't make current!\n");
return false;
}
EGL_UpdateSwapInterval();
if (info->vr)
{
vrsetup_t vrsetup = {sizeof(vrsetup)};
vrsetup.vrplatform = VR_EGL;
vrsetup.egl.getprocaddr = qeglGetProcAddress;
vrsetup.egl.egldisplay = egldpy;
vrsetup.egl.eglconfig = cfg;
vrsetup.egl.eglcontext = eglctx;
if (!info->vr->Prepare(&vrsetup) ||
!info->vr->Init(&vrsetup, info))
{
info->vr->Shutdown();
info->vr = NULL;
}
else
vid.vr = info->vr;
}
return true;
}
//code for headless/pbuffer rendering.
//in terms of energy efficiency its better to use the true headless renderer which stubs out ALL rendering.
//however that's not very useful for screenshots or demo captures, so we offer this route too.
#include "glquake.h"
#include "gl_draw.h"
#include "shader.h"
#include "EGL/eglext.h"
static void EGLHeadless_SwapBuffers(void)
{
if (R2D_Flush)
R2D_Flush();
EGL_SwapBuffers();
}
static qboolean EGLHeadless_Init (rendererstate_t *info, unsigned char *palette)
{
EGLConfig cfg;
void *dpy = NULL;
if (!EGL_LoadLibrary(info->subrenderer))
{
Con_Printf("couldn't load EGL library\n");
return false;
}
#ifdef EGL_EXT_device_base
if (*info->devicename)
{
EGLDeviceEXT devs[64];
EGLint count;
EGLint idx = atoi(info->devicename);
EGLBoolean (EGLAPIENTRY *qeglQueryDevicesEXT) (EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices) = EGL_Proc("eglQueryDevicesEXT");
if (qeglQueryDevicesEXT)
{
qeglQueryDevicesEXT(countof(devs), devs, &count);
if (idx >= 0 && idx < count)
dpy = devs[idx];
}
}
#endif
vid.pixelwidth = (info->width>0)?info->width:640;
vid.pixelheight = (info->height>0)?info->height:480;
vid.activeapp = true;
if (!EGL_InitDisplay(info, EGL_PLATFORM_DEVICE_EXT, dpy, (EGLNativeDisplayType)EGL_NO_DISPLAY, &cfg))
{
Con_Printf("couldn't find suitable EGL config\n");
return false;
}
if (!EGL_InitWindow(info, EGL_PLATFORM_DEVICE_EXT, EGL_NO_SURFACE, (EGLNativeWindowType)EGL_NO_SURFACE, cfg))
{
Con_Printf("couldn't initialise EGL context\n");
return false;
}
if (GL_Init(info, &EGL_Proc))
return true;
Con_Printf(CON_ERROR "Unable to initialise opengl-on-wayland.\n");
return false;
}
static void EGLHeadless_DeInit(void)
{
EGL_Shutdown();
EGL_UnloadLibrary();
GL_ForgetPointers();
}
static qboolean EGLHeadless_ApplyGammaRamps(unsigned int gammarampsize, unsigned short *ramps)
{
//not supported
return false;
}
static void EGLHeadless_SetCaption(const char *text)
{
}
static int EGLHeadless_GetPriority(void)
{
return -1; //lowest priority, so its never auto-used..
}
rendererinfo_t rendererinfo_headless_egl =
{
"Headless OpenGL (EGL)",
{
"egl_headless",
},
QR_OPENGL,
GLDraw_Init,
GLDraw_DeInit,
GL_UpdateFiltering,
GL_LoadTextureMips,
GL_DestroyTexture,
GLR_Init,
GLR_DeInit,
GLR_RenderView,
EGLHeadless_Init,
EGLHeadless_DeInit,
EGLHeadless_SwapBuffers,
EGLHeadless_ApplyGammaRamps,
NULL,
NULL,
NULL,
EGLHeadless_SetCaption, //setcaption
GLVID_GetRGBInfo,
GLSCR_UpdateScreen,
GLBE_SelectMode,
GLBE_DrawMesh_List,
GLBE_DrawMesh_Single,
GLBE_SubmitBatch,
GLBE_GetTempBatch,
GLBE_DrawWorld,
GLBE_Init,
GLBE_GenBrushModelVBO,
GLBE_ClearVBO,
GLBE_UpdateLightmaps,
GLBE_SelectEntity,
GLBE_SelectDLight,
GLBE_Scissor,
GLBE_LightCullModel,
GLBE_VBO_Begin,
GLBE_VBO_Data,
GLBE_VBO_Finish,
GLBE_VBO_Destroy,
GLBE_RenderToTextureUpdate2d,
"",
EGLHeadless_GetPriority
};
#endif