From 4dc7a814a6e75ace4e3d0fb7485d8cbd4a963094 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sat, 23 Nov 2024 23:54:32 -0800 Subject: [PATCH 01/34] use extensions to fetch platform devices for EGL --- include/polyscope/options.h | 5 ++ src/options.cpp | 2 + src/render/opengl/gl_engine_egl.cpp | 75 +++++++++++++++++++++++++---- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/include/polyscope/options.h b/include/polyscope/options.h index a439ddda..6ffcae0f 100644 --- a/include/polyscope/options.h +++ b/include/polyscope/options.h @@ -119,6 +119,11 @@ extern std::function configureImGuiStyleCallback; // assign your own function to create custom styles. If this callback is null, default fonts will be used. extern std::function()> prepareImGuiFontsCallback; +// === Backend and low-level options + +// When using the EGL backend, which device to try to initialize with +// (default is -1 which means try all of them) +extern int eglDeviceIndex; // === Debug options diff --git a/src/options.cpp b/src/options.cpp index 5bcaca32..703ff5da 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -55,6 +55,8 @@ bool openImGuiWindowForUserCallback = true; std::function configureImGuiStyleCallback = configureImGuiStyle; std::function()> prepareImGuiFontsCallback = prepareImGuiFonts; +// Backend and low-level options +int eglDeviceIndex = -1; // means "try all of them" // enabled by default in debug mode #ifndef NDEBUG diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 81a0c3cf..280c7590 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -14,12 +14,25 @@ #include #include +#include +#include + namespace polyscope { namespace render { namespace backend_openGL3 { namespace { // anonymous helpers +// Helper function to get an EGL (extension?) function and error-check that +// we got it successfully +void* getEGLProcAddressAndCheck(std::string name) { + void* procAddr = (void*)(eglGetProcAddress(name.c_str())); + if (!procAddr) { + error("EGL failed to get function pointer for " + name); + } + return procAddr; +} + void checkEGLError(bool fatal = true) { if (!options::enableRenderErrorChecks) { @@ -129,22 +142,66 @@ void GLEngineEGL::initialize() { // === Initialize EGL - // Get the default display - eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (eglDisplay == EGL_NO_DISPLAY) { - exception("ERROR: Failed to initialize EGL, could not get default display"); + // Pre-load required extension functions + PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = + (PFNEGLQUERYDEVICESEXTPROC)getEGLProcAddressAndCheck("eglQueryDevicesEXT"); + + // Query the available EGL devices + const int N_MAX_DEVICE = 256; + EGLDeviceEXT devices[N_MAX_DEVICE]; + EGLint nDevices; + if (!eglQueryDevicesEXT(N_MAX_DEVICE, devices, &nDevices)) { + error("EGL: failed to query devices."); + } + if (nDevices == 0) { + error("EGL: no devices found."); + } + info("EGL: Found " + std::to_string(nDevices) + " EGL devices."); + + if (options::eglDeviceIndex == -1) { + info("EGL: no device index specified, attempting to intialize with each device sequentially until success.") + } else { + info("EGL: device index " + std::to_string(options::eglDeviceIndex) + " manually selected, using that device."); + + if (options::eglDeviceIndex >= nDevices) { + error("EGL: device index " + std::to_string(options::eglDeviceIndex) + " manually selected, but only " + + std::to_string(nDevices) + " devices available."); + } } - // Configure + bool successfulInit = false; EGLint majorVer, minorVer; - bool success = eglInitialize(eglDisplay, &majorVer, &minorVer); - if (!success) { - checkEGLError(false); + for (int32_t iDevice = 0; iDevice < nDevices; iDevice++) { + + if (options::eglDeviceIndex != -1 && iDevice != options::eglDeviceIndex) { + // implement manualy selection by skipping the others + continue; + } + + info("EGL: attempting initialization with device " + std::to_string(iDevice)); + + // Select the first device (custom logic can go here to pick a specific one) + EGLDeviceEXT device = devices[iDevice]; + + // Get an EGLDisplay for the device + // (use the -platform / EXT version because it is the only one that seems to work in headless environments) + eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, device, NULL); + if (eglDisplay == EGL_NO_DISPLAY) { + continue; + } + + // Configure + successfulInit = eglInitialize(eglDisplay, &majorVer, &minorVer); + if (successfulInit) { + break; + } + } + + if (!successfulInit) { exception("ERROR: Failed to initialize EGL"); } checkEGLError(); - // this has something to do with the EGL configuration, I don't understand exactly what // clang-format off const EGLint configAttribs[] = { From 05881c57b7fab6505c4162df42f1f5ae353d774e Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 24 Nov 2024 00:00:36 -0800 Subject: [PATCH 02/34] fix typo --- src/render/opengl/gl_engine_egl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 280c7590..5a35529c 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -159,7 +159,7 @@ void GLEngineEGL::initialize() { info("EGL: Found " + std::to_string(nDevices) + " EGL devices."); if (options::eglDeviceIndex == -1) { - info("EGL: no device index specified, attempting to intialize with each device sequentially until success.") + info("EGL: no device index specified, attempting to intialize with each device sequentially until success."); } else { info("EGL: device index " + std::to_string(options::eglDeviceIndex) + " manually selected, using that device."); From 94f8c6b781b016ba1cc1c9cd9853f1f2150f43d1 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 28 Nov 2024 21:50:02 -0800 Subject: [PATCH 03/34] small fixes, formatter --- src/render/opengl/gl_engine_egl.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 5a35529c..ce79d1d9 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -151,20 +151,20 @@ void GLEngineEGL::initialize() { EGLDeviceEXT devices[N_MAX_DEVICE]; EGLint nDevices; if (!eglQueryDevicesEXT(N_MAX_DEVICE, devices, &nDevices)) { - error("EGL: failed to query devices."); + error("EGL: Failed to query devices."); } if (nDevices == 0) { - error("EGL: no devices found."); + error("EGL: No devices found."); } info("EGL: Found " + std::to_string(nDevices) + " EGL devices."); if (options::eglDeviceIndex == -1) { - info("EGL: no device index specified, attempting to intialize with each device sequentially until success."); + info("EGL: No device index specified, attempting to intialize with each device sequentially until success."); } else { - info("EGL: device index " + std::to_string(options::eglDeviceIndex) + " manually selected, using that device."); + info("EGL: Device index " + std::to_string(options::eglDeviceIndex) + " manually selected, using that device."); if (options::eglDeviceIndex >= nDevices) { - error("EGL: device index " + std::to_string(options::eglDeviceIndex) + " manually selected, but only " + + error("EGL: Device index " + std::to_string(options::eglDeviceIndex) + " manually selected, but only " + std::to_string(nDevices) + " devices available."); } } @@ -178,11 +178,11 @@ void GLEngineEGL::initialize() { continue; } - info("EGL: attempting initialization with device " + std::to_string(iDevice)); - - // Select the first device (custom logic can go here to pick a specific one) + info("EGL: Attempting initialization with device " + std::to_string(iDevice)); EGLDeviceEXT device = devices[iDevice]; + // TODO use eglQueryDeviceStringEXT to prefer hardware-accelerated devices + // Get an EGLDisplay for the device // (use the -platform / EXT version because it is the only one that seems to work in headless environments) eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, device, NULL); @@ -201,6 +201,7 @@ void GLEngineEGL::initialize() { exception("ERROR: Failed to initialize EGL"); } checkEGLError(); + info("EGL: Initialization successful"); // this has something to do with the EGL configuration, I don't understand exactly what // clang-format off From 30c8ce35e7d867e47d61c829263767f0f071edc6 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 28 Nov 2024 23:17:15 -0800 Subject: [PATCH 04/34] device preference sorting --- .../polyscope/render/opengl/gl_engine_egl.h | 3 + src/render/opengl/gl_engine_egl.cpp | 62 ++++++++++++++++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index a57f8863..1f996ef7 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -74,6 +74,9 @@ class GLEngineEGL : public GLEngine { // Internal windowing and engine details EGLDisplay eglDisplay; EGLContext eglContext; + + // helpers + void sortAvailableDevicesByPreference(std::vector& devices); }; } // namespace backend_openGL3 diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index ce79d1d9..db124afa 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -158,8 +158,15 @@ void GLEngineEGL::initialize() { } info("EGL: Found " + std::to_string(nDevices) + " EGL devices."); + // Build an ordered list of which devices to try initializing with + std::vector devicesToTry; if (options::eglDeviceIndex == -1) { info("EGL: No device index specified, attempting to intialize with each device sequentially until success."); + + devicesToTry.resize(nDevices); + std::iota(devicesToTry.begin(), devicesToTry.end(), 0); + sortDevicesByPreference(devicesToTry); + } else { info("EGL: Device index " + std::to_string(options::eglDeviceIndex) + " manually selected, using that device."); @@ -167,22 +174,17 @@ void GLEngineEGL::initialize() { error("EGL: Device index " + std::to_string(options::eglDeviceIndex) + " manually selected, but only " + std::to_string(nDevices) + " devices available."); } + + devicesToTry.push_back(options::eglDeviceIndex); } bool successfulInit = false; EGLint majorVer, minorVer; - for (int32_t iDevice = 0; iDevice < nDevices; iDevice++) { - - if (options::eglDeviceIndex != -1 && iDevice != options::eglDeviceIndex) { - // implement manualy selection by skipping the others - continue; - } + for (int32_t iDevice : eglDeviceIndex) { info("EGL: Attempting initialization with device " + std::to_string(iDevice)); EGLDeviceEXT device = devices[iDevice]; - // TODO use eglQueryDeviceStringEXT to prefer hardware-accelerated devices - // Get an EGLDisplay for the device // (use the -platform / EXT version because it is the only one that seems to work in headless environments) eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, device, NULL); @@ -280,6 +282,50 @@ void GLEngineEGL::initialize() { checkError(); } +void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& devices) { + + // Pre-load required extension functions + PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = + (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLProcAddressAndCheck("eglQueryDeviceStringEXT"); + + // Build a list of devices and assign a score to each + std::vector> scoreDevices; + for (int32_t iDevice : devices) { + int score = 0; + + std::string vendorStr = eglQueryDeviceStringEXT(devices[i], EGL_VENDOR); + + // lower-case it for the steps below + std::transform(vendorStr.begin(), vendorStr.end(), vendorStr.begin(), std::tolower); + + // Problem: we want to detect and prefer discrete graphics cars over integrated GPUs and + // software / VM renderers. However, I can't figure out how to get an "is integrated" + // property from the query device strings above. Even worse, 'AMD" and "Intel" are both + // ambiguous and could refer to the integrated GPU or a discrete GPU. + // + // As a workaround, we assign scores based on the vendor: nvidia is always discrete, amd could be either, intel is + // usually integrated, but still preferred over software renderers + // + // ONEDAY: figure out a better policy to detect discrete devices.... + + // assign scores based on vendors to prefer discrete gpus + if (vendorStr.find("intel") != std::string::npos) score += 1; + if (vendorStr.find("amd") != std::string::npos) score += 2; + if (vendorStr.find("nvidia") != std::string::npos) score += 3; + + scoreDevices.emplace_back(score, iDevice); + } + + // sort them by highest score + std::sort(scoreDevices.begin(), scoreDevices.end()); + std::reverse(scoreDevices.begin(), scoreDevices.end()); + + // store them back in the given array + for (size_t i = 0; i < devices.size(); i++) { + devices[i] = std::get<1>(scoreDevices[i]); + } +} + void GLEngineEGL::initializeImGui() { From d016854cfa74103b76a8f30f189f00a7d20d727e Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 28 Nov 2024 23:39:20 -0800 Subject: [PATCH 05/34] few more fixes --- src/render/opengl/gl_engine_egl.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index db124afa..b2488fe3 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -12,7 +12,9 @@ #include "stb_image.h" #include +#include #include +#include #include #include @@ -165,7 +167,7 @@ void GLEngineEGL::initialize() { devicesToTry.resize(nDevices); std::iota(devicesToTry.begin(), devicesToTry.end(), 0); - sortDevicesByPreference(devicesToTry); + sortAvailableDevicesByPreference(devicesToTry); } else { info("EGL: Device index " + std::to_string(options::eglDeviceIndex) + " manually selected, using that device."); @@ -180,7 +182,7 @@ void GLEngineEGL::initialize() { bool successfulInit = false; EGLint majorVer, minorVer; - for (int32_t iDevice : eglDeviceIndex) { + for (int32_t iDevice : devicesToTry) { info("EGL: Attempting initialization with device " + std::to_string(iDevice)); EGLDeviceEXT device = devices[iDevice]; @@ -293,7 +295,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& devices for (int32_t iDevice : devices) { int score = 0; - std::string vendorStr = eglQueryDeviceStringEXT(devices[i], EGL_VENDOR); + std::string vendorStr = eglQueryDeviceStringEXT(devices[iDevice], EGL_VENDOR); // lower-case it for the steps below std::transform(vendorStr.begin(), vendorStr.end(), vendorStr.begin(), std::tolower); From 8ba375ef0c51d77a0c557990991747e5b67cb542 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 28 Nov 2024 23:44:42 -0800 Subject: [PATCH 06/34] use vector of device --- include/polyscope/render/opengl/gl_engine_egl.h | 2 +- src/render/opengl/gl_engine_egl.cpp | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index 1f996ef7..70e8bfe4 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -76,7 +76,7 @@ class GLEngineEGL : public GLEngine { EGLContext eglContext; // helpers - void sortAvailableDevicesByPreference(std::vector& devices); + void sortAvailableDevicesByPreference(std::vector& devices); }; } // namespace backend_openGL3 diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index b2488fe3..60e1077d 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -284,21 +284,22 @@ void GLEngineEGL::initialize() { checkError(); } -void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& devices) { +void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& devices) { // Pre-load required extension functions PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLProcAddressAndCheck("eglQueryDeviceStringEXT"); // Build a list of devices and assign a score to each - std::vector> scoreDevices; - for (int32_t iDevice : devices) { + std::vector> scoreDevices; + for (EGLDevice device : devices) { int score = 0; - std::string vendorStr = eglQueryDeviceStringEXT(devices[iDevice], EGL_VENDOR); + std::string vendorStr = eglQueryDeviceStringEXT(device, EGL_VENDOR); - // lower-case it for the steps below - std::transform(vendorStr.begin(), vendorStr.end(), vendorStr.begin(), std::tolower); + // lower-case it for the checks below + std::transform(vendorStr.begin(), vendorStr.end(), vendorStr.begin(), + [](unsigned char c) { return std::tolower(c); }); // Problem: we want to detect and prefer discrete graphics cars over integrated GPUs and // software / VM renderers. However, I can't figure out how to get an "is integrated" @@ -315,7 +316,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& devices if (vendorStr.find("amd") != std::string::npos) score += 2; if (vendorStr.find("nvidia") != std::string::npos) score += 3; - scoreDevices.emplace_back(score, iDevice); + scoreDevices.emplace_back(score, device); } // sort them by highest score From c2b08abe0e44cf084156119fbb0883b1a826d707 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 28 Nov 2024 23:46:36 -0800 Subject: [PATCH 07/34] use EGLDeviceEXT --- include/polyscope/render/opengl/gl_engine_egl.h | 3 ++- src/render/opengl/gl_engine_egl.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index 70e8bfe4..aa8999c4 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -12,6 +12,7 @@ #include "glad/glad.h" // glad must come first #include +#include #endif @@ -76,7 +77,7 @@ class GLEngineEGL : public GLEngine { EGLContext eglContext; // helpers - void sortAvailableDevicesByPreference(std::vector& devices); + void sortAvailableDevicesByPreference(std::vector& devices); }; } // namespace backend_openGL3 diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 60e1077d..1f4f94d8 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -284,15 +284,15 @@ void GLEngineEGL::initialize() { checkError(); } -void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& devices) { +void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& devices) { // Pre-load required extension functions PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLProcAddressAndCheck("eglQueryDeviceStringEXT"); // Build a list of devices and assign a score to each - std::vector> scoreDevices; - for (EGLDevice device : devices) { + std::vector> scoreDevices; + for (EGLDeviceEXT device : devices) { int score = 0; std::string vendorStr = eglQueryDeviceStringEXT(device, EGL_VENDOR); From b6c23a217b62f8fe58b320945e335c201fc4940f Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 28 Nov 2024 23:52:17 -0800 Subject: [PATCH 08/34] use list of ints --- .../polyscope/render/opengl/gl_engine_egl.h | 2 +- src/render/opengl/gl_engine_egl.cpp | 29 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index aa8999c4..31d23ce1 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -77,7 +77,7 @@ class GLEngineEGL : public GLEngine { EGLContext eglContext; // helpers - void sortAvailableDevicesByPreference(std::vector& devices); + void sortAvailableDevicesByPreference(std::vector& deviceInds, EGLDeviceEXT rawDevices[]); }; } // namespace backend_openGL3 diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 1f4f94d8..5912d985 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -150,9 +150,9 @@ void GLEngineEGL::initialize() { // Query the available EGL devices const int N_MAX_DEVICE = 256; - EGLDeviceEXT devices[N_MAX_DEVICE]; + EGLDeviceEXT rawDevices[N_MAX_DEVICE]; EGLint nDevices; - if (!eglQueryDevicesEXT(N_MAX_DEVICE, devices, &nDevices)) { + if (!eglQueryDevicesEXT(N_MAX_DEVICE, rawDevices, &nDevices)) { error("EGL: Failed to query devices."); } if (nDevices == 0) { @@ -161,13 +161,13 @@ void GLEngineEGL::initialize() { info("EGL: Found " + std::to_string(nDevices) + " EGL devices."); // Build an ordered list of which devices to try initializing with - std::vector devicesToTry; + std::vector deviceIndsToTry; if (options::eglDeviceIndex == -1) { info("EGL: No device index specified, attempting to intialize with each device sequentially until success."); - devicesToTry.resize(nDevices); - std::iota(devicesToTry.begin(), devicesToTry.end(), 0); - sortAvailableDevicesByPreference(devicesToTry); + deviceIndsToTry.resize(nDevices); + std::iota(deviceIndsToTry.begin(), deviceIndsToTry.end(), 0); + sortAvailableDevicesByPreference(deviceIndsToTry); } else { info("EGL: Device index " + std::to_string(options::eglDeviceIndex) + " manually selected, using that device."); @@ -177,15 +177,15 @@ void GLEngineEGL::initialize() { std::to_string(nDevices) + " devices available."); } - devicesToTry.push_back(options::eglDeviceIndex); + deviceIndsToTry.push_back(options::eglDeviceIndex); } bool successfulInit = false; EGLint majorVer, minorVer; - for (int32_t iDevice : devicesToTry) { + for (int32_t iDevice : deviceIndsToTry) { info("EGL: Attempting initialization with device " + std::to_string(iDevice)); - EGLDeviceEXT device = devices[iDevice]; + EGLDeviceEXT device = rawDevices[iDevice]; // Get an EGLDisplay for the device // (use the -platform / EXT version because it is the only one that seems to work in headless environments) @@ -284,15 +284,16 @@ void GLEngineEGL::initialize() { checkError(); } -void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& devices) { +void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceInds, EGLDeviceEXT rawDevices[]) { // Pre-load required extension functions PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLProcAddressAndCheck("eglQueryDeviceStringEXT"); // Build a list of devices and assign a score to each - std::vector> scoreDevices; - for (EGLDeviceEXT device : devices) { + std::vector> scoreDevices; + for (int32_t iDevice : deviceInds) { + EGLDeviceEXT device = rawDevices[iDevice]; int score = 0; std::string vendorStr = eglQueryDeviceStringEXT(device, EGL_VENDOR); @@ -316,7 +317,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& de if (vendorStr.find("amd") != std::string::npos) score += 2; if (vendorStr.find("nvidia") != std::string::npos) score += 3; - scoreDevices.emplace_back(score, device); + scoreDevices.emplace_back(score, iDevice); } // sort them by highest score @@ -325,7 +326,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& de // store them back in the given array for (size_t i = 0; i < devices.size(); i++) { - devices[i] = std::get<1>(scoreDevices[i]); + deviceInds[i] = std::get<1>(scoreDevices[i]); } } From 7f2294f949ad91980d6c960e4b9c9fabe747e17d Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 28 Nov 2024 23:53:01 -0800 Subject: [PATCH 09/34] two more small fixes --- src/render/opengl/gl_engine_egl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 5912d985..c3b4b118 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -167,7 +167,7 @@ void GLEngineEGL::initialize() { deviceIndsToTry.resize(nDevices); std::iota(deviceIndsToTry.begin(), deviceIndsToTry.end(), 0); - sortAvailableDevicesByPreference(deviceIndsToTry); + sortAvailableDevicesByPreference(deviceIndsToTry, rawDevices); } else { info("EGL: Device index " + std::to_string(options::eglDeviceIndex) + " manually selected, using that device."); @@ -325,7 +325,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI std::reverse(scoreDevices.begin(), scoreDevices.end()); // store them back in the given array - for (size_t i = 0; i < devices.size(); i++) { + for (size_t i = 0; i < deviceInds.size(); i++) { deviceInds[i] = std::get<1>(scoreDevices[i]); } } From 058a4743b8be34ea38fd4279c8a79ecb88b8010d Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Thu, 28 Nov 2024 23:58:57 -0800 Subject: [PATCH 10/34] string safety and logging --- src/render/opengl/gl_engine_egl.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index c3b4b118..49c38722 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -296,7 +296,18 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI EGLDeviceEXT device = rawDevices[iDevice]; int score = 0; - std::string vendorStr = eglQueryDeviceStringEXT(device, EGL_VENDOR); + const char* vendorStrRaw = eglQueryDeviceStringEXT(device, EGL_VENDOR); + + if (vendorStrRaw == nullptr) { + if (polyscope::options::verbosity > 5) { + std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << "NULL" + << " priority score: " << score << std::endl; + } + scoreDevices.emplace_back(score, iDevice); + continue; + } + + std::string vendorStr = vendorStrRaw; // lower-case it for the checks below std::transform(vendorStr.begin(), vendorStr.end(), vendorStr.begin(), @@ -324,6 +335,12 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI std::sort(scoreDevices.begin(), scoreDevices.end()); std::reverse(scoreDevices.begin(), scoreDevices.end()); + // at high verbosity levels, log the priority + if (polyscope::options::verbosity > 5) { + std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << vendorStr + << " priority score: " << score << std::endl; + } + // store them back in the given array for (size_t i = 0; i < deviceInds.size(); i++) { deviceInds[i] = std::get<1>(scoreDevices[i]); From ed0af02ebc29474abddd63f4cccc15f5d52fa775 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:00:31 -0800 Subject: [PATCH 11/34] nest logging correctly --- src/render/opengl/gl_engine_egl.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 49c38722..608e4ce1 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -328,6 +328,12 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI if (vendorStr.find("amd") != std::string::npos) score += 2; if (vendorStr.find("nvidia") != std::string::npos) score += 3; + // at high verbosity levels, log the priority + if (polyscope::options::verbosity > 5) { + std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << vendorStr + << " priority score: " << score << std::endl; + } + scoreDevices.emplace_back(score, iDevice); } @@ -335,11 +341,6 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI std::sort(scoreDevices.begin(), scoreDevices.end()); std::reverse(scoreDevices.begin(), scoreDevices.end()); - // at high verbosity levels, log the priority - if (polyscope::options::verbosity > 5) { - std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << vendorStr - << " priority score: " << score << std::endl; - } // store them back in the given array for (size_t i = 0; i < deviceInds.size(); i++) { From eddea5e7d9101ca19e90d1049bfc778beb596e2b Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:04:55 -0800 Subject: [PATCH 12/34] check for extension --- src/render/opengl/gl_engine_egl.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 608e4ce1..c56d4a07 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -286,6 +286,15 @@ void GLEngineEGL::initialize() { void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceInds, EGLDeviceEXT rawDevices[]) { + // check that we actually have the query extension + const char* extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (extensions && std::string(extensions).find("EGL_EXT_device_query") != std::string::npos) { + // good case, supported + } else { + info("EGL: cannot sort devices by preference, EGL_EXT_device_query is not supported") + return; + } + // Pre-load required extension functions PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLProcAddressAndCheck("eglQueryDeviceStringEXT"); From e4f8d4352ee079aa307b2577405c923a8266eece Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:05:24 -0800 Subject: [PATCH 13/34] missing semicolon --- src/render/opengl/gl_engine_egl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index c56d4a07..1887c40e 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -291,7 +291,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI if (extensions && std::string(extensions).find("EGL_EXT_device_query") != std::string::npos) { // good case, supported } else { - info("EGL: cannot sort devices by preference, EGL_EXT_device_query is not supported") + info("EGL: cannot sort devices by preference, EGL_EXT_device_query is not supported"); return; } From 50954fdb43983f023cb7c95e0ee9357b56e34e6b Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:10:38 -0800 Subject: [PATCH 14/34] more logging --- src/render/opengl/gl_engine_egl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 1887c40e..c5305008 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -184,8 +184,8 @@ void GLEngineEGL::initialize() { EGLint majorVer, minorVer; for (int32_t iDevice : deviceIndsToTry) { - info("EGL: Attempting initialization with device " + std::to_string(iDevice)); EGLDeviceEXT device = rawDevices[iDevice]; + info("EGL: Attempting initialization with device ind: " + std::to_string(iDevice) + " handle: " + std::to_string(device)); // Get an EGLDisplay for the device // (use the -platform / EXT version because it is the only one that seems to work in headless environments) @@ -295,7 +295,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI return; } - // Pre-load required extension functions + // Pre-load query extension functions PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLProcAddressAndCheck("eglQueryDeviceStringEXT"); @@ -309,7 +309,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI if (vendorStrRaw == nullptr) { if (polyscope::options::verbosity > 5) { - std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << "NULL" + std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " . device: " << device << " vendor: " << "NULL" << " priority score: " << score << std::endl; } scoreDevices.emplace_back(score, iDevice); From 044999ef26b186c1b3d48facc931eccd649a4920 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:12:49 -0800 Subject: [PATCH 15/34] cast --- src/render/opengl/gl_engine_egl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index c5305008..05d6712c 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -185,7 +185,7 @@ void GLEngineEGL::initialize() { for (int32_t iDevice : deviceIndsToTry) { EGLDeviceEXT device = rawDevices[iDevice]; - info("EGL: Attempting initialization with device ind: " + std::to_string(iDevice) + " handle: " + std::to_string(device)); + info("EGL: Attempting initialization with device ind: " + std::to_string(iDevice) + " handle: " + std::to_string((size_t)device)); // Get an EGLDisplay for the device // (use the -platform / EXT version because it is the only one that seems to work in headless environments) @@ -309,7 +309,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI if (vendorStrRaw == nullptr) { if (polyscope::options::verbosity > 5) { - std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " . device: " << device << " vendor: " << "NULL" + std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " . device: " << (size_t)device << " vendor: " << "NULL" << " priority score: " << score << std::endl; } scoreDevices.emplace_back(score, iDevice); From 09854e4cdf7254bf44f82cf2f539413e6ab1bc3e Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:15:33 -0800 Subject: [PATCH 16/34] test --- src/render/opengl/gl_engine_egl.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 05d6712c..0cd904db 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -160,6 +160,30 @@ void GLEngineEGL::initialize() { } info("EGL: Found " + std::to_string(nDevices) + " EGL devices."); +{ + PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT"); +PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)eglGetProcAddress("eglQueryDeviceStringEXT"); + +if (!eglQueryDevicesEXT || !eglQueryDeviceStringEXT) { + fprintf(stderr, "Required extension functions not available.\n"); + return EXIT_FAILURE; +} + +EGLDeviceEXT devices[10]; +EGLint num_devices; +if (eglQueryDevicesEXT(10, devices, &num_devices)) { + for (int i = 0; i < num_devices; i++) { + const char* vendor = eglQueryDeviceStringEXT(devices[i], EGL_VENDOR); + printf("Device %d: Vendor: %s\n", i, vendor ? vendor : "Unknown"); + } +} else { + fprintf(stderr, "eglQueryDevicesEXT failed.\n"); +} + +} + + + // Build an ordered list of which devices to try initializing with std::vector deviceIndsToTry; if (options::eglDeviceIndex == -1) { From 348d4f6e1c29dcc13d524d9e9fc1653768b54c7e Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:15:50 -0800 Subject: [PATCH 17/34] test --- src/render/opengl/gl_engine_egl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 0cd904db..3a01b041 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -166,7 +166,7 @@ PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRIN if (!eglQueryDevicesEXT || !eglQueryDeviceStringEXT) { fprintf(stderr, "Required extension functions not available.\n"); - return EXIT_FAILURE; + // return EXIT_FAILURE; } EGLDeviceEXT devices[10]; From cb04777b05859fdceba8cf5f8009b058a9c6e731 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:35:41 -0800 Subject: [PATCH 18/34] try fake initialize --- src/render/opengl/gl_engine_egl.cpp | 48 ++++++++++++----------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 3a01b041..1b70454d 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -160,30 +160,6 @@ void GLEngineEGL::initialize() { } info("EGL: Found " + std::to_string(nDevices) + " EGL devices."); -{ - PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT"); -PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)eglGetProcAddress("eglQueryDeviceStringEXT"); - -if (!eglQueryDevicesEXT || !eglQueryDeviceStringEXT) { - fprintf(stderr, "Required extension functions not available.\n"); - // return EXIT_FAILURE; -} - -EGLDeviceEXT devices[10]; -EGLint num_devices; -if (eglQueryDevicesEXT(10, devices, &num_devices)) { - for (int i = 0; i < num_devices; i++) { - const char* vendor = eglQueryDeviceStringEXT(devices[i], EGL_VENDOR); - printf("Device %d: Vendor: %s\n", i, vendor ? vendor : "Unknown"); - } -} else { - fprintf(stderr, "eglQueryDevicesEXT failed.\n"); -} - -} - - - // Build an ordered list of which devices to try initializing with std::vector deviceIndsToTry; if (options::eglDeviceIndex == -1) { @@ -327,16 +303,32 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI std::vector> scoreDevices; for (int32_t iDevice : deviceInds) { EGLDeviceEXT device = rawDevices[iDevice]; - int score = 0; + scoreDevices.emplace_back(0, iDevice); + int& score = std::get<0>(scoreDevices.back()); + + { + EGLDisplay display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, NULL); + if (display == EGL_NO_DISPLAY) { + fprintf(stderr, "Failed to get EGLDisplay for device %d.\n", i); + continue; + } - const char* vendorStrRaw = eglQueryDeviceStringEXT(device, EGL_VENDOR); + if (!eglInitialize(display, NULL, NULL)) { + fprintf(stderr, "Failed to initialize EGLDisplay for device %d.\n", i); + continue; + } + const char* vendor = eglQueryString(display, EGL_VENDOR); + printf("Device %d Vendor from Display: %s\n", i, vendor ? vendor : "Unknown"); + eglTerminate(display); + } + + const char* vendorStrRaw = eglQueryDeviceStringEXT(device, EGL_VENDOR); if (vendorStrRaw == nullptr) { if (polyscope::options::verbosity > 5) { std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " . device: " << (size_t)device << " vendor: " << "NULL" << " priority score: " << score << std::endl; } - scoreDevices.emplace_back(score, iDevice); continue; } @@ -366,8 +358,6 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << vendorStr << " priority score: " << score << std::endl; } - - scoreDevices.emplace_back(score, iDevice); } // sort them by highest score From 119e4bcb0802469ac82e13c36fbc7332f9b7adc3 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 00:36:52 -0800 Subject: [PATCH 19/34] compile fixes --- src/render/opengl/gl_engine_egl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 1b70454d..9fcbc1d8 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -307,19 +307,19 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI int& score = std::get<0>(scoreDevices.back()); { - EGLDisplay display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, NULL); + EGLDisplay display = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, device, NULL); if (display == EGL_NO_DISPLAY) { - fprintf(stderr, "Failed to get EGLDisplay for device %d.\n", i); + fprintf(stderr, "Failed to get EGLDisplay for device %d.\n", iDevice); continue; } if (!eglInitialize(display, NULL, NULL)) { - fprintf(stderr, "Failed to initialize EGLDisplay for device %d.\n", i); + fprintf(stderr, "Failed to initialize EGLDisplay for device %d.\n", iDevice); continue; } const char* vendor = eglQueryString(display, EGL_VENDOR); - printf("Device %d Vendor from Display: %s\n", i, vendor ? vendor : "Unknown"); + printf("Device %d Vendor from Display: %s\n", iDevice, vendor ? vendor : "Unknown"); eglTerminate(display); } From 9eb6d7ceba9bb22120e7e506b12afb3a41bfb7c9 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 01:12:05 -0800 Subject: [PATCH 20/34] revert debugging --- src/render/opengl/gl_engine_egl.cpp | 30 ++++++++--------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 9fcbc1d8..1887c40e 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -184,8 +184,8 @@ void GLEngineEGL::initialize() { EGLint majorVer, minorVer; for (int32_t iDevice : deviceIndsToTry) { + info("EGL: Attempting initialization with device " + std::to_string(iDevice)); EGLDeviceEXT device = rawDevices[iDevice]; - info("EGL: Attempting initialization with device ind: " + std::to_string(iDevice) + " handle: " + std::to_string((size_t)device)); // Get an EGLDisplay for the device // (use the -platform / EXT version because it is the only one that seems to work in headless environments) @@ -295,7 +295,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI return; } - // Pre-load query extension functions + // Pre-load required extension functions PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)getEGLProcAddressAndCheck("eglQueryDeviceStringEXT"); @@ -303,32 +303,16 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI std::vector> scoreDevices; for (int32_t iDevice : deviceInds) { EGLDeviceEXT device = rawDevices[iDevice]; - scoreDevices.emplace_back(0, iDevice); - int& score = std::get<0>(scoreDevices.back()); - - { - EGLDisplay display = eglGetPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, device, NULL); - if (display == EGL_NO_DISPLAY) { - fprintf(stderr, "Failed to get EGLDisplay for device %d.\n", iDevice); - continue; - } - - if (!eglInitialize(display, NULL, NULL)) { - fprintf(stderr, "Failed to initialize EGLDisplay for device %d.\n", iDevice); - continue; - } - - const char* vendor = eglQueryString(display, EGL_VENDOR); - printf("Device %d Vendor from Display: %s\n", iDevice, vendor ? vendor : "Unknown"); - eglTerminate(display); - } + int score = 0; const char* vendorStrRaw = eglQueryDeviceStringEXT(device, EGL_VENDOR); + if (vendorStrRaw == nullptr) { if (polyscope::options::verbosity > 5) { - std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " . device: " << (size_t)device << " vendor: " << "NULL" + std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << "NULL" << " priority score: " << score << std::endl; } + scoreDevices.emplace_back(score, iDevice); continue; } @@ -358,6 +342,8 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << vendorStr << " priority score: " << score << std::endl; } + + scoreDevices.emplace_back(score, iDevice); } // sort them by highest score From e10b602b289a90b69de1d9dae1c966ea8f6c953b Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 01:17:03 -0800 Subject: [PATCH 21/34] clean up logging --- src/render/initialize_backend.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/render/initialize_backend.cpp b/src/render/initialize_backend.cpp index 95e20772..30591a60 100644 --- a/src/render/initialize_backend.cpp +++ b/src/render/initialize_backend.cpp @@ -55,8 +55,7 @@ void initializeRenderEngine(std::string backend) { initSucces = true; } catch (const std::exception& e) { if (options::verbosity > 0) { - info("Attempting automatic initialization. Could not initialize backend [openGL3_glfw]. Message: " + - std::string(e.what())); + info("Attempting automatic initialization. Could not initialize backend [openGL3_glfw]."); } } if (initSucces) return; @@ -70,16 +69,15 @@ void initializeRenderEngine(std::string backend) { initSucces = true; } catch (const std::exception& e) { if (options::verbosity > 0) { - info("Attempting automatic initialization. Could not initialize backend [openGL3_egl]. Message: " + - std::string(e.what())); + info("Attempting automatic initialization. Could not initialize backend [openGL3_egl]."); } } if (initSucces) { if (options::verbosity > 0) { info("Automatic initialization could not create an interactive backend, and created a headless backend " "instead. This likely means no displays are available. With the headless backend, you can still run " - "Polyscope and even render, for instance to record screenshots. However no interactive windows can be " - "created."); + "Polyscope and even render, for instance to save images of visualizations. However no interactive " + "windows can be created."); } return; } From a60e2c56a90fa4bff9925821fd33820077c87f55 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 01:21:09 -0800 Subject: [PATCH 22/34] cleanup --- src/render/opengl/gl_engine_egl.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 1887c40e..fe04f5c8 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -289,10 +289,10 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI // check that we actually have the query extension const char* extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (extensions && std::string(extensions).find("EGL_EXT_device_query") != std::string::npos) { - // good case, supported + // good case, supported } else { - info("EGL: cannot sort devices by preference, EGL_EXT_device_query is not supported"); - return; + info("EGL: cannot sort devices by preference, EGL_EXT_device_query is not supported"); + return; } // Pre-load required extension functions @@ -305,8 +305,14 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI EGLDeviceEXT device = rawDevices[iDevice]; int score = 0; + // Add the device index as a weak term, so we prefer the original order + score += (deviceInds.size() - iDevice - 1); + + const char* vendorStrRaw = eglQueryDeviceStringEXT(device, EGL_VENDOR); + // NOTE: on many machines (cloud VMs?) the query string above is nullptr, and this whole function does nothing + // useful if (vendorStrRaw == nullptr) { if (polyscope::options::verbosity > 5) { std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << "NULL" @@ -333,9 +339,10 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI // ONEDAY: figure out a better policy to detect discrete devices.... // assign scores based on vendors to prefer discrete gpus - if (vendorStr.find("intel") != std::string::npos) score += 1; - if (vendorStr.find("amd") != std::string::npos) score += 2; - if (vendorStr.find("nvidia") != std::string::npos) score += 3; + const int32_t VENDOR_MULT = 100; // give this score entry a very high preference + if (vendorStr.find("intel") != std::string::npos) score += 1 * VENDOR_MULT; + if (vendorStr.find("amd") != std::string::npos) score += 2 * VENDOR_MULT; + if (vendorStr.find("nvidia") != std::string::npos) score += 3 * VENDOR_MULT; // at high verbosity levels, log the priority if (polyscope::options::verbosity > 5) { From d47099cd56ed10ac9e8ae0834d0f70c514ceada0 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 01:37:27 -0800 Subject: [PATCH 23/34] back to front --- src/render/opengl/gl_engine_egl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index fe04f5c8..a45be795 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -305,9 +305,9 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI EGLDeviceEXT device = rawDevices[iDevice]; int score = 0; - // Add the device index as a weak term, so we prefer the original order - score += (deviceInds.size() - iDevice - 1); - + // Heuristic, non-software renderers seem to come last, so add a term to the score that prefers later-listed entries + // TODO find a way to test for software rsterization for real + score += iDevice; const char* vendorStrRaw = eglQueryDeviceStringEXT(device, EGL_VENDOR); From 0fb4b2708935027c522ed71438a4a04afb35cbba Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 29 Nov 2024 01:39:32 -0800 Subject: [PATCH 24/34] cleanup --- src/render/opengl/gl_engine_egl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index a45be795..6c327ef2 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -163,7 +163,8 @@ void GLEngineEGL::initialize() { // Build an ordered list of which devices to try initializing with std::vector deviceIndsToTry; if (options::eglDeviceIndex == -1) { - info("EGL: No device index specified, attempting to intialize with each device sequentially until success."); + info("EGL: No device index specified, attempting to intialize with each device in heuristic-guess order until " + "success."); deviceIndsToTry.resize(nDevices); std::iota(deviceIndsToTry.begin(), deviceIndsToTry.end(), 0); From a5b4e999b124794e4c08f0eda5c5852f36b658f3 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 27 Dec 2024 12:12:57 -0500 Subject: [PATCH 25/34] add user-facing options for headless setup --- examples/demo-app/demo_app.cpp | 12 +++-- include/polyscope/options.h | 7 ++- include/polyscope/polyscope.h | 6 +++ include/polyscope/render/engine.h | 3 ++ .../polyscope/render/opengl/gl_engine_egl.h | 3 ++ src/options.cpp | 1 + src/polyscope.cpp | 18 ++++++++ src/render/initialize_backend.cpp | 46 +++++++++++-------- 8 files changed, 74 insertions(+), 22 deletions(-) diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index f7a0d6cb..27b826a2 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -846,6 +846,7 @@ int main(int argc, char** argv) { // polyscope::options::maxFPS = -1; polyscope::options::verbosity = 100; polyscope::options::enableRenderErrorChecks = true; + polyscope::options::allowHeadlessBackends = true; // Initialize polyscope polyscope::init(); @@ -871,9 +872,14 @@ int main(int argc, char** argv) { // Add a few gui elements polyscope::state::userCallback = callback; - // Show the gui - polyscope::show(); - + if (polyscope::isHeadless()) { + // save a screenshot to prove we initialized + std::cout << "Headless mode detected, saving screenshot" << std::endl; + polyscope::screenshot("headless_screenshot.png"); + } else { + // Show the gui + polyscope::show(); + } // main loop using manual frameTick() instead // while (true) { // polyscope::frameTick(); diff --git a/include/polyscope/options.h b/include/polyscope/options.h index 6ffcae0f..9a92f381 100644 --- a/include/polyscope/options.h +++ b/include/polyscope/options.h @@ -13,7 +13,9 @@ namespace polyscope { -namespace options { // A general name to use when referring to the program in window headings. +namespace options { + +// A general name to use when referring to the program in window headings. extern std::string programName; // How much should polyscope print to std::out? @@ -28,6 +30,9 @@ extern std::string printPrefix; // Should errors throw exceptions, or just display? (default false) extern bool errorsThrowExceptions; +// Allow initialization to create headless backends (default: false) +extern bool allowHeadlessBackends; + // Don't let the main loop run at more than this speed. (-1 disables) (default: 60) extern int maxFPS; diff --git a/include/polyscope/polyscope.h b/include/polyscope/polyscope.h index c7205399..25f1f29d 100644 --- a/include/polyscope/polyscope.h +++ b/include/polyscope/polyscope.h @@ -66,6 +66,12 @@ void shutdown(bool allowMidFrameShutdown=false); // deciding when to exit your control loop when using frameTick() bool windowRequestsClose(); +// Is Polyscope running in 'headless' mode? Headless means there is no physical display to open windows on, +// e.g. when running on a remote server. It is still possible to run Polyscope in such settings with a supported +// backend (currently, the EGL backend only), and render to save screenshots or for other purposes. +// Can only be called after initialization. +bool isHeadless(); + // === Global variables === namespace state { diff --git a/include/polyscope/render/engine.h b/include/polyscope/render/engine.h index 3d71e8c0..7a89a93b 100644 --- a/include/polyscope/render/engine.h +++ b/include/polyscope/render/engine.h @@ -445,6 +445,9 @@ class Engine { virtual void shutdown() {}; virtual void checkError(bool fatal = false) = 0; void buildEngineGui(); + + // 'headless' means there is no physical display to actually render to, e.g. when running on a remote server + virtual bool isHeadless() { return false; } virtual void clearDisplay(); virtual void bindDisplay(); diff --git a/include/polyscope/render/opengl/gl_engine_egl.h b/include/polyscope/render/opengl/gl_engine_egl.h index 31d23ce1..45f50d41 100644 --- a/include/polyscope/render/opengl/gl_engine_egl.h +++ b/include/polyscope/render/opengl/gl_engine_egl.h @@ -42,6 +42,9 @@ class GLEngineEGL : public GLEngine { virtual void shutdown() override; void swapDisplayBuffers() override; void checkError(bool fatal = false) override; + + // EGL backend is always headless + virtual bool isHeadless() override { return true; } // === Windowing and framework things diff --git a/src/options.cpp b/src/options.cpp index 703ff5da..3b87de4d 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -9,6 +9,7 @@ namespace options { std::string programName = "Polyscope"; int verbosity = 2; std::string printPrefix = "[polyscope] "; +bool allowHeadlessBackends = false; bool errorsThrowExceptions = false; bool debugDrawPickBuffer = false; int maxFPS = 60; diff --git a/src/polyscope.cpp b/src/polyscope.cpp index 575f8e2b..40f7a28e 100644 --- a/src/polyscope.cpp +++ b/src/polyscope.cpp @@ -893,6 +893,14 @@ void show(size_t forFrames) { if (!state::initialized) { exception("must initialize Polyscope with polyscope::init() before calling polyscope::show()."); } + + if (isHeadless() && forFrames == 0) { + info("You called show() while in headless mode. In headless mode there is no display to create windows on. By " + "default, the show() call will block indefinitely. If you did not mean to run in headless mode, check the " + "initialization settings. Otherwise, be sure to set a callback to make something happen while polyscope is " + "showing the UI, or use functions like screenshot() to render directly without calling show()."); + } + unshowRequested = false; // the popContext() doesn't quit until _after_ the last frame, so we need to decrement by 1 to get the count right @@ -934,6 +942,16 @@ bool windowRequestsClose() { return false; } +bool isHeadless() { + if (!isInitialized()) { + exception("must initialize Polyscope with init() before calling isHeadless()."); + } + if (render::engine) { + return render::engine->isHeadless(); + } + return false; +} + void shutdown(bool allowMidFrameShutdown) { if (!allowMidFrameShutdown && contextStack.size() > 1) { diff --git a/src/render/initialize_backend.cpp b/src/render/initialize_backend.cpp index 30591a60..7c19283d 100644 --- a/src/render/initialize_backend.cpp +++ b/src/render/initialize_backend.cpp @@ -46,6 +46,7 @@ void initializeRenderEngine(std::string backend) { // Attempt to automatically initialize by trynig bool initSucces = false; + std::string extraMessage = ""; #ifdef POLYSCOPE_BACKEND_OPENGL3_GLFW_ENABLED // First try GLFW, if available @@ -55,38 +56,47 @@ void initializeRenderEngine(std::string backend) { initSucces = true; } catch (const std::exception& e) { if (options::verbosity > 0) { - info("Attempting automatic initialization. Could not initialize backend [openGL3_glfw]."); + info("Automatic initialization status: could not initialize backend [openGL3_glfw]."); } } if (initSucces) return; #endif #ifdef POLYSCOPE_BACKEND_OPENGL3_EGL_ENABLED - // Then, try EGL if available - engineBackendName = "openGL3_egl"; - try { - backend_openGL3::initializeRenderEngine_egl(); - initSucces = true; - } catch (const std::exception& e) { - if (options::verbosity > 0) { - info("Attempting automatic initialization. Could not initialize backend [openGL3_egl]."); + + if (options::allowHeadlessBackends) { + + // Then, try EGL if available + engineBackendName = "openGL3_egl"; + try { + backend_openGL3::initializeRenderEngine_egl(); + initSucces = true; + } catch (const std::exception& e) { + if (options::verbosity > 0) { + info("Automatic initialization status: could not initialize backend [openGL3_egl]."); + } } - } - if (initSucces) { - if (options::verbosity > 0) { - info("Automatic initialization could not create an interactive backend, and created a headless backend " - "instead. This likely means no displays are available. With the headless backend, you can still run " - "Polyscope and even render, for instance to save images of visualizations. However no interactive " - "windows can be created."); + if (initSucces) { + if (options::verbosity > 0) { + info("Automatic initialization could not create an interactive backend, and created a headless backend " + "instead. This likely means no displays are available. With the headless backend, you can still run " + "Polyscope and even render, for instance to save images of visualizations. However no interactive " + "windows can be created."); + } + return; } - return; + + } else { + extraMessage = " The headless EGL backend was available, but allowHeadlessBackends=false. Set it to true for " + "headless initialization."; } + #endif // Don't bother trying the 'mock' backend, it is unlikely to be what the user wants from the 'auto' option // Failure - exception("Automatic initialization: no Polyscope backends could be initialized successfully."); + exception("Automatic initialization: no Polyscope backends could be initialized successfully." + extraMessage); } else { exception("unrecognized Polyscope backend " + backend); From 2ffb0fef014c67ef443c8bad87ccef5e65df5e06 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 27 Dec 2024 12:30:24 -0500 Subject: [PATCH 26/34] fix framecount check --- src/polyscope.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polyscope.cpp b/src/polyscope.cpp index 40f7a28e..a4f176fd 100644 --- a/src/polyscope.cpp +++ b/src/polyscope.cpp @@ -894,7 +894,7 @@ void show(size_t forFrames) { exception("must initialize Polyscope with polyscope::init() before calling polyscope::show()."); } - if (isHeadless() && forFrames == 0) { + if (isHeadless() && forFrames == std::numeric_limits::max()) { info("You called show() while in headless mode. In headless mode there is no display to create windows on. By " "default, the show() call will block indefinitely. If you did not mean to run in headless mode, check the " "initialization settings. Otherwise, be sure to set a callback to make something happen while polyscope is " From 3ce6a6e945490dd938b19891b993fd9555cf0939 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 27 Dec 2024 19:10:54 -0500 Subject: [PATCH 27/34] try testing EGL on ci --- .github/workflows/linux.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8f7b2b00..2b0a5d00 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -45,10 +45,13 @@ jobs: run: sudo apt-get update && sudo apt-get install -y xorg-dev libglu1-mesa-dev xpra xserver-xorg-video-dummy freeglut3-dev - name: configure - run: cd test && mkdir build && cd build && cmake -DBUILD_SHARED_LIBS=TRUE -DCMAKE_BUILD_TYPE=Debug -DPOLYSCOPE_BACKEND_OPENGL3_GLFW=ON -DPOLYSCOPE_BACKEND_OPENGL_MOCK=ON .. + run: cd test && mkdir build && cd build && cmake -DBUILD_SHARED_LIBS=TRUE -DCMAKE_BUILD_TYPE=Debug -DPOLYSCOPE_BACKEND_OPENGL3_GLFW=ON -DPOLYSCOPE_BACKEND_OPENGL_MOCK=ON -DPOLYSCOPE_BACKEND_OPENGL3_EGL=ON .. - name: build run: cd test/build && make - - name: run test + - name: run test mock backend run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock + + - name: run test egl backend + run: ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_egl From e3752af97be935bf3899d933c56c85b69e4002a9 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 27 Dec 2024 19:17:24 -0500 Subject: [PATCH 28/34] fix ci script --- .github/workflows/linux.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2b0a5d00..d89e65c5 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -27,8 +27,11 @@ jobs: - name: build run: cd test/build && make - - name: run test + - name: run test mock backend run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock + + - name: run test egl backend + run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_egl build_shared: strategy: @@ -54,4 +57,4 @@ jobs: run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock - name: run test egl backend - run: ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_egl + run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_egl From 95982411208b88565fff7b10dbd0938316069086 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Fri, 27 Dec 2024 19:29:09 -0500 Subject: [PATCH 29/34] fix backend string --- .github/workflows/linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d89e65c5..1bd35f07 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -31,7 +31,7 @@ jobs: run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock - name: run test egl backend - run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_egl + run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl build_shared: strategy: @@ -57,4 +57,4 @@ jobs: run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock - name: run test egl backend - run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_egl + run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl From 612c367c0428644c833709c86a440f8c7d89a9ff Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sat, 28 Dec 2024 00:34:17 -0500 Subject: [PATCH 30/34] improve logging string formatting --- src/render/opengl/gl_engine_egl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/render/opengl/gl_engine_egl.cpp b/src/render/opengl/gl_engine_egl.cpp index 6c327ef2..c6c0c01d 100644 --- a/src/render/opengl/gl_engine_egl.cpp +++ b/src/render/opengl/gl_engine_egl.cpp @@ -316,7 +316,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI // useful if (vendorStrRaw == nullptr) { if (polyscope::options::verbosity > 5) { - std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << "NULL" + std::cout << polyscope::options::printPrefix << " EGLDevice " << iDevice << " -- vendor: " << "NULL" << " priority score: " << score << std::endl; } scoreDevices.emplace_back(score, iDevice); @@ -347,7 +347,7 @@ void GLEngineEGL::sortAvailableDevicesByPreference(std::vector& deviceI // at high verbosity levels, log the priority if (polyscope::options::verbosity > 5) { - std::cout << polyscope::options::printPrefix << " EGLDevice ind" << iDevice << " vendor: " << vendorStr + std::cout << polyscope::options::printPrefix << " EGLDevice " << iDevice << " -- vendor: " << vendorStr << " priority score: " << score << std::endl; } From 736eacb0b87bec6a15cbc7287340b11d0661e92f Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sat, 28 Dec 2024 15:06:07 -0500 Subject: [PATCH 31/34] clean up context stack on shutdown --- src/polyscope.cpp | 3 ++- test/include/polyscope_test.h | 7 +------ test/src/basics_test.cpp | 5 +++++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/polyscope.cpp b/src/polyscope.cpp index a4f176fd..a21fd16f 100644 --- a/src/polyscope.cpp +++ b/src/polyscope.cpp @@ -894,7 +894,7 @@ void show(size_t forFrames) { exception("must initialize Polyscope with polyscope::init() before calling polyscope::show()."); } - if (isHeadless() && forFrames == std::numeric_limits::max()) { + if (isHeadless() && forFrames == 0) { info("You called show() while in headless mode. In headless mode there is no display to create windows on. By " "default, the show() call will block indefinitely. If you did not mean to run in headless mode, check the " "initialization settings. Otherwise, be sure to set a callback to make something happen while polyscope is " @@ -973,6 +973,7 @@ void shutdown(bool allowMidFrameShutdown) { // Shut down the render engine render::engine->shutdown(); delete render::engine; + contextStack.clear(); render::engine = nullptr; state::backend = ""; state::initialized = false; diff --git a/test/include/polyscope_test.h b/test/include/polyscope_test.h index c5b4a6e9..c3d8a572 100644 --- a/test/include/polyscope_test.h +++ b/test/include/polyscope_test.h @@ -38,12 +38,7 @@ class PolyscopeTest : public ::testing::Test { // Per-test-suite tear-down. // Called after the last test in this test suite. // Can be omitted if not needed. - /* - static void TearDownTestSuite() { - delete shared_resource_; - shared_resource_ = NULL; - } - */ + static void TearDownTestSuite() { polyscope::shutdown(); } // You can define per-test set-up logic as usual. // virtual void SetUp() { ... } diff --git a/test/src/basics_test.cpp b/test/src/basics_test.cpp index afb2aafc..7f2cdc9d 100644 --- a/test/src/basics_test.cpp +++ b/test/src/basics_test.cpp @@ -88,6 +88,11 @@ TEST_F(PolyscopeTest, ShutdownAndReinitialize) { polyscope::shutdown(); SetUpTestSuite(); polyscope::show(3); + + // do it twice -- we've had some bugs where the first shutdown doesn't clean up properly + polyscope::shutdown(); + SetUpTestSuite(); + polyscope::show(3); } // Make sure that creating an empty buffer does not throw errors From 21438934aab59205353949938246d0ad36adb1a4 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sat, 28 Dec 2024 15:24:05 -0500 Subject: [PATCH 32/34] don't check asan leaks in egl test --- .github/workflows/linux.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1bd35f07..0771284f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -57,4 +57,7 @@ jobs: run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock - name: run test egl backend - run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl + # We get memory leaks inside of EGL that I can't track down. With ASAN, this means the exit code is always nonzero, + # which is indistinguishable from tests failing. The ASAN_OPTIONS=detect_leaks=0 skips checking leaks for this test + # as a workaround. + run: cd test/build && ASAN_OPTIONS=detect_leaks=0 ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl From c5eafee44d68474778194fd6ca0f779e24a27df4 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sat, 28 Dec 2024 15:43:15 -0500 Subject: [PATCH 33/34] use asan settings for both configurations --- .github/workflows/linux.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 0771284f..656953a0 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -31,7 +31,10 @@ jobs: run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL_mock - name: run test egl backend - run: cd test/build && ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl + # We get memory leaks inside of EGL that I can't track down. With ASAN, this means the exit code is always nonzero, + # which is indistinguishable from tests failing. The ASAN_OPTIONS=detect_leaks=0 skips checking leaks for this test + # as a workaround. + run: cd test/build && ASAN_OPTIONS=detect_leaks=0 ./bin/polyscope-test --gtest_catch_exceptions=0 backend=openGL3_egl build_shared: strategy: From 59b7119c213f13e469dd5e1a8d4352883e064519 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 29 Dec 2024 20:07:37 -0500 Subject: [PATCH 34/34] comment clarity --- include/polyscope/options.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/polyscope/options.h b/include/polyscope/options.h index 9a92f381..356412ea 100644 --- a/include/polyscope/options.h +++ b/include/polyscope/options.h @@ -30,8 +30,9 @@ extern std::string printPrefix; // Should errors throw exceptions, or just display? (default false) extern bool errorsThrowExceptions; -// Allow initialization to create headless backends (default: false) -extern bool allowHeadlessBackends; +// Allow initialization to create headless backends when selecting a backend automatically +// (they can still created explicitly by name) (default: false) +extern bool allowHeadlessBackends; // Don't let the main loop run at more than this speed. (-1 disables) (default: 60) extern int maxFPS;