1313
1414#include < algorithm>
1515#include < cctype>
16+ #include < dlfcn.h>
1617#include < set>
1718#include < string>
1819
@@ -23,19 +24,21 @@ namespace polyscope {
2324namespace render {
2425namespace backend_openGL3 {
2526
26- namespace { // anonymous helpers
27+ void initializeRenderEngine_egl () {
2728
28- // Helper function to get an EGL (extension?) function and error-check that
29- // we got it successfully
30- void * getEGLProcAddressAndCheck (std::string name) {
31- void * procAddr = (void *)(eglGetProcAddress (name.c_str ()));
32- if (!procAddr) {
33- error (" EGL failed to get function pointer for " + name);
34- }
35- return procAddr;
29+ GLEngineEGL* glEngineEGL = new GLEngineEGL (); // create the new global engine object
30+ engine = glEngineEGL;
31+
32+ // initialize
33+ glEngineEGL->initialize ();
34+ engine->allocateGlobalBuffersAndPrograms ();
35+ glEngineEGL->applyWindowSize ();
3636}
3737
38- void checkEGLError (bool fatal = true ) {
38+ GLEngineEGL::GLEngineEGL () {}
39+ GLEngineEGL::~GLEngineEGL () {}
40+
41+ void GLEngineEGL::checkEGLError (bool fatal) {
3942
4043 if (!options::enableRenderErrorChecks) {
4144 return ;
@@ -124,29 +127,15 @@ void checkEGLError(bool fatal = true) {
124127 exception (" EGL error occurred. Text: " + errText);
125128 }
126129}
127- } // namespace
128-
129- void initializeRenderEngine_egl () {
130-
131- GLEngineEGL* glEngineEGL = new GLEngineEGL (); // create the new global engine object
132- engine = glEngineEGL;
133-
134- // initialize
135- glEngineEGL->initialize ();
136- engine->allocateGlobalBuffersAndPrograms ();
137- glEngineEGL->applyWindowSize ();
138- }
139-
140- GLEngineEGL::GLEngineEGL () {}
141- GLEngineEGL::~GLEngineEGL () {}
142130
143131void GLEngineEGL::initialize () {
144132
133+
145134 // === Initialize EGL
146135
147- // Pre -load required extension functions
148- PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT =
149- (PFNEGLQUERYDEVICESEXTPROC) getEGLProcAddressAndCheck ( " eglQueryDevicesEXT " );
136+ // Runtime -load shared library functions for EGL
137+ // (see note inside for details)
138+ resolveEGL ( );
150139
151140 // Query the available EGL devices
152141 const int N_MAX_DEVICE = 256 ;
@@ -285,21 +274,94 @@ void GLEngineEGL::initialize() {
285274 checkError ();
286275}
287276
288- void GLEngineEGL::sortAvailableDevicesByPreference (std::vector< int32_t >& deviceInds, EGLDeviceEXT rawDevices[] ) {
277+ void GLEngineEGL::resolveEGL ( ) {
289278
290- // check that we actually have the query extension
279+ // This function does a bunch of gymnastics to avoid taking on libEGL.so as a dependency. This is a
280+ // machine/driver-specific library, so we would have to dynamically load it on the end user's machine. However,
281+ // simply specifying it as a shared library at build time would make it required for Polyscope, even for users
282+ // who would not use EGL, and we don't want that. Instead, we manually dynamically load the functions below.
283+ //
284+ // Note that this is on top of the dynamic loading that always happens when you load exention functions from libEGL.
285+ // We are furthermore loading `libEGL.so` dynamically in the middle of runtime, rather than at load time as via ldd
286+ // etc. At the end of this function we also load EGL extensions in the usual way.
287+
288+
289+ if (options::verbosity > 5 ) {
290+ std::cout << polyscope::options::printPrefix << " Attempting to dlopen libEGL.so" << std::endl;
291+ }
292+ void * handle = dlopen (" libEGL.so" , RTLD_LAZY);
293+ if (!handle) {
294+ error (" EGL: Could not open libEGL.so." );
295+ }
296+ if (options::verbosity > 5 ) {
297+ std::cout << polyscope::options::printPrefix << " ...loaded libEGL.so" << std::endl;
298+ }
299+
300+ // Get EGL functions
301+ if (options::verbosity > 5 ) {
302+ std::cout << polyscope::options::printPrefix << " Attempting to dlsym resolve EGL functions" << std::endl;
303+ }
304+
305+ eglGetError = (eglGetErrorT)dlsym (handle, " eglGetError" );
306+ eglGetPlatformDisplay = (eglGetPlatformDisplayT)dlsym (handle, " eglGetPlatformDisplay" );
307+ eglChooseConfig = (eglChooseConfigT)dlsym (handle, " eglChooseConfig" );
308+ eglInitialize = (eglInitializeT)dlsym (handle, " eglInitialize" );
309+ eglChooseConfig = (eglChooseConfigT)dlsym (handle, " eglChooseConfig" );
310+ eglBindAPI = (eglBindAPIT)dlsym (handle, " eglBindAPI" );
311+ eglCreateContext = (eglCreateContextT)dlsym (handle, " eglCreateContext" );
312+ eglMakeCurrent = (eglMakeCurrentT)dlsym (handle, " eglMakeCurrent" );
313+ eglDestroyContext = (eglDestroyContextT)dlsym (handle, " eglDestroyContext" );
314+ eglTerminate = (eglTerminateT)dlsym (handle, " eglTerminate" );
315+ eglGetProcAddress = (eglGetProcAddressT)dlsym (handle, " eglGetProcAddress" );
316+ eglQueryString = (eglQueryStringT)dlsym (handle, " eglQueryString" );
317+
318+ if (!eglGetError || !eglGetPlatformDisplay || !eglChooseConfig || !eglInitialize || !eglChooseConfig || !eglBindAPI ||
319+ !eglCreateContext || !eglMakeCurrent || !eglDestroyContext || !eglTerminate || !eglGetProcAddress ||
320+ !eglQueryString) {
321+ dlclose (handle);
322+ const char * errTextPtr = dlerror ();
323+ std::string errText = " " ;
324+ if (errTextPtr) {
325+ errText = errTextPtr;
326+ }
327+ error (" EGL: Error loading symbol " + errText);
328+ }
329+ if (options::verbosity > 5 ) {
330+ std::cout << polyscope::options::printPrefix << " ...resolved EGL functions" << std::endl;
331+ }
332+ // ... at this point, we have essentially loaded libEGL.so
333+
334+ // === Resolve EGL extension functions
335+
336+ // Helper function to get an EGL extension function and error-check that
337+ // we got it successfully
338+ auto getEGLExtensionProcAndCheck = [&](std::string name) {
339+ void * procAddr = (void *)(eglGetProcAddress (name.c_str ()));
340+ if (!procAddr) {
341+ error (" EGL failed to get function pointer for " + name);
342+ }
343+ return procAddr;
344+ };
345+
346+ // Pre-load required extension functions
347+ eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)getEGLExtensionProcAndCheck (" eglQueryDevicesEXT" );
291348 const char * extensions = eglQueryString (EGL_NO_DISPLAY, EGL_EXTENSIONS);
292349 if (extensions && std::string (extensions).find (" EGL_EXT_device_query" ) != std::string::npos) {
293- // good case, supported
294- } else {
350+ eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLExtensionProcAndCheck (" eglQueryDeviceStringEXT" );
351+ }
352+ }
353+
354+ void GLEngineEGL::sortAvailableDevicesByPreference (
355+
356+ std::vector<int32_t >& deviceInds, EGLDeviceEXT rawDevices[]) {
357+
358+ // check that we actually have the query extension
359+ const char * extensions = eglQueryString (EGL_NO_DISPLAY, EGL_EXTENSIONS);
360+ if (!eglQueryDeviceStringEXT) {
295361 info (" EGL: cannot sort devices by preference, EGL_EXT_device_query is not supported" );
296362 return ;
297363 }
298364
299- // Pre-load required extension functions
300- PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT =
301- (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLProcAddressAndCheck (" eglQueryDeviceStringEXT" );
302-
303365 // Build a list of devices and assign a score to each
304366 std::vector<std::tuple<int32_t , int32_t >> scoreDevices;
305367 for (int32_t iDevice : deviceInds) {
0 commit comments