From 55acd62d7e547d92cbc68c00d0ad8cbc39d37226 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 12 May 2025 14:40:02 -0700 Subject: [PATCH 01/79] Added return value to ExecuteScriptCommand(). --- lib/script_controller.cpp | 9 +++++---- lib/script_controller.hpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index 27cf8785..b54a4412 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -348,12 +348,12 @@ void ScriptController::PrintCommands() } } -void ScriptController::ExecuteScriptCommand() +bool ScriptController::ExecuteScriptCommand() { if (!script) { cout << "No script stream defined! (Bug?)" << endl; - return; + return false; } istream &scr = *script; @@ -366,7 +366,7 @@ void ScriptController::ExecuteScriptCommand() { cout << "End of script." << endl; scr_level = 0; - return; + return false; } if (scr.peek() == '#') { @@ -394,7 +394,7 @@ void ScriptController::ExecuteScriptCommand() { cout << "Unknown command in script: " << word << endl; PrintCommands(); - break; + return false; } const Command cmd = (Command)(it - commands.begin()); @@ -820,6 +820,7 @@ void ScriptController::ExecuteScriptCommand() done_one_command = 1; } + return true; } thread_local ScriptController *ScriptController::script_ctrl = NULL; diff --git a/lib/script_controller.hpp b/lib/script_controller.hpp index 99b173a5..a16348a4 100644 --- a/lib/script_controller.hpp +++ b/lib/script_controller.hpp @@ -47,7 +47,7 @@ class ScriptController static void ScriptControl(); static void PrintCommands(); - void ExecuteScriptCommand(); + bool ExecuteScriptCommand(); public: ScriptController(Window win_) : win(std::move(win_)) { } From b6a7cb02fd2d26174db79fdf5811213cad5b415d Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 12 May 2025 15:15:43 -0700 Subject: [PATCH 02/79] Added base GLWindow to SdlWindow and added EglWindow basic implementation. --- lib/CMakeLists.txt | 4 + lib/aux_js.cpp | 6 +- lib/aux_vis.cpp | 280 +++++++++++++++++++++++++------------------ lib/aux_vis.hpp | 4 +- lib/egl.cpp | 210 ++++++++++++++++++++++++++++++++ lib/egl.hpp | 50 ++++++++ lib/font.cpp | 4 +- lib/glwindow.cpp | 101 ++++++++++++++++ lib/glwindow.hpp | 58 +++++++++ lib/openglvis.cpp | 6 +- lib/openglvis.hpp | 3 +- lib/sdl.cpp | 83 +------------ lib/sdl.hpp | 25 ++-- lib/threads.cpp | 2 +- lib/vsdata.cpp | 86 +++++++------ lib/vssolution.cpp | 51 ++++---- lib/vssolution3d.cpp | 61 +++++----- lib/vsvector.cpp | 17 +-- lib/vsvector3d.cpp | 31 ++--- lib/window.cpp | 26 +++- lib/window.hpp | 2 +- makefile | 20 ++-- 22 files changed, 777 insertions(+), 353 deletions(-) create mode 100644 lib/egl.cpp create mode 100644 lib/egl.hpp create mode 100644 lib/glwindow.cpp create mode 100644 lib/glwindow.hpp diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 4f63a6c0..1445768e 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -17,9 +17,11 @@ list(APPEND SOURCES aux_vis.cpp coll_reader.cpp data_state.cpp + egl.cpp file_reader.cpp font.cpp gltf.cpp + glwindow.cpp material.cpp openglvis.cpp palettes.cpp @@ -46,10 +48,12 @@ list(APPEND HEADERS aux_vis.hpp coll_reader.hpp data_state.hpp + egl.hpp file_reader.hpp font.hpp geom_utils.hpp gltf.hpp + glwindow.hpp logo.hpp material.hpp openglvis.hpp diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index e3a14f56..831d3c76 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -44,7 +44,7 @@ using namespace mfem; void display(std::stringstream & commands, const int w, const int h) { // reset antialiasing - GetAppWindow()->getRenderer().setAntialiasing(0); + GetGLWindow()->getRenderer().setAntialiasing(0); std::string word; double minv = 0.0, maxv = 0.0; @@ -274,7 +274,7 @@ std::string getHelpString() em::val getScreenBuffer(bool flip_y=false) { MyExpose(); - auto * wnd = GetAppWindow(); + auto * wnd = GetGLWindow(); int w, h; wnd->getGLDrawSize(w, h); @@ -317,7 +317,7 @@ em::val getScreenBuffer(bool flip_y=false) em::val getPNGByteArray() { constexpr const char * filename = "im.png"; - auto * wnd = GetAppWindow(); + auto * wnd = GetGLWindow(); int w, h; wnd->getGLDrawSize(w, h); diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index dbd48b0c..44040e7f 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -18,6 +18,7 @@ #include "mfem.hpp" #include "sdl.hpp" +#include "egl.hpp" #include "palettes.hpp" #include "visual.hpp" #include "gl2ps.h" @@ -48,7 +49,8 @@ static int glvis_multisample = -1; static float line_w = 1.f; static float line_w_aa = gl3::LINE_WIDTH_AA; -static thread_local SdlWindow * wnd = nullptr; +static thread_local GLWindow *wnd = nullptr; +static thread_local SdlWindow *sdl_wnd = nullptr; static bool wndLegacyGl = false; bool wndUseHiDPI = true; // shared with sdl_main.cpp @@ -63,6 +65,11 @@ void SetGLVisCommand(GLVisCommand *cmd) } SdlWindow * GetAppWindow() +{ + return sdl_wnd; +} + +GLWindow *GetGLWindow() { return wnd; } @@ -91,106 +98,106 @@ int InitVisualization (const char name[], int x, int y, int w, int h) #ifdef GLVIS_DEBUG cout << "OpenGL Visualization" << endl; #endif - if (!wnd) + if (!sdl_wnd) { - wnd = new SdlWindow(); - if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) + wnd = sdl_wnd = new SdlWindow(); + if (!sdl_wnd->createWindow(name, x, y, w, h, wndLegacyGl)) { return 1; } } else { - wnd->clearEvents(); + sdl_wnd->clearEvents(); } #ifdef GLVIS_DEBUG cout << "Window should be up" << endl; #endif InitFont(); - wnd->getRenderer().setLineWidth(line_w); - wnd->getRenderer().setLineWidthMS(line_w_aa); + sdl_wnd->getRenderer().setLineWidth(line_w); + sdl_wnd->getRenderer().setLineWidthMS(line_w_aa); // auxReshapeFunc (MyReshape); // not needed, MyExpose calls it // auxReshapeFunc (NULL); void (*exposeFunc)(void) = MyExpose; - wnd->setOnExpose(exposeFunc); + sdl_wnd->setOnExpose(exposeFunc); - wnd->setOnMouseDown(SDL_BUTTON_LEFT, LeftButtonDown); - wnd->setOnMouseUp(SDL_BUTTON_LEFT, LeftButtonUp); - wnd->setOnMouseMove(SDL_BUTTON_LEFT, LeftButtonLoc); - wnd->setOnMouseDown(SDL_BUTTON_MIDDLE, MiddleButtonDown); - wnd->setOnMouseUp(SDL_BUTTON_MIDDLE, MiddleButtonUp); - wnd->setOnMouseMove(SDL_BUTTON_MIDDLE, MiddleButtonLoc); - wnd->setOnMouseDown(SDL_BUTTON_RIGHT, RightButtonDown); - wnd->setOnMouseUp(SDL_BUTTON_RIGHT, RightButtonUp); - wnd->setOnMouseMove(SDL_BUTTON_RIGHT, RightButtonLoc); + sdl_wnd->setOnMouseDown(SDL_BUTTON_LEFT, LeftButtonDown); + sdl_wnd->setOnMouseUp(SDL_BUTTON_LEFT, LeftButtonUp); + sdl_wnd->setOnMouseMove(SDL_BUTTON_LEFT, LeftButtonLoc); + sdl_wnd->setOnMouseDown(SDL_BUTTON_MIDDLE, MiddleButtonDown); + sdl_wnd->setOnMouseUp(SDL_BUTTON_MIDDLE, MiddleButtonUp); + sdl_wnd->setOnMouseMove(SDL_BUTTON_MIDDLE, MiddleButtonLoc); + sdl_wnd->setOnMouseDown(SDL_BUTTON_RIGHT, RightButtonDown); + sdl_wnd->setOnMouseUp(SDL_BUTTON_RIGHT, RightButtonUp); + sdl_wnd->setOnMouseMove(SDL_BUTTON_RIGHT, RightButtonLoc); - wnd->setTouchPinchCallback(TouchPinch); + sdl_wnd->setTouchPinchCallback(TouchPinch); // auxKeyFunc (AUX_p, KeyCtrlP); // handled in vsdata.cpp - wnd->setOnKeyDown (SDLK_s, KeyS); - wnd->setOnKeyDown ('S', KeyS); - - wnd->setOnKeyDown (SDLK_q, KeyQPressed); - // wnd->setOnKeyDown (SDLK_Q, KeyQPressed); - - wnd->setOnKeyDown (SDLK_LEFT, KeyLeftPressed); - wnd->setOnKeyDown (SDLK_RIGHT, KeyRightPressed); - wnd->setOnKeyDown (SDLK_UP, KeyUpPressed); - wnd->setOnKeyDown (SDLK_DOWN, KeyDownPressed); - - wnd->setOnKeyDown (SDLK_KP_0, Key0Pressed); - wnd->setOnKeyDown (SDLK_KP_1, Key1Pressed); - wnd->setOnKeyDown (SDLK_KP_2, Key2Pressed); - wnd->setOnKeyDown (SDLK_KP_3, Key3Pressed); - wnd->setOnKeyDown (SDLK_KP_4, Key4Pressed); - wnd->setOnKeyDown (SDLK_KP_5, Key5Pressed); - wnd->setOnKeyDown (SDLK_KP_6, Key6Pressed); - wnd->setOnKeyDown (SDLK_KP_7, Key7Pressed); - wnd->setOnKeyDown (SDLK_KP_8, Key8Pressed); - wnd->setOnKeyDown (SDLK_KP_9, Key9Pressed); - - wnd->setOnKeyDown (SDLK_KP_MEMSUBTRACT, KeyMinusPressed); - wnd->setOnKeyDown (SDLK_KP_MEMADD, KeyPlusPressed); - - wnd->setOnKeyDown (SDLK_KP_DECIMAL, KeyDeletePressed); - wnd->setOnKeyDown (SDLK_KP_ENTER, KeyEnterPressed); - - wnd->setOnKeyDown (SDLK_PERIOD, KeyDeletePressed); - wnd->setOnKeyDown (SDLK_RETURN, KeyEnterPressed); - - wnd->setOnKeyDown (SDLK_0, Key0Pressed); - wnd->setOnKeyDown (SDLK_1, Key1Pressed); - wnd->setOnKeyDown (SDLK_2, Key2Pressed); - wnd->setOnKeyDown (SDLK_3, Key3Pressed); - wnd->setOnKeyDown (SDLK_4, Key4Pressed); - wnd->setOnKeyDown (SDLK_5, Key5Pressed); - wnd->setOnKeyDown (SDLK_6, Key6Pressed); - wnd->setOnKeyDown (SDLK_7, Key7Pressed); - wnd->setOnKeyDown (SDLK_8, Key8Pressed); - wnd->setOnKeyDown (SDLK_9, Key9Pressed); - - wnd->setOnKeyDown (SDLK_MINUS, KeyMinusPressed); - wnd->setOnKeyDown (SDLK_PLUS, KeyPlusPressed); - wnd->setOnKeyDown (SDLK_EQUALS, KeyPlusPressed); - - wnd->setOnKeyDown (SDLK_j, KeyJPressed); - // wnd->setOnKeyDown (AUX_J, KeyJPressed); - - wnd->setOnKeyDown (SDLK_KP_MULTIPLY, ZoomIn); - wnd->setOnKeyDown (SDLK_KP_DIVIDE, ZoomOut); - - wnd->setOnKeyDown (SDLK_ASTERISK, ZoomIn); - wnd->setOnKeyDown (SDLK_SLASH, ZoomOut); - - wnd->setOnKeyDown (SDLK_LEFTBRACKET, ScaleDown); - wnd->setOnKeyDown (SDLK_RIGHTBRACKET, ScaleUp); - wnd->setOnKeyDown (SDLK_AT, LookAt); + sdl_wnd->setOnKeyDown (SDLK_s, KeyS); + sdl_wnd->setOnKeyDown ('S', KeyS); + + sdl_wnd->setOnKeyDown (SDLK_q, KeyQPressed); + // sdl_wnd->setOnKeyDown (SDLK_Q, KeyQPressed); + + sdl_wnd->setOnKeyDown (SDLK_LEFT, KeyLeftPressed); + sdl_wnd->setOnKeyDown (SDLK_RIGHT, KeyRightPressed); + sdl_wnd->setOnKeyDown (SDLK_UP, KeyUpPressed); + sdl_wnd->setOnKeyDown (SDLK_DOWN, KeyDownPressed); + + sdl_wnd->setOnKeyDown (SDLK_KP_0, Key0Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_1, Key1Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_2, Key2Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_3, Key3Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_4, Key4Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_5, Key5Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_6, Key6Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_7, Key7Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_8, Key8Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_9, Key9Pressed); + + sdl_wnd->setOnKeyDown (SDLK_KP_MEMSUBTRACT, KeyMinusPressed); + sdl_wnd->setOnKeyDown (SDLK_KP_MEMADD, KeyPlusPressed); + + sdl_wnd->setOnKeyDown (SDLK_KP_DECIMAL, KeyDeletePressed); + sdl_wnd->setOnKeyDown (SDLK_KP_ENTER, KeyEnterPressed); + + sdl_wnd->setOnKeyDown (SDLK_PERIOD, KeyDeletePressed); + sdl_wnd->setOnKeyDown (SDLK_RETURN, KeyEnterPressed); + + sdl_wnd->setOnKeyDown (SDLK_0, Key0Pressed); + sdl_wnd->setOnKeyDown (SDLK_1, Key1Pressed); + sdl_wnd->setOnKeyDown (SDLK_2, Key2Pressed); + sdl_wnd->setOnKeyDown (SDLK_3, Key3Pressed); + sdl_wnd->setOnKeyDown (SDLK_4, Key4Pressed); + sdl_wnd->setOnKeyDown (SDLK_5, Key5Pressed); + sdl_wnd->setOnKeyDown (SDLK_6, Key6Pressed); + sdl_wnd->setOnKeyDown (SDLK_7, Key7Pressed); + sdl_wnd->setOnKeyDown (SDLK_8, Key8Pressed); + sdl_wnd->setOnKeyDown (SDLK_9, Key9Pressed); + + sdl_wnd->setOnKeyDown (SDLK_MINUS, KeyMinusPressed); + sdl_wnd->setOnKeyDown (SDLK_PLUS, KeyPlusPressed); + sdl_wnd->setOnKeyDown (SDLK_EQUALS, KeyPlusPressed); + + sdl_wnd->setOnKeyDown (SDLK_j, KeyJPressed); + // sdl_wnd->setOnKeyDown (AUX_J, KeyJPressed); + + sdl_wnd->setOnKeyDown (SDLK_KP_MULTIPLY, ZoomIn); + sdl_wnd->setOnKeyDown (SDLK_KP_DIVIDE, ZoomOut); + + sdl_wnd->setOnKeyDown (SDLK_ASTERISK, ZoomIn); + sdl_wnd->setOnKeyDown (SDLK_SLASH, ZoomOut); + + sdl_wnd->setOnKeyDown (SDLK_LEFTBRACKET, ScaleDown); + sdl_wnd->setOnKeyDown (SDLK_RIGHTBRACKET, ScaleUp); + sdl_wnd->setOnKeyDown (SDLK_AT, LookAt); #ifndef __EMSCRIPTEN__ - wnd->setOnKeyDown(SDLK_LEFTPAREN, ShrinkWindow); - wnd->setOnKeyDown(SDLK_RIGHTPAREN, EnlargeWindow); + sdl_wnd->setOnKeyDown(SDLK_LEFTPAREN, ShrinkWindow); + sdl_wnd->setOnKeyDown(SDLK_RIGHTPAREN, EnlargeWindow); if (locscene) { @@ -202,6 +209,37 @@ int InitVisualization (const char name[], int x, int y, int w, int h) return 0; } +int InitHeadless(int w, int h) +{ + +#ifdef GLVIS_DEBUG + cout << "OpenGL+EGL Visualization" << endl; +#endif + if (!wnd) + { + EglWindow *egl_wnd; + wnd = egl_wnd = new EglWindow(); + if (!egl_wnd->createWindow(w, h, wndLegacyGl)) + { + return 1; + } + } + + InitFont(); + wnd->getRenderer().setLineWidth(line_w); + wnd->getRenderer().setLineWidthMS(line_w_aa); + +#ifndef __EMSCRIPTEN__ + if (locscene) + { + delete locscene; + } +#endif + locscene = nullptr; + + return 0; +} + void SendKeySequence(const char *seq) { for (const char* key = seq; *key != '\0'; key++) @@ -215,53 +253,53 @@ void SendKeySequence(const char *seq) SendExposeEvent(); break; case 'l': // left arrow - wnd->signalKeyDown(SDLK_LEFT); + sdl_wnd->signalKeyDown(SDLK_LEFT); break; case 'r': // right arrow - wnd->signalKeyDown(SDLK_RIGHT); + sdl_wnd->signalKeyDown(SDLK_RIGHT); break; case 'u': // up arrow - wnd->signalKeyDown(SDLK_UP); + sdl_wnd->signalKeyDown(SDLK_UP); break; case 'd': // down arrow - wnd->signalKeyDown(SDLK_DOWN); + sdl_wnd->signalKeyDown(SDLK_DOWN); break; case '3': // F3 - wnd->signalKeyDown(SDLK_F3); + sdl_wnd->signalKeyDown(SDLK_F3); break; case '5': // F5 - wnd->signalKeyDown(SDLK_F5); + sdl_wnd->signalKeyDown(SDLK_F5); break; case '6': // F6 - wnd->signalKeyDown(SDLK_F6); + sdl_wnd->signalKeyDown(SDLK_F6); break; case '7': // F7 - wnd->signalKeyDown(SDLK_F7); + sdl_wnd->signalKeyDown(SDLK_F7); break; case '1': // F11 or F12 key++; switch (*key) { case '1': // F11 - wnd->signalKeyDown(SDLK_F11); + sdl_wnd->signalKeyDown(SDLK_F11); break; case '2': // F12 - wnd->callKeyDown(SDLK_F12); + sdl_wnd->callKeyDown(SDLK_F12); break; } break; case '.': // Keypad ./Del - wnd->signalKeyDown(SDLK_PERIOD); + sdl_wnd->signalKeyDown(SDLK_PERIOD); break; case 'E': // Keypad Enter - wnd->signalKeyDown(SDLK_RETURN); + sdl_wnd->signalKeyDown(SDLK_RETURN); break; } continue; } else { - wnd->signalKeyDown(*key); + sdl_wnd->signalKeyDown(*key); } } } @@ -278,7 +316,7 @@ void CallKeySequence(const char *seq) { if (*key != '~') { - wnd->callKeyDown(*key); + sdl_wnd->callKeyDown(*key); } else { @@ -286,46 +324,46 @@ void CallKeySequence(const char *seq) switch (*key) { case 'l': // left arrow - wnd->callKeyDown(SDLK_LEFT); + sdl_wnd->callKeyDown(SDLK_LEFT); break; case 'r': // right arrow - wnd->callKeyDown(SDLK_RIGHT); + sdl_wnd->callKeyDown(SDLK_RIGHT); break; case 'u': // up arrow - wnd->callKeyDown(SDLK_UP); + sdl_wnd->callKeyDown(SDLK_UP); break; case 'd': // down arrow - wnd->callKeyDown(SDLK_DOWN); + sdl_wnd->callKeyDown(SDLK_DOWN); break; case '3': // F3 - wnd->callKeyDown(SDLK_F3); + sdl_wnd->callKeyDown(SDLK_F3); break; case '5': // F5 - wnd->callKeyDown(SDLK_F5); + sdl_wnd->callKeyDown(SDLK_F5); break; case '6': // F6 - wnd->callKeyDown(SDLK_F6); + sdl_wnd->callKeyDown(SDLK_F6); break; case '7': // F7 - wnd->callKeyDown(SDLK_F7); + sdl_wnd->callKeyDown(SDLK_F7); break; case '1': // F11 or F12 key++; switch (*key) { case '1': // F11 - wnd->callKeyDown(SDLK_F11); + sdl_wnd->callKeyDown(SDLK_F11); break; case '2': // F12 - wnd->callKeyDown(SDLK_F12); + sdl_wnd->callKeyDown(SDLK_F12); break; } break; case '.': // Keypad ./Del - wnd->callKeyDown(SDLK_PERIOD); + sdl_wnd->callKeyDown(SDLK_PERIOD); break; case 'E': // Keypad Enter - wnd->callKeyDown(SDLK_RETURN); + sdl_wnd->callKeyDown(SDLK_RETURN); break; } } @@ -366,16 +404,22 @@ void RunVisualization() { visualize = 1; #ifndef __EMSCRIPTEN__ - wnd->mainLoop(); + if (sdl_wnd) + { + sdl_wnd->mainLoop(); + } #endif - InitIdleFuncs(); + if (sdl_wnd) + { + InitIdleFuncs(); + } } void EndVisualization() { visualize = 0; delete wnd; - wnd = nullptr; + wnd = sdl_wnd = nullptr; } void SendExposeEvent() @@ -457,7 +501,7 @@ void InitIdleFuncs() LastIdleFunc = 0; if (glvis_command) { - wnd->setOnIdle(MainIdleFunc); + sdl_wnd->setOnIdle(MainIdleFunc); } } @@ -467,7 +511,7 @@ bool CommunicationIdleFunc() if (status < 0) { cout << "GLVisCommand signalled exit" << endl; - wnd->signalQuit(); + sdl_wnd->signalQuit(); } else if (status == 1) { @@ -526,7 +570,7 @@ bool MainIdleFunc() void AddIdleFunc(void (*Func)(void)) { IdleFuncs.Union(Func); - wnd->setOnIdle(MainIdleFunc); + sdl_wnd->setOnIdle(MainIdleFunc); } void RemoveIdleFunc(void (*Func)(void)) @@ -534,7 +578,7 @@ void RemoveIdleFunc(void (*Func)(void)) IdleFuncs.DeleteFirst(Func); if (IdleFuncs.Size() == 0 && glvis_command == nullptr) { - wnd->setOnIdle(NULL); + sdl_wnd->setOnIdle(NULL); } } @@ -1265,7 +1309,7 @@ void KeyCtrlP() void KeyQPressed() { - wnd->signalQuit(); + sdl_wnd->signalQuit(); visualize = 0; } @@ -1551,7 +1595,7 @@ const double window_scale_factor = 1.1; void ShrinkWindow() { int w, h; - wnd->getWindowSize(w, h); + sdl_wnd->getWindowSize(w, h); w = (int)ceil(w / window_scale_factor); h = (int)ceil(h / window_scale_factor); @@ -1563,7 +1607,7 @@ void ShrinkWindow() void EnlargeWindow() { int w, h; - wnd->getWindowSize(w, h); + sdl_wnd->getWindowSize(w, h); w = (int)ceil(w * window_scale_factor); h = (int)ceil(h * window_scale_factor); @@ -1574,18 +1618,18 @@ void EnlargeWindow() void MoveResizeWindow(int x, int y, int w, int h) { - wnd->setWindowSize(w, h); - wnd->setWindowPos(x, y); + sdl_wnd->setWindowSize(w, h); + sdl_wnd->setWindowPos(x, y); } void ResizeWindow(int w, int h) { - wnd->setWindowSize(w, h); + sdl_wnd->setWindowSize(w, h); } void SetWindowTitle(const char *title) { - wnd->setWindowTitle(title); + sdl_wnd->setWindowTitle(title); } int GetUseTexture() diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 4c03b48d..e4cb1403 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -28,6 +28,7 @@ void SetGLVisCommand(GLVisCommand *cmd); /// Initializes the visualization and some keys. int InitVisualization(const char name[], int x, int y, int w, int h); +int InitHeadless(int w, int h); void SetVisualizationScene(VisualizationScene * scene, int view = 3, const char *keys = NULL); @@ -45,7 +46,8 @@ void MyExpose(); void MainLoop(); -SdlWindow * GetAppWindow(); +SdlWindow* GetAppWindow(); +GLWindow* GetGLWindow(); VisualizationScene * GetVisualizationScene(); void SetLegacyGLOnly(bool status); diff --git a/lib/egl.cpp b/lib/egl.cpp new file mode 100644 index 00000000..50994f35 --- /dev/null +++ b/lib/egl.cpp @@ -0,0 +1,210 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#include "egl.hpp" +#include "aux_vis.hpp" +#include +#include +#include + +#ifdef GLVIS_DEBUG +#define PRINT_DEBUG(s) std::cerr << s +#else +#define PRINT_DEBUG(s) {} +#endif + +EglWindow::EglWindow() +{ + // Initialize EGL + disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (disp == EGL_NO_DISPLAY) + { + std::cerr << "FATAL: Failed to get an EGL display: " << eglGetError() << + std::endl; + return; + } + + EGLint major, minor; + + if (!eglInitialize(disp, &major, &minor)) + { + std::cerr << "FATAL: Failed to initialize EGL: " << eglGetError() << std::endl; + return; + } + + std::cout << "Using EGL " << major << "." << minor << std::endl; +} + +EglWindow::~EglWindow() +{ + if (ctx != EGL_NO_CONTEXT) { eglDestroyContext(disp, ctx); } + if (surf != EGL_NO_SURFACE) { eglDestroySurface(disp, surf); } + eglTerminate(disp); +} + +bool EglWindow::createWindow(int w, int h, bool legacyGlOnly) +{ + // 1. Select an appropriate configuration + + const int multisamples = GetMultisample(); + + std::vector configAttribs = + { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_CONFORMANT, EGL_OPENGL_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_SAMPLE_BUFFERS, (multisamples > 0)?(1):(0), + EGL_SAMPLES, multisamples, + EGL_NONE + }; + + EGLint numConfigs; + + if (multisamples > 0) + { + if (!eglChooseConfig(disp, configAttribs.data(), NULL, 0, &numConfigs) || + numConfigs < 1) + { + std::cerr << "EGL with multisampling is not supported, turning it off" << + std::endl; + // turn off multisampling + auto it = std::find(configAttribs.begin(), configAttribs.end(), + EGL_SAMPLE_BUFFERS); + *(++it) = 0; + } + } + + EGLConfig eglCfg; + + if (!eglChooseConfig(disp, configAttribs.data(), &eglCfg, 1, &numConfigs) || + numConfigs < 1) + { + std::cerr << "Cannot find working EGL configuration!" << std::endl; + return false; + } + + // 2. Create a surface + const EGLint pbufferAttribs[] = + { + EGL_WIDTH, w, + EGL_HEIGHT, h, + EGL_NONE + }; + + surf = eglCreatePbufferSurface(disp, eglCfg, pbufferAttribs); + if (surf == EGL_NO_SURFACE) + { + std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << + std::endl; + return false; + } + + // 3. Bind the API + if (!eglBindAPI(EGL_OPENGL_API)) + { + std::cerr << "Cannot bind OpenGL API, error: " << eglGetError() << std::endl; + return false; + } + + // 4. Create a context and make it current + if (legacyGlOnly) + { + // Try and probe for a core/compatibility context. Needed for Mac OS X, + // which will only support OpenGL 2.1 if you don't create a core context. + PRINT_DEBUG("Opening OpenGL core profile context..." << std::flush); + const EGLint attrListCore[] = + { + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_NONE + }; + ctx = eglCreateContext(disp, eglCfg, EGL_NO_CONTEXT, attrListCore); + if (ctx == EGL_NO_CONTEXT) + { + PRINT_DEBUG("failed." << std::endl); + PRINT_DEBUG("Opening OpenGL core profile context..." << std::flush); + const EGLint attrListCompat[] = + { + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, + EGL_NONE + }; + ctx = eglCreateContext(disp, eglCfg, EGL_NO_CONTEXT, attrListCompat); + if (ctx == EGL_NO_CONTEXT) + { + PRINT_DEBUG("failed." << std::endl); + } + else + { + PRINT_DEBUG("success!" << std::endl); + } + } + else + { + PRINT_DEBUG("success!" << std::endl); + } + } + + if (ctx == EGL_NO_CONTEXT) + { + PRINT_DEBUG("Opening OpenGL context with no flags..." << std::flush); + ctx = eglCreateContext(disp, eglCfg, EGL_NO_CONTEXT, NULL); + if (ctx == EGL_NO_CONTEXT) + { + PRINT_DEBUG("failed." << std::endl); + std::cerr << "Cannot create an EGL context, error: " << eglGetError() << + std::endl; + return false; + } + else + { + PRINT_DEBUG("success!" << std::endl); + } + } + + if (!eglMakeCurrent(disp, surf, surf, ctx)) + { + std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() + << std::endl; + return false; + } + +#ifndef __EMSCRIPTEN__ + glEnable(GL_DEBUG_OUTPUT); +#endif + + PRINT_DEBUG("EGL context is ready" << std::endl); + + return initGLEW(legacyGlOnly); +} + +void EglWindow::getGLDrawSize(int& w, int& h) +{ + EGLint egl_w, egl_h; + eglQuerySurface(disp, surf, EGL_WIDTH, &egl_w); + eglQuerySurface(disp, surf, EGL_HEIGHT, &egl_h); + w = egl_w; + h = egl_h; +} + +void EglWindow::signalExpose() +{ + MyExpose(); +} + +void EglWindow::screenshot(std::string filename, bool convert) +{ + Screenshot(filename.c_str(), convert); +} diff --git a/lib/egl.hpp b/lib/egl.hpp new file mode 100644 index 00000000..7a568082 --- /dev/null +++ b/lib/egl.hpp @@ -0,0 +1,50 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_EGL_HPP +#define GLVIS_EGL_HPP + +#include "glwindow.hpp" + +#include + +class EglWindow : public GLWindow +{ + EGLDisplay disp{EGL_NO_DISPLAY}; + EGLSurface surf{EGL_NO_SURFACE}; + EGLContext ctx{EGL_NO_CONTEXT}; + +public: + EglWindow(); + ~EglWindow(); + + bool createWindow(int w, int h, bool legacyGlOnly); + + void getGLDrawSize(int& w, int& h) override; + + //use the default values of SdlWindow, LoadFont() ignores them anyway + void getDpi(int& wdpi, int& hdpi) const override { wdpi = hdpi = 72; } + bool isHighDpi() const override { return true; } + + bool isGlInitialized() const override { return ctx != EGL_NO_CONTEXT; } + + void signalExpose() override; + void signalSwap() override { } + + bool isExposePending() const override { return false; } + //used in Screenshot, as there is no swapping, the single buffer is always + //up to date and can be read directly + bool isSwapPending() const override { return true; } + + void screenshot(std::string filename, bool convert = false) override; +}; + +#endif //GLVIS_EGL_HPP \ No newline at end of file diff --git a/lib/font.cpp b/lib/font.cpp index 74ca1bdb..01895e70 100644 --- a/lib/font.cpp +++ b/lib/font.cpp @@ -44,12 +44,12 @@ bool GlVisFont::LoadFont(const std::string& path, int font_index, int font_size) } face_has_kerning = FT_HAS_KERNING(face); int ppi_w, ppi_h; - GetAppWindow()->getDpi(ppi_w, ppi_h); + GetGLWindow()->getDpi(ppi_w, ppi_h); const bool use_fixed_ppi_h = true; if (use_fixed_ppi_h) { double ratio = double(ppi_w)/ppi_h; - ppi_h = GetAppWindow()->isHighDpi() ? 192 : 96; + ppi_h = GetGLWindow()->isHighDpi() ? 192 : 96; ppi_w = ratio*ppi_h + 0.5; #ifdef GLVIS_DEBUG cout << "Fonts use fixed ppi: " << ppi_w << " x " << ppi_h << endl; diff --git a/lib/glwindow.cpp b/lib/glwindow.cpp new file mode 100644 index 00000000..9316947c --- /dev/null +++ b/lib/glwindow.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#include "glwindow.hpp" +#include "aux_vis.hpp" +#include "gl/renderer_core.hpp" +#include "gl/renderer_ff.hpp" + +#ifdef GLVIS_DEBUG +#define PRINT_DEBUG(s) std::cerr << s +#else +#define PRINT_DEBUG(s) {} +#endif + +bool GLWindow::initGLEW(bool legacyGlOnly) +{ + GLenum err = glewInit(); +#ifdef GLEW_ERROR_NO_GLX_DISPLAY + // NOTE: Hacky workaround for Wayland initialization failure + // See https://github.com/nigels-com/glew/issues/172 + if (err == GLEW_ERROR_NO_GLX_DISPLAY) + { + std::cerr << "GLEW: No GLX display found. If you are using Wayland this can " + << "be ignored." << std::endl; + err = GLEW_OK; + } +#endif + if (err != GLEW_OK) + { + std::cerr << "FATAL: Failed to initialize GLEW: " + << glewGetErrorString(err) << std::endl; + return false; + } + + // print versions + PRINT_DEBUG("Using GLEW " << glewGetString(GLEW_VERSION) << std::endl); + PRINT_DEBUG("Using GL " << glGetString(GL_VERSION) << std::endl); + + renderer.reset(new gl3::MeshRenderer); + renderer->setSamplesMSAA(GetMultisample()); +#ifndef __EMSCRIPTEN__ + if (!GLEW_VERSION_1_1) + { + std::cerr << "FATAL: Minimum of OpenGL 1.1 is required." << std::endl; + return false; + } + if (!GLEW_VERSION_1_3) + { + // Multitexturing was introduced into the core OpenGL specification in + // version 1.3; for versions before, we need to load the functions from + // the ARB_multitexture extension. + if (GLEW_ARB_multitexture) + { + glActiveTexture = glActiveTextureARB; + glClientActiveTexture = glClientActiveTextureARB; + glMultiTexCoord2f = glMultiTexCoord2fARB; + } + else + { + std::cerr << "FATAL: Missing OpenGL multitexture support." << std::endl; + return false; + } + } + if (!GLEW_VERSION_3_0 && GLEW_EXT_transform_feedback) + { + glBindBufferBase = glBindBufferBaseEXT; + // Use an explicit typecast to suppress an error from inconsistent types + // that are present in older versions of GLEW. + glTransformFeedbackVaryings = + (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)glTransformFeedbackVaryingsEXT; + glBeginTransformFeedback = glBeginTransformFeedbackEXT; + glEndTransformFeedback = glEndTransformFeedbackEXT; + } + if (!legacyGlOnly && (GLEW_VERSION_3_0 + || (GLEW_VERSION_2_0 && GLEW_EXT_transform_feedback))) + { + // We require both shaders and transform feedback EXT_transform_feedback + // was made core in OpenGL 3.0 + PRINT_DEBUG("Loading CoreGLDevice..." << std::endl); + renderer->setDevice(); + } + else + { + PRINT_DEBUG("Shader support missing, loading FFGLDevice..." << std::endl); + renderer->setDevice(); + } + +#else + renderer->setDevice(); +#endif + + return true; +} diff --git a/lib/glwindow.hpp b/lib/glwindow.hpp new file mode 100644 index 00000000..fc0a87f4 --- /dev/null +++ b/lib/glwindow.hpp @@ -0,0 +1,58 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_GLWINDOW_HPP +#define GLVIS_GLWINDOW_HPP + +#include "gl/renderer.hpp" +#include + +class GLWindow +{ +protected: + std::unique_ptr renderer; + + bool initGLEW(bool legacyGlOnly); +public: + virtual ~GLWindow() = default; + + /// Returns the renderer object + inline gl3::MeshRenderer& getRenderer() { return *renderer.get(); } + + /// Returns the drawable size + virtual void getGLDrawSize(int& w, int& h) { w = h = 0; } + + /// Returns the resolution (DPI) of the display + virtual void getDpi(int& wdpi, int& hdpi) const { wdpi = hdpi = 0; } + + /// Checks if the display has high resolution (DPI) + virtual bool isHighDpi() const { return false; } + + /// Returns true if the OpenGL context was successfully initialized + virtual bool isGlInitialized() const { return false; } + + /// Signals expose event when objects have been updated + virtual void signalExpose() = 0; + + /// Signals swap event when the back buffer is ready for swapping + virtual void signalSwap() = 0; + + /// Checks if the swap event is pending + virtual bool isSwapPending() const { return false; } + + /// Checks if the expose event is pending + virtual bool isExposePending() const { return false; } + + /// Saves a screenshot ot the file, performing conversion optionally + virtual void screenshot(std::string filename, bool convert = false) { } +}; + +#endif //GLVIS_GLWINDOW_HPP diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index c5981a75..89513a23 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -130,7 +130,7 @@ VisualizationScene::VisualizationScene() cut_updated = false; background = BG_WHITE; - GetAppWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); + GetGLWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); _use_cust_l0_pos = false; light_mat_idx = 3; use_light = true; @@ -1068,12 +1068,12 @@ void VisualizationScene::ToggleBackground() if (background == BG_BLK) { background = BG_WHITE; - GetAppWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); + GetGLWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); } else { background = BG_BLK; - GetAppWindow()->getRenderer().setClearColor(0.f, 0.f, 0.f, 1.f); + GetGLWindow()->getRenderer().setClearColor(0.f, 0.f, 0.f, 1.f); } } diff --git a/lib/openglvis.hpp b/lib/openglvis.hpp index 1455242d..1cc16ac4 100644 --- a/lib/openglvis.hpp +++ b/lib/openglvis.hpp @@ -65,7 +65,8 @@ class VisualizationScene // How to scale the visualized object(s) double xscale, yscale, zscale; - SdlWindow * wnd; + GLWindow *wnd; + SdlWindow *sdl_wnd; glm::mat4 proj_mtx; diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 3131d757..a9ce71a4 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -11,8 +11,6 @@ #include #include "aux_vis.hpp" -#include "gl/renderer_core.hpp" -#include "gl/renderer_ff.hpp" #include "sdl.hpp" #include "sdl_main.hpp" #ifdef __EMSCRIPTEN__ @@ -73,7 +71,7 @@ SdlMainThread& GetMainThread() return inst; } -bool SdlWindow::isGlInitialized() +bool SdlWindow::isGlInitialized() const { return (handle.gl_ctx != 0); } @@ -104,82 +102,7 @@ bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, window_id = SDL_GetWindowID(handle.hwnd); - GLenum err = glewInit(); -#ifdef GLEW_ERROR_NO_GLX_DISPLAY - // NOTE: Hacky workaround for Wayland initialization failure - // See https://github.com/nigels-com/glew/issues/172 - if (err == GLEW_ERROR_NO_GLX_DISPLAY) - { - cerr << "GLEW: No GLX display found. If you are using Wayland this can " - << "be ignored." << endl; - err = GLEW_OK; - } -#endif - if (err != GLEW_OK) - { - cerr << "FATAL: Failed to initialize GLEW: " - << glewGetErrorString(err) << endl; - return false; - } - - // print versions - PRINT_DEBUG("Using GLEW " << glewGetString(GLEW_VERSION) << std::endl); - PRINT_DEBUG("Using GL " << glGetString(GL_VERSION) << std::endl); - - renderer.reset(new gl3::MeshRenderer); - renderer->setSamplesMSAA(GetMultisample()); -#ifndef __EMSCRIPTEN__ - if (!GLEW_VERSION_1_1) - { - cerr << "FATAL: Minimum of OpenGL 1.1 is required." << endl; - return false; - } - if (!GLEW_VERSION_1_3) - { - // Multitexturing was introduced into the core OpenGL specification in - // version 1.3; for versions before, we need to load the functions from - // the ARB_multitexture extension. - if (GLEW_ARB_multitexture) - { - glActiveTexture = glActiveTextureARB; - glClientActiveTexture = glClientActiveTextureARB; - glMultiTexCoord2f = glMultiTexCoord2fARB; - } - else - { - cerr << "FATAL: Missing OpenGL multitexture support." << endl; - return false; - } - } - if (!GLEW_VERSION_3_0 && GLEW_EXT_transform_feedback) - { - glBindBufferBase = glBindBufferBaseEXT; - // Use an explicit typecast to suppress an error from inconsistent types - // that are present in older versions of GLEW. - glTransformFeedbackVaryings = - (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)glTransformFeedbackVaryingsEXT; - glBeginTransformFeedback = glBeginTransformFeedbackEXT; - glEndTransformFeedback = glEndTransformFeedbackEXT; - } - if (!legacyGlOnly && (GLEW_VERSION_3_0 - || (GLEW_VERSION_2_0 && GLEW_EXT_transform_feedback))) - { - // We require both shaders and transform feedback EXT_transform_feedback - // was made core in OpenGL 3.0 - PRINT_DEBUG("Loading CoreGLDevice..." << endl); - renderer->setDevice(); - } - else - { - PRINT_DEBUG("Shader support missing, loading FFGLDevice..." << endl); - renderer->setDevice(); - } - -#else - renderer->setDevice(); -#endif - - return true; + return initGLEW(legacyGlOnly); } SdlWindow::~SdlWindow() @@ -576,7 +499,7 @@ void SdlWindow::getGLDrawSize(int& w, int& h) SDL_GL_GetDrawableSize(handle.hwnd, &w, &h); } -void SdlWindow::getDpi(int& w, int& h) +void SdlWindow::getDpi(int& w, int& h) const { w = default_dpi; h = default_dpi; diff --git a/lib/sdl.hpp b/lib/sdl.hpp index 4a82b773..a9ddf96c 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -19,7 +19,7 @@ #include #include #include -#include "gl/renderer.hpp" +#include "glwindow.hpp" #include "sdl_helper.hpp" struct EventInfo @@ -39,7 +39,7 @@ typedef bool (*IdleDelegate)(); class SdlMainThread; SdlMainThread& GetMainThread(); -class SdlWindow +class SdlWindow : public GLWindow { private: friend class SdlMainThread; @@ -77,7 +77,7 @@ class SdlWindow int window_id = -1; Handle handle; - std::unique_ptr renderer; + static const int high_dpi_threshold = 144; // The display is high-dpi when: // - SDL's "screen coordinates" sizes are different from the pixel sizes, or @@ -220,27 +220,26 @@ class SdlWindow } void getWindowSize(int& w, int& h); - void getGLDrawSize(int& w, int& h); - void getDpi(int& wdpi, int& hdpi); + void getGLDrawSize(int& w, int& h) override; + void getDpi(int& wdpi, int& hdpi) const override; /// This property is set by createWindow(). - bool isHighDpi() const { return high_dpi; } + bool isHighDpi() const override { return high_dpi; } - gl3::MeshRenderer& getRenderer() { return *renderer.get(); } void setWindowTitle(std::string& title); void setWindowTitle(const char* title); void setWindowSize(int w, int h); void setWindowPos(int x, int y); void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE); - void signalExpose() { wnd_state = RenderState::ExposePending; } - void signalSwap() { wnd_state = RenderState::SwapPending; } + void signalExpose() override { wnd_state = RenderState::ExposePending; } + void signalSwap() override { wnd_state = RenderState::SwapPending; } void signalQuit() { running = false; } /// Returns the keyboard events that have been logged by the window. std::string getSavedKeys() const { return saved_keys; } /// Queues a screenshot to be taken. - void screenshot(std::string filename, bool convert = false) + void screenshot(std::string filename, bool convert = false) override { takeScreenshot = true; screenshot_file = filename; @@ -255,10 +254,10 @@ class SdlWindow operator bool() { return handle.isInitialized(); } bool isWindowInitialized() { return handle.isInitialized(); } /// Returns true if the OpenGL context was successfully initialized. - bool isGlInitialized(); + bool isGlInitialized() const override; - bool isSwapPending() { return wnd_state == RenderState::SwapPending; } - bool isExposePending() { return wnd_state == RenderState::ExposePending; } + bool isSwapPending() const override { return wnd_state == RenderState::SwapPending; } + bool isExposePending() const override { return wnd_state == RenderState::ExposePending; } #ifdef __EMSCRIPTEN__ std::string getCanvasId() const { return canvas_id_; } diff --git a/lib/threads.cpp b/lib/threads.cpp index 0d55180e..d38d97eb 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -500,7 +500,7 @@ int GLVisCommand::Execute() cout << "Command: screenshot -> " << screenshot_filename << endl; // Allow SdlWindow to handle the expose and screenshot action, in case // any actions need to be taken before MyExpose(). - GetAppWindow()->screenshot(screenshot_filename, true); + GetGLWindow()->screenshot(screenshot_filename, true); break; } diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp index d80e6fab..a0d2a69f 100644 --- a/lib/vsdata.cpp +++ b/lib/vsdata.cpp @@ -933,8 +933,8 @@ void KeyKPressed() void KeyAPressed() { - bool curr_aa = GetAppWindow()->getRenderer().getAntialiasing(); - GetAppWindow()->getRenderer().setAntialiasing(!curr_aa); + bool curr_aa = GetGLWindow()->getRenderer().getAntialiasing(); + GetGLWindow()->getRenderer().setAntialiasing(!curr_aa); cout << "Multisampling/Antialiasing: " << strings_off_on[!curr_aa ? 1 : 0] << endl; @@ -1170,7 +1170,14 @@ void VisualizationSceneScalarData::Toggle2DView() gl3::SceneInfo VisualizationSceneScalarData::GetSceneObjs() { int w, h; - wnd->getWindowSize(w, h); + if (sdl_wnd) + { + sdl_wnd->getWindowSize(w, h); + } + else + { + wnd->getGLDrawSize(w, h); + } gl3::SceneInfo scene {}; gl3::RenderParams params {}; @@ -1371,7 +1378,8 @@ void VisualizationSceneScalarData::Init() { vsdata = this; window = &win; - wnd = GetAppWindow(); + wnd = GetGLWindow(); + sdl_wnd = GetAppWindow(); arrow_type = arrow_scaling_type = 0; scaling = 0; @@ -1391,53 +1399,54 @@ void VisualizationSceneScalarData::Init() // static int init = 0; // if (!init) + if (sdl_wnd) { // init = 1; - wnd->setOnKeyDown('l', KeylPressed); - wnd->setOnKeyDown('L', KeyLPressed); + sdl_wnd->setOnKeyDown('l', KeylPressed); + sdl_wnd->setOnKeyDown('L', KeyLPressed); - wnd->setOnKeyDown('s', KeySPressed); + sdl_wnd->setOnKeyDown('s', KeySPressed); - // wnd->setOnKeyDown('a', KeyaPressed); - wnd->setOnKeyDown('a', Key_Mod_a_Pressed); - wnd->setOnKeyDown('A', KeyAPressed); + // sdl_wnd->setOnKeyDown('a', KeyaPressed); + sdl_wnd->setOnKeyDown('a', Key_Mod_a_Pressed); + sdl_wnd->setOnKeyDown('A', KeyAPressed); - wnd->setOnKeyDown('r', KeyrPressed); - wnd->setOnKeyDown('R', KeyRPressed); + sdl_wnd->setOnKeyDown('r', KeyrPressed); + sdl_wnd->setOnKeyDown('R', KeyRPressed); - wnd->setOnKeyDown('p', KeypPressed); - wnd->setOnKeyDown('P', KeyPPressed); + sdl_wnd->setOnKeyDown('p', KeypPressed); + sdl_wnd->setOnKeyDown('P', KeyPPressed); - wnd->setOnKeyDown('h', KeyHPressed); - wnd->setOnKeyDown('H', KeyHPressed); + sdl_wnd->setOnKeyDown('h', KeyHPressed); + sdl_wnd->setOnKeyDown('H', KeyHPressed); - wnd->setOnKeyDown(SDLK_F5, KeyF5Pressed); - wnd->setOnKeyDown(SDLK_F6, KeyF6Pressed); - wnd->setOnKeyDown(SDLK_F7, KeyF7Pressed); + sdl_wnd->setOnKeyDown(SDLK_F5, KeyF5Pressed); + sdl_wnd->setOnKeyDown(SDLK_F6, KeyF6Pressed); + sdl_wnd->setOnKeyDown(SDLK_F7, KeyF7Pressed); - wnd->setOnKeyDown(SDLK_BACKSLASH, KeyBackslashPressed); - wnd->setOnKeyDown('t', KeyTPressed); - wnd->setOnKeyDown('T', KeyTPressed); + sdl_wnd->setOnKeyDown(SDLK_BACKSLASH, KeyBackslashPressed); + sdl_wnd->setOnKeyDown('t', KeyTPressed); + sdl_wnd->setOnKeyDown('T', KeyTPressed); - wnd->setOnKeyDown('g', KeygPressed); - wnd->setOnKeyDown('G', KeyGPressed); + sdl_wnd->setOnKeyDown('g', KeygPressed); + sdl_wnd->setOnKeyDown('G', KeyGPressed); - wnd->setOnKeyDown('c', KeycPressed); - wnd->setOnKeyDown('C', KeyCPressed); + sdl_wnd->setOnKeyDown('c', KeycPressed); + sdl_wnd->setOnKeyDown('C', KeyCPressed); - wnd->setOnKeyDown('k', KeykPressed); - wnd->setOnKeyDown('K', KeyKPressed); + sdl_wnd->setOnKeyDown('k', KeykPressed); + sdl_wnd->setOnKeyDown('K', KeyKPressed); - wnd->setOnKeyDown(SDLK_F1, KeyF1Pressed); - wnd->setOnKeyDown(SDLK_F2, KeyF2Pressed); + sdl_wnd->setOnKeyDown(SDLK_F1, KeyF1Pressed); + sdl_wnd->setOnKeyDown(SDLK_F2, KeyF2Pressed); - wnd->setOnKeyDown(SDLK_COMMA, KeyCommaPressed); - wnd->setOnKeyDown(SDLK_LESS, KeyLessPressed); - wnd->setOnKeyDown('~', KeyTildePressed); - wnd->setOnKeyDown('`', KeyGravePressed); + sdl_wnd->setOnKeyDown(SDLK_COMMA, KeyCommaPressed); + sdl_wnd->setOnKeyDown(SDLK_LESS, KeyLessPressed); + sdl_wnd->setOnKeyDown('~', KeyTildePressed); + sdl_wnd->setOnKeyDown('`', KeyGravePressed); - wnd->setOnKeyDown(SDLK_EXCLAIM, KeyToggleTexture); + sdl_wnd->setOnKeyDown(SDLK_EXCLAIM, KeyToggleTexture); } // Set_Light(); @@ -1753,8 +1762,11 @@ void VisualizationSceneScalarData::SetLevelLines ( void VisualizationSceneScalarData::PrintState() { - cout << "\nkeys: " << GetAppWindow()->getSavedKeys() << "\n" - << "\nlight " << strings_off_on[use_light ? 1 : 0] + if (sdl_wnd) + { + cout << "\nkeys: " << sdl_wnd->getSavedKeys() << "\n"; + } + cout << "\nlight " << strings_off_on[use_light ? 1 : 0] << "\nperspective " << strings_off_on[OrthogonalProjection ? 0 : 1] << "\nviewcenter " << ViewCenterX << ' ' << ViewCenterY << "\nzoom " << (OrthogonalProjection ? ViewScale : diff --git a/lib/vssolution.cpp b/lib/vssolution.cpp index 44638f00..6c0d28ec 100644 --- a/lib/vssolution.cpp +++ b/lib/vssolution.cpp @@ -476,42 +476,43 @@ void VisualizationSceneSolution::Init() // static int init = 0; // if (!init) + if (sdl_wnd) { // init = 1; - wnd->setOnKeyDown('b', KeyBPressed); - wnd->setOnKeyDown('B', KeyBPressed); + sdl_wnd->setOnKeyDown('b', KeyBPressed); + sdl_wnd->setOnKeyDown('B', KeyBPressed); - wnd->setOnKeyDown('m', KeyMPressed); - wnd->setOnKeyDown('M', KeyMPressed); + sdl_wnd->setOnKeyDown('m', KeyMPressed); + sdl_wnd->setOnKeyDown('M', KeyMPressed); - wnd->setOnKeyDown('n', KeyNPressed); - wnd->setOnKeyDown('N', KeyNPressed); + sdl_wnd->setOnKeyDown('n', KeyNPressed); + sdl_wnd->setOnKeyDown('N', KeyNPressed); - wnd->setOnKeyDown('o', KeyoPressed); - wnd->setOnKeyDown('O', KeyOPressed); + sdl_wnd->setOnKeyDown('o', KeyoPressed); + sdl_wnd->setOnKeyDown('O', KeyOPressed); - wnd->setOnKeyDown('e', KeyEPressed); - wnd->setOnKeyDown('E', KeyEPressed); + sdl_wnd->setOnKeyDown('e', KeyEPressed); + sdl_wnd->setOnKeyDown('E', KeyEPressed); - wnd->setOnKeyDown('f', KeyFPressed); - wnd->setOnKeyDown('F', KeyFPressed); + sdl_wnd->setOnKeyDown('f', KeyFPressed); + sdl_wnd->setOnKeyDown('F', KeyFPressed); - wnd->setOnKeyDown('i', KeyiPressed); - wnd->setOnKeyDown('I', KeyIPressed); + sdl_wnd->setOnKeyDown('i', KeyiPressed); + sdl_wnd->setOnKeyDown('I', KeyIPressed); - wnd->setOnKeyDown('y', KeyyPressed); - wnd->setOnKeyDown('Y', KeyYPressed); - wnd->setOnKeyDown('z', KeyzPressed); - wnd->setOnKeyDown('Z', KeyZPressed); + sdl_wnd->setOnKeyDown('y', KeyyPressed); + sdl_wnd->setOnKeyDown('Y', KeyYPressed); + sdl_wnd->setOnKeyDown('z', KeyzPressed); + sdl_wnd->setOnKeyDown('Z', KeyZPressed); - wnd->setOnKeyDown(SDLK_F3, KeyF3Pressed); - wnd->setOnKeyDown(SDLK_F4, KeyF4Pressed); - wnd->setOnKeyDown(SDLK_F8, KeyF8Pressed); - wnd->setOnKeyDown(SDLK_F9, KeyF9Pressed); - wnd->setOnKeyDown(SDLK_F10, KeyF10Pressed); - wnd->setOnKeyDown(SDLK_F11, KeyF11Pressed); - wnd->setOnKeyDown(SDLK_F12, KeyF12Pressed); + sdl_wnd->setOnKeyDown(SDLK_F3, KeyF3Pressed); + sdl_wnd->setOnKeyDown(SDLK_F4, KeyF4Pressed); + sdl_wnd->setOnKeyDown(SDLK_F8, KeyF8Pressed); + sdl_wnd->setOnKeyDown(SDLK_F9, KeyF9Pressed); + sdl_wnd->setOnKeyDown(SDLK_F10, KeyF10Pressed); + sdl_wnd->setOnKeyDown(SDLK_F11, KeyF11Pressed); + sdl_wnd->setOnKeyDown(SDLK_F12, KeyF12Pressed); } Prepare(); diff --git a/lib/vssolution3d.cpp b/lib/vssolution3d.cpp index ada41aec..373b1d4a 100644 --- a/lib/vssolution3d.cpp +++ b/lib/vssolution3d.cpp @@ -762,52 +762,53 @@ void VisualizationSceneSolution3d::Init() // static int init = 0; // if (!init) + if (sdl_wnd) { // init = 1; - wnd->setOnKeyDown('m', KeymPressed); - wnd->setOnKeyDown('M', KeyMPressed); + sdl_wnd->setOnKeyDown('m', KeymPressed); + sdl_wnd->setOnKeyDown('M', KeyMPressed); - wnd->setOnKeyDown('e', KeyePressed); - wnd->setOnKeyDown('E', KeyEPressed); + sdl_wnd->setOnKeyDown('e', KeyePressed); + sdl_wnd->setOnKeyDown('E', KeyEPressed); - wnd->setOnKeyDown('f', KeyFPressed); - // wnd->setOnKeyDown('F', KeyFPressed); + sdl_wnd->setOnKeyDown('f', KeyFPressed); + // sdl_wnd->setOnKeyDown('F', KeyFPressed); - wnd->setOnKeyDown('i', KeyiPressed); - wnd->setOnKeyDown('I', KeyIPressed); + sdl_wnd->setOnKeyDown('i', KeyiPressed); + sdl_wnd->setOnKeyDown('I', KeyIPressed); - wnd->setOnKeyDown('o', KeyoPressed); - wnd->setOnKeyDown('O', KeyOPressed); + sdl_wnd->setOnKeyDown('o', KeyoPressed); + sdl_wnd->setOnKeyDown('O', KeyOPressed); - wnd->setOnKeyDown('w', KeywPressed); - wnd->setOnKeyDown('W', KeyWPressed); + sdl_wnd->setOnKeyDown('w', KeywPressed); + sdl_wnd->setOnKeyDown('W', KeyWPressed); - wnd->setOnKeyDown('x', KeyxPressed); - wnd->setOnKeyDown('X', KeyXPressed); + sdl_wnd->setOnKeyDown('x', KeyxPressed); + sdl_wnd->setOnKeyDown('X', KeyXPressed); - wnd->setOnKeyDown('y', KeyyPressed); - wnd->setOnKeyDown('Y', KeyYPressed); + sdl_wnd->setOnKeyDown('y', KeyyPressed); + sdl_wnd->setOnKeyDown('Y', KeyYPressed); - wnd->setOnKeyDown('z', KeyzPressed); - wnd->setOnKeyDown('Z', KeyZPressed); + sdl_wnd->setOnKeyDown('z', KeyzPressed); + sdl_wnd->setOnKeyDown('Z', KeyZPressed); - wnd->setOnKeyDown('u', KeyuPressed); - wnd->setOnKeyDown('U', KeyUPressed); + sdl_wnd->setOnKeyDown('u', KeyuPressed); + sdl_wnd->setOnKeyDown('U', KeyUPressed); - wnd->setOnKeyDown('v', KeyvPressed); - wnd->setOnKeyDown('V', KeyVPressed); + sdl_wnd->setOnKeyDown('v', KeyvPressed); + sdl_wnd->setOnKeyDown('V', KeyVPressed); - wnd->setOnKeyDown(SDLK_F3, KeyF3Pressed); - wnd->setOnKeyDown(SDLK_F4, KeyF4Pressed); - wnd->setOnKeyDown(SDLK_NUMLOCKCLEAR, ToggleMagicKey); + sdl_wnd->setOnKeyDown(SDLK_F3, KeyF3Pressed); + sdl_wnd->setOnKeyDown(SDLK_F4, KeyF4Pressed); + sdl_wnd->setOnKeyDown(SDLK_NUMLOCKCLEAR, ToggleMagicKey); - wnd->setOnKeyDown(SDLK_F8, KeyF8Pressed); - wnd->setOnKeyDown(SDLK_F9, KeyF9Pressed); - wnd->setOnKeyDown(SDLK_F10, KeyF10Pressed); + sdl_wnd->setOnKeyDown(SDLK_F8, KeyF8Pressed); + sdl_wnd->setOnKeyDown(SDLK_F9, KeyF9Pressed); + sdl_wnd->setOnKeyDown(SDLK_F10, KeyF10Pressed); - wnd->setOnKeyDown(SDLK_F11, KeyF11Pressed); - wnd->setOnKeyDown(SDLK_F12, KeyF12Pressed); + sdl_wnd->setOnKeyDown(SDLK_F11, KeyF11Pressed); + sdl_wnd->setOnKeyDown(SDLK_F12, KeyF12Pressed); } Prepare(); PrepareLines(); diff --git a/lib/vsvector.cpp b/lib/vsvector.cpp index 206f47fd..c320aa72 100644 --- a/lib/vsvector.cpp +++ b/lib/vsvector.cpp @@ -496,17 +496,18 @@ void VisualizationSceneVector::Init() // static int init = 0; // if (!init) + if (sdl_wnd) { // init = 1; - wnd->setOnKeyDown('d', KeyDPressed); - wnd->setOnKeyDown('D', KeyDPressed); - wnd->setOnKeyDown('n', KeyNPressed); - wnd->setOnKeyDown('b', KeyBPressed); - wnd->setOnKeyDown('v', KeyvPressed); - wnd->setOnKeyDown('V', KeyVPressed); - wnd->setOnKeyDown('u', KeyuPressed); - wnd->setOnKeyDown('U', KeyUPressed); + sdl_wnd->setOnKeyDown('d', KeyDPressed); + sdl_wnd->setOnKeyDown('D', KeyDPressed); + sdl_wnd->setOnKeyDown('n', KeyNPressed); + sdl_wnd->setOnKeyDown('b', KeyBPressed); + sdl_wnd->setOnKeyDown('v', KeyvPressed); + sdl_wnd->setOnKeyDown('V', KeyVPressed); + sdl_wnd->setOnKeyDown('u', KeyuPressed); + sdl_wnd->setOnKeyDown('U', KeyUPressed); } // Vec2Scalar is VecLength diff --git a/lib/vsvector3d.cpp b/lib/vsvector3d.cpp index c9f99f90..11c41af1 100644 --- a/lib/vsvector3d.cpp +++ b/lib/vsvector3d.cpp @@ -414,31 +414,32 @@ void VisualizationSceneVector3d::Init() // static int init = 0; // if (!init) + if (sdl_wnd) { // init = 1; - wnd->setOnKeyDown('d', KeyDPressed); - wnd->setOnKeyDown('D', KeyDPressed); + sdl_wnd->setOnKeyDown('d', KeyDPressed); + sdl_wnd->setOnKeyDown('D', KeyDPressed); - wnd->setOnKeyDown('n', KeyNPressed); - wnd->setOnKeyDown('N', KeyNPressed); + sdl_wnd->setOnKeyDown('n', KeyNPressed); + sdl_wnd->setOnKeyDown('N', KeyNPressed); - wnd->setOnKeyDown('b', KeyBPressed); - wnd->setOnKeyDown('B', KeyBPressed); + sdl_wnd->setOnKeyDown('b', KeyBPressed); + sdl_wnd->setOnKeyDown('B', KeyBPressed); - wnd->setOnKeyDown('r', KeyrPressed); // adds another function to 'r' and 'R' - wnd->setOnKeyDown('R', KeyRPressed); // the other function is in vsdata.cpp + sdl_wnd->setOnKeyDown('r', KeyrPressed); // adds another function to 'r' and 'R' + sdl_wnd->setOnKeyDown('R', KeyRPressed); // the other function is in vsdata.cpp - wnd->setOnKeyDown('u', KeyuPressed); // Keys u, U are also used in - wnd->setOnKeyDown('U', KeyUPressed); // VisualizationSceneSolution3d + sdl_wnd->setOnKeyDown('u', KeyuPressed); // Keys u, U are also used in + sdl_wnd->setOnKeyDown('U', KeyUPressed); // VisualizationSceneSolution3d - wnd->setOnKeyDown('w', KeywPressed); // Keys w, W are also used in - wnd->setOnKeyDown('W', KeyWPressed); // VisualizationSceneSolution3d + sdl_wnd->setOnKeyDown('w', KeywPressed); // Keys w, W are also used in + sdl_wnd->setOnKeyDown('W', KeyWPressed); // VisualizationSceneSolution3d - wnd->setOnKeyDown('v', KeyvPressed); // Keys v, V are also used in - wnd->setOnKeyDown('V', KeyVPressed); // VisualizationSceneSolution3d + sdl_wnd->setOnKeyDown('v', KeyvPressed); // Keys v, V are also used in + sdl_wnd->setOnKeyDown('V', KeyVPressed); // VisualizationSceneSolution3d - wnd->setOnKeyDown('F', VectorKeyFPressed); + sdl_wnd->setOnKeyDown('F', VectorKeyFPressed); } } diff --git a/lib/window.cpp b/lib/window.cpp index c5573ddc..a00d616a 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -30,7 +30,7 @@ Window &Window::operator=(Window &&w) } // Visualize the data in the global variables mesh, sol/grid_f, etc -bool Window::GLVisInitVis(StreamCollection input_streams) +bool Window::GLVisInitVis(StreamCollection input_streams, bool headless) { DataState::FieldType field_type = data_state.GetType(); @@ -48,15 +48,29 @@ bool Window::GLVisInitVis(StreamCollection input_streams) const char *win_title = (window_title == string_default) ? window_titles[(int)field_type] : window_title; - if (InitVisualization(win_title, window_x, window_y, window_w, window_h)) + if (!headless) { - cerr << "Initializing the visualization failed." << endl; - return false; + if (InitVisualization(win_title, window_x, window_y, window_w, window_h)) + { + cerr << "Initializing the visualization failed." << endl; + return false; + } + } + else + { + if (InitHeadless(window_w, window_h)) + { + cerr << "Initializing the visualization failed." << endl; + return false; + } } if (input_streams.size() > 0) { - GetAppWindow()->setOnKeyDown(SDLK_SPACE, ThreadsPauseFunc); + if (!headless) + { + GetAppWindow()->setOnKeyDown(SDLK_SPACE, ThreadsPauseFunc); + } internal.glvis_command.reset(new GLVisCommand(*this)); SetGLVisCommand(glvis_command.get()); internal.comm_thread.reset(new communication_thread(std::move(input_streams), @@ -65,7 +79,7 @@ bool Window::GLVisInitVis(StreamCollection input_streams) locwin = this; - if (data_state.quad_f) + if (!headless && data_state.quad_f) { GetAppWindow()->setOnKeyDown('Q', SwitchQuadSolution); } diff --git a/lib/window.hpp b/lib/window.hpp index b9f794cd..06cc73f2 100644 --- a/lib/window.hpp +++ b/lib/window.hpp @@ -53,7 +53,7 @@ struct Window Window& operator=(Window &&w); /// Visualize the data in the global variables mesh, sol/grid_f, etc - bool GLVisInitVis(StreamCollection input_streams); + bool GLVisInitVis(StreamCollection input_streams, bool headless = false); void GLVisStartVis(); /// Switch the quadrature function representation and update the visualization diff --git a/makefile b/makefile index 3f53000a..ed50fda9 100644 --- a/makefile +++ b/makefile @@ -223,6 +223,7 @@ EMCC_OPTS += $(if $(GLM_DIR),-I$(GLM_DIR)) GLVIS_FLAGS += $(GL_OPTS) GLVIS_LIBS += $(GL_LIBS) +GLVIS_LIBS += -lEGL # Take screenshots internally with libtiff, libpng, or sdl2? GLVIS_USE_LIBTIFF ?= NO @@ -253,12 +254,13 @@ ALL_SOURCE_FILES = \ lib/gl/renderer.cpp lib/gl/renderer_core.cpp lib/gl/renderer_ff.cpp \ lib/gl/shader.cpp lib/gl/types.cpp lib/aux_js.cpp lib/aux_vis.cpp \ lib/coll_reader.cpp lib/data_state.cpp lib/file_reader.cpp \ - lib/font.cpp lib/gl2ps.c lib/gltf.cpp lib/material.cpp \ - lib/openglvis.cpp lib/palettes_base.cpp lib/palettes.cpp lib/sdl.cpp \ - lib/script_controller.cpp lib/sdl_helper.cpp lib/sdl_main.cpp \ - lib/sdl_windows.cpp lib/sdl_x11.cpp lib/stream_reader.cpp \ - lib/threads.cpp lib/vsdata.cpp lib/vssolution.cpp lib/vssolution3d.cpp \ - lib/vsvector.cpp lib/vsvector3d.cpp lib/window.cpp + lib/egl.cpp lib/font.cpp lib/gl2ps.c lib/gltf.cpp lib/glwindow.cpp \ + lib/material.cpp lib/openglvis.cpp lib/palettes_base.cpp \ + lib/palettes.cpp lib/sdl.cpp lib/script_controller.cpp \ + lib/sdl_helper.cpp lib/sdl_main.cpp lib/sdl_windows.cpp \ + lib/sdl_x11.cpp lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp \ + lib/vssolution.cpp lib/vssolution3d.cpp lib/vsvector.cpp \ + lib/vsvector3d.cpp lib/window.cpp OBJC_SOURCE_FILES = $(if $(NOTMAC),,lib/sdl_mac.mm) DESKTOP_ONLY_SOURCE_FILES = \ lib/gl/renderer_ff.cpp lib/threads.cpp lib/gl2ps.c lib/sdl_x11.cpp @@ -273,9 +275,9 @@ HEADER_FILES = \ lib/gl/attr_traits.hpp lib/gl/platform_gl.hpp lib/gl/renderer.hpp \ lib/gl/shader.hpp lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp \ lib/gl/types.hpp lib/aux_vis.hpp lib/coll_reader.hpp \ - lib/data_state.hpp lib/file_reader.hpp lib/font.hpp \ - lib/geom_utils.hpp lib/gl2ps.h lib/gltf.hpp lib/logo.hpp \ - lib/material.hpp lib/openglvis.hpp lib/palettes_base.hpp \ + lib/data_state.hpp lib/egl.hpp lib/file_reader.hpp lib/font.hpp \ + lib/geom_utils.hpp lib/gl2ps.h lib/gltf.hpp lib/glwindow.hpp \ + lib/logo.hpp lib/material.hpp lib/openglvis.hpp lib/palettes_base.hpp \ lib/palettes.hpp lib/script_controller.hpp lib/sdl_helper.hpp \ lib/sdl.hpp lib/sdl_mac.hpp lib/sdl_main.hpp lib/sdl_windows.hpp \ lib/sdl_x11.hpp lib/stream_reader.hpp lib/threads.hpp lib/visual.hpp \ From 2005b8847dc5c4155379197d3348a14ed1af6fbd Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 12 May 2025 15:24:34 -0700 Subject: [PATCH 03/79] Added headless command to scripts with basic implementation. --- lib/script_controller.cpp | 34 ++++++++++++++++++++++++++++++---- lib/script_controller.hpp | 1 + 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index b54a4412..10711553 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -56,6 +56,7 @@ enum class Command Scale, Translate, PlotCaption, + Headless, //---------- Max }; @@ -117,6 +118,7 @@ ScriptCommands::ScriptCommands() (*this)[Command::Scale] = {"scale", "", "Set the scaling factor."}; (*this)[Command::Translate] = {"translate", " ", "Set the translation coordinates."}; (*this)[Command::PlotCaption] = {"plot_caption", "''", "Set the plot caption."}; + (*this)[Command::Headless] = {"headless", "", "Change the session to headless."}; } int ScriptController::ScriptReadSolution(istream &scr, DataState &state) @@ -814,6 +816,9 @@ bool ScriptController::ExecuteScriptCommand() MyExpose(); } break; + case Command::Headless: + cout << "The session cannot become headless after initialization" << endl; + break; case Command::Max: //dummy break; } @@ -889,6 +894,9 @@ void ScriptController::PlayScript(Window win, istream &scr) scr >> script.win.window_x >> script.win.window_y >> script.win.window_w >> script.win.window_h; break; + case Command::Headless: + script.headless = true; + break; case Command::DataCollCycle: scr >> script.dc_cycle; break; @@ -961,24 +969,42 @@ void ScriptController::PlayScript(Window win, istream &scr) script.script = &scr; script.win.data_state.keys.clear(); + const bool headless = script.headless; + // Make sure the singleton object returned by GetMainThread() is // initialized from the main thread. - GetMainThread(); + if (!headless) + { + GetMainThread(); + } + std::thread worker_thread { [&](ScriptController local_script) { script_ctrl = &local_script; - if (local_script.win.GLVisInitVis({})) + if (local_script.win.GLVisInitVis({}, local_script.headless)) { - GetAppWindow()->setOnKeyDown(SDLK_SPACE, ScriptControl); + if (!local_script.headless) + { + GetAppWindow()->setOnKeyDown(SDLK_SPACE, ScriptControl); + } + else + { + // execute all commands, updating the scene every time + do { MyExpose(); } + while (local_script.ExecuteScriptCommand()); + } local_script.win.GLVisStartVis(); } }, std::move(script) }; - SDLMainLoop(); + if (!headless) + { + SDLMainLoop(); + } worker_thread.join(); } diff --git a/lib/script_controller.hpp b/lib/script_controller.hpp index a16348a4..8411e485 100644 --- a/lib/script_controller.hpp +++ b/lib/script_controller.hpp @@ -24,6 +24,7 @@ class ScriptController { Window win; + bool headless = false; string dc_protocol = string_default; int dc_cycle = 0; From a3e2c63310535abbc18e2781cb7416ffce6ddc3b Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 12 May 2025 16:37:01 -0700 Subject: [PATCH 04/79] Improved compatibility of EglWindow. --- lib/aux_vis.cpp | 241 ++++++++++++++++++++++++----------------------- lib/aux_vis.hpp | 4 +- lib/egl.cpp | 36 ++++++- lib/egl.hpp | 18 +++- lib/glwindow.hpp | 24 ++++- lib/sdl.cpp | 6 +- lib/sdl.hpp | 20 ++-- lib/vsdata.cpp | 9 +- lib/window.cpp | 18 +--- 9 files changed, 210 insertions(+), 166 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 44040e7f..87f166d4 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -92,144 +92,147 @@ void SetUseHiDPI(bool status) void MyExpose(GLsizei w, GLsizei h); void MyExpose(); -int InitVisualization (const char name[], int x, int y, int w, int h) +int InitVisualization (const char name[], int x, int y, int w, int h, + bool headless) { - #ifdef GLVIS_DEBUG - cout << "OpenGL Visualization" << endl; + if (!headless) + { + cout << "OpenGL Visualization" << endl; + } + else + { + cout << "OpenGL+EGL Visualization" << endl; + } #endif - if (!sdl_wnd) + + if (!headless) { - wnd = sdl_wnd = new SdlWindow(); - if (!sdl_wnd->createWindow(name, x, y, w, h, wndLegacyGl)) + if (!sdl_wnd) { - return 1; + delete wnd; + wnd = sdl_wnd = new SdlWindow(); + if (!sdl_wnd->createWindow(name, x, y, w, h, wndLegacyGl)) + { + return 1; + } + } + else + { + sdl_wnd->clearEvents(); } } else { - sdl_wnd->clearEvents(); + if (sdl_wnd) + { + delete sdl_wnd; + wnd = sdl_wnd = nullptr; + } + if (!wnd) + { + wnd = new EglWindow(); + if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) + { + return 1; + } + } } #ifdef GLVIS_DEBUG cout << "Window should be up" << endl; #endif InitFont(); - sdl_wnd->getRenderer().setLineWidth(line_w); - sdl_wnd->getRenderer().setLineWidthMS(line_w_aa); + wnd->getRenderer().setLineWidth(line_w); + wnd->getRenderer().setLineWidthMS(line_w_aa); // auxReshapeFunc (MyReshape); // not needed, MyExpose calls it // auxReshapeFunc (NULL); void (*exposeFunc)(void) = MyExpose; - sdl_wnd->setOnExpose(exposeFunc); - - sdl_wnd->setOnMouseDown(SDL_BUTTON_LEFT, LeftButtonDown); - sdl_wnd->setOnMouseUp(SDL_BUTTON_LEFT, LeftButtonUp); - sdl_wnd->setOnMouseMove(SDL_BUTTON_LEFT, LeftButtonLoc); - sdl_wnd->setOnMouseDown(SDL_BUTTON_MIDDLE, MiddleButtonDown); - sdl_wnd->setOnMouseUp(SDL_BUTTON_MIDDLE, MiddleButtonUp); - sdl_wnd->setOnMouseMove(SDL_BUTTON_MIDDLE, MiddleButtonLoc); - sdl_wnd->setOnMouseDown(SDL_BUTTON_RIGHT, RightButtonDown); - sdl_wnd->setOnMouseUp(SDL_BUTTON_RIGHT, RightButtonUp); - sdl_wnd->setOnMouseMove(SDL_BUTTON_RIGHT, RightButtonLoc); - - sdl_wnd->setTouchPinchCallback(TouchPinch); - - // auxKeyFunc (AUX_p, KeyCtrlP); // handled in vsdata.cpp - sdl_wnd->setOnKeyDown (SDLK_s, KeyS); - sdl_wnd->setOnKeyDown ('S', KeyS); - - sdl_wnd->setOnKeyDown (SDLK_q, KeyQPressed); - // sdl_wnd->setOnKeyDown (SDLK_Q, KeyQPressed); - - sdl_wnd->setOnKeyDown (SDLK_LEFT, KeyLeftPressed); - sdl_wnd->setOnKeyDown (SDLK_RIGHT, KeyRightPressed); - sdl_wnd->setOnKeyDown (SDLK_UP, KeyUpPressed); - sdl_wnd->setOnKeyDown (SDLK_DOWN, KeyDownPressed); - - sdl_wnd->setOnKeyDown (SDLK_KP_0, Key0Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_1, Key1Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_2, Key2Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_3, Key3Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_4, Key4Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_5, Key5Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_6, Key6Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_7, Key7Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_8, Key8Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_9, Key9Pressed); - - sdl_wnd->setOnKeyDown (SDLK_KP_MEMSUBTRACT, KeyMinusPressed); - sdl_wnd->setOnKeyDown (SDLK_KP_MEMADD, KeyPlusPressed); - - sdl_wnd->setOnKeyDown (SDLK_KP_DECIMAL, KeyDeletePressed); - sdl_wnd->setOnKeyDown (SDLK_KP_ENTER, KeyEnterPressed); - - sdl_wnd->setOnKeyDown (SDLK_PERIOD, KeyDeletePressed); - sdl_wnd->setOnKeyDown (SDLK_RETURN, KeyEnterPressed); - - sdl_wnd->setOnKeyDown (SDLK_0, Key0Pressed); - sdl_wnd->setOnKeyDown (SDLK_1, Key1Pressed); - sdl_wnd->setOnKeyDown (SDLK_2, Key2Pressed); - sdl_wnd->setOnKeyDown (SDLK_3, Key3Pressed); - sdl_wnd->setOnKeyDown (SDLK_4, Key4Pressed); - sdl_wnd->setOnKeyDown (SDLK_5, Key5Pressed); - sdl_wnd->setOnKeyDown (SDLK_6, Key6Pressed); - sdl_wnd->setOnKeyDown (SDLK_7, Key7Pressed); - sdl_wnd->setOnKeyDown (SDLK_8, Key8Pressed); - sdl_wnd->setOnKeyDown (SDLK_9, Key9Pressed); - - sdl_wnd->setOnKeyDown (SDLK_MINUS, KeyMinusPressed); - sdl_wnd->setOnKeyDown (SDLK_PLUS, KeyPlusPressed); - sdl_wnd->setOnKeyDown (SDLK_EQUALS, KeyPlusPressed); - - sdl_wnd->setOnKeyDown (SDLK_j, KeyJPressed); - // sdl_wnd->setOnKeyDown (AUX_J, KeyJPressed); - - sdl_wnd->setOnKeyDown (SDLK_KP_MULTIPLY, ZoomIn); - sdl_wnd->setOnKeyDown (SDLK_KP_DIVIDE, ZoomOut); - - sdl_wnd->setOnKeyDown (SDLK_ASTERISK, ZoomIn); - sdl_wnd->setOnKeyDown (SDLK_SLASH, ZoomOut); - - sdl_wnd->setOnKeyDown (SDLK_LEFTBRACKET, ScaleDown); - sdl_wnd->setOnKeyDown (SDLK_RIGHTBRACKET, ScaleUp); - sdl_wnd->setOnKeyDown (SDLK_AT, LookAt); - -#ifndef __EMSCRIPTEN__ - sdl_wnd->setOnKeyDown(SDLK_LEFTPAREN, ShrinkWindow); - sdl_wnd->setOnKeyDown(SDLK_RIGHTPAREN, EnlargeWindow); - - if (locscene) + if (sdl_wnd) { - delete locscene; + sdl_wnd->setOnExpose(exposeFunc); + + sdl_wnd->setOnMouseDown(SDL_BUTTON_LEFT, LeftButtonDown); + sdl_wnd->setOnMouseUp(SDL_BUTTON_LEFT, LeftButtonUp); + sdl_wnd->setOnMouseMove(SDL_BUTTON_LEFT, LeftButtonLoc); + sdl_wnd->setOnMouseDown(SDL_BUTTON_MIDDLE, MiddleButtonDown); + sdl_wnd->setOnMouseUp(SDL_BUTTON_MIDDLE, MiddleButtonUp); + sdl_wnd->setOnMouseMove(SDL_BUTTON_MIDDLE, MiddleButtonLoc); + sdl_wnd->setOnMouseDown(SDL_BUTTON_RIGHT, RightButtonDown); + sdl_wnd->setOnMouseUp(SDL_BUTTON_RIGHT, RightButtonUp); + sdl_wnd->setOnMouseMove(SDL_BUTTON_RIGHT, RightButtonLoc); + + sdl_wnd->setTouchPinchCallback(TouchPinch); + + // auxKeyFunc (AUX_p, KeyCtrlP); // handled in vsdata.cpp + sdl_wnd->setOnKeyDown (SDLK_s, KeyS); + sdl_wnd->setOnKeyDown ('S', KeyS); + + sdl_wnd->setOnKeyDown (SDLK_q, KeyQPressed); + // sdl_wnd->setOnKeyDown (SDLK_Q, KeyQPressed); + + sdl_wnd->setOnKeyDown (SDLK_LEFT, KeyLeftPressed); + sdl_wnd->setOnKeyDown (SDLK_RIGHT, KeyRightPressed); + sdl_wnd->setOnKeyDown (SDLK_UP, KeyUpPressed); + sdl_wnd->setOnKeyDown (SDLK_DOWN, KeyDownPressed); + + sdl_wnd->setOnKeyDown (SDLK_KP_0, Key0Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_1, Key1Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_2, Key2Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_3, Key3Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_4, Key4Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_5, Key5Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_6, Key6Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_7, Key7Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_8, Key8Pressed); + sdl_wnd->setOnKeyDown (SDLK_KP_9, Key9Pressed); + + sdl_wnd->setOnKeyDown (SDLK_KP_MEMSUBTRACT, KeyMinusPressed); + sdl_wnd->setOnKeyDown (SDLK_KP_MEMADD, KeyPlusPressed); + + sdl_wnd->setOnKeyDown (SDLK_KP_DECIMAL, KeyDeletePressed); + sdl_wnd->setOnKeyDown (SDLK_KP_ENTER, KeyEnterPressed); + + sdl_wnd->setOnKeyDown (SDLK_PERIOD, KeyDeletePressed); + sdl_wnd->setOnKeyDown (SDLK_RETURN, KeyEnterPressed); + + sdl_wnd->setOnKeyDown (SDLK_0, Key0Pressed); + sdl_wnd->setOnKeyDown (SDLK_1, Key1Pressed); + sdl_wnd->setOnKeyDown (SDLK_2, Key2Pressed); + sdl_wnd->setOnKeyDown (SDLK_3, Key3Pressed); + sdl_wnd->setOnKeyDown (SDLK_4, Key4Pressed); + sdl_wnd->setOnKeyDown (SDLK_5, Key5Pressed); + sdl_wnd->setOnKeyDown (SDLK_6, Key6Pressed); + sdl_wnd->setOnKeyDown (SDLK_7, Key7Pressed); + sdl_wnd->setOnKeyDown (SDLK_8, Key8Pressed); + sdl_wnd->setOnKeyDown (SDLK_9, Key9Pressed); + + sdl_wnd->setOnKeyDown (SDLK_MINUS, KeyMinusPressed); + sdl_wnd->setOnKeyDown (SDLK_PLUS, KeyPlusPressed); + sdl_wnd->setOnKeyDown (SDLK_EQUALS, KeyPlusPressed); + + sdl_wnd->setOnKeyDown (SDLK_j, KeyJPressed); + // sdl_wnd->setOnKeyDown (AUX_J, KeyJPressed); + + sdl_wnd->setOnKeyDown (SDLK_KP_MULTIPLY, ZoomIn); + sdl_wnd->setOnKeyDown (SDLK_KP_DIVIDE, ZoomOut); + + sdl_wnd->setOnKeyDown (SDLK_ASTERISK, ZoomIn); + sdl_wnd->setOnKeyDown (SDLK_SLASH, ZoomOut); + + sdl_wnd->setOnKeyDown (SDLK_LEFTBRACKET, ScaleDown); + sdl_wnd->setOnKeyDown (SDLK_RIGHTBRACKET, ScaleUp); + sdl_wnd->setOnKeyDown (SDLK_AT, LookAt); } -#endif - locscene = nullptr; - return 0; -} - -int InitHeadless(int w, int h) -{ - -#ifdef GLVIS_DEBUG - cout << "OpenGL+EGL Visualization" << endl; -#endif - if (!wnd) +#ifndef __EMSCRIPTEN__ + if (sdl_wnd) { - EglWindow *egl_wnd; - wnd = egl_wnd = new EglWindow(); - if (!egl_wnd->createWindow(w, h, wndLegacyGl)) - { - return 1; - } + sdl_wnd->setOnKeyDown(SDLK_LEFTPAREN, ShrinkWindow); + sdl_wnd->setOnKeyDown(SDLK_RIGHTPAREN, EnlargeWindow); } - InitFont(); - wnd->getRenderer().setLineWidth(line_w); - wnd->getRenderer().setLineWidthMS(line_w_aa); - -#ifndef __EMSCRIPTEN__ if (locscene) { delete locscene; @@ -1595,7 +1598,7 @@ const double window_scale_factor = 1.1; void ShrinkWindow() { int w, h; - sdl_wnd->getWindowSize(w, h); + wnd->getWindowSize(w, h); w = (int)ceil(w / window_scale_factor); h = (int)ceil(h / window_scale_factor); @@ -1607,7 +1610,7 @@ void ShrinkWindow() void EnlargeWindow() { int w, h; - sdl_wnd->getWindowSize(w, h); + wnd->getWindowSize(w, h); w = (int)ceil(w * window_scale_factor); h = (int)ceil(h * window_scale_factor); @@ -1618,18 +1621,18 @@ void EnlargeWindow() void MoveResizeWindow(int x, int y, int w, int h) { - sdl_wnd->setWindowSize(w, h); - sdl_wnd->setWindowPos(x, y); + wnd->setWindowSize(w, h); + wnd->setWindowPos(x, y); } void ResizeWindow(int w, int h) { - sdl_wnd->setWindowSize(w, h); + wnd->setWindowSize(w, h); } void SetWindowTitle(const char *title) { - sdl_wnd->setWindowTitle(title); + wnd->setWindowTitle(title); } int GetUseTexture() diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index e4cb1403..f9aedade 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -27,8 +27,8 @@ class GLVisCommand; void SetGLVisCommand(GLVisCommand *cmd); /// Initializes the visualization and some keys. -int InitVisualization(const char name[], int x, int y, int w, int h); -int InitHeadless(int w, int h); +int InitVisualization(const char name[], int x, int y, int w, int h, + bool headless = false); void SetVisualizationScene(VisualizationScene * scene, int view = 3, const char *keys = NULL); diff --git a/lib/egl.cpp b/lib/egl.cpp index 50994f35..ea9744cb 100644 --- a/lib/egl.cpp +++ b/lib/egl.cpp @@ -50,7 +50,8 @@ EglWindow::~EglWindow() eglTerminate(disp); } -bool EglWindow::createWindow(int w, int h, bool legacyGlOnly) +bool EglWindow::createWindow(const char *, int, int, int w, int h, + bool legacyGlOnly) { // 1. Select an appropriate configuration @@ -88,8 +89,6 @@ bool EglWindow::createWindow(int w, int h, bool legacyGlOnly) } } - EGLConfig eglCfg; - if (!eglChooseConfig(disp, configAttribs.data(), &eglCfg, 1, &numConfigs) || numConfigs < 1) { @@ -190,7 +189,7 @@ bool EglWindow::createWindow(int w, int h, bool legacyGlOnly) return initGLEW(legacyGlOnly); } -void EglWindow::getGLDrawSize(int& w, int& h) +void EglWindow::getGLDrawSize(int& w, int& h) const { EGLint egl_w, egl_h; eglQuerySurface(disp, surf, EGL_WIDTH, &egl_w); @@ -199,6 +198,35 @@ void EglWindow::getGLDrawSize(int& w, int& h) h = egl_h; } +void EglWindow::setWindowSize(int w, int h) +{ + const EGLint pbufferAttribs[] = + { + EGL_WIDTH, w, + EGL_HEIGHT, h, + EGL_NONE + }; + + EGLSurface surf_new = eglCreatePbufferSurface(disp, eglCfg, pbufferAttribs); + if (surf_new == EGL_NO_SURFACE) + { + std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << + std::endl; + return; + } + + if (!eglMakeCurrent(disp, surf_new, surf_new, ctx)) + { + std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() + << std::endl; + return; + } + + eglDestroySurface(disp, surf); + + surf = surf_new; +} + void EglWindow::signalExpose() { MyExpose(); diff --git a/lib/egl.hpp b/lib/egl.hpp index 7a568082..bb81eb97 100644 --- a/lib/egl.hpp +++ b/lib/egl.hpp @@ -21,27 +21,35 @@ class EglWindow : public GLWindow EGLDisplay disp{EGL_NO_DISPLAY}; EGLSurface surf{EGL_NO_SURFACE}; EGLContext ctx{EGL_NO_CONTEXT}; + EGLConfig eglCfg{}; public: EglWindow(); ~EglWindow(); - bool createWindow(int w, int h, bool legacyGlOnly); + /** @brief Creates a new OpenGL window. Returns false if EGL or OpenGL + initialization fails. */ + bool createWindow(const char *title, int x, int y, int w, int h, + bool legacyGlOnly) override; - void getGLDrawSize(int& w, int& h) override; + void getWindowSize(int& w, int& h) const override { getGLDrawSize(w, h); } + void getGLDrawSize(int& w, int& h) const override; - //use the default values of SdlWindow, LoadFont() ignores them anyway + // use the default values of SdlWindow, LoadFont() ignores them anyway void getDpi(int& wdpi, int& hdpi) const override { wdpi = hdpi = 72; } bool isHighDpi() const override { return true; } + void setWindowSize(int w, int h) override; + + bool isWindowInitialized() const override { return surf != EGL_NO_SURFACE; } bool isGlInitialized() const override { return ctx != EGL_NO_CONTEXT; } void signalExpose() override; void signalSwap() override { } bool isExposePending() const override { return false; } - //used in Screenshot, as there is no swapping, the single buffer is always - //up to date and can be read directly + // used in Screenshot, as there is no swapping, the single buffer is always + // up to date and can be read directly bool isSwapPending() const override { return true; } void screenshot(std::string filename, bool convert = false) override; diff --git a/lib/glwindow.hpp b/lib/glwindow.hpp index fc0a87f4..6beef227 100644 --- a/lib/glwindow.hpp +++ b/lib/glwindow.hpp @@ -24,11 +24,18 @@ class GLWindow public: virtual ~GLWindow() = default; + /// Creates a new OpenGL window + virtual bool createWindow(const char *title, int x, int y, int w, int h, + bool legacyGlOnly) = 0; + /// Returns the renderer object inline gl3::MeshRenderer& getRenderer() { return *renderer.get(); } + /// Returns size of the window + virtual void getWindowSize(int& w, int& h) const { w = h = 0; } + /// Returns the drawable size - virtual void getGLDrawSize(int& w, int& h) { w = h = 0; } + virtual void getGLDrawSize(int& w, int& h) const { w = h = 0; } /// Returns the resolution (DPI) of the display virtual void getDpi(int& wdpi, int& hdpi) const { wdpi = hdpi = 0; } @@ -36,6 +43,21 @@ class GLWindow /// Checks if the display has high resolution (DPI) virtual bool isHighDpi() const { return false; } + /// Set title of the window (string version) + virtual void setWindowTitle(const std::string& title) { } + + /// Set title of the window (C-string version) + virtual void setWindowTitle(const char* title) { } + + /// Set window size + virtual void setWindowSize(int w, int h) { } + + /// Set window position + virtual void setWindowPos(int x, int y) { } + + /// Returns true if the window has been succesfully initialized + virtual bool isWindowInitialized() const { return false; } + /// Returns true if the OpenGL context was successfully initialized virtual bool isGlInitialized() const { return false; } diff --git a/lib/sdl.cpp b/lib/sdl.cpp index a9ce71a4..47f79291 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -465,7 +465,7 @@ void SdlWindow::signalLoop() events_available.notify_all(); } -void SdlWindow::getWindowSize(int& w, int& h) +void SdlWindow::getWindowSize(int& w, int& h) const { w = 0; h = 0; @@ -494,7 +494,7 @@ void SdlWindow::getWindowSize(int& w, int& h) } } -void SdlWindow::getGLDrawSize(int& w, int& h) +void SdlWindow::getGLDrawSize(int& w, int& h) const { SDL_GL_GetDrawableSize(handle.hwnd, &w, &h); } @@ -532,7 +532,7 @@ void SdlWindow::getDpi(int& w, int& h) const } } -void SdlWindow::setWindowTitle(std::string& title) +void SdlWindow::setWindowTitle(const std::string& title) { setWindowTitle(title.c_str()); } diff --git a/lib/sdl.hpp b/lib/sdl.hpp index a9ddf96c..7067f2e5 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -171,10 +171,10 @@ class SdlWindow : public GLWindow // thread only. static void StartSDL(bool server_mode); - /// Creates a new OpenGL window. Returns false if SDL or OpenGL initialization - /// fails. + /** @brief Creates a new OpenGL window. Returns false if SDL or OpenGL + initialization fails. */ bool createWindow(const char * title, int x, int y, int w, int h, - bool legacyGlOnly); + bool legacyGlOnly) override; /// Runs the window loop. void mainLoop(); @@ -219,16 +219,16 @@ class SdlWindow : public GLWindow } } - void getWindowSize(int& w, int& h); - void getGLDrawSize(int& w, int& h) override; + void getWindowSize(int& w, int& h) const override; + void getGLDrawSize(int& w, int& h) const override; void getDpi(int& wdpi, int& hdpi) const override; /// This property is set by createWindow(). bool isHighDpi() const override { return high_dpi; } - void setWindowTitle(std::string& title); - void setWindowTitle(const char* title); - void setWindowSize(int w, int h); - void setWindowPos(int x, int y); + void setWindowTitle(const std::string& title) override; + void setWindowTitle(const char* title) override; + void setWindowSize(int w, int h) override; + void setWindowPos(int x, int y) override; void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE); void signalExpose() override { wnd_state = RenderState::ExposePending; } @@ -252,7 +252,7 @@ class SdlWindow : public GLWindow void swapBuffer(); operator bool() { return handle.isInitialized(); } - bool isWindowInitialized() { return handle.isInitialized(); } + bool isWindowInitialized() const override { return handle.isInitialized(); } /// Returns true if the OpenGL context was successfully initialized. bool isGlInitialized() const override; diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp index a0d2a69f..456555a2 100644 --- a/lib/vsdata.cpp +++ b/lib/vsdata.cpp @@ -1170,14 +1170,7 @@ void VisualizationSceneScalarData::Toggle2DView() gl3::SceneInfo VisualizationSceneScalarData::GetSceneObjs() { int w, h; - if (sdl_wnd) - { - sdl_wnd->getWindowSize(w, h); - } - else - { - wnd->getGLDrawSize(w, h); - } + wnd->getWindowSize(w, h); gl3::SceneInfo scene {}; gl3::RenderParams params {}; diff --git a/lib/window.cpp b/lib/window.cpp index a00d616a..31ffd174 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -48,21 +48,11 @@ bool Window::GLVisInitVis(StreamCollection input_streams, bool headless) const char *win_title = (window_title == string_default) ? window_titles[(int)field_type] : window_title; - if (!headless) + if (InitVisualization(win_title, window_x, window_y, window_w, window_h, + headless)) { - if (InitVisualization(win_title, window_x, window_y, window_w, window_h)) - { - cerr << "Initializing the visualization failed." << endl; - return false; - } - } - else - { - if (InitHeadless(window_w, window_h)) - { - cerr << "Initializing the visualization failed." << endl; - return false; - } + cerr << "Initializing the visualization failed." << endl; + return false; } if (input_streams.size() > 0) From 8ca321bc42834670583a7fd88c99ff4355eae40a Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 12 May 2025 19:52:20 -0700 Subject: [PATCH 05/79] Implemented the main event loop in EglWindow. --- lib/aux_vis.cpp | 28 ++++++----- lib/egl.cpp | 98 +++++++++++++++++++++++++++++++++++++-- lib/egl.hpp | 47 ++++++++++++++++++- lib/glwindow.hpp | 49 +++++++++++++++++--- lib/script_controller.cpp | 9 ++-- lib/sdl.hpp | 37 +++------------ lib/threads.cpp | 2 +- lib/threads.hpp | 2 +- 8 files changed, 211 insertions(+), 61 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 87f166d4..a05de1ca 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -137,6 +137,10 @@ int InitVisualization (const char name[], int x, int y, int w, int h, return 1; } } + else + { + wnd->clearEvents(); + } } #ifdef GLVIS_DEBUG @@ -149,10 +153,10 @@ int InitVisualization (const char name[], int x, int y, int w, int h, // auxReshapeFunc (MyReshape); // not needed, MyExpose calls it // auxReshapeFunc (NULL); void (*exposeFunc)(void) = MyExpose; + wnd->setOnExpose(exposeFunc); + if (sdl_wnd) { - sdl_wnd->setOnExpose(exposeFunc); - sdl_wnd->setOnMouseDown(SDL_BUTTON_LEFT, LeftButtonDown); sdl_wnd->setOnMouseUp(SDL_BUTTON_LEFT, LeftButtonUp); sdl_wnd->setOnMouseMove(SDL_BUTTON_LEFT, LeftButtonLoc); @@ -407,15 +411,9 @@ void RunVisualization() { visualize = 1; #ifndef __EMSCRIPTEN__ - if (sdl_wnd) - { - sdl_wnd->mainLoop(); - } + wnd->mainLoop(); #endif - if (sdl_wnd) - { - InitIdleFuncs(); - } + InitIdleFuncs(); } void EndVisualization() @@ -504,7 +502,7 @@ void InitIdleFuncs() LastIdleFunc = 0; if (glvis_command) { - sdl_wnd->setOnIdle(MainIdleFunc); + wnd->setOnIdle(MainIdleFunc); } } @@ -514,7 +512,7 @@ bool CommunicationIdleFunc() if (status < 0) { cout << "GLVisCommand signalled exit" << endl; - sdl_wnd->signalQuit(); + wnd->signalQuit(); } else if (status == 1) { @@ -573,7 +571,7 @@ bool MainIdleFunc() void AddIdleFunc(void (*Func)(void)) { IdleFuncs.Union(Func); - sdl_wnd->setOnIdle(MainIdleFunc); + wnd->setOnIdle(MainIdleFunc); } void RemoveIdleFunc(void (*Func)(void)) @@ -581,7 +579,7 @@ void RemoveIdleFunc(void (*Func)(void)) IdleFuncs.DeleteFirst(Func); if (IdleFuncs.Size() == 0 && glvis_command == nullptr) { - sdl_wnd->setOnIdle(NULL); + wnd->setOnIdle(NULL); } } @@ -1312,7 +1310,7 @@ void KeyCtrlP() void KeyQPressed() { - sdl_wnd->signalQuit(); + wnd->signalQuit(); visualize = 0; } diff --git a/lib/egl.cpp b/lib/egl.cpp index ea9744cb..0663cb88 100644 --- a/lib/egl.cpp +++ b/lib/egl.cpp @@ -189,6 +189,96 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, return initGLEW(legacyGlOnly); } +void EglWindow::queueEvents(std::vector events) +{ + { + std::lock_guard evt_guard{event_mutex}; + waiting_events.insert(waiting_events.end(), events.begin(), events.end()); + } + if (is_multithreaded) + { + events_available.notify_all(); + } +} + +void EglWindow::mainLoop() +{ + running = true; + while (running) + { + mainIter(); + } +} + +void EglWindow::mainIter() +{ + bool sleep = false; + bool events_pending = false; + { + lock_guard evt_guard{event_mutex}; + events_pending = !waiting_events.empty(); + } + if (events_pending) + { + do + { + Event e; + // Fetch next event from the queue + { + lock_guard evt_guard{event_mutex}; + e = waiting_events.front(); + waiting_events.pop_front(); + events_pending = !waiting_events.empty(); + } + + switch (e.type) + { + case EventType::Screenshot: + Screenshot(screenshot_filename.c_str(), e.event.screenshot.convert); + break; + case EventType::Quit: + running = false; + break; + } + } + while (events_pending); + } + else if (onIdle) + { + sleep = onIdle(); + } + else + { + // No actions performed this iteration. + sleep = true; + } + + if (wnd_state == RenderState::ExposePending) + { + onExpose(); + wnd_state = RenderState::Updated; + } + else if (sleep) + { + unique_lock event_lock{event_mutex}; + events_available.wait(event_lock, [this]() + { + // Sleep until events from WM or glvis_command can be handled + return !waiting_events.empty() || call_idle_func; + }); + } +} + +void EglWindow::signalLoop() +{ + // Note: not executed from the main thread + { + lock_guard evt_guard{event_mutex}; + call_idle_func = true; + } + events_available.notify_all(); +} + void EglWindow::getGLDrawSize(int& w, int& h) const { EGLint egl_w, egl_h; @@ -227,12 +317,14 @@ void EglWindow::setWindowSize(int w, int h) surf = surf_new; } -void EglWindow::signalExpose() +void EglWindow::signalQuit() { - MyExpose(); + queueEvents({{EventType::Quit}}); } void EglWindow::screenshot(std::string filename, bool convert) { - Screenshot(filename.c_str(), convert); + screenshot_filename = filename; + queueEvents({{EventType::Screenshot, Event::Events::Screenshot{convert}}}); + signalExpose(); } diff --git a/lib/egl.hpp b/lib/egl.hpp index bb81eb97..c3a78fef 100644 --- a/lib/egl.hpp +++ b/lib/egl.hpp @@ -16,6 +16,10 @@ #include +#include +#include +#include + class EglWindow : public GLWindow { EGLDisplay disp{EGL_NO_DISPLAY}; @@ -23,6 +27,38 @@ class EglWindow : public GLWindow EGLContext ctx{EGL_NO_CONTEXT}; EGLConfig eglCfg{}; + bool running{false}; + + bool is_multithreaded{true}; + bool call_idle_func{false}; + + enum class EventType + { + Screenshot, + Quit, + }; + struct Event + { + EventType type; + union Events + { + struct Screenshot + { + bool convert; + } screenshot; + + struct Quit { } quit; + } event; + }; + + std::string screenshot_filename; + + std::condition_variable events_available; + std::mutex event_mutex; + std::deque waiting_events; + + void queueEvents(std::vector events); + public: EglWindow(); ~EglWindow(); @@ -32,6 +68,12 @@ class EglWindow : public GLWindow bool createWindow(const char *title, int x, int y, int w, int h, bool legacyGlOnly) override; + /// Runs the window loop. + void mainLoop() override; + void mainIter() override; + + void signalLoop() override; + void getWindowSize(int& w, int& h) const override { getGLDrawSize(w, h); } void getGLDrawSize(int& w, int& h) const override; @@ -44,8 +86,9 @@ class EglWindow : public GLWindow bool isWindowInitialized() const override { return surf != EGL_NO_SURFACE; } bool isGlInitialized() const override { return ctx != EGL_NO_CONTEXT; } - void signalExpose() override; - void signalSwap() override { } + void signalQuit() override; + // as there is no swap, switch to updated state right away + void signalSwap() override { wnd_state = RenderState::Updated; } bool isExposePending() const override { return false; } // used in Screenshot, as there is no swapping, the single buffer is always diff --git a/lib/glwindow.hpp b/lib/glwindow.hpp index 6beef227..358fe022 100644 --- a/lib/glwindow.hpp +++ b/lib/glwindow.hpp @@ -17,9 +17,27 @@ class GLWindow { +public: + typedef bool (*IdleDelegate)(); + typedef void (*Delegate)(); + protected: + enum class RenderState + { + // window displayed is fully current (no events or backbuffer updates pending) + Updated, + // events issued which may require a call to MyExpose + ExposePending, + // back buffer updated by MyExpose, now awaiting swap to be displayed on window + SwapPending + }; + RenderState wnd_state{RenderState::Updated}; + std::unique_ptr renderer; + IdleDelegate onIdle{}; + Delegate onExpose{}; + bool initGLEW(bool legacyGlOnly); public: virtual ~GLWindow() = default; @@ -31,6 +49,22 @@ class GLWindow /// Returns the renderer object inline gl3::MeshRenderer& getRenderer() { return *renderer.get(); } + /// Runs the window loop. + virtual void mainLoop() = 0; + virtual void mainIter() = 0; + + /// Signals addition of a new event + virtual void signalLoop() = 0; + + void setOnIdle(IdleDelegate func) { onIdle = func; } + void setOnExpose(Delegate func) { onExpose = func; } + + virtual void clearEvents() + { + onIdle = nullptr; + onExpose = nullptr; + } + /// Returns size of the window virtual void getWindowSize(int& w, int& h) const { w = h = 0; } @@ -61,17 +95,20 @@ class GLWindow /// Returns true if the OpenGL context was successfully initialized virtual bool isGlInitialized() const { return false; } + /// Signals quit event + virtual void signalQuit() { } + /// Signals expose event when objects have been updated - virtual void signalExpose() = 0; + virtual void signalExpose() { wnd_state = RenderState::ExposePending; } /// Signals swap event when the back buffer is ready for swapping - virtual void signalSwap() = 0; - - /// Checks if the swap event is pending - virtual bool isSwapPending() const { return false; } + virtual void signalSwap() { wnd_state = RenderState::SwapPending; } /// Checks if the expose event is pending - virtual bool isExposePending() const { return false; } + virtual bool isExposePending() const { return wnd_state == RenderState::ExposePending; } + + /// Checks if the swap event is pending + virtual bool isSwapPending() const { return wnd_state == RenderState::SwapPending; } /// Saves a screenshot ot the file, performing conversion optionally virtual void screenshot(std::string filename, bool convert = false) { } diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index 10711553..1a6c0b6c 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -368,6 +368,10 @@ bool ScriptController::ExecuteScriptCommand() { cout << "End of script." << endl; scr_level = 0; + if (headless) + { + GetGLWindow()->signalQuit(); + } return false; } if (scr.peek() == '#') @@ -833,7 +837,7 @@ thread_local ScriptController *ScriptController::script_ctrl = NULL; void ScriptController::ScriptIdleFunc() { script_ctrl->ExecuteScriptCommand(); - if (script_ctrl->scr_level == 0) + if (script_ctrl->scr_level == 0 && !script_ctrl->headless) { ScriptControl(); } @@ -993,8 +997,7 @@ void ScriptController::PlayScript(Window win, istream &scr) else { // execute all commands, updating the scene every time - do { MyExpose(); } - while (local_script.ExecuteScriptCommand()); + ScriptControl(); } local_script.win.GLVisStartVis(); } diff --git a/lib/sdl.hpp b/lib/sdl.hpp index 7067f2e5..d5a06fa6 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -33,8 +33,6 @@ typedef void (*TouchDelegate)(SDL_MultiGestureEvent&); typedef void (*MouseDelegate)(EventInfo*); typedef std::function KeyDelegate; typedef void (*WindowDelegate)(int, int); -typedef void (*Delegate)(); -typedef bool (*IdleDelegate)(); class SdlMainThread; SdlMainThread& GetMainThread(); @@ -94,8 +92,7 @@ class SdlWindow : public GLWindow bool running; - IdleDelegate onIdle{nullptr}; - Delegate onExpose{nullptr}; + WindowDelegate onReshape{nullptr}; std::map onKeyDown; std::map onMouseDown; @@ -108,18 +105,6 @@ class SdlWindow : public GLWindow std::string canvas_id_; #endif - enum class RenderState - { - // window displayed is fully current (no events or backbuffer updates pending) - Updated, - // events issued which may require a call to MyExpose - ExposePending, - // back buffer updated by MyExpose, now awaiting swap to be displayed on window - SwapPending - }; - - RenderState wnd_state{RenderState::Updated}; - bool update_before_expose{false}; // bool requiresExpose; @@ -177,14 +162,12 @@ class SdlWindow : public GLWindow bool legacyGlOnly) override; /// Runs the window loop. - void mainLoop(); - void mainIter(); + void mainLoop() override; + void mainIter() override; // Called by worker threads in GLVisCommand::signal() - void signalLoop(); + void signalLoop() override; - void setOnIdle(IdleDelegate func) { onIdle = func; } - void setOnExpose(Delegate func) { onExpose = func; } void setOnReshape(WindowDelegate func) { onReshape = func; } void setOnKeyDown(int key, Delegate func) @@ -200,10 +183,9 @@ class SdlWindow : public GLWindow void setTouchPinchCallback(TouchDelegate cb) { onTouchPinch = cb; } void setTouchRotateCallback(TouchDelegate cb) { onTouchRotate = cb; } - void clearEvents() + void clearEvents() override { - onIdle = nullptr; - onExpose = nullptr; + GLWindow::clearEvents(); onReshape = nullptr; onKeyDown.clear(); onMouseUp.clear(); @@ -231,9 +213,7 @@ class SdlWindow : public GLWindow void setWindowPos(int x, int y) override; void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE); - void signalExpose() override { wnd_state = RenderState::ExposePending; } - void signalSwap() override { wnd_state = RenderState::SwapPending; } - void signalQuit() { running = false; } + void signalQuit() override { running = false; } /// Returns the keyboard events that have been logged by the window. std::string getSavedKeys() const { return saved_keys; } @@ -256,9 +236,6 @@ class SdlWindow : public GLWindow /// Returns true if the OpenGL context was successfully initialized. bool isGlInitialized() const override; - bool isSwapPending() const override { return wnd_state == RenderState::SwapPending; } - bool isExposePending() const override { return wnd_state == RenderState::ExposePending; } - #ifdef __EMSCRIPTEN__ std::string getCanvasId() const { return canvas_id_; } void setCanvasId(std::string canvas_id) { canvas_id_ = canvas_id; } diff --git a/lib/threads.cpp b/lib/threads.cpp index d38d97eb..c6627396 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -24,7 +24,7 @@ GLVisCommand::GLVisCommand(Window &win_) : win(win_) { // should be set in this thread by a call to InitVisualization() - thread_wnd = GetAppWindow(); + thread_wnd = GetGLWindow(); num_waiting = 0; terminating = false; diff --git a/lib/threads.hpp b/lib/threads.hpp index c63e3e82..f1be77b4 100644 --- a/lib/threads.hpp +++ b/lib/threads.hpp @@ -23,7 +23,7 @@ class GLVisCommand private: // Pointers to global GLVis data Window &win; - SdlWindow *thread_wnd; + GLWindow *thread_wnd; std::mutex glvis_mutex; std::condition_variable glvis_cond; From 1f18f20e92f3b7038ca9265e642eefb4e7f65ce9 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 12 May 2025 23:00:32 -0700 Subject: [PATCH 06/79] Fixed script screenshot command. --- lib/script_controller.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index 1a6c0b6c..888853a2 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -507,6 +507,8 @@ bool ScriptController::ExecuteScriptCommand() cout << "Script: screenshot: " << flush; + //perform expose before screenshot to read from the back buffer + MyExpose(); if (Screenshot(word.c_str(), true)) { cout << "Screenshot(" << word << ") failed." << endl; From d2d7ebbbd6c85b83c1e6d26cca6caa1fabb7953e Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 12 May 2025 23:34:28 -0700 Subject: [PATCH 07/79] Moved key and mouse handling to GLWindow and implemented in EglWindow. --- lib/aux_vis.cpp | 193 +++++++++++++++++++++---------------------- lib/aux_vis.hpp | 18 ++-- lib/egl.cpp | 19 ++++- lib/egl.hpp | 8 ++ lib/glwindow.cpp | 25 ++++++ lib/glwindow.hpp | 49 +++++++++++ lib/sdl.cpp | 44 ++-------- lib/sdl.hpp | 48 +---------- lib/vsdata.cpp | 70 ++++++++-------- lib/vssolution.cpp | 51 ++++++------ lib/vssolution3d.cpp | 61 +++++++------- lib/vsvector.cpp | 17 ++-- lib/vsvector3d.cpp | 31 ++++--- lib/window.cpp | 4 +- 14 files changed, 328 insertions(+), 310 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index a05de1ca..6d1e486a 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -155,87 +155,84 @@ int InitVisualization (const char name[], int x, int y, int w, int h, void (*exposeFunc)(void) = MyExpose; wnd->setOnExpose(exposeFunc); + wnd->setOnMouseDown(SDL_BUTTON_LEFT, LeftButtonDown); + wnd->setOnMouseUp(SDL_BUTTON_LEFT, LeftButtonUp); + wnd->setOnMouseMove(SDL_BUTTON_LEFT, LeftButtonLoc); + wnd->setOnMouseDown(SDL_BUTTON_MIDDLE, MiddleButtonDown); + wnd->setOnMouseUp(SDL_BUTTON_MIDDLE, MiddleButtonUp); + wnd->setOnMouseMove(SDL_BUTTON_MIDDLE, MiddleButtonLoc); + wnd->setOnMouseDown(SDL_BUTTON_RIGHT, RightButtonDown); + wnd->setOnMouseUp(SDL_BUTTON_RIGHT, RightButtonUp); + wnd->setOnMouseMove(SDL_BUTTON_RIGHT, RightButtonLoc); + if (sdl_wnd) { - sdl_wnd->setOnMouseDown(SDL_BUTTON_LEFT, LeftButtonDown); - sdl_wnd->setOnMouseUp(SDL_BUTTON_LEFT, LeftButtonUp); - sdl_wnd->setOnMouseMove(SDL_BUTTON_LEFT, LeftButtonLoc); - sdl_wnd->setOnMouseDown(SDL_BUTTON_MIDDLE, MiddleButtonDown); - sdl_wnd->setOnMouseUp(SDL_BUTTON_MIDDLE, MiddleButtonUp); - sdl_wnd->setOnMouseMove(SDL_BUTTON_MIDDLE, MiddleButtonLoc); - sdl_wnd->setOnMouseDown(SDL_BUTTON_RIGHT, RightButtonDown); - sdl_wnd->setOnMouseUp(SDL_BUTTON_RIGHT, RightButtonUp); - sdl_wnd->setOnMouseMove(SDL_BUTTON_RIGHT, RightButtonLoc); - sdl_wnd->setTouchPinchCallback(TouchPinch); + } - // auxKeyFunc (AUX_p, KeyCtrlP); // handled in vsdata.cpp - sdl_wnd->setOnKeyDown (SDLK_s, KeyS); - sdl_wnd->setOnKeyDown ('S', KeyS); + // auxKeyFunc (AUX_p, KeyCtrlP); // handled in vsdata.cpp + wnd->setOnKeyDown (SDLK_s, KeyS); + wnd->setOnKeyDown ('S', KeyS); - sdl_wnd->setOnKeyDown (SDLK_q, KeyQPressed); - // sdl_wnd->setOnKeyDown (SDLK_Q, KeyQPressed); + wnd->setOnKeyDown (SDLK_q, KeyQPressed); + // wnd->setOnKeyDown (SDLK_Q, KeyQPressed); - sdl_wnd->setOnKeyDown (SDLK_LEFT, KeyLeftPressed); - sdl_wnd->setOnKeyDown (SDLK_RIGHT, KeyRightPressed); - sdl_wnd->setOnKeyDown (SDLK_UP, KeyUpPressed); - sdl_wnd->setOnKeyDown (SDLK_DOWN, KeyDownPressed); + wnd->setOnKeyDown (SDLK_LEFT, KeyLeftPressed); + wnd->setOnKeyDown (SDLK_RIGHT, KeyRightPressed); + wnd->setOnKeyDown (SDLK_UP, KeyUpPressed); + wnd->setOnKeyDown (SDLK_DOWN, KeyDownPressed); - sdl_wnd->setOnKeyDown (SDLK_KP_0, Key0Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_1, Key1Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_2, Key2Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_3, Key3Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_4, Key4Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_5, Key5Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_6, Key6Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_7, Key7Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_8, Key8Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_9, Key9Pressed); + wnd->setOnKeyDown (SDLK_KP_0, Key0Pressed); + wnd->setOnKeyDown (SDLK_KP_1, Key1Pressed); + wnd->setOnKeyDown (SDLK_KP_2, Key2Pressed); + wnd->setOnKeyDown (SDLK_KP_3, Key3Pressed); + wnd->setOnKeyDown (SDLK_KP_4, Key4Pressed); + wnd->setOnKeyDown (SDLK_KP_5, Key5Pressed); + wnd->setOnKeyDown (SDLK_KP_6, Key6Pressed); + wnd->setOnKeyDown (SDLK_KP_7, Key7Pressed); + wnd->setOnKeyDown (SDLK_KP_8, Key8Pressed); + wnd->setOnKeyDown (SDLK_KP_9, Key9Pressed); - sdl_wnd->setOnKeyDown (SDLK_KP_MEMSUBTRACT, KeyMinusPressed); - sdl_wnd->setOnKeyDown (SDLK_KP_MEMADD, KeyPlusPressed); + wnd->setOnKeyDown (SDLK_KP_MEMSUBTRACT, KeyMinusPressed); + wnd->setOnKeyDown (SDLK_KP_MEMADD, KeyPlusPressed); - sdl_wnd->setOnKeyDown (SDLK_KP_DECIMAL, KeyDeletePressed); - sdl_wnd->setOnKeyDown (SDLK_KP_ENTER, KeyEnterPressed); + wnd->setOnKeyDown (SDLK_KP_DECIMAL, KeyDeletePressed); + wnd->setOnKeyDown (SDLK_KP_ENTER, KeyEnterPressed); - sdl_wnd->setOnKeyDown (SDLK_PERIOD, KeyDeletePressed); - sdl_wnd->setOnKeyDown (SDLK_RETURN, KeyEnterPressed); + wnd->setOnKeyDown (SDLK_PERIOD, KeyDeletePressed); + wnd->setOnKeyDown (SDLK_RETURN, KeyEnterPressed); - sdl_wnd->setOnKeyDown (SDLK_0, Key0Pressed); - sdl_wnd->setOnKeyDown (SDLK_1, Key1Pressed); - sdl_wnd->setOnKeyDown (SDLK_2, Key2Pressed); - sdl_wnd->setOnKeyDown (SDLK_3, Key3Pressed); - sdl_wnd->setOnKeyDown (SDLK_4, Key4Pressed); - sdl_wnd->setOnKeyDown (SDLK_5, Key5Pressed); - sdl_wnd->setOnKeyDown (SDLK_6, Key6Pressed); - sdl_wnd->setOnKeyDown (SDLK_7, Key7Pressed); - sdl_wnd->setOnKeyDown (SDLK_8, Key8Pressed); - sdl_wnd->setOnKeyDown (SDLK_9, Key9Pressed); + wnd->setOnKeyDown (SDLK_0, Key0Pressed); + wnd->setOnKeyDown (SDLK_1, Key1Pressed); + wnd->setOnKeyDown (SDLK_2, Key2Pressed); + wnd->setOnKeyDown (SDLK_3, Key3Pressed); + wnd->setOnKeyDown (SDLK_4, Key4Pressed); + wnd->setOnKeyDown (SDLK_5, Key5Pressed); + wnd->setOnKeyDown (SDLK_6, Key6Pressed); + wnd->setOnKeyDown (SDLK_7, Key7Pressed); + wnd->setOnKeyDown (SDLK_8, Key8Pressed); + wnd->setOnKeyDown (SDLK_9, Key9Pressed); - sdl_wnd->setOnKeyDown (SDLK_MINUS, KeyMinusPressed); - sdl_wnd->setOnKeyDown (SDLK_PLUS, KeyPlusPressed); - sdl_wnd->setOnKeyDown (SDLK_EQUALS, KeyPlusPressed); + wnd->setOnKeyDown (SDLK_MINUS, KeyMinusPressed); + wnd->setOnKeyDown (SDLK_PLUS, KeyPlusPressed); + wnd->setOnKeyDown (SDLK_EQUALS, KeyPlusPressed); - sdl_wnd->setOnKeyDown (SDLK_j, KeyJPressed); - // sdl_wnd->setOnKeyDown (AUX_J, KeyJPressed); + wnd->setOnKeyDown (SDLK_j, KeyJPressed); + // wnd->setOnKeyDown (AUX_J, KeyJPressed); - sdl_wnd->setOnKeyDown (SDLK_KP_MULTIPLY, ZoomIn); - sdl_wnd->setOnKeyDown (SDLK_KP_DIVIDE, ZoomOut); + wnd->setOnKeyDown (SDLK_KP_MULTIPLY, ZoomIn); + wnd->setOnKeyDown (SDLK_KP_DIVIDE, ZoomOut); - sdl_wnd->setOnKeyDown (SDLK_ASTERISK, ZoomIn); - sdl_wnd->setOnKeyDown (SDLK_SLASH, ZoomOut); + wnd->setOnKeyDown (SDLK_ASTERISK, ZoomIn); + wnd->setOnKeyDown (SDLK_SLASH, ZoomOut); - sdl_wnd->setOnKeyDown (SDLK_LEFTBRACKET, ScaleDown); - sdl_wnd->setOnKeyDown (SDLK_RIGHTBRACKET, ScaleUp); - sdl_wnd->setOnKeyDown (SDLK_AT, LookAt); - } + wnd->setOnKeyDown (SDLK_LEFTBRACKET, ScaleDown); + wnd->setOnKeyDown (SDLK_RIGHTBRACKET, ScaleUp); + wnd->setOnKeyDown (SDLK_AT, LookAt); #ifndef __EMSCRIPTEN__ - if (sdl_wnd) - { - sdl_wnd->setOnKeyDown(SDLK_LEFTPAREN, ShrinkWindow); - sdl_wnd->setOnKeyDown(SDLK_RIGHTPAREN, EnlargeWindow); - } + wnd->setOnKeyDown(SDLK_LEFTPAREN, ShrinkWindow); + wnd->setOnKeyDown(SDLK_RIGHTPAREN, EnlargeWindow); if (locscene) { @@ -260,53 +257,53 @@ void SendKeySequence(const char *seq) SendExposeEvent(); break; case 'l': // left arrow - sdl_wnd->signalKeyDown(SDLK_LEFT); + wnd->signalKeyDown(SDLK_LEFT); break; case 'r': // right arrow - sdl_wnd->signalKeyDown(SDLK_RIGHT); + wnd->signalKeyDown(SDLK_RIGHT); break; case 'u': // up arrow - sdl_wnd->signalKeyDown(SDLK_UP); + wnd->signalKeyDown(SDLK_UP); break; case 'd': // down arrow - sdl_wnd->signalKeyDown(SDLK_DOWN); + wnd->signalKeyDown(SDLK_DOWN); break; case '3': // F3 - sdl_wnd->signalKeyDown(SDLK_F3); + wnd->signalKeyDown(SDLK_F3); break; case '5': // F5 - sdl_wnd->signalKeyDown(SDLK_F5); + wnd->signalKeyDown(SDLK_F5); break; case '6': // F6 - sdl_wnd->signalKeyDown(SDLK_F6); + wnd->signalKeyDown(SDLK_F6); break; case '7': // F7 - sdl_wnd->signalKeyDown(SDLK_F7); + wnd->signalKeyDown(SDLK_F7); break; case '1': // F11 or F12 key++; switch (*key) { case '1': // F11 - sdl_wnd->signalKeyDown(SDLK_F11); + wnd->signalKeyDown(SDLK_F11); break; case '2': // F12 - sdl_wnd->callKeyDown(SDLK_F12); + wnd->callKeyDown(SDLK_F12); break; } break; case '.': // Keypad ./Del - sdl_wnd->signalKeyDown(SDLK_PERIOD); + wnd->signalKeyDown(SDLK_PERIOD); break; case 'E': // Keypad Enter - sdl_wnd->signalKeyDown(SDLK_RETURN); + wnd->signalKeyDown(SDLK_RETURN); break; } continue; } else { - sdl_wnd->signalKeyDown(*key); + wnd->signalKeyDown(*key); } } } @@ -323,7 +320,7 @@ void CallKeySequence(const char *seq) { if (*key != '~') { - sdl_wnd->callKeyDown(*key); + wnd->callKeyDown(*key); } else { @@ -331,46 +328,46 @@ void CallKeySequence(const char *seq) switch (*key) { case 'l': // left arrow - sdl_wnd->callKeyDown(SDLK_LEFT); + wnd->callKeyDown(SDLK_LEFT); break; case 'r': // right arrow - sdl_wnd->callKeyDown(SDLK_RIGHT); + wnd->callKeyDown(SDLK_RIGHT); break; case 'u': // up arrow - sdl_wnd->callKeyDown(SDLK_UP); + wnd->callKeyDown(SDLK_UP); break; case 'd': // down arrow - sdl_wnd->callKeyDown(SDLK_DOWN); + wnd->callKeyDown(SDLK_DOWN); break; case '3': // F3 - sdl_wnd->callKeyDown(SDLK_F3); + wnd->callKeyDown(SDLK_F3); break; case '5': // F5 - sdl_wnd->callKeyDown(SDLK_F5); + wnd->callKeyDown(SDLK_F5); break; case '6': // F6 - sdl_wnd->callKeyDown(SDLK_F6); + wnd->callKeyDown(SDLK_F6); break; case '7': // F7 - sdl_wnd->callKeyDown(SDLK_F7); + wnd->callKeyDown(SDLK_F7); break; case '1': // F11 or F12 key++; switch (*key) { case '1': // F11 - sdl_wnd->callKeyDown(SDLK_F11); + wnd->callKeyDown(SDLK_F11); break; case '2': // F12 - sdl_wnd->callKeyDown(SDLK_F12); + wnd->callKeyDown(SDLK_F12); break; } break; case '.': // Keypad ./Del - sdl_wnd->callKeyDown(SDLK_PERIOD); + wnd->callKeyDown(SDLK_PERIOD); break; case 'E': // Keypad Enter - sdl_wnd->callKeyDown(SDLK_RETURN); + wnd->callKeyDown(SDLK_RETURN); break; } } @@ -647,7 +644,7 @@ inline void ComputeSphereAngles(int &newx, int &newy, new_sph_t = atan2(y, x); } -void LeftButtonDown (EventInfo *event) +void LeftButtonDown(GLWindow::MouseEventInfo *event) { locscene -> spinning = 0; RemoveIdleFunc(MainLoop); @@ -665,7 +662,7 @@ void LeftButtonDown (EventInfo *event) starty = oldy; } -void LeftButtonLoc (EventInfo *event) +void LeftButtonLoc(GLWindow::MouseEventInfo *event) { GLint newx = event->mouse_x; GLint newy = event->mouse_y; @@ -723,7 +720,7 @@ void LeftButtonLoc (EventInfo *event) } } -void LeftButtonUp (EventInfo *event) +void LeftButtonUp(GLWindow::MouseEventInfo *event) { GLint newx = event->mouse_x; GLint newy = event->mouse_y; @@ -749,13 +746,13 @@ void LeftButtonUp (EventInfo *event) } } -void MiddleButtonDown (EventInfo *event) +void MiddleButtonDown(GLWindow::MouseEventInfo *event) { startx = oldx = event->mouse_x; starty = oldy = event->mouse_y; } -void MiddleButtonLoc (EventInfo *event) +void MiddleButtonLoc(GLWindow::MouseEventInfo *event) { GLint newx = event->mouse_x; GLint newy = event->mouse_y; @@ -823,16 +820,16 @@ void MiddleButtonLoc (EventInfo *event) oldy = newy; } -void MiddleButtonUp (EventInfo*) +void MiddleButtonUp(GLWindow::MouseEventInfo*) {} -void RightButtonDown (EventInfo *event) +void RightButtonDown(GLWindow::MouseEventInfo *event) { startx = oldx = event->mouse_x; starty = oldy = event->mouse_y; } -void RightButtonLoc (EventInfo *event) +void RightButtonLoc(GLWindow::MouseEventInfo *event) { GLint newx = event->mouse_x; GLint newy = event->mouse_y; @@ -880,7 +877,7 @@ void RightButtonLoc (EventInfo *event) oldy = newy; } -void RightButtonUp (EventInfo*) +void RightButtonUp(GLWindow::MouseEventInfo*) {} void TouchPinch(SDL_MultiGestureEvent & e) diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index f9aedade..67c01d4a 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -55,15 +55,15 @@ void SetLegacyGLOnly(bool status); void AddIdleFunc(void (*Func)(void)); void RemoveIdleFunc(void (*Func)(void)); -void LeftButtonDown (EventInfo *event); -void LeftButtonLoc (EventInfo *event); -void LeftButtonUp (EventInfo *event); -void MiddleButtonDown(EventInfo *event); -void MiddleButtonLoc (EventInfo *event); -void MiddleButtonUp (EventInfo *event); -void RightButtonDown (EventInfo *event); -void RightButtonLoc (EventInfo *event); -void RightButtonUp (EventInfo *event); +void LeftButtonDown (GLWindow::MouseEventInfo *event); +void LeftButtonLoc (GLWindow::MouseEventInfo *event); +void LeftButtonUp (GLWindow::MouseEventInfo *event); +void MiddleButtonDown(GLWindow::MouseEventInfo *event); +void MiddleButtonLoc (GLWindow::MouseEventInfo *event); +void MiddleButtonUp (GLWindow::MouseEventInfo *event); +void RightButtonDown (GLWindow::MouseEventInfo *event); +void RightButtonLoc (GLWindow::MouseEventInfo *event); +void RightButtonUp (GLWindow::MouseEventInfo *event); void TouchPinch(SDL_MultiGestureEvent & e); diff --git a/lib/egl.cpp b/lib/egl.cpp index 0663cb88..f1695ee8 100644 --- a/lib/egl.cpp +++ b/lib/egl.cpp @@ -233,6 +233,13 @@ void EglWindow::mainIter() switch (e.type) { + case EventType::Keydown: + if (onKeyDown[e.event.keydown.k]) + { + onKeyDown[e.event.keydown.k](e.event.keydown.m); + recordKey(e.event.keydown.k, e.event.keydown.m); + } + break; case EventType::Screenshot: Screenshot(screenshot_filename.c_str(), e.event.screenshot.convert); break; @@ -317,6 +324,13 @@ void EglWindow::setWindowSize(int w, int h) surf = surf_new; } +void EglWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) +{ + Event::Events e; + e.keydown = {k, m}; + queueEvents({{EventType::Keydown, e}}); +} + void EglWindow::signalQuit() { queueEvents({{EventType::Quit}}); @@ -325,6 +339,9 @@ void EglWindow::signalQuit() void EglWindow::screenshot(std::string filename, bool convert) { screenshot_filename = filename; - queueEvents({{EventType::Screenshot, Event::Events::Screenshot{convert}}}); + Event::Events e; + e.screenshot = {convert}; + queueEvents({{EventType::Screenshot, e}}); + // Queue up an expose, so Screenshot() can pull image from updated buffer signalExpose(); } diff --git a/lib/egl.hpp b/lib/egl.hpp index c3a78fef..ef726802 100644 --- a/lib/egl.hpp +++ b/lib/egl.hpp @@ -34,6 +34,7 @@ class EglWindow : public GLWindow enum class EventType { + Keydown, Screenshot, Quit, }; @@ -42,6 +43,12 @@ class EglWindow : public GLWindow EventType type; union Events { + struct Keydown + { + SDL_Keycode k; + SDL_Keymod m; + } keydown; + struct Screenshot { bool convert; @@ -86,6 +93,7 @@ class EglWindow : public GLWindow bool isWindowInitialized() const override { return surf != EGL_NO_SURFACE; } bool isGlInitialized() const override { return ctx != EGL_NO_CONTEXT; } + void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) override; void signalQuit() override; // as there is no swap, switch to updated state right away void signalSwap() override { wnd_state = RenderState::Updated; } diff --git a/lib/glwindow.cpp b/lib/glwindow.cpp index 9316947c..51e4dcbf 100644 --- a/lib/glwindow.cpp +++ b/lib/glwindow.cpp @@ -99,3 +99,28 @@ bool GLWindow::initGLEW(bool legacyGlOnly) return true; } + +void GLWindow::recordKey(SDL_Keycode sym, SDL_Keymod mod) +{ + // Record the key in 'saved_keys': + bool isAlt = mod & (KMOD_ALT); + bool isCtrl = mod & (KMOD_CTRL); + if (isAlt || isCtrl) + { + saved_keys += "["; + } + if (isCtrl) { saved_keys += "C-"; } + if (isAlt) { saved_keys += "Alt-"; } + if (sym >= 32 && sym < 127) + { + saved_keys += (char)(sym); + } + else + { + saved_keys += SDL_GetKeyName(sym); + } + if (isAlt || isCtrl) + { + saved_keys += "]"; + } +} diff --git a/lib/glwindow.hpp b/lib/glwindow.hpp index 358fe022..3e64a213 100644 --- a/lib/glwindow.hpp +++ b/lib/glwindow.hpp @@ -14,12 +14,25 @@ #include "gl/renderer.hpp" #include +#include +#include +#include class GLWindow { public: + typedef Uint8 SDL_Mousebutton; + struct MouseEventInfo + { + Sint32 mouse_x; + Sint32 mouse_y; + SDL_Keymod keymod; + }; + typedef bool (*IdleDelegate)(); typedef void (*Delegate)(); + typedef std::function KeyDelegate; + typedef void (*MouseDelegate)(MouseEventInfo*); protected: enum class RenderState @@ -37,8 +50,16 @@ class GLWindow IdleDelegate onIdle{}; Delegate onExpose{}; + std::map onKeyDown; + std::map onMouseDown; + std::map onMouseUp; + std::map onMouseMove; + + std::string saved_keys; bool initGLEW(bool legacyGlOnly); + void recordKey(SDL_Keycode k, SDL_Keymod m); + public: virtual ~GLWindow() = default; @@ -59,10 +80,32 @@ class GLWindow void setOnIdle(IdleDelegate func) { onIdle = func; } void setOnExpose(Delegate func) { onExpose = func; } + void setOnKeyDown(SDL_Keycode key, Delegate func) + { + onKeyDown[key] = [func](SDL_Keymod) { func(); }; + } + void setOnKeyDown(SDL_Keycode key, KeyDelegate func) { onKeyDown[key] = func; } + + void setOnMouseDown(SDL_Mousebutton btn, MouseDelegate func) { onMouseDown[btn] = func; } + void setOnMouseUp(SDL_Mousebutton btn, MouseDelegate func) { onMouseUp[btn] = func; } + void setOnMouseMove(SDL_Mousebutton btn, MouseDelegate func) { onMouseMove[btn] = func; } + virtual void clearEvents() { onIdle = nullptr; onExpose = nullptr; + onKeyDown.clear(); + onMouseUp.clear(); + onMouseDown.clear(); + onMouseMove.clear(); + } + + void callKeyDown(SDL_Keycode k, SDL_Keymod mod = KMOD_NONE) + { + if (onKeyDown[k]) + { + onKeyDown[k](mod); + } } /// Returns size of the window @@ -95,6 +138,9 @@ class GLWindow /// Returns true if the OpenGL context was successfully initialized virtual bool isGlInitialized() const { return false; } + /// Signals key down event + virtual void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) { } + /// Signals quit event virtual void signalQuit() { } @@ -110,6 +156,9 @@ class GLWindow /// Checks if the swap event is pending virtual bool isSwapPending() const { return wnd_state == RenderState::SwapPending; } + /// Returns the keyboard events that have been logged by the window. + std::string getSavedKeys() const { return saved_keys; } + /// Saves a screenshot ot the file, performing conversion optionally virtual void screenshot(std::string filename, bool convert = false) { } }; diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 47f79291..35dc51da 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -136,7 +136,7 @@ void SdlWindow::windowEvent(SDL_WindowEvent& ew) void SdlWindow::motionEvent(SDL_MouseMotionEvent& em) { - EventInfo info = + MouseEventInfo info = { em.x, em.y, SDL_GetModState() @@ -168,7 +168,7 @@ void SdlWindow::mouseEventDown(SDL_MouseButtonEvent& eb) { if (onMouseDown[eb.button]) { - EventInfo info = + MouseEventInfo info = { eb.x, eb.y, SDL_GetModState() @@ -181,7 +181,7 @@ void SdlWindow::mouseEventUp(SDL_MouseButtonEvent& eb) { if (onMouseUp[eb.button]) { - EventInfo info = + MouseEventInfo info = { eb.x, eb.y, SDL_GetModState() @@ -201,7 +201,7 @@ void SdlWindow::keyDownEvent(SDL_Keysym& ks) && (ks.mod & (KMOD_CTRL | KMOD_LALT | KMOD_GUI)) == 0) { lastKeyDownProcessed = false; - lastKeyDownMods = ks.mod; + lastKeyDownMods = (SDL_Keymod)ks.mod; lastKeyDownChar = ks.sym; return; } @@ -211,23 +211,9 @@ void SdlWindow::keyDownEvent(SDL_Keysym& ks) lastKeyDownProcessed = true; if (onKeyDown[ks.sym]) { - onKeyDown[ks.sym](ks.mod); + onKeyDown[ks.sym]((SDL_Keymod)ks.mod); - // Record the key in 'saved_keys': - bool isAlt = ks.mod & (KMOD_ALT); - bool isCtrl = ks.mod & (KMOD_CTRL); - saved_keys += "["; - if (isCtrl) { saved_keys += "C-"; } - if (isAlt) { saved_keys += "Alt-"; } - if (ks.sym >= 32 && ks.sym < 127) - { - saved_keys += (char)(ks.sym); - } - else - { - saved_keys += SDL_GetKeyName(ks.sym); - } - saved_keys += "]"; + recordKey(ks.sym, (SDL_Keymod)ks.mod); } } @@ -245,22 +231,10 @@ void SdlWindow::textInputEvent(const SDL_TextInputEvent &tie) } if (onKeyDown[c]) { - onKeyDown[c](lastKeyDownMods & ~(KMOD_CAPS | KMOD_LSHIFT | KMOD_RSHIFT)); + onKeyDown[c]((SDL_Keymod)(lastKeyDownMods & ~(KMOD_CAPS | KMOD_LSHIFT | + KMOD_RSHIFT))); - // Record the key in 'saved_keys': - bool isAlt = lastKeyDownMods & (KMOD_ALT); - bool isCtrl = lastKeyDownMods & (KMOD_CTRL); - if (isAlt || isCtrl) - { - saved_keys += "["; - } - if (isCtrl) { saved_keys += "C-"; } - if (isAlt) { saved_keys += "Alt-"; } - saved_keys += c; - if (isAlt || isCtrl) - { - saved_keys += "]"; - } + recordKey(c, lastKeyDownMods); } } diff --git a/lib/sdl.hpp b/lib/sdl.hpp index d5a06fa6..e2e8ec2b 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -13,25 +13,13 @@ #define GLVIS_SDL_HPP #include -#include -#include -#include #include #include #include #include "glwindow.hpp" #include "sdl_helper.hpp" -struct EventInfo -{ - GLint mouse_x; - GLint mouse_y; - SDL_Keymod keymod; -}; - typedef void (*TouchDelegate)(SDL_MultiGestureEvent&); -typedef void (*MouseDelegate)(EventInfo*); -typedef std::function KeyDelegate; typedef void (*WindowDelegate)(int, int); class SdlMainThread; @@ -94,10 +82,6 @@ class SdlWindow : public GLWindow WindowDelegate onReshape{nullptr}; - std::map onKeyDown; - std::map onMouseDown; - std::map onMouseUp; - std::map onMouseMove; TouchDelegate onTouchPinch{nullptr}; TouchDelegate onTouchRotate{nullptr}; @@ -112,7 +96,7 @@ class SdlWindow : public GLWindow std::string screenshot_file; bool screenshot_convert; bool lastKeyDownProcessed; - Uint16 lastKeyDownMods; + SDL_Keymod lastKeyDownMods; char lastKeyDownChar; // internal event handlers @@ -142,8 +126,6 @@ class SdlWindow : public GLWindow } } - std::string saved_keys; - std::condition_variable events_available; std::mutex event_mutex; // The window-specific events collected by the main event thread. @@ -169,17 +151,6 @@ class SdlWindow : public GLWindow void signalLoop() override; void setOnReshape(WindowDelegate func) { onReshape = func; } - - void setOnKeyDown(int key, Delegate func) - { - onKeyDown[key] = [func](GLenum) { func(); }; - } - void setOnKeyDown(int key, KeyDelegate func) { onKeyDown[key] = func; } - - void setOnMouseDown(int btn, MouseDelegate func) { onMouseDown[btn] = func; } - void setOnMouseUp(int btn, MouseDelegate func) { onMouseUp[btn] = func; } - void setOnMouseMove(int btn, MouseDelegate func) { onMouseMove[btn] = func; } - void setTouchPinchCallback(TouchDelegate cb) { onTouchPinch = cb; } void setTouchRotateCallback(TouchDelegate cb) { onTouchRotate = cb; } @@ -187,18 +158,6 @@ class SdlWindow : public GLWindow { GLWindow::clearEvents(); onReshape = nullptr; - onKeyDown.clear(); - onMouseUp.clear(); - onMouseDown.clear(); - onMouseMove.clear(); - } - - void callKeyDown(SDL_Keycode k, Uint16 mod=0) - { - if (onKeyDown[k]) - { - onKeyDown[k](mod); - } } void getWindowSize(int& w, int& h) const override; @@ -212,12 +171,9 @@ class SdlWindow : public GLWindow void setWindowSize(int w, int h) override; void setWindowPos(int x, int y) override; - void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE); + void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) override; void signalQuit() override { running = false; } - /// Returns the keyboard events that have been logged by the window. - std::string getSavedKeys() const { return saved_keys; } - /// Queues a screenshot to be taken. void screenshot(std::string filename, bool convert = false) override { diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp index 456555a2..0beb2cf4 100644 --- a/lib/vsdata.cpp +++ b/lib/vsdata.cpp @@ -1392,54 +1392,53 @@ void VisualizationSceneScalarData::Init() // static int init = 0; // if (!init) - if (sdl_wnd) { // init = 1; - sdl_wnd->setOnKeyDown('l', KeylPressed); - sdl_wnd->setOnKeyDown('L', KeyLPressed); + wnd->setOnKeyDown('l', KeylPressed); + wnd->setOnKeyDown('L', KeyLPressed); - sdl_wnd->setOnKeyDown('s', KeySPressed); + wnd->setOnKeyDown('s', KeySPressed); - // sdl_wnd->setOnKeyDown('a', KeyaPressed); - sdl_wnd->setOnKeyDown('a', Key_Mod_a_Pressed); - sdl_wnd->setOnKeyDown('A', KeyAPressed); + // wnd->setOnKeyDown('a', KeyaPressed); + wnd->setOnKeyDown('a', Key_Mod_a_Pressed); + wnd->setOnKeyDown('A', KeyAPressed); - sdl_wnd->setOnKeyDown('r', KeyrPressed); - sdl_wnd->setOnKeyDown('R', KeyRPressed); + wnd->setOnKeyDown('r', KeyrPressed); + wnd->setOnKeyDown('R', KeyRPressed); - sdl_wnd->setOnKeyDown('p', KeypPressed); - sdl_wnd->setOnKeyDown('P', KeyPPressed); + wnd->setOnKeyDown('p', KeypPressed); + wnd->setOnKeyDown('P', KeyPPressed); - sdl_wnd->setOnKeyDown('h', KeyHPressed); - sdl_wnd->setOnKeyDown('H', KeyHPressed); + wnd->setOnKeyDown('h', KeyHPressed); + wnd->setOnKeyDown('H', KeyHPressed); - sdl_wnd->setOnKeyDown(SDLK_F5, KeyF5Pressed); - sdl_wnd->setOnKeyDown(SDLK_F6, KeyF6Pressed); - sdl_wnd->setOnKeyDown(SDLK_F7, KeyF7Pressed); + wnd->setOnKeyDown(SDLK_F5, KeyF5Pressed); + wnd->setOnKeyDown(SDLK_F6, KeyF6Pressed); + wnd->setOnKeyDown(SDLK_F7, KeyF7Pressed); - sdl_wnd->setOnKeyDown(SDLK_BACKSLASH, KeyBackslashPressed); - sdl_wnd->setOnKeyDown('t', KeyTPressed); - sdl_wnd->setOnKeyDown('T', KeyTPressed); + wnd->setOnKeyDown(SDLK_BACKSLASH, KeyBackslashPressed); + wnd->setOnKeyDown('t', KeyTPressed); + wnd->setOnKeyDown('T', KeyTPressed); - sdl_wnd->setOnKeyDown('g', KeygPressed); - sdl_wnd->setOnKeyDown('G', KeyGPressed); + wnd->setOnKeyDown('g', KeygPressed); + wnd->setOnKeyDown('G', KeyGPressed); - sdl_wnd->setOnKeyDown('c', KeycPressed); - sdl_wnd->setOnKeyDown('C', KeyCPressed); + wnd->setOnKeyDown('c', KeycPressed); + wnd->setOnKeyDown('C', KeyCPressed); - sdl_wnd->setOnKeyDown('k', KeykPressed); - sdl_wnd->setOnKeyDown('K', KeyKPressed); + wnd->setOnKeyDown('k', KeykPressed); + wnd->setOnKeyDown('K', KeyKPressed); - sdl_wnd->setOnKeyDown(SDLK_F1, KeyF1Pressed); - sdl_wnd->setOnKeyDown(SDLK_F2, KeyF2Pressed); + wnd->setOnKeyDown(SDLK_F1, KeyF1Pressed); + wnd->setOnKeyDown(SDLK_F2, KeyF2Pressed); - sdl_wnd->setOnKeyDown(SDLK_COMMA, KeyCommaPressed); - sdl_wnd->setOnKeyDown(SDLK_LESS, KeyLessPressed); - sdl_wnd->setOnKeyDown('~', KeyTildePressed); - sdl_wnd->setOnKeyDown('`', KeyGravePressed); + wnd->setOnKeyDown(SDLK_COMMA, KeyCommaPressed); + wnd->setOnKeyDown(SDLK_LESS, KeyLessPressed); + wnd->setOnKeyDown('~', KeyTildePressed); + wnd->setOnKeyDown('`', KeyGravePressed); - sdl_wnd->setOnKeyDown(SDLK_EXCLAIM, KeyToggleTexture); + wnd->setOnKeyDown(SDLK_EXCLAIM, KeyToggleTexture); } // Set_Light(); @@ -1755,11 +1754,8 @@ void VisualizationSceneScalarData::SetLevelLines ( void VisualizationSceneScalarData::PrintState() { - if (sdl_wnd) - { - cout << "\nkeys: " << sdl_wnd->getSavedKeys() << "\n"; - } - cout << "\nlight " << strings_off_on[use_light ? 1 : 0] + cout << "\nkeys: " << wnd->getSavedKeys() << "\n" + << "\nlight " << strings_off_on[use_light ? 1 : 0] << "\nperspective " << strings_off_on[OrthogonalProjection ? 0 : 1] << "\nviewcenter " << ViewCenterX << ' ' << ViewCenterY << "\nzoom " << (OrthogonalProjection ? ViewScale : diff --git a/lib/vssolution.cpp b/lib/vssolution.cpp index 6c0d28ec..44638f00 100644 --- a/lib/vssolution.cpp +++ b/lib/vssolution.cpp @@ -476,43 +476,42 @@ void VisualizationSceneSolution::Init() // static int init = 0; // if (!init) - if (sdl_wnd) { // init = 1; - sdl_wnd->setOnKeyDown('b', KeyBPressed); - sdl_wnd->setOnKeyDown('B', KeyBPressed); + wnd->setOnKeyDown('b', KeyBPressed); + wnd->setOnKeyDown('B', KeyBPressed); - sdl_wnd->setOnKeyDown('m', KeyMPressed); - sdl_wnd->setOnKeyDown('M', KeyMPressed); + wnd->setOnKeyDown('m', KeyMPressed); + wnd->setOnKeyDown('M', KeyMPressed); - sdl_wnd->setOnKeyDown('n', KeyNPressed); - sdl_wnd->setOnKeyDown('N', KeyNPressed); + wnd->setOnKeyDown('n', KeyNPressed); + wnd->setOnKeyDown('N', KeyNPressed); - sdl_wnd->setOnKeyDown('o', KeyoPressed); - sdl_wnd->setOnKeyDown('O', KeyOPressed); + wnd->setOnKeyDown('o', KeyoPressed); + wnd->setOnKeyDown('O', KeyOPressed); - sdl_wnd->setOnKeyDown('e', KeyEPressed); - sdl_wnd->setOnKeyDown('E', KeyEPressed); + wnd->setOnKeyDown('e', KeyEPressed); + wnd->setOnKeyDown('E', KeyEPressed); - sdl_wnd->setOnKeyDown('f', KeyFPressed); - sdl_wnd->setOnKeyDown('F', KeyFPressed); + wnd->setOnKeyDown('f', KeyFPressed); + wnd->setOnKeyDown('F', KeyFPressed); - sdl_wnd->setOnKeyDown('i', KeyiPressed); - sdl_wnd->setOnKeyDown('I', KeyIPressed); + wnd->setOnKeyDown('i', KeyiPressed); + wnd->setOnKeyDown('I', KeyIPressed); - sdl_wnd->setOnKeyDown('y', KeyyPressed); - sdl_wnd->setOnKeyDown('Y', KeyYPressed); - sdl_wnd->setOnKeyDown('z', KeyzPressed); - sdl_wnd->setOnKeyDown('Z', KeyZPressed); + wnd->setOnKeyDown('y', KeyyPressed); + wnd->setOnKeyDown('Y', KeyYPressed); + wnd->setOnKeyDown('z', KeyzPressed); + wnd->setOnKeyDown('Z', KeyZPressed); - sdl_wnd->setOnKeyDown(SDLK_F3, KeyF3Pressed); - sdl_wnd->setOnKeyDown(SDLK_F4, KeyF4Pressed); - sdl_wnd->setOnKeyDown(SDLK_F8, KeyF8Pressed); - sdl_wnd->setOnKeyDown(SDLK_F9, KeyF9Pressed); - sdl_wnd->setOnKeyDown(SDLK_F10, KeyF10Pressed); - sdl_wnd->setOnKeyDown(SDLK_F11, KeyF11Pressed); - sdl_wnd->setOnKeyDown(SDLK_F12, KeyF12Pressed); + wnd->setOnKeyDown(SDLK_F3, KeyF3Pressed); + wnd->setOnKeyDown(SDLK_F4, KeyF4Pressed); + wnd->setOnKeyDown(SDLK_F8, KeyF8Pressed); + wnd->setOnKeyDown(SDLK_F9, KeyF9Pressed); + wnd->setOnKeyDown(SDLK_F10, KeyF10Pressed); + wnd->setOnKeyDown(SDLK_F11, KeyF11Pressed); + wnd->setOnKeyDown(SDLK_F12, KeyF12Pressed); } Prepare(); diff --git a/lib/vssolution3d.cpp b/lib/vssolution3d.cpp index 373b1d4a..ada41aec 100644 --- a/lib/vssolution3d.cpp +++ b/lib/vssolution3d.cpp @@ -762,53 +762,52 @@ void VisualizationSceneSolution3d::Init() // static int init = 0; // if (!init) - if (sdl_wnd) { // init = 1; - sdl_wnd->setOnKeyDown('m', KeymPressed); - sdl_wnd->setOnKeyDown('M', KeyMPressed); + wnd->setOnKeyDown('m', KeymPressed); + wnd->setOnKeyDown('M', KeyMPressed); - sdl_wnd->setOnKeyDown('e', KeyePressed); - sdl_wnd->setOnKeyDown('E', KeyEPressed); + wnd->setOnKeyDown('e', KeyePressed); + wnd->setOnKeyDown('E', KeyEPressed); - sdl_wnd->setOnKeyDown('f', KeyFPressed); - // sdl_wnd->setOnKeyDown('F', KeyFPressed); + wnd->setOnKeyDown('f', KeyFPressed); + // wnd->setOnKeyDown('F', KeyFPressed); - sdl_wnd->setOnKeyDown('i', KeyiPressed); - sdl_wnd->setOnKeyDown('I', KeyIPressed); + wnd->setOnKeyDown('i', KeyiPressed); + wnd->setOnKeyDown('I', KeyIPressed); - sdl_wnd->setOnKeyDown('o', KeyoPressed); - sdl_wnd->setOnKeyDown('O', KeyOPressed); + wnd->setOnKeyDown('o', KeyoPressed); + wnd->setOnKeyDown('O', KeyOPressed); - sdl_wnd->setOnKeyDown('w', KeywPressed); - sdl_wnd->setOnKeyDown('W', KeyWPressed); + wnd->setOnKeyDown('w', KeywPressed); + wnd->setOnKeyDown('W', KeyWPressed); - sdl_wnd->setOnKeyDown('x', KeyxPressed); - sdl_wnd->setOnKeyDown('X', KeyXPressed); + wnd->setOnKeyDown('x', KeyxPressed); + wnd->setOnKeyDown('X', KeyXPressed); - sdl_wnd->setOnKeyDown('y', KeyyPressed); - sdl_wnd->setOnKeyDown('Y', KeyYPressed); + wnd->setOnKeyDown('y', KeyyPressed); + wnd->setOnKeyDown('Y', KeyYPressed); - sdl_wnd->setOnKeyDown('z', KeyzPressed); - sdl_wnd->setOnKeyDown('Z', KeyZPressed); + wnd->setOnKeyDown('z', KeyzPressed); + wnd->setOnKeyDown('Z', KeyZPressed); - sdl_wnd->setOnKeyDown('u', KeyuPressed); - sdl_wnd->setOnKeyDown('U', KeyUPressed); + wnd->setOnKeyDown('u', KeyuPressed); + wnd->setOnKeyDown('U', KeyUPressed); - sdl_wnd->setOnKeyDown('v', KeyvPressed); - sdl_wnd->setOnKeyDown('V', KeyVPressed); + wnd->setOnKeyDown('v', KeyvPressed); + wnd->setOnKeyDown('V', KeyVPressed); - sdl_wnd->setOnKeyDown(SDLK_F3, KeyF3Pressed); - sdl_wnd->setOnKeyDown(SDLK_F4, KeyF4Pressed); - sdl_wnd->setOnKeyDown(SDLK_NUMLOCKCLEAR, ToggleMagicKey); + wnd->setOnKeyDown(SDLK_F3, KeyF3Pressed); + wnd->setOnKeyDown(SDLK_F4, KeyF4Pressed); + wnd->setOnKeyDown(SDLK_NUMLOCKCLEAR, ToggleMagicKey); - sdl_wnd->setOnKeyDown(SDLK_F8, KeyF8Pressed); - sdl_wnd->setOnKeyDown(SDLK_F9, KeyF9Pressed); - sdl_wnd->setOnKeyDown(SDLK_F10, KeyF10Pressed); + wnd->setOnKeyDown(SDLK_F8, KeyF8Pressed); + wnd->setOnKeyDown(SDLK_F9, KeyF9Pressed); + wnd->setOnKeyDown(SDLK_F10, KeyF10Pressed); - sdl_wnd->setOnKeyDown(SDLK_F11, KeyF11Pressed); - sdl_wnd->setOnKeyDown(SDLK_F12, KeyF12Pressed); + wnd->setOnKeyDown(SDLK_F11, KeyF11Pressed); + wnd->setOnKeyDown(SDLK_F12, KeyF12Pressed); } Prepare(); PrepareLines(); diff --git a/lib/vsvector.cpp b/lib/vsvector.cpp index c320aa72..206f47fd 100644 --- a/lib/vsvector.cpp +++ b/lib/vsvector.cpp @@ -496,18 +496,17 @@ void VisualizationSceneVector::Init() // static int init = 0; // if (!init) - if (sdl_wnd) { // init = 1; - sdl_wnd->setOnKeyDown('d', KeyDPressed); - sdl_wnd->setOnKeyDown('D', KeyDPressed); - sdl_wnd->setOnKeyDown('n', KeyNPressed); - sdl_wnd->setOnKeyDown('b', KeyBPressed); - sdl_wnd->setOnKeyDown('v', KeyvPressed); - sdl_wnd->setOnKeyDown('V', KeyVPressed); - sdl_wnd->setOnKeyDown('u', KeyuPressed); - sdl_wnd->setOnKeyDown('U', KeyUPressed); + wnd->setOnKeyDown('d', KeyDPressed); + wnd->setOnKeyDown('D', KeyDPressed); + wnd->setOnKeyDown('n', KeyNPressed); + wnd->setOnKeyDown('b', KeyBPressed); + wnd->setOnKeyDown('v', KeyvPressed); + wnd->setOnKeyDown('V', KeyVPressed); + wnd->setOnKeyDown('u', KeyuPressed); + wnd->setOnKeyDown('U', KeyUPressed); } // Vec2Scalar is VecLength diff --git a/lib/vsvector3d.cpp b/lib/vsvector3d.cpp index 11c41af1..c9f99f90 100644 --- a/lib/vsvector3d.cpp +++ b/lib/vsvector3d.cpp @@ -414,32 +414,31 @@ void VisualizationSceneVector3d::Init() // static int init = 0; // if (!init) - if (sdl_wnd) { // init = 1; - sdl_wnd->setOnKeyDown('d', KeyDPressed); - sdl_wnd->setOnKeyDown('D', KeyDPressed); + wnd->setOnKeyDown('d', KeyDPressed); + wnd->setOnKeyDown('D', KeyDPressed); - sdl_wnd->setOnKeyDown('n', KeyNPressed); - sdl_wnd->setOnKeyDown('N', KeyNPressed); + wnd->setOnKeyDown('n', KeyNPressed); + wnd->setOnKeyDown('N', KeyNPressed); - sdl_wnd->setOnKeyDown('b', KeyBPressed); - sdl_wnd->setOnKeyDown('B', KeyBPressed); + wnd->setOnKeyDown('b', KeyBPressed); + wnd->setOnKeyDown('B', KeyBPressed); - sdl_wnd->setOnKeyDown('r', KeyrPressed); // adds another function to 'r' and 'R' - sdl_wnd->setOnKeyDown('R', KeyRPressed); // the other function is in vsdata.cpp + wnd->setOnKeyDown('r', KeyrPressed); // adds another function to 'r' and 'R' + wnd->setOnKeyDown('R', KeyRPressed); // the other function is in vsdata.cpp - sdl_wnd->setOnKeyDown('u', KeyuPressed); // Keys u, U are also used in - sdl_wnd->setOnKeyDown('U', KeyUPressed); // VisualizationSceneSolution3d + wnd->setOnKeyDown('u', KeyuPressed); // Keys u, U are also used in + wnd->setOnKeyDown('U', KeyUPressed); // VisualizationSceneSolution3d - sdl_wnd->setOnKeyDown('w', KeywPressed); // Keys w, W are also used in - sdl_wnd->setOnKeyDown('W', KeyWPressed); // VisualizationSceneSolution3d + wnd->setOnKeyDown('w', KeywPressed); // Keys w, W are also used in + wnd->setOnKeyDown('W', KeyWPressed); // VisualizationSceneSolution3d - sdl_wnd->setOnKeyDown('v', KeyvPressed); // Keys v, V are also used in - sdl_wnd->setOnKeyDown('V', KeyVPressed); // VisualizationSceneSolution3d + wnd->setOnKeyDown('v', KeyvPressed); // Keys v, V are also used in + wnd->setOnKeyDown('V', KeyVPressed); // VisualizationSceneSolution3d - sdl_wnd->setOnKeyDown('F', VectorKeyFPressed); + wnd->setOnKeyDown('F', VectorKeyFPressed); } } diff --git a/lib/window.cpp b/lib/window.cpp index 31ffd174..1104077a 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -69,9 +69,9 @@ bool Window::GLVisInitVis(StreamCollection input_streams, bool headless) locwin = this; - if (!headless && data_state.quad_f) + if (data_state.quad_f) { - GetAppWindow()->setOnKeyDown('Q', SwitchQuadSolution); + GetGLWindow()->setOnKeyDown('Q', SwitchQuadSolution); } double mesh_range = -1.0; From 2ac54300e6adf19f85a35968034e820c3585bbe8 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 12 May 2025 23:51:22 -0700 Subject: [PATCH 08/79] Improved conformity of GLWindow and removed references to SdlWindow. --- lib/aux_js.cpp | 6 +++--- lib/aux_vis.cpp | 4 ++-- lib/aux_vis.hpp | 4 ++-- lib/font.cpp | 4 ++-- lib/glwindow.hpp | 8 ++++++++ lib/openglvis.cpp | 6 +++--- lib/openglvis.hpp | 3 +-- lib/script_controller.cpp | 2 +- lib/sdl.hpp | 9 --------- lib/threads.cpp | 4 ++-- lib/vsdata.cpp | 7 +++---- lib/vssolution.hpp | 1 - lib/window.cpp | 2 +- 13 files changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 831d3c76..e3a14f56 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -44,7 +44,7 @@ using namespace mfem; void display(std::stringstream & commands, const int w, const int h) { // reset antialiasing - GetGLWindow()->getRenderer().setAntialiasing(0); + GetAppWindow()->getRenderer().setAntialiasing(0); std::string word; double minv = 0.0, maxv = 0.0; @@ -274,7 +274,7 @@ std::string getHelpString() em::val getScreenBuffer(bool flip_y=false) { MyExpose(); - auto * wnd = GetGLWindow(); + auto * wnd = GetAppWindow(); int w, h; wnd->getGLDrawSize(w, h); @@ -317,7 +317,7 @@ em::val getScreenBuffer(bool flip_y=false) em::val getPNGByteArray() { constexpr const char * filename = "im.png"; - auto * wnd = GetGLWindow(); + auto * wnd = GetAppWindow(); int w, h; wnd->getGLDrawSize(w, h); diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 6d1e486a..64a9bf6a 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -64,12 +64,12 @@ void SetGLVisCommand(GLVisCommand *cmd) glvis_command = cmd; } -SdlWindow * GetAppWindow() +SdlWindow * GetSdlWindow() { return sdl_wnd; } -GLWindow *GetGLWindow() +GLWindow *GetAppWindow() { return wnd; } diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 67c01d4a..2d3e4221 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -46,8 +46,8 @@ void MyExpose(); void MainLoop(); -SdlWindow* GetAppWindow(); -GLWindow* GetGLWindow(); +SdlWindow* GetSdlWindow(); +GLWindow* GetAppWindow(); VisualizationScene * GetVisualizationScene(); void SetLegacyGLOnly(bool status); diff --git a/lib/font.cpp b/lib/font.cpp index 01895e70..74ca1bdb 100644 --- a/lib/font.cpp +++ b/lib/font.cpp @@ -44,12 +44,12 @@ bool GlVisFont::LoadFont(const std::string& path, int font_index, int font_size) } face_has_kerning = FT_HAS_KERNING(face); int ppi_w, ppi_h; - GetGLWindow()->getDpi(ppi_w, ppi_h); + GetAppWindow()->getDpi(ppi_w, ppi_h); const bool use_fixed_ppi_h = true; if (use_fixed_ppi_h) { double ratio = double(ppi_w)/ppi_h; - ppi_h = GetGLWindow()->isHighDpi() ? 192 : 96; + ppi_h = GetAppWindow()->isHighDpi() ? 192 : 96; ppi_w = ratio*ppi_h + 0.5; #ifdef GLVIS_DEBUG cout << "Fonts use fixed ppi: " << ppi_w << " x " << ppi_h << endl; diff --git a/lib/glwindow.hpp b/lib/glwindow.hpp index 3e64a213..5432e29f 100644 --- a/lib/glwindow.hpp +++ b/lib/glwindow.hpp @@ -55,6 +55,9 @@ class GLWindow std::map onMouseUp; std::map onMouseMove; +#ifdef __EMSCRIPTEN__ + std::string canvas_id; +#endif std::string saved_keys; bool initGLEW(bool legacyGlOnly); @@ -161,6 +164,11 @@ class GLWindow /// Saves a screenshot ot the file, performing conversion optionally virtual void screenshot(std::string filename, bool convert = false) { } + +#ifdef __EMSCRIPTEN__ + std::string getCanvasId() const { return canvas_id; } + void setCanvasId(std::string canvas_id_) { canvas_id = canvas_id_; } +#endif }; #endif //GLVIS_GLWINDOW_HPP diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index 89513a23..c5981a75 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -130,7 +130,7 @@ VisualizationScene::VisualizationScene() cut_updated = false; background = BG_WHITE; - GetGLWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); + GetAppWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); _use_cust_l0_pos = false; light_mat_idx = 3; use_light = true; @@ -1068,12 +1068,12 @@ void VisualizationScene::ToggleBackground() if (background == BG_BLK) { background = BG_WHITE; - GetGLWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); + GetAppWindow()->getRenderer().setClearColor(1.f, 1.f, 1.f, 1.f); } else { background = BG_BLK; - GetGLWindow()->getRenderer().setClearColor(0.f, 0.f, 0.f, 1.f); + GetAppWindow()->getRenderer().setClearColor(0.f, 0.f, 0.f, 1.f); } } diff --git a/lib/openglvis.hpp b/lib/openglvis.hpp index 1cc16ac4..279545cc 100644 --- a/lib/openglvis.hpp +++ b/lib/openglvis.hpp @@ -18,7 +18,7 @@ #include "palettes.hpp" #include "mfem.hpp" #include "geom_utils.hpp" -#include "sdl.hpp" +#include "glwindow.hpp" #include "gltf.hpp" // Visualization header file @@ -66,7 +66,6 @@ class VisualizationScene double xscale, yscale, zscale; GLWindow *wnd; - SdlWindow *sdl_wnd; glm::mat4 proj_mtx; diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index 888853a2..c3b8bc06 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -370,7 +370,7 @@ bool ScriptController::ExecuteScriptCommand() scr_level = 0; if (headless) { - GetGLWindow()->signalQuit(); + GetAppWindow()->signalQuit(); } return false; } diff --git a/lib/sdl.hpp b/lib/sdl.hpp index e2e8ec2b..753a8f9d 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -85,10 +85,6 @@ class SdlWindow : public GLWindow TouchDelegate onTouchPinch{nullptr}; TouchDelegate onTouchRotate{nullptr}; -#ifdef __EMSCRIPTEN__ - std::string canvas_id_; -#endif - bool update_before_expose{false}; // bool requiresExpose; @@ -191,11 +187,6 @@ class SdlWindow : public GLWindow bool isWindowInitialized() const override { return handle.isInitialized(); } /// Returns true if the OpenGL context was successfully initialized. bool isGlInitialized() const override; - -#ifdef __EMSCRIPTEN__ - std::string getCanvasId() const { return canvas_id_; } - void setCanvasId(std::string canvas_id) { canvas_id_ = canvas_id; } -#endif }; #endif diff --git a/lib/threads.cpp b/lib/threads.cpp index c6627396..0d55180e 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -24,7 +24,7 @@ GLVisCommand::GLVisCommand(Window &win_) : win(win_) { // should be set in this thread by a call to InitVisualization() - thread_wnd = GetGLWindow(); + thread_wnd = GetAppWindow(); num_waiting = 0; terminating = false; @@ -500,7 +500,7 @@ int GLVisCommand::Execute() cout << "Command: screenshot -> " << screenshot_filename << endl; // Allow SdlWindow to handle the expose and screenshot action, in case // any actions need to be taken before MyExpose(). - GetGLWindow()->screenshot(screenshot_filename, true); + GetAppWindow()->screenshot(screenshot_filename, true); break; } diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp index 0beb2cf4..da92a51d 100644 --- a/lib/vsdata.cpp +++ b/lib/vsdata.cpp @@ -933,8 +933,8 @@ void KeyKPressed() void KeyAPressed() { - bool curr_aa = GetGLWindow()->getRenderer().getAntialiasing(); - GetGLWindow()->getRenderer().setAntialiasing(!curr_aa); + bool curr_aa = GetAppWindow()->getRenderer().getAntialiasing(); + GetAppWindow()->getRenderer().setAntialiasing(!curr_aa); cout << "Multisampling/Antialiasing: " << strings_off_on[!curr_aa ? 1 : 0] << endl; @@ -1371,8 +1371,7 @@ void VisualizationSceneScalarData::Init() { vsdata = this; window = &win; - wnd = GetGLWindow(); - sdl_wnd = GetAppWindow(); + wnd = GetAppWindow(); arrow_type = arrow_scaling_type = 0; scaling = 0; diff --git a/lib/vssolution.hpp b/lib/vssolution.hpp index b41be912..00d05a08 100644 --- a/lib/vssolution.hpp +++ b/lib/vssolution.hpp @@ -15,7 +15,6 @@ #include "mfem.hpp" using namespace mfem; -#include "sdl.hpp" #include "gl/types.hpp" #include "vsdata.hpp" diff --git a/lib/window.cpp b/lib/window.cpp index 1104077a..a2b4b41c 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -71,7 +71,7 @@ bool Window::GLVisInitVis(StreamCollection input_streams, bool headless) if (data_state.quad_f) { - GetGLWindow()->setOnKeyDown('Q', SwitchQuadSolution); + GetAppWindow()->setOnKeyDown('Q', SwitchQuadSolution); } double mesh_range = -1.0; From 1bd3662f06f094c8c33f1d0ba9194eca8c364771 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 13 May 2025 00:17:28 -0700 Subject: [PATCH 09/79] Minor fixup in EglWindow. --- lib/egl.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/egl.cpp b/lib/egl.cpp index f1695ee8..a4dbfb83 100644 --- a/lib/egl.cpp +++ b/lib/egl.cpp @@ -13,7 +13,6 @@ #include "aux_vis.hpp" #include #include -#include #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s @@ -57,8 +56,10 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, const int multisamples = GetMultisample(); - std::vector configAttribs = + EGLint configAttribs[] = { + EGL_SAMPLE_BUFFERS, (multisamples > 0)?(1):(0), // must be first + EGL_SAMPLES, multisamples, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, EGL_BLUE_SIZE, 8, @@ -68,8 +69,6 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, EGL_DEPTH_SIZE, 24, EGL_CONFORMANT, EGL_OPENGL_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_SAMPLE_BUFFERS, (multisamples > 0)?(1):(0), - EGL_SAMPLES, multisamples, EGL_NONE }; @@ -77,19 +76,17 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, if (multisamples > 0) { - if (!eglChooseConfig(disp, configAttribs.data(), NULL, 0, &numConfigs) || + if (!eglChooseConfig(disp, configAttribs, NULL, 0, &numConfigs) || numConfigs < 1) { std::cerr << "EGL with multisampling is not supported, turning it off" << std::endl; // turn off multisampling - auto it = std::find(configAttribs.begin(), configAttribs.end(), - EGL_SAMPLE_BUFFERS); - *(++it) = 0; + configAttribs[1] = 0; } } - if (!eglChooseConfig(disp, configAttribs.data(), &eglCfg, 1, &numConfigs) || + if (!eglChooseConfig(disp, configAttribs, &eglCfg, 1, &numConfigs) || numConfigs < 1) { std::cerr << "Cannot find working EGL configuration!" << std::endl; @@ -134,7 +131,7 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, if (ctx == EGL_NO_CONTEXT) { PRINT_DEBUG("failed." << std::endl); - PRINT_DEBUG("Opening OpenGL core profile context..." << std::flush); + PRINT_DEBUG("Opening OpenGL compatibility profile context..." << std::flush); const EGLint attrListCompat[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, From aa2086c59b5bbfbc6338a0947d0f43406a420c78 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 13 May 2025 01:22:50 -0700 Subject: [PATCH 10/79] Unified handling of screenshot between scripts and commands. --- lib/script_controller.cpp | 15 ++++----------- lib/threads.cpp | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index c3b8bc06..b30ecb9a 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -505,17 +505,10 @@ bool ScriptController::ExecuteScriptCommand() { scr >> ws >> word; - cout << "Script: screenshot: " << flush; - - //perform expose before screenshot to read from the back buffer - MyExpose(); - if (Screenshot(word.c_str(), true)) - { - cout << "Screenshot(" << word << ") failed." << endl; - done_one_command = 1; - continue; - } - cout << "-> " << word << endl; + cout << "Script: screenshot: -> " << word << endl; + // Allow GlWindow to handle the expose and screenshot action, in case + // any actions need to be taken before MyExpose(). + GetAppWindow()->screenshot(word, true); if (scr_min_val > win.vs->GetMinV()) { diff --git a/lib/threads.cpp b/lib/threads.cpp index 0d55180e..a0853e62 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -498,7 +498,7 @@ int GLVisCommand::Execute() case SCREENSHOT: { cout << "Command: screenshot -> " << screenshot_filename << endl; - // Allow SdlWindow to handle the expose and screenshot action, in case + // Allow GlWindow to handle the expose and screenshot action, in case // any actions need to be taken before MyExpose(). GetAppWindow()->screenshot(screenshot_filename, true); break; From f9332e9bac7c077bf6883c7d5a77256e5963b789 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 13 May 2025 01:44:04 -0700 Subject: [PATCH 11/79] Added headless command option working with scripts and saved streams. --- glvis.cpp | 45 ++++++++++++++++++++++++++++----------- lib/script_controller.cpp | 17 ++++++++------- lib/script_controller.hpp | 1 - lib/window.cpp | 3 ++- lib/window.hpp | 3 ++- 5 files changed, 46 insertions(+), 23 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 7ef13259..01dca84f 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -93,7 +93,7 @@ class Session inline DataState& GetState() { return win.data_state; } inline const DataState& GetState() const { return win.data_state; } - void StartSession() + void StartSession(bool detached = true) { auto funcThread = [](Window w, StreamCollection is) { @@ -104,10 +104,13 @@ class Session }; handler = std::thread {funcThread, std::move(win), std::move(input_streams)}; - handler.detach(); + if (detached) + { + handler.detach(); + } } - bool StartSavedSession(std::string stream_file) + bool StartSavedSession(std::string stream_file, bool detached = true) { unique_ptr ifs(new ifstream(stream_file)); if (!(*ifs)) @@ -121,7 +124,7 @@ class Session reader.ReadStream(*ifs, data_type); input_streams.emplace_back(std::move(ifs)); - StartSession(); + StartSession(detached); return true; } @@ -148,6 +151,10 @@ class Session return 0; } + void WaitForSession() + { + handler.join(); + } }; void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient, @@ -459,6 +466,9 @@ int main (int argc, char *argv[]) "Set the window height."); args.AddOption(&win.window_title, "-wt", "--window-title", "Set the window title."); + args.AddOption(&win.headless, "-hl", "--headless", + "-no-hl", "--no-headless", + "Start headless (no GUI) visualization."); args.AddOption(&c_plot_caption, "-c", "--plot-caption", "Set the plot caption (visible when colorbar is visible)."); args.AddOption(&font_name, "-fn", "--font", @@ -591,20 +601,31 @@ int main (int argc, char *argv[]) // check for saved stream file if (stream_file != string_none) { - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. - GetMainThread(); + // backup the headless flag as the window is moved + const bool headless = win.headless; - Session stream_session(win.data_state.fix_elem_orient, - win.data_state.save_coloring, - win.plot_caption); + if (!headless) + { + // Make sure the singleton object returned by GetMainThread() is + // initialized from the main thread. + GetMainThread(); + } + + Session stream_session(std::move(win)); - if (!stream_session.StartSavedSession(stream_file)) + if (!stream_session.StartSavedSession(stream_file, !headless)) { return 1; } - SDLMainLoop(); + if (!headless) + { + SDLMainLoop(); + } + else + { + stream_session.WaitForSession(); + } return 0; } diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index b30ecb9a..9944e85a 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -368,7 +368,7 @@ bool ScriptController::ExecuteScriptCommand() { cout << "End of script." << endl; scr_level = 0; - if (headless) + if (win.headless) { GetAppWindow()->signalQuit(); } @@ -832,7 +832,7 @@ thread_local ScriptController *ScriptController::script_ctrl = NULL; void ScriptController::ScriptIdleFunc() { script_ctrl->ExecuteScriptCommand(); - if (script_ctrl->scr_level == 0 && !script_ctrl->headless) + if (script_ctrl->scr_level == 0 && !script_ctrl->win.headless) { ScriptControl(); } @@ -894,7 +894,7 @@ void ScriptController::PlayScript(Window win, istream &scr) script.win.window_h; break; case Command::Headless: - script.headless = true; + script.win.headless = true; break; case Command::DataCollCycle: scr >> script.dc_cycle; @@ -968,12 +968,13 @@ void ScriptController::PlayScript(Window win, istream &scr) script.script = &scr; script.win.data_state.keys.clear(); - const bool headless = script.headless; + // backup the headless flag as the window is moved + const bool headless = script.win.headless; - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. if (!headless) { + // Make sure the singleton object returned by GetMainThread() is + // initialized from the main thread. GetMainThread(); } @@ -983,9 +984,9 @@ void ScriptController::PlayScript(Window win, istream &scr) [&](ScriptController local_script) { script_ctrl = &local_script; - if (local_script.win.GLVisInitVis({}, local_script.headless)) + if (local_script.win.GLVisInitVis({})) { - if (!local_script.headless) + if (!local_script.win.headless) { GetAppWindow()->setOnKeyDown(SDLK_SPACE, ScriptControl); } diff --git a/lib/script_controller.hpp b/lib/script_controller.hpp index 8411e485..a16348a4 100644 --- a/lib/script_controller.hpp +++ b/lib/script_controller.hpp @@ -24,7 +24,6 @@ class ScriptController { Window win; - bool headless = false; string dc_protocol = string_default; int dc_cycle = 0; diff --git a/lib/window.cpp b/lib/window.cpp index a2b4b41c..2a588537 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -23,6 +23,7 @@ Window &Window::operator=(Window &&w) window_w = w.window_w; window_h = w.window_h; window_title = w.window_title; + headless = w.headless; plot_caption = std::move(w.plot_caption); extra_caption = std::move(w.extra_caption); @@ -30,7 +31,7 @@ Window &Window::operator=(Window &&w) } // Visualize the data in the global variables mesh, sol/grid_f, etc -bool Window::GLVisInitVis(StreamCollection input_streams, bool headless) +bool Window::GLVisInitVis(StreamCollection input_streams) { DataState::FieldType field_type = data_state.GetType(); diff --git a/lib/window.hpp b/lib/window.hpp index 06cc73f2..7e7fda2e 100644 --- a/lib/window.hpp +++ b/lib/window.hpp @@ -45,6 +45,7 @@ struct Window int window_w = 400; int window_h = 350; const char *window_title = string_default; + bool headless = false; std::string plot_caption; std::string extra_caption; @@ -53,7 +54,7 @@ struct Window Window& operator=(Window &&w); /// Visualize the data in the global variables mesh, sol/grid_f, etc - bool GLVisInitVis(StreamCollection input_streams, bool headless = false); + bool GLVisInitVis(StreamCollection input_streams); void GLVisStartVis(); /// Switch the quadrature function representation and update the visualization From b828af17fb3529e6373ef359847f62e2eb0958da Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 13 May 2025 02:23:56 -0700 Subject: [PATCH 12/79] Added automatic closing after the end of stream for headless runs. --- lib/threads.cpp | 29 +++++++++++++++++++++++++++-- lib/threads.hpp | 10 ++++++++-- lib/window.cpp | 2 +- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/threads.cpp b/lib/threads.cpp index a0853e62..7c704558 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -438,6 +438,20 @@ int GLVisCommand::Autopause(const char *mode) return 0; } +int GLVisCommand::Quit() +{ + if (lock() < 0) + { + return -1; + } + command = QUIT; + if (signal() < 0) + { + return -2; + } + return 0; +} + int GLVisCommand::Execute() { if (!command_ready) @@ -753,6 +767,11 @@ int GLVisCommand::Execute() break; } + case QUIT: + { + GetAppWindow()->signalQuit(); + break; + } } command = NO_COMMAND; @@ -877,8 +896,9 @@ ThreadCommands::ThreadCommands() } communication_thread::communication_thread(StreamCollection _is, - GLVisCommand* cmd) - : is(std::move(_is)), glvis_command(cmd) + GLVisCommand* cmd, + bool end_quit_) + : is(std::move(_is)), glvis_command(cmd), end_quit(end_quit_) { new_m = NULL; new_g = NULL; @@ -1475,6 +1495,11 @@ void communication_thread::execute() cout << "Stream: end of input." << endl; + if (end_quit) + { + glvis_command->Quit(); + } + comm_terminate: for (size_t i = 0; i < is.size(); i++) { diff --git a/lib/threads.hpp b/lib/threads.hpp index f1be77b4..d3707a1c 100644 --- a/lib/threads.hpp +++ b/lib/threads.hpp @@ -56,7 +56,8 @@ class GLVisCommand PALETTE_REPEAT = 20, LEVELLINES = 21, AXIS_NUMBERFORMAT = 22, - COLORBAR_NUMBERFORMAT = 23 + COLORBAR_NUMBERFORMAT = 23, + QUIT = 24, }; std::atomic command_ready{false}; @@ -129,6 +130,7 @@ class GLVisCommand int ColorbarNumberFormat(string formatting); int Camera(const double cam[]); int Autopause(const char *mode); + int Quit(); // called by the main execution thread int Execute(); @@ -163,11 +165,15 @@ class communication_thread // signal for thread cancellation std::atomic terminate_thread {false}; + // flag for closing the window at the end of stream + bool end_quit; + static void print_commands(); void execute(); public: - communication_thread(StreamCollection _is, GLVisCommand* cmd); + communication_thread(StreamCollection _is, GLVisCommand* cmd, + bool end_quit = false); ~communication_thread(); }; diff --git a/lib/window.cpp b/lib/window.cpp index 2a588537..ec3c0467 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -65,7 +65,7 @@ bool Window::GLVisInitVis(StreamCollection input_streams) internal.glvis_command.reset(new GLVisCommand(*this)); SetGLVisCommand(glvis_command.get()); internal.comm_thread.reset(new communication_thread(std::move(input_streams), - glvis_command.get())); + glvis_command.get(), headless)); } locwin = this; From a513701e19f32bb939592921288f235402537638 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 13 May 2025 03:03:51 -0700 Subject: [PATCH 13/79] Added cmd line headless mode. --- glvis.cpp | 23 ++++++++++++++++++----- lib/egl.cpp | 7 ++++++- lib/egl.hpp | 1 - 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 01dca84f..679aca62 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -797,14 +797,27 @@ int main (int argc, char *argv[]) if (ierr) { exit(ierr); } } - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. - GetMainThread(); + // backup the headless flag as the window is moved + const bool headless = win.headless; + + if (!headless) + { + // Make sure the singleton object returned by GetMainThread() is + // initialized from the main thread. + GetMainThread(); + } Session single_session(std::move(win)); - single_session.StartSession(); + single_session.StartSession(!headless); - SDLMainLoop(); + if (!headless) + { + SDLMainLoop(); + } + else + { + single_session.WaitForSession(); + } } cout << "Thank you for using GLVis." << endl; diff --git a/lib/egl.cpp b/lib/egl.cpp index a4dbfb83..b500b6a6 100644 --- a/lib/egl.cpp +++ b/lib/egl.cpp @@ -238,6 +238,11 @@ void EglWindow::mainIter() } break; case EventType::Screenshot: + if (isExposePending()) + { + onExpose(); + wnd_state = RenderState::Updated; + } Screenshot(screenshot_filename.c_str(), e.event.screenshot.convert); break; case EventType::Quit: @@ -257,7 +262,7 @@ void EglWindow::mainIter() sleep = true; } - if (wnd_state == RenderState::ExposePending) + if (isExposePending()) { onExpose(); wnd_state = RenderState::Updated; diff --git a/lib/egl.hpp b/lib/egl.hpp index ef726802..0ab89ad1 100644 --- a/lib/egl.hpp +++ b/lib/egl.hpp @@ -98,7 +98,6 @@ class EglWindow : public GLWindow // as there is no swap, switch to updated state right away void signalSwap() override { wnd_state = RenderState::Updated; } - bool isExposePending() const override { return false; } // used in Screenshot, as there is no swapping, the single buffer is always // up to date and can be read directly bool isSwapPending() const override { return true; } From 7dd72ecee636fad5641f7cdb0b680a08003b39e8 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 13 May 2025 21:31:07 -0700 Subject: [PATCH 14/79] Changed HiDPI of EGL to the cmd-line set option. --- lib/aux_vis.cpp | 7 ++++++- lib/aux_vis.hpp | 2 ++ lib/egl.cpp | 5 +++++ lib/egl.hpp | 2 +- lib/sdl_main.cpp | 4 ++-- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 64a9bf6a..9394d1de 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -52,7 +52,7 @@ static float line_w_aa = gl3::LINE_WIDTH_AA; static thread_local GLWindow *wnd = nullptr; static thread_local SdlWindow *sdl_wnd = nullptr; static bool wndLegacyGl = false; -bool wndUseHiDPI = true; // shared with sdl_main.cpp +static bool wndUseHiDPI = true; void SDLMainLoop(bool server_mode) { @@ -89,6 +89,11 @@ void SetUseHiDPI(bool status) wndUseHiDPI = status; } +bool GetUseHiDPI() +{ + return wndUseHiDPI; +} + void MyExpose(GLsizei w, GLsizei h); void MyExpose(); diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 2d3e4221..55bf9776 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -143,6 +143,8 @@ bool SetFont(const vector& patterns, int height); void SetFont(const std::string& fn); void SetUseHiDPI(bool status); +bool GetUseHiDPI(); + function NumberFormatter(int precision=4, char format='d', bool showsign=false); function NumberFormatter(string formatting); diff --git a/lib/egl.cpp b/lib/egl.cpp index b500b6a6..92b4e402 100644 --- a/lib/egl.cpp +++ b/lib/egl.cpp @@ -297,6 +297,11 @@ void EglWindow::getGLDrawSize(int& w, int& h) const h = egl_h; } +bool EglWindow::isHighDpi() const +{ + return GetUseHiDPI(); +} + void EglWindow::setWindowSize(int w, int h) { const EGLint pbufferAttribs[] = diff --git a/lib/egl.hpp b/lib/egl.hpp index 0ab89ad1..cb5a83a9 100644 --- a/lib/egl.hpp +++ b/lib/egl.hpp @@ -86,7 +86,7 @@ class EglWindow : public GLWindow // use the default values of SdlWindow, LoadFont() ignores them anyway void getDpi(int& wdpi, int& hdpi) const override { wdpi = hdpi = 72; } - bool isHighDpi() const override { return true; } + bool isHighDpi() const override; void setWindowSize(int w, int h) override; diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp index d9a6941a..442b8bc7 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl_main.cpp @@ -28,7 +28,7 @@ #endif extern int GetMultisample(); -extern bool wndUseHiDPI; +extern bool GetUseHiDPI(); struct SdlMainThread::CreateWindowCmd { @@ -616,7 +616,7 @@ void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd) #ifndef __EMSCRIPTEN__ win_flags |= SDL_WINDOW_RESIZABLE; #endif - if (wndUseHiDPI) + if (GetUseHiDPI()) { win_flags |= SDL_WINDOW_ALLOW_HIGHDPI; } From f900537a19f6e96e6fa9dc6c56fe02360d4510cc Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 13 May 2025 22:37:18 -0700 Subject: [PATCH 15/79] After-merge fixup. --- lib/aux_vis.cpp | 3 ++- lib/aux_vis.hpp | 3 ++- lib/openglvis.cpp | 2 +- lib/openglvis.hpp | 2 +- lib/script_controller.cpp | 4 ++-- lib/threads.cpp | 2 +- lib/window.hpp | 6 +++--- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 501dfb82..21091b16 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -97,7 +97,8 @@ bool GetUseHiDPI() void MyExpose(GLsizei w, GLsizei h); void MyExpose(); -SdlWindow* InitVisualization(const char name[], int x, int y, int w, int h, bool headless) +GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, + bool headless) { #ifdef GLVIS_DEBUG if (!headless) diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 19ef59a6..210862e6 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -27,7 +27,8 @@ class GLVisCommand; void SetGLVisCommand(GLVisCommand *cmd); /// Initializes the visualization and some keys. -SdlWindow* InitVisualization(const char name[], int x, int y, int w, int h, bool headless = false); +GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, + bool headless = false); void SetVisualizationScene(VisualizationScene * scene, int view = 3, const char *keys = NULL); diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index 6d131896..e0e9cadd 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -112,7 +112,7 @@ void Camera::Print() << std::endl; } -VisualizationScene::VisualizationScene(SdlWindow &wnd_) +VisualizationScene::VisualizationScene(GLWindow &wnd_) { wnd = &wnd_; diff --git a/lib/openglvis.hpp b/lib/openglvis.hpp index 99b5253e..5c371063 100644 --- a/lib/openglvis.hpp +++ b/lib/openglvis.hpp @@ -171,7 +171,7 @@ class VisualizationScene const gl3::GlDrawable &gl_drawable); public: - VisualizationScene(SdlWindow &wnd); + VisualizationScene(GLWindow &wnd); virtual ~VisualizationScene(); int spinning, OrthogonalProjection, print, movie; diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index 571ffcb0..d3806e72 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -370,7 +370,7 @@ bool ScriptController::ExecuteScriptCommand() scr_level = 0; if (win.headless) { - GetAppWindow()->signalQuit(); + win.wnd->signalQuit(); } return false; } @@ -508,7 +508,7 @@ bool ScriptController::ExecuteScriptCommand() cout << "Script: screenshot: -> " << word << endl; // Allow GlWindow to handle the expose and screenshot action, in case // any actions need to be taken before MyExpose(). - GetAppWindow()->screenshot(word, true); + win.wnd->screenshot(word, true); if (scr_min_val > win.vs->GetMinV()) { diff --git a/lib/threads.cpp b/lib/threads.cpp index 759c9f84..c53717cf 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -769,7 +769,7 @@ int GLVisCommand::Execute() case QUIT: { - GetAppWindow()->signalQuit(); + thread_wnd->signalQuit(); break; } } diff --git a/lib/window.hpp b/lib/window.hpp index 02c3c3d2..5608c9c5 100644 --- a/lib/window.hpp +++ b/lib/window.hpp @@ -17,7 +17,7 @@ #include "data_state.hpp" #include "stream_reader.hpp" -class SdlWindow; +class GLWindow; class VisualizationSceneScalarData; class communication_thread; class GLVisCommand; @@ -30,7 +30,7 @@ struct Window private: struct { - std::unique_ptr wnd; + std::unique_ptr wnd; std::unique_ptr vs; std::unique_ptr comm_thread; std::unique_ptr glvis_command; @@ -38,7 +38,7 @@ struct Window public: DataState data_state; - const std::unique_ptr &wnd{internal.wnd}; + const std::unique_ptr &wnd{internal.wnd}; const std::unique_ptr &vs{internal.vs}; const std::unique_ptr &comm_thread{internal.comm_thread}; const std::unique_ptr &glvis_command{internal.glvis_command}; From 50bcdb0d11031fd036fce80e711c4b8589ecf194 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 13 May 2025 23:38:52 -0700 Subject: [PATCH 16/79] Implemented proper build options for EGL. --- CMakeLists.txt | 16 ++++++++++++++++ lib/aux_vis.cpp | 5 +++++ lib/egl.cpp | 3 +++ lib/egl.hpp | 2 ++ makefile | 10 +++++++++- 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6c903c3..3234f7dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,10 @@ option(GLVIS_USE_LIBPNG "Use libpng for taking screenshots internally" ON) +option(GLVIS_USE_EGL + "Use EGL for headless rendering" + OFF) + # # Handle a few other definitions # @@ -227,6 +231,18 @@ if (NOT EMSCRIPTEN) message(FATAL_ERROR "Fontconfig not found.") endif (FONTCONFIG_LIBRARY) + # Find EGL + if (GLVIS_USE_EGL) + find_package(OpenGL OPTIONAL_COMPONENTS EGL) + if (OpenGL_EGL_FOUND) + list(APPEND _glvis_compile_defs "GLVIS_USE_EGL") + list(APPEND _glvis_libraries OpenGL::EGL) + else() + message(WARNING "EGL library not found. EGL disabled.") + set(GLVIS_USE_EGL OFF) + endif (OpenGL_EGL_FOUND) + endif (GLVIS_USE_EGL) + # Find threading library find_package(Threads REQUIRED) list(APPEND _glvis_libraries "${CMAKE_THREAD_LIBS_INIT}") diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 21091b16..0873422f 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -130,6 +130,7 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, } else { +#ifdef GLVIS_USE_EGL sdl_wnd = nullptr; if (!wnd) { @@ -145,6 +146,10 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, { wnd->clearEvents(); } +#else //GLVIS_USE_EGL + cerr << "EGL is required for headless rendering!" << endl; + return NULL; +#endif //GLVIS_USE_EGL } #ifdef GLVIS_DEBUG diff --git a/lib/egl.cpp b/lib/egl.cpp index 92b4e402..a9aca599 100644 --- a/lib/egl.cpp +++ b/lib/egl.cpp @@ -9,6 +9,7 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. +#ifdef GLVIS_USE_EGL #include "egl.hpp" #include "aux_vis.hpp" #include @@ -352,3 +353,5 @@ void EglWindow::screenshot(std::string filename, bool convert) // Queue up an expose, so Screenshot() can pull image from updated buffer signalExpose(); } + +#endif //GLVIS_USE_EGL diff --git a/lib/egl.hpp b/lib/egl.hpp index cb5a83a9..27c41557 100644 --- a/lib/egl.hpp +++ b/lib/egl.hpp @@ -11,6 +11,7 @@ #ifndef GLVIS_EGL_HPP #define GLVIS_EGL_HPP +#ifdef GLVIS_USE_EGL #include "glwindow.hpp" @@ -105,4 +106,5 @@ class EglWindow : public GLWindow void screenshot(std::string filename, bool convert = false) override; }; +#endif //GLVIS_USE_EGL #endif //GLVIS_EGL_HPP \ No newline at end of file diff --git a/makefile b/makefile index ed50fda9..6531047c 100644 --- a/makefile +++ b/makefile @@ -223,7 +223,6 @@ EMCC_OPTS += $(if $(GLM_DIR),-I$(GLM_DIR)) GLVIS_FLAGS += $(GL_OPTS) GLVIS_LIBS += $(GL_LIBS) -GLVIS_LIBS += -lEGL # Take screenshots internally with libtiff, libpng, or sdl2? GLVIS_USE_LIBTIFF ?= NO @@ -242,6 +241,15 @@ else # no flag --> SDL screenshots endif +# EGL headless rendering +GLVIS_USE_EGL ?= NO +EGL_OPTS = -DGLVIS_USE_EGL +EGL_LIBS = -lEGL +ifeq ($(GLVIS_USE_EGL),YES) + GLVIS_FLAGS += $(EGL_OPTS) + GLVIS_LIBS += $(EGL_LIBS) +endif + PTHREAD_LIB = -lpthread GLVIS_LIBS += $(PTHREAD_LIB) From 92141279753ddc71f1b45f1e40155c9a948d199c Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 00:27:16 -0700 Subject: [PATCH 17/79] Moved EGL and SDL files to respective subfolders. --- glvis.cpp | 1 + lib/CMakeLists.txt | 30 +++++++++++----------- lib/aux_vis.cpp | 4 +-- lib/aux_vis.hpp | 3 ++- lib/{ => egl}/egl.cpp | 2 +- lib/{ => egl}/egl.hpp | 2 +- lib/script_controller.cpp | 1 + lib/{ => sdl}/sdl.cpp | 2 +- lib/{ => sdl}/sdl.hpp | 2 +- lib/{ => sdl}/sdl_helper.cpp | 0 lib/{ => sdl}/sdl_helper.hpp | 2 +- lib/{ => sdl}/sdl_mac.hpp | 0 lib/{ => sdl}/sdl_mac.mm | 0 lib/{ => sdl}/sdl_main.cpp | 2 +- lib/{ => sdl}/sdl_main.hpp | 0 lib/{ => sdl}/sdl_windows.cpp | 0 lib/{ => sdl}/sdl_windows.hpp | 0 lib/{ => sdl}/sdl_x11.cpp | 0 lib/{ => sdl}/sdl_x11.hpp | 0 makefile | 48 ++++++++++++++++++----------------- 20 files changed, 52 insertions(+), 47 deletions(-) rename lib/{ => egl}/egl.cpp (99%) rename lib/{ => egl}/egl.hpp (99%) rename lib/{ => sdl}/sdl.cpp (99%) rename lib/{ => sdl}/sdl.hpp (99%) rename lib/{ => sdl}/sdl_helper.cpp (100%) rename lib/{ => sdl}/sdl_helper.hpp (97%) rename lib/{ => sdl}/sdl_mac.hpp (100%) rename lib/{ => sdl}/sdl_mac.mm (100%) rename lib/{ => sdl}/sdl_main.cpp (99%) rename lib/{ => sdl}/sdl_main.hpp (100%) rename lib/{ => sdl}/sdl_windows.cpp (100%) rename lib/{ => sdl}/sdl_windows.hpp (100%) rename lib/{ => sdl}/sdl_x11.cpp (100%) rename lib/{ => sdl}/sdl_x11.hpp (100%) diff --git a/glvis.cpp b/glvis.cpp index 679aca62..2969213e 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -39,6 +39,7 @@ #include "lib/stream_reader.hpp" #include "lib/file_reader.hpp" #include "lib/coll_reader.hpp" +#include "lib/sdl/sdl.hpp" using namespace std; using namespace mfem; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 1445768e..3deedcd6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,14 +10,17 @@ # CONTRIBUTING.md for details. list(APPEND SOURCES + egl/egl.cpp gl/renderer.cpp gl/renderer_core.cpp gl/shader.cpp gl/types.cpp + sdl/sdl.cpp + sdl/sdl_helper.cpp + sdl/sdl_main.cpp aux_vis.cpp coll_reader.cpp data_state.cpp - egl.cpp file_reader.cpp font.cpp gltf.cpp @@ -27,9 +30,6 @@ list(APPEND SOURCES palettes.cpp palettes_base.cpp script_controller.cpp - sdl.cpp - sdl_helper.cpp - sdl_main.cpp stream_reader.cpp vsdata.cpp vssolution.cpp @@ -39,16 +39,20 @@ list(APPEND SOURCES window.cpp) list(APPEND HEADERS + egl/egl.hpp gl/attr_traits.hpp gl/platform_gl.hpp gl/renderer.hpp gl/renderer_core.hpp gl/shader.hpp gl/types.hpp + sdl/sdl.hpp + sdl/sdl_helper.hpp + sdl/sdl_main.hpp + sdl/sdl_mac.hpp aux_vis.hpp coll_reader.hpp data_state.hpp - egl.hpp file_reader.hpp font.hpp geom_utils.hpp @@ -60,10 +64,6 @@ list(APPEND HEADERS palettes.hpp palettes_base.hpp script_controller.hpp - sdl.hpp - sdl_helper.hpp - sdl_main.hpp - sdl_mac.hpp stream_reader.hpp visual.hpp vsdata.hpp @@ -92,15 +92,15 @@ if(EMSCRIPTEN) target_link_libraries(libglvis PUBLIC "${_glvis_libraries}") else() # Desktop build target - list(APPEND SOURCES gl/renderer_ff.cpp threads.cpp gl2ps.c sdl_x11.cpp) - list(APPEND HEADERS gl/renderer_ff.hpp threads.hpp gl2ps.h sdl_x11.hpp) + list(APPEND SOURCES gl/renderer_ff.cpp threads.cpp gl2ps.c sdl/sdl_x11.cpp) + list(APPEND HEADERS gl/renderer_ff.hpp threads.hpp gl2ps.h sdl/sdl_x11.hpp) if (APPLE) - list(APPEND SOURCES sdl_mac.mm) - list(APPEND HEADERS sdl_mac.hpp) + list(APPEND SOURCES sdl/sdl_mac.mm) + list(APPEND HEADERS sdl/sdl_mac.hpp) endif() if (WIN32) - list(APPEND SOURCES sdl_windows.cpp) - list(APPEND HEADERS sdl_windows.hpp) + list(APPEND SOURCES sdl/sdl_windows.cpp) + list(APPEND HEADERS sdl/sdl_windows.hpp) endif() add_library(glvis ${SOURCES} ${HEADERS}) target_compile_definitions(glvis PUBLIC "${_glvis_compile_defs}") diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 0873422f..673406d5 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -17,8 +17,8 @@ #include #include "mfem.hpp" -#include "sdl.hpp" -#include "egl.hpp" +#include "sdl/sdl.hpp" +#include "egl/egl.hpp" #include "palettes.hpp" #include "visual.hpp" #include "gl2ps.h" diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 210862e6..2c6bf483 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -16,7 +16,7 @@ #include "gl/types.hpp" #include "openglvis.hpp" -#include "sdl.hpp" +#include "glwindow.hpp" #include "font.hpp" #include @@ -43,6 +43,7 @@ void MyExpose(); void MainLoop(); +class SdlWindow; SdlWindow* GetSdlWindow(); GLWindow* GetAppWindow(); VisualizationScene * GetVisualizationScene(); diff --git a/lib/egl.cpp b/lib/egl/egl.cpp similarity index 99% rename from lib/egl.cpp rename to lib/egl/egl.cpp index a9aca599..712a176a 100644 --- a/lib/egl.cpp +++ b/lib/egl/egl.cpp @@ -11,7 +11,7 @@ #ifdef GLVIS_USE_EGL #include "egl.hpp" -#include "aux_vis.hpp" +#include "../aux_vis.hpp" #include #include diff --git a/lib/egl.hpp b/lib/egl/egl.hpp similarity index 99% rename from lib/egl.hpp rename to lib/egl/egl.hpp index 27c41557..1be219ea 100644 --- a/lib/egl.hpp +++ b/lib/egl/egl.hpp @@ -13,7 +13,7 @@ #define GLVIS_EGL_HPP #ifdef GLVIS_USE_EGL -#include "glwindow.hpp" +#include "../glwindow.hpp" #include diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index d3806e72..5384d515 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -14,6 +14,7 @@ #include "coll_reader.hpp" #include "stream_reader.hpp" #include "visual.hpp" +#include "sdl/sdl.hpp" #include #include diff --git a/lib/sdl.cpp b/lib/sdl/sdl.cpp similarity index 99% rename from lib/sdl.cpp rename to lib/sdl/sdl.cpp index 35dc51da..2747b5dd 100644 --- a/lib/sdl.cpp +++ b/lib/sdl/sdl.cpp @@ -10,7 +10,7 @@ // CONTRIBUTING.md for details. #include -#include "aux_vis.hpp" +#include "../aux_vis.hpp" #include "sdl.hpp" #include "sdl_main.hpp" #ifdef __EMSCRIPTEN__ diff --git a/lib/sdl.hpp b/lib/sdl/sdl.hpp similarity index 99% rename from lib/sdl.hpp rename to lib/sdl/sdl.hpp index 753a8f9d..20f1c3e9 100644 --- a/lib/sdl.hpp +++ b/lib/sdl/sdl.hpp @@ -16,7 +16,7 @@ #include #include #include -#include "glwindow.hpp" +#include "../glwindow.hpp" #include "sdl_helper.hpp" typedef void (*TouchDelegate)(SDL_MultiGestureEvent&); diff --git a/lib/sdl_helper.cpp b/lib/sdl/sdl_helper.cpp similarity index 100% rename from lib/sdl_helper.cpp rename to lib/sdl/sdl_helper.cpp diff --git a/lib/sdl_helper.hpp b/lib/sdl/sdl_helper.hpp similarity index 97% rename from lib/sdl_helper.hpp rename to lib/sdl/sdl_helper.hpp index d0990e49..2bf2c795 100644 --- a/lib/sdl_helper.hpp +++ b/lib/sdl/sdl_helper.hpp @@ -12,7 +12,7 @@ #ifndef GLVIS_SDL_HELPER_HPP #define GLVIS_SDL_HELPER_HPP -#include "gl/platform_gl.hpp" +#include "../gl/platform_gl.hpp" #include class SdlNativePlatform diff --git a/lib/sdl_mac.hpp b/lib/sdl/sdl_mac.hpp similarity index 100% rename from lib/sdl_mac.hpp rename to lib/sdl/sdl_mac.hpp diff --git a/lib/sdl_mac.mm b/lib/sdl/sdl_mac.mm similarity index 100% rename from lib/sdl_mac.mm rename to lib/sdl/sdl_mac.mm diff --git a/lib/sdl_main.cpp b/lib/sdl/sdl_main.cpp similarity index 99% rename from lib/sdl_main.cpp rename to lib/sdl/sdl_main.cpp index 442b8bc7..74b28c5e 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl/sdl_main.cpp @@ -15,7 +15,7 @@ #include "sdl_main.hpp" #include "sdl_helper.hpp" -#include "logo.hpp" +#include "../logo.hpp" #ifdef SDL_VIDEO_DRIVER_COCOA #include "sdl_mac.hpp" diff --git a/lib/sdl_main.hpp b/lib/sdl/sdl_main.hpp similarity index 100% rename from lib/sdl_main.hpp rename to lib/sdl/sdl_main.hpp diff --git a/lib/sdl_windows.cpp b/lib/sdl/sdl_windows.cpp similarity index 100% rename from lib/sdl_windows.cpp rename to lib/sdl/sdl_windows.cpp diff --git a/lib/sdl_windows.hpp b/lib/sdl/sdl_windows.hpp similarity index 100% rename from lib/sdl_windows.hpp rename to lib/sdl/sdl_windows.hpp diff --git a/lib/sdl_x11.cpp b/lib/sdl/sdl_x11.cpp similarity index 100% rename from lib/sdl_x11.cpp rename to lib/sdl/sdl_x11.cpp diff --git a/lib/sdl_x11.hpp b/lib/sdl/sdl_x11.hpp similarity index 100% rename from lib/sdl_x11.hpp rename to lib/sdl/sdl_x11.hpp diff --git a/makefile b/makefile index 6531047c..e0dbce91 100644 --- a/makefile +++ b/makefile @@ -257,40 +257,42 @@ LIBS = $(strip $(GLVIS_LIBS) $(GLVIS_LDFLAGS)) CCC = $(strip $(CXX) $(GLVIS_FLAGS)) Ccc = $(strip $(CC) $(CFLAGS) $(GL_OPTS)) -# generated with 'echo lib/gl/*.c* lib/*.c*', does not include lib/*.m (Obj-C) +# generated with 'echo lib/egl/*.c* lib/gl/*.c* lib/sdl/*.c* lib/*.c*', does not include lib/*.m (Obj-C) ALL_SOURCE_FILES = \ - lib/gl/renderer.cpp lib/gl/renderer_core.cpp lib/gl/renderer_ff.cpp \ - lib/gl/shader.cpp lib/gl/types.cpp lib/aux_js.cpp lib/aux_vis.cpp \ - lib/coll_reader.cpp lib/data_state.cpp lib/file_reader.cpp \ - lib/egl.cpp lib/font.cpp lib/gl2ps.c lib/gltf.cpp lib/glwindow.cpp \ - lib/material.cpp lib/openglvis.cpp lib/palettes_base.cpp \ - lib/palettes.cpp lib/sdl.cpp lib/script_controller.cpp \ - lib/sdl_helper.cpp lib/sdl_main.cpp lib/sdl_windows.cpp \ - lib/sdl_x11.cpp lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp \ + lib/egl/egl.cpp lib/gl/renderer_core.cpp lib/gl/renderer.cpp \ + lib/gl/renderer_ff.cpp lib/gl/shader.cpp lib/gl/types.cpp \ + lib/sdl/sdl.cpp lib/sdl/sdl_helper.cpp lib/sdl/sdl_main.cpp \ + lib/sdl/sdl_windows.cpp lib/sdl/sdl_x11.cpp lib/aux_js.cpp \ + lib/aux_vis.cpp lib/coll_reader.cpp lib/data_state.cpp \ + lib/file_reader.cpp lib/font.cpp lib/gl2ps.c lib/gltf.cpp \ + lib/glwindow.cpp lib/material.cpp lib/openglvis.cpp \ + lib/palettes_base.cpp lib/palettes.cpp lib/script_controller.cpp \ + lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp \ lib/vssolution.cpp lib/vssolution3d.cpp lib/vsvector.cpp \ lib/vsvector3d.cpp lib/window.cpp OBJC_SOURCE_FILES = $(if $(NOTMAC),,lib/sdl_mac.mm) DESKTOP_ONLY_SOURCE_FILES = \ - lib/gl/renderer_ff.cpp lib/threads.cpp lib/gl2ps.c lib/sdl_x11.cpp + lib/gl/renderer_ff.cpp lib/threads.cpp lib/gl2ps.c lib/sdl/sdl_x11.cpp WEB_ONLY_SOURCE_FILES = lib/aux_js.cpp LOGO_FILE = share/logo.rgba LOGO_FILE_CPP = $(LOGO_FILE).bin.cpp COMMON_SOURCE_FILES = $(filter-out \ $(DESKTOP_ONLY_SOURCE_FILES) $(WEB_ONLY_SOURCE_FILES),$(ALL_SOURCE_FILES)) -# generated with 'echo lib/gl/*.h* lib/*.h*' +# generated with 'echo lib/egl/*.h* lib/gl/*.h* lib/sdl/*.h* lib/*.h*' HEADER_FILES = \ - lib/gl/attr_traits.hpp lib/gl/platform_gl.hpp lib/gl/renderer.hpp \ - lib/gl/shader.hpp lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp \ - lib/gl/types.hpp lib/aux_vis.hpp lib/coll_reader.hpp \ - lib/data_state.hpp lib/egl.hpp lib/file_reader.hpp lib/font.hpp \ - lib/geom_utils.hpp lib/gl2ps.h lib/gltf.hpp lib/glwindow.hpp \ - lib/logo.hpp lib/material.hpp lib/openglvis.hpp lib/palettes_base.hpp \ - lib/palettes.hpp lib/script_controller.hpp lib/sdl_helper.hpp \ - lib/sdl.hpp lib/sdl_mac.hpp lib/sdl_main.hpp lib/sdl_windows.hpp \ - lib/sdl_x11.hpp lib/stream_reader.hpp lib/threads.hpp lib/visual.hpp \ - lib/vsdata.hpp lib/vssolution.hpp lib/vssolution3d.hpp \ - lib/vsvector.hpp lib/vsvector3d.hpp lib/window.hpp + lib/egl/egl.hpp lib/gl/attr_traits.hpp lib/gl/platform_gl.hpp \ + lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp lib/gl/renderer.hpp \ + lib/gl/shader.hpp lib/gl/types.hpp lib/sdl/sdl_helper.hpp \ + lib/sdl/sdl.hpp lib/sdl/sdl_mac.hpp lib/sdl/sdl_main.hpp \ + lib/sdl/sdl_windows.hpp lib/sdl/sdl_x11.hpp lib/aux_vis.hpp \ + lib/coll_reader.hpp lib/data_state.hpp lib/file_reader.hpp \ + lib/font.hpp lib/geom_utils.hpp lib/gl2ps.h lib/gltf.hpp \ + lib/glwindow.hpp lib/logo.hpp lib/material.hpp lib/openglvis.hpp \ + lib/palettes_base.hpp lib/palettes.hpp lib/script_controller.hpp \ + lib/stream_reader.hpp lib/threads.hpp lib/visual.hpp lib/vsdata.hpp \ + lib/vssolution.hpp lib/vssolution3d.hpp lib/vsvector.hpp \ + lib/vsvector3d.hpp lib/window.hpp DESKTOP_SOURCE_FILES = $(COMMON_SOURCE_FILES) $(DESKTOP_ONLY_SOURCE_FILES) $(LOGO_FILE_CPP) WEB_SOURCE_FILES = $(COMMON_SOURCE_FILES) $(WEB_ONLY_SOURCE_FILES) @@ -345,7 +347,7 @@ lib/libglvis.js: $(BYTECODE_FILES) $(CONFIG_MK) $(MFEM_LIB_FILE) $(EMCC) $(EMCC_OPTS) -o $@ $(BYTECODE_FILES) $(EMCC_LIBS) --embed-file $(FONT_FILE) clean: - rm -rf lib/*.o lib/*.bc lib/gl/*.o lib/gl/*.bc lib/*~ *~ glvis + rm -rf lib/*.o lib/*.bc lib/egl/*.o lib/egl/*.bc lib/gl/*.o lib/gl/*.bc lib/sdl/*.o lib/sdl/*.bc lib/*~ *~ glvis rm -rf $(LOGO_FILE_CPP) share/*.o rm -rf lib/libglvis.a lib/libglvis.js *.dSYM rm -rf GLVis.app From 74ff75ffa4f5b13d6d9350854fbe70c0c021a18e Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 00:40:52 -0700 Subject: [PATCH 18/79] Fixed makefile for Mac. --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index e0dbce91..54bd79f9 100644 --- a/makefile +++ b/makefile @@ -270,7 +270,7 @@ ALL_SOURCE_FILES = \ lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp \ lib/vssolution.cpp lib/vssolution3d.cpp lib/vsvector.cpp \ lib/vsvector3d.cpp lib/window.cpp -OBJC_SOURCE_FILES = $(if $(NOTMAC),,lib/sdl_mac.mm) +OBJC_SOURCE_FILES = $(if $(NOTMAC),,lib/sdl/sdl_mac.mm) DESKTOP_ONLY_SOURCE_FILES = \ lib/gl/renderer_ff.cpp lib/threads.cpp lib/gl2ps.c lib/sdl/sdl_x11.cpp WEB_ONLY_SOURCE_FILES = lib/aux_js.cpp From a83d77310cb95fdd60846ed12bffb54542446223 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 13:42:33 -0700 Subject: [PATCH 19/79] Added EGL to CI. --- .github/workflows/builds.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 59fb6a45..fb925a78 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -199,7 +199,7 @@ jobs: run: | glvis_target="opt" [[ ${{ matrix.target }} == "dbg" ]] && glvis_target="debug"; - cd glvis && make ${glvis_target} -j3 + cd glvis && make ${glvis_target} -j3 -E GLVIS_USE_EGL=YES - name: build GLVis (cmake) if: matrix.build-system == 'cmake' @@ -215,6 +215,7 @@ jobs: -D CMAKE_TOOLCHAIN_FILE:STRING=${toolchain_file} \ -D CMAKE_BUILD_TYPE:STRING=${build_type} \ -D ENABLE_TESTS:BOOL=TRUE \ + -D GLVIS_USE_EGL:BOOL=ON \ -D mfem_DIR:PATH=${GITHUB_WORKSPACE}/${MFEM_TOP_DIR}/build \ -D GLVIS_BASELINE_SYS=${{ matrix.os }} \ .. From 1d22e526c213465778405e253126909698ac20ee Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 14:20:14 -0700 Subject: [PATCH 20/79] Added EGL for Ubuntu only --- .github/workflows/builds.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index fb925a78..2a6997a5 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -37,23 +37,38 @@ jobs: target: [dbg, opt] mpi: [seq] build-system: [cmake] + backend: [sdl] include: - os: ubuntu-latest target: opt mpi: seq build-system: make + backend: sdl + - os: ubuntu-latest + target: dbg + mpi: seq + build-system: make + backend: egl + - os: ubuntu-latest + target: opt + mpi: seq + build-system: cmake + backend: egl - os: ubuntu-latest target: dbg mpi: par build-system: make + backend: sdl - os: macos-latest target: opt mpi: seq build-system: make + backend: sdl - os: macos-latest target: dbg mpi: par build-system: make + backend: sdl name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }} runs-on: ${{ matrix.os }} @@ -199,7 +214,9 @@ jobs: run: | glvis_target="opt" [[ ${{ matrix.target }} == "dbg" ]] && glvis_target="debug"; - cd glvis && make ${glvis_target} -j3 -E GLVIS_USE_EGL=YES + use_egl="NO" + [[ ${{ matrix.backend }} == "egl" ]] && use_egl="YES" + cd glvis && make ${glvis_target} -j3 GLVIS_USE_EGL=${use_egl} - name: build GLVis (cmake) if: matrix.build-system == 'cmake' @@ -210,12 +227,14 @@ jobs: [[ ${{ matrix.target }} == "dbg" ]] && build_type="Debug"; [[ ${{ matrix.os }} == "windows-latest" ]] \ && toolchain_file="${VCPKG_INSTALLATION_ROOT}\\scripts\\buildsystems\\vcpkg.cmake" + use_egl="OFF" + [[ ${{ matrix.backend }} == "egl" ]] && use_egl="ON" cd glvis && mkdir build && cd build cmake \ -D CMAKE_TOOLCHAIN_FILE:STRING=${toolchain_file} \ -D CMAKE_BUILD_TYPE:STRING=${build_type} \ -D ENABLE_TESTS:BOOL=TRUE \ - -D GLVIS_USE_EGL:BOOL=ON \ + -D GLVIS_USE_EGL:BOOL=${use_egl} \ -D mfem_DIR:PATH=${GITHUB_WORKSPACE}/${MFEM_TOP_DIR}/build \ -D GLVIS_BASELINE_SYS=${{ matrix.os }} \ .. From 5e4a28832099914867b50539d1b8868b69a656df Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 14:27:28 -0700 Subject: [PATCH 21/79] Fixed name of jobs. --- .github/workflows/builds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 2a6997a5..22d6d406 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -69,7 +69,7 @@ jobs: mpi: par build-system: make backend: sdl - name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }} + name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.backend}}-${{ matrix.target }}-${{ matrix.mpi }} runs-on: ${{ matrix.os }} From f417f1f8e7de7cfd6b6a02de183cb7f68288d3c0 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 14:38:00 -0700 Subject: [PATCH 22/79] Fixed atrifact name. --- .github/workflows/builds.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 22d6d406..6cb1877e 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -69,7 +69,7 @@ jobs: mpi: par build-system: make backend: sdl - name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.backend}}-${{ matrix.target }}-${{ matrix.mpi }} + name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.backend }}-${{ matrix.target }}-${{ matrix.mpi }} runs-on: ${{ matrix.os }} @@ -280,5 +280,5 @@ jobs: if: always() && matrix.build-system == 'cmake' && matrix.os != 'windows-latest' uses: actions/upload-artifact@v4 with: - name: test-screenshots-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.mpi }} + name: test-screenshots-${{ matrix.os }}-${{ matrix.backend }}-${{ matrix.target }}-${{ matrix.mpi }} path: glvis/build/test_screenshots.tar.gz From bac40b6f76e14342f6e247d47aabf2bea8a8e182 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 02:45:09 -0700 Subject: [PATCH 23/79] Used EGL in tests automatically when available. --- tests/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4a4ef136..a397d90b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,6 +49,12 @@ set(stream_tests add_custom_target(rebaseline) +set(TEST_ARGS "-lw 1 -mslw 1 -nohidpi") + +if(${GLVIS_USE_EGL}) + set(TEST_ARGS "${TEST_ARGS} -hl") +endif() + foreach(test_name IN LISTS stream_tests) add_test(NAME stream_${test_name} @@ -56,7 +62,7 @@ foreach(test_name IN LISTS stream_tests) -e ${CMAKE_CURRENT_BINARY_DIR}/../glvis -s ${CMAKE_CURRENT_SOURCE_DIR}/data/streams/${test_name}.saved -b ${CMAKE_CURRENT_SOURCE_DIR}/data/baselines/${GLVIS_BASELINE_SYS} - -a "-lw 1 -mslw 1 -nohidpi") + -a ${TEST_ARGS}) add_custom_target(_rebaseline_stream_${test_name} COMMAND ${CMAKE_COMMAND} -E make_directory From 1d0f9c48203a32528338366233c4905bdaf2d29f Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 03:37:39 -0700 Subject: [PATCH 24/79] Revert "Used EGL in tests automatically when available." This reverts commit bac40b6f76e14342f6e247d47aabf2bea8a8e182. --- tests/CMakeLists.txt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a397d90b..4a4ef136 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,12 +49,6 @@ set(stream_tests add_custom_target(rebaseline) -set(TEST_ARGS "-lw 1 -mslw 1 -nohidpi") - -if(${GLVIS_USE_EGL}) - set(TEST_ARGS "${TEST_ARGS} -hl") -endif() - foreach(test_name IN LISTS stream_tests) add_test(NAME stream_${test_name} @@ -62,7 +56,7 @@ foreach(test_name IN LISTS stream_tests) -e ${CMAKE_CURRENT_BINARY_DIR}/../glvis -s ${CMAKE_CURRENT_SOURCE_DIR}/data/streams/${test_name}.saved -b ${CMAKE_CURRENT_SOURCE_DIR}/data/baselines/${GLVIS_BASELINE_SYS} - -a ${TEST_ARGS}) + -a "-lw 1 -mslw 1 -nohidpi") add_custom_target(_rebaseline_stream_${test_name} COMMAND ${CMAKE_COMMAND} -E make_directory From 50872dffde36087e7b214681db5f015357dcec88 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 03:43:25 -0700 Subject: [PATCH 25/79] Updated Changelog and Install. --- CHANGELOG | 10 ++++++++++ INSTALL | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 15c5e145..a3eef1c6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,16 @@ https://glvis.org +Version 4.4.1 (development) +=================================== + +- Added headless (no GUI) visualization, relying on EGL standard. It is + available through '-hl' command-line option for non-server visualization or + 'headless' script command (before the visualization commands). End of the + loaded stream or script file end the visualization in this mode. Note GLVis + must be compiled with with EGL (see INSTALL). + + Version 4.4 released on May 1, 2025 =================================== diff --git a/INSTALL b/INSTALL index 39216342..e6a9c4a9 100644 --- a/INSTALL +++ b/INSTALL @@ -38,6 +38,9 @@ Besides a C++ compiler, GLVis depends on the following external packages: - the XQuartz app (on Mac OS X, if running GLVis remotely through ssh) https://www.xquartz.org +- the EGL library; used for headless rendering (optional) + https://www.khronos.org/egl + There are two build systems, one based on GNU make and one based on CMake, as described below. Choose the one that matches the build system you used to build MFEM. @@ -87,6 +90,7 @@ cmake \ \ -D GLVIS_USE_LIBTIFF=OFF \ -D GLVIS_USE_LIBPNG=ON \ + -D GLVIS_USE_EGL=OFF \ \ -D MFEM_DIR=/path/to/directory/with/MFEMConfig.cmake \ \ @@ -111,6 +115,8 @@ Some important variables for CMake are: - GLVIS_USE_LIBTIFF: Use libtiff for creating screenshots. Default is "OFF". +- GLVIS_USE_EGL: Use EGL for headless rendering. Default is "OFF". + - GLVIS_MULTISAMPLE and GLVIS_MS_LINEWIDTH: See building considerations below for more information on these variables. From 00bac4a483b0c44506bcd3366e121bd91604a929 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 16:12:56 -0700 Subject: [PATCH 26/79] Fixed caching of MPI --- .github/workflows/builds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 6cb1877e..f3e10ea5 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -150,7 +150,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.MFEM_TOP_DIR }} - key: ${{ runner.os }}-build-${{ env.MFEM_TOP_DIR }}-${{ env.MFEM_COMMIT }}-${{ matrix.target }}-${{ matrix.build-system}}-v2.4 + key: ${{ runner.os }}-build-${{ env.MFEM_TOP_DIR }}-${{ env.MFEM_COMMIT }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-$-v2.4 # We are using the defaults of the MFEM action here, which is to use master # branch. There is an implicit assumption here that mfem master hasn't From e10712fe85ac9272ef702ef59fe24d765385e2f7 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 16:23:09 -0700 Subject: [PATCH 27/79] Made EGL option default on Ubuntu and removed it from the matrix. --- .github/workflows/builds.yml | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index f3e10ea5..e70fc5e3 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -37,39 +37,24 @@ jobs: target: [dbg, opt] mpi: [seq] build-system: [cmake] - backend: [sdl] include: - os: ubuntu-latest target: opt mpi: seq build-system: make - backend: sdl - - os: ubuntu-latest - target: dbg - mpi: seq - build-system: make - backend: egl - - os: ubuntu-latest - target: opt - mpi: seq - build-system: cmake - backend: egl - os: ubuntu-latest target: dbg mpi: par build-system: make - backend: sdl - os: macos-latest target: opt mpi: seq build-system: make - backend: sdl - os: macos-latest target: dbg mpi: par build-system: make - backend: sdl - name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.backend }}-${{ matrix.target }}-${{ matrix.mpi }} + name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }} runs-on: ${{ matrix.os }} @@ -215,7 +200,7 @@ jobs: glvis_target="opt" [[ ${{ matrix.target }} == "dbg" ]] && glvis_target="debug"; use_egl="NO" - [[ ${{ matrix.backend }} == "egl" ]] && use_egl="YES" + [[ ${{ matrix.os }} == "ubuntu-latest" ]] && use_egl="YES" cd glvis && make ${glvis_target} -j3 GLVIS_USE_EGL=${use_egl} - name: build GLVis (cmake) @@ -228,7 +213,7 @@ jobs: [[ ${{ matrix.os }} == "windows-latest" ]] \ && toolchain_file="${VCPKG_INSTALLATION_ROOT}\\scripts\\buildsystems\\vcpkg.cmake" use_egl="OFF" - [[ ${{ matrix.backend }} == "egl" ]] && use_egl="ON" + [[ ${{ matrix.os }} == "ubuntu-latest" ]] && use_egl="ON" cd glvis && mkdir build && cd build cmake \ -D CMAKE_TOOLCHAIN_FILE:STRING=${toolchain_file} \ @@ -280,5 +265,5 @@ jobs: if: always() && matrix.build-system == 'cmake' && matrix.os != 'windows-latest' uses: actions/upload-artifact@v4 with: - name: test-screenshots-${{ matrix.os }}-${{ matrix.backend }}-${{ matrix.target }}-${{ matrix.mpi }} + name: test-screenshots-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.mpi }} path: glvis/build/test_screenshots.tar.gz From b8a4be6feb232e717984c2e009dccdd37855ec99 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 16:26:39 -0700 Subject: [PATCH 28/79] Fixed caching of MFEM in release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f97a1252..a324d622 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -118,7 +118,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.MFEM_TOP_DIR }} - key: ${{ runner.os }}-build-${{ env.MFEM_TOP_DIR }}-${{ env.MFEM_COMMIT }}-${{ matrix.target }}-${{ matrix.build-system}}-v2.2 + key: ${{ runner.os }}-build-${{ env.MFEM_TOP_DIR }}-${{ env.MFEM_COMMIT }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-v2.2 # We are using the defaults of the MFEM action here, which is to use master # branch. There is an implicit assumption here that mfem master hasn't From 84d47b56be9dbf59143a31eadf7247bad7594fd3 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 14 May 2025 16:27:26 -0700 Subject: [PATCH 29/79] Fixed typo in builds.yml --- .github/workflows/builds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index e70fc5e3..a9c12117 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -135,7 +135,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.MFEM_TOP_DIR }} - key: ${{ runner.os }}-build-${{ env.MFEM_TOP_DIR }}-${{ env.MFEM_COMMIT }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-$-v2.4 + key: ${{ runner.os }}-build-${{ env.MFEM_TOP_DIR }}-${{ env.MFEM_COMMIT }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-v2.4 # We are using the defaults of the MFEM action here, which is to use master # branch. There is an implicit assumption here that mfem master hasn't From eee2bfe84ededa8f986600e5d950559c77e242d9 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 19 May 2025 12:43:51 -0700 Subject: [PATCH 30/79] Added EGL to server mode. --- glvis.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 2969213e..80849fda 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -75,11 +75,13 @@ class Session public: Session(bool fix_elem_orient, bool save_coloring, - string plot_caption) + string plot_caption, + bool headless) { win.data_state.fix_elem_orient = fix_elem_orient; win.data_state.save_coloring = save_coloring; win.plot_caption = plot_caption; + win.headless = headless; } Session(Window other_win) @@ -159,7 +161,7 @@ class Session }; void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient, - bool save_coloring, string plot_caption) + bool save_coloring, string plot_caption, bool headless = false) { std::vector current_sessions; string data_type; @@ -309,7 +311,7 @@ void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient, while (1); } - Session new_session(fix_elem_orient, save_coloring, plot_caption); + Session new_session(fix_elem_orient, save_coloring, plot_caption, headless); constexpr int tmp_filename_size = 50; char tmp_file[tmp_filename_size]; @@ -680,20 +682,29 @@ int main (int argc, char *argv[]) // server mode, read the mesh and the solution from a socket if (input == INPUT_SERVER_MODE) { - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. - GetMainThread(); + if (!win.headless) + { + // Make sure the singleton object returned by GetMainThread() is + // initialized from the main thread. + GetMainThread(); + } // Run server in new thread std::thread serverThread{GLVisServer, portnum, save_stream, win.data_state.fix_elem_orient, win.data_state.save_coloring, - win.plot_caption}; - - // Start SDL in main thread - SDLMainLoop(true); + win.plot_caption, win.headless}; - serverThread.detach(); + if (!win.headless) + { + // Start SDL in main thread + SDLMainLoop(true); + serverThread.detach(); + } + else + { + serverThread.join(); + } } else // input != 1, non-server mode { From d60b05dc01949f52c97a9ab35e0de3fce2e511ea Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 12:25:00 -0700 Subject: [PATCH 31/79] Implemented EGL main thread server. --- glvis.cpp | 53 ++--- lib/egl/egl.cpp | 411 +++++++++++++++++++++++++++++++------- lib/egl/egl.hpp | 67 ++++++- lib/script_controller.cpp | 13 +- 4 files changed, 445 insertions(+), 99 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 80849fda..15e69ec6 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -40,6 +40,7 @@ #include "lib/file_reader.hpp" #include "lib/coll_reader.hpp" #include "lib/sdl/sdl.hpp" +#include "lib/egl/egl.hpp" using namespace std; using namespace mfem; @@ -96,7 +97,7 @@ class Session inline DataState& GetState() { return win.data_state; } inline const DataState& GetState() const { return win.data_state; } - void StartSession(bool detached = true) + void StartSession() { auto funcThread = [](Window w, StreamCollection is) { @@ -107,13 +108,10 @@ class Session }; handler = std::thread {funcThread, std::move(win), std::move(input_streams)}; - if (detached) - { - handler.detach(); - } + handler.detach(); } - bool StartSavedSession(std::string stream_file, bool detached = true) + bool StartSavedSession(std::string stream_file) { unique_ptr ifs(new ifstream(stream_file)); if (!(*ifs)) @@ -127,7 +125,7 @@ class Session reader.ReadStream(*ifs, data_type); input_streams.emplace_back(std::move(ifs)); - StartSession(detached); + StartSession(); return true; } @@ -153,11 +151,6 @@ class Session StartSession(); return 0; } - - void WaitForSession() - { - handler.join(); - } }; void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient, @@ -607,16 +600,20 @@ int main (int argc, char *argv[]) // backup the headless flag as the window is moved const bool headless = win.headless; + // Make sure the returned singleton object is + // initialized from the main thread. if (!headless) { - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. GetMainThread(); } + else + { + EglMainThread::Get(); + } Session stream_session(std::move(win)); - if (!stream_session.StartSavedSession(stream_file, !headless)) + if (!stream_session.StartSavedSession(stream_file)) { return 1; } @@ -627,7 +624,7 @@ int main (int argc, char *argv[]) } else { - stream_session.WaitForSession(); + EglMainThread::Get().MainLoop(); } return 0; } @@ -682,12 +679,16 @@ int main (int argc, char *argv[]) // server mode, read the mesh and the solution from a socket if (input == INPUT_SERVER_MODE) { + // Make sure the returned singleton object is + // initialized from the main thread. if (!win.headless) { - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. GetMainThread(); } + else + { + EglMainThread::Get(); + } // Run server in new thread std::thread serverThread{GLVisServer, portnum, save_stream, @@ -699,12 +700,12 @@ int main (int argc, char *argv[]) { // Start SDL in main thread SDLMainLoop(true); - serverThread.detach(); } else { - serverThread.join(); + EglMainThread::Get().MainLoop(true); } + serverThread.detach(); } else // input != 1, non-server mode { @@ -812,15 +813,19 @@ int main (int argc, char *argv[]) // backup the headless flag as the window is moved const bool headless = win.headless; + // Make sure the returned singleton object is + // initialized from the main thread. if (!headless) { - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. GetMainThread(); } + else + { + EglMainThread::Get(); + } Session single_session(std::move(win)); - single_session.StartSession(!headless); + single_session.StartSession(); if (!headless) { @@ -828,7 +833,7 @@ int main (int argc, char *argv[]) } else { - single_session.WaitForSession(); + EglMainThread::Get().MainLoop(); } } diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 712a176a..2aaff7f8 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -14,6 +14,7 @@ #include "../aux_vis.hpp" #include #include +#include #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s @@ -21,38 +22,42 @@ #define PRINT_DEBUG(s) {} #endif -EglWindow::EglWindow() +struct EglMainThread::CreateWndCmd { - // Initialize EGL - disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (disp == EGL_NO_DISPLAY) - { - std::cerr << "FATAL: Failed to get an EGL display: " << eglGetError() << - std::endl; - return; - } + EglWindow *wnd; + int w, h; + promise out_handle; +}; - EGLint major, minor; +struct EglMainThread::ResizeWndCmd +{ + Handle *handle; + int w, h; +}; - if (!eglInitialize(disp, &major, &minor)) +struct EglMainThread::DeleteWndCmd +{ + EglWindow *wnd; + Handle *handle; +}; + +struct EglMainThread::CtrlCmd +{ + CtrlCmdType type; + union { - std::cerr << "FATAL: Failed to initialize EGL: " << eglGetError() << std::endl; - return; - } + CreateWndCmd *create_cmd; + ResizeWndCmd *resize_cmd; + DeleteWndCmd *delete_cmd; + }; - std::cout << "Using EGL " << major << "." << minor << std::endl; -} + promise finished; +}; -EglWindow::~EglWindow() +bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) { - if (ctx != EGL_NO_CONTEXT) { eglDestroyContext(disp, ctx); } - if (surf != EGL_NO_SURFACE) { eglDestroySurface(disp, surf); } - eglTerminate(disp); -} + Handle new_handle; -bool EglWindow::createWindow(const char *, int, int, int w, int h, - bool legacyGlOnly) -{ // 1. Select an appropriate configuration const int multisamples = GetMultisample(); @@ -87,7 +92,7 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, } } - if (!eglChooseConfig(disp, configAttribs, &eglCfg, 1, &numConfigs) || + if (!eglChooseConfig(disp, configAttribs, &new_handle.eglCfg, 1, &numConfigs) || numConfigs < 1) { std::cerr << "Cannot find working EGL configuration!" << std::endl; @@ -97,28 +102,171 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, // 2. Create a surface const EGLint pbufferAttribs[] = { - EGL_WIDTH, w, - EGL_HEIGHT, h, + EGL_WIDTH, cmd.w, + EGL_HEIGHT, cmd.h, + EGL_NONE + }; + + new_handle.surf = eglCreatePbufferSurface(disp, new_handle.eglCfg, + pbufferAttribs); + if (new_handle.surf == EGL_NO_SURFACE) + { + std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << + std::endl; + return false; + } + + windows.push_back(cmd.wnd); + cmd.out_handle.set_value(std::move(new_handle)); + + return true; +} + +bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) +{ + const EGLint pbufferAttribs[] = + { + EGL_WIDTH, cmd.w, + EGL_HEIGHT, cmd.h, EGL_NONE }; - surf = eglCreatePbufferSurface(disp, eglCfg, pbufferAttribs); - if (surf == EGL_NO_SURFACE) + EGLSurface surf_new = eglCreatePbufferSurface(disp, cmd.handle->eglCfg, + pbufferAttribs); + if (surf_new == EGL_NO_SURFACE) { std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << std::endl; return false; } + if (!eglDestroySurface(disp, cmd.handle->surf)) + { + std::cerr << "Cannot destroy surface, error: " << eglGetError() << std::endl; + return false; + } + + cmd.handle->surf = surf_new; + + return true; +} + +bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) +{ + if (cmd.handle->ctx != EGL_NO_CONTEXT) + { + if (!eglDestroyContext(disp, cmd.handle->ctx)) + { + std::cerr << "Cannot destroy context, error: " << eglGetError() << std::endl; + return false; + } + cmd.handle->ctx = EGL_NO_CONTEXT; + } + + if (cmd.handle->surf != EGL_NO_SURFACE) + { + if (!eglDestroySurface(disp, cmd.handle->surf)) + { + std::cerr << "Cannot destroy surface, error: " << eglGetError() << std::endl; + return false; + } + cmd.handle->surf = EGL_NO_SURFACE; + } + + windows.remove(cmd.wnd); + + return true; +} + +void EglMainThread::QueueWndCmd(CtrlCmd cmd, bool sync) +{ + future wait_complete; + if (sync) + { + wait_complete = cmd.finished.get_future(); + } + // queue up our event + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(cmd)); + } + // wake up the main thread to handle our event + events_available.notify_all(); + + if (sync) { wait_complete.get(); } +} + +EglMainThread::EglMainThread() +{ + if (disp != EGL_NO_DISPLAY) + { + return; + } + + // Initialize EGL + disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (disp == EGL_NO_DISPLAY) + { + std::cerr << "FATAL: Failed to get an EGL display: " << eglGetError() << + std::endl; + return; + } + + EGLint major, minor; + + if (!eglInitialize(disp, &major, &minor)) + { + std::cerr << "FATAL: Failed to initialize EGL: " << eglGetError() << std::endl; + return; + } + + std::cout << "Using EGL " << major << "." << minor << std::endl; +} + +EglMainThread::~EglMainThread() +{ + eglTerminate(disp); +} + +EglMainThread &EglMainThread::Get() +{ + static EglMainThread singleton; + return singleton; +} + +EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, + int h, bool legacy_gl) +{ + CtrlCmd cmd; + cmd.type = CtrlCmdType::Create; + + CreateWndCmd crt_cmd; + cmd.create_cmd = &crt_cmd; + + crt_cmd.wnd = caller; + crt_cmd.w = w; + crt_cmd.h = h; + + auto res_handle = crt_cmd.out_handle.get_future(); + + QueueWndCmd(std::move(cmd), false); + + Handle out_hnd = res_handle.get(); + + if (out_hnd.surf == EGL_NO_SURFACE) + { + return out_hnd; + } + // 3. Bind the API if (!eglBindAPI(EGL_OPENGL_API)) { std::cerr << "Cannot bind OpenGL API, error: " << eglGetError() << std::endl; - return false; + return out_hnd; } // 4. Create a context and make it current - if (legacyGlOnly) + if (legacy_gl) { // Try and probe for a core/compatibility context. Needed for Mac OS X, // which will only support OpenGL 2.1 if you don't create a core context. @@ -128,8 +276,9 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_NONE }; - ctx = eglCreateContext(disp, eglCfg, EGL_NO_CONTEXT, attrListCore); - if (ctx == EGL_NO_CONTEXT) + out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, + attrListCore); + if (out_hnd.ctx == EGL_NO_CONTEXT) { PRINT_DEBUG("failed." << std::endl); PRINT_DEBUG("Opening OpenGL compatibility profile context..." << std::flush); @@ -138,8 +287,9 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, EGL_NONE }; - ctx = eglCreateContext(disp, eglCfg, EGL_NO_CONTEXT, attrListCompat); - if (ctx == EGL_NO_CONTEXT) + out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, + attrListCompat); + if (out_hnd.ctx == EGL_NO_CONTEXT) { PRINT_DEBUG("failed." << std::endl); } @@ -154,16 +304,18 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, } } - if (ctx == EGL_NO_CONTEXT) + if (out_hnd.ctx == EGL_NO_CONTEXT) { PRINT_DEBUG("Opening OpenGL context with no flags..." << std::flush); - ctx = eglCreateContext(disp, eglCfg, EGL_NO_CONTEXT, NULL); - if (ctx == EGL_NO_CONTEXT) + out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, + NULL); + if (out_hnd.ctx == EGL_NO_CONTEXT) { PRINT_DEBUG("failed." << std::endl); std::cerr << "Cannot create an EGL context, error: " << eglGetError() << std::endl; - return false; + + return out_hnd; } else { @@ -171,10 +323,157 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, } } - if (!eglMakeCurrent(disp, surf, surf, ctx)) + if (!eglMakeCurrent(disp, out_hnd.surf, out_hnd.surf, out_hnd.ctx)) { std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() << std::endl; + + return out_hnd; + } + + return out_hnd; +} + +void EglMainThread::ResizeWindow(Handle &handle, int w, int h) +{ + if (!handle.isInitialized()) { return; } + + CtrlCmd cmd; + cmd.type = CtrlCmdType::Resize; + + ResizeWndCmd res_cmd; + cmd.resize_cmd = &res_cmd; + + res_cmd.handle = &handle; + res_cmd.w = w; + res_cmd.h = h; + + QueueWndCmd(std::move(cmd), true); + + if (!eglMakeCurrent(disp, handle.surf, handle.surf, handle.ctx)) + { + std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() + << std::endl; + return; + } +} + +void EglMainThread::DeleteWindow(EglWindow *caller, Handle &handle) +{ + if (!handle.isInitialized()) { return; } + + CtrlCmd cmd; + cmd.type = CtrlCmdType::Delete; + + DeleteWndCmd del_cmd; + cmd.delete_cmd = &del_cmd; + + del_cmd.wnd = caller; + del_cmd.handle = &handle; + + QueueWndCmd(std::move(cmd), true); +} + +void EglMainThread::MainLoop(bool server) +{ + server_mode = server; + bool terminating = false; + + while (true) + { + bool events_pending = false; + { + lock_guard evt_guard{window_cmd_mtx}; + events_pending = !window_cmds.empty(); + } + if (events_pending) + { + do + { + CtrlCmd cmd; + // Fetch next event from the queue + { + lock_guard evt_guard{window_cmd_mtx}; + cmd = std::move(window_cmds.front()); + window_cmds.pop_front(); + events_pending = !window_cmds.empty(); + // Skip non-delete events if terminating + if (terminating && cmd.type != CtrlCmdType::Delete) + { + continue; + } + } + + switch (cmd.type) + { + case CtrlCmdType::Create: + if (!CreateWndImpl(*cmd.create_cmd)) + { + terminating = true; + } + break; + case CtrlCmdType::Resize: + if (!ResizeWndImpl(*cmd.resize_cmd)) + { + terminating = true; + } + break; + case CtrlCmdType::Delete: + if (!DeleteWndImpl(*cmd.delete_cmd)) + { + terminating = true; + } + break; + } + + // Signal completion of the command, in case worker thread is waiting. + cmd.finished.set_value(); + } + while (events_pending); + } + + if (windows.size() == 0) + { + if (!server_mode || terminating) + { + break; + } + } + + if (terminating) + { + for (EglWindow *wnd : windows) + { + wnd->signalQuit(); + } + } + + { + unique_lock event_lock{window_cmd_mtx}; + events_available.wait(event_lock, [this]() + { + // Sleep until events from windows can be handled + return !window_cmds.empty(); + }); + } + } +} + +EglWindow::EglWindow() +{ +} + +EglWindow::~EglWindow() +{ + EglMainThread::Get().DeleteWindow(this, handle); +} + +bool EglWindow::createWindow(const char *, int, int, int w, int h, + bool legacyGlOnly) +{ + handle = EglMainThread::Get().CreateWindow(this, w, h, legacyGlOnly); + if (!handle.isInitialized()) + { return false; } @@ -292,8 +591,10 @@ void EglWindow::signalLoop() void EglWindow::getGLDrawSize(int& w, int& h) const { EGLint egl_w, egl_h; - eglQuerySurface(disp, surf, EGL_WIDTH, &egl_w); - eglQuerySurface(disp, surf, EGL_HEIGHT, &egl_h); + + EGLDisplay disp = EglMainThread::Get().GetDisplay(); + eglQuerySurface(disp, handle.surf, EGL_WIDTH, &egl_w); + eglQuerySurface(disp, handle.surf, EGL_HEIGHT, &egl_h); w = egl_w; h = egl_h; } @@ -305,31 +606,7 @@ bool EglWindow::isHighDpi() const void EglWindow::setWindowSize(int w, int h) { - const EGLint pbufferAttribs[] = - { - EGL_WIDTH, w, - EGL_HEIGHT, h, - EGL_NONE - }; - - EGLSurface surf_new = eglCreatePbufferSurface(disp, eglCfg, pbufferAttribs); - if (surf_new == EGL_NO_SURFACE) - { - std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << - std::endl; - return; - } - - if (!eglMakeCurrent(disp, surf_new, surf_new, ctx)) - { - std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() - << std::endl; - return; - } - - eglDestroySurface(disp, surf); - - surf = surf_new; + EglMainThread::Get().ResizeWindow(handle, w, h); } void EglWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index 1be219ea..18416c37 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -20,13 +20,68 @@ #include #include #include +#include -class EglWindow : public GLWindow +class EglWindow; +class EglMainThread { EGLDisplay disp{EGL_NO_DISPLAY}; - EGLSurface surf{EGL_NO_SURFACE}; - EGLContext ctx{EGL_NO_CONTEXT}; - EGLConfig eglCfg{}; + + bool server_mode{false}; + + std::list windows; + + struct CreateWndCmd; + struct ResizeWndCmd; + struct DeleteWndCmd; + + enum class CtrlCmdType + { + Create, + Resize, + Delete, + }; + + struct CtrlCmd; + + std::condition_variable events_available; + std::mutex window_cmd_mtx; + std::deque window_cmds; + + bool CreateWndImpl(CreateWndCmd &cmd); + bool ResizeWndImpl(ResizeWndCmd &cmd); + bool DeleteWndImpl(DeleteWndCmd &cmd); + void QueueWndCmd(CtrlCmd cmd, bool sync); + +public: + struct Handle + { + EGLSurface surf{EGL_NO_SURFACE}; + EGLContext ctx{EGL_NO_CONTEXT}; + EGLConfig eglCfg{}; + + bool isInitialized() + { + return surf != EGL_NO_SURFACE && ctx != EGL_NO_CONTEXT; + } + }; + + EglMainThread(); + ~EglMainThread(); + + static EglMainThread& Get(); + EGLDisplay GetDisplay() const { return disp; } + + Handle CreateWindow(EglWindow *caller, int w, int h, bool legacy_gl); + void ResizeWindow(Handle &hnd, int w, int h); + void DeleteWindow(EglWindow *caller, Handle &hnd); + + void MainLoop(bool server = false); +}; + +class EglWindow : public GLWindow +{ + EglMainThread::Handle handle; bool running{false}; @@ -91,8 +146,8 @@ class EglWindow : public GLWindow void setWindowSize(int w, int h) override; - bool isWindowInitialized() const override { return surf != EGL_NO_SURFACE; } - bool isGlInitialized() const override { return ctx != EGL_NO_CONTEXT; } + bool isWindowInitialized() const override { return handle.surf != EGL_NO_SURFACE; } + bool isGlInitialized() const override { return handle.ctx != EGL_NO_CONTEXT; } void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) override; void signalQuit() override; diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index 5384d515..13ec7982 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -15,6 +15,7 @@ #include "stream_reader.hpp" #include "visual.hpp" #include "sdl/sdl.hpp" +#include "egl/egl.hpp" #include #include @@ -972,12 +973,16 @@ void ScriptController::PlayScript(Window win, istream &scr) // backup the headless flag as the window is moved const bool headless = script.win.headless; + // Make sure the returned singleton object is + // initialized from the main thread. if (!headless) { - // Make sure the singleton object returned by GetMainThread() is - // initialized from the main thread. GetMainThread(); } + else + { + EglMainThread::Get(); + } std::thread worker_thread @@ -1006,5 +1011,9 @@ void ScriptController::PlayScript(Window win, istream &scr) { SDLMainLoop(); } + else + { + EglMainThread::Get().MainLoop(); + } worker_thread.join(); } From 1398c436ce622693e202bbce78deae8747d63a76 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 15:38:49 -0700 Subject: [PATCH 32/79] Unified usage of the main thread. --- glvis.cpp | 57 ++++++--------------------------------- lib/aux_vis.cpp | 15 +++++++++-- lib/aux_vis.hpp | 3 ++- lib/egl/egl.hpp | 4 +-- lib/glwindow.hpp | 10 +++++++ lib/script_controller.cpp | 19 ++----------- lib/sdl/sdl.cpp | 26 +++++++++--------- lib/sdl/sdl.hpp | 2 +- lib/sdl/sdl_main.hpp | 4 +-- 9 files changed, 53 insertions(+), 87 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 15e69ec6..b98c4a5b 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -602,14 +602,7 @@ int main (int argc, char *argv[]) // Make sure the returned singleton object is // initialized from the main thread. - if (!headless) - { - GetMainThread(); - } - else - { - EglMainThread::Get(); - } + GetMainThread(headless); Session stream_session(std::move(win)); @@ -618,14 +611,8 @@ int main (int argc, char *argv[]) return 1; } - if (!headless) - { - SDLMainLoop(); - } - else - { - EglMainThread::Get().MainLoop(); - } + MainThreadLoop(headless); + return 0; } @@ -681,14 +668,7 @@ int main (int argc, char *argv[]) { // Make sure the returned singleton object is // initialized from the main thread. - if (!win.headless) - { - GetMainThread(); - } - else - { - EglMainThread::Get(); - } + GetMainThread(win.headless); // Run server in new thread std::thread serverThread{GLVisServer, portnum, save_stream, @@ -696,15 +676,8 @@ int main (int argc, char *argv[]) win.data_state.save_coloring, win.plot_caption, win.headless}; - if (!win.headless) - { - // Start SDL in main thread - SDLMainLoop(true); - } - else - { - EglMainThread::Get().MainLoop(true); - } + // Start message loop in main thread + MainThreadLoop(win.headless, true); serverThread.detach(); } else // input != 1, non-server mode @@ -815,26 +788,12 @@ int main (int argc, char *argv[]) // Make sure the returned singleton object is // initialized from the main thread. - if (!headless) - { - GetMainThread(); - } - else - { - EglMainThread::Get(); - } + GetMainThread(headless); Session single_session(std::move(win)); single_session.StartSession(); - if (!headless) - { - SDLMainLoop(); - } - else - { - EglMainThread::Get().MainLoop(); - } + MainThreadLoop(headless); } cout << "Thank you for using GLVis." << endl; diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 673406d5..9d77675a 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -18,6 +18,7 @@ #include "mfem.hpp" #include "sdl/sdl.hpp" +#include "sdl/sdl_main.hpp" #include "egl/egl.hpp" #include "palettes.hpp" #include "visual.hpp" @@ -54,9 +55,19 @@ static thread_local SdlWindow *sdl_wnd = nullptr; static bool wndLegacyGl = false; static bool wndUseHiDPI = true; -void SDLMainLoop(bool server_mode) +MainThread& GetMainThread(bool headless) { - SdlWindow::StartSDL(server_mode); + if (headless) + { + return EglMainThread::Get(); + } + + return GetSdlMainThread(); +} + +void MainThreadLoop(bool headless, bool server_mode) +{ + GetMainThread(headless).MainLoop(server_mode); } void SetGLVisCommand(GLVisCommand *cmd) diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 2c6bf483..bf73aa28 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -21,7 +21,8 @@ #include -void SDLMainLoop(bool server_mode = false); +MainThread& GetMainThread(bool headless = false); +void MainThreadLoop(bool headless = false, bool server_mode = false); class GLVisCommand; void SetGLVisCommand(GLVisCommand *cmd); diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index 18416c37..8f35252a 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -23,7 +23,7 @@ #include class EglWindow; -class EglMainThread +class EglMainThread : public MainThread { EGLDisplay disp{EGL_NO_DISPLAY}; @@ -76,7 +76,7 @@ class EglMainThread void ResizeWindow(Handle &hnd, int w, int h); void DeleteWindow(EglWindow *caller, Handle &hnd); - void MainLoop(bool server = false); + void MainLoop(bool server = false) override; }; class EglWindow : public GLWindow diff --git a/lib/glwindow.hpp b/lib/glwindow.hpp index 5432e29f..d9e872cd 100644 --- a/lib/glwindow.hpp +++ b/lib/glwindow.hpp @@ -18,6 +18,16 @@ #include #include +class MainThread +{ +public: + virtual ~MainThread() { } + + // Handles all operations that are expected to be handled on the main + // thread (i.e. events and window creation) + virtual void MainLoop(bool server_mode) = 0; +}; + class GLWindow { public: diff --git a/lib/script_controller.cpp b/lib/script_controller.cpp index 13ec7982..5546720c 100644 --- a/lib/script_controller.cpp +++ b/lib/script_controller.cpp @@ -975,15 +975,7 @@ void ScriptController::PlayScript(Window win, istream &scr) // Make sure the returned singleton object is // initialized from the main thread. - if (!headless) - { - GetMainThread(); - } - else - { - EglMainThread::Get(); - } - + GetMainThread(headless); std::thread worker_thread { @@ -1007,13 +999,6 @@ void ScriptController::PlayScript(Window win, istream &scr) std::move(script) }; - if (!headless) - { - SDLMainLoop(); - } - else - { - EglMainThread::Get().MainLoop(); - } + MainThreadLoop(headless); worker_thread.join(); } diff --git a/lib/sdl/sdl.cpp b/lib/sdl/sdl.cpp index 2747b5dd..d6b212f7 100644 --- a/lib/sdl/sdl.cpp +++ b/lib/sdl/sdl.cpp @@ -65,7 +65,7 @@ SdlWindow::Handle::~Handle() } } -SdlMainThread& GetMainThread() +SdlMainThread& GetSdlMainThread() { static SdlMainThread inst; return inst; @@ -80,7 +80,7 @@ SdlWindow::SdlWindow() {} void SdlWindow::StartSDL(bool server_mode) { - GetMainThread().MainLoop(server_mode); + GetSdlMainThread().MainLoop(server_mode); } const int default_dpi = 72; @@ -92,7 +92,7 @@ bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, is_multithreaded = false; #endif // create a new SDL window - handle = GetMainThread().GetHandle(this, title, x, y, w, h, legacyGlOnly); + handle = GetSdlMainThread().GetHandle(this, title, x, y, w, h, legacyGlOnly); // at this point, window should be up if (!handle.isInitialized()) @@ -108,7 +108,7 @@ bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, SdlWindow::~SdlWindow() { // Let the main SDL thread delete the handles - GetMainThread().DeleteHandle(std::move(handle)); + GetSdlMainThread().DeleteHandle(std::move(handle)); } void SdlWindow::windowEvent(SDL_WindowEvent& ew) @@ -258,8 +258,8 @@ void SdlWindow::mainIter() { if (!is_multithreaded) { - // Pull events from GetMainThread() object - GetMainThread().DispatchSDLEvents(); + // Pull events from GetSdlMainThread() object + GetSdlMainThread().DispatchSDLEvents(); } bool events_pending = false; bool sleep = false; @@ -365,7 +365,7 @@ void SdlWindow::mainIter() // To avoid this issue, we just call [NSOpenGLContext update] // immediately before the expose event. SdlCocoaPlatform* platform = - dynamic_cast(GetMainThread().GetPlatform()); + dynamic_cast(GetSdlMainThread().GetPlatform()); if (platform) { platform->ContextUpdate(); @@ -411,7 +411,7 @@ void SdlWindow::mainLoop() // TODO: Temporary workaround - after merge, everyone should update to // latest SDL SdlCocoaPlatform* mac_platform - = dynamic_cast(GetMainThread().GetPlatform()); + = dynamic_cast(GetSdlMainThread().GetPlatform()); if (mac_platform && mac_platform->UseThreadWorkaround()) { mac_platform->SwapWindow(); @@ -513,12 +513,12 @@ void SdlWindow::setWindowTitle(const std::string& title) void SdlWindow::setWindowTitle(const char * title) { - GetMainThread().SetWindowTitle(handle, title); + GetSdlMainThread().SetWindowTitle(handle, title); } void SdlWindow::setWindowSize(int w, int h) { - GetMainThread().SetWindowSize(handle, pixel_scale_x*w, pixel_scale_y*h); + GetSdlMainThread().SetWindowSize(handle, pixel_scale_x*w, pixel_scale_y*h); update_before_expose = true; } @@ -529,9 +529,9 @@ void SdlWindow::setWindowPos(int x, int y) SDL_WINDOWPOS_ISCENTERED(x); bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(y); - GetMainThread().SetWindowPosition(handle, - uc_x ? x : pixel_scale_x*x, - uc_y ? y : pixel_scale_y*y); + GetSdlMainThread().SetWindowPosition(handle, + uc_x ? x : pixel_scale_x*x, + uc_y ? y : pixel_scale_y*y); update_before_expose = true; } diff --git a/lib/sdl/sdl.hpp b/lib/sdl/sdl.hpp index 20f1c3e9..a7f3e039 100644 --- a/lib/sdl/sdl.hpp +++ b/lib/sdl/sdl.hpp @@ -23,7 +23,7 @@ typedef void (*TouchDelegate)(SDL_MultiGestureEvent&); typedef void (*WindowDelegate)(int, int); class SdlMainThread; -SdlMainThread& GetMainThread(); +SdlMainThread& GetSdlMainThread(); class SdlWindow : public GLWindow { diff --git a/lib/sdl/sdl_main.hpp b/lib/sdl/sdl_main.hpp index d5b627c2..b94b4b17 100644 --- a/lib/sdl/sdl_main.hpp +++ b/lib/sdl/sdl_main.hpp @@ -17,7 +17,7 @@ #include "sdl.hpp" -class SdlMainThread +class SdlMainThread : public MainThread { private: using Handle = SdlWindow::Handle; @@ -33,7 +33,7 @@ class SdlMainThread // Handles all SDL operations that are expected to be handled on the main // SDL thread (i.e. events and window creation) - void MainLoop(bool server_mode); + void MainLoop(bool server_mode) override; // Dequeues all incoming events from SDL, and queues them up to their // matching windows. Intended to be called only in single-threaded mode. From 700a67c04dcd744e21aded64fd2f32aa0548d91b Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 16:37:31 -0700 Subject: [PATCH 33/79] Separated EGL main thread. --- lib/CMakeLists.txt | 2 + lib/aux_vis.cpp | 1 + lib/egl/egl.cpp | 438 +-------------------------------------------- lib/egl/egl.hpp | 49 +---- makefile | 34 ++-- 5 files changed, 24 insertions(+), 500 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 3deedcd6..22ab9746 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -11,6 +11,7 @@ list(APPEND SOURCES egl/egl.cpp + egl/egl_main.cpp gl/renderer.cpp gl/renderer_core.cpp gl/shader.cpp @@ -40,6 +41,7 @@ list(APPEND SOURCES list(APPEND HEADERS egl/egl.hpp + egl/egl_main.hpp gl/attr_traits.hpp gl/platform_gl.hpp gl/renderer.hpp diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 9d77675a..a1441c78 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -20,6 +20,7 @@ #include "sdl/sdl.hpp" #include "sdl/sdl_main.hpp" #include "egl/egl.hpp" +#include "egl/egl_main.hpp" #include "palettes.hpp" #include "visual.hpp" #include "gl2ps.h" diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 2aaff7f8..480c8d25 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -11,6 +11,7 @@ #ifdef GLVIS_USE_EGL #include "egl.hpp" +#include "egl_main.hpp" #include "../aux_vis.hpp" #include #include @@ -22,443 +23,6 @@ #define PRINT_DEBUG(s) {} #endif -struct EglMainThread::CreateWndCmd -{ - EglWindow *wnd; - int w, h; - promise out_handle; -}; - -struct EglMainThread::ResizeWndCmd -{ - Handle *handle; - int w, h; -}; - -struct EglMainThread::DeleteWndCmd -{ - EglWindow *wnd; - Handle *handle; -}; - -struct EglMainThread::CtrlCmd -{ - CtrlCmdType type; - union - { - CreateWndCmd *create_cmd; - ResizeWndCmd *resize_cmd; - DeleteWndCmd *delete_cmd; - }; - - promise finished; -}; - -bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) -{ - Handle new_handle; - - // 1. Select an appropriate configuration - - const int multisamples = GetMultisample(); - - EGLint configAttribs[] = - { - EGL_SAMPLE_BUFFERS, (multisamples > 0)?(1):(0), // must be first - EGL_SAMPLES, multisamples, - EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, - EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, - EGL_BLUE_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_RED_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 24, - EGL_CONFORMANT, EGL_OPENGL_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_NONE - }; - - EGLint numConfigs; - - if (multisamples > 0) - { - if (!eglChooseConfig(disp, configAttribs, NULL, 0, &numConfigs) || - numConfigs < 1) - { - std::cerr << "EGL with multisampling is not supported, turning it off" << - std::endl; - // turn off multisampling - configAttribs[1] = 0; - } - } - - if (!eglChooseConfig(disp, configAttribs, &new_handle.eglCfg, 1, &numConfigs) || - numConfigs < 1) - { - std::cerr << "Cannot find working EGL configuration!" << std::endl; - return false; - } - - // 2. Create a surface - const EGLint pbufferAttribs[] = - { - EGL_WIDTH, cmd.w, - EGL_HEIGHT, cmd.h, - EGL_NONE - }; - - new_handle.surf = eglCreatePbufferSurface(disp, new_handle.eglCfg, - pbufferAttribs); - if (new_handle.surf == EGL_NO_SURFACE) - { - std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << - std::endl; - return false; - } - - windows.push_back(cmd.wnd); - cmd.out_handle.set_value(std::move(new_handle)); - - return true; -} - -bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) -{ - const EGLint pbufferAttribs[] = - { - EGL_WIDTH, cmd.w, - EGL_HEIGHT, cmd.h, - EGL_NONE - }; - - EGLSurface surf_new = eglCreatePbufferSurface(disp, cmd.handle->eglCfg, - pbufferAttribs); - if (surf_new == EGL_NO_SURFACE) - { - std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << - std::endl; - return false; - } - - if (!eglDestroySurface(disp, cmd.handle->surf)) - { - std::cerr << "Cannot destroy surface, error: " << eglGetError() << std::endl; - return false; - } - - cmd.handle->surf = surf_new; - - return true; -} - -bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) -{ - if (cmd.handle->ctx != EGL_NO_CONTEXT) - { - if (!eglDestroyContext(disp, cmd.handle->ctx)) - { - std::cerr << "Cannot destroy context, error: " << eglGetError() << std::endl; - return false; - } - cmd.handle->ctx = EGL_NO_CONTEXT; - } - - if (cmd.handle->surf != EGL_NO_SURFACE) - { - if (!eglDestroySurface(disp, cmd.handle->surf)) - { - std::cerr << "Cannot destroy surface, error: " << eglGetError() << std::endl; - return false; - } - cmd.handle->surf = EGL_NO_SURFACE; - } - - windows.remove(cmd.wnd); - - return true; -} - -void EglMainThread::QueueWndCmd(CtrlCmd cmd, bool sync) -{ - future wait_complete; - if (sync) - { - wait_complete = cmd.finished.get_future(); - } - // queue up our event - { - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_back(std::move(cmd)); - } - // wake up the main thread to handle our event - events_available.notify_all(); - - if (sync) { wait_complete.get(); } -} - -EglMainThread::EglMainThread() -{ - if (disp != EGL_NO_DISPLAY) - { - return; - } - - // Initialize EGL - disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (disp == EGL_NO_DISPLAY) - { - std::cerr << "FATAL: Failed to get an EGL display: " << eglGetError() << - std::endl; - return; - } - - EGLint major, minor; - - if (!eglInitialize(disp, &major, &minor)) - { - std::cerr << "FATAL: Failed to initialize EGL: " << eglGetError() << std::endl; - return; - } - - std::cout << "Using EGL " << major << "." << minor << std::endl; -} - -EglMainThread::~EglMainThread() -{ - eglTerminate(disp); -} - -EglMainThread &EglMainThread::Get() -{ - static EglMainThread singleton; - return singleton; -} - -EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, - int h, bool legacy_gl) -{ - CtrlCmd cmd; - cmd.type = CtrlCmdType::Create; - - CreateWndCmd crt_cmd; - cmd.create_cmd = &crt_cmd; - - crt_cmd.wnd = caller; - crt_cmd.w = w; - crt_cmd.h = h; - - auto res_handle = crt_cmd.out_handle.get_future(); - - QueueWndCmd(std::move(cmd), false); - - Handle out_hnd = res_handle.get(); - - if (out_hnd.surf == EGL_NO_SURFACE) - { - return out_hnd; - } - - // 3. Bind the API - if (!eglBindAPI(EGL_OPENGL_API)) - { - std::cerr << "Cannot bind OpenGL API, error: " << eglGetError() << std::endl; - return out_hnd; - } - - // 4. Create a context and make it current - if (legacy_gl) - { - // Try and probe for a core/compatibility context. Needed for Mac OS X, - // which will only support OpenGL 2.1 if you don't create a core context. - PRINT_DEBUG("Opening OpenGL core profile context..." << std::flush); - const EGLint attrListCore[] = - { - EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, - EGL_NONE - }; - out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, - attrListCore); - if (out_hnd.ctx == EGL_NO_CONTEXT) - { - PRINT_DEBUG("failed." << std::endl); - PRINT_DEBUG("Opening OpenGL compatibility profile context..." << std::flush); - const EGLint attrListCompat[] = - { - EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, - EGL_NONE - }; - out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, - attrListCompat); - if (out_hnd.ctx == EGL_NO_CONTEXT) - { - PRINT_DEBUG("failed." << std::endl); - } - else - { - PRINT_DEBUG("success!" << std::endl); - } - } - else - { - PRINT_DEBUG("success!" << std::endl); - } - } - - if (out_hnd.ctx == EGL_NO_CONTEXT) - { - PRINT_DEBUG("Opening OpenGL context with no flags..." << std::flush); - out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, - NULL); - if (out_hnd.ctx == EGL_NO_CONTEXT) - { - PRINT_DEBUG("failed." << std::endl); - std::cerr << "Cannot create an EGL context, error: " << eglGetError() << - std::endl; - - return out_hnd; - } - else - { - PRINT_DEBUG("success!" << std::endl); - } - } - - if (!eglMakeCurrent(disp, out_hnd.surf, out_hnd.surf, out_hnd.ctx)) - { - std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() - << std::endl; - - return out_hnd; - } - - return out_hnd; -} - -void EglMainThread::ResizeWindow(Handle &handle, int w, int h) -{ - if (!handle.isInitialized()) { return; } - - CtrlCmd cmd; - cmd.type = CtrlCmdType::Resize; - - ResizeWndCmd res_cmd; - cmd.resize_cmd = &res_cmd; - - res_cmd.handle = &handle; - res_cmd.w = w; - res_cmd.h = h; - - QueueWndCmd(std::move(cmd), true); - - if (!eglMakeCurrent(disp, handle.surf, handle.surf, handle.ctx)) - { - std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() - << std::endl; - return; - } -} - -void EglMainThread::DeleteWindow(EglWindow *caller, Handle &handle) -{ - if (!handle.isInitialized()) { return; } - - CtrlCmd cmd; - cmd.type = CtrlCmdType::Delete; - - DeleteWndCmd del_cmd; - cmd.delete_cmd = &del_cmd; - - del_cmd.wnd = caller; - del_cmd.handle = &handle; - - QueueWndCmd(std::move(cmd), true); -} - -void EglMainThread::MainLoop(bool server) -{ - server_mode = server; - bool terminating = false; - - while (true) - { - bool events_pending = false; - { - lock_guard evt_guard{window_cmd_mtx}; - events_pending = !window_cmds.empty(); - } - if (events_pending) - { - do - { - CtrlCmd cmd; - // Fetch next event from the queue - { - lock_guard evt_guard{window_cmd_mtx}; - cmd = std::move(window_cmds.front()); - window_cmds.pop_front(); - events_pending = !window_cmds.empty(); - // Skip non-delete events if terminating - if (terminating && cmd.type != CtrlCmdType::Delete) - { - continue; - } - } - - switch (cmd.type) - { - case CtrlCmdType::Create: - if (!CreateWndImpl(*cmd.create_cmd)) - { - terminating = true; - } - break; - case CtrlCmdType::Resize: - if (!ResizeWndImpl(*cmd.resize_cmd)) - { - terminating = true; - } - break; - case CtrlCmdType::Delete: - if (!DeleteWndImpl(*cmd.delete_cmd)) - { - terminating = true; - } - break; - } - - // Signal completion of the command, in case worker thread is waiting. - cmd.finished.set_value(); - } - while (events_pending); - } - - if (windows.size() == 0) - { - if (!server_mode || terminating) - { - break; - } - } - - if (terminating) - { - for (EglWindow *wnd : windows) - { - wnd->signalQuit(); - } - } - - { - unique_lock event_lock{window_cmd_mtx}; - events_available.wait(event_lock, [this]() - { - // Sleep until events from windows can be handled - return !window_cmds.empty(); - }); - } - } -} - EglWindow::EglWindow() { } diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index 8f35252a..f0a8ca6e 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -22,37 +22,8 @@ #include #include -class EglWindow; -class EglMainThread : public MainThread +class EglWindow : public GLWindow { - EGLDisplay disp{EGL_NO_DISPLAY}; - - bool server_mode{false}; - - std::list windows; - - struct CreateWndCmd; - struct ResizeWndCmd; - struct DeleteWndCmd; - - enum class CtrlCmdType - { - Create, - Resize, - Delete, - }; - - struct CtrlCmd; - - std::condition_variable events_available; - std::mutex window_cmd_mtx; - std::deque window_cmds; - - bool CreateWndImpl(CreateWndCmd &cmd); - bool ResizeWndImpl(ResizeWndCmd &cmd); - bool DeleteWndImpl(DeleteWndCmd &cmd); - void QueueWndCmd(CtrlCmd cmd, bool sync); - public: struct Handle { @@ -66,22 +37,8 @@ class EglMainThread : public MainThread } }; - EglMainThread(); - ~EglMainThread(); - - static EglMainThread& Get(); - EGLDisplay GetDisplay() const { return disp; } - - Handle CreateWindow(EglWindow *caller, int w, int h, bool legacy_gl); - void ResizeWindow(Handle &hnd, int w, int h); - void DeleteWindow(EglWindow *caller, Handle &hnd); - - void MainLoop(bool server = false) override; -}; - -class EglWindow : public GLWindow -{ - EglMainThread::Handle handle; +private: + Handle handle; bool running{false}; diff --git a/makefile b/makefile index 54bd79f9..6331f8a1 100644 --- a/makefile +++ b/makefile @@ -259,11 +259,11 @@ Ccc = $(strip $(CC) $(CFLAGS) $(GL_OPTS)) # generated with 'echo lib/egl/*.c* lib/gl/*.c* lib/sdl/*.c* lib/*.c*', does not include lib/*.m (Obj-C) ALL_SOURCE_FILES = \ - lib/egl/egl.cpp lib/gl/renderer_core.cpp lib/gl/renderer.cpp \ - lib/gl/renderer_ff.cpp lib/gl/shader.cpp lib/gl/types.cpp \ - lib/sdl/sdl.cpp lib/sdl/sdl_helper.cpp lib/sdl/sdl_main.cpp \ - lib/sdl/sdl_windows.cpp lib/sdl/sdl_x11.cpp lib/aux_js.cpp \ - lib/aux_vis.cpp lib/coll_reader.cpp lib/data_state.cpp \ + lib/egl/egl.cpp lib/egl/egl_main.cpp lib/gl/renderer_core.cpp \ + lib/gl/renderer.cpp lib/gl/renderer_ff.cpp lib/gl/shader.cpp \ + lib/gl/types.cpp lib/sdl/sdl.cpp lib/sdl/sdl_helper.cpp \ + lib/sdl/sdl_main.cpp lib/sdl/sdl_windows.cpp lib/sdl/sdl_x11.cpp \ + lib/aux_js.cpp lib/aux_vis.cpp lib/coll_reader.cpp lib/data_state.cpp \ lib/file_reader.cpp lib/font.cpp lib/gl2ps.c lib/gltf.cpp \ lib/glwindow.cpp lib/material.cpp lib/openglvis.cpp \ lib/palettes_base.cpp lib/palettes.cpp lib/script_controller.cpp \ @@ -281,18 +281,18 @@ COMMON_SOURCE_FILES = $(filter-out \ # generated with 'echo lib/egl/*.h* lib/gl/*.h* lib/sdl/*.h* lib/*.h*' HEADER_FILES = \ - lib/egl/egl.hpp lib/gl/attr_traits.hpp lib/gl/platform_gl.hpp \ - lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp lib/gl/renderer.hpp \ - lib/gl/shader.hpp lib/gl/types.hpp lib/sdl/sdl_helper.hpp \ - lib/sdl/sdl.hpp lib/sdl/sdl_mac.hpp lib/sdl/sdl_main.hpp \ - lib/sdl/sdl_windows.hpp lib/sdl/sdl_x11.hpp lib/aux_vis.hpp \ - lib/coll_reader.hpp lib/data_state.hpp lib/file_reader.hpp \ - lib/font.hpp lib/geom_utils.hpp lib/gl2ps.h lib/gltf.hpp \ - lib/glwindow.hpp lib/logo.hpp lib/material.hpp lib/openglvis.hpp \ - lib/palettes_base.hpp lib/palettes.hpp lib/script_controller.hpp \ - lib/stream_reader.hpp lib/threads.hpp lib/visual.hpp lib/vsdata.hpp \ - lib/vssolution.hpp lib/vssolution3d.hpp lib/vsvector.hpp \ - lib/vsvector3d.hpp lib/window.hpp + lib/egl/egl.hpp lib/egl/egl_main.hpp lib/gl/attr_traits.hpp \ + lib/gl/platform_gl.hpp lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp \ + lib/gl/renderer.hpp lib/gl/shader.hpp lib/gl/types.hpp \ + lib/sdl/sdl_helper.hpp lib/sdl/sdl.hpp lib/sdl/sdl_mac.hpp \ + lib/sdl/sdl_main.hpp lib/sdl/sdl_windows.hpp lib/sdl/sdl_x11.hpp \ + lib/aux_vis.hpp lib/coll_reader.hpp lib/data_state.hpp \ + lib/file_reader.hpp lib/font.hpp lib/geom_utils.hpp lib/gl2ps.h \ + lib/gltf.hpp lib/glwindow.hpp lib/logo.hpp lib/material.hpp \ + lib/openglvis.hpp lib/palettes_base.hpp lib/palettes.hpp \ + lib/script_controller.hpp lib/stream_reader.hpp lib/threads.hpp \ + lib/visual.hpp lib/vsdata.hpp lib/vssolution.hpp lib/vssolution3d.hpp \ + lib/vsvector.hpp lib/vsvector3d.hpp lib/window.hpp DESKTOP_SOURCE_FILES = $(COMMON_SOURCE_FILES) $(DESKTOP_ONLY_SOURCE_FILES) $(LOGO_FILE_CPP) WEB_SOURCE_FILES = $(COMMON_SOURCE_FILES) $(WEB_ONLY_SOURCE_FILES) From 05c6ac2e436283b7ba796e8c087a1a9d02d7ee2b Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 16:57:22 -0700 Subject: [PATCH 34/79] Added the forgotten egl_main files. --- lib/egl/egl_main.cpp | 461 +++++++++++++++++++++++++++++++++++++++++++ lib/egl/egl_main.hpp | 65 ++++++ 2 files changed, 526 insertions(+) create mode 100644 lib/egl/egl_main.cpp create mode 100644 lib/egl/egl_main.hpp diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp new file mode 100644 index 00000000..7d56a787 --- /dev/null +++ b/lib/egl/egl_main.cpp @@ -0,0 +1,461 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifdef GLVIS_USE_EGL +#include "egl_main.hpp" +#include "../aux_vis.hpp" +#include +#include + +#ifdef GLVIS_DEBUG +#define PRINT_DEBUG(s) std::cerr << s +#else +#define PRINT_DEBUG(s) {} +#endif + +struct EglMainThread::CreateWndCmd +{ + EglWindow *wnd; + int w, h; + promise out_handle; +}; + +struct EglMainThread::ResizeWndCmd +{ + Handle *handle; + int w, h; +}; + +struct EglMainThread::DeleteWndCmd +{ + EglWindow *wnd; + Handle *handle; +}; + +struct EglMainThread::CtrlCmd +{ + CtrlCmdType type; + union + { + CreateWndCmd *create_cmd; + ResizeWndCmd *resize_cmd; + DeleteWndCmd *delete_cmd; + }; + + promise finished; +}; + +bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) +{ + Handle new_handle; + + // 1. Select an appropriate configuration + + const int multisamples = GetMultisample(); + + EGLint configAttribs[] = + { + EGL_SAMPLE_BUFFERS, (multisamples > 0)?(1):(0), // must be first + EGL_SAMPLES, multisamples, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_CONFORMANT, EGL_OPENGL_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + + EGLint numConfigs; + + if (multisamples > 0) + { + if (!eglChooseConfig(disp, configAttribs, NULL, 0, &numConfigs) || + numConfigs < 1) + { + std::cerr << "EGL with multisampling is not supported, turning it off" << + std::endl; + // turn off multisampling + configAttribs[1] = 0; + } + } + + if (!eglChooseConfig(disp, configAttribs, &new_handle.eglCfg, 1, &numConfigs) || + numConfigs < 1) + { + std::cerr << "Cannot find working EGL configuration!" << std::endl; + return false; + } + + // 2. Create a surface + const EGLint pbufferAttribs[] = + { + EGL_WIDTH, cmd.w, + EGL_HEIGHT, cmd.h, + EGL_NONE + }; + + new_handle.surf = eglCreatePbufferSurface(disp, new_handle.eglCfg, + pbufferAttribs); + if (new_handle.surf == EGL_NO_SURFACE) + { + std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << + std::endl; + return false; + } + + windows.push_back(cmd.wnd); + cmd.out_handle.set_value(std::move(new_handle)); + + return true; +} + +bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) +{ + const EGLint pbufferAttribs[] = + { + EGL_WIDTH, cmd.w, + EGL_HEIGHT, cmd.h, + EGL_NONE + }; + + EGLSurface surf_new = eglCreatePbufferSurface(disp, cmd.handle->eglCfg, + pbufferAttribs); + if (surf_new == EGL_NO_SURFACE) + { + std::cerr << "Cannot create a pixel buffer, error: " << eglGetError() << + std::endl; + return false; + } + + if (!eglDestroySurface(disp, cmd.handle->surf)) + { + std::cerr << "Cannot destroy surface, error: " << eglGetError() << std::endl; + return false; + } + + cmd.handle->surf = surf_new; + + return true; +} + +bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) +{ + if (cmd.handle->ctx != EGL_NO_CONTEXT) + { + if (!eglDestroyContext(disp, cmd.handle->ctx)) + { + std::cerr << "Cannot destroy context, error: " << eglGetError() << std::endl; + return false; + } + cmd.handle->ctx = EGL_NO_CONTEXT; + } + + if (cmd.handle->surf != EGL_NO_SURFACE) + { + if (!eglDestroySurface(disp, cmd.handle->surf)) + { + std::cerr << "Cannot destroy surface, error: " << eglGetError() << std::endl; + return false; + } + cmd.handle->surf = EGL_NO_SURFACE; + } + + windows.remove(cmd.wnd); + + return true; +} + +void EglMainThread::QueueWndCmd(CtrlCmd cmd, bool sync) +{ + future wait_complete; + if (sync) + { + wait_complete = cmd.finished.get_future(); + } + // queue up our event + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(cmd)); + } + // wake up the main thread to handle our event + events_available.notify_all(); + + if (sync) { wait_complete.get(); } +} + +EglMainThread::EglMainThread() +{ + if (disp != EGL_NO_DISPLAY) + { + return; + } + + // Initialize EGL + disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (disp == EGL_NO_DISPLAY) + { + std::cerr << "FATAL: Failed to get an EGL display: " << eglGetError() << + std::endl; + return; + } + + EGLint major, minor; + + if (!eglInitialize(disp, &major, &minor)) + { + std::cerr << "FATAL: Failed to initialize EGL: " << eglGetError() << std::endl; + return; + } + + std::cout << "Using EGL " << major << "." << minor << std::endl; +} + +EglMainThread::~EglMainThread() +{ + eglTerminate(disp); +} + +EglMainThread &EglMainThread::Get() +{ + static EglMainThread singleton; + return singleton; +} + +EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, + int h, bool legacy_gl) +{ + CtrlCmd cmd; + cmd.type = CtrlCmdType::Create; + + CreateWndCmd crt_cmd; + cmd.create_cmd = &crt_cmd; + + crt_cmd.wnd = caller; + crt_cmd.w = w; + crt_cmd.h = h; + + auto res_handle = crt_cmd.out_handle.get_future(); + + QueueWndCmd(std::move(cmd), false); + + Handle out_hnd = res_handle.get(); + + if (out_hnd.surf == EGL_NO_SURFACE) + { + return out_hnd; + } + + // 3. Bind the API + if (!eglBindAPI(EGL_OPENGL_API)) + { + std::cerr << "Cannot bind OpenGL API, error: " << eglGetError() << std::endl; + return out_hnd; + } + + // 4. Create a context and make it current + if (legacy_gl) + { + // Try and probe for a core/compatibility context. Needed for Mac OS X, + // which will only support OpenGL 2.1 if you don't create a core context. + PRINT_DEBUG("Opening OpenGL core profile context..." << std::flush); + const EGLint attrListCore[] = + { + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_NONE + }; + out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, + attrListCore); + if (out_hnd.ctx == EGL_NO_CONTEXT) + { + PRINT_DEBUG("failed." << std::endl); + PRINT_DEBUG("Opening OpenGL compatibility profile context..." << std::flush); + const EGLint attrListCompat[] = + { + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, + EGL_NONE + }; + out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, + attrListCompat); + if (out_hnd.ctx == EGL_NO_CONTEXT) + { + PRINT_DEBUG("failed." << std::endl); + } + else + { + PRINT_DEBUG("success!" << std::endl); + } + } + else + { + PRINT_DEBUG("success!" << std::endl); + } + } + + if (out_hnd.ctx == EGL_NO_CONTEXT) + { + PRINT_DEBUG("Opening OpenGL context with no flags..." << std::flush); + out_hnd.ctx = eglCreateContext(disp, out_hnd.eglCfg, EGL_NO_CONTEXT, + NULL); + if (out_hnd.ctx == EGL_NO_CONTEXT) + { + PRINT_DEBUG("failed." << std::endl); + std::cerr << "Cannot create an EGL context, error: " << eglGetError() << + std::endl; + + return out_hnd; + } + else + { + PRINT_DEBUG("success!" << std::endl); + } + } + + if (!eglMakeCurrent(disp, out_hnd.surf, out_hnd.surf, out_hnd.ctx)) + { + std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() + << std::endl; + + return out_hnd; + } + + return out_hnd; +} + +void EglMainThread::ResizeWindow(Handle &handle, int w, int h) +{ + if (!handle.isInitialized()) { return; } + + CtrlCmd cmd; + cmd.type = CtrlCmdType::Resize; + + ResizeWndCmd res_cmd; + cmd.resize_cmd = &res_cmd; + + res_cmd.handle = &handle; + res_cmd.w = w; + res_cmd.h = h; + + QueueWndCmd(std::move(cmd), true); + + if (!eglMakeCurrent(disp, handle.surf, handle.surf, handle.ctx)) + { + std::cerr << "Cannot set the EGL context as current, error: " << eglGetError() + << std::endl; + return; + } +} + +void EglMainThread::DeleteWindow(EglWindow *caller, Handle &handle) +{ + if (!handle.isInitialized()) { return; } + + CtrlCmd cmd; + cmd.type = CtrlCmdType::Delete; + + DeleteWndCmd del_cmd; + cmd.delete_cmd = &del_cmd; + + del_cmd.wnd = caller; + del_cmd.handle = &handle; + + QueueWndCmd(std::move(cmd), true); +} + +void EglMainThread::MainLoop(bool server) +{ + server_mode = server; + bool terminating = false; + + while (true) + { + bool events_pending = false; + { + lock_guard evt_guard{window_cmd_mtx}; + events_pending = !window_cmds.empty(); + } + if (events_pending) + { + do + { + CtrlCmd cmd; + // Fetch next event from the queue + { + lock_guard evt_guard{window_cmd_mtx}; + cmd = std::move(window_cmds.front()); + window_cmds.pop_front(); + events_pending = !window_cmds.empty(); + // Skip non-delete events if terminating + if (terminating && cmd.type != CtrlCmdType::Delete) + { + continue; + } + } + + switch (cmd.type) + { + case CtrlCmdType::Create: + if (!CreateWndImpl(*cmd.create_cmd)) + { + terminating = true; + } + break; + case CtrlCmdType::Resize: + if (!ResizeWndImpl(*cmd.resize_cmd)) + { + terminating = true; + } + break; + case CtrlCmdType::Delete: + if (!DeleteWndImpl(*cmd.delete_cmd)) + { + terminating = true; + } + break; + } + + // Signal completion of the command, in case worker thread is waiting. + cmd.finished.set_value(); + } + while (events_pending); + } + + if (windows.size() == 0) + { + if (!server_mode || terminating) + { + break; + } + } + + if (terminating) + { + for (EglWindow *wnd : windows) + { + wnd->signalQuit(); + } + } + + { + unique_lock event_lock{window_cmd_mtx}; + events_available.wait(event_lock, [this]() + { + // Sleep until events from windows can be handled + return !window_cmds.empty(); + }); + } + } +} + +#endif //GLVIS_USE_EGL diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp new file mode 100644 index 00000000..d44b09f0 --- /dev/null +++ b/lib/egl/egl_main.hpp @@ -0,0 +1,65 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_EGL_MAIN_HPP +#define GLVIS_EGL_MAIN_HPP +#ifdef GLVIS_USE_EGL + +#include "egl.hpp" + +class EglMainThread : public MainThread +{ + using Handle = EglWindow::Handle; + EGLDisplay disp{EGL_NO_DISPLAY}; + + bool server_mode{false}; + + std::list windows; + + struct CreateWndCmd; + struct ResizeWndCmd; + struct DeleteWndCmd; + + enum class CtrlCmdType + { + Create, + Resize, + Delete, + }; + + struct CtrlCmd; + + std::condition_variable events_available; + std::mutex window_cmd_mtx; + std::deque window_cmds; + + bool CreateWndImpl(CreateWndCmd &cmd); + bool ResizeWndImpl(ResizeWndCmd &cmd); + bool DeleteWndImpl(DeleteWndCmd &cmd); + void QueueWndCmd(CtrlCmd cmd, bool sync); + +public: + + EglMainThread(); + ~EglMainThread(); + + static EglMainThread& Get(); + EGLDisplay GetDisplay() const { return disp; } + + Handle CreateWindow(EglWindow *caller, int w, int h, bool legacy_gl); + void ResizeWindow(Handle &hnd, int w, int h); + void DeleteWindow(EglWindow *caller, Handle &hnd); + + void MainLoop(bool server = false) override; +}; + +#endif //GLVIS_USE_EGL +#endif //GLVIS_EGL_MAIN_HPP From 50ed4f2b9dde4ac2a421e2bdda65a7e35f0b46e9 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 17:03:52 -0700 Subject: [PATCH 35/79] Fixed the windows counting if the main thread is too fast. --- lib/egl/egl_main.cpp | 11 ++++++++++- lib/egl/egl_main.hpp | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 7d56a787..1580b661 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -116,6 +116,14 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) } windows.push_back(cmd.wnd); + if (num_windows < 0) + { + num_windows = 1; + } + else + { + num_windows++; + } cmd.out_handle.set_value(std::move(new_handle)); return true; @@ -173,6 +181,7 @@ bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) } windows.remove(cmd.wnd); + num_windows--; return true; } @@ -431,7 +440,7 @@ void EglMainThread::MainLoop(bool server) while (events_pending); } - if (windows.size() == 0) + if (num_windows == 0) { if (!server_mode || terminating) { diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index d44b09f0..8d04509c 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -23,6 +23,7 @@ class EglMainThread : public MainThread bool server_mode{false}; std::list windows; + int num_windows{-1}; struct CreateWndCmd; struct ResizeWndCmd; From 07705ea656bf3229770bfd348c0c62eb77221e34 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 17:08:29 -0700 Subject: [PATCH 36/79] Fixed fencing of EGL in aux_vis.cpp. --- lib/aux_vis.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index a1441c78..57c775b0 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -58,10 +58,12 @@ static bool wndUseHiDPI = true; MainThread& GetMainThread(bool headless) { +#ifdef GLVIS_USE_EGL if (headless) { return EglMainThread::Get(); } +#endif return GetSdlMainThread(); } From 6c4d47bb523419e68c83c2a2b34feb988376c97c Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 18:50:20 -0700 Subject: [PATCH 37/79] Small fix for aux_js. --- lib/aux_js.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 49a34fac..5ab9d038 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -244,7 +244,7 @@ void processKey(int sym, bool ctrl=false, bool shift=false, bool alt=false) mod |= ctrl ? KMOD_CTRL : 0; mod |= shift ? KMOD_SHIFT : 0; mod |= alt ? KMOD_ALT : 0; - win.wnd->callKeyDown(sym, mod); + win.wnd->callKeyDown(sym, (SDL_Keymod)mod); } void setupResizeEventCallback(const std::string & id) From 35e9156e04353c06b082e11018a338e5fc2e5108 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 19:10:15 -0700 Subject: [PATCH 38/79] Removed using namespace std pollution. --- lib/aux_vis.cpp | 1 + lib/aux_vis.hpp | 30 +++++++++++++------------ lib/egl/egl.cpp | 12 ++++++---- lib/egl/egl_main.cpp | 2 ++ lib/font.cpp | 3 +++ lib/font.hpp | 12 +++++----- lib/gl/renderer.cpp | 4 ++-- lib/gl/renderer.hpp | 14 ++++++------ lib/gl/renderer_core.cpp | 2 ++ lib/gl/renderer_core.hpp | 4 ++-- lib/gl/renderer_ff.cpp | 6 ++--- lib/gl/shader.cpp | 4 ++-- lib/gl/types.hpp | 2 -- lib/openglvis.cpp | 17 ++++++++------- lib/palettes_base.cpp | 1 + lib/palettes_base.hpp | 46 +++++++++++++++++++-------------------- lib/script_controller.hpp | 18 +++++++-------- lib/sdl/sdl.cpp | 3 +-- lib/sdl/sdl_main.cpp | 2 ++ lib/sdl/sdl_main.hpp | 18 +++++++-------- lib/threads.hpp | 4 ++-- lib/vsdata.hpp | 12 +++++----- lib/window.cpp | 4 ++-- 23 files changed, 117 insertions(+), 104 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 57c775b0..bf8f20a3 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -37,6 +37,7 @@ #endif using namespace mfem; +using namespace std; static thread_local int visualize = 0; static thread_local VisualizationScene *locscene = NULL; diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index bf73aa28..f0e1916d 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -138,16 +138,16 @@ float GetLineWidthMS(); void InitFont(); GlVisFont * GetFont(); -bool SetFont(const vector& patterns, int height); +bool SetFont(const std::vector& patterns, int height); void SetFont(const std::string& fn); void SetUseHiDPI(bool status); bool GetUseHiDPI(); -function NumberFormatter(int precision=4, char format='d', - bool showsign=false); -function NumberFormatter(string formatting); -bool isValidNumberFormatting(const string& formatting); +std::function NumberFormatter(int precision=4, + char format='d', bool showsign=false); +std::function NumberFormatter(std::string formatting); +bool isValidNumberFormatting(const std::string& formatting); // This is a helper function for prompting the user for inputs. The benefit // over using just `cin >> input` is that you can specify a type and optionally @@ -155,22 +155,23 @@ bool isValidNumberFormatting(const string& formatting); // True function. If the input cannot be type casted to the expected type, or // if it fails the validation, the user is asked again for a new input. template -T prompt(const string question, +T prompt(const std::string question, const T* default_value = nullptr, -function validator = [](T) { return true; }) +std::function validator = [](T) { return true; }) { T input; - string strInput; + std::string strInput; while (true) { - cout << question << " "; - getline(cin, strInput); - stringstream buf(strInput); + std::cout << question << " "; + std::getline(std::cin, strInput); + std::stringstream buf(strInput); if (strInput.empty() && default_value != nullptr) { - cout << "Input empty. Using default value: " << *default_value << endl; + std::cout << "Input empty. Using default value: " << *default_value + << std::endl; return *default_value; } @@ -182,12 +183,13 @@ function validator = [](T) { return true; }) } else { - cout << "Input is not valid. Please try again." << endl; + std::cout << "Input is not valid. Please try again." << std::endl; } } else { - cout << "Input can not be casted to expected type. Please try again." << endl; + std::cout << "Input can not be casted to expected type. Please try again." + << std::endl; } } return input; diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 480c8d25..5e7ab865 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -23,6 +23,10 @@ #define PRINT_DEBUG(s) {} #endif +using namespace std; + +using namespace std; + EglWindow::EglWindow() { } @@ -45,15 +49,15 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, glEnable(GL_DEBUG_OUTPUT); #endif - PRINT_DEBUG("EGL context is ready" << std::endl); + PRINT_DEBUG("EGL context is ready" << endl); return initGLEW(legacyGlOnly); } -void EglWindow::queueEvents(std::vector events) +void EglWindow::queueEvents(vector events) { { - std::lock_guard evt_guard{event_mutex}; + lock_guard evt_guard{event_mutex}; waiting_events.insert(waiting_events.end(), events.begin(), events.end()); } if (is_multithreaded) @@ -185,7 +189,7 @@ void EglWindow::signalQuit() queueEvents({{EventType::Quit}}); } -void EglWindow::screenshot(std::string filename, bool convert) +void EglWindow::screenshot(string filename, bool convert) { screenshot_filename = filename; Event::Events e; diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 1580b661..038abe65 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -21,6 +21,8 @@ #define PRINT_DEBUG(s) {} #endif +using namespace std; + struct EglMainThread::CreateWndCmd { EglWindow *wnd; diff --git a/lib/font.cpp b/lib/font.cpp index 74ca1bdb..5f041eae 100644 --- a/lib/font.cpp +++ b/lib/font.cpp @@ -14,6 +14,9 @@ #include "font.hpp" #include "aux_vis.hpp" +using std::cout; +using std::endl; + struct vert_tex2d { float x, y; diff --git a/lib/font.hpp b/lib/font.hpp index 24ec2873..619b09f3 100644 --- a/lib/font.hpp +++ b/lib/font.hpp @@ -21,15 +21,13 @@ #include "gl/platform_gl.hpp" -using namespace std; - class GlVisFont { public: struct glyph { - uint32_t w, h; - int32_t bear_x, bear_y; + std::uint32_t w, h; + std::int32_t bear_x, bear_y; float adv_x, adv_y; float tex_x; }; @@ -42,7 +40,7 @@ class GlVisFont glyph font_chars[256]; float tex_w; float tex_h; - uint32_t font_tex; + std::uint32_t font_tex; FT_Library library; FT_Face face; @@ -53,7 +51,7 @@ class GlVisFont const glyph &GetTexChar(char c) const { - return font_chars[(uint8_t) c]; + return font_chars[(std::uint8_t) c]; } /// Get the width and height of the bounding box containing the rendered text @@ -66,7 +64,7 @@ class GlVisFont { if (FT_Init_FreeType(&library)) { - cout << "GLVis: Can not initialize FreeType library!" << endl; + std::cout << "GLVis: Can not initialize FreeType library!" << std::endl; } init = true; } diff --git a/lib/gl/renderer.cpp b/lib/gl/renderer.cpp index ad9421ef..03ba46e9 100644 --- a/lib/gl/renderer.cpp +++ b/lib/gl/renderer.cpp @@ -148,7 +148,7 @@ void MeshRenderer::render(const RenderQueue& queue) if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - cerr << "Unable to create multisampled renderbuffer." << flush; + std::cerr << "Unable to create multisampled renderbuffer." << std::flush; glDeleteFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -268,7 +268,7 @@ void MeshRenderer::render(const RenderQueue& queue) if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - cerr << "Unable to create resolve renderbuffer." << endl; + std::cerr << "Unable to create resolve renderbuffer." << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/lib/gl/renderer.hpp b/lib/gl/renderer.hpp index f0c9b524..5d15da78 100644 --- a/lib/gl/renderer.hpp +++ b/lib/gl/renderer.hpp @@ -55,11 +55,11 @@ struct RenderParams bool contains_translucent; }; -typedef vector> RenderQueue; +typedef std::vector> RenderQueue; struct SceneInfo { - vector needs_buffering; + std::vector needs_buffering; RenderQueue queue; }; @@ -88,9 +88,9 @@ struct FeedbackText struct CaptureBuffer { - vector lines; - vector triangles; - vector text; + std::vector lines; + std::vector triangles; + std::vector text; }; // OpenGL device interface representing rendering capabilities @@ -195,7 +195,7 @@ class GLDevice class MeshRenderer { - unique_ptr device; + std::unique_ptr device; bool msaa_enable; int msaa_samples; GLuint color_tex, alpha_tex, font_tex; @@ -243,7 +243,7 @@ class MeshRenderer std::cerr << "GL_MAX_SAMPLES = " << msaa_samples << " but requested " << samples << "x MSAA. "; std::cerr << "Setting antialiasing mode to " - << msaa_samples << "x MSAA." << endl; + << msaa_samples << "x MSAA." << std::endl; } else { diff --git a/lib/gl/renderer_core.cpp b/lib/gl/renderer_core.cpp index 4b026307..3e4af146 100644 --- a/lib/gl/renderer_core.cpp +++ b/lib/gl/renderer_core.cpp @@ -40,6 +40,8 @@ const std::string PRINTING_FS = namespace gl3 { +using namespace std; + const std::vector CoreGLDevice::unif_list = { "useClipPlane", diff --git a/lib/gl/renderer_core.hpp b/lib/gl/renderer_core.hpp index 41f41ab2..f3ec9872 100644 --- a/lib/gl/renderer_core.hpp +++ b/lib/gl/renderer_core.hpp @@ -68,9 +68,9 @@ class CoreGLDevice : public GLDevice void drawDeviceBufferImpl(GLenum shape, int count, bool indexed); void processTriangleXfbBuffer(CaptureBuffer& cbuf, - const vector& verts); + const std::vector& verts); void processLineXfbBuffer(CaptureBuffer& cbuf, - const vector& verts); + const std::vector& verts); public: CoreGLDevice() diff --git a/lib/gl/renderer_ff.cpp b/lib/gl/renderer_ff.cpp index e507acb6..a4f023bb 100644 --- a/lib/gl/renderer_ff.cpp +++ b/lib/gl/renderer_ff.cpp @@ -192,7 +192,7 @@ void FFGLDevice::bufferToDevice(array_layout layout, IVertexBuffer& buf) bufferFFDeviceImpl(static_cast&>(buf)); break; default: - cerr << "WARNING: Unhandled vertex layout " << layout << endl; + std::cerr << "WARNING: Unhandled vertex layout " << layout << std::endl; } } @@ -232,7 +232,7 @@ void FFGLDevice::bufferToDevice(array_layout layout, IIndexedBuffer& buf) bufferFFDeviceImpl(static_cast&>(buf)); break; default: - cerr << "WARNING: Unhandled vertex layout " << layout << endl; + std::cerr << "WARNING: Unhandled vertex layout " << layout << std::endl; } } @@ -370,7 +370,7 @@ void FFGLDevice::captureXfbBuffer(PaletteState& pal, CaptureBuffer& cbuf, return; } // allocate feedback buffer - vector xfb_buf; + std::vector xfb_buf; xfb_buf.resize(sizebuf); glFeedbackBuffer(sizebuf, fbType, xfb_buf.data()); // draw with feedback capture diff --git a/lib/gl/shader.cpp b/lib/gl/shader.cpp index 9f72aa84..46c0a676 100644 --- a/lib/gl/shader.cpp +++ b/lib/gl/shader.cpp @@ -269,7 +269,7 @@ bool ShaderProgram::linkShaders(const std::vector& shaders) glGetProgramiv(program_id, GL_LINK_STATUS, &stat); if (stat == GL_FALSE) { - cerr << "fatal: Shader linking failed" << endl; + std::cerr << "fatal: Shader linking failed" << std::endl; } return (stat == GL_TRUE); } @@ -283,7 +283,7 @@ void ShaderProgram::mapShaderUniforms() for (int i = 0; i < num_unifs; i++) { - vector unif_buf(max_unif_len+1); + std::vector unif_buf(max_unif_len+1); GLsizei name_length; GLint gl_size; GLenum gl_type; diff --git a/lib/gl/types.hpp b/lib/gl/types.hpp index 43cd9563..4d8beeb1 100644 --- a/lib/gl/types.hpp +++ b/lib/gl/types.hpp @@ -33,8 +33,6 @@ #include "platform_gl.hpp" -using namespace std; - class VisualizationScene; namespace gl3 diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index e0e9cadd..c092dddb 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -15,6 +15,7 @@ #include "aux_vis.hpp" using namespace mfem; +using std::cout; const int NUM_MATERIALS = 5; extern Material materials[5]; @@ -481,7 +482,7 @@ VisualizationScene::AddPaletteMaterial(glTF_Builder &bld) /* wrapT: */ glTF_Builder::wrap_type::CLAMP_TO_EDGE); // create palette image const int palette_size = palette.GetNumColors(); - vector> palette_data = palette.GetPalette()->GetData(); + std::vector> palette_data = palette.GetPalette()->GetData(); #if 0 glGetTextureImage( palette.GetColorTexture(), 0, @@ -565,7 +566,7 @@ VisualizationScene::AddPaletteLinesMaterial( } glTF_Builder::node_id -VisualizationScene::AddModelNode(glTF_Builder &bld, const string &nodeName) +VisualizationScene::AddModelNode(glTF_Builder &bld, const std::string &nodeName) { auto new_node = bld.addNode(nodeName); // Coordinate system switch: (x,y,z) -> (x,z,-y). @@ -584,12 +585,12 @@ VisualizationScene::AddModelNode(glTF_Builder &bld, const string &nodeName) // Used in VisualizationScene::AddTriangles() below. void minmax(const float *data, size_t components, size_t stride, size_t count, - vector &mins, vector &maxs) + std::vector &mins, std::vector &maxs) { if (count == 0) { - mins.assign(components, +numeric_limits::infinity()); - maxs.assign(components, -numeric_limits::infinity()); + mins.assign(components, +std::numeric_limits::infinity()); + maxs.assign(components, -std::numeric_limits::infinity()); return; } mins.resize(components); @@ -654,7 +655,7 @@ int VisualizationScene::AddTriangles(glTF_Builder &bld, } const gl3::IVertexBuffer *surf_buf = nullptr; const gl3::IIndexedBuffer *surf_ibuf = nullptr; - const vector *surf_indices = nullptr; + const std::vector *surf_indices = nullptr; if (num_ibuf) { surf_buf = surf_ibuf = gl_drawable.indexed_buffers[ibuf_layout][1].get(); @@ -669,7 +670,7 @@ int VisualizationScene::AddTriangles(glTF_Builder &bld, const size_t surf_vertices_stride = surf_buf->getStride(); // in bytes const float *surf_vertices_data = reinterpret_cast(surf_buf->getData()); - vector vmins, vmaxs; + std::vector vmins, vmaxs; int components = surf_vertices_stride/sizeof(float); switch (buf_layout) { @@ -884,7 +885,7 @@ int VisualizationScene::AddLines(glTF_Builder &bld, const size_t lines_vertices_stride = lines_buf->getStride(); // in bytes const float *lines_vertices_data = reinterpret_cast(lines_buf->getData()); - vector vmins, vmaxs; + std::vector vmins, vmaxs; int components = lines_vertices_stride/sizeof(float); switch (buf_layout) { diff --git a/lib/palettes_base.cpp b/lib/palettes_base.cpp index 1393c2d2..28c85d43 100644 --- a/lib/palettes_base.cpp +++ b/lib/palettes_base.cpp @@ -13,6 +13,7 @@ #include "palettes_default.cpp" #include "gl/renderer.hpp" +using namespace std; void RGBAf::Print(ostream& os) const { diff --git a/lib/palettes_base.hpp b/lib/palettes_base.hpp index 7ecd437f..61df7859 100644 --- a/lib/palettes_base.hpp +++ b/lib/palettes_base.hpp @@ -22,8 +22,6 @@ #include #include -using namespace std; - struct RGBAf { float r, g, b, a; @@ -31,31 +29,31 @@ struct RGBAf constexpr RGBAf(float r = 0.0, float g = 0.0, float b = 0.0, float a = 1.0) : r(r), g(g), b(b), a(a) {} - void Print(ostream& os = cout) const; + void Print(std::ostream& os = std::cout) const; - array AsArray() const { return {r, g, b, a}; } + std::array AsArray() const { return {r, g, b, a}; } }; class Palette { public: - const string name; + const std::string name; /// Constructors - Palette(const string& name) : name(name) {} + Palette(const std::string& name) : name(name) {} /// Constructor from vector of RGBAf - Palette(const string& name, const vector& colors) + Palette(const std::string& name, const std::vector& colors) : name(name), colors(colors) {} /// Constructor from Nx3 array template - Palette(const string& name, const array,N>& arr); + Palette(const std::string& name, const std::array,N>& arr); /// Constructor from Nx4 array template - Palette(const string& name, const array,N>& arr); + Palette(const std::string& name, const std::array,N>& arr); /// Get size int Size() const { return colors.size(); } @@ -64,25 +62,25 @@ class Palette void AddColor(float r, float g, float b, float a = 1.0); /// Print each color of this palette to a stream - void Print(ostream& os = cout) const; + void Print(std::ostream& os = std::cout) const; /// Get color at index i (optionally, use reversed order) RGBAf Color(int i, bool reversed = false) const; /// Get all colors as a vector of float arrays - vector> GetData(bool reversed = false) const; + std::vector> GetData(bool reversed = false) const; /// Are any alpha != 1.0? bool IsTranslucent() const; private: - vector colors; + std::vector colors; }; -using TexHandle = gl3::resource::TextureHandle; /// Generates the texture data for a given palette, to be used in OpenGL class Texture { + using TexHandle = gl3::resource::TextureHandle; public: // What type of texture is this (discrete, smooth, alphamap) enum class TextureType @@ -147,8 +145,8 @@ class Texture static void InitStaticGL(); /// Generate alpha/regular texture data - vector GenerateAlphaTextureData(); - vector> GenerateTextureData(); + std::vector GenerateAlphaTextureData(); + std::vector> GenerateTextureData(); /// Set the number of cycles void SetCycles(int cycles); @@ -173,10 +171,10 @@ std::unique_ptr as_unique(Args&&... args) class PaletteRegistry { private: - vector> palettes; + std::vector> palettes; /// Find the index of a palette by name - int GetIndexByName(const string& name) const; + int GetIndexByName(const std::string& name) const; public: /// Empty constructor @@ -184,28 +182,28 @@ class PaletteRegistry /// Constructor via a const vector of Palettes; if name already exists, skip /// Used for loading compiled palettes (i.e. `palettes_default.cpp`) - PaletteRegistry(const vector& paletteRefs); + PaletteRegistry(const std::vector& paletteRefs); /// Adds an existing palette to the registry void AddPalette(Palette& palette); /// Create a new palette with the given name and add it to the registry - void AddPalette(const string& name); + void AddPalette(const std::string& name); /// Returns true if name is unique - bool IsNameUnique(const string& name) const; + bool IsNameUnique(const std::string& name) const; /// Get a palette pointer by index; if not found, returns last palette Palette* Get(int index) const; /// Get a palette pointer by name; if not found, returns last palette - Palette* Get(const string& name) const; + Palette* Get(const std::string& name) const; /// Prints a summary (index + name) of all palettes - void PrintSummary(ostream& os = cout) const; + void PrintSummary(std::ostream& os = std::cout) const; /// Prints all colors for all palettes - void PrintAll(ostream& os = cout) const; + void PrintAll(std::ostream& os = std::cout) const; /// Number of palettes in the registry int NumPalettes() const { return palettes.size(); } @@ -218,7 +216,7 @@ class PaletteRegistry ... see `share/palettes-crameri.txt` for an example */ - void Load(const string& palette_filename); + void Load(const std::string& palette_filename); }; diff --git a/lib/script_controller.hpp b/lib/script_controller.hpp index a16348a4..04d1d833 100644 --- a/lib/script_controller.hpp +++ b/lib/script_controller.hpp @@ -24,22 +24,22 @@ class ScriptController { Window win; - string dc_protocol = string_default; + std::string dc_protocol = string_default; int dc_cycle = 0; - istream *script = NULL; + std::istream *script = NULL; int scr_running = 0; int scr_level = 0; std::unique_ptr init_nodes; double scr_min_val, scr_max_val; - static int ScriptReadSolution(istream &scr, DataState &state); - static int ScriptReadQuadrature(istream &scr, DataState &state); - static int ScriptReadParSolution(istream &scr, DataState &state); - static int ScriptReadParQuadrature(istream &scr, DataState &state); - int ScriptReadDisplMesh(istream &scr, DataState &state); - int ScriptReadDataColl(istream &scr, DataState &state, bool mesh_only = true, - bool quad = false); + static int ScriptReadSolution(std::istream &scr, DataState &state); + static int ScriptReadQuadrature(std::istream &scr, DataState &state); + static int ScriptReadParSolution(std::istream &scr, DataState &state); + static int ScriptReadParQuadrature(std::istream &scr, DataState &state); + int ScriptReadDisplMesh(std::istream &scr, DataState &state); + int ScriptReadDataColl(std::istream &scr, DataState &state, + bool mesh_only = true, bool quad = false); //key handlers using thread-local singleton static thread_local ScriptController *script_ctrl; diff --git a/lib/sdl/sdl.cpp b/lib/sdl/sdl.cpp index d6b212f7..197aba8b 100644 --- a/lib/sdl/sdl.cpp +++ b/lib/sdl/sdl.cpp @@ -22,8 +22,7 @@ #include "sdl_mac.hpp" #endif -using std::cerr; -using std::endl; +using namespace std; #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s diff --git a/lib/sdl/sdl_main.cpp b/lib/sdl/sdl_main.cpp index 74b28c5e..91dac48c 100644 --- a/lib/sdl/sdl_main.cpp +++ b/lib/sdl/sdl_main.cpp @@ -30,6 +30,8 @@ extern int GetMultisample(); extern bool GetUseHiDPI(); +using namespace std; + struct SdlMainThread::CreateWindowCmd { SdlWindow* wnd; diff --git a/lib/sdl/sdl_main.hpp b/lib/sdl/sdl_main.hpp index b94b4b17..2a2ae4ad 100644 --- a/lib/sdl/sdl_main.hpp +++ b/lib/sdl/sdl_main.hpp @@ -113,14 +113,14 @@ class SdlMainThread : public MainThread // A flag indicating whether the main loop will *begin* terminating bool terminating {false}; - unique_ptr bg_wnd{nullptr, SDL_DestroyWindow}; + std::unique_ptr bg_wnd{nullptr, SDL_DestroyWindow}; // ------------------------------------------------------------------------- // Objects for handling passing of window control commands to the main event // loop. - mutex window_cmd_mtx; - vector window_cmds; + std::mutex window_cmd_mtx; + std::vector window_cmds; int num_windows {-1}; // -1: waiting for window to be created @@ -128,17 +128,17 @@ class SdlMainThread : public MainThread // Objects for handling dispatching events from the main event loop to // worker threads. - unordered_map hwnd_to_window; - unordered_map> wnd_events; + std::unordered_map hwnd_to_window; + std::unordered_map> wnd_events; std::set fingers; bool disable_mouse {false}; - mutex gl_ctx_mtx; + std::mutex gl_ctx_mtx; - mutex event_mtx; - condition_variable event_cv; + std::mutex event_mtx; + std::condition_variable event_cv; bool try_create_platform{false}; - unique_ptr platform; + std::unique_ptr platform; int title_height_offset {0}; }; diff --git a/lib/threads.hpp b/lib/threads.hpp index d3707a1c..6de18d9b 100644 --- a/lib/threads.hpp +++ b/lib/threads.hpp @@ -126,8 +126,8 @@ class GLVisCommand int Palette(int pal); int PaletteRepeat(int n); int Levellines(double minv, double maxv, int number); - int AxisNumberFormat(string formatting); - int ColorbarNumberFormat(string formatting); + int AxisNumberFormat(std::string formatting); + int ColorbarNumberFormat(std::string formatting); int Camera(const double cam[]); int Autopause(const char *mode); int Quit(); diff --git a/lib/vsdata.hpp b/lib/vsdata.hpp index 0020abc1..76ab4cd1 100644 --- a/lib/vsdata.hpp +++ b/lib/vsdata.hpp @@ -81,10 +81,12 @@ class VisualizationSceneScalarData : public VisualizationScene int auto_ref_max, auto_ref_min_surf_vert, auto_ref_max_surf_vert; // Formatter for axes & colorbar numbers. Set defaults. - function axis_formatter = NumberFormatter(4, 'd', false); - function colorbar_formatter = NumberFormatter(4, 'd', false); + std::function axis_formatter + = NumberFormatter(4, 'd', false); + std::function colorbar_formatter + = NumberFormatter(4, 'd', false); - vector updated_bufs; + std::vector updated_bufs; gl3::GlDrawable axes_buf; gl3::GlDrawable coord_cross_buf; gl3::GlDrawable color_bar; @@ -277,7 +279,7 @@ class VisualizationSceneScalarData : public VisualizationScene void PrepareCaption(); void SetColorbarNumberFormat(int precision, char format, bool showsign); - void SetColorbarNumberFormat(string formatting); + void SetColorbarNumberFormat(std::string formatting); void PrepareColorBar(double minval, double maxval, Array * level = NULL, @@ -286,7 +288,7 @@ class VisualizationSceneScalarData : public VisualizationScene void SetAxisLabels(const char * a_x, const char * a_y, const char * a_z); void SetAxisNumberFormat(int precision, char format, bool showsign); - void SetAxisNumberFormat(string formatting); + void SetAxisNumberFormat(std::string formatting); void PrepareAxes(); void ToggleDrawAxes() diff --git a/lib/window.cpp b/lib/window.cpp index 5cbcf0a1..33648d1a 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -53,7 +53,7 @@ bool Window::GLVisInitVis(StreamCollection input_streams) window_h, headless)); if (!wnd) { - cerr << "Initializing the visualization failed." << endl; + std::cerr << "Initializing the visualization failed." << std::endl; return false; } @@ -187,7 +187,7 @@ void Window::GLVisStartVis() internal.comm_thread.reset(); internal.glvis_command.reset(); } - cout << "GLVis window closed." << endl; + std::cout << "GLVis window closed." << std::endl; } void Window::SwitchQuadSolution(DataState::QuadSolution quad_type) From fe4536afb2dd2204ef414892e6e0e62a0bb5021d Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 19:17:05 -0700 Subject: [PATCH 39/79] Minor modification to minimize changeset. --- glvis.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index b98c4a5b..72839474 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -151,6 +151,7 @@ class Session StartSession(); return 0; } + }; void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient, @@ -600,7 +601,7 @@ int main (int argc, char *argv[]) // backup the headless flag as the window is moved const bool headless = win.headless; - // Make sure the returned singleton object is + // Make sure the singleton object returned by GetMainThread() is // initialized from the main thread. GetMainThread(headless); @@ -612,7 +613,6 @@ int main (int argc, char *argv[]) } MainThreadLoop(headless); - return 0; } @@ -666,7 +666,7 @@ int main (int argc, char *argv[]) // server mode, read the mesh and the solution from a socket if (input == INPUT_SERVER_MODE) { - // Make sure the returned singleton object is + // Make sure the singleton object returned by GetMainThread() is // initialized from the main thread. GetMainThread(win.headless); @@ -786,7 +786,7 @@ int main (int argc, char *argv[]) // backup the headless flag as the window is moved const bool headless = win.headless; - // Make sure the returned singleton object is + // Make sure the singleton object returned by GetMainThread() is // initialized from the main thread. GetMainThread(headless); From 112b08b0e5b3c2732a5e281ddd46f3c3e45df9a6 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 19:33:37 -0700 Subject: [PATCH 40/79] Updated Changelog. --- CHANGELOG | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a3eef1c6..e6f41e07 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,11 +12,11 @@ Version 4.4.1 (development) =================================== -- Added headless (no GUI) visualization, relying on EGL standard. It is - available through '-hl' command-line option for non-server visualization or - 'headless' script command (before the visualization commands). End of the - loaded stream or script file end the visualization in this mode. Note GLVis - must be compiled with with EGL (see INSTALL). +- Added headless (no GUI) visualization, relying on the EGL standard. It is + available through '-hl' command-line option or 'headless' script command + (before the visualization commands). End of the loaded stream or script file + end the visualization in this mode. Note GLVis must be compiled with with EGL + (see INSTALL). Version 4.4 released on May 1, 2025 From 7f57081f11a3b013a852e7298fbc2a2a7865d5aa Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 22 May 2025 20:34:34 -0700 Subject: [PATCH 41/79] Added graceful closing of EGL server. --- lib/egl/egl.cpp | 2 -- lib/egl/egl_main.cpp | 28 ++++++++++++++++++++++++++++ lib/egl/egl_main.hpp | 5 +++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 5e7ab865..0587c41d 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -25,8 +25,6 @@ using namespace std; -using namespace std; - EglWindow::EglWindow() { } diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 038abe65..a92152a7 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -14,6 +14,7 @@ #include "../aux_vis.hpp" #include #include +#include #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s @@ -206,6 +207,11 @@ void EglMainThread::QueueWndCmd(CtrlCmd cmd, bool sync) if (sync) { wait_complete.get(); } } +void EglMainThread::InterruptHandler(int param) +{ + EglMainThread::Get().Terminate(); +} + EglMainThread::EglMainThread() { if (disp != EGL_NO_DISPLAY) @@ -384,11 +390,28 @@ void EglMainThread::DeleteWindow(EglWindow *caller, Handle &handle) QueueWndCmd(std::move(cmd), true); } +void EglMainThread::Terminate() +{ + CtrlCmd cmd; + cmd.type = CtrlCmdType::Terminate; + + // queue to the front to get priority + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_front(std::move(cmd)); + } + // wake up the main thread to handle our event + events_available.notify_all(); +} + void EglMainThread::MainLoop(bool server) { server_mode = server; bool terminating = false; + // set up interrupt handler for graceful closing + signal(SIGINT, InterruptHandler); + while (true) { bool events_pending = false; @@ -434,6 +457,11 @@ void EglMainThread::MainLoop(bool server) terminating = true; } break; + case CtrlCmdType::Terminate: + // do not wait for windows to open + if (num_windows < 0) { num_windows = 0; } + terminating = true; + break; } // Signal completion of the command, in case worker thread is waiting. diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index 8d04509c..0ccaf71f 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -21,6 +21,7 @@ class EglMainThread : public MainThread EGLDisplay disp{EGL_NO_DISPLAY}; bool server_mode{false}; + bool terminating{false}; std::list windows; int num_windows{-1}; @@ -34,6 +35,7 @@ class EglMainThread : public MainThread Create, Resize, Delete, + Terminate, }; struct CtrlCmd; @@ -47,6 +49,8 @@ class EglMainThread : public MainThread bool DeleteWndImpl(DeleteWndCmd &cmd); void QueueWndCmd(CtrlCmd cmd, bool sync); + static void InterruptHandler(int param); + public: EglMainThread(); @@ -58,6 +62,7 @@ class EglMainThread : public MainThread Handle CreateWindow(EglWindow *caller, int w, int h, bool legacy_gl); void ResizeWindow(Handle &hnd, int w, int h); void DeleteWindow(EglWindow *caller, Handle &hnd); + void Terminate(); void MainLoop(bool server = false) override; }; From 4752012083b158fe088a3b382abbaf366da3428e Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 09:40:00 -0700 Subject: [PATCH 42/79] Non-persistent mode for server. --- CHANGELOG | 3 +++ glvis.cpp | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index e6f41e07..b4281abc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,9 @@ Version 4.4.1 (development) end the visualization in this mode. Note GLVis must be compiled with with EGL (see INSTALL). +- Added non-persistent mode of the server, when the server terminates after all + visualization windows are closed. + Version 4.4 released on May 1, 2025 =================================== diff --git a/glvis.cpp b/glvis.cpp index 72839474..2410dc3b 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -380,6 +380,7 @@ int main (int argc, char *argv[]) const char *palette_file = string_none; const char *font_name = string_default; int portnum = 19916; + bool persistent = true; int multisample = GetMultisample(); double line_width = GetLineWidth(); double ms_line_width = GetLineWidthMS(); @@ -448,6 +449,9 @@ int main (int argc, char *argv[]) "Save the mesh coloring generated when opening only a mesh."); args.AddOption(&portnum, "-p", "--listen-port", "Specify the port number on which to accept connections."); + args.AddOption(&persistent, "-pr", "--persistent", + "-no-pr", "--no-persistent", + "Keep server running after all windows are closed."); args.AddOption(&secure, "-sec", "--secure-sockets", "-no-sec", "--standard-sockets", "Enable or disable GnuTLS secure sockets."); @@ -677,7 +681,7 @@ int main (int argc, char *argv[]) win.plot_caption, win.headless}; // Start message loop in main thread - MainThreadLoop(win.headless, true); + MainThreadLoop(win.headless, persistent); serverThread.detach(); } else // input != 1, non-server mode From 3cac8159a645cd3b75ed66e512374173832b06f0 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 10:45:40 -0700 Subject: [PATCH 43/79] Fixed canvas id in sdl.cpp. --- lib/sdl/sdl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/sdl/sdl.cpp b/lib/sdl/sdl.cpp index 197aba8b..16fbd089 100644 --- a/lib/sdl/sdl.cpp +++ b/lib/sdl/sdl.cpp @@ -445,13 +445,13 @@ void SdlWindow::getWindowSize(int& w, int& h) const if (handle.isInitialized()) { #ifdef __EMSCRIPTEN__ - if (canvas_id_.empty()) + if (canvas_id.empty()) { - std::cerr << "error: id is undefined: " << canvas_id_ << std::endl; + std::cerr << "error: id is undefined: " << canvas_id << std::endl; return; } double dw, dh; - auto err = emscripten_get_element_css_size(canvas_id_.c_str(), &dw, &dh); + auto err = emscripten_get_element_css_size(canvas_id.c_str(), &dw, &dh); w = int(dw); h = int(dh); if (err != EMSCRIPTEN_RESULT_SUCCESS) From 744659509015f3a2efdf917b612b8b29358921b6 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 10:55:32 -0700 Subject: [PATCH 44/79] Fixed definition of GLWindow in aux_js. --- lib/aux_js.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 5ab9d038..3d2dfcc8 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -13,6 +13,7 @@ #include "palettes.hpp" #include "stream_reader.hpp" #include "visual.hpp" +#include "glwindow.hpp" #ifdef GLVIS_USE_LIBPNG #include From 31889e6a72b21e31dd075e71de7d13768ed7a95c Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 11:07:02 -0700 Subject: [PATCH 45/79] Fixed dereferencing of window in aux_js. --- lib/aux_js.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 3d2dfcc8..210a3546 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -275,9 +275,8 @@ std::string getHelpString() em::val getScreenBuffer(bool flip_y=false) { MyExpose(); - auto * wnd = win.wnd; int w, h; - wnd->getGLDrawSize(w, h); + win.wnd->getGLDrawSize(w, h); // 4 bytes for rgba const size_t buffer_size = w*h*4; @@ -318,13 +317,12 @@ em::val getScreenBuffer(bool flip_y=false) em::val getPNGByteArray() { constexpr const char * filename = "im.png"; - auto * wnd = win.wnd; int w, h; - wnd->getGLDrawSize(w, h); + win.wnd->getGLDrawSize(w, h); MyExpose(); // save to in-memory file - int status = SaveAsPNG(filename, w, h, wnd->isHighDpi(), true); + int status = SaveAsPNG(filename, w, h, win.wnd->isHighDpi(), true); if (status != 0) { fprintf(stderr, "unable to generate png\n"); From 51cdf69af9c2d7bd0e9e463232e8eb93d0c567fb Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 20:22:42 -0700 Subject: [PATCH 46/79] Initial support of CGL. --- CMakeLists.txt | 9 +++ lib/aux_vis.cpp | 10 ++-- lib/egl/egl.cpp | 13 +++- lib/egl/egl.hpp | 31 +++++++++- lib/egl/egl_main.cpp | 139 ++++++++++++++++++++++++++++++++++++++++++- lib/egl/egl_main.hpp | 11 ++-- makefile | 7 +++ 7 files changed, 203 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3234f7dd..c9d11209 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,10 @@ option(GLVIS_USE_EGL "Use EGL for headless rendering" OFF) +option(GLVIS_USE_CGL + "Use CGL for headless rendering" + OFF) + # # Handle a few other definitions # @@ -243,6 +247,11 @@ if (NOT EMSCRIPTEN) endif (OpenGL_EGL_FOUND) endif (GLVIS_USE_EGL) + # Find CGL + if (GLVIS_USE_CGL) + list(APPEND _glvis_compile_defs "GLVIS_USE_CGL") + endif (GLVIS_USE_CGL) + # Find threading library find_package(Threads REQUIRED) list(APPEND _glvis_libraries "${CMAKE_THREAD_LIBS_INIT}") diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index bf8f20a3..c1a2e240 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -59,7 +59,7 @@ static bool wndUseHiDPI = true; MainThread& GetMainThread(bool headless) { -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) if (headless) { return EglMainThread::Get(); @@ -145,7 +145,7 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, } else { -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) sdl_wnd = nullptr; if (!wnd) { @@ -161,10 +161,10 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, { wnd->clearEvents(); } -#else //GLVIS_USE_EGL - cerr << "EGL is required for headless rendering!" << endl; +#else //GLVIS_USE_EGL || GLVIS_USE_CGL + cerr << "EGL or CGL are required for headless rendering!" << endl; return NULL; -#endif //GLVIS_USE_EGL +#endif //GLVIS_USE_EGL || GLVIS_USE_CGL } #ifdef GLVIS_DEBUG diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 0587c41d..45dc7d90 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -9,7 +9,7 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) #include "egl.hpp" #include "egl_main.hpp" #include "../aux_vis.hpp" @@ -156,6 +156,7 @@ void EglWindow::signalLoop() void EglWindow::getGLDrawSize(int& w, int& h) const { +#ifdef GLVIS_USE_EGL EGLint egl_w, egl_h; EGLDisplay disp = EglMainThread::Get().GetDisplay(); @@ -163,6 +164,14 @@ void EglWindow::getGLDrawSize(int& w, int& h) const eglQuerySurface(disp, handle.surf, EGL_HEIGHT, &egl_h); w = egl_w; h = egl_h; +#endif +#ifdef GLVIS_USE_CGL + GLint cgl_w, cgl_h; + glGetRenderbufferParameteriv(handle.buf_color, GL_RENDERBUFFER_WIDTH, &cgl_w); + glGetRenderbufferParameteriv(handle.buf_color, GL_RENDERBUFFER_HEIGHT, &cgl_h); + w = cgl_w; + h = cgl_h; +#endif } bool EglWindow::isHighDpi() const @@ -197,4 +206,4 @@ void EglWindow::screenshot(string filename, bool convert) signalExpose(); } -#endif //GLVIS_USE_EGL +#endif //GLVIS_USE_EGL || GLVIS_USE_CGL diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index f0a8ca6e..e5bff302 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -11,11 +11,19 @@ #ifndef GLVIS_EGL_HPP #define GLVIS_EGL_HPP -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) #include "../glwindow.hpp" +#ifdef GLVIS_USE_EGL #include +#endif + +#ifdef GLVIS_USE_CGL +#include +#include +#include +#endif #include #include @@ -27,13 +35,25 @@ class EglWindow : public GLWindow public: struct Handle { - EGLSurface surf{EGL_NO_SURFACE}; +#ifdef GLVIS_USE_EGL + EGLSurface surf {EGL_NO_SURFACE}; EGLContext ctx{EGL_NO_CONTEXT}; EGLConfig eglCfg{}; +#endif +#ifdef GLVIS_USE_CGL + GLuint buf_frame {}, buf_color {}, buf_depth {}; + CGLPixelFormatObj pixFmt {}; + CGLContextObj ctx{}; +#endif bool isInitialized() { +#ifdef GLVIS_USE_EGL return surf != EGL_NO_SURFACE && ctx != EGL_NO_CONTEXT; +#endif +#ifdef GLVIS_USE_CGL + return ctx != nullptr; +#endif } }; @@ -103,8 +123,13 @@ class EglWindow : public GLWindow void setWindowSize(int w, int h) override; +#if defined(GLVIS_USE_EGL) bool isWindowInitialized() const override { return handle.surf != EGL_NO_SURFACE; } bool isGlInitialized() const override { return handle.ctx != EGL_NO_CONTEXT; } +#elif defined(GLVIS_USE_CGL) + bool isWindowInitialized() const override { return isGlInitialized(); } + bool isGlInitialized() const override { return handle.ctx != nullptr; } +#endif void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) override; void signalQuit() override; @@ -118,5 +143,5 @@ class EglWindow : public GLWindow void screenshot(std::string filename, bool convert = false) override; }; -#endif //GLVIS_USE_EGL +#endif //GLVIS_USE_EGL || GLVIS_USE_CGL #endif //GLVIS_EGL_HPP \ No newline at end of file diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index a92152a7..13baa2ca 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -9,7 +9,7 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) #include "egl_main.hpp" #include "../aux_vis.hpp" #include @@ -28,6 +28,7 @@ struct EglMainThread::CreateWndCmd { EglWindow *wnd; int w, h; + bool legacy_gl; promise out_handle; }; @@ -60,6 +61,7 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) { Handle new_handle; +#ifdef GLVIS_USE_EGL // 1. Select an appropriate configuration const int multisamples = GetMultisample(); @@ -117,6 +119,48 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) std::endl; return false; } +#endif +#ifdef GLVIS_USE_CGL + const int multisamples = GetMultisample(); + + CGLPixelFormatAttribute pixAttribs[] = + { + kCGLPFASampleBuffers, CGLPixelFormatAttribute((multisamples > 0)?(1):(0)), // must be first + kCGLPFASamples, CGLPixelFormatAttribute(multisamples), + //kCGLPFAOpenGLProfile, CGLPixelFormatAttribute((cmd.legacy_gl)?(kCGLOGLPVersion_Legacy):(kCGLOGLPVersion_3_2_Core)), + kCGLPFAColorSize, CGLPixelFormatAttribute(24), + kCGLPFAAlphaSize, CGLPixelFormatAttribute(8), + kCGLPFADepthSize, CGLPixelFormatAttribute(24), + //kCGLPFAStencilSize, CGLPixelFormatAttribute(stencilBits), + //kCGLPFAAccumSize, CGLPixelFormatAttribute(accumBits), + kCGLPFAAccelerated, + CGLPixelFormatAttribute(0) + }; + + GLint numConfigs; + GLError err = CGLChoosePixelFormat(pixAttribs, &new_handle.pixFmt, &numConfigs); + if (multisamples > 0 && (err != kCGLNoError || numConfigs < 1)) + { + std::cerr << "CGL with multisampling is not supported, turning it off" << + std::endl; + pixAttribs[0] = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttribs, &new_handle.pixFmt, &numConfigs); + } + if (err != kCGLNoError || numConfigs < 1) + { + std::cerr << "Cannot set up CGL pixel format configuration, error: " + << CGLErrorString(err) << std::endl; + return false; + } + + err = CGLCreateContext(new_handle.pixFmt, NULL, &new_handle.ctx); + if (err != kCGLNoError) + { + std::cerr << "Cannot create an OpenGL context, error: " + << CGLErrorString(err) << std::endl; + return false; + } +#endif windows.push_back(cmd.wnd); if (num_windows < 0) @@ -134,6 +178,7 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) { +#ifdef GLVIS_USE_EGL const EGLint pbufferAttribs[] = { EGL_WIDTH, cmd.w, @@ -157,12 +202,13 @@ bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) } cmd.handle->surf = surf_new; - +#endif return true; } bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) { +#ifdef GLVIS_USE_EGL if (cmd.handle->ctx != EGL_NO_CONTEXT) { if (!eglDestroyContext(disp, cmd.handle->ctx)) @@ -182,7 +228,39 @@ bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) } cmd.handle->surf = EGL_NO_SURFACE; } +#endif +#ifdef GLVIS_USE_CGL + if (cmd.handle->isInitialized()) + { + glDeleteFramebuffers(1, &cmd.handle->buf_frame); + glDeleteRenderbuffers(1, &cmd.handle->buf_color); + glDeleteRenderbuffers(1, &cmd.handle->buf_depth); + } + + if (cmd.handle->ctx) + { + CGLError err = CGLDestroyContext(cmd.handle->ctx); + if (err != kCGLNoError) + { + std::cerr << "Cannot destroy context, error: " + << CGLErrorString(err) << std::endl; + return false; + } + cmd.handle->ctx = nullptr; + } + if (cmd.handle->pixFmt) + { + CGLError err = CGLDestroyPixelFormat(cmd.handle->pixFmt); + if (err != kCGLNoError) + { + std::cerr << "Cannot destroy pixel format, error: " + << CGLErrorString(err) << std::endl; + return false; + } + cmd.handle->pixFmt = nullptr; + } +#endif windows.remove(cmd.wnd); num_windows--; @@ -214,6 +292,7 @@ void EglMainThread::InterruptHandler(int param) EglMainThread::EglMainThread() { +#ifdef GLVIS_USE_EGL if (disp != EGL_NO_DISPLAY) { return; @@ -237,11 +316,20 @@ EglMainThread::EglMainThread() } std::cout << "Using EGL " << major << "." << minor << std::endl; +#endif +#ifdef GLVIS_USE_CGL + GLint major, minor; + CGLGetVersion(&major, &minor); + + std::cout << "Using CGL " << major << "." << minor << std::endl; +#endif } EglMainThread::~EglMainThread() { +#ifdef GLVIS_USE_EGL eglTerminate(disp); +#endif } EglMainThread &EglMainThread::Get() @@ -262,6 +350,7 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, crt_cmd.wnd = caller; crt_cmd.w = w; crt_cmd.h = h; + crt_cmd.legacy_gl = legacy_gl; auto res_handle = crt_cmd.out_handle.get_future(); @@ -269,6 +358,7 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, Handle out_hnd = res_handle.get(); +#ifdef GLVIS_USE_EGL if (out_hnd.surf == EGL_NO_SURFACE) { return out_hnd; @@ -346,6 +436,28 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, return out_hnd; } +#endif //GLVIS_USE_EGL +#ifdef GLVIS_USE_CGL + if (!out_hnd.isInitialized()) + { + return out_hnd; + } + + CGLError err = CGLSetCurrentContext(out_hnd.ctx); + if (err != kCGLNoError) + { + std::cerr << "Cannot set the CGL context as current, error: " + << CGLErrorString(err) << std::endl; + + return out_hnd; + } + + glGenFramebuffers(1, &out_hnd.buf_frame); + glGenRenderbuffers(1, &out_hnd.buf_color); + glGenRenderbuffers(1, &out_hnd.buf_depth); + + ResizeWindow(out_hnd, cmd.create_cmd->w, cmd.create_cmd->h); +#endif //GLVIS_USE_CGL return out_hnd; } @@ -354,6 +466,7 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) { if (!handle.isInitialized()) { return; } +#ifdef GLVIS_USE_EGL CtrlCmd cmd; cmd.type = CtrlCmdType::Resize; @@ -372,6 +485,26 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) << std::endl; return; } +#endif +#ifdef GLVIS_USE_CGL + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); + + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_depth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); + + glBindFramebuffer(GL_FRAMEBUFFER, handle.buf_frame); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, handle.buf_color); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, handle.buf_depth); + + if (glGetError() != GL_NO_ERROR || + glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + std::cerr << "Cannot resize the framebuffer!" << std::endl; + } +#endif } void EglMainThread::DeleteWindow(EglWindow *caller, Handle &handle) @@ -497,4 +630,4 @@ void EglMainThread::MainLoop(bool server) } } -#endif //GLVIS_USE_EGL +#endif //GLVIS_USE_EGL || GLVIS_USE_CGL diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index 0ccaf71f..4f76e7fb 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -11,16 +11,17 @@ #ifndef GLVIS_EGL_MAIN_HPP #define GLVIS_EGL_MAIN_HPP -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) #include "egl.hpp" class EglMainThread : public MainThread { using Handle = EglWindow::Handle; - EGLDisplay disp{EGL_NO_DISPLAY}; - - bool server_mode{false}; +#ifdef GLVIS_USE_EGL + EGLDisplay disp {EGL_NO_DISPLAY}; +#endif + bool server_mode {false}; bool terminating{false}; std::list windows; @@ -57,7 +58,9 @@ class EglMainThread : public MainThread ~EglMainThread(); static EglMainThread& Get(); +#ifdef GLVIS_USE_EGL EGLDisplay GetDisplay() const { return disp; } +#endif Handle CreateWindow(EglWindow *caller, int w, int h, bool legacy_gl); void ResizeWindow(Handle &hnd, int w, int h); diff --git a/makefile b/makefile index 6331f8a1..f6ca1a6b 100644 --- a/makefile +++ b/makefile @@ -250,6 +250,13 @@ ifeq ($(GLVIS_USE_EGL),YES) GLVIS_LIBS += $(EGL_LIBS) endif +# CGL headless rendering +GLVIS_USE_CGL ?= NO +CGL_OPTS = -DGLVIS_USE_CGL +ifeq ($(GLVIS_USE_CGL),YES) + GLVIS_FLAGS += $(CGL_OPTS) +endif + PTHREAD_LIB = -lpthread GLVIS_LIBS += $(PTHREAD_LIB) From f451a7b35f88ebcc4d31ff883db3f9deceb08522 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 20:57:28 -0700 Subject: [PATCH 47/79] Fixed compilation on Mac. --- lib/egl/egl_main.cpp | 14 -------------- lib/egl/egl_main.hpp | 14 +++++++++++++- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 13baa2ca..6617be70 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -13,7 +13,6 @@ #include "egl_main.hpp" #include "../aux_vis.hpp" #include -#include #include #ifdef GLVIS_DEBUG @@ -44,19 +43,6 @@ struct EglMainThread::DeleteWndCmd Handle *handle; }; -struct EglMainThread::CtrlCmd -{ - CtrlCmdType type; - union - { - CreateWndCmd *create_cmd; - ResizeWndCmd *resize_cmd; - DeleteWndCmd *delete_cmd; - }; - - promise finished; -}; - bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) { Handle new_handle; diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index 4f76e7fb..c2b336f4 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -14,6 +14,7 @@ #if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) #include "egl.hpp" +#include class EglMainThread : public MainThread { @@ -39,7 +40,18 @@ class EglMainThread : public MainThread Terminate, }; - struct CtrlCmd; + struct CtrlCmd + { + CtrlCmdType type; + union + { + CreateWndCmd *create_cmd; + ResizeWndCmd *resize_cmd; + DeleteWndCmd *delete_cmd; + }; + + std::promise finished; + }; std::condition_variable events_available; std::mutex window_cmd_mtx; From 58421a7c506a286345c522fec011cce81bdbac15 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 21:59:02 -0700 Subject: [PATCH 48/79] Removed unused terminating flag. --- lib/egl/egl_main.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index 0ccaf71f..62cf267e 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -21,7 +21,6 @@ class EglMainThread : public MainThread EGLDisplay disp{EGL_NO_DISPLAY}; bool server_mode{false}; - bool terminating{false}; std::list windows; int num_windows{-1}; From 3e8637b62e3becf04799b359c9d401fbe517b232 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 28 May 2025 11:34:26 -0700 Subject: [PATCH 49/79] Added CGL build to CI on Mac. --- .github/workflows/builds.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index a9c12117..c2541d67 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -201,7 +201,9 @@ jobs: [[ ${{ matrix.target }} == "dbg" ]] && glvis_target="debug"; use_egl="NO" [[ ${{ matrix.os }} == "ubuntu-latest" ]] && use_egl="YES" - cd glvis && make ${glvis_target} -j3 GLVIS_USE_EGL=${use_egl} + use_cgl="NO" + [[ ${{ matrix.os }} == "macos-latest" ]] && use_cgl="YES" + cd glvis && make ${glvis_target} -j3 GLVIS_USE_EGL=${use_egl} GLVIS_USE_CGL=${use_cgl} - name: build GLVis (cmake) if: matrix.build-system == 'cmake' @@ -214,12 +216,15 @@ jobs: && toolchain_file="${VCPKG_INSTALLATION_ROOT}\\scripts\\buildsystems\\vcpkg.cmake" use_egl="OFF" [[ ${{ matrix.os }} == "ubuntu-latest" ]] && use_egl="ON" + use_cgl="NO" + [[ ${{ matrix.os }} == "macos-latest" ]] && use_cgl="YES" cd glvis && mkdir build && cd build cmake \ -D CMAKE_TOOLCHAIN_FILE:STRING=${toolchain_file} \ -D CMAKE_BUILD_TYPE:STRING=${build_type} \ -D ENABLE_TESTS:BOOL=TRUE \ -D GLVIS_USE_EGL:BOOL=${use_egl} \ + -D GLVIS_USE_CGL:BOOL=${use_cgl} \ -D mfem_DIR:PATH=${GITHUB_WORKSPACE}/${MFEM_TOP_DIR}/build \ -D GLVIS_BASELINE_SYS=${{ matrix.os }} \ .. From 12808ddfc20c3232daf91bc11005fc3c1b97344a Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 22:00:57 -0700 Subject: [PATCH 50/79] Fixed a typo. --- lib/egl/egl_main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 6617be70..2bdc232f 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -124,7 +124,8 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) }; GLint numConfigs; - GLError err = CGLChoosePixelFormat(pixAttribs, &new_handle.pixFmt, &numConfigs); + CGLError err = CGLChoosePixelFormat(pixAttribs, &new_handle.pixFmt, + &numConfigs); if (multisamples > 0 && (err != kCGLNoError || numConfigs < 1)) { std::cerr << "CGL with multisampling is not supported, turning it off" << From 3c930dcbd0ab49f3d1327d1d56f2811d145c4f1b Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 27 May 2025 22:05:13 -0700 Subject: [PATCH 51/79] Silenced deprecation warning for CGL. --- lib/egl/egl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index e5bff302..b4e5ce91 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -20,6 +20,7 @@ #endif #ifdef GLVIS_USE_CGL +#define GL_SILENCE_DEPRECATION // CGL has been deprecated since MacOS 10.14 #include #include #include From 152ff7ca5b123e990d056baf81cc723bee90989a Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 28 May 2025 13:12:06 -0700 Subject: [PATCH 52/79] Improved pixel format setup. --- lib/egl/egl_main.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 2bdc232f..80cf9d21 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -109,29 +109,40 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) #ifdef GLVIS_USE_CGL const int multisamples = GetMultisample(); - CGLPixelFormatAttribute pixAttribs[] = + vector pixAttribs = { kCGLPFASampleBuffers, CGLPixelFormatAttribute((multisamples > 0)?(1):(0)), // must be first kCGLPFASamples, CGLPixelFormatAttribute(multisamples), - //kCGLPFAOpenGLProfile, CGLPixelFormatAttribute((cmd.legacy_gl)?(kCGLOGLPVersion_Legacy):(kCGLOGLPVersion_3_2_Core)), kCGLPFAColorSize, CGLPixelFormatAttribute(24), kCGLPFAAlphaSize, CGLPixelFormatAttribute(8), kCGLPFADepthSize, CGLPixelFormatAttribute(24), - //kCGLPFAStencilSize, CGLPixelFormatAttribute(stencilBits), - //kCGLPFAAccumSize, CGLPixelFormatAttribute(accumBits), - kCGLPFAAccelerated, + kCGLPFAAccelerated, // must be last CGLPixelFormatAttribute(0) }; + if (cmd.legacy_gl) + { + auto it = (pixAttribs.end() -= 2); + pixAttribs.insert(it, {kCGLPFAOpenGLProfile, kCGLOGLPVersion_Legacy}); + } + GLint numConfigs; - CGLError err = CGLChoosePixelFormat(pixAttribs, &new_handle.pixFmt, + CGLError err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pixFmt, &numConfigs); if (multisamples > 0 && (err != kCGLNoError || numConfigs < 1)) { std::cerr << "CGL with multisampling is not supported, turning it off" << std::endl; - pixAttribs[0] = CGLPixelFormatAttribute(0); - err = CGLChoosePixelFormat(pixAttribs, &new_handle.pixFmt, &numConfigs); + pixAttribs[1] = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pixFmt, &numConfigs); + } + if (err != kCGLNoError || numConfigs < 1) + { + std::cerr << "CGL with hardware acceleration not supported, turning it off" << + std::endl; + pixAttribs.pop_back(); + pixAttribs.back() = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pixFmt, &numConfigs); } if (err != kCGLNoError || numConfigs < 1) { From 614211014243aaebf8c2825869eace9a79fae862 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Wed, 28 May 2025 13:32:46 -0700 Subject: [PATCH 53/79] Fixed insertion of legacy flag. --- lib/egl/egl_main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 80cf9d21..4266b4dc 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -122,8 +122,9 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) if (cmd.legacy_gl) { + // insert legacy OpenGL compatibility requirement auto it = (pixAttribs.end() -= 2); - pixAttribs.insert(it, {kCGLPFAOpenGLProfile, kCGLOGLPVersion_Legacy}); + pixAttribs.insert(it, {kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_Legacy)}); } GLint numConfigs; From 3f18f22a1b3935b177df0629661465a0d47de881 Mon Sep 17 00:00:00 2001 From: camierjs Date: Thu, 2 Oct 2025 18:56:19 -0700 Subject: [PATCH 54/79] Setup config --- .gitignore | 3 +++ CMakeLists.txt | 10 ++++++++++ lib/aux_vis.cpp | 26 ++++++++++++++++++++++---- lib/egl/egl.cpp | 6 +++--- lib/egl/egl.hpp | 4 ++-- lib/egl/egl_main.cpp | 3 ++- lib/egl/egl_main.hpp | 5 ++++- lib/glwindow.hpp | 3 ++- 8 files changed, 48 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index ff588ebb..80fb7860 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ compile_commands.json # OS-specific: Mac *.dSYM .DS_Store + +user.cmake +nvtx.hpp \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index c9d11209..63ee8f9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,16 @@ endif ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") project(glvis NONE) +# Load user settings +set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/user.cmake" CACHE PATH + "User configuration file. This file is not part of the GLVis source code and + is not distributed with GLVis. It can be used to set user-specific options, + such as paths to external libraries, compiler flags, etc.") +include("${USER_CONFIG}" OPTIONAL RESULT_VARIABLE USER_CONFIG_LOADED) +if (USER_CONFIG_LOADED) + message(STATUS "USER_CONFIG = ${USER_CONFIG} (LOADED)") +endif() + # Import MFEM. The following variables can be used to help CMake find MFEM: # * MFEM_DIR - absolute path to the MFEM build or install prefix. # * mfem_DIR - absolute path to where MFEMConfig.cmake is. diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index c1a2e240..5a9462bd 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -11,7 +11,7 @@ #include #include -#include +// #include #include #include #include @@ -25,6 +25,15 @@ #include "visual.hpp" #include "gl2ps.h" +#if defined(__has_include) && __has_include("../nvtx.hpp") && !defined(_WIN32) +#undef NVTX_COLOR +#define NVTX_COLOR ::nvtx::kCyan +#include "../nvtx.hpp" +#else +#define dbg(...) +#endif + + #if defined(GLVIS_USE_LIBTIFF) #include "tiffio.h" #elif defined(GLVIS_USE_LIBPNG) @@ -59,7 +68,8 @@ static bool wndUseHiDPI = true; MainThread& GetMainThread(bool headless) { -#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) + dbg(); +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) if (headless) { return EglMainThread::Get(); @@ -115,6 +125,7 @@ void MyExpose(); GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, bool headless) { + dbg(); #ifdef GLVIS_DEBUG if (!headless) { @@ -122,7 +133,13 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, } else { +#if defined(GLVIS_USE_EGL) cout << "OpenGL+EGL Visualization" << endl; +#elif defined(GLVIS_USE_CGL) + cout << "OpenGL+CGL Visualization" << endl; +#else + cout << "Headless rendering requires EGL or CGL!" << endl; +#endif } #endif @@ -145,10 +162,11 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, } else { -#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) sdl_wnd = nullptr; if (!wnd) { + dbg("new EGL window"); wnd = new EglWindow(); if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) { @@ -1029,7 +1047,7 @@ int SaveAsPNG(const char *fname, int w, int h, bool is_hidpi, bool with_alpha, } png_uint_32 ppi = is_hidpi ? 144 : 72; // pixels/inch - png_uint_32 ppm = ppi/0.0254 + 0.5; // pixels/meter + auto ppm = static_cast(ppi/0.0254 + 0.5); // pixels/meter png_set_pHYs(png_ptr, info_ptr, ppm, ppm, PNG_RESOLUTION_METER); png_init_io(png_ptr, fp); diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 45dc7d90..4a7aeafb 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -9,13 +9,13 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) + #include "egl.hpp" #include "egl_main.hpp" #include "../aux_vis.hpp" #include #include -#include #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s @@ -193,7 +193,7 @@ void EglWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) void EglWindow::signalQuit() { - queueEvents({{EventType::Quit}}); + queueEvents({{EventType::Quit, {}}}); } void EglWindow::screenshot(string filename, bool convert) diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index b4e5ce91..8627f5b6 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -11,7 +11,8 @@ #ifndef GLVIS_EGL_HPP #define GLVIS_EGL_HPP -#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) + +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) #include "../glwindow.hpp" @@ -29,7 +30,6 @@ #include #include #include -#include class EglWindow : public GLWindow { diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 4266b4dc..343e7bd8 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -9,7 +9,7 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) #include "egl_main.hpp" #include "../aux_vis.hpp" #include @@ -451,6 +451,7 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, return out_hnd; } + assert(out_hnd.buf_frame != 0); glGenFramebuffers(1, &out_hnd.buf_frame); glGenRenderbuffers(1, &out_hnd.buf_color); glGenRenderbuffers(1, &out_hnd.buf_depth); diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index 9fe7b477..3ae6a980 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -11,11 +11,14 @@ #ifndef GLVIS_EGL_MAIN_HPP #define GLVIS_EGL_MAIN_HPP + #if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) -#include "egl.hpp" +#include #include +#include "egl.hpp" + class EglMainThread : public MainThread { using Handle = EglWindow::Handle; diff --git a/lib/glwindow.hpp b/lib/glwindow.hpp index d9e872cd..caa811b8 100644 --- a/lib/glwindow.hpp +++ b/lib/glwindow.hpp @@ -12,12 +12,13 @@ #ifndef GLVIS_GLWINDOW_HPP #define GLVIS_GLWINDOW_HPP -#include "gl/renderer.hpp" #include #include #include #include +#include "gl/renderer.hpp" + class MainThread { public: From c1f5a0ecb9e246d8aede6e78bb2c9463152bd0d2 Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 11:06:24 -0700 Subject: [PATCH 55/79] Fix CGL macos --- lib/CMakeLists.txt | 4 + lib/aux_vis.cpp | 48 +++-- lib/cgl/cgl.cpp | 256 ++++++++++++++++++++++ lib/cgl/cgl.hpp | 117 +++++++++++ lib/cgl/cgl_main.cpp | 491 +++++++++++++++++++++++++++++++++++++++++++ lib/cgl/cgl_main.hpp | 78 +++++++ lib/egl/egl.cpp | 13 +- lib/egl/egl.hpp | 45 +--- lib/egl/egl_main.cpp | 151 +------------ lib/egl/egl_main.hpp | 8 +- makefile | 2 + 11 files changed, 993 insertions(+), 220 deletions(-) create mode 100644 lib/cgl/cgl.cpp create mode 100644 lib/cgl/cgl.hpp create mode 100644 lib/cgl/cgl_main.cpp create mode 100644 lib/cgl/cgl_main.hpp diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 22ab9746..98752ded 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,6 +10,8 @@ # CONTRIBUTING.md for details. list(APPEND SOURCES + cgl/cgl.cpp + cgl/cgl_main.cpp egl/egl.cpp egl/egl_main.cpp gl/renderer.cpp @@ -40,6 +42,8 @@ list(APPEND SOURCES window.cpp) list(APPEND HEADERS + cgl/cgl.hpp + cgl/cgl_main.hpp egl/egl.hpp egl/egl_main.hpp gl/attr_traits.hpp diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 5a9462bd..554ab868 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -19,20 +19,16 @@ #include "mfem.hpp" #include "sdl/sdl.hpp" #include "sdl/sdl_main.hpp" +#include "cgl/cgl.hpp" +#include "cgl/cgl_main.hpp" #include "egl/egl.hpp" #include "egl/egl_main.hpp" #include "palettes.hpp" #include "visual.hpp" #include "gl2ps.h" -#if defined(__has_include) && __has_include("../nvtx.hpp") && !defined(_WIN32) -#undef NVTX_COLOR #define NVTX_COLOR ::nvtx::kCyan #include "../nvtx.hpp" -#else -#define dbg(...) -#endif - #if defined(GLVIS_USE_LIBTIFF) #include "tiffio.h" @@ -69,13 +65,12 @@ static bool wndUseHiDPI = true; MainThread& GetMainThread(bool headless) { dbg(); -#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) - if (headless) - { - return EglMainThread::Get(); - } +#if defined(GLVIS_USE_EGL) + if (headless) { return EglMainThread::Get(); } +#endif +#if defined(GLVIS_USE_CGL) + if (headless) { return CGLMainThread::Get(); } #endif - return GetSdlMainThread(); } @@ -127,10 +122,7 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, { dbg(); #ifdef GLVIS_DEBUG - if (!headless) - { - cout << "OpenGL Visualization" << endl; - } + if (!headless) { cout << "OpenGL Visualization" << endl; } else { #if defined(GLVIS_USE_EGL) @@ -162,7 +154,7 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, } else { -#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) +#if defined(GLVIS_USE_EGL) sdl_wnd = nullptr; if (!wnd) { @@ -179,10 +171,28 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, { wnd->clearEvents(); } -#else //GLVIS_USE_EGL || GLVIS_USE_CGL +#elif defined(GLVIS_USE_CGL) + sdl_wnd = nullptr; + if (!wnd) + { + dbg("new CGL window"); + wnd = new CGLWindow(); + if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) + { + dbg("❌ Failed to create CGL window"); + delete wnd; + wnd = nullptr; + return NULL; + } + } + else + { + wnd->clearEvents(); + } +#else // GLVIS_USE_EGL || GLVIS_USE_CGL cerr << "EGL or CGL are required for headless rendering!" << endl; return NULL; -#endif //GLVIS_USE_EGL || GLVIS_USE_CGL +#endif // GLVIS_USE_EGL || GLVIS_USE_CGL } #ifdef GLVIS_DEBUG diff --git a/lib/cgl/cgl.cpp b/lib/cgl/cgl.cpp new file mode 100644 index 00000000..c34ffd44 --- /dev/null +++ b/lib/cgl/cgl.cpp @@ -0,0 +1,256 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifdef GLVIS_USE_CGL + +#include +#include + +#include "cgl.hpp" +#include "cgl_main.hpp" + +#include "../aux_vis.hpp" + +#define NVTX_COLOR ::nvtx::kMagenta +#include "../../nvtx.hpp" // IWYU pragma: keep + +using namespace std; + +CGLWindow::CGLWindow() +{ + dbg("Could create window"); +} + +CGLWindow::~CGLWindow() +{ + dbg(); + CGLMainThread::Get().DeleteWindow(this, handle); +} +// return initGLEW(legacyGlOnly); + +bool CGLWindow::initGLEW(bool legacyGlOnly) +{ + return GLWindow::initGLEW(legacyGlOnly); +} + +bool CGLWindow::createWindow(const char *title, int x, int y, int w, int h, + bool legacyGlOnly) +{ + dbg("title: '{}' x:{} y:{} {}x{} legacyGlOnly:{}", + title, x, y, w, h, legacyGlOnly); + handle = CGLMainThread::Get().CreateWindow(this, w, h, legacyGlOnly); + assert(glGetError() == GL_NO_ERROR); + + if (!handle.isInitialized()) + { + dbg("❌ Cannot create CGL window"); + return false; + } + + // dbg("CGL context is ready"); + // return initGLEW(legacyGlOnly); + return true; +} + +void CGLWindow::queueEvents(vector events) +{ + dbg(); + { + lock_guard evt_guard{event_mutex}; + waiting_events.insert(waiting_events.end(), events.begin(), events.end()); + } + if (is_multithreaded) + { + events_available.notify_all(); + } +} + +void CGLWindow::mainLoop() +{ + dbg(); + running = true; + while (running) + { + mainIter(); + } +} + +void CGLWindow::mainIter() +{ + dbg(); + bool sleep = false; + bool events_pending = false; + { + lock_guard evt_guard{event_mutex}; + events_pending = !waiting_events.empty(); + } + if (events_pending) + { + do + { + Event e; + // Fetch next event from the queue + { + lock_guard evt_guard{event_mutex}; + e = waiting_events.front(); + waiting_events.pop_front(); + events_pending = !waiting_events.empty(); + } + + switch (e.type) + { + case EventType::Keydown: + if (onKeyDown[e.event.keydown.k]) + { + onKeyDown[e.event.keydown.k](e.event.keydown.m); + recordKey(e.event.keydown.k, e.event.keydown.m); + } + break; + case EventType::Screenshot: + if (isExposePending()) + { + onExpose(); + wnd_state = RenderState::Updated; + } + Screenshot(screenshot_filename.c_str(), e.event.screenshot.convert); + break; + case EventType::Quit: + running = false; + break; + } + } + while (events_pending); + } + else if (onIdle) + { + sleep = onIdle(); + } + else + { + // No actions performed this iteration. + sleep = true; + } + + if (isExposePending()) + { + onExpose(); + wnd_state = RenderState::Updated; + } + else if (sleep) + { + unique_lock event_lock{event_mutex}; + events_available.wait(event_lock, [this]() + { + // Sleep until events from WM or glvis_command can be handled + return !waiting_events.empty() || call_idle_func; + }); + } +} + +void CGLWindow::signalLoop() +{ + dbg(); + // Note: not executed from the main thread + { + lock_guard evt_guard{event_mutex}; + call_idle_func = true; + } + events_available.notify_all(); +} + +void CGLWindow::getGLDrawSize(int& w, int& h) const +{ + const auto err = glGetError(); + dbg("glGetError: {:x}", err); + std::cout << "glGetError:" << err << std::endl; + + assert(glGetError() == GL_NO_ERROR); + GLint cgl_w, cgl_h; + static_assert(sizeof(GLint) == sizeof(int), + "CGL width size must be 4 bytes"); + // CGLGetRenderbufferParameter(handle, cgl_w, cgl_h); + { + dbg("glGetError:{}", glGetError()); + assert(glGetError() == GL_NO_ERROR); + assert(handle.isInitialized()); + + // Optional: Ensure context is current if not guaranteed elsewhere + CGLError err = CGLSetCurrentContext(handle.ctx.get()); + if (err != kCGLNoError) { dbg("❌ Cannot set CGL context as current");} + // Bind the color renderbuffer (assuming that's what we want to query) + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); + if (glGetError() != GL_NO_ERROR) + { + dbg("❌ Failed to bind renderbuffer"); + // return EXIT_FAILURE; + } + // Query width and height + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &cgl_w); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &cgl_h); + // Check for query errors + GLenum gl_err = glGetError(); + if (gl_err != GL_NO_ERROR) + { + dbg("❌ GL error during parameter query: {}", gl_err); + // Unbind to clean up + glBindRenderbuffer(GL_RENDERBUFFER, 0); + return ; + } + dbg("Renderbuffer parameters - width: {}, height: {}", cgl_w, cgl_h); + // Unbind to restore state + glBindRenderbuffer(GL_RENDERBUFFER, 0); + assert(glGetError() == GL_NO_ERROR); + } + + // cgl_w = 1024, cgl_h = 768; // TEMP + dbg("CGL draw size: {}x{}", (int) cgl_w, (int)cgl_h); + w = cgl_w; + h = cgl_h; +} + +bool CGLWindow::isHighDpi() const +{ + dbg("{}", GetUseHiDPI()); + return GetUseHiDPI(); +} + +void CGLWindow::setWindowSize(int w, int h) +{ + dbg("w:{} h:{}", w, h); + CGLMainThread::Get().ResizeWindow(handle, w, h); +} + +void CGLWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) +{ + dbg("k:{} m:{}", (int)k, (int)m); + Event::Events e; + e.keydown = {k, m}; + queueEvents({{EventType::Keydown, e}}); +} + +void CGLWindow::signalQuit() +{ + dbg("Quit"); + queueEvents({{EventType::Quit, {}}}); +} + +void CGLWindow::screenshot(string filename, bool convert) +{ + dbg("filename:{} convert:{}", filename, convert); + screenshot_filename = filename; + Event::Events e; + e.screenshot = {convert}; + queueEvents({{EventType::Screenshot, e}}); + // Queue up an expose, so Screenshot() can pull image from updated buffer + signalExpose(); +} + +#endif // GLVIS_USE_CGL diff --git a/lib/cgl/cgl.hpp b/lib/cgl/cgl.hpp new file mode 100644 index 00000000..ecf83146 --- /dev/null +++ b/lib/cgl/cgl.hpp @@ -0,0 +1,117 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. +#pragma once + +#ifdef GLVIS_USE_CGL + +#include + +#include "../gl/platform_gl.hpp" +#include + +#include "../glwindow.hpp" + +class CGLWindow : public GLWindow +{ +public: + struct CGLHandle + { + GLuint buf_frame, buf_color, buf_depth; + CGLPixelFormatObj pix; + + using CGLContextDeleter = void(*)(CGLContextObj); + std::unique_ptr<_CGLContextObject, CGLContextDeleter> ctx + {nullptr, [](CGLContextObj ctx) { CGLDestroyContext(ctx); }}; + + bool isInitialized() const { return ctx != nullptr; } + }; + +private: + CGLHandle handle; + bool running { false }; + bool is_multithreaded { true }; + bool call_idle_func { false }; + + enum class EventType + { + Keydown, + Screenshot, + Quit, + }; + + struct Event + { + EventType type; + union Events + { + struct Keydown + { + SDL_Keycode k; + SDL_Keymod m; + } keydown; + + struct Screenshot + { + bool convert; + } screenshot; + + struct Quit { } quit; + } event; + }; + + std::string screenshot_filename; + std::condition_variable events_available; + std::mutex event_mutex; + std::deque waiting_events; + + void queueEvents(std::vector events); + +public: + CGLWindow(); + ~CGLWindow(); + + bool initGLEW(bool legacyGlOnly); + + /// Creates a new OpenGL window. Returns false if initialization fails + bool createWindow(const char *title, int x, int y, int w, int h, + bool legacyGlOnly) override; + + /// Runs the window loop. + void mainLoop() override; + void mainIter() override; + + void signalLoop() override; + + void getWindowSize(int& w, int& h) const override { getGLDrawSize(w, h); } + void getGLDrawSize(int& w, int& h) const override; + + // use the default values of SdlWindow, LoadFont() ignores them anyway + void getDpi(int& wdpi, int& hdpi) const override { wdpi = hdpi = 72; } + bool isHighDpi() const override; + + void setWindowSize(int w, int h) override; + + bool isWindowInitialized() const override { return isGlInitialized(); } + bool isGlInitialized() const override { return handle.ctx != nullptr; } + + void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) override; + void signalQuit() override; + // as there is no swap, switch to updated state right away + void signalSwap() override { wnd_state = RenderState::Updated; } + + // used in Screenshot, as there is no swapping, the single buffer is always + // up to date and can be read directly + bool isSwapPending() const override { return true; } + + void screenshot(std::string filename, bool convert = false) override; +}; + +#endif // GLVIS_USE_CGL \ No newline at end of file diff --git a/lib/cgl/cgl_main.cpp b/lib/cgl/cgl_main.cpp new file mode 100644 index 00000000..67004854 --- /dev/null +++ b/lib/cgl/cgl_main.cpp @@ -0,0 +1,491 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifdef GLVIS_USE_CGL + +#include +#include + +#include "cgl_main.hpp" + +#include "../aux_vis.hpp" + +#define NVTX_COLOR ::nvtx::kYellow +#include "../../nvtx.hpp" // IWYU pragma: keep + +using namespace std; + +/////////////////////////////////////////////////////////////////////////////// +struct CGLMainThread::CreateWndCmd +{ + CGLWindow *wnd; + int w, h; + bool legacy_gl; + promise handle; +}; + +struct CGLMainThread::ResizeWndCmd +{ + Handle *handle; + int w, h; +}; + +struct CGLMainThread::DeleteWndCmd +{ + CGLWindow *wnd; + Handle *handle; +}; + +/////////////////////////////////////////////////////////////////////////////// +bool CGLMainThread::CreateWndImpl(CreateWndCmd &cmd) +{ + dbg(); + Handle handle; + + const int multisamples = GetMultisample(); + dbg("multisamples:{}", multisamples); + + vector pixAttribs = + { + kCGLPFASampleBuffers, CGLPixelFormatAttribute((multisamples > 0)?(1):(0)), // must be first + kCGLPFASamples, CGLPixelFormatAttribute(multisamples), + kCGLPFAColorSize, CGLPixelFormatAttribute(24), + kCGLPFAAlphaSize, CGLPixelFormatAttribute(8), + kCGLPFADepthSize, CGLPixelFormatAttribute(24), + kCGLPFAAccelerated, // must be last + CGLPixelFormatAttribute(0) + }; + + if (cmd.legacy_gl) + { + dbg("Inserting legacy OpenGL profile"); + // insert legacy OpenGL compatibility requirement + auto it = (pixAttribs.end() -= 2); + pixAttribs.insert(it, {kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_Legacy)}); + } + + dbg("Choosing CGL pixel format"); + GLint numConfigs; + CGLError err = CGLChoosePixelFormat(pixAttribs.data(), &handle.pix, + &numConfigs); + if (multisamples > 0 && (err != kCGLNoError || numConfigs < 1)) + { + std::cerr << "CGL with multisampling is not supported, turning it off" << + std::endl; + pixAttribs[1] = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttribs.data(), &handle.pix, &numConfigs); + } + if (err != kCGLNoError || numConfigs < 1) + { + std::cerr << "CGL with hardware acceleration not supported, turning it off" << + std::endl; + pixAttribs.pop_back(); + pixAttribs.back() = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttribs.data(), &handle.pix, &numConfigs); + } + if (err != kCGLNoError || numConfigs < 1) + { + std::cerr << "Cannot set up CGL pixel format configuration, error: " + << CGLErrorString(err) << std::endl; + return false; + } + + dbg("Creating CGL context"); + CGLContextObj ctx; + err = CGLCreateContext(handle.pix, nullptr, &ctx); + assert(err == kCGLNoError); + if (err != kCGLNoError) + { + std::cerr << "Cannot create an OpenGL context, error: " + << CGLErrorString(err) << std::endl; + return false; + } + + handle.ctx.reset(ctx); + assert(handle.ctx != nullptr); + + windows.push_back(cmd.wnd); + if (num_windows < 0) + { + num_windows = 1; + } + else + { + num_windows++; + } + cmd.handle.set_value(std::move(handle)); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +bool CGLMainThread::ResizeWndImpl(ResizeWndCmd &cmd) +{ + dbg(); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +bool CGLMainThread::DeleteWndImpl(DeleteWndCmd &cmd) +{ + dbg(); + if (cmd.handle->isInitialized()) + { + dbg("Destroying buffers"); + // CGLDeleteFrameBuffers(cmd.handle); + + dbg(); + assert(glGetError() == GL_NO_ERROR); + assert(cmd.handle); + static_assert(sizeof(cmd.handle->buf_frame) == sizeof(GLuint), + "Buffer frame size must be 4 bytes"); + dbg("Deleting frame buffers"); + glDeleteFramebuffers(1, &cmd.handle->buf_frame); + dbg("Deleting color buffers"); + glDeleteRenderbuffers(1, &cmd.handle->buf_color); + dbg("Deleting depth buffers"); + glDeleteRenderbuffers(1, &cmd.handle->buf_depth); + dbg("✅"); + assert(glGetError() == GL_NO_ERROR); + } + + if (cmd.handle->pix) + { + dbg("Destroying pixel format"); + CGLError err = CGLDestroyPixelFormat(cmd.handle->pix); + if (err != kCGLNoError) + { + std::cerr << "Cannot destroy pixel format, error: " + << CGLErrorString(err) << std::endl; + return false; + } + cmd.handle->pix = nullptr; + } + + dbg("Deleting window"); + windows.remove(cmd.wnd); + num_windows--; + + dbg("✅"); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +void CGLMainThread::QueueWndCmd(CtrlCmd cmd, bool sync) +{ + dbg(); + future wait_complete; + if (sync) + { + dbg("Waiting for command to complete"); + wait_complete = cmd.finished.get_future(); + } + // queue up our event + { + dbg("Queueing command"); + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(cmd)); + } + // wake up the main thread to handle our event + events_available.notify_all(); + + if (sync) { dbg("Waiting for command to complete"); wait_complete.get(); } +} + +/////////////////////////////////////////////////////////////////////////////// +void CGLMainThread::InterruptHandler(int param) +{ + dbg(); + CGLMainThread::Get().Terminate(); +} + +/////////////////////////////////////////////////////////////////////////////// +CGLMainThread::CGLMainThread() +{ + dbg(); + GLint major, minor; + CGLGetVersion(&major, &minor); + std::cout << "Using CGL " << major << "." << minor << std::endl; +} + +/////////////////////////////////////////////////////////////////////////////// +CGLMainThread::~CGLMainThread() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +CGLMainThread &CGLMainThread::Get() +{ + dbg(); + static CGLMainThread singleton; + return singleton; +} + +/////////////////////////////////////////////////////////////////////////////// +CGLWindow::CGLHandle CGLMainThread::CreateWindow(CGLWindow *caller, + int w, + int h, bool legacy_gl) +{ + dbg(); + CtrlCmd cmd; + cmd.type = CtrlCmdType::Create; + + CreateWndCmd create_window_cmd; + cmd.create_cmd = &create_window_cmd; + create_window_cmd.wnd = caller; + create_window_cmd.w = w; + create_window_cmd.h = h; + create_window_cmd.legacy_gl = legacy_gl; + + auto result_handle = create_window_cmd.handle.get_future(); + + dbg("Queueing create window command"); + QueueWndCmd(std::move(cmd), false); + + dbg("Waiting for window creation to complete"); + Handle handle = result_handle.get(); + assert(handle.isInitialized()); + + if (!handle.isInitialized()) + { + dbg("❌ Failed to create window"); + return handle; + } + + dbg("Setting CGL context"); + CGLError err = CGLSetCurrentContext(handle.ctx.get()); + if (err != kCGLNoError) + { + dbg("❌ Failed to set context"); + return handle; + } + + { + // need to initialize GLEW before using any OpenGL functions + const auto status = caller->initGLEW(legacy_gl); + assert(status && glGetError() == GL_NO_ERROR); + + dbg("GL error: {}", glGetError()); + const GLubyte* renderer = glGetString(GL_RENDERER); + dbg("OpenGL renderer: {}", (const char*)renderer); + if (strstr((const char*)renderer, "Software") != nullptr) + { + dbg("⚠️ Using software renderer; expect limited functionality"); + } + + const GLubyte* version = glGetString(GL_VERSION); + dbg("OpenGL version: {}", (const char*)version); + dbg("GL error: {}", glGetError()); + } + + glGenFramebuffers(1, &handle.buf_frame); + assert(glGetError() == GL_NO_ERROR); + glCheckFramebufferStatus(GL_FRAMEBUFFER); + assert(glGetError() == GL_NO_ERROR); + + dbg("GL error: {}", glGetError()); + dbg("new_handle.buf_frame:{}", (int)handle.buf_frame); + assert(handle.buf_frame != 0); + dbg("Creating buf_color and buf_depth renderbuffers on main thread"); + glGenRenderbuffers(1, &handle.buf_color); + dbg("new_handle.buf_color:{}", (int)handle.buf_color); + glGenRenderbuffers(1, &handle.buf_depth); + dbg("new_handle.buf_depth:{}", (int)handle.buf_depth); + ResizeWindow(handle, w, h); // Perform initial resize here + + // Release current context + CGLSetCurrentContext(nullptr); + assert(glGetError() == GL_NO_ERROR); + + // dbg("Making context current on this thread"); + // CGLInitializeFrameBuffers(handle, cmd.create_cmd->w, cmd.create_cmd->h); + + err = CGLSetCurrentContext(handle.ctx.get()); + if (err != kCGLNoError) + { + dbg("❌ Cannot set CGL context as current"); + } + dbg("✅"); + return handle; +} + +/////////////////////////////////////////////////////////////////////////////// +void CGLMainThread::ResizeWindow(Handle &handle, int w, int h) +{ + dbg("Resizing window to {}x{}", w, h); + // CGLResizeWindow(handle, w, h); + dbg("Resizing window to {}x{}", w, h); + assert(glGetError() == GL_NO_ERROR); + + if (!handle.isInitialized()) + { + dbg("❌ Handle not initialized, cannot resize"); + return; + } + + dbg("glBindRenderbuffer buf_color"); + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); + + dbg("glRenderbufferStorage"); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); + + dbg("glBindRenderbuffer buf_depth"); + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_depth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); + + dbg("glBindFramebuffer buf_frame"); + glBindFramebuffer(GL_FRAMEBUFFER, handle.buf_frame); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, handle.buf_color); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, handle.buf_depth); + + if (glGetError() != GL_NO_ERROR || + glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + dbg("❌ Cannot resize the framebuffer!"); + } + + assert(glGetError() == GL_NO_ERROR); + dbg("✅"); +} + +/////////////////////////////////////////////////////////////////////////////// +void CGLMainThread::DeleteWindow(CGLWindow *caller, Handle &handle) +{ + dbg(); + if (!handle.isInitialized()) { return; } + + CtrlCmd cmd; + cmd.type = CtrlCmdType::Delete; + + DeleteWndCmd del_cmd; + cmd.delete_cmd = &del_cmd; + + del_cmd.wnd = caller; + del_cmd.handle = &handle; + + QueueWndCmd(std::move(cmd), true); + dbg("✅"); +} + +/////////////////////////////////////////////////////////////////////////////// +void CGLMainThread::Terminate() +{ + dbg(); + CtrlCmd cmd; + cmd.type = CtrlCmdType::Terminate; + + // queue to the front to get priority + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_front(std::move(cmd)); + } + // wake up the main thread to handle our event + events_available.notify_all(); +} + +/////////////////////////////////////////////////////////////////////////////// +void CGLMainThread::MainLoop(bool server) +{ + dbg(); + server_mode = server; + bool terminating = false; + + // set up interrupt handler for graceful closing + signal(SIGINT, InterruptHandler); + + while (true) + { + bool events_pending = false; + { + lock_guard evt_guard{window_cmd_mtx}; + events_pending = !window_cmds.empty(); + } + if (events_pending) + { + do + { + CtrlCmd cmd; + // Fetch next event from the queue + { + lock_guard evt_guard{window_cmd_mtx}; + cmd = std::move(window_cmds.front()); + window_cmds.pop_front(); + events_pending = !window_cmds.empty(); + // Skip non-delete events if terminating + if (terminating && cmd.type != CtrlCmdType::Delete) + { + continue; + } + } + + switch (cmd.type) + { + case CtrlCmdType::Create: + if (!CreateWndImpl(*cmd.create_cmd)) + { + terminating = true; + } + break; + case CtrlCmdType::Resize: + if (!ResizeWndImpl(*cmd.resize_cmd)) + { + terminating = true; + } + break; + case CtrlCmdType::Delete: + if (!DeleteWndImpl(*cmd.delete_cmd)) + { + terminating = true; + } + break; + case CtrlCmdType::Terminate: + // do not wait for windows to open + if (num_windows < 0) { num_windows = 0; } + terminating = true; + break; + } + + // Signal completion of the command, in case worker thread is waiting. + cmd.finished.set_value(); + } + while (events_pending); + } + + if (num_windows == 0) + { + if (!server_mode || terminating) + { + break; + } + } + + if (terminating) + { + for (CGLWindow *wnd : windows) + { + wnd->signalQuit(); + } + } + + { + unique_lock event_lock{window_cmd_mtx}; + events_available.wait(event_lock, [this]() + { + // Sleep until events from windows can be handled + return !window_cmds.empty(); + }); + } + } +} + +#endif // GLVIS_USE_CGL diff --git a/lib/cgl/cgl_main.hpp b/lib/cgl/cgl_main.hpp new file mode 100644 index 00000000..455c063a --- /dev/null +++ b/lib/cgl/cgl_main.hpp @@ -0,0 +1,78 @@ +// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. +#pragma once + +#ifdef GLVIS_USE_CGL + +#include +#include + +#include "cgl.hpp" + +class CGLMainThread : public MainThread +{ + using Handle = CGLWindow::CGLHandle; + bool server_mode {false}; + std::list windows; + int num_windows {-1}; + + struct CreateWndCmd; + struct ResizeWndCmd; + struct DeleteWndCmd; + + enum class CtrlCmdType + { + Create, + Resize, + Delete, + Terminate, + }; + + struct CtrlCmd + { + CtrlCmdType type; + union + { + CreateWndCmd *create_cmd; + ResizeWndCmd *resize_cmd; + DeleteWndCmd *delete_cmd; + }; + std::promise finished; + }; + + std::condition_variable events_available; + std::mutex window_cmd_mtx; + std::deque window_cmds; + + bool CreateWndImpl(CreateWndCmd &cmd); + bool ResizeWndImpl(ResizeWndCmd &cmd); + bool DeleteWndImpl(DeleteWndCmd &cmd); + void QueueWndCmd(CtrlCmd cmd, bool sync); + + static void InterruptHandler(int param); + +public: + + CGLMainThread(); + ~CGLMainThread(); + + static CGLMainThread& Get(); + + Handle CreateWindow(CGLWindow *caller, int w, int h, + bool legacy_gl); + void ResizeWindow(Handle &hnd, int w, int h); + void DeleteWindow(CGLWindow *caller, Handle &hnd); + void Terminate(); + + void MainLoop(bool server = false) override; +}; + +#endif //GLVIS_USE_CGL diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 4a7aeafb..dc08c0cd 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -9,7 +9,7 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) +#ifdef GLVIS_USE_EGL #include "egl.hpp" #include "egl_main.hpp" @@ -156,7 +156,6 @@ void EglWindow::signalLoop() void EglWindow::getGLDrawSize(int& w, int& h) const { -#ifdef GLVIS_USE_EGL EGLint egl_w, egl_h; EGLDisplay disp = EglMainThread::Get().GetDisplay(); @@ -164,14 +163,6 @@ void EglWindow::getGLDrawSize(int& w, int& h) const eglQuerySurface(disp, handle.surf, EGL_HEIGHT, &egl_h); w = egl_w; h = egl_h; -#endif -#ifdef GLVIS_USE_CGL - GLint cgl_w, cgl_h; - glGetRenderbufferParameteriv(handle.buf_color, GL_RENDERBUFFER_WIDTH, &cgl_w); - glGetRenderbufferParameteriv(handle.buf_color, GL_RENDERBUFFER_HEIGHT, &cgl_h); - w = cgl_w; - h = cgl_h; -#endif } bool EglWindow::isHighDpi() const @@ -206,4 +197,4 @@ void EglWindow::screenshot(string filename, bool convert) signalExpose(); } -#endif //GLVIS_USE_EGL || GLVIS_USE_CGL +#endif // GLVIS_USE_EGL diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index 8627f5b6..28308cc8 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -12,20 +12,11 @@ #ifndef GLVIS_EGL_HPP #define GLVIS_EGL_HPP -#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) +#ifdef GLVIS_USE_EGL #include "../glwindow.hpp" -#ifdef GLVIS_USE_EGL #include -#endif - -#ifdef GLVIS_USE_CGL -#define GL_SILENCE_DEPRECATION // CGL has been deprecated since MacOS 10.14 -#include -#include -#include -#endif #include #include @@ -36,35 +27,22 @@ class EglWindow : public GLWindow public: struct Handle { -#ifdef GLVIS_USE_EGL EGLSurface surf {EGL_NO_SURFACE}; EGLContext ctx{EGL_NO_CONTEXT}; EGLConfig eglCfg{}; -#endif -#ifdef GLVIS_USE_CGL - GLuint buf_frame {}, buf_color {}, buf_depth {}; - CGLPixelFormatObj pixFmt {}; - CGLContextObj ctx{}; -#endif bool isInitialized() { -#ifdef GLVIS_USE_EGL return surf != EGL_NO_SURFACE && ctx != EGL_NO_CONTEXT; -#endif -#ifdef GLVIS_USE_CGL - return ctx != nullptr; -#endif } }; private: Handle handle; - bool running{false}; - - bool is_multithreaded{true}; - bool call_idle_func{false}; + bool running { false }; + bool is_multithreaded { true }; + bool call_idle_func { false }; enum class EventType { @@ -72,6 +50,7 @@ class EglWindow : public GLWindow Screenshot, Quit, }; + struct Event { EventType type; @@ -93,7 +72,6 @@ class EglWindow : public GLWindow }; std::string screenshot_filename; - std::condition_variable events_available; std::mutex event_mutex; std::deque waiting_events; @@ -104,8 +82,8 @@ class EglWindow : public GLWindow EglWindow(); ~EglWindow(); - /** @brief Creates a new OpenGL window. Returns false if EGL or OpenGL - initialization fails. */ + /** @brief Creates a new OpenGL window. + Returns false if EGL or OpenGL initialization fails. */ bool createWindow(const char *title, int x, int y, int w, int h, bool legacyGlOnly) override; @@ -124,13 +102,8 @@ class EglWindow : public GLWindow void setWindowSize(int w, int h) override; -#if defined(GLVIS_USE_EGL) bool isWindowInitialized() const override { return handle.surf != EGL_NO_SURFACE; } bool isGlInitialized() const override { return handle.ctx != EGL_NO_CONTEXT; } -#elif defined(GLVIS_USE_CGL) - bool isWindowInitialized() const override { return isGlInitialized(); } - bool isGlInitialized() const override { return handle.ctx != nullptr; } -#endif void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) override; void signalQuit() override; @@ -144,5 +117,5 @@ class EglWindow : public GLWindow void screenshot(std::string filename, bool convert = false) override; }; -#endif //GLVIS_USE_EGL || GLVIS_USE_CGL -#endif //GLVIS_EGL_HPP \ No newline at end of file +#endif // GLVIS_USE_EGL +#endif // GLVIS_EGL_HPP \ No newline at end of file diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 343e7bd8..c147730a 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -9,7 +9,7 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) +#ifdef GLVIS_USE_EGL #include "egl_main.hpp" #include "../aux_vis.hpp" #include @@ -47,7 +47,6 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) { Handle new_handle; -#ifdef GLVIS_USE_EGL // 1. Select an appropriate configuration const int multisamples = GetMultisample(); @@ -105,61 +104,6 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) std::endl; return false; } -#endif -#ifdef GLVIS_USE_CGL - const int multisamples = GetMultisample(); - - vector pixAttribs = - { - kCGLPFASampleBuffers, CGLPixelFormatAttribute((multisamples > 0)?(1):(0)), // must be first - kCGLPFASamples, CGLPixelFormatAttribute(multisamples), - kCGLPFAColorSize, CGLPixelFormatAttribute(24), - kCGLPFAAlphaSize, CGLPixelFormatAttribute(8), - kCGLPFADepthSize, CGLPixelFormatAttribute(24), - kCGLPFAAccelerated, // must be last - CGLPixelFormatAttribute(0) - }; - - if (cmd.legacy_gl) - { - // insert legacy OpenGL compatibility requirement - auto it = (pixAttribs.end() -= 2); - pixAttribs.insert(it, {kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_Legacy)}); - } - - GLint numConfigs; - CGLError err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pixFmt, - &numConfigs); - if (multisamples > 0 && (err != kCGLNoError || numConfigs < 1)) - { - std::cerr << "CGL with multisampling is not supported, turning it off" << - std::endl; - pixAttribs[1] = CGLPixelFormatAttribute(0); - err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pixFmt, &numConfigs); - } - if (err != kCGLNoError || numConfigs < 1) - { - std::cerr << "CGL with hardware acceleration not supported, turning it off" << - std::endl; - pixAttribs.pop_back(); - pixAttribs.back() = CGLPixelFormatAttribute(0); - err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pixFmt, &numConfigs); - } - if (err != kCGLNoError || numConfigs < 1) - { - std::cerr << "Cannot set up CGL pixel format configuration, error: " - << CGLErrorString(err) << std::endl; - return false; - } - - err = CGLCreateContext(new_handle.pixFmt, NULL, &new_handle.ctx); - if (err != kCGLNoError) - { - std::cerr << "Cannot create an OpenGL context, error: " - << CGLErrorString(err) << std::endl; - return false; - } -#endif windows.push_back(cmd.wnd); if (num_windows < 0) @@ -177,7 +121,6 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) { -#ifdef GLVIS_USE_EGL const EGLint pbufferAttribs[] = { EGL_WIDTH, cmd.w, @@ -201,13 +144,11 @@ bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) } cmd.handle->surf = surf_new; -#endif return true; } bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) { -#ifdef GLVIS_USE_EGL if (cmd.handle->ctx != EGL_NO_CONTEXT) { if (!eglDestroyContext(disp, cmd.handle->ctx)) @@ -227,39 +168,6 @@ bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) } cmd.handle->surf = EGL_NO_SURFACE; } -#endif -#ifdef GLVIS_USE_CGL - if (cmd.handle->isInitialized()) - { - glDeleteFramebuffers(1, &cmd.handle->buf_frame); - glDeleteRenderbuffers(1, &cmd.handle->buf_color); - glDeleteRenderbuffers(1, &cmd.handle->buf_depth); - } - - if (cmd.handle->ctx) - { - CGLError err = CGLDestroyContext(cmd.handle->ctx); - if (err != kCGLNoError) - { - std::cerr << "Cannot destroy context, error: " - << CGLErrorString(err) << std::endl; - return false; - } - cmd.handle->ctx = nullptr; - } - - if (cmd.handle->pixFmt) - { - CGLError err = CGLDestroyPixelFormat(cmd.handle->pixFmt); - if (err != kCGLNoError) - { - std::cerr << "Cannot destroy pixel format, error: " - << CGLErrorString(err) << std::endl; - return false; - } - cmd.handle->pixFmt = nullptr; - } -#endif windows.remove(cmd.wnd); num_windows--; @@ -291,7 +199,6 @@ void EglMainThread::InterruptHandler(int param) EglMainThread::EglMainThread() { -#ifdef GLVIS_USE_EGL if (disp != EGL_NO_DISPLAY) { return; @@ -315,20 +222,11 @@ EglMainThread::EglMainThread() } std::cout << "Using EGL " << major << "." << minor << std::endl; -#endif -#ifdef GLVIS_USE_CGL - GLint major, minor; - CGLGetVersion(&major, &minor); - - std::cout << "Using CGL " << major << "." << minor << std::endl; -#endif } EglMainThread::~EglMainThread() { -#ifdef GLVIS_USE_EGL eglTerminate(disp); -#endif } EglMainThread &EglMainThread::Get() @@ -357,7 +255,6 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, Handle out_hnd = res_handle.get(); -#ifdef GLVIS_USE_EGL if (out_hnd.surf == EGL_NO_SURFACE) { return out_hnd; @@ -435,29 +332,6 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, return out_hnd; } -#endif //GLVIS_USE_EGL -#ifdef GLVIS_USE_CGL - if (!out_hnd.isInitialized()) - { - return out_hnd; - } - - CGLError err = CGLSetCurrentContext(out_hnd.ctx); - if (err != kCGLNoError) - { - std::cerr << "Cannot set the CGL context as current, error: " - << CGLErrorString(err) << std::endl; - - return out_hnd; - } - - assert(out_hnd.buf_frame != 0); - glGenFramebuffers(1, &out_hnd.buf_frame); - glGenRenderbuffers(1, &out_hnd.buf_color); - glGenRenderbuffers(1, &out_hnd.buf_depth); - - ResizeWindow(out_hnd, cmd.create_cmd->w, cmd.create_cmd->h); -#endif //GLVIS_USE_CGL return out_hnd; } @@ -466,7 +340,6 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) { if (!handle.isInitialized()) { return; } -#ifdef GLVIS_USE_EGL CtrlCmd cmd; cmd.type = CtrlCmdType::Resize; @@ -485,26 +358,6 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) << std::endl; return; } -#endif -#ifdef GLVIS_USE_CGL - glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); - - glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_depth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); - - glBindFramebuffer(GL_FRAMEBUFFER, handle.buf_frame); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, handle.buf_color); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, handle.buf_depth); - - if (glGetError() != GL_NO_ERROR || - glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - { - std::cerr << "Cannot resize the framebuffer!" << std::endl; - } -#endif } void EglMainThread::DeleteWindow(EglWindow *caller, Handle &handle) @@ -630,4 +483,4 @@ void EglMainThread::MainLoop(bool server) } } -#endif //GLVIS_USE_EGL || GLVIS_USE_CGL +#endif // GLVIS_USE_EGL diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index 3ae6a980..0f0c4b23 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -12,7 +12,7 @@ #ifndef GLVIS_EGL_MAIN_HPP #define GLVIS_EGL_MAIN_HPP -#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) +#ifdef GLVIS_USE_EGL #include #include @@ -22,9 +22,7 @@ class EglMainThread : public MainThread { using Handle = EglWindow::Handle; -#ifdef GLVIS_USE_EGL EGLDisplay disp {EGL_NO_DISPLAY}; -#endif bool server_mode {false}; std::list windows; @@ -84,5 +82,5 @@ class EglMainThread : public MainThread void MainLoop(bool server = false) override; }; -#endif //GLVIS_USE_EGL -#endif //GLVIS_EGL_MAIN_HPP +#endif // GLVIS_USE_EGL +#endif // GLVIS_EGL_MAIN_HPP diff --git a/makefile b/makefile index f6ca1a6b..b136d25a 100644 --- a/makefile +++ b/makefile @@ -266,6 +266,7 @@ Ccc = $(strip $(CC) $(CFLAGS) $(GL_OPTS)) # generated with 'echo lib/egl/*.c* lib/gl/*.c* lib/sdl/*.c* lib/*.c*', does not include lib/*.m (Obj-C) ALL_SOURCE_FILES = \ + lib/cgl/cgl.cpp lib/cgl/cgl_main.cpp \ lib/egl/egl.cpp lib/egl/egl_main.cpp lib/gl/renderer_core.cpp \ lib/gl/renderer.cpp lib/gl/renderer_ff.cpp lib/gl/shader.cpp \ lib/gl/types.cpp lib/sdl/sdl.cpp lib/sdl/sdl_helper.cpp \ @@ -288,6 +289,7 @@ COMMON_SOURCE_FILES = $(filter-out \ # generated with 'echo lib/egl/*.h* lib/gl/*.h* lib/sdl/*.h* lib/*.h*' HEADER_FILES = \ + lib/cgl/cgl.hpp lib/cgl/cgl_main.hpp \ lib/egl/egl.hpp lib/egl/egl_main.hpp lib/gl/attr_traits.hpp \ lib/gl/platform_gl.hpp lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp \ lib/gl/renderer.hpp lib/gl/shader.hpp lib/gl/types.hpp \ From 1a998bb86ed86974f8de954f61184f6b96f2fbdf Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 11:49:46 -0700 Subject: [PATCH 56/79] Meld back to EGL fused with CGL --- lib/CMakeLists.txt | 4 - lib/aux_vis.cpp | 54 +++++----- lib/egl/egl.cpp | 71 ++++++++++++- lib/egl/egl.hpp | 45 +++++++-- lib/egl/egl_main.cpp | 231 ++++++++++++++++++++++++++++++++++++++++++- lib/egl/egl_main.hpp | 6 +- makefile | 1 - 7 files changed, 366 insertions(+), 46 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 98752ded..22ab9746 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,8 +10,6 @@ # CONTRIBUTING.md for details. list(APPEND SOURCES - cgl/cgl.cpp - cgl/cgl_main.cpp egl/egl.cpp egl/egl_main.cpp gl/renderer.cpp @@ -42,8 +40,6 @@ list(APPEND SOURCES window.cpp) list(APPEND HEADERS - cgl/cgl.hpp - cgl/cgl_main.hpp egl/egl.hpp egl/egl_main.hpp gl/attr_traits.hpp diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 554ab868..1cf6fcf2 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -19,8 +19,8 @@ #include "mfem.hpp" #include "sdl/sdl.hpp" #include "sdl/sdl_main.hpp" -#include "cgl/cgl.hpp" -#include "cgl/cgl_main.hpp" +// #include "cgl/cgl.hpp" +// #include "cgl/cgl_main.hpp" #include "egl/egl.hpp" #include "egl/egl_main.hpp" #include "palettes.hpp" @@ -65,11 +65,11 @@ static bool wndUseHiDPI = true; MainThread& GetMainThread(bool headless) { dbg(); -#if defined(GLVIS_USE_EGL) - if (headless) { return EglMainThread::Get(); } -#endif -#if defined(GLVIS_USE_CGL) - if (headless) { return CGLMainThread::Get(); } +#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) + if (headless) + { + return EglMainThread::Get(); + } #endif return GetSdlMainThread(); } @@ -154,11 +154,11 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, } else { -#if defined(GLVIS_USE_EGL) +#if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) sdl_wnd = nullptr; if (!wnd) { - dbg("new EGL window"); + dbg("new XGL window"); wnd = new EglWindow(); if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) { @@ -171,24 +171,24 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, { wnd->clearEvents(); } -#elif defined(GLVIS_USE_CGL) - sdl_wnd = nullptr; - if (!wnd) - { - dbg("new CGL window"); - wnd = new CGLWindow(); - if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) - { - dbg("❌ Failed to create CGL window"); - delete wnd; - wnd = nullptr; - return NULL; - } - } - else - { - wnd->clearEvents(); - } + // #elif defined(GLVIS_USE_CGL) + // sdl_wnd = nullptr; + // if (!wnd) + // { + // dbg("new CGL window"); + // wnd = new CGLWindow(); + // if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) + // { + // dbg("❌ Failed to create CGL window"); + // delete wnd; + // wnd = nullptr; + // return NULL; + // } + // } + // else + // { + // wnd->clearEvents(); + // } #else // GLVIS_USE_EGL || GLVIS_USE_CGL cerr << "EGL or CGL are required for headless rendering!" << endl; return NULL; diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index dc08c0cd..07470e74 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -9,13 +9,14 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) #include "egl.hpp" #include "egl_main.hpp" #include "../aux_vis.hpp" #include #include +// #include #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s @@ -23,6 +24,9 @@ #define PRINT_DEBUG(s) {} #endif +#define NVTX_COLOR ::nvtx::kMagenta +#include "../../nvtx.hpp" // IWYU pragma: keep + using namespace std; EglWindow::EglWindow() @@ -34,15 +38,30 @@ EglWindow::~EglWindow() EglMainThread::Get().DeleteWindow(this, handle); } +#ifdef GLVIS_USE_CGL +bool EglWindow::initGLEW(bool legacyGlOnly) +{ + return GLWindow::initGLEW(legacyGlOnly); +} +#endif + + bool EglWindow::createWindow(const char *, int, int, int w, int h, bool legacyGlOnly) { handle = EglMainThread::Get().CreateWindow(this, w, h, legacyGlOnly); + assert(glGetError() == GL_NO_ERROR); + if (!handle.isInitialized()) { + dbg("❌ Cannot create GL window"); return false; } +#ifdef GLVIS_USE_CGL + return true; +#endif + #ifndef __EMSCRIPTEN__ glEnable(GL_DEBUG_OUTPUT); #endif @@ -156,6 +175,7 @@ void EglWindow::signalLoop() void EglWindow::getGLDrawSize(int& w, int& h) const { +#ifdef GLVIS_USE_EGL EGLint egl_w, egl_h; EGLDisplay disp = EglMainThread::Get().GetDisplay(); @@ -163,6 +183,55 @@ void EglWindow::getGLDrawSize(int& w, int& h) const eglQuerySurface(disp, handle.surf, EGL_HEIGHT, &egl_h); w = egl_w; h = egl_h; +#endif +#ifdef GLVIS_USE_CGL + + const auto err = glGetError(); + dbg("glGetError: {:x}", err); + std::cout << "glGetError:" << err << std::endl; + + assert(glGetError() == GL_NO_ERROR); + GLint cgl_w, cgl_h; + static_assert(sizeof(GLint) == sizeof(int), + "CGL width size must be 4 bytes"); + // CGLGetRenderbufferParameter(handle, cgl_w, cgl_h); + { + // dbg("glGetError:{}", glGetError()); + assert(glGetError() == GL_NO_ERROR); + // assert(handle.isInitialized()); + + // Optional: Ensure context is current if not guaranteed elsewhere + CGLError ctx_err = CGLSetCurrentContext(handle.ctx.get()); + if (ctx_err != kCGLNoError) { dbg("❌ Cannot set CGL context as current");} + // Bind the color renderbuffer (assuming that's what we want to query) + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); + if (glGetError() != GL_NO_ERROR) + { + dbg("❌ Failed to bind renderbuffer"); + // return EXIT_FAILURE; + } + // Query width and height + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &cgl_w); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &cgl_h); + // Check for query errors + GLenum gl_err = glGetError(); + if (gl_err != GL_NO_ERROR) + { + dbg("❌ GL error during parameter query: {}", gl_err); + // Unbind to clean up + glBindRenderbuffer(GL_RENDERBUFFER, 0); + return ; + } + dbg("Renderbuffer parameters - width: {}, height: {}", cgl_w, cgl_h); + // Unbind to restore state + glBindRenderbuffer(GL_RENDERBUFFER, 0); + assert(glGetError() == GL_NO_ERROR); + } + + dbg("CGL draw size: {}x{}", (int) cgl_w, (int)cgl_h); + w = cgl_w; + h = cgl_h; +#endif } bool EglWindow::isHighDpi() const diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index 28308cc8..9cf86981 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -12,13 +12,20 @@ #ifndef GLVIS_EGL_HPP #define GLVIS_EGL_HPP -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) #include "../glwindow.hpp" +#ifdef GLVIS_USE_EGL #include +#endif + +#ifdef GLVIS_USE_CGL +#include +#endif #include +#include #include #include @@ -27,22 +34,37 @@ class EglWindow : public GLWindow public: struct Handle { +#ifdef GLVIS_USE_EGL EGLSurface surf {EGL_NO_SURFACE}; EGLContext ctx{EGL_NO_CONTEXT}; EGLConfig eglCfg{}; +#endif +#ifdef GLVIS_USE_CGL + GLuint buf_frame, buf_color, buf_depth; + CGLPixelFormatObj pix; + + using CGLContextDeleter = void(*)(CGLContextObj); + std::unique_ptr<_CGLContextObject, CGLContextDeleter> ctx + {nullptr, [](CGLContextObj ctx) { CGLDestroyContext(ctx); }}; +#endif bool isInitialized() { +#ifdef GLVIS_USE_EGL return surf != EGL_NO_SURFACE && ctx != EGL_NO_CONTEXT; +#endif +#ifdef GLVIS_USE_CGL + return ctx != nullptr; +#endif } }; private: Handle handle; - bool running { false }; - bool is_multithreaded { true }; - bool call_idle_func { false }; + bool running {false}; + bool is_multithreaded {true}; + bool call_idle_func {false}; enum class EventType { @@ -82,8 +104,12 @@ class EglWindow : public GLWindow EglWindow(); ~EglWindow(); - /** @brief Creates a new OpenGL window. - Returns false if EGL or OpenGL initialization fails. */ +#if defined(GLVIS_USE_CGL) + bool initGLEW(bool legacyGlOnly); +#endif + + /** @brief Creates a new OpenGL window. Returns false if EGL or OpenGL + initialization fails. */ bool createWindow(const char *title, int x, int y, int w, int h, bool legacyGlOnly) override; @@ -102,8 +128,13 @@ class EglWindow : public GLWindow void setWindowSize(int w, int h) override; +#if defined(GLVIS_USE_EGL) bool isWindowInitialized() const override { return handle.surf != EGL_NO_SURFACE; } bool isGlInitialized() const override { return handle.ctx != EGL_NO_CONTEXT; } +#elif defined(GLVIS_USE_CGL) + bool isWindowInitialized() const override { return isGlInitialized(); } + bool isGlInitialized() const override { return handle.ctx != nullptr; } +#endif void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) override; void signalQuit() override; @@ -117,5 +148,5 @@ class EglWindow : public GLWindow void screenshot(std::string filename, bool convert = false) override; }; -#endif // GLVIS_USE_EGL +#endif // GLVIS_USE_EGL || GLVIS_USE_CGL #endif // GLVIS_EGL_HPP \ No newline at end of file diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index c147730a..98ea43da 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -9,18 +9,23 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#ifdef GLVIS_USE_EGL -#include "egl_main.hpp" -#include "../aux_vis.hpp" +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) + #include #include +#include "egl_main.hpp" +#include "../aux_vis.hpp" + #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s #else #define PRINT_DEBUG(s) {} #endif +#define NVTX_COLOR ::nvtx::kYellow +#include "../../nvtx.hpp" // IWYU pragma: keep + using namespace std; struct EglMainThread::CreateWndCmd @@ -47,6 +52,7 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) { Handle new_handle; +#ifdef GLVIS_USE_EGL // 1. Select an appropriate configuration const int multisamples = GetMultisample(); @@ -104,6 +110,70 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) std::endl; return false; } +#endif +#ifdef GLVIS_USE_CGL + const int multisamples = GetMultisample(); + dbg("multisamples:{}", multisamples); + + vector pixAttribs = + { + kCGLPFASampleBuffers, CGLPixelFormatAttribute((multisamples > 0)?(1):(0)), // must be first + kCGLPFASamples, CGLPixelFormatAttribute(multisamples), + kCGLPFAColorSize, CGLPixelFormatAttribute(24), + kCGLPFAAlphaSize, CGLPixelFormatAttribute(8), + kCGLPFADepthSize, CGLPixelFormatAttribute(24), + kCGLPFAAccelerated, // must be last + CGLPixelFormatAttribute(0) + }; + + if (cmd.legacy_gl) + { + dbg("Inserting legacy OpenGL profile"); + // insert legacy OpenGL compatibility requirement + auto it = (pixAttribs.end() -= 2); + pixAttribs.insert(it, {kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_Legacy)}); + } + + dbg("Choosing CGL pixel format"); + GLint numConfigs; + CGLError err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pix, + &numConfigs); + if (multisamples > 0 && (err != kCGLNoError || numConfigs < 1)) + { + std::cerr << "CGL with multisampling is not supported, turning it off" << + std::endl; + pixAttribs[1] = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pix, &numConfigs); + } + if (err != kCGLNoError || numConfigs < 1) + { + std::cerr << "CGL with hardware acceleration not supported, turning it off" << + std::endl; + pixAttribs.pop_back(); + pixAttribs.back() = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pix, &numConfigs); + } + if (err != kCGLNoError || numConfigs < 1) + { + std::cerr << "Cannot set up CGL pixel format configuration, error: " + << CGLErrorString(err) << std::endl; + return false; + } + + dbg("Creating CGL context"); + CGLContextObj ctx; + err = CGLCreateContext(new_handle.pix, nullptr, &ctx); + assert(err == kCGLNoError); + if (err != kCGLNoError) + { + std::cerr << "Cannot create an OpenGL context, error: " + << CGLErrorString(err) << std::endl; + return false; + } + + new_handle.ctx.reset(ctx); + assert(new_handle.ctx != nullptr); +#endif windows.push_back(cmd.wnd); if (num_windows < 0) @@ -121,6 +191,7 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) { +#ifdef GLVIS_USE_EGL const EGLint pbufferAttribs[] = { EGL_WIDTH, cmd.w, @@ -144,11 +215,13 @@ bool EglMainThread::ResizeWndImpl(ResizeWndCmd &cmd) } cmd.handle->surf = surf_new; +#endif return true; } bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) { +#ifdef GLVIS_USE_EGL if (cmd.handle->ctx != EGL_NO_CONTEXT) { if (!eglDestroyContext(disp, cmd.handle->ctx)) @@ -168,6 +241,42 @@ bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) } cmd.handle->surf = EGL_NO_SURFACE; } +#endif +#ifdef GLVIS_USE_CGL + + if (cmd.handle->isInitialized()) + { + dbg("Destroying buffers"); + // CGLDeleteFrameBuffers(cmd.handle); + + dbg(); + assert(glGetError() == GL_NO_ERROR); + assert(cmd.handle); + static_assert(sizeof(cmd.handle->buf_frame) == sizeof(GLuint), + "Buffer frame size must be 4 bytes"); + dbg("Deleting frame buffers"); + glDeleteFramebuffers(1, &cmd.handle->buf_frame); + dbg("Deleting color buffers"); + glDeleteRenderbuffers(1, &cmd.handle->buf_color); + dbg("Deleting depth buffers"); + glDeleteRenderbuffers(1, &cmd.handle->buf_depth); + dbg("✅"); + assert(glGetError() == GL_NO_ERROR); + } + + if (cmd.handle->pix) + { + dbg("Destroying pixel format"); + CGLError err = CGLDestroyPixelFormat(cmd.handle->pix); + if (err != kCGLNoError) + { + std::cerr << "Cannot destroy pixel format, error: " + << CGLErrorString(err) << std::endl; + return false; + } + cmd.handle->pix = nullptr; + } +#endif windows.remove(cmd.wnd); num_windows--; @@ -199,6 +308,7 @@ void EglMainThread::InterruptHandler(int param) EglMainThread::EglMainThread() { +#ifdef GLVIS_USE_EGL if (disp != EGL_NO_DISPLAY) { return; @@ -222,11 +332,19 @@ EglMainThread::EglMainThread() } std::cout << "Using EGL " << major << "." << minor << std::endl; +#endif +#ifdef GLVIS_USE_CGL + GLint major, minor; + CGLGetVersion(&major, &minor); + std::cout << "Using CGL " << major << "." << minor << std::endl; +#endif } EglMainThread::~EglMainThread() { +#ifdef GLVIS_USE_EGL eglTerminate(disp); +#endif } EglMainThread &EglMainThread::Get() @@ -255,6 +373,7 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, Handle out_hnd = res_handle.get(); +#ifdef GLVIS_USE_EGL if (out_hnd.surf == EGL_NO_SURFACE) { return out_hnd; @@ -332,6 +451,70 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, return out_hnd; } +#endif //GLVIS_USE_EGL +#ifdef GLVIS_USE_CGL + + if (!out_hnd.isInitialized()) + { + dbg("❌ Failed to create window"); + return out_hnd; + } + + // dbg("Setting CGL context"); + CGLError err = CGLSetCurrentContext(out_hnd.ctx.get()); + if (err != kCGLNoError) + { + dbg("❌ Failed to set context"); + return out_hnd; + } + + { + // need to initialize GLEW before using any OpenGL functions + const auto status = caller->initGLEW(legacy_gl); + assert(status && glGetError() == GL_NO_ERROR); + + dbg("GL error: {}", glGetError()); + const GLubyte* renderer = glGetString(GL_RENDERER); + dbg("OpenGL renderer: {}", (const char*)renderer); + if (strstr((const char*)renderer, "Software") != nullptr) + { + dbg("⚠️ Using software renderer; expect limited functionality"); + } + + const GLubyte* version = glGetString(GL_VERSION); + dbg("OpenGL version: {}", (const char*)version); + dbg("GL error: {}", glGetError()); + } + + glGenFramebuffers(1, &out_hnd.buf_frame); + assert(glGetError() == GL_NO_ERROR); + glCheckFramebufferStatus(GL_FRAMEBUFFER); + assert(glGetError() == GL_NO_ERROR); + + dbg("GL error: {}", glGetError()); + dbg("new_handle.buf_frame:{}", (int)out_hnd.buf_frame); + assert(out_hnd.buf_frame != 0); + dbg("Creating buf_color and buf_depth renderbuffers on main thread"); + glGenRenderbuffers(1, &out_hnd.buf_color); + dbg("new_handle.buf_color:{}", (int)out_hnd.buf_color); + glGenRenderbuffers(1, &out_hnd.buf_depth); + dbg("new_handle.buf_depth:{}", (int)out_hnd.buf_depth); + ResizeWindow(out_hnd, w, h); // Perform initial resize here + + // Release current context + CGLSetCurrentContext(nullptr); + assert(glGetError() == GL_NO_ERROR); + + // dbg("Making context current on this thread"); + // CGLInitializeFrameBuffers(handle, cmd.create_cmd->w, cmd.create_cmd->h); + + err = CGLSetCurrentContext(out_hnd.ctx.get()); + if (err != kCGLNoError) + { + dbg("❌ Cannot set CGL context as current"); + } + dbg("✅"); +#endif return out_hnd; } @@ -340,6 +523,7 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) { if (!handle.isInitialized()) { return; } +#ifdef GLVIS_USE_EGL CtrlCmd cmd; cmd.type = CtrlCmdType::Resize; @@ -358,6 +542,45 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) << std::endl; return; } +#endif +#ifdef GLVIS_USE_CGL + + dbg("Resizing window to {}x{}", w, h); + // CGLResizeWindow(handle, w, h); + dbg("Resizing window to {}x{}", w, h); + assert(glGetError() == GL_NO_ERROR); + + if (!handle.isInitialized()) + { + dbg("❌ Handle not initialized, cannot resize"); + return; + } + + dbg("glBindRenderbuffer buf_color"); + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); + + dbg("glRenderbufferStorage"); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); + + dbg("glBindRenderbuffer buf_depth"); + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_depth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); + + dbg("glBindFramebuffer buf_frame"); + glBindFramebuffer(GL_FRAMEBUFFER, handle.buf_frame); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, handle.buf_color); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, handle.buf_depth); + + if (glGetError() != GL_NO_ERROR || + glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + dbg("❌ Cannot resize the framebuffer!"); + } + + assert(glGetError() == GL_NO_ERROR); +#endif } void EglMainThread::DeleteWindow(EglWindow *caller, Handle &handle) @@ -483,4 +706,4 @@ void EglMainThread::MainLoop(bool server) } } -#endif // GLVIS_USE_EGL +#endif // GLVIS_USE_EGL || GLVIS_USE_CGL diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index 0f0c4b23..6643c603 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -12,7 +12,7 @@ #ifndef GLVIS_EGL_MAIN_HPP #define GLVIS_EGL_MAIN_HPP -#ifdef GLVIS_USE_EGL +#if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) #include #include @@ -22,11 +22,13 @@ class EglMainThread : public MainThread { using Handle = EglWindow::Handle; +#ifdef GLVIS_USE_EGL EGLDisplay disp {EGL_NO_DISPLAY}; +#endif bool server_mode {false}; std::list windows; - int num_windows{-1}; + int num_windows {-1}; struct CreateWndCmd; struct ResizeWndCmd; diff --git a/makefile b/makefile index b136d25a..270bf323 100644 --- a/makefile +++ b/makefile @@ -266,7 +266,6 @@ Ccc = $(strip $(CC) $(CFLAGS) $(GL_OPTS)) # generated with 'echo lib/egl/*.c* lib/gl/*.c* lib/sdl/*.c* lib/*.c*', does not include lib/*.m (Obj-C) ALL_SOURCE_FILES = \ - lib/cgl/cgl.cpp lib/cgl/cgl_main.cpp \ lib/egl/egl.cpp lib/egl/egl_main.cpp lib/gl/renderer_core.cpp \ lib/gl/renderer.cpp lib/gl/renderer_ff.cpp lib/gl/shader.cpp \ lib/gl/types.cpp lib/sdl/sdl.cpp lib/sdl/sdl_helper.cpp \ From cf0af34bcb78d1bef924cdcc8c1b55c1e151d87b Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 11:55:09 -0700 Subject: [PATCH 57/79] Remove CGL sub directory --- lib/aux_vis.cpp | 21 -- lib/cgl/cgl.cpp | 256 ---------------------- lib/cgl/cgl.hpp | 117 ----------- lib/cgl/cgl_main.cpp | 491 ------------------------------------------- lib/cgl/cgl_main.hpp | 78 ------- 5 files changed, 963 deletions(-) delete mode 100644 lib/cgl/cgl.cpp delete mode 100644 lib/cgl/cgl.hpp delete mode 100644 lib/cgl/cgl_main.cpp delete mode 100644 lib/cgl/cgl_main.hpp diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 1cf6fcf2..4ea11ad9 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -19,8 +19,6 @@ #include "mfem.hpp" #include "sdl/sdl.hpp" #include "sdl/sdl_main.hpp" -// #include "cgl/cgl.hpp" -// #include "cgl/cgl_main.hpp" #include "egl/egl.hpp" #include "egl/egl_main.hpp" #include "palettes.hpp" @@ -158,7 +156,6 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, sdl_wnd = nullptr; if (!wnd) { - dbg("new XGL window"); wnd = new EglWindow(); if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) { @@ -171,24 +168,6 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, { wnd->clearEvents(); } - // #elif defined(GLVIS_USE_CGL) - // sdl_wnd = nullptr; - // if (!wnd) - // { - // dbg("new CGL window"); - // wnd = new CGLWindow(); - // if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl)) - // { - // dbg("❌ Failed to create CGL window"); - // delete wnd; - // wnd = nullptr; - // return NULL; - // } - // } - // else - // { - // wnd->clearEvents(); - // } #else // GLVIS_USE_EGL || GLVIS_USE_CGL cerr << "EGL or CGL are required for headless rendering!" << endl; return NULL; diff --git a/lib/cgl/cgl.cpp b/lib/cgl/cgl.cpp deleted file mode 100644 index c34ffd44..00000000 --- a/lib/cgl/cgl.cpp +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced -// at the Lawrence Livermore National Laboratory. All Rights reserved. See files -// LICENSE and NOTICE for details. LLNL-CODE-443271. -// -// This file is part of the GLVis visualization tool and library. For more -// information and source code availability see https://glvis.org. -// -// GLVis is free software; you can redistribute it and/or modify it under the -// terms of the BSD-3 license. We welcome feedback and contributions, see file -// CONTRIBUTING.md for details. - -#ifdef GLVIS_USE_CGL - -#include -#include - -#include "cgl.hpp" -#include "cgl_main.hpp" - -#include "../aux_vis.hpp" - -#define NVTX_COLOR ::nvtx::kMagenta -#include "../../nvtx.hpp" // IWYU pragma: keep - -using namespace std; - -CGLWindow::CGLWindow() -{ - dbg("Could create window"); -} - -CGLWindow::~CGLWindow() -{ - dbg(); - CGLMainThread::Get().DeleteWindow(this, handle); -} -// return initGLEW(legacyGlOnly); - -bool CGLWindow::initGLEW(bool legacyGlOnly) -{ - return GLWindow::initGLEW(legacyGlOnly); -} - -bool CGLWindow::createWindow(const char *title, int x, int y, int w, int h, - bool legacyGlOnly) -{ - dbg("title: '{}' x:{} y:{} {}x{} legacyGlOnly:{}", - title, x, y, w, h, legacyGlOnly); - handle = CGLMainThread::Get().CreateWindow(this, w, h, legacyGlOnly); - assert(glGetError() == GL_NO_ERROR); - - if (!handle.isInitialized()) - { - dbg("❌ Cannot create CGL window"); - return false; - } - - // dbg("CGL context is ready"); - // return initGLEW(legacyGlOnly); - return true; -} - -void CGLWindow::queueEvents(vector events) -{ - dbg(); - { - lock_guard evt_guard{event_mutex}; - waiting_events.insert(waiting_events.end(), events.begin(), events.end()); - } - if (is_multithreaded) - { - events_available.notify_all(); - } -} - -void CGLWindow::mainLoop() -{ - dbg(); - running = true; - while (running) - { - mainIter(); - } -} - -void CGLWindow::mainIter() -{ - dbg(); - bool sleep = false; - bool events_pending = false; - { - lock_guard evt_guard{event_mutex}; - events_pending = !waiting_events.empty(); - } - if (events_pending) - { - do - { - Event e; - // Fetch next event from the queue - { - lock_guard evt_guard{event_mutex}; - e = waiting_events.front(); - waiting_events.pop_front(); - events_pending = !waiting_events.empty(); - } - - switch (e.type) - { - case EventType::Keydown: - if (onKeyDown[e.event.keydown.k]) - { - onKeyDown[e.event.keydown.k](e.event.keydown.m); - recordKey(e.event.keydown.k, e.event.keydown.m); - } - break; - case EventType::Screenshot: - if (isExposePending()) - { - onExpose(); - wnd_state = RenderState::Updated; - } - Screenshot(screenshot_filename.c_str(), e.event.screenshot.convert); - break; - case EventType::Quit: - running = false; - break; - } - } - while (events_pending); - } - else if (onIdle) - { - sleep = onIdle(); - } - else - { - // No actions performed this iteration. - sleep = true; - } - - if (isExposePending()) - { - onExpose(); - wnd_state = RenderState::Updated; - } - else if (sleep) - { - unique_lock event_lock{event_mutex}; - events_available.wait(event_lock, [this]() - { - // Sleep until events from WM or glvis_command can be handled - return !waiting_events.empty() || call_idle_func; - }); - } -} - -void CGLWindow::signalLoop() -{ - dbg(); - // Note: not executed from the main thread - { - lock_guard evt_guard{event_mutex}; - call_idle_func = true; - } - events_available.notify_all(); -} - -void CGLWindow::getGLDrawSize(int& w, int& h) const -{ - const auto err = glGetError(); - dbg("glGetError: {:x}", err); - std::cout << "glGetError:" << err << std::endl; - - assert(glGetError() == GL_NO_ERROR); - GLint cgl_w, cgl_h; - static_assert(sizeof(GLint) == sizeof(int), - "CGL width size must be 4 bytes"); - // CGLGetRenderbufferParameter(handle, cgl_w, cgl_h); - { - dbg("glGetError:{}", glGetError()); - assert(glGetError() == GL_NO_ERROR); - assert(handle.isInitialized()); - - // Optional: Ensure context is current if not guaranteed elsewhere - CGLError err = CGLSetCurrentContext(handle.ctx.get()); - if (err != kCGLNoError) { dbg("❌ Cannot set CGL context as current");} - // Bind the color renderbuffer (assuming that's what we want to query) - glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); - if (glGetError() != GL_NO_ERROR) - { - dbg("❌ Failed to bind renderbuffer"); - // return EXIT_FAILURE; - } - // Query width and height - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &cgl_w); - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &cgl_h); - // Check for query errors - GLenum gl_err = glGetError(); - if (gl_err != GL_NO_ERROR) - { - dbg("❌ GL error during parameter query: {}", gl_err); - // Unbind to clean up - glBindRenderbuffer(GL_RENDERBUFFER, 0); - return ; - } - dbg("Renderbuffer parameters - width: {}, height: {}", cgl_w, cgl_h); - // Unbind to restore state - glBindRenderbuffer(GL_RENDERBUFFER, 0); - assert(glGetError() == GL_NO_ERROR); - } - - // cgl_w = 1024, cgl_h = 768; // TEMP - dbg("CGL draw size: {}x{}", (int) cgl_w, (int)cgl_h); - w = cgl_w; - h = cgl_h; -} - -bool CGLWindow::isHighDpi() const -{ - dbg("{}", GetUseHiDPI()); - return GetUseHiDPI(); -} - -void CGLWindow::setWindowSize(int w, int h) -{ - dbg("w:{} h:{}", w, h); - CGLMainThread::Get().ResizeWindow(handle, w, h); -} - -void CGLWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) -{ - dbg("k:{} m:{}", (int)k, (int)m); - Event::Events e; - e.keydown = {k, m}; - queueEvents({{EventType::Keydown, e}}); -} - -void CGLWindow::signalQuit() -{ - dbg("Quit"); - queueEvents({{EventType::Quit, {}}}); -} - -void CGLWindow::screenshot(string filename, bool convert) -{ - dbg("filename:{} convert:{}", filename, convert); - screenshot_filename = filename; - Event::Events e; - e.screenshot = {convert}; - queueEvents({{EventType::Screenshot, e}}); - // Queue up an expose, so Screenshot() can pull image from updated buffer - signalExpose(); -} - -#endif // GLVIS_USE_CGL diff --git a/lib/cgl/cgl.hpp b/lib/cgl/cgl.hpp deleted file mode 100644 index ecf83146..00000000 --- a/lib/cgl/cgl.hpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced -// at the Lawrence Livermore National Laboratory. All Rights reserved. See files -// LICENSE and NOTICE for details. LLNL-CODE-443271. -// -// This file is part of the GLVis visualization tool and library. For more -// information and source code availability see https://glvis.org. -// -// GLVis is free software; you can redistribute it and/or modify it under the -// terms of the BSD-3 license. We welcome feedback and contributions, see file -// CONTRIBUTING.md for details. -#pragma once - -#ifdef GLVIS_USE_CGL - -#include - -#include "../gl/platform_gl.hpp" -#include - -#include "../glwindow.hpp" - -class CGLWindow : public GLWindow -{ -public: - struct CGLHandle - { - GLuint buf_frame, buf_color, buf_depth; - CGLPixelFormatObj pix; - - using CGLContextDeleter = void(*)(CGLContextObj); - std::unique_ptr<_CGLContextObject, CGLContextDeleter> ctx - {nullptr, [](CGLContextObj ctx) { CGLDestroyContext(ctx); }}; - - bool isInitialized() const { return ctx != nullptr; } - }; - -private: - CGLHandle handle; - bool running { false }; - bool is_multithreaded { true }; - bool call_idle_func { false }; - - enum class EventType - { - Keydown, - Screenshot, - Quit, - }; - - struct Event - { - EventType type; - union Events - { - struct Keydown - { - SDL_Keycode k; - SDL_Keymod m; - } keydown; - - struct Screenshot - { - bool convert; - } screenshot; - - struct Quit { } quit; - } event; - }; - - std::string screenshot_filename; - std::condition_variable events_available; - std::mutex event_mutex; - std::deque waiting_events; - - void queueEvents(std::vector events); - -public: - CGLWindow(); - ~CGLWindow(); - - bool initGLEW(bool legacyGlOnly); - - /// Creates a new OpenGL window. Returns false if initialization fails - bool createWindow(const char *title, int x, int y, int w, int h, - bool legacyGlOnly) override; - - /// Runs the window loop. - void mainLoop() override; - void mainIter() override; - - void signalLoop() override; - - void getWindowSize(int& w, int& h) const override { getGLDrawSize(w, h); } - void getGLDrawSize(int& w, int& h) const override; - - // use the default values of SdlWindow, LoadFont() ignores them anyway - void getDpi(int& wdpi, int& hdpi) const override { wdpi = hdpi = 72; } - bool isHighDpi() const override; - - void setWindowSize(int w, int h) override; - - bool isWindowInitialized() const override { return isGlInitialized(); } - bool isGlInitialized() const override { return handle.ctx != nullptr; } - - void signalKeyDown(SDL_Keycode k, SDL_Keymod m = KMOD_NONE) override; - void signalQuit() override; - // as there is no swap, switch to updated state right away - void signalSwap() override { wnd_state = RenderState::Updated; } - - // used in Screenshot, as there is no swapping, the single buffer is always - // up to date and can be read directly - bool isSwapPending() const override { return true; } - - void screenshot(std::string filename, bool convert = false) override; -}; - -#endif // GLVIS_USE_CGL \ No newline at end of file diff --git a/lib/cgl/cgl_main.cpp b/lib/cgl/cgl_main.cpp deleted file mode 100644 index 67004854..00000000 --- a/lib/cgl/cgl_main.cpp +++ /dev/null @@ -1,491 +0,0 @@ -// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced -// at the Lawrence Livermore National Laboratory. All Rights reserved. See files -// LICENSE and NOTICE for details. LLNL-CODE-443271. -// -// This file is part of the GLVis visualization tool and library. For more -// information and source code availability see https://glvis.org. -// -// GLVis is free software; you can redistribute it and/or modify it under the -// terms of the BSD-3 license. We welcome feedback and contributions, see file -// CONTRIBUTING.md for details. - -#ifdef GLVIS_USE_CGL - -#include -#include - -#include "cgl_main.hpp" - -#include "../aux_vis.hpp" - -#define NVTX_COLOR ::nvtx::kYellow -#include "../../nvtx.hpp" // IWYU pragma: keep - -using namespace std; - -/////////////////////////////////////////////////////////////////////////////// -struct CGLMainThread::CreateWndCmd -{ - CGLWindow *wnd; - int w, h; - bool legacy_gl; - promise handle; -}; - -struct CGLMainThread::ResizeWndCmd -{ - Handle *handle; - int w, h; -}; - -struct CGLMainThread::DeleteWndCmd -{ - CGLWindow *wnd; - Handle *handle; -}; - -/////////////////////////////////////////////////////////////////////////////// -bool CGLMainThread::CreateWndImpl(CreateWndCmd &cmd) -{ - dbg(); - Handle handle; - - const int multisamples = GetMultisample(); - dbg("multisamples:{}", multisamples); - - vector pixAttribs = - { - kCGLPFASampleBuffers, CGLPixelFormatAttribute((multisamples > 0)?(1):(0)), // must be first - kCGLPFASamples, CGLPixelFormatAttribute(multisamples), - kCGLPFAColorSize, CGLPixelFormatAttribute(24), - kCGLPFAAlphaSize, CGLPixelFormatAttribute(8), - kCGLPFADepthSize, CGLPixelFormatAttribute(24), - kCGLPFAAccelerated, // must be last - CGLPixelFormatAttribute(0) - }; - - if (cmd.legacy_gl) - { - dbg("Inserting legacy OpenGL profile"); - // insert legacy OpenGL compatibility requirement - auto it = (pixAttribs.end() -= 2); - pixAttribs.insert(it, {kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_Legacy)}); - } - - dbg("Choosing CGL pixel format"); - GLint numConfigs; - CGLError err = CGLChoosePixelFormat(pixAttribs.data(), &handle.pix, - &numConfigs); - if (multisamples > 0 && (err != kCGLNoError || numConfigs < 1)) - { - std::cerr << "CGL with multisampling is not supported, turning it off" << - std::endl; - pixAttribs[1] = CGLPixelFormatAttribute(0); - err = CGLChoosePixelFormat(pixAttribs.data(), &handle.pix, &numConfigs); - } - if (err != kCGLNoError || numConfigs < 1) - { - std::cerr << "CGL with hardware acceleration not supported, turning it off" << - std::endl; - pixAttribs.pop_back(); - pixAttribs.back() = CGLPixelFormatAttribute(0); - err = CGLChoosePixelFormat(pixAttribs.data(), &handle.pix, &numConfigs); - } - if (err != kCGLNoError || numConfigs < 1) - { - std::cerr << "Cannot set up CGL pixel format configuration, error: " - << CGLErrorString(err) << std::endl; - return false; - } - - dbg("Creating CGL context"); - CGLContextObj ctx; - err = CGLCreateContext(handle.pix, nullptr, &ctx); - assert(err == kCGLNoError); - if (err != kCGLNoError) - { - std::cerr << "Cannot create an OpenGL context, error: " - << CGLErrorString(err) << std::endl; - return false; - } - - handle.ctx.reset(ctx); - assert(handle.ctx != nullptr); - - windows.push_back(cmd.wnd); - if (num_windows < 0) - { - num_windows = 1; - } - else - { - num_windows++; - } - cmd.handle.set_value(std::move(handle)); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -bool CGLMainThread::ResizeWndImpl(ResizeWndCmd &cmd) -{ - dbg(); - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -bool CGLMainThread::DeleteWndImpl(DeleteWndCmd &cmd) -{ - dbg(); - if (cmd.handle->isInitialized()) - { - dbg("Destroying buffers"); - // CGLDeleteFrameBuffers(cmd.handle); - - dbg(); - assert(glGetError() == GL_NO_ERROR); - assert(cmd.handle); - static_assert(sizeof(cmd.handle->buf_frame) == sizeof(GLuint), - "Buffer frame size must be 4 bytes"); - dbg("Deleting frame buffers"); - glDeleteFramebuffers(1, &cmd.handle->buf_frame); - dbg("Deleting color buffers"); - glDeleteRenderbuffers(1, &cmd.handle->buf_color); - dbg("Deleting depth buffers"); - glDeleteRenderbuffers(1, &cmd.handle->buf_depth); - dbg("✅"); - assert(glGetError() == GL_NO_ERROR); - } - - if (cmd.handle->pix) - { - dbg("Destroying pixel format"); - CGLError err = CGLDestroyPixelFormat(cmd.handle->pix); - if (err != kCGLNoError) - { - std::cerr << "Cannot destroy pixel format, error: " - << CGLErrorString(err) << std::endl; - return false; - } - cmd.handle->pix = nullptr; - } - - dbg("Deleting window"); - windows.remove(cmd.wnd); - num_windows--; - - dbg("✅"); - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -void CGLMainThread::QueueWndCmd(CtrlCmd cmd, bool sync) -{ - dbg(); - future wait_complete; - if (sync) - { - dbg("Waiting for command to complete"); - wait_complete = cmd.finished.get_future(); - } - // queue up our event - { - dbg("Queueing command"); - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_back(std::move(cmd)); - } - // wake up the main thread to handle our event - events_available.notify_all(); - - if (sync) { dbg("Waiting for command to complete"); wait_complete.get(); } -} - -/////////////////////////////////////////////////////////////////////////////// -void CGLMainThread::InterruptHandler(int param) -{ - dbg(); - CGLMainThread::Get().Terminate(); -} - -/////////////////////////////////////////////////////////////////////////////// -CGLMainThread::CGLMainThread() -{ - dbg(); - GLint major, minor; - CGLGetVersion(&major, &minor); - std::cout << "Using CGL " << major << "." << minor << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////// -CGLMainThread::~CGLMainThread() -{ -} - -/////////////////////////////////////////////////////////////////////////////// -CGLMainThread &CGLMainThread::Get() -{ - dbg(); - static CGLMainThread singleton; - return singleton; -} - -/////////////////////////////////////////////////////////////////////////////// -CGLWindow::CGLHandle CGLMainThread::CreateWindow(CGLWindow *caller, - int w, - int h, bool legacy_gl) -{ - dbg(); - CtrlCmd cmd; - cmd.type = CtrlCmdType::Create; - - CreateWndCmd create_window_cmd; - cmd.create_cmd = &create_window_cmd; - create_window_cmd.wnd = caller; - create_window_cmd.w = w; - create_window_cmd.h = h; - create_window_cmd.legacy_gl = legacy_gl; - - auto result_handle = create_window_cmd.handle.get_future(); - - dbg("Queueing create window command"); - QueueWndCmd(std::move(cmd), false); - - dbg("Waiting for window creation to complete"); - Handle handle = result_handle.get(); - assert(handle.isInitialized()); - - if (!handle.isInitialized()) - { - dbg("❌ Failed to create window"); - return handle; - } - - dbg("Setting CGL context"); - CGLError err = CGLSetCurrentContext(handle.ctx.get()); - if (err != kCGLNoError) - { - dbg("❌ Failed to set context"); - return handle; - } - - { - // need to initialize GLEW before using any OpenGL functions - const auto status = caller->initGLEW(legacy_gl); - assert(status && glGetError() == GL_NO_ERROR); - - dbg("GL error: {}", glGetError()); - const GLubyte* renderer = glGetString(GL_RENDERER); - dbg("OpenGL renderer: {}", (const char*)renderer); - if (strstr((const char*)renderer, "Software") != nullptr) - { - dbg("⚠️ Using software renderer; expect limited functionality"); - } - - const GLubyte* version = glGetString(GL_VERSION); - dbg("OpenGL version: {}", (const char*)version); - dbg("GL error: {}", glGetError()); - } - - glGenFramebuffers(1, &handle.buf_frame); - assert(glGetError() == GL_NO_ERROR); - glCheckFramebufferStatus(GL_FRAMEBUFFER); - assert(glGetError() == GL_NO_ERROR); - - dbg("GL error: {}", glGetError()); - dbg("new_handle.buf_frame:{}", (int)handle.buf_frame); - assert(handle.buf_frame != 0); - dbg("Creating buf_color and buf_depth renderbuffers on main thread"); - glGenRenderbuffers(1, &handle.buf_color); - dbg("new_handle.buf_color:{}", (int)handle.buf_color); - glGenRenderbuffers(1, &handle.buf_depth); - dbg("new_handle.buf_depth:{}", (int)handle.buf_depth); - ResizeWindow(handle, w, h); // Perform initial resize here - - // Release current context - CGLSetCurrentContext(nullptr); - assert(glGetError() == GL_NO_ERROR); - - // dbg("Making context current on this thread"); - // CGLInitializeFrameBuffers(handle, cmd.create_cmd->w, cmd.create_cmd->h); - - err = CGLSetCurrentContext(handle.ctx.get()); - if (err != kCGLNoError) - { - dbg("❌ Cannot set CGL context as current"); - } - dbg("✅"); - return handle; -} - -/////////////////////////////////////////////////////////////////////////////// -void CGLMainThread::ResizeWindow(Handle &handle, int w, int h) -{ - dbg("Resizing window to {}x{}", w, h); - // CGLResizeWindow(handle, w, h); - dbg("Resizing window to {}x{}", w, h); - assert(glGetError() == GL_NO_ERROR); - - if (!handle.isInitialized()) - { - dbg("❌ Handle not initialized, cannot resize"); - return; - } - - dbg("glBindRenderbuffer buf_color"); - glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); - - dbg("glRenderbufferStorage"); - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); - - dbg("glBindRenderbuffer buf_depth"); - glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_depth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); - - dbg("glBindFramebuffer buf_frame"); - glBindFramebuffer(GL_FRAMEBUFFER, handle.buf_frame); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, handle.buf_color); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, handle.buf_depth); - - if (glGetError() != GL_NO_ERROR || - glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - { - dbg("❌ Cannot resize the framebuffer!"); - } - - assert(glGetError() == GL_NO_ERROR); - dbg("✅"); -} - -/////////////////////////////////////////////////////////////////////////////// -void CGLMainThread::DeleteWindow(CGLWindow *caller, Handle &handle) -{ - dbg(); - if (!handle.isInitialized()) { return; } - - CtrlCmd cmd; - cmd.type = CtrlCmdType::Delete; - - DeleteWndCmd del_cmd; - cmd.delete_cmd = &del_cmd; - - del_cmd.wnd = caller; - del_cmd.handle = &handle; - - QueueWndCmd(std::move(cmd), true); - dbg("✅"); -} - -/////////////////////////////////////////////////////////////////////////////// -void CGLMainThread::Terminate() -{ - dbg(); - CtrlCmd cmd; - cmd.type = CtrlCmdType::Terminate; - - // queue to the front to get priority - { - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_front(std::move(cmd)); - } - // wake up the main thread to handle our event - events_available.notify_all(); -} - -/////////////////////////////////////////////////////////////////////////////// -void CGLMainThread::MainLoop(bool server) -{ - dbg(); - server_mode = server; - bool terminating = false; - - // set up interrupt handler for graceful closing - signal(SIGINT, InterruptHandler); - - while (true) - { - bool events_pending = false; - { - lock_guard evt_guard{window_cmd_mtx}; - events_pending = !window_cmds.empty(); - } - if (events_pending) - { - do - { - CtrlCmd cmd; - // Fetch next event from the queue - { - lock_guard evt_guard{window_cmd_mtx}; - cmd = std::move(window_cmds.front()); - window_cmds.pop_front(); - events_pending = !window_cmds.empty(); - // Skip non-delete events if terminating - if (terminating && cmd.type != CtrlCmdType::Delete) - { - continue; - } - } - - switch (cmd.type) - { - case CtrlCmdType::Create: - if (!CreateWndImpl(*cmd.create_cmd)) - { - terminating = true; - } - break; - case CtrlCmdType::Resize: - if (!ResizeWndImpl(*cmd.resize_cmd)) - { - terminating = true; - } - break; - case CtrlCmdType::Delete: - if (!DeleteWndImpl(*cmd.delete_cmd)) - { - terminating = true; - } - break; - case CtrlCmdType::Terminate: - // do not wait for windows to open - if (num_windows < 0) { num_windows = 0; } - terminating = true; - break; - } - - // Signal completion of the command, in case worker thread is waiting. - cmd.finished.set_value(); - } - while (events_pending); - } - - if (num_windows == 0) - { - if (!server_mode || terminating) - { - break; - } - } - - if (terminating) - { - for (CGLWindow *wnd : windows) - { - wnd->signalQuit(); - } - } - - { - unique_lock event_lock{window_cmd_mtx}; - events_available.wait(event_lock, [this]() - { - // Sleep until events from windows can be handled - return !window_cmds.empty(); - }); - } - } -} - -#endif // GLVIS_USE_CGL diff --git a/lib/cgl/cgl_main.hpp b/lib/cgl/cgl_main.hpp deleted file mode 100644 index 455c063a..00000000 --- a/lib/cgl/cgl_main.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced -// at the Lawrence Livermore National Laboratory. All Rights reserved. See files -// LICENSE and NOTICE for details. LLNL-CODE-443271. -// -// This file is part of the GLVis visualization tool and library. For more -// information and source code availability see https://glvis.org. -// -// GLVis is free software; you can redistribute it and/or modify it under the -// terms of the BSD-3 license. We welcome feedback and contributions, see file -// CONTRIBUTING.md for details. -#pragma once - -#ifdef GLVIS_USE_CGL - -#include -#include - -#include "cgl.hpp" - -class CGLMainThread : public MainThread -{ - using Handle = CGLWindow::CGLHandle; - bool server_mode {false}; - std::list windows; - int num_windows {-1}; - - struct CreateWndCmd; - struct ResizeWndCmd; - struct DeleteWndCmd; - - enum class CtrlCmdType - { - Create, - Resize, - Delete, - Terminate, - }; - - struct CtrlCmd - { - CtrlCmdType type; - union - { - CreateWndCmd *create_cmd; - ResizeWndCmd *resize_cmd; - DeleteWndCmd *delete_cmd; - }; - std::promise finished; - }; - - std::condition_variable events_available; - std::mutex window_cmd_mtx; - std::deque window_cmds; - - bool CreateWndImpl(CreateWndCmd &cmd); - bool ResizeWndImpl(ResizeWndCmd &cmd); - bool DeleteWndImpl(DeleteWndCmd &cmd); - void QueueWndCmd(CtrlCmd cmd, bool sync); - - static void InterruptHandler(int param); - -public: - - CGLMainThread(); - ~CGLMainThread(); - - static CGLMainThread& Get(); - - Handle CreateWindow(CGLWindow *caller, int w, int h, - bool legacy_gl); - void ResizeWindow(Handle &hnd, int w, int h); - void DeleteWindow(CGLWindow *caller, Handle &hnd); - void Terminate(); - - void MainLoop(bool server = false) override; -}; - -#endif //GLVIS_USE_CGL From 938fbabb2649fd5e01e68fea83456a5524e3b91e Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 12:02:21 -0700 Subject: [PATCH 58/79] Cleanup --- .gitignore | 1 - lib/aux_vis.cpp | 6 ------ lib/egl/egl.cpp | 18 ++++++----------- lib/egl/egl_main.cpp | 48 +++++++------------------------------------- makefile | 1 - 5 files changed, 13 insertions(+), 61 deletions(-) diff --git a/.gitignore b/.gitignore index 80fb7860..5225d0ea 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,3 @@ compile_commands.json .DS_Store user.cmake -nvtx.hpp \ No newline at end of file diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 4ea11ad9..1a792ac4 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -11,7 +11,6 @@ #include #include -// #include #include #include #include @@ -25,9 +24,6 @@ #include "visual.hpp" #include "gl2ps.h" -#define NVTX_COLOR ::nvtx::kCyan -#include "../nvtx.hpp" - #if defined(GLVIS_USE_LIBTIFF) #include "tiffio.h" #elif defined(GLVIS_USE_LIBPNG) @@ -62,7 +58,6 @@ static bool wndUseHiDPI = true; MainThread& GetMainThread(bool headless) { - dbg(); #if defined(GLVIS_USE_EGL) or defined(GLVIS_USE_CGL) if (headless) { @@ -118,7 +113,6 @@ void MyExpose(); GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, bool headless) { - dbg(); #ifdef GLVIS_DEBUG if (!headless) { cout << "OpenGL Visualization" << endl; } else diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 07470e74..5806da0c 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -16,7 +16,6 @@ #include "../aux_vis.hpp" #include #include -// #include #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s @@ -24,9 +23,6 @@ #define PRINT_DEBUG(s) {} #endif -#define NVTX_COLOR ::nvtx::kMagenta -#include "../../nvtx.hpp" // IWYU pragma: keep - using namespace std; EglWindow::EglWindow() @@ -54,7 +50,6 @@ bool EglWindow::createWindow(const char *, int, int, int w, int h, if (!handle.isInitialized()) { - dbg("❌ Cannot create GL window"); return false; } @@ -187,7 +182,6 @@ void EglWindow::getGLDrawSize(int& w, int& h) const #ifdef GLVIS_USE_CGL const auto err = glGetError(); - dbg("glGetError: {:x}", err); std::cout << "glGetError:" << err << std::endl; assert(glGetError() == GL_NO_ERROR); @@ -202,13 +196,15 @@ void EglWindow::getGLDrawSize(int& w, int& h) const // Optional: Ensure context is current if not guaranteed elsewhere CGLError ctx_err = CGLSetCurrentContext(handle.ctx.get()); - if (ctx_err != kCGLNoError) { dbg("❌ Cannot set CGL context as current");} + if (ctx_err != kCGLNoError) + { + PRINT_DEBUG("❌ Cannot set CGL context as current"); + } // Bind the color renderbuffer (assuming that's what we want to query) glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); if (glGetError() != GL_NO_ERROR) { - dbg("❌ Failed to bind renderbuffer"); - // return EXIT_FAILURE; + return; } // Query width and height glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &cgl_w); @@ -217,18 +213,16 @@ void EglWindow::getGLDrawSize(int& w, int& h) const GLenum gl_err = glGetError(); if (gl_err != GL_NO_ERROR) { - dbg("❌ GL error during parameter query: {}", gl_err); + PRINT_DEBUG("❌ GL error during parameter query"); // Unbind to clean up glBindRenderbuffer(GL_RENDERBUFFER, 0); return ; } - dbg("Renderbuffer parameters - width: {}, height: {}", cgl_w, cgl_h); // Unbind to restore state glBindRenderbuffer(GL_RENDERBUFFER, 0); assert(glGetError() == GL_NO_ERROR); } - dbg("CGL draw size: {}x{}", (int) cgl_w, (int)cgl_h); w = cgl_w; h = cgl_h; #endif diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 98ea43da..af6ff79b 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -23,9 +23,6 @@ #define PRINT_DEBUG(s) {} #endif -#define NVTX_COLOR ::nvtx::kYellow -#include "../../nvtx.hpp" // IWYU pragma: keep - using namespace std; struct EglMainThread::CreateWndCmd @@ -113,7 +110,6 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) #endif #ifdef GLVIS_USE_CGL const int multisamples = GetMultisample(); - dbg("multisamples:{}", multisamples); vector pixAttribs = { @@ -128,13 +124,11 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) if (cmd.legacy_gl) { - dbg("Inserting legacy OpenGL profile"); // insert legacy OpenGL compatibility requirement auto it = (pixAttribs.end() -= 2); pixAttribs.insert(it, {kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_Legacy)}); } - dbg("Choosing CGL pixel format"); GLint numConfigs; CGLError err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pix, &numConfigs); @@ -160,7 +154,6 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) return false; } - dbg("Creating CGL context"); CGLContextObj ctx; err = CGLCreateContext(new_handle.pix, nullptr, &ctx); assert(err == kCGLNoError); @@ -246,27 +239,18 @@ bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) if (cmd.handle->isInitialized()) { - dbg("Destroying buffers"); - // CGLDeleteFrameBuffers(cmd.handle); - - dbg(); assert(glGetError() == GL_NO_ERROR); assert(cmd.handle); static_assert(sizeof(cmd.handle->buf_frame) == sizeof(GLuint), "Buffer frame size must be 4 bytes"); - dbg("Deleting frame buffers"); glDeleteFramebuffers(1, &cmd.handle->buf_frame); - dbg("Deleting color buffers"); glDeleteRenderbuffers(1, &cmd.handle->buf_color); - dbg("Deleting depth buffers"); glDeleteRenderbuffers(1, &cmd.handle->buf_depth); - dbg("✅"); assert(glGetError() == GL_NO_ERROR); } if (cmd.handle->pix) { - dbg("Destroying pixel format"); CGLError err = CGLDestroyPixelFormat(cmd.handle->pix); if (err != kCGLNoError) { @@ -456,7 +440,6 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, if (!out_hnd.isInitialized()) { - dbg("❌ Failed to create window"); return out_hnd; } @@ -464,7 +447,6 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, CGLError err = CGLSetCurrentContext(out_hnd.ctx.get()); if (err != kCGLNoError) { - dbg("❌ Failed to set context"); return out_hnd; } @@ -473,17 +455,13 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, const auto status = caller->initGLEW(legacy_gl); assert(status && glGetError() == GL_NO_ERROR); - dbg("GL error: {}", glGetError()); const GLubyte* renderer = glGetString(GL_RENDERER); - dbg("OpenGL renderer: {}", (const char*)renderer); if (strstr((const char*)renderer, "Software") != nullptr) { - dbg("⚠️ Using software renderer; expect limited functionality"); + PRINT_DEBUG("⚠️ Using software renderer; expect limited functionality"); } - const GLubyte* version = glGetString(GL_VERSION); - dbg("OpenGL version: {}", (const char*)version); - dbg("GL error: {}", glGetError()); + // const GLubyte* version = glGetString(GL_VERSION); } glGenFramebuffers(1, &out_hnd.buf_frame); @@ -491,14 +469,9 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, glCheckFramebufferStatus(GL_FRAMEBUFFER); assert(glGetError() == GL_NO_ERROR); - dbg("GL error: {}", glGetError()); - dbg("new_handle.buf_frame:{}", (int)out_hnd.buf_frame); assert(out_hnd.buf_frame != 0); - dbg("Creating buf_color and buf_depth renderbuffers on main thread"); glGenRenderbuffers(1, &out_hnd.buf_color); - dbg("new_handle.buf_color:{}", (int)out_hnd.buf_color); glGenRenderbuffers(1, &out_hnd.buf_depth); - dbg("new_handle.buf_depth:{}", (int)out_hnd.buf_depth); ResizeWindow(out_hnd, w, h); // Perform initial resize here // Release current context @@ -511,9 +484,8 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, err = CGLSetCurrentContext(out_hnd.ctx.get()); if (err != kCGLNoError) { - dbg("❌ Cannot set CGL context as current"); + PRINT_DEBUG("❌ Cannot set CGL context as current"); } - dbg("✅"); #endif return out_hnd; @@ -545,28 +517,22 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) #endif #ifdef GLVIS_USE_CGL - dbg("Resizing window to {}x{}", w, h); - // CGLResizeWindow(handle, w, h); - dbg("Resizing window to {}x{}", w, h); - assert(glGetError() == GL_NO_ERROR); if (!handle.isInitialized()) { - dbg("❌ Handle not initialized, cannot resize"); return; } - dbg("glBindRenderbuffer buf_color"); glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); - dbg("glRenderbufferStorage"); + PRINT_DEBUG("glRenderbufferStorage"); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); - dbg("glBindRenderbuffer buf_depth"); + PRINT_DEBUG("glBindRenderbuffer buf_depth"); glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); - dbg("glBindFramebuffer buf_frame"); + PRINT_DEBUG("glBindFramebuffer buf_frame"); glBindFramebuffer(GL_FRAMEBUFFER, handle.buf_frame); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, handle.buf_color); @@ -576,7 +542,7 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) if (glGetError() != GL_NO_ERROR || glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - dbg("❌ Cannot resize the framebuffer!"); + PRINT_DEBUG("❌ Cannot resize the framebuffer!"); } assert(glGetError() == GL_NO_ERROR); diff --git a/makefile b/makefile index 270bf323..f6ca1a6b 100644 --- a/makefile +++ b/makefile @@ -288,7 +288,6 @@ COMMON_SOURCE_FILES = $(filter-out \ # generated with 'echo lib/egl/*.h* lib/gl/*.h* lib/sdl/*.h* lib/*.h*' HEADER_FILES = \ - lib/cgl/cgl.hpp lib/cgl/cgl_main.hpp \ lib/egl/egl.hpp lib/egl/egl_main.hpp lib/gl/attr_traits.hpp \ lib/gl/platform_gl.hpp lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp \ lib/gl/renderer.hpp lib/gl/shader.hpp lib/gl/types.hpp \ From ace43194f7506c109e2e01d5ebb48bbb010bfa2b Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 12:20:48 -0700 Subject: [PATCH 59/79] Simplify --- lib/egl/egl.cpp | 74 ++++++----------------------- lib/egl/egl.hpp | 7 +-- lib/egl/egl_main.cpp | 108 +++++++++---------------------------------- 3 files changed, 40 insertions(+), 149 deletions(-) diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 5806da0c..6e712998 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -11,11 +11,13 @@ #if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) +#include +#include +#include + #include "egl.hpp" #include "egl_main.hpp" #include "../aux_vis.hpp" -#include -#include #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s @@ -25,36 +27,22 @@ using namespace std; -EglWindow::EglWindow() -{ -} - EglWindow::~EglWindow() { EglMainThread::Get().DeleteWindow(this, handle); } -#ifdef GLVIS_USE_CGL -bool EglWindow::initGLEW(bool legacyGlOnly) -{ - return GLWindow::initGLEW(legacyGlOnly); -} -#endif - - bool EglWindow::createWindow(const char *, int, int, int w, int h, bool legacyGlOnly) { handle = EglMainThread::Get().CreateWindow(this, w, h, legacyGlOnly); - assert(glGetError() == GL_NO_ERROR); - if (!handle.isInitialized()) { return false; } #ifdef GLVIS_USE_CGL - return true; + return true; // CGL already called initGLEW() during CreateWindow() #endif #ifndef __EMSCRIPTEN__ @@ -180,49 +168,17 @@ void EglWindow::getGLDrawSize(int& w, int& h) const h = egl_h; #endif #ifdef GLVIS_USE_CGL - - const auto err = glGetError(); - std::cout << "glGetError:" << err << std::endl; - - assert(glGetError() == GL_NO_ERROR); GLint cgl_w, cgl_h; - static_assert(sizeof(GLint) == sizeof(int), - "CGL width size must be 4 bytes"); - // CGLGetRenderbufferParameter(handle, cgl_w, cgl_h); - { - // dbg("glGetError:{}", glGetError()); - assert(glGetError() == GL_NO_ERROR); - // assert(handle.isInitialized()); - - // Optional: Ensure context is current if not guaranteed elsewhere - CGLError ctx_err = CGLSetCurrentContext(handle.ctx.get()); - if (ctx_err != kCGLNoError) - { - PRINT_DEBUG("❌ Cannot set CGL context as current"); - } - // Bind the color renderbuffer (assuming that's what we want to query) - glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); - if (glGetError() != GL_NO_ERROR) - { - return; - } - // Query width and height - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &cgl_w); - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &cgl_h); - // Check for query errors - GLenum gl_err = glGetError(); - if (gl_err != GL_NO_ERROR) - { - PRINT_DEBUG("❌ GL error during parameter query"); - // Unbind to clean up - glBindRenderbuffer(GL_RENDERBUFFER, 0); - return ; - } - // Unbind to restore state - glBindRenderbuffer(GL_RENDERBUFFER, 0); - assert(glGetError() == GL_NO_ERROR); - } - + // Bind the color renderbuffer (assuming that's what we want to query) + glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); + assert(glGetError() == GL_NO_ERROR); + // Query width and height + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &cgl_w); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &cgl_h); + assert(glGetError() == GL_NO_ERROR); + // Unbind to restore state + glBindRenderbuffer(GL_RENDERBUFFER, 0); + assert(glGetError() == GL_NO_ERROR); w = cgl_w; h = cgl_h; #endif diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index 9cf86981..4564a29d 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -101,13 +101,9 @@ class EglWindow : public GLWindow void queueEvents(std::vector events); public: - EglWindow(); + EglWindow() = default; ~EglWindow(); -#if defined(GLVIS_USE_CGL) - bool initGLEW(bool legacyGlOnly); -#endif - /** @brief Creates a new OpenGL window. Returns false if EGL or OpenGL initialization fails. */ bool createWindow(const char *title, int x, int y, int w, int h, @@ -119,6 +115,7 @@ class EglWindow : public GLWindow void signalLoop() override; + bool initGLEW(bool legacyGlOnly) { return GLWindow::initGLEW(legacyGlOnly); } void getWindowSize(int& w, int& h) const override { getGLDrawSize(w, h); } void getGLDrawSize(int& w, int& h) const override; diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index af6ff79b..15e3fcaa 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -11,8 +11,9 @@ #if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) -#include +#include #include +#include #include "egl_main.hpp" #include "../aux_vis.hpp" @@ -47,13 +48,11 @@ struct EglMainThread::DeleteWndCmd bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) { + const int multisamples = GetMultisample(); Handle new_handle; #ifdef GLVIS_USE_EGL // 1. Select an appropriate configuration - - const int multisamples = GetMultisample(); - EGLint configAttribs[] = { EGL_SAMPLE_BUFFERS, (multisamples > 0)?(1):(0), // must be first @@ -109,9 +108,7 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) } #endif #ifdef GLVIS_USE_CGL - const int multisamples = GetMultisample(); - - vector pixAttribs = + vector pixAttrs = { kCGLPFASampleBuffers, CGLPixelFormatAttribute((multisamples > 0)?(1):(0)), // must be first kCGLPFASamples, CGLPixelFormatAttribute(multisamples), @@ -125,27 +122,27 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) if (cmd.legacy_gl) { // insert legacy OpenGL compatibility requirement - auto it = (pixAttribs.end() -= 2); - pixAttribs.insert(it, {kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_Legacy)}); + auto it = (pixAttrs.end() -= 2); + pixAttrs.insert(it, {kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_Legacy)}); } GLint numConfigs; - CGLError err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pix, + CGLError err = CGLChoosePixelFormat(pixAttrs.data(), &new_handle.pix, &numConfigs); if (multisamples > 0 && (err != kCGLNoError || numConfigs < 1)) { std::cerr << "CGL with multisampling is not supported, turning it off" << std::endl; - pixAttribs[1] = CGLPixelFormatAttribute(0); - err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pix, &numConfigs); + pixAttrs[1] = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttrs.data(), &new_handle.pix, &numConfigs); } if (err != kCGLNoError || numConfigs < 1) { std::cerr << "CGL with hardware acceleration not supported, turning it off" << std::endl; - pixAttribs.pop_back(); - pixAttribs.back() = CGLPixelFormatAttribute(0); - err = CGLChoosePixelFormat(pixAttribs.data(), &new_handle.pix, &numConfigs); + pixAttrs.pop_back(); + pixAttrs.back() = CGLPixelFormatAttribute(0); + err = CGLChoosePixelFormat(pixAttrs.data(), &new_handle.pix, &numConfigs); } if (err != kCGLNoError || numConfigs < 1) { @@ -156,16 +153,13 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) CGLContextObj ctx; err = CGLCreateContext(new_handle.pix, nullptr, &ctx); - assert(err == kCGLNoError); if (err != kCGLNoError) { std::cerr << "Cannot create an OpenGL context, error: " << CGLErrorString(err) << std::endl; return false; } - new_handle.ctx.reset(ctx); - assert(new_handle.ctx != nullptr); #endif windows.push_back(cmd.wnd); @@ -236,19 +230,12 @@ bool EglMainThread::DeleteWndImpl(DeleteWndCmd &cmd) } #endif #ifdef GLVIS_USE_CGL - if (cmd.handle->isInitialized()) { - assert(glGetError() == GL_NO_ERROR); - assert(cmd.handle); - static_assert(sizeof(cmd.handle->buf_frame) == sizeof(GLuint), - "Buffer frame size must be 4 bytes"); glDeleteFramebuffers(1, &cmd.handle->buf_frame); glDeleteRenderbuffers(1, &cmd.handle->buf_color); glDeleteRenderbuffers(1, &cmd.handle->buf_depth); - assert(glGetError() == GL_NO_ERROR); } - if (cmd.handle->pix) { CGLError err = CGLDestroyPixelFormat(cmd.handle->pix); @@ -437,57 +424,23 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, } #endif //GLVIS_USE_EGL #ifdef GLVIS_USE_CGL - - if (!out_hnd.isInitialized()) - { - return out_hnd; - } - - // dbg("Setting CGL context"); + if (!out_hnd.isInitialized()) { return out_hnd; } CGLError err = CGLSetCurrentContext(out_hnd.ctx.get()); - if (err != kCGLNoError) + if (err != kCGLNoError) { return out_hnd; } + // initialize GLEW before using any OpenGL functions + const auto status = caller->initGLEW(legacy_gl); + assert(status && glGetError() == GL_NO_ERROR); + const auto *renderer = glGetString(GL_RENDERER); + if (strstr((const char*)renderer, "Software") != nullptr) { - return out_hnd; - } - - { - // need to initialize GLEW before using any OpenGL functions - const auto status = caller->initGLEW(legacy_gl); - assert(status && glGetError() == GL_NO_ERROR); - - const GLubyte* renderer = glGetString(GL_RENDERER); - if (strstr((const char*)renderer, "Software") != nullptr) - { - PRINT_DEBUG("⚠️ Using software renderer; expect limited functionality"); - } - - // const GLubyte* version = glGetString(GL_VERSION); + PRINT_DEBUG("Using software renderer; expect limited functionality"); } - glGenFramebuffers(1, &out_hnd.buf_frame); - assert(glGetError() == GL_NO_ERROR); glCheckFramebufferStatus(GL_FRAMEBUFFER); - assert(glGetError() == GL_NO_ERROR); - - assert(out_hnd.buf_frame != 0); glGenRenderbuffers(1, &out_hnd.buf_color); glGenRenderbuffers(1, &out_hnd.buf_depth); - ResizeWindow(out_hnd, w, h); // Perform initial resize here - - // Release current context - CGLSetCurrentContext(nullptr); - assert(glGetError() == GL_NO_ERROR); - - // dbg("Making context current on this thread"); - // CGLInitializeFrameBuffers(handle, cmd.create_cmd->w, cmd.create_cmd->h); - - err = CGLSetCurrentContext(out_hnd.ctx.get()); - if (err != kCGLNoError) - { - PRINT_DEBUG("❌ Cannot set CGL context as current"); - } + ResizeWindow(out_hnd, w, h); #endif - return out_hnd; } @@ -516,36 +469,21 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) } #endif #ifdef GLVIS_USE_CGL - - - if (!handle.isInitialized()) - { - return; - } - + if (!handle.isInitialized()) { return; } glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); - - PRINT_DEBUG("glRenderbufferStorage"); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); - - PRINT_DEBUG("glBindRenderbuffer buf_depth"); glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); - - PRINT_DEBUG("glBindFramebuffer buf_frame"); glBindFramebuffer(GL_FRAMEBUFFER, handle.buf_frame); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, handle.buf_color); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, handle.buf_depth); - if (glGetError() != GL_NO_ERROR || glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - PRINT_DEBUG("❌ Cannot resize the framebuffer!"); + PRINT_DEBUG("Cannot resize the framebuffer!"); } - - assert(glGetError() == GL_NO_ERROR); #endif } From 4701cedb896355fedbd2248419b5f42c1bb514de Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 12:26:21 -0700 Subject: [PATCH 60/79] Cleanup --- lib/egl/egl.cpp | 4 ++-- lib/egl/egl_main.cpp | 2 +- lib/egl/egl_main.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/egl/egl.cpp b/lib/egl/egl.cpp index 6e712998..79e15fd7 100644 --- a/lib/egl/egl.cpp +++ b/lib/egl/egl.cpp @@ -169,7 +169,7 @@ void EglWindow::getGLDrawSize(int& w, int& h) const #endif #ifdef GLVIS_USE_CGL GLint cgl_w, cgl_h; - // Bind the color renderbuffer (assuming that's what we want to query) + // Bind the color renderbuffer glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); assert(glGetError() == GL_NO_ERROR); // Query width and height @@ -216,4 +216,4 @@ void EglWindow::screenshot(string filename, bool convert) signalExpose(); } -#endif // GLVIS_USE_EGL +#endif // GLVIS_USE_EGL || GLVIS_USE_CGL diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 15e3fcaa..71d3a906 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -160,7 +160,7 @@ bool EglMainThread::CreateWndImpl(CreateWndCmd &cmd) return false; } new_handle.ctx.reset(ctx); -#endif +#endif // GLVIS_USE_CGL windows.push_back(cmd.wnd); if (num_windows < 0) diff --git a/lib/egl/egl_main.hpp b/lib/egl/egl_main.hpp index 6643c603..ef33a52f 100644 --- a/lib/egl/egl_main.hpp +++ b/lib/egl/egl_main.hpp @@ -84,5 +84,5 @@ class EglMainThread : public MainThread void MainLoop(bool server = false) override; }; -#endif // GLVIS_USE_EGL +#endif // GLVIS_USE_EGL || GLVIS_USE_CGL #endif // GLVIS_EGL_MAIN_HPP From c34e758172d00c1818801f1cc53948d5954aa5e3 Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 12:29:31 -0700 Subject: [PATCH 61/79] Revert cerr use --- lib/egl/egl_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 71d3a906..1ca9aba6 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -482,7 +482,7 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) if (glGetError() != GL_NO_ERROR || glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - PRINT_DEBUG("Cannot resize the framebuffer!"); + std::cerr << "Cannot resize the framebuffer!" << std::endl; } #endif } From 844e336243751540b6beb278b086377d0c3a1f7d Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 12:34:16 -0700 Subject: [PATCH 62/79] Cleanup and remove cmake user settings --- .gitignore | 2 -- CMakeLists.txt | 10 ---------- lib/egl/egl.hpp | 2 +- lib/egl/egl_main.cpp | 1 - 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 5225d0ea..ff588ebb 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,3 @@ compile_commands.json # OS-specific: Mac *.dSYM .DS_Store - -user.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 63ee8f9c..c9d11209 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,16 +24,6 @@ endif ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") project(glvis NONE) -# Load user settings -set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/user.cmake" CACHE PATH - "User configuration file. This file is not part of the GLVis source code and - is not distributed with GLVis. It can be used to set user-specific options, - such as paths to external libraries, compiler flags, etc.") -include("${USER_CONFIG}" OPTIONAL RESULT_VARIABLE USER_CONFIG_LOADED) -if (USER_CONFIG_LOADED) - message(STATUS "USER_CONFIG = ${USER_CONFIG} (LOADED)") -endif() - # Import MFEM. The following variables can be used to help CMake find MFEM: # * MFEM_DIR - absolute path to the MFEM build or install prefix. # * mfem_DIR - absolute path to where MFEMConfig.cmake is. diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index 4564a29d..d02b3f70 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -146,4 +146,4 @@ class EglWindow : public GLWindow }; #endif // GLVIS_USE_EGL || GLVIS_USE_CGL -#endif // GLVIS_EGL_HPP \ No newline at end of file +#endif // GLVIS_EGL_HPP diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 1ca9aba6..b7612771 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -469,7 +469,6 @@ void EglMainThread::ResizeWindow(Handle &handle, int w, int h) } #endif #ifdef GLVIS_USE_CGL - if (!handle.isInitialized()) { return; } glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_color); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); glBindRenderbuffer(GL_RENDERBUFFER, handle.buf_depth); From fef7e0677a460444602feb5db151acb43273b58c Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 12:51:27 -0700 Subject: [PATCH 63/79] Add GL_SILENCE_DEPRECATION and update INSTALL --- INSTALL | 2 ++ lib/egl/egl.hpp | 1 + 2 files changed, 3 insertions(+) diff --git a/INSTALL b/INSTALL index e6a9c4a9..cadb4531 100644 --- a/INSTALL +++ b/INSTALL @@ -117,6 +117,8 @@ Some important variables for CMake are: - GLVIS_USE_EGL: Use EGL for headless rendering. Default is "OFF". +- GLVIS_USE_CGL: Use CGL for headless rendering on macos. Default is "OFF". + - GLVIS_MULTISAMPLE and GLVIS_MS_LINEWIDTH: See building considerations below for more information on these variables. diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index d02b3f70..c6e7659f 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -21,6 +21,7 @@ #endif #ifdef GLVIS_USE_CGL +#define GL_SILENCE_DEPRECATION #include #endif From 7421750ce4e6ab94827260ec4d21d7dca18a4313 Mon Sep 17 00:00:00 2001 From: camierjs Date: Sun, 5 Oct 2025 12:57:23 -0700 Subject: [PATCH 64/79] Avoid unused variable 'status' --- lib/egl/egl_main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index b7612771..28feae90 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -429,7 +429,8 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, if (err != kCGLNoError) { return out_hnd; } // initialize GLEW before using any OpenGL functions const auto status = caller->initGLEW(legacy_gl); - assert(status && glGetError() == GL_NO_ERROR); + MFEM_VERIFY(status, "GLEW initialization failed!"); + assert(glGetError() == GL_NO_ERROR); const auto *renderer = glGetString(GL_RENDERER); if (strstr((const char*)renderer, "Software") != nullptr) { From e873d0c5c2524b4b1fb6bdc0669970a323cbfd5a Mon Sep 17 00:00:00 2001 From: camierjs Date: Mon, 6 Oct 2025 14:28:15 -0700 Subject: [PATCH 65/79] Address reviewers comments --- lib/egl/egl.hpp | 7 +++---- lib/egl/egl_main.cpp | 11 +---------- lib/glwindow.hpp | 1 + 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/lib/egl/egl.hpp b/lib/egl/egl.hpp index c6e7659f..9e8763e7 100644 --- a/lib/egl/egl.hpp +++ b/lib/egl/egl.hpp @@ -21,7 +21,7 @@ #endif #ifdef GLVIS_USE_CGL -#define GL_SILENCE_DEPRECATION +#define GL_SILENCE_DEPRECATION // CGL has been deprecated since MacOS 10.14 #include #endif @@ -41,8 +41,8 @@ class EglWindow : public GLWindow EGLConfig eglCfg{}; #endif #ifdef GLVIS_USE_CGL - GLuint buf_frame, buf_color, buf_depth; - CGLPixelFormatObj pix; + GLuint buf_frame {}, buf_color {}, buf_depth {}; + CGLPixelFormatObj pix {}; using CGLContextDeleter = void(*)(CGLContextObj); std::unique_ptr<_CGLContextObject, CGLContextDeleter> ctx @@ -116,7 +116,6 @@ class EglWindow : public GLWindow void signalLoop() override; - bool initGLEW(bool legacyGlOnly) { return GLWindow::initGLEW(legacyGlOnly); } void getWindowSize(int& w, int& h) const override { getGLDrawSize(w, h); } void getGLDrawSize(int& w, int& h) const override; diff --git a/lib/egl/egl_main.cpp b/lib/egl/egl_main.cpp index 28feae90..073842d6 100644 --- a/lib/egl/egl_main.cpp +++ b/lib/egl/egl_main.cpp @@ -11,7 +11,6 @@ #if defined(GLVIS_USE_EGL) || defined(GLVIS_USE_CGL) -#include #include #include @@ -428,16 +427,8 @@ EglMainThread::Handle EglMainThread::CreateWindow(EglWindow *caller, int w, CGLError err = CGLSetCurrentContext(out_hnd.ctx.get()); if (err != kCGLNoError) { return out_hnd; } // initialize GLEW before using any OpenGL functions - const auto status = caller->initGLEW(legacy_gl); - MFEM_VERIFY(status, "GLEW initialization failed!"); - assert(glGetError() == GL_NO_ERROR); - const auto *renderer = glGetString(GL_RENDERER); - if (strstr((const char*)renderer, "Software") != nullptr) - { - PRINT_DEBUG("Using software renderer; expect limited functionality"); - } + caller->initGLEW(legacy_gl); glGenFramebuffers(1, &out_hnd.buf_frame); - glCheckFramebufferStatus(GL_FRAMEBUFFER); glGenRenderbuffers(1, &out_hnd.buf_color); glGenRenderbuffers(1, &out_hnd.buf_depth); ResizeWindow(out_hnd, w, h); diff --git a/lib/glwindow.hpp b/lib/glwindow.hpp index caa811b8..a4843e94 100644 --- a/lib/glwindow.hpp +++ b/lib/glwindow.hpp @@ -31,6 +31,7 @@ class MainThread class GLWindow { + friend class EglMainThread; public: typedef Uint8 SDL_Mousebutton; struct MouseEventInfo From 4977af20b4d648b39a6a7445483cdf6d0a8c7995 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 6 Oct 2025 17:41:18 -0700 Subject: [PATCH 66/79] Added error check to initGLEW(). --- lib/glwindow.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/glwindow.cpp b/lib/glwindow.cpp index 51e4dcbf..3ed2bf61 100644 --- a/lib/glwindow.cpp +++ b/lib/glwindow.cpp @@ -97,6 +97,12 @@ bool GLWindow::initGLEW(bool legacyGlOnly) renderer->setDevice(); #endif + if (glGetError() != GL_NO_ERROR) + { + std::cerr << "Renderer init OpenGL error: " << glGetError() << std::endl; + return false; + } + return true; } From 0bf5c63a1dc426333814ba1a838b65611c3bdded Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Mon, 6 Oct 2025 17:52:23 -0700 Subject: [PATCH 67/79] Fixed error reporting. --- lib/glwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/glwindow.cpp b/lib/glwindow.cpp index 3ed2bf61..1a6f00db 100644 --- a/lib/glwindow.cpp +++ b/lib/glwindow.cpp @@ -97,9 +97,9 @@ bool GLWindow::initGLEW(bool legacyGlOnly) renderer->setDevice(); #endif - if (glGetError() != GL_NO_ERROR) + if ((err = glGetError()) != GL_NO_ERROR) { - std::cerr << "Renderer init OpenGL error: " << glGetError() << std::endl; + std::cerr << "Renderer init OpenGL error: " << err << std::endl; return false; } From 27263da90422edc8726af430e9656741b2120bed Mon Sep 17 00:00:00 2001 From: camierjs Date: Tue, 7 Oct 2025 09:49:50 -0700 Subject: [PATCH 68/79] if GLEW_KHR_debug, enable GL_DEBUG_OUTPUT --- lib/sdl/sdl_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sdl/sdl_main.cpp b/lib/sdl/sdl_main.cpp index 91dac48c..922d3e57 100644 --- a/lib/sdl/sdl_main.cpp +++ b/lib/sdl/sdl_main.cpp @@ -645,7 +645,7 @@ void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd) #ifndef __EMSCRIPTEN__ SDL_GL_SetSwapInterval(0); - glEnable(GL_DEBUG_OUTPUT); + if (GLEW_KHR_debug) { glEnable(GL_DEBUG_OUTPUT); } #endif // Register window internally in the main thread so it can receive events From 8bbc29c755d84e66f562adc0d432eb2174b37c38 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 7 Oct 2025 10:28:30 -0700 Subject: [PATCH 69/79] Made the GLEW_KHR_debug condition Mac specific. --- lib/sdl/sdl_main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/sdl/sdl_main.cpp b/lib/sdl/sdl_main.cpp index 922d3e57..e785dd89 100644 --- a/lib/sdl/sdl_main.cpp +++ b/lib/sdl/sdl_main.cpp @@ -645,7 +645,11 @@ void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd) #ifndef __EMSCRIPTEN__ SDL_GL_SetSwapInterval(0); +#ifdef SDL_VIDEO_DRIVER_COCOA if (GLEW_KHR_debug) { glEnable(GL_DEBUG_OUTPUT); } +#else + glEnable(GL_DEBUG_OUTPUT); +#endif #endif // Register window internally in the main thread so it can receive events From a497eb0a633a6116d5688c900a80f14086680247 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 7 Oct 2025 12:53:37 -0700 Subject: [PATCH 70/79] Made CGL default on Mac. --- CMakeLists.txt | 2 +- makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c9d11209..d5ee8638 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,7 @@ option(GLVIS_USE_EGL option(GLVIS_USE_CGL "Use CGL for headless rendering" - OFF) + APPLE) # # Handle a few other definitions diff --git a/makefile b/makefile index f6ca1a6b..dd415d54 100644 --- a/makefile +++ b/makefile @@ -251,7 +251,7 @@ ifeq ($(GLVIS_USE_EGL),YES) endif # CGL headless rendering -GLVIS_USE_CGL ?= NO +GLVIS_USE_CGL ?= $(if $(NOTMAC),NO,YES) CGL_OPTS = -DGLVIS_USE_CGL ifeq ($(GLVIS_USE_CGL),YES) GLVIS_FLAGS += $(CGL_OPTS) From 5b54075ce25153e4a225df066e9670ff627c845f Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Tue, 7 Oct 2025 14:03:31 -0700 Subject: [PATCH 71/79] Updated INSTALL. --- INSTALL | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index cadb4531..10b6198c 100644 --- a/INSTALL +++ b/INSTALL @@ -117,7 +117,8 @@ Some important variables for CMake are: - GLVIS_USE_EGL: Use EGL for headless rendering. Default is "OFF". -- GLVIS_USE_CGL: Use CGL for headless rendering on macos. Default is "OFF". +- GLVIS_USE_CGL: Use CGL for headless rendering on Mac OS X. Default is "ON" on + this platform, "OFF" otherwise. - GLVIS_MULTISAMPLE and GLVIS_MS_LINEWIDTH: See building considerations below for more information on these variables. From deb90e24cdd188c31a953da324cd14fa9b7541d2 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 15 Jan 2026 14:55:52 -0800 Subject: [PATCH 72/79] Updated changelog for CGL. --- CHANGELOG | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fd116769..03a0f3cb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,11 +14,11 @@ Version 4.4.1 (development) Unlike previous GLVis releases, this version requires a C++17 compiler. -- Added headless (no GUI) visualization, relying on the EGL standard. It is - available through '-hl' command-line option or 'headless' script command - (before the visualization commands). End of the loaded stream or script file - end the visualization in this mode. Note GLVis must be compiled with with EGL - (see INSTALL). +- Added headless (no GUI) visualization, relying on the EGL (Linux) or CGL + (MacOS) interface. It is available through '-hl' command-line option or + 'headless' script command (before the visualization commands). End of the + loaded stream or script file end the visualization in this mode. Note GLVis + must be compiled with with EGL/CGL (see INSTALL). - Added non-persistent mode of the server, when the server terminates after all visualization windows are closed. From 7370253955f246f3780f409a7473e5d461621e81 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Fri, 16 Jan 2026 11:04:12 -0800 Subject: [PATCH 73/79] Fixed grammer in Changelog. --- CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 03a0f3cb..64bcec06 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,8 +16,8 @@ Unlike previous GLVis releases, this version requires a C++17 compiler. - Added headless (no GUI) visualization, relying on the EGL (Linux) or CGL (MacOS) interface. It is available through '-hl' command-line option or - 'headless' script command (before the visualization commands). End of the - loaded stream or script file end the visualization in this mode. Note GLVis + 'headless' script command (before the visualization commands). The end of the + loaded stream or script file ends the visualization in this mode. Note GLVis must be compiled with with EGL/CGL (see INSTALL). - Added non-persistent mode of the server, when the server terminates after all From c3c526811c5ce78e460b5b0484f79264fa873710 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 29 Jan 2026 17:10:52 -0800 Subject: [PATCH 74/79] Make style --- lib/palettes.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/palettes.cpp b/lib/palettes.cpp index bd8110d8..a6d33830 100644 --- a/lib/palettes.cpp +++ b/lib/palettes.cpp @@ -168,7 +168,8 @@ void PaletteState::SetIndex(int num) if ((num >= 0) && (num < Palettes->NumPalettes())) { curr_palette = num; - cout << "Palette: " << num+1 << ") " << Palettes->Get(curr_palette)->name << endl; + cout << "Palette: " << num+1 << ") " << Palettes->Get(curr_palette)->name + << endl; } else { @@ -182,7 +183,8 @@ void PaletteState::SetByName(const std::string& palette_name) if ((num >= 0) && (num < Palettes->NumPalettes())) { curr_palette = num; - cout << "Palette: " << num+1 << ") " << Palettes->Get(curr_palette)->name << endl; + cout << "Palette: " << num+1 << ") " << Palettes->Get(curr_palette)->name + << endl; } else { @@ -196,7 +198,8 @@ bool PaletteState::UseDefaultIndex() if ((num >= 0) && (num < Palettes->NumPalettes())) { curr_palette = num; - cout << "Palette: " << num+1 << ") " << Palettes->Get(curr_palette)->name << endl; + cout << "Palette: " << num+1 << ") " << Palettes->Get(curr_palette)->name + << endl; return true; } return false; From 053e08a615a28087fac54f4538d0768eff32ffc8 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Thu, 29 Jan 2026 16:58:11 -0800 Subject: [PATCH 75/79] Fixed memory management. --- lib/aux_vis.cpp | 5 ----- lib/window.cpp | 5 +++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 204ad80d..9be53342 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -263,11 +263,6 @@ GLWindow* InitVisualization(const char name[], int x, int y, int w, int h, #ifndef __EMSCRIPTEN__ wnd->setOnKeyDown(SDLK_LEFTPAREN, ShrinkWindow); wnd->setOnKeyDown(SDLK_RIGHTPAREN, EnlargeWindow); - - if (locscene) - { - delete locscene; - } #endif locscene = nullptr; diff --git a/lib/window.cpp b/lib/window.cpp index 1f3f01fb..89985aaa 100644 --- a/lib/window.cpp +++ b/lib/window.cpp @@ -49,8 +49,9 @@ bool Window::GLVisInitVis(StreamCollection input_streams) const char *win_title = (window_title == nullptr) ? window_titles[(int)field_type] : window_title; - internal.wnd.reset(InitVisualization(win_title, window_x, window_y, window_w, - window_h, headless)); + GLWindow *new_wnd = InitVisualization(win_title, window_x, window_y, window_w, + window_h, headless); + if (new_wnd != wnd.get()) { internal.wnd.reset(new_wnd); } if (!wnd) { std::cerr << "Initializing the visualization failed." << std::endl; From 070644a5d8eda3f7134efb4301fa02cec4aae04a Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Fri, 30 Jan 2026 14:12:08 -0800 Subject: [PATCH 76/79] Fixed nullptr derefencing in display(). --- lib/aux_js.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 210a3546..5801ce41 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -45,7 +45,10 @@ using namespace mfem; void display(std::stringstream & commands, const int w, const int h) { // reset antialiasing - win.wnd->getRenderer().setAntialiasing(0); + if (win.wnd) + { + win.wnd->getRenderer().setAntialiasing(0); + } std::string word; double minv = 0.0, maxv = 0.0; From 08896c34a58221e588576b42d346876570838ac3 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Fri, 30 Jan 2026 17:16:12 -0800 Subject: [PATCH 77/79] Fix of initial draw in display(). --- lib/aux_js.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 5801ce41..b3a36383 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -76,7 +76,7 @@ void display(std::stringstream & commands, const int w, const int h) win.window_w = w; win.window_h = h; - win.GLVisInitVis({}); + if (!win.GLVisInitVis({})) { return; } CallKeySequence(win.data_state.keys.c_str()); From 5bf67e30a9c3b70398e162a84b16e1d8383be90e Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Fri, 30 Jan 2026 18:05:36 -0800 Subject: [PATCH 78/79] Removed duplicate processing of keys. --- lib/aux_js.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index b3a36383..56d394b7 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -78,8 +78,6 @@ void display(std::stringstream & commands, const int w, const int h) if (!win.GLVisInitVis({})) { return; } - CallKeySequence(win.data_state.keys.c_str()); - if (minv || maxv) { win.vs->SetValueRange(minv, maxv); From 7dab82b4ebd977e648385fcb5756aa2f36645739 Mon Sep 17 00:00:00 2001 From: Jan Nikl Date: Fri, 30 Jan 2026 18:33:26 -0800 Subject: [PATCH 79/79] Replaced sending keys by calling keys in display(). --- lib/aux_js.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 56d394b7..3fcfbd4b 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -50,14 +50,14 @@ void display(std::stringstream & commands, const int w, const int h) win.wnd->getRenderer().setAntialiasing(0); } - std::string word; + std::string word, keys; double minv = 0.0, maxv = 0.0; while (commands >> word) { if (word == "keys") { std::cout << "parsing 'keys'" << std::endl; - commands >> win.data_state.keys; + commands >> keys; } else if (word == "valuerange") { @@ -78,6 +78,8 @@ void display(std::stringstream & commands, const int w, const int h) if (!win.GLVisInitVis({})) { return; } + CallKeySequence(keys.c_str()); + if (minv || maxv) { win.vs->SetValueRange(minv, maxv);