From 4254f1856ac85773c95f179281350f6939a2d6ee Mon Sep 17 00:00:00 2001 From: Max Yang Date: Fri, 28 May 2021 13:34:14 -0700 Subject: [PATCH 01/47] Make a bunch of variables thread_local --- glvis.cpp | 13 +++++++------ lib/aux_vis.cpp | 25 +++++++++++++------------ lib/sdl.cpp | 3 ++- lib/threads.cpp | 6 +++--- lib/threads.hpp | 8 ++++---- lib/vsdata.cpp | 4 ++-- lib/vssolution.cpp | 6 +++--- lib/vssolution3d.cpp | 4 ++-- lib/vsvector.cpp | 12 ++++++------ lib/vsvector3d.cpp | 6 +++--- 10 files changed, 45 insertions(+), 42 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 90f6f542..7a7aeb17 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -55,11 +55,12 @@ string extra_caption; // Global variables int input = 1; -StreamState stream_state; -VisualizationSceneScalarData *vs = NULL; -communication_thread *comm_thread = NULL; +thread_local StreamState stream_state; +thread_local VisualizationSceneScalarData *vs = NULL; +extern thread_local GLVisCommand* glvis_command; +thread_local communication_thread *comm_thread = NULL; -GeometryRefiner GLVisGeometryRefiner; +thread_local GeometryRefiner GLVisGeometryRefiner; const char *window_titles[] = { "GLVis [scalar data]", "GLVis [vector data]", "GLVis [mesh]" @@ -70,7 +71,7 @@ int scr_level = 0; Vector *init_nodes = NULL; double scr_min_val, scr_max_val; -Array input_streams; +thread_local Array input_streams; extern char **environ; @@ -115,7 +116,7 @@ bool GLVisInitVis(int field_type) { GetAppWindow()->setOnKeyDown(SDLK_SPACE, ThreadsPauseFunc); glvis_command = new GLVisCommand(&vs, stream_state, &keep_attr); - comm_thread = new communication_thread(input_streams); + comm_thread = new communication_thread(input_streams, glvis_command); } double mesh_range = -1.0; diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 1ec14c18..612453ba 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -33,8 +33,9 @@ using namespace mfem; #include #endif -int visualize = 0; -VisualizationScene * locscene; +thread_local int visualize = 0; +thread_local VisualizationScene * locscene; +thread_local GLVisCommand *glvis_command = NULL; #ifdef GLVIS_MULTISAMPLE static int glvis_multisample = GLVIS_MULTISAMPLE; @@ -45,7 +46,7 @@ static int glvis_multisample = -1; float line_w = 1.f; float line_w_aa = gl3::LINE_WIDTH_AA; -SdlWindow * wnd = nullptr; +thread_local SdlWindow * wnd = nullptr; bool wndLegacyGl = false; SdlWindow * GetAppWindow() @@ -236,7 +237,7 @@ void SendKeySequence(const char *seq) } -static bool disableSendExposeEvent = false; +thread_local bool disableSendExposeEvent = false; void CallKeySequence(const char *seq) { @@ -398,8 +399,8 @@ void MyExpose() } -Array IdleFuncs; -int LastIdleFunc; +thread_local Array IdleFuncs; +thread_local int LastIdleFunc; void InitIdleFuncs() { @@ -433,12 +434,12 @@ void RemoveIdleFunc(void (*Func)(void)) } -double xang = 0., yang = 0.; -gl3::GlMatrix srot; -double sph_t, sph_u; -static GLint oldx, oldy, startx, starty; +thread_local double xang = 0., yang = 0.; +thread_local gl3::GlMatrix srot; +thread_local double sph_t, sph_u; +thread_local GLint oldx, oldy, startx, starty; -int constrained_spinning = 0; +thread_local int constrained_spinning = 0; void MainLoop() @@ -1548,7 +1549,7 @@ vector fc_font_patterns = constexpr int default_font_size = 12; int font_size = default_font_size; -GlVisFont glvis_font; +thread_local GlVisFont glvis_font; std::string priority_font; void InitFont() diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 63b31533..f0f97694 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -46,7 +46,8 @@ using std::endl; #endif extern int GetMultisample(); -extern int visualize; +extern thread_local int visualize; +extern thread_local GLVisCommand* glvis_command; struct SdlWindow::Handle { diff --git a/lib/threads.cpp b/lib/threads.cpp index fc3d809b..67acb194 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -16,7 +16,6 @@ using namespace std; extern const char *strings_off_on[]; // defined in vsdata.cpp -GLVisCommand *glvis_command = NULL; GLVisCommand::GLVisCommand( VisualizationSceneScalarData **_vs, StreamState& state, bool *_keep_attr) @@ -713,8 +712,9 @@ GLVisCommand::~GLVisCommand() } } -communication_thread::communication_thread(Array &_is) - : is(_is) +communication_thread::communication_thread(const Array &_is, + GLVisCommand* cmd) + : is(_is), glvis_command(cmd) { new_m = NULL; new_g = NULL; diff --git a/lib/threads.hpp b/lib/threads.hpp index e270df52..a458e1ec 100644 --- a/lib/threads.hpp +++ b/lib/threads.hpp @@ -136,13 +136,13 @@ class GLVisCommand ~GLVisCommand(); }; -extern GLVisCommand *glvis_command; - class communication_thread { private: // streams to read data from - Array &is; + Array is; + + GLVisCommand* glvis_command; // data that may be dynamically allocated by the thread std::unique_ptr new_m; @@ -157,7 +157,7 @@ class communication_thread void execute(); public: - communication_thread(Array &_is); + communication_thread(const Array &_is, GLVisCommand* cmd); ~communication_thread(); }; diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp index aa5c5fc9..9b318b4c 100644 --- a/lib/vsdata.cpp +++ b/lib/vsdata.cpp @@ -463,8 +463,8 @@ void VisualizationSceneScalarData::PrepareCaption() GetFont()->getObjectSize(caption, caption_w, caption_h); } -VisualizationSceneScalarData * vsdata; -extern VisualizationScene * locscene; +thread_local VisualizationSceneScalarData * vsdata; +extern thread_local VisualizationScene * locscene; void KeycPressed() { diff --git a/lib/vssolution.cpp b/lib/vssolution.cpp index cff12196..3522fcb8 100644 --- a/lib/vssolution.cpp +++ b/lib/vssolution.cpp @@ -24,9 +24,9 @@ using namespace mfem; using namespace std; -VisualizationSceneSolution *vssol; -extern VisualizationScene *locscene; -extern GeometryRefiner GLVisGeometryRefiner; +thread_local VisualizationSceneSolution *vssol; +extern thread_local VisualizationScene *locscene; +extern thread_local GeometryRefiner GLVisGeometryRefiner; #ifdef GLVIS_ISFINITE /* This test for INFs or NaNs is the same as the one used in hypre's PCG and diff --git a/lib/vssolution3d.cpp b/lib/vssolution3d.cpp index b3369a06..27551664 100644 --- a/lib/vssolution3d.cpp +++ b/lib/vssolution3d.cpp @@ -21,8 +21,8 @@ using namespace mfem; using namespace std; -VisualizationSceneSolution3d *vssol3d; -extern GeometryRefiner GLVisGeometryRefiner; +thread_local VisualizationSceneSolution3d *vssol3d; +extern thread_local GeometryRefiner GLVisGeometryRefiner; // Definitions of some more keys diff --git a/lib/vsvector.cpp b/lib/vsvector.cpp index 67cf1dfc..65e64125 100644 --- a/lib/vsvector.cpp +++ b/lib/vsvector.cpp @@ -99,13 +99,13 @@ std::string VisualizationSceneVector::GetHelpString() const return os.str(); } -VisualizationSceneVector * vsvector; -extern VisualizationScene * locscene; -extern VisualizationSceneSolution * vssol; -extern GeometryRefiner GLVisGeometryRefiner; +thread_local VisualizationSceneVector * vsvector; +extern thread_local VisualizationScene * locscene; +extern thread_local VisualizationSceneSolution * vssol; +extern thread_local GeometryRefiner GLVisGeometryRefiner; -static int ianim = 0; -static int ianimmax = 10; +thread_local int ianim = 0; +thread_local int ianimmax = 10; void KeyDPressed() { diff --git a/lib/vsvector3d.cpp b/lib/vsvector3d.cpp index 0820537a..90c5f798 100644 --- a/lib/vsvector3d.cpp +++ b/lib/vsvector3d.cpp @@ -100,9 +100,9 @@ std::string VisualizationSceneVector3d::GetHelpString() const return os.str(); } -VisualizationSceneVector3d *vsvector3d; -extern VisualizationScene *locscene; -extern GeometryRefiner GLVisGeometryRefiner; +thread_local VisualizationSceneVector3d *vsvector3d; +extern thread_local VisualizationScene *locscene; +extern thread_local GeometryRefiner GLVisGeometryRefiner; static void KeyDPressed() { From 4bd9748097d178f044e344db94e63da56e4c2e94 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Fri, 28 May 2021 14:08:39 -0700 Subject: [PATCH 02/47] Move glvis_command multiplexing to IdleFunc --- lib/aux_vis.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++--- lib/sdl.cpp | 28 ++------------------- lib/sdl.hpp | 5 ++-- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 612453ba..4281a34f 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -323,6 +323,7 @@ void SetVisualizationScene(VisualizationScene * scene, int view, void RunVisualization() { + visualize = 1; #ifndef __EMSCRIPTEN__ wnd->mainLoop(); #endif @@ -401,16 +402,75 @@ void MyExpose() thread_local Array IdleFuncs; thread_local int LastIdleFunc; +thread_local bool use_idle = false; + +bool MainIdleFunc(); void InitIdleFuncs() { IdleFuncs.SetSize(0); LastIdleFunc = 0; - wnd->setOnIdle(NULL); + if (glvis_command) + { + wnd->setOnIdle(MainIdleFunc); + } +} + +bool CommunicationIdleFunc() +{ + int status = glvis_command->Execute(); + if (status < 0) + { + cout << "GLVisCommand signalled exit" << endl; + wnd->signalQuit(); + } + else if (status == 1) + { + // no commands right now - main loop should sleep + return true; + } + return false; } -void MainIdleFunc() +bool MainIdleFunc() { + bool sleep = true; +#ifndef __EMSCRIPTEN__ + if (glvis_command && visualize == 1 + && !(IdleFuncs.Size() > 0 && use_idle)) + { + // Execute the next event from the communication thread if: + // - a valid GLVisCommand has been set + // - the communication thread is not stopped + // - The idle function flag is not set, or no idle functions have been + // registered + sleep = CommunicationIdleFunc(); + if (IdleFuncs.Size() > 0) { sleep = false; } + } + else if (IdleFuncs.Size() > 0) + { + LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size(); + if (IdleFuncs[LastIdleFunc]) + { + (*IdleFuncs[LastIdleFunc])(); + } + // Continue executing idle functions + sleep = false; + } + use_idle = !use_idle; +#else + if (IdleFuncs.Size() > 0) + { + LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size(); + if (IdleFuncs[LastIdleFunc]) + { + (*IdleFuncs[LastIdleFunc])(this); + } + // Continue executing idle functions + sleep = false; + } +#endif + return sleep; LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size(); if (IdleFuncs[LastIdleFunc]) { @@ -427,7 +487,7 @@ void AddIdleFunc(void (*Func)(void)) void RemoveIdleFunc(void (*Func)(void)) { IdleFuncs.DeleteFirst(Func); - if (IdleFuncs.Size() == 0) + if (IdleFuncs.Size() == 0 && glvis_command == nullptr) { wnd->setOnIdle(NULL); } diff --git a/lib/sdl.cpp b/lib/sdl.cpp index f0f97694..5e8db3cc 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -46,8 +46,6 @@ using std::endl; #endif extern int GetMultisample(); -extern thread_local int visualize; -extern thread_local GLVisCommand* glvis_command; struct SdlWindow::Handle { @@ -606,7 +604,6 @@ void SdlWindow::multiGestureEvent(SDL_MultiGestureEvent & e) void SdlWindow::mainIter() { SDL_Event e; - static bool useIdle = false; static bool disable_mouse = false; if (SDL_PollEvent(&e)) { @@ -667,28 +664,8 @@ void SdlWindow::mainIter() #ifndef __EMSCRIPTEN__ else if (onIdle) { - if (glvis_command == NULL || visualize == 2 || useIdle) - { - onIdle(); - } - else - { - if (glvis_command->Execute() < 0) - { - running = false; - } - } - useIdle = !useIdle; - } - else - { - int status; - if (glvis_command && visualize == 1 && - (status = glvis_command->Execute()) != 1) - { - if (status < 0) { running = false; } - } - else + bool sleep = onIdle(); + if (sleep) { // Wait for the next event (without consuming CPU cycles, if possible) // See also: SdlWindow::signalLoop() @@ -731,7 +708,6 @@ void SdlWindow::mainLoop() ((SdlWindow*) arg)->mainIter(); }, this, 0, true); #else - visualize = 1; while (running) { mainIter(); diff --git a/lib/sdl.hpp b/lib/sdl.hpp index 88e16314..2854e29d 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -32,6 +32,7 @@ typedef void (*MouseDelegate)(EventInfo*); typedef std::function KeyDelegate; typedef void (*WindowDelegate)(int, int); typedef void (*Delegate)(); +typedef bool (*IdleDelegate)(); class SdlWindow { @@ -59,7 +60,7 @@ class SdlWindow bool running; - Delegate onIdle{nullptr}; + IdleDelegate onIdle{nullptr}; Delegate onExpose{nullptr}; WindowDelegate onReshape{nullptr}; std::map onKeyDown; @@ -118,7 +119,7 @@ class SdlWindow // Called by worker threads in GLVisCommand::signal() void signalLoop(); - void setOnIdle(Delegate func) { onIdle = func; } + void setOnIdle(IdleDelegate func) { onIdle = func; } void setOnExpose(Delegate func) { onExpose = func; } void setOnReshape(WindowDelegate func) { onReshape = func; } From 95e97f897b80fa633bc155f449e0c077886a3253 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Fri, 21 May 2021 08:10:34 -0700 Subject: [PATCH 03/47] Add an SdlWindow::MainThread class to handle SDL operations on a single thread --- lib/sdl.cpp | 470 ++++++++++++++++++++++++++++++++++++++++------------ lib/sdl.hpp | 26 ++- 2 files changed, 386 insertions(+), 110 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 5e8db3cc..79844589 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -89,19 +89,280 @@ struct SdlWindow::Handle } }; +struct SdlWindow::MainThread +{ +public: + MainThread() + { + // Create thread to handle all SDL-related commands + event_thread = std::thread(&MainThread::MainLoop, this); + } + + ~MainThread() + { + terminating = true; + } + + bool SdlInitialized() const { return sdl_init; } + + Uint32 GetCustomEvent() const { return glvis_event_type; } + + // Handles all SDL operations that are expected to be handled on the main + // SDL thread (i.e. events and window creation) + void MainLoop() + { + if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) + { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) + { + cerr << "FATAL: Failed to initialize SDL: " << SDL_GetError() << endl; + } + SDL_EnableScreenSaver(); + glvis_event_type = SDL_RegisterEvents(1); + if (glvis_event_type == (Uint32)(-1)) + { + cerr << "SDL_RegisterEvents(1) failed: " << SDL_GetError() << endl; + } + } + + SDL_version sdl_ver; + SDL_GetVersion(&sdl_ver); + PRINT_DEBUG("Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor + << "." << (int)sdl_ver.patch << std::endl); + sdl_init = true; + while (1) + { + if (terminating) { break; } + // Process all pending create window commands + unordered_map window_tmp; + { + lock_guard req_lock{window_create_mtx}; + window_tmp = std::move(window_create_reqs); + } + + for (auto it = window_tmp.begin(); it != window_tmp.end(); ++it) + { + createWindowImpl(it->second); + } + + { + lock_guard ready_lock{window_create_mtx}; + window_ready = std::move(window_tmp); + } + // Let waiting window workers get their created handles + window_created.notify_all(); + + // Process all pending delete window commands + { + lock_guard req_lock{window_delete_mtx}; + // Handle destructors will be called when the temporary vector is + // freed. + vector> to_delete = std::move(windows_to_delete); + } + + // Dequeue all events from the system window manager + SDL_Event e; + while (SDL_PollEvent(&e)) + { + unsigned int windowId = -1; + bool sendToAll = false; + switch (e.type) + { + case SDL_QUIT: + sendToAll = true; + terminating = true; + break; + case SDL_WINDOWEVENT: + windowId = getWindowID(e.window); + break; + case SDL_FINGERDOWN: + fingers.insert(e.tfinger.fingerId); + if (fingers.size() >= 2) { disable_mouse = true; } + break; + case SDL_FINGERUP: + fingers.erase(e.tfinger.fingerId); + if (fingers.size() < 2) { disable_mouse = false; } + break; + case SDL_MULTIGESTURE: + // No window ID is provided with multigesture events. We'll + // have to use focus status within the windows themselves in + // order to resolve the correct target. + sendToAll = true; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + windowId = getWindowID(e.key); + break; + case SDL_TEXTINPUT: + windowId = getWindowID(e.text); + break; + case SDL_MOUSEMOTION: + if (!disable_mouse) { windowId = getWindowID(e.motion); } + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + windowId = getWindowID(e.button); + break; + } + if (sendToAll == true) + { + for (auto wnds : hwnd_to_window) + { + int windowId = wnds.first; + wnd_events[windowId].emplace_back(e); + } + } + else if (windowId != -1) + { + wnd_events[windowId].emplace_back(e); + } + } + // Send events to window worker threads + for (auto wnds : hwnd_to_window) + { + int windowId = wnds.first; + SdlWindow* wnd = wnds.second; + if (!wnd_events[windowId].empty()) + { + wnd->queueEvents(std::move(wnd_events[windowId])); + } + else + { + // Wake up the worker thread anyways, to execute onIdle + wnd->queueEvents({}); + } + } + // Wait for the next event (without consuming CPU cycles, if possible) + // See also: SdlWindow::signalLoop() + if (platform) + { + platform->WaitEvent(); + } + else + { + if (!SDL_PollEvent(nullptr)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(8)); + } + } + } + } + + // Executed from a window worker thread. Returns a handle to a new window + // and associated OpenGL context. + std::unique_ptr GetHandle(SdlWindow* wnd, + const std::string& title, int x, int y, + int w, int h, bool legacyGlOnly) + { + std::thread::id this_thread_id = std::this_thread::get_id(); + CreateWindowCmd new_cmd { wnd, title, x, y, w, h, legacyGlOnly, false }; + // Move our create request into the pending queue + { + lock_guard req_lock{window_create_mtx}; + window_create_reqs[this_thread_id] = std::move(new_cmd); + } + // Wake up the main thread to create our window + platform->SendEvent(); + + unique_ptr out_hnd; + { + unique_lock wnd_lock{window_create_mtx}; + // Wait for window to be created in main thread + window_created.wait(wnd_lock, [this, this_thread_id]() + { + return window_ready[this_thread_id].window_create_executed; + }); + + // Remove window creation command from the pending map + auto it = window_ready.find(this_thread_id); + out_hnd = std::move(it->second.out_handle); + window_ready.erase(it); + } + if (out_hnd->isInitialized()) + { + // Make the OpenGL context current on the worker thread. + // Since SDL calls aren't guaranteed to be thread-safe, we guard + // the call to SDL_GL_MakeCurrent. + lock_guard ctx_lock{gl_ctx_mtx}; + SDL_GL_MakeCurrent(out_hnd->hwnd, out_hnd->gl_ctx); + } + return out_hnd; + } + + // Executed from a window worker thread. Deletes a handle to a window and + // the corresponding OpenGL context. + void DeleteHandle(std::unique_ptr to_delete) + { + lock_guard delete_lock{window_delete_mtx}; + windows_to_delete.emplace_back(std::move(to_delete)); + } +private: + struct CreateWindowCmd + { + SdlWindow* wnd; + std::string title; + int x, y, w, h; + bool legacy_gl_only; + bool window_create_executed; + std::unique_ptr out_handle; + }; + + template + Uint32 getWindowID(const T& eventStruct) + { + return eventStruct.windowID; + } + + void probeGLContextSupport(bool legacyGlOnly); + + void createWindowImpl(CreateWindowCmd& cmd); + + Uint32 glvis_event_type {(Uint32)-1}; + bool sdl_init {false}; + + atomic terminating {false}; + thread event_thread; + + // ------------------------------------------------------------------------- + // Objects for handling creation of window handles by worker threads. + + mutex window_create_mtx; + condition_variable window_created; + unordered_map window_create_reqs; + unordered_map window_ready; + + // ------------------------------------------------------------------------- + // Objects for handling deletion of window handles by worker threads. + + mutex window_delete_mtx; + vector> windows_to_delete; + + // ------------------------------------------------------------------------- + // Objects for handling dispatching events from the main event loop to + // worker threads. + + unordered_map hwnd_to_window; + unordered_map> wnd_events; + std::set fingers; + bool disable_mouse {false}; + + mutex gl_ctx_mtx; + + unique_ptr platform; +}; + +SdlWindow::MainThread SdlWindow::main_thread{}; + bool SdlWindow::isGlInitialized() { return (handle->gl_ctx != 0); } -// Initialize static member -Uint32 SdlWindow::glvis_event_type = (Uint32)(-1); - SdlWindow::SdlWindow() {} // Setup the correct OpenGL context flags in SDL for when we actually open the // window. -void SdlWindow::probeGLContextSupport(bool legacyGlOnly) +void SdlWindow::MainThread::probeGLContextSupport(bool legacyGlOnly) { Uint32 win_flags_hidden = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; #ifndef __EMSCRIPTEN__ @@ -204,31 +465,8 @@ void SdlWindow::probeGLContextSupport(bool legacyGlOnly) #endif } -bool SdlWindow::createWindow(const char * title, int x, int y, int w, int h, - bool legacyGlOnly) +void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) { - if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) - { - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) - { - cerr << "FATAL: Failed to initialize SDL: " << SDL_GetError() << endl; - return false; - } - SDL_EnableScreenSaver(); - if (glvis_event_type == (Uint32)(-1)) - { - glvis_event_type = SDL_RegisterEvents(1); - if (glvis_event_type == (Uint32)(-1)) - { - cerr << "SDL_RegisterEvents(1) failed: " << SDL_GetError() << endl; - return false; - } - } - } - - // destroy any existing SDL window - handle.reset(); - Uint32 win_flags = SDL_WINDOW_OPENGL; // Hide window until we adjust its size for high-dpi displays win_flags |= SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN; @@ -237,19 +475,64 @@ bool SdlWindow::createWindow(const char * title, int x, int y, int w, int h, #endif SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24); - probeGLContextSupport(legacyGlOnly); - handle.reset(new Handle(title, x, y, w, h, win_flags)); + probeGLContextSupport(cmd.legacy_gl_only); + cmd.out_handle.reset(new Handle(cmd.title, cmd.x, cmd.y, cmd.w, cmd.h, + win_flags)); + cmd.window_create_executed = true; // at this point, window should be up - if (!handle->isInitialized()) + if (!cmd.out_handle->isInitialized()) { PRINT_DEBUG("failed." << endl); cerr << "FATAL: window and/or OpenGL context creation failed." << endl; - return false; + return; } else { - PRINT_DEBUG("done." << endl); + PRINT_DEBUG("Handle for window created." << endl); + } + + // Register window internally in the main thread so it can receive events + int wnd_id = SDL_GetWindowID(cmd.out_handle->hwnd); + hwnd_to_window[wnd_id] = cmd.wnd; + + // Unset GL context in this thread + { + lock_guard ctx_lock{gl_ctx_mtx}; + SDL_GL_MakeCurrent(cmd.out_handle->hwnd, nullptr); + } + + if (!platform) + { + SDL_SysWMinfo sysinfo; + SDL_VERSION(&sysinfo.version); + if (!SDL_GetWindowWMInfo(cmd.out_handle->hwnd, &sysinfo)) + { + sysinfo.subsystem = SDL_SYSWM_UNKNOWN; + } + switch (sysinfo.subsystem) + { +#if defined(SDL_VIDEO_DRIVER_X11) + case SDL_SYSWM_X11: + { + // Disable XInput extension events since they are generated even + // outside the GLVis window. + Display *dpy = sysinfo.info.x11.display; + Window win = sysinfo.info.x11.window; + platform.reset(new SdlX11Platform(dpy, win)); + } + break; +#elif defined(SDL_VIDEO_DRIVER_COCOA) + case SDL_SYSWM_COCOA: + { + platform.reset(new SdlCocoaPlatform); + } + break; +#endif + default: + // unhandled window manager + break; + } } const int PixelStride = 4; @@ -271,7 +554,7 @@ bool SdlWindow::createWindow(const char * title, int x, int y, int w, int h, 0xFF000000); if (iconSurf) { - SDL_SetWindowIcon(handle->hwnd, iconSurf); + SDL_SetWindowIcon(cmd.out_handle->hwnd, iconSurf); SDL_FreeSurface(iconSurf); } else @@ -279,6 +562,25 @@ bool SdlWindow::createWindow(const char * title, int x, int y, int w, int h, PRINT_DEBUG("Unable to set window logo: " << SDL_GetError() << endl); } } +} + +bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, + bool legacyGlOnly) +{ + if (!SdlWindow::main_thread.SdlInitialized()) + { + return false; + } + + // create a new SDL window + handle = SdlWindow::main_thread.GetHandle(this, title, x, y, w, h, + legacyGlOnly); + + // at this point, window should be up + if (!handle->isInitialized()) + { + return false; + } #ifndef __EMSCRIPTEN__ SDL_GL_SetSwapInterval(0); @@ -297,11 +599,6 @@ bool SdlWindow::createWindow(const char * title, int x, int y, int w, int h, PRINT_DEBUG("Using GLEW " << glewGetString(GLEW_VERSION) << std::endl); PRINT_DEBUG("Using GL " << glGetString(GL_VERSION) << std::endl); - SDL_version sdl_ver; - SDL_GetVersion(&sdl_ver); - PRINT_DEBUG("Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor - << "." << (int)sdl_ver.patch << std::endl); - renderer.reset(new gl3::MeshRenderer); renderer->setSamplesMSAA(GetMultisample()); #ifndef __EMSCRIPTEN__ @@ -394,43 +691,15 @@ bool SdlWindow::createWindow(const char * title, int x, int y, int w, int h, << sdl_pixel_scale_y*pixel_scale_y << endl; } } - SDL_ShowWindow(handle->hwnd); - - SDL_SysWMinfo sysinfo; - SDL_VERSION(&sysinfo.version); - if (!SDL_GetWindowWMInfo(handle->hwnd, &sysinfo)) - { - sysinfo.subsystem = SDL_SYSWM_UNKNOWN; - } - switch (sysinfo.subsystem) - { -#if defined(SDL_VIDEO_DRIVER_X11) - case SDL_SYSWM_X11: - { - // Disable XInput extension events since they are generated even - // outside the GLVis window. - Display *dpy = sysinfo.info.x11.display; - Window win = sysinfo.info.x11.window; - platform.reset(new SdlX11Platform(dpy, win)); - } - break; -#elif defined(SDL_VIDEO_DRIVER_COCOA) - case SDL_SYSWM_COCOA: - { - platform.reset(new SdlCocoaPlatform); - } - break; -#endif - default: - // unhandled window manager - break; - } return true; } -// defined here because the Handle destructor needs to be visible -SdlWindow::~SdlWindow() {} +SdlWindow::~SdlWindow() +{ + // Let the main SDL thread delete the handles + SdlWindow::main_thread.DeleteHandle(std::move(handle)); +} void SdlWindow::windowEvent(SDL_WindowEvent& ew) { @@ -603,13 +872,24 @@ void SdlWindow::multiGestureEvent(SDL_MultiGestureEvent & e) void SdlWindow::mainIter() { - SDL_Event e; - static bool disable_mouse = false; - if (SDL_PollEvent(&e)) + bool events_pending = false; + { + lock_guard evt_guard{event_mutex}; + events_pending = !waiting_events.empty(); + } + if (events_pending) { bool keep_going; do { + SDL_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(); + } keep_going = false; switch (e.type) { @@ -619,16 +899,6 @@ void SdlWindow::mainIter() case SDL_WINDOWEVENT: windowEvent(e.window); break; - case SDL_FINGERDOWN: - fingers.insert(e.tfinger.fingerId); - if (fingers.size() >= 2) { disable_mouse = true; } - keep_going = true; - break; - case SDL_FINGERUP: - fingers.erase(e.tfinger.fingerId); - if (fingers.size() < 2) { disable_mouse = false; } - keep_going = true; - break; case SDL_MULTIGESTURE: multiGestureEvent(e.mgesture); keep_going = true; @@ -647,19 +917,19 @@ void SdlWindow::mainIter() keyEvent(e.text.text[0]); break; case SDL_MOUSEMOTION: - if (!disable_mouse) { motionEvent(e.motion); } + motionEvent(e.motion); // continue processing events keep_going = true; break; case SDL_MOUSEBUTTONDOWN: - if (!disable_mouse) { mouseEventDown(e.button); } + mouseEventDown(e.button); break; case SDL_MOUSEBUTTONUP: - if (!disable_mouse) { mouseEventUp(e.button); } + mouseEventUp(e.button); break; } } - while (keep_going && SDL_PollEvent(&e)); + while (keep_going && events_pending); } #ifndef __EMSCRIPTEN__ else if (onIdle) @@ -667,19 +937,9 @@ void SdlWindow::mainIter() bool sleep = onIdle(); if (sleep) { - // Wait for the next event (without consuming CPU cycles, if possible) - // See also: SdlWindow::signalLoop() - if (platform) - { - platform->WaitEvent(); - } - else - { - if (!SDL_PollEvent(nullptr)) - { - std::this_thread::sleep_for(std::chrono::milliseconds(8)); - } - } + // Wait for next wakeup event from main event thread + unique_lock event_lock{event_mutex}; + events_available.wait(event_lock); } } #else @@ -687,10 +947,6 @@ void SdlWindow::mainIter() { onIdle(); } - else - { - // pass - } #endif if (wnd_state == RenderState::ExposePending) { @@ -736,7 +992,7 @@ void SdlWindow::signalLoop() { SDL_Event event; SDL_memset(&event, 0, sizeof(event)); - event.type = glvis_event_type; + event.type = main_thread.GetCustomEvent(); const int glvis_event_code = 1; event.user.code = glvis_event_code; event.user.data1 = nullptr; diff --git a/lib/sdl.hpp b/lib/sdl.hpp index 2854e29d..e82d2630 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "gl/renderer.hpp" #include "sdl_helper.hpp" @@ -38,6 +40,10 @@ class SdlWindow { private: struct Handle; + struct MainThread; + + static MainThread main_thread; + std::unique_ptr handle; std::unique_ptr renderer; static const int high_dpi_threshold = 144; @@ -56,7 +62,6 @@ class SdlWindow std::unique_ptr platform; - static Uint32 glvis_event_type; bool running; @@ -69,7 +74,6 @@ class SdlWindow std::map onMouseMove; TouchDelegate onTouchPinch{nullptr}; TouchDelegate onTouchRotate{nullptr}; - std::set fingers; bool ctrlDown{false}; @@ -93,7 +97,6 @@ class SdlWindow bool takeScreenshot{false}; std::string screenshot_file; - void probeGLContextSupport(bool legacyGlOnly); // internal event handlers void windowEvent(SDL_WindowEvent& ew); void motionEvent(SDL_MouseMotionEvent& em); @@ -103,7 +106,23 @@ class SdlWindow void keyEvent(char c); void multiGestureEvent(SDL_MultiGestureEvent & e); + // Hand off events to the SdlWindow. Intended to be called by the main SDL + // thread in MainThread::MainLoop(). + void queueEvents(std::vector events) + { + { + std::lock_guard evt_guard{event_mutex}; + waiting_events.insert(waiting_events.end(), events.begin(), events.end()); + } + events_available.notify_all(); + } + std::string saved_keys; + + std::condition_variable events_available; + std::mutex event_mutex; + // The window-specific events collected by the main event thread. + std::deque waiting_events; public: SdlWindow(); ~SdlWindow(); @@ -112,6 +131,7 @@ class SdlWindow /// fails. bool createWindow(const char * title, int x, int y, int w, int h, bool legacyGlOnly); + /// Runs the window loop. void mainLoop(); void mainIter(); From 1c324e40e769f6e8b176ff440f94acb14867a8cb Mon Sep 17 00:00:00 2001 From: Max Yang Date: Fri, 21 May 2021 13:21:43 -0700 Subject: [PATCH 04/47] Get things to display --- glvis.cpp | 20 +++++- lib/aux_vis.cpp | 4 ++ lib/aux_vis.hpp | 2 + lib/sdl.cpp | 187 ++++++++++++++++++++++++++++++++---------------- lib/sdl.hpp | 6 +- 5 files changed, 152 insertions(+), 67 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 7a7aeb17..46857756 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -1028,10 +1028,24 @@ int main (int argc, char *argv[]) ifs >> data_type >> ws; int ft = stream_state.ReadStream(ifs, data_type); input_streams.Append(&ifs); - if (GLVisInitVis(ft)) + std::thread worker_thread { - GLVisStartVis(); - } + [&](StreamState local_state, Array is) + { + // set the thread-local StreamState and input streams + input_streams = is; + stream_state = std::move(local_state); + if (GLVisInitVis(ft)) + { + GLVisStartVis(); + } + }, + std::move(stream_state), + input_streams + }; + + SDLMainLoop(); + worker_thread.join(); return 0; } diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 4281a34f..38e4b491 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -48,6 +48,10 @@ float line_w_aa = gl3::LINE_WIDTH_AA; thread_local SdlWindow * wnd = nullptr; bool wndLegacyGl = false; +void SDLMainLoop() +{ + SdlWindow::StartSDL(); +} SdlWindow * GetAppWindow() { diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 93542868..098fe923 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -19,6 +19,8 @@ #include "font.hpp" #include "openglvis.hpp" +void SDLMainLoop(); + /// Initializes the visualization and some keys. int InitVisualization(const char name[], int x, int y, int w, int h); diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 79844589..0471faf9 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -94,13 +94,6 @@ struct SdlWindow::MainThread public: MainThread() { - // Create thread to handle all SDL-related commands - event_thread = std::thread(&MainThread::MainLoop, this); - } - - ~MainThread() - { - terminating = true; } bool SdlInitialized() const { return sdl_init; } @@ -132,7 +125,6 @@ struct SdlWindow::MainThread sdl_init = true; while (1) { - if (terminating) { break; } // Process all pending create window commands unordered_map window_tmp; { @@ -158,6 +150,20 @@ struct SdlWindow::MainThread // Handle destructors will be called when the temporary vector is // freed. vector> to_delete = std::move(windows_to_delete); + for (int ihnd = 0; ihnd < to_delete.size(); ihnd++) + { + if (to_delete[ihnd]) + { + int wnd_id = SDL_GetWindowID(to_delete[ihnd]->hwnd); + hwnd_to_window.erase(wnd_id); + wnd_events.erase(wnd_id); + num_windows--; + } + } + } + if (num_windows == 0) + { + break; // all windows closed } // Dequeue all events from the system window manager @@ -217,6 +223,7 @@ struct SdlWindow::MainThread wnd_events[windowId].emplace_back(e); } } + if (terminating) { break; } // Send events to window worker threads for (auto wnds : hwnd_to_window) { @@ -262,7 +269,7 @@ struct SdlWindow::MainThread window_create_reqs[this_thread_id] = std::move(new_cmd); } // Wake up the main thread to create our window - platform->SendEvent(); + if (platform) { platform->SendEvent(); } unique_ptr out_hnd; { @@ -270,7 +277,7 @@ struct SdlWindow::MainThread // Wait for window to be created in main thread window_created.wait(wnd_lock, [this, this_thread_id]() { - return window_ready[this_thread_id].window_create_executed; + return window_ready.find(this_thread_id) != window_ready.end(); }); // Remove window creation command from the pending map @@ -315,13 +322,14 @@ struct SdlWindow::MainThread void probeGLContextSupport(bool legacyGlOnly); + void getDpi(Handle* handle, int& wdpi, int& hdpi); + void createWindowImpl(CreateWindowCmd& cmd); Uint32 glvis_event_type {(Uint32)-1}; bool sdl_init {false}; atomic terminating {false}; - thread event_thread; // ------------------------------------------------------------------------- // Objects for handling creation of window handles by worker threads. @@ -331,6 +339,8 @@ struct SdlWindow::MainThread unordered_map window_create_reqs; unordered_map window_ready; + int num_windows {-1}; // -1: waiting for window to be created + // ------------------------------------------------------------------------- // Objects for handling deletion of window handles by worker threads. @@ -360,6 +370,11 @@ bool SdlWindow::isGlInitialized() SdlWindow::SdlWindow() {} +void SdlWindow::StartSDL() +{ + main_thread.MainLoop(); +} + // Setup the correct OpenGL context flags in SDL for when we actually open the // window. void SdlWindow::MainThread::probeGLContextSupport(bool legacyGlOnly) @@ -465,6 +480,39 @@ void SdlWindow::MainThread::probeGLContextSupport(bool legacyGlOnly) #endif } +const int default_dpi = 72; +void SdlWindow::MainThread::getDpi(Handle* handle, int& w, int& h) +{ + w = default_dpi; + h = default_dpi; + if (!handle) + { + PRINT_DEBUG("warning: unable to get dpi: handle is null" << endl); + return; + } + int disp = SDL_GetWindowDisplayIndex(handle->hwnd); + if (disp < 0) + { + PRINT_DEBUG("warning: problem getting display index: " << SDL_GetError() + << endl); + PRINT_DEBUG("returning default dpi of " << default_dpi << endl); + return; + } + + float f_w, f_h; + if (SDL_GetDisplayDPI(disp, NULL, &f_w, &f_h)) + { + PRINT_DEBUG("warning: problem getting dpi, setting to " << default_dpi + << ": " << SDL_GetError() << endl); + } + else + { + PRINT_DEBUG("Screen DPI: w = " << f_w << " ppi, h = " << f_h << " ppi"); + w = f_w + 0.5f; + h = f_h + 0.5f; + PRINT_DEBUG(" (" << w << " x " << h << ')' << endl); + } +} void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) { Uint32 win_flags = SDL_WINDOW_OPENGL; @@ -496,12 +544,6 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) int wnd_id = SDL_GetWindowID(cmd.out_handle->hwnd); hwnd_to_window[wnd_id] = cmd.wnd; - // Unset GL context in this thread - { - lock_guard ctx_lock{gl_ctx_mtx}; - SDL_GL_MakeCurrent(cmd.out_handle->hwnd, nullptr); - } - if (!platform) { SDL_SysWMinfo sysinfo; @@ -562,16 +604,74 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) PRINT_DEBUG("Unable to set window logo: " << SDL_GetError() << endl); } } + + // Detect if we are using a high-dpi display and resize the window unless it + // was already resized by SDL's underlying backend. + { + Handle* handle = cmd.out_handle.get(); + SdlWindow* wnd = cmd.wnd; + int scr_w, scr_h, pix_w, pix_h, wdpi, hdpi; + // SDL_GetWindowSize() -- size in "screen coordinates" + SDL_GetWindowSize(handle->hwnd, &scr_w, &scr_h); + // SDL_GL_GetDrawableSize() -- size in pixels + SDL_GL_GetDrawableSize(handle->hwnd, &pix_w, &pix_h); + wnd->high_dpi = false; + wnd->pixel_scale_x = wnd->pixel_scale_y = 1.0f; + float sdl_pixel_scale_x = 1.0f, sdl_pixel_scale_y = 1.0f; + // If "screen" and "pixel" sizes are different, assume high-dpi and no + // need to scale the window. + if (scr_w == pix_w && scr_h == pix_h) + { + getDpi(handle, wdpi, hdpi); + if (std::max(wdpi, hdpi) >= high_dpi_threshold) + { + wnd->high_dpi = true; + wnd->pixel_scale_x = wnd->pixel_scale_y = 2.0f; + // the following two calls use 'pixel_scale_*' + SDL_SetWindowSize(handle->hwnd, + wnd->pixel_scale_x*cmd.w, + wnd->pixel_scale_y*cmd.h); + bool uc_x = SDL_WINDOWPOS_ISUNDEFINED(cmd.x) || + SDL_WINDOWPOS_ISCENTERED(cmd.x); + bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(cmd.y) || + SDL_WINDOWPOS_ISCENTERED(cmd.y); + SDL_SetWindowPosition(handle->hwnd, + uc_x ? cmd.x : wnd->pixel_scale_x*cmd.x, + uc_y ? cmd.y : wnd->pixel_scale_y*cmd.y); + } + } + else + { + wnd->high_dpi = true; + // keep 'pixel_scale_*' = 1, scaling is done inside SDL + sdl_pixel_scale_x = float(pix_w)/scr_w; + sdl_pixel_scale_y = float(pix_h)/scr_h; + } + if (wnd->high_dpi) + { + cout << "High-dpi display detected: using window scaling: " + << sdl_pixel_scale_x*wnd->pixel_scale_x << " x " + << sdl_pixel_scale_y*wnd->pixel_scale_y << endl; + } + } + + // Unset GL context in this thread + { + lock_guard ctx_lock{gl_ctx_mtx}; + SDL_GL_MakeCurrent(cmd.out_handle->hwnd, nullptr); + } + + SDL_ShowWindow(cmd.out_handle->hwnd); + if (num_windows == -1) + { + num_windows = 0; + } + num_windows++; } bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, bool legacyGlOnly) { - if (!SdlWindow::main_thread.SdlInitialized()) - { - return false; - } - // create a new SDL window handle = SdlWindow::main_thread.GetHandle(this, title, x, y, w, h, legacyGlOnly); @@ -652,46 +752,6 @@ bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, renderer->setDevice(); #endif - // Detect if we are using a high-dpi display and resize the window unless it - // was already resized by SDL's underlying backend. - { - int scr_w, scr_h, pix_w, pix_h, wdpi, hdpi; - // SDL_GetWindowSize() -- size in "screen coordinates" - SDL_GetWindowSize(handle->hwnd, &scr_w, &scr_h); - // SDL_GL_GetDrawableSize() -- size in pixels - SDL_GL_GetDrawableSize(handle->hwnd, &pix_w, &pix_h); - high_dpi = false; - pixel_scale_x = pixel_scale_y = 1.0f; - float sdl_pixel_scale_x = 1.0f, sdl_pixel_scale_y = 1.0f; - // If "screen" and "pixel" sizes are different, assume high-dpi and no - // need to scale the window. - if (scr_w == pix_w && scr_h == pix_h) - { - getDpi(wdpi, hdpi); - if (std::max(wdpi, hdpi) >= high_dpi_threshold) - { - high_dpi = true; - pixel_scale_x = pixel_scale_y = 2.0f; - // the following two calls use 'pixel_scale_*' - setWindowSize(w, h); - setWindowPos(x, y); - } - } - else - { - high_dpi = true; - // keep 'pixel_scale_*' = 1, scaling is done inside SDL - sdl_pixel_scale_x = float(pix_w)/scr_w; - sdl_pixel_scale_y = float(pix_h)/scr_h; - } - if (high_dpi) - { - cout << "High-dpi display detected: using window scaling: " - << sdl_pixel_scale_x*pixel_scale_x << " x " - << sdl_pixel_scale_y*pixel_scale_y << endl; - } - } - return true; } @@ -717,6 +777,8 @@ void SdlWindow::windowEvent(SDL_WindowEvent& ew) wnd_state = RenderState::ExposePending; } break; + case SDL_WINDOWEVENT_CLOSE: + running = false; default: break; } @@ -1036,7 +1098,6 @@ void SdlWindow::getGLDrawSize(int& w, int& h) SDL_GL_GetDrawableSize(handle->hwnd, &w, &h); } -const int default_dpi = 72; void SdlWindow::getDpi(int& w, int& h) { w = default_dpi; diff --git a/lib/sdl.hpp b/lib/sdl.hpp index e82d2630..e10cda85 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -62,7 +62,6 @@ class SdlWindow std::unique_ptr platform; - bool running; IdleDelegate onIdle{nullptr}; @@ -110,6 +109,7 @@ class SdlWindow // thread in MainThread::MainLoop(). void queueEvents(std::vector events) { + if (running) { std::lock_guard evt_guard{event_mutex}; waiting_events.insert(waiting_events.end(), events.begin(), events.end()); @@ -127,6 +127,10 @@ class SdlWindow SdlWindow(); ~SdlWindow(); + // Initializes SDL2 and starts the main loop. Should be called from the main + // thread only. + static void StartSDL(); + /// 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, From 7f157080671c876af56eb8ed6c502fc173cadd72 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Fri, 21 May 2021 14:46:30 -0700 Subject: [PATCH 05/47] Consolidate SDL main thread commands from worker threads --- lib/aux_vis.cpp | 4 +- lib/aux_vis.hpp | 2 +- lib/sdl.cpp | 254 ++++++++++++++++++++++++++++++------------------ lib/sdl.hpp | 3 +- 4 files changed, 161 insertions(+), 102 deletions(-) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 38e4b491..469269c8 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -48,9 +48,9 @@ float line_w_aa = gl3::LINE_WIDTH_AA; thread_local SdlWindow * wnd = nullptr; bool wndLegacyGl = false; -void SDLMainLoop() +void SDLMainLoop(bool server_mode) { - SdlWindow::StartSDL(); + SdlWindow::StartSDL(server_mode); } SdlWindow * GetAppWindow() diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 098fe923..389f27d0 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -19,7 +19,7 @@ #include "font.hpp" #include "openglvis.hpp" -void SDLMainLoop(); +void SDLMainLoop(bool server_mode = false); /// Initializes the visualization and some keys. int InitVisualization(const char name[], int x, int y, int w, int h); diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 0471faf9..553f85d5 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "sdl.hpp" #include "threads.hpp" #include "aux_vis.hpp" @@ -102,7 +103,7 @@ struct SdlWindow::MainThread // Handles all SDL operations that are expected to be handled on the main // SDL thread (i.e. events and window creation) - void MainLoop() + void MainLoop(bool server_mode) { if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { @@ -125,43 +126,49 @@ struct SdlWindow::MainThread sdl_init = true; while (1) { - // Process all pending create window commands - unordered_map window_tmp; + // Process all pending window commands + vector pending_cmds; { - lock_guard req_lock{window_create_mtx}; - window_tmp = std::move(window_create_reqs); + lock_guard cmd_lock{window_cmd_mtx}; + pending_cmds = std::move(window_cmds); } - for (auto it = window_tmp.begin(); it != window_tmp.end(); ++it) + for (SdlCtrlCommand& cmd : pending_cmds) { - createWindowImpl(it->second); - } - - { - lock_guard ready_lock{window_create_mtx}; - window_ready = std::move(window_tmp); - } - // Let waiting window workers get their created handles - window_created.notify_all(); - - // Process all pending delete window commands - { - lock_guard req_lock{window_delete_mtx}; - // Handle destructors will be called when the temporary vector is - // freed. - vector> to_delete = std::move(windows_to_delete); - for (int ihnd = 0; ihnd < to_delete.size(); ihnd++) + switch (cmd.type) { - if (to_delete[ihnd]) - { - int wnd_id = SDL_GetWindowID(to_delete[ihnd]->hwnd); - hwnd_to_window.erase(wnd_id); - wnd_events.erase(wnd_id); - num_windows--; - } + case SdlCmdType::Create: + createWindowImpl(*cmd.cmd_create); + break; + case SdlCmdType::Delete: + if (cmd.cmd_delete) + { + unique_ptr to_delete = std::move(cmd.cmd_delete); + int wnd_id = SDL_GetWindowID(to_delete->hwnd); + hwnd_to_window.erase(wnd_id); + wnd_events.erase(wnd_id); + num_windows--; + } + break; + case SdlCmdType::SetTitle: + SDL_SetWindowTitle(cmd.handle->hwnd, cmd.cmd_title.c_str()); + break; + case SdlCmdType::SetSize: + SDL_SetWindowSize(cmd.handle->hwnd, + cmd.cmd_set_size.first, + cmd.cmd_set_size.second); + break; + case SdlCmdType::SetPosition: + SDL_SetWindowPosition(cmd.handle->hwnd, + cmd.cmd_set_position.first, + cmd.cmd_set_position.second); + break; + default: + cerr << "Error in main thread: unknown window control command.\n"; + break; } } - if (num_windows == 0) + if (!server_mode && num_windows == 0) { break; // all windows closed } @@ -261,30 +268,22 @@ struct SdlWindow::MainThread const std::string& title, int x, int y, int w, int h, bool legacyGlOnly) { - std::thread::id this_thread_id = std::this_thread::get_id(); - CreateWindowCmd new_cmd { wnd, title, x, y, w, h, legacyGlOnly, false }; + CreateWindowCmd cmd_create = { wnd, title, x, y, w, h, legacyGlOnly, false }; + future> res_handle = cmd_create.out_handle.get_future(); + + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::Create; + main_thread_cmd.cmd_create = &cmd_create; + // Move our create request into the pending queue { - lock_guard req_lock{window_create_mtx}; - window_create_reqs[this_thread_id] = std::move(new_cmd); + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(main_thread_cmd)); } // Wake up the main thread to create our window if (platform) { platform->SendEvent(); } - unique_ptr out_hnd; - { - unique_lock wnd_lock{window_create_mtx}; - // Wait for window to be created in main thread - window_created.wait(wnd_lock, [this, this_thread_id]() - { - return window_ready.find(this_thread_id) != window_ready.end(); - }); - - // Remove window creation command from the pending map - auto it = window_ready.find(this_thread_id); - out_hnd = std::move(it->second.out_handle); - window_ready.erase(it); - } + unique_ptr out_hnd = res_handle.get(); if (out_hnd->isInitialized()) { // Make the OpenGL context current on the worker thread. @@ -300,9 +299,62 @@ struct SdlWindow::MainThread // the corresponding OpenGL context. void DeleteHandle(std::unique_ptr to_delete) { - lock_guard delete_lock{window_delete_mtx}; - windows_to_delete.emplace_back(std::move(to_delete)); + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::Delete; + main_thread_cmd.cmd_delete = std::move(to_delete); + + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(main_thread_cmd)); + } + if (platform) { platform->SendEvent(); } + } + + // Issues a command on the main thread to set the window title. + void SetWindowTitle(Handle* handle, std::string title) + { + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::SetTitle; + main_thread_cmd.handle = handle; + main_thread_cmd.cmd_title = std::move(title); + + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(main_thread_cmd)); + } + if (platform) { platform->SendEvent(); } + } + + // Issues a command on the main thread to set the window size. + void SetWindowSize(Handle* handle, int w, int h) + { + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::SetSize; + main_thread_cmd.handle = handle; + main_thread_cmd.cmd_set_size = {w, h}; + + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(main_thread_cmd)); + } + if (platform) { platform->SendEvent(); } } + + // Issues a command on the main thread to set the window position. + void SetWindowPosition(Handle* handle, int x, int y) + { + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::SetPosition; + main_thread_cmd.handle = handle; + main_thread_cmd.cmd_set_position = {x, y}; + + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(main_thread_cmd)); + } + if (platform) { platform->SendEvent(); } + } + private: struct CreateWindowCmd { @@ -311,7 +363,29 @@ struct SdlWindow::MainThread int x, y, w, h; bool legacy_gl_only; bool window_create_executed; - std::unique_ptr out_handle; + promise> out_handle; + }; + + enum class SdlCmdType + { + None, + Create, + Delete, + SetTitle, + SetSize, + SetPosition + }; + + struct SdlCtrlCommand + { + SdlCmdType type {SdlCmdType::None}; + + Handle* handle {nullptr}; + CreateWindowCmd* cmd_create; + unique_ptr cmd_delete; + string cmd_title; + pair cmd_set_size; + pair cmd_set_position; }; template @@ -332,21 +406,14 @@ struct SdlWindow::MainThread atomic terminating {false}; // ------------------------------------------------------------------------- - // Objects for handling creation of window handles by worker threads. + // Objects for handling passing of window control commands to the main event + // loop. - mutex window_create_mtx; - condition_variable window_created; - unordered_map window_create_reqs; - unordered_map window_ready; + mutex window_cmd_mtx; + vector window_cmds; int num_windows {-1}; // -1: waiting for window to be created - // ------------------------------------------------------------------------- - // Objects for handling deletion of window handles by worker threads. - - mutex window_delete_mtx; - vector> windows_to_delete; - // ------------------------------------------------------------------------- // Objects for handling dispatching events from the main event loop to // worker threads. @@ -370,9 +437,9 @@ bool SdlWindow::isGlInitialized() SdlWindow::SdlWindow() {} -void SdlWindow::StartSDL() +void SdlWindow::StartSDL(bool server_mode) { - main_thread.MainLoop(); + main_thread.MainLoop(server_mode); } // Setup the correct OpenGL context flags in SDL for when we actually open the @@ -524,12 +591,13 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24); probeGLContextSupport(cmd.legacy_gl_only); - cmd.out_handle.reset(new Handle(cmd.title, cmd.x, cmd.y, cmd.w, cmd.h, - win_flags)); + unique_ptr new_handle; + new_handle.reset(new Handle(cmd.title, cmd.x, cmd.y, cmd.w, cmd.h, + win_flags)); cmd.window_create_executed = true; // at this point, window should be up - if (!cmd.out_handle->isInitialized()) + if (!new_handle->isInitialized()) { PRINT_DEBUG("failed." << endl); cerr << "FATAL: window and/or OpenGL context creation failed." << endl; @@ -540,15 +608,20 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) PRINT_DEBUG("Handle for window created." << endl); } +#ifndef __EMSCRIPTEN__ + SDL_GL_SetSwapInterval(0); + glEnable(GL_DEBUG_OUTPUT); +#endif + // Register window internally in the main thread so it can receive events - int wnd_id = SDL_GetWindowID(cmd.out_handle->hwnd); + int wnd_id = SDL_GetWindowID(new_handle->hwnd); hwnd_to_window[wnd_id] = cmd.wnd; if (!platform) { SDL_SysWMinfo sysinfo; SDL_VERSION(&sysinfo.version); - if (!SDL_GetWindowWMInfo(cmd.out_handle->hwnd, &sysinfo)) + if (!SDL_GetWindowWMInfo(new_handle->hwnd, &sysinfo)) { sysinfo.subsystem = SDL_SYSWM_UNKNOWN; } @@ -596,7 +669,7 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) 0xFF000000); if (iconSurf) { - SDL_SetWindowIcon(cmd.out_handle->hwnd, iconSurf); + SDL_SetWindowIcon(new_handle->hwnd, iconSurf); SDL_FreeSurface(iconSurf); } else @@ -608,7 +681,7 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) // Detect if we are using a high-dpi display and resize the window unless it // was already resized by SDL's underlying backend. { - Handle* handle = cmd.out_handle.get(); + Handle* handle = new_handle.get(); SdlWindow* wnd = cmd.wnd; int scr_w, scr_h, pix_w, pix_h, wdpi, hdpi; // SDL_GetWindowSize() -- size in "screen coordinates" @@ -658,15 +731,16 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) // Unset GL context in this thread { lock_guard ctx_lock{gl_ctx_mtx}; - SDL_GL_MakeCurrent(cmd.out_handle->hwnd, nullptr); + SDL_GL_MakeCurrent(new_handle->hwnd, nullptr); } - SDL_ShowWindow(cmd.out_handle->hwnd); + SDL_ShowWindow(new_handle->hwnd); if (num_windows == -1) { num_windows = 0; } num_windows++; + cmd.out_handle.set_value(std::move(new_handle)); } bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, @@ -682,11 +756,6 @@ bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, return false; } -#ifndef __EMSCRIPTEN__ - SDL_GL_SetSwapInterval(0); - glEnable(GL_DEBUG_OUTPUT); -#endif - GLenum err = glewInit(); if (err != GLEW_OK) { @@ -1138,32 +1207,23 @@ void SdlWindow::setWindowTitle(std::string& title) void SdlWindow::setWindowTitle(const char * title) { - if (handle) - { - SDL_SetWindowTitle(handle->hwnd, title); - } + main_thread.SetWindowTitle(handle.get(), title); } void SdlWindow::setWindowSize(int w, int h) { - if (handle) - { - SDL_SetWindowSize(handle->hwnd, pixel_scale_x*w, pixel_scale_y*h); - } + main_thread.SetWindowSize(handle.get(), pixel_scale_x*w, pixel_scale_y*h); } void SdlWindow::setWindowPos(int x, int y) { - if (handle) - { - bool uc_x = SDL_WINDOWPOS_ISUNDEFINED(x) || - SDL_WINDOWPOS_ISCENTERED(x); - bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(y) || - SDL_WINDOWPOS_ISCENTERED(y); - SDL_SetWindowPosition(handle->hwnd, - uc_x ? x : pixel_scale_x*x, - uc_y ? y : pixel_scale_y*y); - } + bool uc_x = SDL_WINDOWPOS_ISUNDEFINED(x) || + SDL_WINDOWPOS_ISCENTERED(x); + bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(y) || + SDL_WINDOWPOS_ISCENTERED(y); + main_thread.SetWindowPosition(handle.get(), + uc_x ? x : pixel_scale_x*x, + uc_y ? y : pixel_scale_y*y); } void SdlWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) diff --git a/lib/sdl.hpp b/lib/sdl.hpp index e10cda85..a66a8f34 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -109,7 +109,6 @@ class SdlWindow // thread in MainThread::MainLoop(). void queueEvents(std::vector events) { - if (running) { std::lock_guard evt_guard{event_mutex}; waiting_events.insert(waiting_events.end(), events.begin(), events.end()); @@ -129,7 +128,7 @@ class SdlWindow // Initializes SDL2 and starts the main loop. Should be called from the main // thread only. - static void StartSDL(); + static void StartSDL(bool server_mode); /// Creates a new OpenGL window. Returns false if SDL or OpenGL initialization /// fails. From b135df28ee32af49f3215677f83dd2efcd753fb0 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 24 May 2021 14:08:48 -0700 Subject: [PATCH 06/47] Fixing window exits and resizes; miscellaneous cleanup --- lib/sdl.cpp | 95 ++++++++++++++++++++++++++++------------------------- lib/sdl.hpp | 4 +-- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 553f85d5..6abafc45 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -170,7 +170,7 @@ struct SdlWindow::MainThread } if (!server_mode && num_windows == 0) { - break; // all windows closed + terminating = true; // all windows closed } // Dequeue all events from the system window manager @@ -182,8 +182,10 @@ struct SdlWindow::MainThread switch (e.type) { case SDL_QUIT: - sendToAll = true; - terminating = true; + if (!server_mode) + { + terminating = true; + } break; case SDL_WINDOWEVENT: windowId = getWindowID(e.window); @@ -230,7 +232,6 @@ struct SdlWindow::MainThread wnd_events[windowId].emplace_back(e); } } - if (terminating) { break; } // Send events to window worker threads for (auto wnds : hwnd_to_window) { @@ -246,6 +247,7 @@ struct SdlWindow::MainThread wnd->queueEvents({}); } } + if (terminating) { break; } // Wait for the next event (without consuming CPU cycles, if possible) // See also: SdlWindow::signalLoop() if (platform) @@ -303,11 +305,7 @@ struct SdlWindow::MainThread main_thread_cmd.type = SdlCmdType::Delete; main_thread_cmd.cmd_delete = std::move(to_delete); - { - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_back(std::move(main_thread_cmd)); - } - if (platform) { platform->SendEvent(); } + queueWindowEvent(std::move(main_thread_cmd)); } // Issues a command on the main thread to set the window title. @@ -318,11 +316,7 @@ struct SdlWindow::MainThread main_thread_cmd.handle = handle; main_thread_cmd.cmd_title = std::move(title); - { - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_back(std::move(main_thread_cmd)); - } - if (platform) { platform->SendEvent(); } + queueWindowEvent(std::move(main_thread_cmd)); } // Issues a command on the main thread to set the window size. @@ -333,11 +327,7 @@ struct SdlWindow::MainThread main_thread_cmd.handle = handle; main_thread_cmd.cmd_set_size = {w, h}; - { - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_back(std::move(main_thread_cmd)); - } - if (platform) { platform->SendEvent(); } + queueWindowEvent(std::move(main_thread_cmd)); } // Issues a command on the main thread to set the window position. @@ -348,10 +338,11 @@ struct SdlWindow::MainThread main_thread_cmd.handle = handle; main_thread_cmd.cmd_set_position = {x, y}; - { - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_back(std::move(main_thread_cmd)); - } + queueWindowEvent(std::move(main_thread_cmd)); + } + + void SendEvent() + { if (platform) { platform->SendEvent(); } } @@ -388,6 +379,15 @@ struct SdlWindow::MainThread pair cmd_set_position; }; + void queueWindowEvent(SdlCtrlCommand cmd) + { + { + lock_guard req_lock{window_cmd_mtx}; + window_cmds.emplace_back(std::move(cmd)); + } + if (platform) { platform->SendEvent(); } + } + template Uint32 getWindowID(const T& eventStruct) { @@ -834,12 +834,6 @@ void SdlWindow::windowEvent(SDL_WindowEvent& ew) { switch (ew.event) { - case SDL_WINDOWEVENT_SIZE_CHANGED: - if (onReshape) - { - onReshape(ew.data1, ew.data2); - } - break; case SDL_WINDOWEVENT_EXPOSED: if (onExpose) { @@ -848,6 +842,11 @@ void SdlWindow::windowEvent(SDL_WindowEvent& ew) break; case SDL_WINDOWEVENT_CLOSE: running = false; + break; + case SDL_WINDOWEVENT_MOVED: + case SDL_WINDOWEVENT_RESIZED: + swap_before_expose = true; + break; default: break; } @@ -1029,6 +1028,10 @@ void SdlWindow::mainIter() break; case SDL_WINDOWEVENT: windowEvent(e.window); + if (wnd_state != RenderState::ExposePending) + { + keep_going = true; + } break; case SDL_MULTIGESTURE: multiGestureEvent(e.mgesture); @@ -1081,6 +1084,22 @@ void SdlWindow::mainIter() #endif if (wnd_state == RenderState::ExposePending) { +#ifdef SDL_VIDEO_DRIVER_COCOA + // There is some weird behavior with SDL on Cocoa, where the swap + // immediately following a resize event causes the most recent onExpose() + // to display incorrectly. I suspect it has something to do with + // [NSOpenGLContext update] getting called after the context flush within + // Cocoa_GL_SwapBuffers(). + // + // To work around this, we do an extra swap before onExpose() so that the + // [NSOpenGLContext update] call is done before we start rendering to the + // back buffer. + if (swap_before_expose) + { + SDL_GL_SwapWindow(handle->hwnd); + swap_before_expose = false; + } +#endif onExpose(); wnd_state = RenderState::SwapPending; } @@ -1115,21 +1134,7 @@ void SdlWindow::mainLoop() void SdlWindow::signalLoop() { // Note: not executed from the main thread - if (platform) - { - platform->SendEvent(); - } - else - { - SDL_Event event; - SDL_memset(&event, 0, sizeof(event)); - event.type = main_thread.GetCustomEvent(); - const int glvis_event_code = 1; - event.user.code = glvis_event_code; - event.user.data1 = nullptr; - event.user.data2 = nullptr; - SDL_PushEvent(&event); - } + main_thread.SendEvent(); } void SdlWindow::getWindowSize(int& w, int& h) @@ -1213,6 +1218,7 @@ void SdlWindow::setWindowTitle(const char * title) void SdlWindow::setWindowSize(int w, int h) { main_thread.SetWindowSize(handle.get(), pixel_scale_x*w, pixel_scale_y*h); + swap_before_expose = true; } void SdlWindow::setWindowPos(int x, int y) @@ -1224,6 +1230,7 @@ void SdlWindow::setWindowPos(int x, int y) main_thread.SetWindowPosition(handle.get(), uc_x ? x : pixel_scale_x*x, uc_y ? y : pixel_scale_y*y); + swap_before_expose = true; } void SdlWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) diff --git a/lib/sdl.hpp b/lib/sdl.hpp index a66a8f34..dfe6b5f5 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -60,8 +60,6 @@ class SdlWindow // scaled "screen coordinates" on all high-dpi displays. float pixel_scale_x = 1.0f, pixel_scale_y = 1.0f; - std::unique_ptr platform; - bool running; IdleDelegate onIdle{nullptr}; @@ -92,6 +90,8 @@ class SdlWindow RenderState wnd_state{RenderState::Updated}; + bool swap_before_expose{false}; + //bool requiresExpose; bool takeScreenshot{false}; std::string screenshot_file; From d42bf14a44e5c948f7cd50123258139611af054d Mon Sep 17 00:00:00 2001 From: Max Yang Date: Fri, 28 May 2021 16:03:39 -0700 Subject: [PATCH 07/47] Make SdlWindow::Handle a move-only type, hold it directly as a value --- lib/sdl.cpp | 159 ++++++++++++++++++++++++---------------------------- lib/sdl.hpp | 38 +++++++++++-- 2 files changed, 107 insertions(+), 90 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 6abafc45..a0cb2013 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -48,47 +48,38 @@ using std::endl; extern int GetMultisample(); -struct SdlWindow::Handle +SdlWindow::Handle::Handle(const std::string& title, int x, int y, int w, int h, + Uint32 wndflags) + : hwnd(nullptr) + , gl_ctx(0) { - SDL_Window * hwnd; - SDL_GLContext gl_ctx; - Handle(const std::string& title, int x, int y, int w, int h, - Uint32 wndflags) - : hwnd(nullptr) - , gl_ctx(0) - { - hwnd = SDL_CreateWindow(title.c_str(), x, y, w, h, wndflags); - if (!hwnd) - { - PRINT_DEBUG("SDL window creation failed with error: " - << SDL_GetError() << endl); - return; - } - gl_ctx = SDL_GL_CreateContext(hwnd); - if (!gl_ctx) - { - PRINT_DEBUG("OpenGL context creation failed with error: " - << SDL_GetError() << endl); - } + hwnd = SDL_CreateWindow(title.c_str(), x, y, w, h, wndflags); + if (!hwnd) + { + PRINT_DEBUG("SDL window creation failed with error: " + << SDL_GetError() << endl); + return; } - - ~Handle() + gl_ctx = SDL_GL_CreateContext(hwnd); + if (!gl_ctx) { - if (gl_ctx) - { - SDL_GL_DeleteContext(gl_ctx); - } - if (hwnd) - { - SDL_DestroyWindow(hwnd); - } + PRINT_DEBUG("OpenGL context creation failed with error: " + << SDL_GetError() << endl); } +} - bool isInitialized() +SdlWindow::Handle::~Handle() +{ + if (gl_ctx) { - return (hwnd != nullptr && gl_ctx != 0); + SDL_GL_DeleteContext(gl_ctx); } -}; + if (hwnd) + { + SDL_DestroyWindow(hwnd); + } +} + struct SdlWindow::MainThread { @@ -141,10 +132,10 @@ struct SdlWindow::MainThread createWindowImpl(*cmd.cmd_create); break; case SdlCmdType::Delete: - if (cmd.cmd_delete) + if (cmd.cmd_delete.isInitialized()) { - unique_ptr to_delete = std::move(cmd.cmd_delete); - int wnd_id = SDL_GetWindowID(to_delete->hwnd); + Handle to_delete = std::move(cmd.cmd_delete); + int wnd_id = SDL_GetWindowID(to_delete.hwnd); hwnd_to_window.erase(wnd_id); wnd_events.erase(wnd_id); num_windows--; @@ -266,12 +257,11 @@ struct SdlWindow::MainThread // Executed from a window worker thread. Returns a handle to a new window // and associated OpenGL context. - std::unique_ptr GetHandle(SdlWindow* wnd, - const std::string& title, int x, int y, - int w, int h, bool legacyGlOnly) + Handle GetHandle(SdlWindow* wnd, const std::string& title, + int x, int y, int w, int h, bool legacyGlOnly) { CreateWindowCmd cmd_create = { wnd, title, x, y, w, h, legacyGlOnly, false }; - future> res_handle = cmd_create.out_handle.get_future(); + future res_handle = cmd_create.out_handle.get_future(); SdlCtrlCommand main_thread_cmd; main_thread_cmd.type = SdlCmdType::Create; @@ -285,21 +275,21 @@ struct SdlWindow::MainThread // Wake up the main thread to create our window if (platform) { platform->SendEvent(); } - unique_ptr out_hnd = res_handle.get(); - if (out_hnd->isInitialized()) + Handle out_hnd = res_handle.get(); + if (out_hnd.isInitialized()) { // Make the OpenGL context current on the worker thread. // Since SDL calls aren't guaranteed to be thread-safe, we guard // the call to SDL_GL_MakeCurrent. lock_guard ctx_lock{gl_ctx_mtx}; - SDL_GL_MakeCurrent(out_hnd->hwnd, out_hnd->gl_ctx); + SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx); } return out_hnd; } // Executed from a window worker thread. Deletes a handle to a window and // the corresponding OpenGL context. - void DeleteHandle(std::unique_ptr to_delete) + void DeleteHandle(Handle to_delete) { SdlCtrlCommand main_thread_cmd; main_thread_cmd.type = SdlCmdType::Delete; @@ -309,33 +299,33 @@ struct SdlWindow::MainThread } // Issues a command on the main thread to set the window title. - void SetWindowTitle(Handle* handle, std::string title) + void SetWindowTitle(const Handle& handle, std::string title) { SdlCtrlCommand main_thread_cmd; main_thread_cmd.type = SdlCmdType::SetTitle; - main_thread_cmd.handle = handle; + main_thread_cmd.handle = &handle; main_thread_cmd.cmd_title = std::move(title); queueWindowEvent(std::move(main_thread_cmd)); } // Issues a command on the main thread to set the window size. - void SetWindowSize(Handle* handle, int w, int h) + void SetWindowSize(const Handle& handle, int w, int h) { SdlCtrlCommand main_thread_cmd; main_thread_cmd.type = SdlCmdType::SetSize; - main_thread_cmd.handle = handle; + main_thread_cmd.handle = &handle; main_thread_cmd.cmd_set_size = {w, h}; queueWindowEvent(std::move(main_thread_cmd)); } // Issues a command on the main thread to set the window position. - void SetWindowPosition(Handle* handle, int x, int y) + void SetWindowPosition(const Handle& handle, int x, int y) { SdlCtrlCommand main_thread_cmd; main_thread_cmd.type = SdlCmdType::SetPosition; - main_thread_cmd.handle = handle; + main_thread_cmd.handle = &handle; main_thread_cmd.cmd_set_position = {x, y}; queueWindowEvent(std::move(main_thread_cmd)); @@ -354,7 +344,7 @@ struct SdlWindow::MainThread int x, y, w, h; bool legacy_gl_only; bool window_create_executed; - promise> out_handle; + promise out_handle; }; enum class SdlCmdType @@ -371,9 +361,9 @@ struct SdlWindow::MainThread { SdlCmdType type {SdlCmdType::None}; - Handle* handle {nullptr}; + const Handle* handle {nullptr}; CreateWindowCmd* cmd_create; - unique_ptr cmd_delete; + Handle cmd_delete; string cmd_title; pair cmd_set_size; pair cmd_set_position; @@ -396,7 +386,7 @@ struct SdlWindow::MainThread void probeGLContextSupport(bool legacyGlOnly); - void getDpi(Handle* handle, int& wdpi, int& hdpi); + void getDpi(const Handle& handle, int& wdpi, int& hdpi); void createWindowImpl(CreateWindowCmd& cmd); @@ -432,7 +422,7 @@ SdlWindow::MainThread SdlWindow::main_thread{}; bool SdlWindow::isGlInitialized() { - return (handle->gl_ctx != 0); + return (handle.gl_ctx != 0); } SdlWindow::SdlWindow() {} @@ -548,16 +538,16 @@ void SdlWindow::MainThread::probeGLContextSupport(bool legacyGlOnly) } const int default_dpi = 72; -void SdlWindow::MainThread::getDpi(Handle* handle, int& w, int& h) +void SdlWindow::MainThread::getDpi(const Handle& handle, int& w, int& h) { w = default_dpi; h = default_dpi; - if (!handle) + if (!handle.isInitialized()) { PRINT_DEBUG("warning: unable to get dpi: handle is null" << endl); return; } - int disp = SDL_GetWindowDisplayIndex(handle->hwnd); + int disp = SDL_GetWindowDisplayIndex(handle.hwnd); if (disp < 0) { PRINT_DEBUG("warning: problem getting display index: " << SDL_GetError() @@ -591,13 +581,11 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24); probeGLContextSupport(cmd.legacy_gl_only); - unique_ptr new_handle; - new_handle.reset(new Handle(cmd.title, cmd.x, cmd.y, cmd.w, cmd.h, - win_flags)); + Handle new_handle(cmd.title, cmd.x, cmd.y, cmd.w, cmd.h, win_flags); cmd.window_create_executed = true; // at this point, window should be up - if (!new_handle->isInitialized()) + if (!new_handle.isInitialized()) { PRINT_DEBUG("failed." << endl); cerr << "FATAL: window and/or OpenGL context creation failed." << endl; @@ -614,14 +602,14 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) #endif // Register window internally in the main thread so it can receive events - int wnd_id = SDL_GetWindowID(new_handle->hwnd); + int wnd_id = SDL_GetWindowID(new_handle.hwnd); hwnd_to_window[wnd_id] = cmd.wnd; if (!platform) { SDL_SysWMinfo sysinfo; SDL_VERSION(&sysinfo.version); - if (!SDL_GetWindowWMInfo(new_handle->hwnd, &sysinfo)) + if (!SDL_GetWindowWMInfo(new_handle.hwnd, &sysinfo)) { sysinfo.subsystem = SDL_SYSWM_UNKNOWN; } @@ -669,7 +657,7 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) 0xFF000000); if (iconSurf) { - SDL_SetWindowIcon(new_handle->hwnd, iconSurf); + SDL_SetWindowIcon(new_handle.hwnd, iconSurf); SDL_FreeSurface(iconSurf); } else @@ -681,13 +669,12 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) // Detect if we are using a high-dpi display and resize the window unless it // was already resized by SDL's underlying backend. { - Handle* handle = new_handle.get(); SdlWindow* wnd = cmd.wnd; int scr_w, scr_h, pix_w, pix_h, wdpi, hdpi; // SDL_GetWindowSize() -- size in "screen coordinates" - SDL_GetWindowSize(handle->hwnd, &scr_w, &scr_h); + SDL_GetWindowSize(new_handle.hwnd, &scr_w, &scr_h); // SDL_GL_GetDrawableSize() -- size in pixels - SDL_GL_GetDrawableSize(handle->hwnd, &pix_w, &pix_h); + SDL_GL_GetDrawableSize(new_handle.hwnd, &pix_w, &pix_h); wnd->high_dpi = false; wnd->pixel_scale_x = wnd->pixel_scale_y = 1.0f; float sdl_pixel_scale_x = 1.0f, sdl_pixel_scale_y = 1.0f; @@ -695,20 +682,20 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) // need to scale the window. if (scr_w == pix_w && scr_h == pix_h) { - getDpi(handle, wdpi, hdpi); + getDpi(new_handle, wdpi, hdpi); if (std::max(wdpi, hdpi) >= high_dpi_threshold) { wnd->high_dpi = true; wnd->pixel_scale_x = wnd->pixel_scale_y = 2.0f; // the following two calls use 'pixel_scale_*' - SDL_SetWindowSize(handle->hwnd, + SDL_SetWindowSize(new_handle.hwnd, wnd->pixel_scale_x*cmd.w, wnd->pixel_scale_y*cmd.h); bool uc_x = SDL_WINDOWPOS_ISUNDEFINED(cmd.x) || SDL_WINDOWPOS_ISCENTERED(cmd.x); bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(cmd.y) || SDL_WINDOWPOS_ISCENTERED(cmd.y); - SDL_SetWindowPosition(handle->hwnd, + SDL_SetWindowPosition(new_handle.hwnd, uc_x ? cmd.x : wnd->pixel_scale_x*cmd.x, uc_y ? cmd.y : wnd->pixel_scale_y*cmd.y); } @@ -731,10 +718,10 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) // Unset GL context in this thread { lock_guard ctx_lock{gl_ctx_mtx}; - SDL_GL_MakeCurrent(new_handle->hwnd, nullptr); + SDL_GL_MakeCurrent(new_handle.hwnd, nullptr); } - SDL_ShowWindow(new_handle->hwnd); + SDL_ShowWindow(new_handle.hwnd); if (num_windows == -1) { num_windows = 0; @@ -751,7 +738,7 @@ bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, legacyGlOnly); // at this point, window should be up - if (!handle->isInitialized()) + if (!handle.isInitialized()) { return false; } @@ -1096,7 +1083,7 @@ void SdlWindow::mainIter() // back buffer. if (swap_before_expose) { - SDL_GL_SwapWindow(handle->hwnd); + SDL_GL_SwapWindow(handle.hwnd); swap_before_expose = false; } #endif @@ -1119,7 +1106,7 @@ void SdlWindow::mainLoop() mainIter(); if (wnd_state == RenderState::SwapPending) { - SDL_GL_SwapWindow(handle->hwnd); + SDL_GL_SwapWindow(handle.hwnd); wnd_state = RenderState::Updated; } if (takeScreenshot) @@ -1141,7 +1128,7 @@ void SdlWindow::getWindowSize(int& w, int& h) { w = 0; h = 0; - if (handle) + if (handle.isInitialized()) { #ifdef __EMSCRIPTEN__ if (canvas_id_.empty()) @@ -1160,7 +1147,7 @@ void SdlWindow::getWindowSize(int& w, int& h) return; } #else - SDL_GetWindowSize(handle->hwnd, &w, &h); + SDL_GetWindowSize(handle.hwnd, &w, &h); #endif w /= pixel_scale_x; h /= pixel_scale_y; @@ -1169,19 +1156,19 @@ void SdlWindow::getWindowSize(int& w, int& h) void SdlWindow::getGLDrawSize(int& w, int& h) { - SDL_GL_GetDrawableSize(handle->hwnd, &w, &h); + SDL_GL_GetDrawableSize(handle.hwnd, &w, &h); } void SdlWindow::getDpi(int& w, int& h) { w = default_dpi; h = default_dpi; - if (!handle) + if (!handle.isInitialized()) { PRINT_DEBUG("warning: unable to get dpi: handle is null" << endl); return; } - int disp = SDL_GetWindowDisplayIndex(handle->hwnd); + int disp = SDL_GetWindowDisplayIndex(handle.hwnd); if (disp < 0) { PRINT_DEBUG("warning: problem getting display index: " << SDL_GetError() @@ -1212,12 +1199,12 @@ void SdlWindow::setWindowTitle(std::string& title) void SdlWindow::setWindowTitle(const char * title) { - main_thread.SetWindowTitle(handle.get(), title); + main_thread.SetWindowTitle(handle, title); } void SdlWindow::setWindowSize(int w, int h) { - main_thread.SetWindowSize(handle.get(), pixel_scale_x*w, pixel_scale_y*h); + main_thread.SetWindowSize(handle, pixel_scale_x*w, pixel_scale_y*h); swap_before_expose = true; } @@ -1227,7 +1214,7 @@ void SdlWindow::setWindowPos(int x, int y) SDL_WINDOWPOS_ISCENTERED(x); bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(y); - main_thread.SetWindowPosition(handle.get(), + main_thread.SetWindowPosition(handle, uc_x ? x : pixel_scale_x*x, uc_y ? y : pixel_scale_y*y); swap_before_expose = true; @@ -1252,5 +1239,5 @@ void SdlWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) void SdlWindow::swapBuffer() { - SDL_GL_SwapWindow(handle->hwnd); + SDL_GL_SwapWindow(handle.hwnd); } diff --git a/lib/sdl.hpp b/lib/sdl.hpp index dfe6b5f5..19d31883 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -39,12 +39,42 @@ typedef bool (*IdleDelegate)(); class SdlWindow { private: - struct Handle; + struct Handle + { + SDL_Window * hwnd{nullptr}; + SDL_GLContext gl_ctx{}; + + Handle() = default; + + Handle(const std::string& title, int x, int y, int w, int h, + Uint32 wndflags); + + ~Handle(); + + Handle(Handle&& other) + : hwnd(other.hwnd), gl_ctx(other.gl_ctx) + { + other.hwnd = nullptr; + other.gl_ctx = 0; + } + + Handle& operator= (Handle&& other) + { + std::swap(hwnd, other.hwnd); + std::swap(gl_ctx, other.gl_ctx); + return *this; + } + + bool isInitialized() const + { + return (hwnd != nullptr && gl_ctx != 0); + } + }; struct MainThread; static MainThread main_thread; - std::unique_ptr handle; + Handle handle; std::unique_ptr renderer; static const int high_dpi_threshold = 144; // The display is high-dpi when: @@ -207,8 +237,8 @@ class SdlWindow void swapBuffer(); - operator bool() { return (bool) handle ; } - bool isWindowInitialized() { return (bool) handle; } + operator bool() { return handle.isInitialized(); } + bool isWindowInitialized() { return handle.isInitialized(); } /// Returns true if the OpenGL context was successfully initialized. bool isGlInitialized(); From b7621398cfabf1cd91f2a9d98abcc1ef9a6f5dc9 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Tue, 1 Jun 2021 10:29:19 -0700 Subject: [PATCH 08/47] Initial commit of a thread-based server mode --- glvis.cpp | 529 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 291 insertions(+), 238 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 46857756..2b7887d2 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -842,11 +842,23 @@ void PlayScript(istream &scr) script = &scr; stream_state.keys.clear(); - if (GLVisInitVis((stream_state.grid_f->VectorDim() == 1) ? 0 : 1)) + std::thread worker_thread { - GetAppWindow()->setOnKeyDown(SDLK_SPACE, ScriptControl); - GLVisStartVis(); - } + [&](StreamState local_state) + { + // set the thread-local StreamState + stream_state = std::move(local_state); + if (GLVisInitVis((stream_state.grid_f->VectorDim() == 1) ? 0 : 1)) + { + GetAppWindow()->setOnKeyDown(SDLK_SPACE, ScriptControl); + GLVisStartVis(); + } + }, + std::move(stream_state) + }; + + SDLMainLoop(); + worker_thread.join(); delete init_nodes; init_nodes = NULL; @@ -856,11 +868,278 @@ void PlayScript(istream &scr) script = NULL; } +struct Session +{ + Array input_streams; + StreamState state; + int ft = -1; + std::thread handler; + + Session(bool fix_elem_orient, + bool save_coloring) + { + state.fix_elem_orient = fix_elem_orient; + state.save_coloring = save_coloring; + } + + ~Session() + { + if (handler.joinable()) + { + handler.join(); + } + for (int i = 0; i < input_streams.Size(); i++) + { + socketstream *sock = dynamic_cast(input_streams[i]); + if (sock) { sock->rdbuf()->socketbuf::close(); } + delete input_streams[i]; + } + input_streams.DeleteAll(); + } + + Session(Session&& from) + : state(std::move(from.state)) + , ft(from.ft) + , handler(std::move(from.handler)) + { + Swap(input_streams, from.input_streams); + from.ft = -1; + from.state = {}; + from.handler = {}; + } + + Session& operator= (Session&& from) + { + Swap(input_streams, from.input_streams); + std::swap(state, from.state); + std::swap(ft, from.ft); + std::swap(handler, from.handler); + return *this; + } + + void StartSession() + { + auto funcThread = + [&](StreamState state, int ft, Array is) + { + stream_state = std::move(state); + input_streams = is; + + if (GLVisInitVis(ft)) + { + GLVisStartVis(); + } + }; + handler = std::thread {funcThread, std::move(state), ft, input_streams}; + } + + void StartSavedSession(std::string stream_file) + { + } +}; + +void GLVisServer(int portnum, bool mac, bool fix_elem_orient, + bool save_coloring) +{ + std::vector current_sessions; + string data_type; + int viscount = 0, nproc = 1, proc = 0; + +#ifdef MFEM_USE_GNUTLS + unique_ptr state; + unique_ptr params; + if (secure) + { + state.reset(new GnuTLS_global_state); + // state->set_log_level(1000); + string home_dir(getenv("HOME")); + string server_dir = home_dir + "/.config/glvis/server/"; +#ifndef MFEM_USE_GNUTLS_X509 + string pubkey = server_dir + "pubring.gpg"; + string privkey = server_dir + "secring.gpg"; + string trustedkeys = server_dir + "trusted-clients.gpg"; +#else + string pubkey = server_dir + "cert.pem"; + string privkey = server_dir + "key.pem"; + string trustedkeys = server_dir + "trusted-clients.pem"; +#endif + params.reset(new GnuTLS_session_params( + *state, pubkey.c_str(), privkey.c_str(), + trustedkeys.c_str(), GNUTLS_SERVER)); + if (!params->status.good()) + { + cout << " public key = " << pubkey << '\n' + << " private key = " << privkey << '\n' + << " trusted keys = " << trustedkeys << endl; + cout << "Error setting GLVis server parameters.\n" + "Generate your GLVis keys with:" + " bash glvis-keygen.sh [\"Your Name\"] [\"Your Email\"]" + << endl; + return 3; + } + } +#endif + + const int backlog = 128; + socketserver server(portnum, backlog); + if (server.good()) + { + cout << "Waiting for data on port " << portnum << " ..." << endl; + } + else + { + cout << "Server already running on port " << portnum << ".\n" << endl; + exit(2); + } + while (1) + { + + socketstream *isock; +#ifndef MFEM_USE_GNUTLS + isock = new socketstream; +#else + isock = secure ? new socketstream(*params) : new socketstream(false); +#endif + Array input_streams; + while (server.accept(*isock) < 0) + { +#ifdef GLVIS_DEBUG + cout << "GLVis: server.accept(...) failed." << endl; +#endif + } + + *isock >> data_type >> ws; + + if (mac) + { + viscount++; + } + + int par_data = 0; + if (data_type == "parallel") + { + par_data = 1; + np = 0; + do + { + *isock >> nproc >> proc; +#ifdef GLVIS_DEBUG + cout << "new connection: parallel " << nproc << ' ' << proc + << endl; +#endif + if (np == 0) + { + if (nproc <= 0) + { + cout << "Invalid number of processors: " << nproc << endl; + mfem_error(); + } + input_streams.SetSize(nproc); + input_streams = NULL; + } + else + { + if (nproc != input_streams.Size()) + { + cout << "Unexpected number of processors: " << nproc + << ", expected: " << input_streams.Size() << endl; + mfem_error(); + } + } + if (0 > proc || proc >= nproc) + { + cout << "Invalid processor rank: " << proc + << ", number of processors: " << nproc << endl; + mfem_error(); + } + if (input_streams[proc]) + { + cout << "Second connection attempt from processor rank: " + << proc << endl; + mfem_error(); + } + + input_streams[proc] = isock; +#ifndef MFEM_USE_GNUTLS + isock = new socketstream; +#else + isock = secure ? new socketstream(*params) : + new socketstream(false); +#endif + np++; + if (np == nproc) + { + break; + } + // read next available socket stream + while (server.accept(*isock) < 0) + { +#ifdef GLVIS_DEBUG + cout << "GLVis: server.accept(...) failed." << endl; +#endif + } + *isock >> data_type >> ws; // "parallel" + if (data_type != "parallel") + { + cout << "Expected keyword \"parallel\", got \"" << data_type + << '"' << endl; + mfem_error(); + } + } + while (1); + } + + Session new_session(fix_elem_orient, save_coloring); + + char tmp_file[50]; + if (mac) + { + sprintf(tmp_file,"glvis-saved.%04d",viscount); + ofstream ofs(tmp_file); + if (!par_data) + { + ofs << data_type << '\n'; + ofs << isock->rdbuf(); + isock->close(); + } + else + { + ReadInputStreams(new_session.state); + ofs.precision(8); + ofs << "solution\n"; + new_session.state.mesh->Print(ofs); + new_session.state.grid_f->Save(ofs); + } + ofs.close(); + cout << "Data saved in " << tmp_file << endl; + } + else + { + if (!par_data) + { + new_session.ft = new_session.state.ReadStream(*isock, data_type); + new_session.input_streams.Append(isock); + } + else + { + delete isock; + new_session.ft = ReadInputStreams(new_session.state); + } + // Pass ownership of input streams into session object + new_session.input_streams = input_streams; + input_streams.DeleteAll(); + + new_session.StartSession(); + } + current_sessions.emplace_back(std::move(new_session)); + isock = nullptr; + } +} int main (int argc, char *argv[]) { // variables for command line arguments - bool multi_session = true; // not added as option + bool multi_session = false; // not added as option bool mac = false; const char *stream_file = string_none; const char *script_file = string_none; @@ -1081,8 +1360,6 @@ int main (int argc, char *argv[]) } #endif - int childPID, viscount = 0, nproc = 1, proc = 0; - // server mode, read the mesh and the solution from a socket if (input == 1) { @@ -1092,239 +1369,15 @@ int main (int argc, char *argv[]) signal(SIGCHLD, SIG_IGN); } -#ifdef MFEM_USE_GNUTLS - GnuTLS_global_state *state = NULL; - GnuTLS_session_params *params = NULL; - if (secure) - { - state = new GnuTLS_global_state; - // state->set_log_level(1000); - string home_dir(getenv("HOME")); - string server_dir = home_dir + "/.config/glvis/server/"; -#ifndef MFEM_USE_GNUTLS_X509 - string pubkey = server_dir + "pubring.gpg"; - string privkey = server_dir + "secring.gpg"; - string trustedkeys = server_dir + "trusted-clients.gpg"; -#else - string pubkey = server_dir + "cert.pem"; - string privkey = server_dir + "key.pem"; - string trustedkeys = server_dir + "trusted-clients.pem"; -#endif - params = new GnuTLS_session_params( - *state, pubkey.c_str(), privkey.c_str(), - trustedkeys.c_str(), GNUTLS_SERVER); - if (!params->status.good()) - { - cout << " public key = " << pubkey << '\n' - << " private key = " << privkey << '\n' - << " trusted keys = " << trustedkeys << endl; - cout << "Error setting GLVis server parameters.\n" - "Generate your GLVis keys with:" - " bash glvis-keygen.sh [\"Your Name\"] [\"Your Email\"]" - << endl; - delete params; delete state; - return 3; - } - } -#endif + // Run server in new thread + std::thread serverThread{GLVisServer, portnum, mac, + stream_state.fix_elem_orient, + stream_state.save_coloring}; - const int backlog = 128; - socketserver server(portnum, backlog); - if (server.good()) - { - cout << "Waiting for data on port " << portnum << " ..." << endl; - } - else - { - cout << "Server already running on port " << portnum << ".\n" << endl; -#ifdef MFEM_USE_GNUTLS - delete params; delete state; -#endif - return 2; - } + // Start SDL in main thread + SDLMainLoop(true); - socketstream *isock; -#ifndef MFEM_USE_GNUTLS - isock = new socketstream; -#else - isock = secure ? new socketstream(*params) : new socketstream(false); -#endif - while (1) - { - while (server.accept(*isock) < 0) - { -#ifdef GLVIS_DEBUG - cout << "GLVis: server.accept(...) failed." << endl; -#endif - } - - *isock >> data_type >> ws; - - if (mac) - { - viscount++; - } - - int par_data = 0; - if (data_type == "parallel") - { - par_data = 1; - np = 0; - do - { - *isock >> nproc >> proc; -#ifdef GLVIS_DEBUG - cout << "new connection: parallel " << nproc << ' ' << proc - << endl; -#endif - if (np == 0) - { - if (nproc <= 0) - { - cout << "Invalid number of processors: " << nproc << endl; - mfem_error(); - } - input_streams.SetSize(nproc); - input_streams = NULL; - } - else - { - if (nproc != input_streams.Size()) - { - cout << "Unexpected number of processors: " << nproc - << ", expected: " << input_streams.Size() << endl; - mfem_error(); - } - } - if (0 > proc || proc >= nproc) - { - cout << "Invalid processor rank: " << proc - << ", number of processors: " << nproc << endl; - mfem_error(); - } - if (input_streams[proc]) - { - cout << "Second connection attempt from processor rank: " - << proc << endl; - mfem_error(); - } - - input_streams[proc] = isock; -#ifndef MFEM_USE_GNUTLS - isock = new socketstream; -#else - isock = secure ? new socketstream(*params) : - new socketstream(false); -#endif - np++; - if (np == nproc) - { - break; - } - // read next available socket stream - while (server.accept(*isock) < 0) - { -#ifdef GLVIS_DEBUG - cout << "GLVis: server.accept(...) failed." << endl; -#endif - } - *isock >> data_type >> ws; // "parallel" - if (data_type != "parallel") - { - cout << "Expected keyword \"parallel\", got \"" << data_type - << '"' << endl; - mfem_error(); - } - } - while (1); - } - - char tmp_file[50]; - if (multi_session) - { - if (mac) - { - sprintf(tmp_file,"glvis-saved.%04d",viscount); - ofstream ofs(tmp_file); - if (!par_data) - { - ofs << data_type << '\n'; - ofs << isock->rdbuf(); - isock->close(); - } - else - { - ReadInputStreams(stream_state); - CloseInputStreams(false); - ofs.precision(8); - ofs << "solution\n"; - stream_state.mesh->Print(ofs); - stream_state.grid_f->Save(ofs); - } - ofs.close(); - cout << "Data saved in " << tmp_file << endl; - } - childPID = fork(); - } - else - { - childPID = 0; - } - - switch (childPID) - { - case -1: - cout << "The process couldn't fork. Exit." << endl; - exit(1); - - case 0: // This is the child process - server.close(); - if (mac) - { - // exec ourself - const char *args[4] = { argv[0], "-saved", tmp_file, NULL }; - execve(args[0], (char* const*)args, environ); - exit(0); - } - else - { - if (multi_session) - { - signal(SIGINT, SIG_IGN); - } - int ft; - if (!par_data) - { - ft = stream_state.ReadStream(*isock, data_type); - input_streams.Append(isock); - } - else - { - delete isock; - ft = ReadInputStreams(stream_state); - } - if (GLVisInitVis(ft)) - { - GLVisStartVis(); - } - CloseInputStreams(false); - exit(0); - } - - default : // This is the parent process - if (!par_data) - { - isock->rdbuf()->socketbuf::close(); - } - else - { - CloseInputStreams(true); - } - } - } -#ifdef MFEM_USE_GNUTLS - delete params; delete state; -#endif + serverThread.detach(); } else // input != 1, non-server mode { From ed1044387acbb9e563ae3521ba967cf73d805267 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Tue, 1 Jun 2021 10:50:47 -0700 Subject: [PATCH 09/47] Re-enable -mac command for server mode, use Session object for non-server mode --- glvis.cpp | 56 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 2b7887d2..8d400e43 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -882,6 +882,11 @@ struct Session state.save_coloring = save_coloring; } + Session(int other_ft, StreamState other_state) + : state(std::move(other_state)) + , ft(other_ft) + { } + ~Session() { if (handler.joinable()) @@ -933,8 +938,21 @@ struct Session handler = std::thread {funcThread, std::move(state), ft, input_streams}; } - void StartSavedSession(std::string stream_file) + bool StartSavedSession(std::string stream_file) { + ifstream ifs(stream_file); + if (!ifs) + { + cout << "Can not open stream file: " << stream_file << endl; + return false; + } + string data_type; + ifs >> data_type >> ws; + ft = state.ReadStream(ifs, data_type); + input_streams.Append(&ifs); + + StartSession(); + return true; } }; @@ -1112,6 +1130,8 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, } ofs.close(); cout << "Data saved in " << tmp_file << endl; + + new_session.StartSavedSession(tmp_file); } else { @@ -1298,33 +1318,15 @@ int main (int argc, char *argv[]) // check for saved stream file if (stream_file != string_none) { - ifstream ifs(stream_file); - if (!ifs) + Session stream_session(stream_state.fix_elem_orient, + stream_state.save_coloring); + + if (!stream_session.StartSavedSession(stream_file)) { - cout << "Can not open stream file: " << stream_file << endl; return 1; } - ifs >> data_type >> ws; - int ft = stream_state.ReadStream(ifs, data_type); - input_streams.Append(&ifs); - std::thread worker_thread - { - [&](StreamState local_state, Array is) - { - // set the thread-local StreamState and input streams - input_streams = is; - stream_state = std::move(local_state); - if (GLVisInitVis(ft)) - { - GLVisStartVis(); - } - }, - std::move(stream_state), - input_streams - }; SDLMainLoop(); - worker_thread.join(); return 0; } @@ -1401,10 +1403,10 @@ int main (int argc, char *argv[]) { field_type = (use_soln) ? 0 : 2; } - if (GLVisInitVis(field_type)) - { - GLVisStartVis(); - } + Session single_session(field_type, std::move(stream_state)); + single_session.StartSession(); + + SDLMainLoop(); } cout << "Thank you for using GLVis." << endl; From 8dfef72646ca64fc7aa287f5dfc865ba9f4c4e4a Mon Sep 17 00:00:00 2001 From: Max Yang Date: Thu, 3 Jun 2021 11:59:23 -0700 Subject: [PATCH 10/47] Fixes for X11, split syswm code into new file to avoid macro conflicts --- lib/CMakeLists.txt | 3 +- lib/sdl.cpp | 37 ++----------- lib/sdl_helper.cpp | 52 ++++++++++++++++++ lib/sdl_helper.hpp | 12 +++++ lib/sdl_mac.mm | 2 +- lib/sdl_x11.cpp | 128 +++++++++++++++++++++++++++++++++++++++++++++ lib/sdl_x11.hpp | 87 +++--------------------------- makefile | 6 +-- 8 files changed, 209 insertions(+), 118 deletions(-) create mode 100644 lib/sdl_helper.cpp create mode 100644 lib/sdl_x11.cpp diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a02c0b3e..7d51c160 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -20,6 +20,7 @@ list(APPEND SOURCES openglvis.cpp palettes.cpp sdl.cpp + sdl_helper.cpp stream_reader.cpp vsdata.cpp vssolution.cpp @@ -71,7 +72,7 @@ if(EMSCRIPTEN) target_link_libraries(libglvis PUBLIC "${_glvis_libraries}") else() # Desktop build target - list(APPEND SOURCES gl/renderer_ff.cpp threads.cpp gl2ps.c) + 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) if (APPLE) list(APPEND SOURCES sdl_mac.mm) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index a0cb2013..23e18408 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -30,11 +30,6 @@ #if defined(SDL_VIDEO_DRIVER_COCOA) #include "sdl_mac.hpp" #endif -#ifndef __EMSCRIPTEN__ -#include -#else -#include -#endif using std::cerr; @@ -135,6 +130,7 @@ struct SdlWindow::MainThread if (cmd.cmd_delete.isInitialized()) { Handle to_delete = std::move(cmd.cmd_delete); + platform->UnregisterWindow(to_delete.hwnd); int wnd_id = SDL_GetWindowID(to_delete.hwnd); hwnd_to_window.erase(wnd_id); wnd_events.erase(wnd_id); @@ -607,36 +603,9 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) if (!platform) { - SDL_SysWMinfo sysinfo; - SDL_VERSION(&sysinfo.version); - if (!SDL_GetWindowWMInfo(new_handle.hwnd, &sysinfo)) - { - sysinfo.subsystem = SDL_SYSWM_UNKNOWN; - } - switch (sysinfo.subsystem) - { -#if defined(SDL_VIDEO_DRIVER_X11) - case SDL_SYSWM_X11: - { - // Disable XInput extension events since they are generated even - // outside the GLVis window. - Display *dpy = sysinfo.info.x11.display; - Window win = sysinfo.info.x11.window; - platform.reset(new SdlX11Platform(dpy, win)); - } - break; -#elif defined(SDL_VIDEO_DRIVER_COCOA) - case SDL_SYSWM_COCOA: - { - platform.reset(new SdlCocoaPlatform); - } - break; -#endif - default: - // unhandled window manager - break; - } + platform = SdlNativePlatform::Create(new_handle.hwnd); } + platform->RegisterWindow(new_handle.hwnd); const int PixelStride = 4; int stride = (int) sqrt(logo_rgba_len / PixelStride); diff --git a/lib/sdl_helper.cpp b/lib/sdl_helper.cpp new file mode 100644 index 00000000..709531f0 --- /dev/null +++ b/lib/sdl_helper.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2010-2021, 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 + +#include "sdl_helper.hpp" + +#ifndef __EMSCRIPTEN__ +#include +#else +#include +#endif + +#include "sdl_x11.hpp" +#include "sdl_mac.hpp" + +using namespace std; + +std::unique_ptr +SdlNativePlatform::Create(SDL_Window* window) +{ + SDL_SysWMinfo sysinfo; + SDL_VERSION(&sysinfo.version); + if (!SDL_GetWindowWMInfo(window, &sysinfo)) + { + cerr << "Error: unable to get window manager information for the " + << "current window." << endl; + return {}; + } + switch (sysinfo.subsystem) + { +#if defined(SDL_VIDEO_DRIVER_X11) + case SDL_SYSWM_X11: + return std::unique_ptr {new SdlX11Platform}; +#elif defined(SDL_VIDEO_DRIVER_COCOA) + case SDL_SYSWM_COCOA: + return std::unique_ptr {new SdlCocoaPlatform}; +#endif + case SDL_SYSWM_UNKNOWN: + default: + cerr << "Error: unrecognized window manager system." << endl; + return {}; + } +} diff --git a/lib/sdl_helper.hpp b/lib/sdl_helper.hpp index e97fea24..bf60dcaf 100644 --- a/lib/sdl_helper.hpp +++ b/lib/sdl_helper.hpp @@ -12,10 +12,22 @@ #ifndef GLVIS_SDL_HELPER_HPP #define GLVIS_SDL_HELPER_HPP +#include "gl/platform_gl.hpp" +#include + class SdlNativePlatform { public: + static std::unique_ptr Create(SDL_Window* window); + virtual ~SdlNativePlatform() = default; + + // Registers the window handle with this platform handler, in order to + // wait for events. This is needed for X11, which has one event pipe + // per window. + virtual void RegisterWindow(SDL_Window* window) { } + // Unregisters the window handle. + virtual void UnregisterWindow(SDL_Window* window) { } // SDL_WaitEvent only polls for events and sleeps, instead of actually // blocking. This method calls the system-native blocking event pump. virtual void WaitEvent() = 0; diff --git a/lib/sdl_mac.mm b/lib/sdl_mac.mm index 82f4591b..0593c677 100644 --- a/lib/sdl_mac.mm +++ b/lib/sdl_mac.mm @@ -9,8 +9,8 @@ // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. -#import #include "sdl_mac.hpp" +#import void SdlCocoaPlatform::WaitEvent() { @autoreleasepool diff --git a/lib/sdl_x11.cpp b/lib/sdl_x11.cpp new file mode 100644 index 00000000..bdf316e4 --- /dev/null +++ b/lib/sdl_x11.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2010-2021, 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 "sdl_x11.hpp" + +#ifdef SDL_VIDEO_DRIVER_X11 + +#include +#include +#include + +#ifndef __EMSCRIPTEN__ +#include +#else +#include +#endif + +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 +#include +#endif // SDL_VIDEO_DRIVER_X11_XINPUT2 + +using namespace std; + +void SdlX11Platform::RegisterWindow(SDL_Window* window) +{ + SDL_SysWMinfo sysinfo; + SDL_VERSION(&sysinfo.version); + if (!SDL_GetWindowWMInfo(window, &sysinfo)) + { + cerr << "Error: unable to get window manager information for the " + << "current window." << endl; + return; + } + if (sysinfo.subsystem != SDL_SYSWM_X11) + { + cerr << "Error: created SDL window is not an X11 window." << endl; + return; + } + Display *disp = sysinfo.info.x11.display; + Window wnd = sysinfo.info.x11.window; + + display_fds.emplace(window, ConnectionNumber(disp)); + +#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 + // Disable XInput extension events since they are generated even outside + // the GLVis window. + Window root_win = DefaultRootWindow(disp); + unsigned char mask[4] = {0,0,0,0}; + XIEventMask event_mask; + event_mask.deviceid = XIAllMasterDevices; + event_mask.mask_len = sizeof(mask); + event_mask.mask = mask; +#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 + const char Xi_lib[] = SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2; +#else + const char Xi_lib[] = "libXi.so"; +#endif + typedef int (*XISelectEvents_ptr)(Display *, Window, XIEventMask *, int); + XISelectEvents_ptr XISelectEvents_ = NULL; + void *lib = SDL_LoadObject(Xi_lib); + if (lib != NULL) + { + XISelectEvents_ = + (XISelectEvents_ptr)SDL_LoadFunction(lib, "XISelectEvents"); + } + if (XISelectEvents_ == NULL) + { + cerr << "Error accessing XISelectEvents!" << endl; + exit(EXIT_FAILURE); + } + if (XISelectEvents_(disp, root_win, &event_mask, 1) != Success) + { + cerr << "Failed to disable XInput on the default root window!" << endl; + } + if (XISelectEvents_(disp, wnd, &event_mask, 1) != Success) + { + cerr << "Failed to disable XInput on the current window!" << endl; + } +#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 + SDL_UnloadObject(lib); +#endif +#endif // SDL_VIDEO_DRIVER_X11_XINPUT2 +} + +void SdlX11Platform::UnregisterWindow(SDL_Window* window) +{ + display_fds.erase(window); +} + +void SdlX11Platform::WaitEvent() +{ + vector pfd; + pfd.emplace_back( pollfd{ event_pfd[0], POLLIN, 0 } ); + + for (auto wnd : display_fds) + { + // add file descriptors for the window event queue + pfd.emplace_back( pollfd{ wnd.second, POLLIN, 0 } ); + } + + int nstr; + do + { + nstr = poll(pfd.data(), pfd.size(), -1); + } + while (nstr == -1 && errno == EINTR); + + if (nstr == -1) { perror("poll()"); } + + int n = 0; + // Read out the pending GLVisCommand-sent events, if any + do + { + array buf; + n = read(event_pfd[0], buf.data(), buf.size()); + } + while (n > 0); +} + +#endif // SDL_VIDEO_DRIVER_X11 diff --git a/lib/sdl_x11.hpp b/lib/sdl_x11.hpp index aaa5c301..7ba9cc2f 100644 --- a/lib/sdl_x11.hpp +++ b/lib/sdl_x11.hpp @@ -12,66 +12,23 @@ #ifndef GLVIS_SDL_X11_HPP #define GLVIS_SDL_X11_HPP -#ifdef SDL_VIDEO_DRIVER_X11 - #include "sdl_helper.hpp" #include "gl/platform_gl.hpp" + +#include + #include #include // pipe, fcntl, write #include // fcntl #include // errno, EAGAIN #include // perror -#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 -#include -#endif // SDL_VIDEO_DRIVER_X11_XINPUT2 class SdlX11Platform final : public SdlNativePlatform { public: - SdlX11Platform(Display* xdisplay, Window xwindow) - : disp(xdisplay), wnd(xwindow) + SdlX11Platform() { -#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 - // Disable XInput extension events since they are generated even outside - // the GLVis window. - Window root_win = DefaultRootWindow(disp); - unsigned char mask[4] = {0,0,0,0}; - XIEventMask event_mask; - event_mask.deviceid = XIAllMasterDevices; - event_mask.mask_len = sizeof(mask); - event_mask.mask = mask; -#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 - const char Xi_lib[] = SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2; -#else - const char Xi_lib[] = "libXi.so"; -#endif - typedef int (*XISelectEvents_ptr)(Display *, Window, XIEventMask *, int); - XISelectEvents_ptr XISelectEvents_ = NULL; - void *lib = SDL_LoadObject(Xi_lib); - if (lib != NULL) - { - XISelectEvents_ = - (XISelectEvents_ptr)SDL_LoadFunction(lib, "XISelectEvents"); - } - if (XISelectEvents_ == NULL) - { - cerr << "Error accessing XISelectEvents!" << endl; - exit(EXIT_FAILURE); - } - if (XISelectEvents_(disp, root_win, &event_mask, 1) != Success) - { - cerr << "Failed to disable XInput on the default root window!" << endl; - } - if (XISelectEvents_(disp, wnd, &event_mask, 1) != Success) - { - cerr << "Failed to disable XInput on the current window!" << endl; - } -#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 - SDL_UnloadObject(lib); -#endif -#endif // SDL_VIDEO_DRIVER_X11_XINPUT2 - // Create pipe for external events if (pipe(event_pfd) == -1) { @@ -88,37 +45,12 @@ class SdlX11Platform final : public SdlNativePlatform close(event_pfd[1]); } - void WaitEvent() - { - constexpr int nfd = 2; - int nstr; - struct pollfd pfd[nfd]; - - pfd[0].fd = ConnectionNumber(disp); - pfd[0].events = POLLIN; - pfd[0].revents = 0; + void RegisterWindow(SDL_Window* window); - pfd[1].fd = event_pfd[0]; - pfd[1].events = POLLIN; - pfd[1].revents = 0; + void UnregisterWindow(SDL_Window* window); - do - { - nstr = poll(pfd, nfd, -1); - } - while (nstr == -1 && errno == EINTR); + void WaitEvent(); - if (nstr == -1) { perror("poll()"); } - - int n = 0; - // Read out the pending GLVisCommand-sent events, if any - do - { - std::array buf; - n = read(event_pfd[0], buf.data(), buf.size()); - } - while (n > 0); - } void SendEvent() { char c = 's'; @@ -130,11 +62,8 @@ class SdlX11Platform final : public SdlNativePlatform } private: - Display* disp; - Window wnd; int event_pfd[2]; // pfd[0] -- reading, pfd[1] -- writing + std::unordered_map display_fds; }; -#endif // SDL_VIDEO_DRIVER_X11 - #endif // GLVIS_SDL_X11_HPP diff --git a/makefile b/makefile index 3c1a2a1a..748a68ae 100644 --- a/makefile +++ b/makefile @@ -215,10 +215,10 @@ 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/font.cpp \ lib/gl2ps.c lib/material.cpp lib/openglvis.cpp lib/palettes.cpp lib/sdl.cpp \ - lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp lib/vssolution.cpp \ - lib/vssolution3d.cpp lib/vsvector.cpp lib/vsvector3d.cpp + lib/sdl_helper.cpp lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp \ + lib/vssolution.cpp lib/vssolution3d.cpp lib/vsvector.cpp lib/vsvector3d.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 +DESKTOP_ONLY_SOURCE_FILES = lib/gl/renderer_ff.cpp lib/threads.cpp lib/gl2ps.c lib/sdl_x11.cpp WEB_ONLY_SOURCE_FILES = lib/aux_js.cpp LOGO_FILE = share/logo.rgba LOGO_FILE_CPP = $(LOGO_FILE).bin.cpp From 12a24f754ad5e642de8b1fb1302e2b409eb0d7a1 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Thu, 3 Jun 2021 13:22:36 -0700 Subject: [PATCH 11/47] Fixing build issue on gcc 4.9.3 --- lib/aux_vis.hpp | 2 +- lib/openglvis.hpp | 2 +- lib/sdl.cpp | 2 +- lib/sdl.hpp | 1 + lib/vsdata.hpp | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 389f27d0..ef3f6e41 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -15,9 +15,9 @@ #include "gl/platform_gl.hpp" #include "gl/types.hpp" +#include "openglvis.hpp" #include "sdl.hpp" #include "font.hpp" -#include "openglvis.hpp" void SDLMainLoop(bool server_mode = false); diff --git a/lib/openglvis.hpp b/lib/openglvis.hpp index a23bf93a..bab4e07d 100644 --- a/lib/openglvis.hpp +++ b/lib/openglvis.hpp @@ -13,12 +13,12 @@ #define GLVIS_OPENGLVIS_HPP #include -#include "sdl.hpp" #include "gl/types.hpp" #include "material.hpp" #include "palettes.hpp" #include "mfem.hpp" #include "geom_utils.hpp" +#include "sdl.hpp" // Visualization header file diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 23e18408..821018db 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -13,12 +13,12 @@ #include #include #include -#include "sdl.hpp" #include "threads.hpp" #include "aux_vis.hpp" #include "logo.hpp" #include "gl/renderer_core.hpp" #include "gl/renderer_ff.hpp" +#include "sdl.hpp" #ifdef __EMSCRIPTEN__ #include #include diff --git a/lib/sdl.hpp b/lib/sdl.hpp index 19d31883..f59373af 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "gl/renderer.hpp" #include "sdl_helper.hpp" diff --git a/lib/vsdata.hpp b/lib/vsdata.hpp index c5fbd3c5..cf26ab38 100644 --- a/lib/vsdata.hpp +++ b/lib/vsdata.hpp @@ -14,8 +14,8 @@ #include -#include "openglvis.hpp" #include "mfem.hpp" +#include "openglvis.hpp" using namespace mfem; From d6c75f3c226b977cebe057085e6b1a7a3e3279f3 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Fri, 4 Jun 2021 17:34:53 -0700 Subject: [PATCH 12/47] Fix save file opening; remove global input_streams variable --- glvis.cpp | 50 ++++++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 8d400e43..ca9b06f0 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -71,8 +71,6 @@ int scr_level = 0; Vector *init_nodes = NULL; double scr_min_val, scr_max_val; -thread_local Array input_streams; - extern char **environ; void PrintSampleUsage(ostream &out); @@ -90,13 +88,11 @@ int ReadParMeshAndGridFunction(int np, const char *mesh_prefix, const char *sol_prefix, StreamState& state, int keep_attr); -int ReadInputStreams(StreamState& state); - -void CloseInputStreams(bool); +int ReadInputStreams(StreamState& state, const Array& input_streams); // Visualize the data in the global variables mesh, sol/grid_f, etc // 0 - scalar data, 1 - vector data, 2 - mesh only, (-1) - unknown -bool GLVisInitVis(int field_type) +bool GLVisInitVis(int field_type, const Array& input_streams) { if (field_type < 0 || field_type > 2) { @@ -250,7 +246,7 @@ void GLVisStartVis() { RunVisualization(); // deletes vs vs = NULL; - if (input_streams.Size() > 0) + if (glvis_command) { glvis_command->Terminate(); delete comm_thread; @@ -848,7 +844,7 @@ void PlayScript(istream &scr) { // set the thread-local StreamState stream_state = std::move(local_state); - if (GLVisInitVis((stream_state.grid_f->VectorDim() == 1) ? 0 : 1)) + if (GLVisInitVis((stream_state.grid_f->VectorDim() == 1) ? 0 : 1, {})) { GetAppWindow()->setOnKeyDown(SDLK_SPACE, ScriptControl); GLVisStartVis(); @@ -870,6 +866,7 @@ void PlayScript(istream &scr) struct Session { + ifstream save_file; Array input_streams; StreamState state; int ft = -1; @@ -895,8 +892,6 @@ struct Session } for (int i = 0; i < input_streams.Size(); i++) { - socketstream *sock = dynamic_cast(input_streams[i]); - if (sock) { sock->rdbuf()->socketbuf::close(); } delete input_streams[i]; } input_streams.DeleteAll(); @@ -925,12 +920,12 @@ struct Session void StartSession() { auto funcThread = - [&](StreamState state, int ft, Array is) + [&](StreamState state, int ft, const Array& is) { + // Set thread-local stream state stream_state = std::move(state); - input_streams = is; - if (GLVisInitVis(ft)) + if (GLVisInitVis(ft, is)) { GLVisStartVis(); } @@ -940,16 +935,16 @@ struct Session bool StartSavedSession(std::string stream_file) { - ifstream ifs(stream_file); - if (!ifs) + save_file.open(stream_file); + if (!save_file) { cout << "Can not open stream file: " << stream_file << endl; return false; } string data_type; - ifs >> data_type >> ws; - ft = state.ReadStream(ifs, data_type); - input_streams.Append(&ifs); + save_file >> data_type >> ws; + ft = state.ReadStream(save_file, data_type); + input_streams.Append(&save_file); StartSession(); return true; @@ -1122,7 +1117,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, } else { - ReadInputStreams(new_session.state); + ReadInputStreams(new_session.state, input_streams); ofs.precision(8); ofs << "solution\n"; new_session.state.mesh->Print(ofs); @@ -1143,7 +1138,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, else { delete isock; - new_session.ft = ReadInputStreams(new_session.state); + new_session.ft = ReadInputStreams(new_session.state, input_streams); } // Pass ownership of input streams into session object new_session.input_streams = input_streams; @@ -1652,7 +1647,7 @@ int ReadParMeshAndGridFunction(int np, const char *mesh_prefix, return err; } -int ReadInputStreams(StreamState& state) +int ReadInputStreams(StreamState& state, const Array& input_streams) { int nproc = input_streams.Size(); Array mesh_array(nproc); @@ -1725,16 +1720,3 @@ int ReadInputStreams(StreamState& state) return field_type; } -void CloseInputStreams(bool parent) -{ - for (int i = 0; i < input_streams.Size(); i++) - { - if (parent) - { - socketstream *sock = dynamic_cast(input_streams[i]); - if (sock) { sock->rdbuf()->socketbuf::close(); } - } - delete input_streams[i]; - } - input_streams.DeleteAll(); -} From 4c5cdb7ebb2a967449c55f79853a4ef4af4579a8 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Tue, 25 May 2021 11:03:04 -0700 Subject: [PATCH 13/47] Show prompt for terminating server thread --- lib/sdl.cpp | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 821018db..76eab5d6 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -169,10 +169,7 @@ struct SdlWindow::MainThread switch (e.type) { case SDL_QUIT: - if (!server_mode) - { - terminating = true; - } + terminating = exitDialog(); break; case SDL_WINDOWEVENT: windowId = getWindowID(e.window); @@ -374,6 +371,36 @@ struct SdlWindow::MainThread if (platform) { platform->SendEvent(); } } + bool exitDialog() + { + const int ID_Exit = 1; + const int ID_Continue = 2; + const SDL_MessageBoxButtonData buttons[] = + { + {0, ID_Exit, "Yes"}, + {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, ID_Continue, "No"} + }; + + const SDL_MessageBoxData request_box + { + SDL_MESSAGEBOX_INFORMATION, + nullptr, + "GLVis Server", + "Stop GLVis server thread?", + 2, + buttons, + nullptr + }; + + int button_id; + if (SDL_ShowMessageBox(&request_box, &button_id) < 0) + { + return true; + } + + return button_id == ID_Exit; + } + template Uint32 getWindowID(const T& eventStruct) { From 238bfc5ad71655f1dc0edf9fa2bd62899bdb5699 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 11:00:54 -0700 Subject: [PATCH 14/47] Use vector> to manage input stream ownership --- glvis.cpp | 77 +++++++++++++++++-------------------------------- lib/threads.cpp | 50 ++++++++++++++++---------------- lib/threads.hpp | 7 +++-- 3 files changed, 57 insertions(+), 77 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index ca9b06f0..7c4b10ba 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -73,6 +73,8 @@ double scr_min_val, scr_max_val; extern char **environ; +using StreamCollection = vector>; + void PrintSampleUsage(ostream &out); // read the mesh and the solution from a file @@ -88,11 +90,11 @@ int ReadParMeshAndGridFunction(int np, const char *mesh_prefix, const char *sol_prefix, StreamState& state, int keep_attr); -int ReadInputStreams(StreamState& state, const Array& input_streams); +int ReadInputStreams(StreamState& state, const StreamCollection& input_streams); // Visualize the data in the global variables mesh, sol/grid_f, etc // 0 - scalar data, 1 - vector data, 2 - mesh only, (-1) - unknown -bool GLVisInitVis(int field_type, const Array& input_streams) +bool GLVisInitVis(int field_type, StreamCollection input_streams) { if (field_type < 0 || field_type > 2) { @@ -108,11 +110,11 @@ bool GLVisInitVis(int field_type, const Array& input_streams) return false; } - if (input_streams.Size() > 0) + if (input_streams.size() > 0) { GetAppWindow()->setOnKeyDown(SDLK_SPACE, ThreadsPauseFunc); glvis_command = new GLVisCommand(&vs, stream_state, &keep_attr); - comm_thread = new communication_thread(input_streams, glvis_command); + comm_thread = new communication_thread(std::move(input_streams), glvis_command); } double mesh_range = -1.0; @@ -866,8 +868,7 @@ void PlayScript(istream &scr) struct Session { - ifstream save_file; - Array input_streams; + StreamCollection input_streams; StreamState state; int ft = -1; std::thread handler; @@ -890,61 +891,40 @@ struct Session { handler.join(); } - for (int i = 0; i < input_streams.Size(); i++) - { - delete input_streams[i]; - } - input_streams.DeleteAll(); } - Session(Session&& from) - : state(std::move(from.state)) - , ft(from.ft) - , handler(std::move(from.handler)) - { - Swap(input_streams, from.input_streams); - from.ft = -1; - from.state = {}; - from.handler = {}; - } - - Session& operator= (Session&& from) - { - Swap(input_streams, from.input_streams); - std::swap(state, from.state); - std::swap(ft, from.ft); - std::swap(handler, from.handler); - return *this; - } + Session(Session&& from) = default; + Session& operator= (Session&& from) = default; void StartSession() { auto funcThread = - [&](StreamState state, int ft, const Array& is) + [&](StreamState state, int ft, StreamCollection is) { // Set thread-local stream state stream_state = std::move(state); - if (GLVisInitVis(ft, is)) + if (GLVisInitVis(ft, std::move(is))) { GLVisStartVis(); } }; - handler = std::thread {funcThread, std::move(state), ft, input_streams}; + handler = std::thread {funcThread, + std::move(state), ft, std::move(input_streams)}; } bool StartSavedSession(std::string stream_file) { - save_file.open(stream_file); - if (!save_file) + ifstream ifs(stream_file); + if (!ifs) { cout << "Can not open stream file: " << stream_file << endl; return false; } string data_type; - save_file >> data_type >> ws; - ft = state.ReadStream(save_file, data_type); - input_streams.Append(&save_file); + ifs >> data_type >> ws; + ft = state.ReadStream(ifs, data_type); + input_streams.emplace_back(new ifstream{std::move(ifs)}); StartSession(); return true; @@ -1013,7 +993,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, #else isock = secure ? new socketstream(*params) : new socketstream(false); #endif - Array input_streams; + vector> input_streams; while (server.accept(*isock) < 0) { #ifdef GLVIS_DEBUG @@ -1047,15 +1027,14 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, cout << "Invalid number of processors: " << nproc << endl; mfem_error(); } - input_streams.SetSize(nproc); - input_streams = NULL; + input_streams.resize(nproc); } else { - if (nproc != input_streams.Size()) + if (nproc != input_streams.size()) { cout << "Unexpected number of processors: " << nproc - << ", expected: " << input_streams.Size() << endl; + << ", expected: " << input_streams.size() << endl; mfem_error(); } } @@ -1072,7 +1051,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, mfem_error(); } - input_streams[proc] = isock; + input_streams[proc].reset(isock); #ifndef MFEM_USE_GNUTLS isock = new socketstream; #else @@ -1133,7 +1112,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, if (!par_data) { new_session.ft = new_session.state.ReadStream(*isock, data_type); - new_session.input_streams.Append(isock); + new_session.input_streams.emplace_back(isock); } else { @@ -1141,9 +1120,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, new_session.ft = ReadInputStreams(new_session.state, input_streams); } // Pass ownership of input streams into session object - new_session.input_streams = input_streams; - input_streams.DeleteAll(); - + new_session.input_streams = std::move(input_streams); new_session.StartSession(); } current_sessions.emplace_back(std::move(new_session)); @@ -1647,9 +1624,9 @@ int ReadParMeshAndGridFunction(int np, const char *mesh_prefix, return err; } -int ReadInputStreams(StreamState& state, const Array& input_streams) +int ReadInputStreams(StreamState& state, const StreamCollection& input_streams) { - int nproc = input_streams.Size(); + int nproc = input_streams.size(); Array mesh_array(nproc); Array gf_array(nproc); string data_type; diff --git a/lib/threads.cpp b/lib/threads.cpp index 67acb194..c86c44c8 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -712,14 +712,14 @@ GLVisCommand::~GLVisCommand() } } -communication_thread::communication_thread(const Array &_is, +communication_thread::communication_thread(StreamCollection _is, GLVisCommand* cmd) - : is(_is), glvis_command(cmd) + : is(std::move(_is)), glvis_command(cmd) { new_m = NULL; new_g = NULL; - if (is.Size() > 0) + if (is.size() > 0) { tid = std::thread(&communication_thread::execute, this); } @@ -727,7 +727,7 @@ communication_thread::communication_thread(const Array &_is, communication_thread::~communication_thread() { - if (is.Size() > 0) + if (is.size() > 0) { terminate_thread = true; tid.join(); @@ -843,7 +843,7 @@ void communication_thread::execute() *is[0] >> ws >> filename; // all processors sent the screenshot command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'screenshot' *is[i] >> ws >> ident; // filename @@ -861,7 +861,7 @@ void communication_thread::execute() *is[0] >> ws >> keys; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'keys' *is[i] >> ws >> ident; // keys @@ -879,7 +879,7 @@ void communication_thread::execute() *is[0] >> w >> h; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'window_size' *is[i] >> t >> t; @@ -897,7 +897,7 @@ void communication_thread::execute() *is[0] >> x >> y >> w >> h; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'window_geometry' *is[i] >> t >> t >> t >> t; @@ -918,7 +918,7 @@ void communication_thread::execute() getline(*is[0], title, c); // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'window_title' *is[i] >> ws >> c; @@ -940,7 +940,7 @@ void communication_thread::execute() getline(*is[0], caption, c); // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'plot_caption' *is[i] >> ws >> c; @@ -966,7 +966,7 @@ void communication_thread::execute() getline(*is[0], label_z, c); // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'axis_label' *is[i] >> ws >> c; @@ -987,7 +987,7 @@ void communication_thread::execute() else if (ident == "pause") { // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'pause' } @@ -1004,7 +1004,7 @@ void communication_thread::execute() *is[0] >> theta >> phi; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'view' *is[i] >> a >> a; @@ -1022,7 +1022,7 @@ void communication_thread::execute() *is[0] >> factor; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'zoom' *is[i] >> a; @@ -1040,7 +1040,7 @@ void communication_thread::execute() *is[0] >> tot >> bdr; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'subdivisions' *is[i] >> a >> a; @@ -1058,7 +1058,7 @@ void communication_thread::execute() *is[0] >> minv >> maxv; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'valuerange' *is[i] >> a >> a; @@ -1076,7 +1076,7 @@ void communication_thread::execute() *is[0] >> ws >> shd; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'shading' *is[i] >> ws >> ident; @@ -1094,7 +1094,7 @@ void communication_thread::execute() *is[0] >> x >> y; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'viewcenter' *is[i] >> a >> a; @@ -1112,7 +1112,7 @@ void communication_thread::execute() *is[0] >> ws >> mode; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'autoscale' *is[i] >> ws >> ident; @@ -1130,7 +1130,7 @@ void communication_thread::execute() *is[0] >> pal; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'palette' *is[i] >> a; @@ -1148,7 +1148,7 @@ void communication_thread::execute() *is[0] >> n; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'palette_repeat' *is[i] >> a; @@ -1169,7 +1169,7 @@ void communication_thread::execute() } // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'camera' for (int j = 0; j < 9; j++) @@ -1190,7 +1190,7 @@ void communication_thread::execute() *is[0] >> ws >> mode; // all processors sent the command - for (int i = 1; i < is.Size(); i++) + for (int i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'autopause' *is[i] >> ws >> ident; @@ -1210,9 +1210,9 @@ void communication_thread::execute() cout << "Stream: end of input." << endl; comm_terminate: - for (int i = 0; i < is.Size(); i++) + for (int i = 0; i < is.size(); i++) { - socketstream *isock = dynamic_cast(is[i]); + socketstream *isock = dynamic_cast(is[i].get()); if (isock) { isock->close(); diff --git a/lib/threads.hpp b/lib/threads.hpp index a458e1ec..0fddb1d6 100644 --- a/lib/threads.hpp +++ b/lib/threads.hpp @@ -138,9 +138,12 @@ class GLVisCommand class communication_thread { +public: + using StreamCollection = std::vector>; + private: // streams to read data from - Array is; + StreamCollection is; GLVisCommand* glvis_command; @@ -157,7 +160,7 @@ class communication_thread void execute(); public: - communication_thread(const Array &_is, GLVisCommand* cmd); + communication_thread(StreamCollection _is, GLVisCommand* cmd); ~communication_thread(); }; From b3df197c3484345feef774dd25dd824477efefc9 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 11:20:40 -0700 Subject: [PATCH 15/47] Remove last of the POSIX dependencies --- CMakeLists.txt | 12 +++++++----- glvis.cpp | 7 ------- lib/aux_vis.cpp | 7 ++----- lib/sdl_x11.hpp | 5 ++++- lib/stream_reader.cpp | 6 ++++-- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eff6b060..05250512 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -204,12 +204,14 @@ if (NOT EMSCRIPTEN) endif (FONTCONFIG_LIBRARY) # Find threading library - set(CMAKE_THREAD_PREFER_PTHREAD ON) find_package(Threads REQUIRED) - if (NOT CMAKE_USE_PTHREADS_INIT) - message(FATAL_ERROR "The required pthreads library was not found.") - else() - list(APPEND _glvis_libraries "${CMAKE_THREAD_LIBS_INIT}") + list(APPEND _glvis_libraries "${CMAKE_THREAD_LIBS_INIT}") + if (CMAKE_USE_PTHREADS_INIT) + message(STATUS "System threading library: pthreads") + elseif (CMAKE_USE_WIN32_THREADS_INIT) + message(STATUS "System threading library: Win32 threads") + elseif (Threads_FOUND) + message(STATUS "System threading library: other") endif() else(NOT EMSCRIPTEN) diff --git a/glvis.cpp b/glvis.cpp index 7c4b10ba..628010c3 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include @@ -1337,12 +1336,6 @@ int main (int argc, char *argv[]) // server mode, read the mesh and the solution from a socket if (input == 1) { - // get rid of zombies - if (multi_session) - { - signal(SIGCHLD, SIG_IGN); - } - // Run server in new thread std::thread serverThread{GLVisServer, portnum, mac, stream_state.fix_elem_orient, diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 469269c8..ebcf8e83 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include "mfem.hpp" using namespace mfem; @@ -509,7 +509,6 @@ thread_local int constrained_spinning = 0; void MainLoop() { static int p = 1; - struct timespec req; if (locscene->spinning) { if (!constrained_spinning) @@ -522,9 +521,7 @@ void MainLoop() locscene->PreRotate(xang, 0.0, 0.0, 1.0); SendExposeEvent(); } - req.tv_sec = 0; - req.tv_nsec = 10000000; - nanosleep (&req, NULL); // sleep for 0.01 seconds + std::this_thread::sleep_for(std::chrono::milliseconds{10}); // sleep for 0.01 seconds } if (locscene->movie) { diff --git a/lib/sdl_x11.hpp b/lib/sdl_x11.hpp index 7ba9cc2f..502d1d79 100644 --- a/lib/sdl_x11.hpp +++ b/lib/sdl_x11.hpp @@ -13,7 +13,8 @@ #define GLVIS_SDL_X11_HPP #include "sdl_helper.hpp" -#include "gl/platform_gl.hpp" + +#ifdef SDL_VIDEO_DRIVER_X11 #include @@ -66,4 +67,6 @@ class SdlX11Platform final : public SdlNativePlatform std::unordered_map display_fds; }; +#endif // SDL_VIDEO_DRIVER_X11 + #endif // GLVIS_SDL_X11_HPP diff --git a/lib/stream_reader.cpp b/lib/stream_reader.cpp index 24ba3d95..4c32e176 100644 --- a/lib/stream_reader.cpp +++ b/lib/stream_reader.cpp @@ -12,6 +12,8 @@ #include "stream_reader.hpp" #include "visual.hpp" +#include + using namespace std; using namespace mfem; @@ -83,8 +85,8 @@ void StreamState::SetMeshSolution() grid_f->MakeOwner(cfec); { Array coloring; - srandom(time(0)); - double a = double(random()) / (double(RAND_MAX) + 1.); + srand(time(0)); + double a = double(rand()) / (double(RAND_MAX) + 1.); int el0 = (int)floor(a * mesh->GetNE()); cout << "Generating coloring starting with element " << el0+1 << " / " << mesh->GetNE() << endl; From e43155c54662dcdb91d51fd2970a917d4e23590a Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 11:32:06 -0700 Subject: [PATCH 16/47] Fix case where platform is nullptr --- lib/sdl.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 76eab5d6..a8670fc0 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -130,7 +130,10 @@ struct SdlWindow::MainThread if (cmd.cmd_delete.isInitialized()) { Handle to_delete = std::move(cmd.cmd_delete); - platform->UnregisterWindow(to_delete.hwnd); + if (platform) + { + platform->UnregisterWindow(to_delete.hwnd); + } int wnd_id = SDL_GetWindowID(to_delete.hwnd); hwnd_to_window.erase(wnd_id); wnd_events.erase(wnd_id); @@ -632,7 +635,10 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) { platform = SdlNativePlatform::Create(new_handle.hwnd); } - platform->RegisterWindow(new_handle.hwnd); + if (platform) + { + platform->RegisterWindow(new_handle.hwnd); + } const int PixelStride = 4; int stride = (int) sqrt(logo_rgba_len / PixelStride); From 8e9a8d46926d2d8f1f51ce13355c48eaa8bff175 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 11:36:59 -0700 Subject: [PATCH 17/47] Only prompt for exit in server mode --- lib/sdl.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index a8670fc0..e5571163 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -172,7 +172,14 @@ struct SdlWindow::MainThread switch (e.type) { case SDL_QUIT: - terminating = exitDialog(); + if (server_mode) + { + terminating = exitDialog(); + } + else + { + terminating = true; + } break; case SDL_WINDOWEVENT: windowId = getWindowID(e.window); From 1c7eebc3b0358fa059a067c9a93af14ce226e5a4 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 12:58:42 -0700 Subject: [PATCH 18/47] Final build fixups for Windows builds - Don't build logo when we're on Windows - Use imported SDL2 targets when possible - needed for correct DLL handling - Don't use M_PI constant --- CMakeLists.txt | 22 ++++++++++++++++++---- glvis.cpp | 2 -- lib/gl/types.cpp | 2 ++ lib/logo.hpp | 4 ++++ lib/openglvis.cpp | 1 + lib/palettes.cpp | 1 + lib/sdl.cpp | 2 ++ share/CMakeLists.txt | 4 ++++ 8 files changed, 32 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05250512..3d31d702 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,9 @@ set(_glvis_libraries) list(APPEND _glvis_compile_defs "GLVIS_MULTISAMPLE=${GLVIS_MULTISAMPLE}") list(APPEND _glvis_compile_defs "GLVIS_MS_LINEWIDTH=${GLVIS_MS_LINEWIDTH}") +if (NOT WIN32) + list(APPEND _glvis_compile_defs "GLVIS_USE_LOGO") +endif() if (CMAKE_BUILD_TYPE MATCHES "Debug|debug|DEBUG") list(APPEND _glvis_compile_defs "GLVIS_DEBUG") @@ -111,7 +114,9 @@ if (NOT EMSCRIPTEN) find_package(SDL2 QUIET HINTS ${SDL2_DIR}) # The SDL2 target 'SDL2::SDL2' is not always defined, so instead we use # directly 'SDL2_INCLUDE_DIRS' and 'SDL2_LIBRARIES'. - if (SDL2_INCLUDE_DIRS AND SDL2_LIBRARIES) + if (TARGET SDL2::SDL2) + list(APPEND _glvis_libraries SDL2::SDL2) + elseif (SDL2_INCLUDE_DIRS AND SDL2_LIBRARIES) # SDL2_INCLUDE_DIRS is defined with 'SDL2' at the end of the path we need to # strip that. set(new_list_) @@ -120,6 +125,8 @@ if (NOT EMSCRIPTEN) list(APPEND new_list_ "${new_path_}") endforeach() set(SDL2_INCLUDE_DIRS ${new_list_}) + list(APPEND _glvis_include_dirs "${SDL2_INCLUDE_DIRS}") + list(APPEND _glvis_libraries "${SDL2_LIBRARIES}") else() # find_package() did not work; try more direct approach. set(SDL2_DIR ${SDL2_DIR_SAVE}) @@ -134,9 +141,9 @@ if (NOT EMSCRIPTEN) if (NOT (SDL2_INCLUDE_DIRS AND SDL2_LIBRARIES)) message(FATAL_ERROR "SDL2 library not found. Please set SDL2_DIR.") endif() + list(APPEND _glvis_include_dirs "${SDL2_INCLUDE_DIRS}") + list(APPEND _glvis_libraries "${SDL2_LIBRARIES}") endif() - list(APPEND _glvis_include_dirs "${SDL2_INCLUDE_DIRS}") - list(APPEND _glvis_libraries "${SDL2_LIBRARIES}") message(STATUS "SDL2 found: ${SDL2_LIBRARIES}") message(STATUS "SDL2_INCLUDE_DIRS = ${SDL2_INCLUDE_DIRS}") @@ -276,7 +283,14 @@ if(NOT EMSCRIPTEN) add_executable(glvis-exe glvis.cpp) set_target_properties(glvis-exe PROPERTIES OUTPUT_NAME glvis) - target_link_libraries(glvis-exe PRIVATE glvis glvis_logo) + target_link_libraries(glvis-exe PRIVATE glvis) + + if (WIN32) + set_target_properties(glvis-exe PROPERTIES WIN32_EXECUTABLE TRUE) + target_link_libraries(glvis-exe PRIVATE SDL2::SDL2main) + else() + target_link_libraries(glvis-exe PRIVATE glvis_logo) + endif (WIN32) # Install the executable install(TARGETS glvis-exe RUNTIME DESTINATION bin) diff --git a/glvis.cpp b/glvis.cpp index 628010c3..2db3ec43 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -20,8 +20,6 @@ #include #include -#include - #include "mfem.hpp" #include "lib/palettes.hpp" #include "lib/visual.hpp" diff --git a/lib/gl/types.cpp b/lib/gl/types.cpp index a0bef475..b1f69dd9 100644 --- a/lib/gl/types.cpp +++ b/lib/gl/types.cpp @@ -24,6 +24,8 @@ void GlDrawable::addCone(float x, float y, float z, float phi = acos(vz/rhos); float theta = atan2 (vy, vx); + const double M_PI = glm::pi(); + glm::mat4 mtx(1.0); mtx = glm::translate(mtx, glm::vec3(x, y, z)); mtx = glm::rotate(mtx, theta, glm::vec3(0.f, 0.f, 1.f)); diff --git a/lib/logo.hpp b/lib/logo.hpp index 33412994..911f067e 100644 --- a/lib/logo.hpp +++ b/lib/logo.hpp @@ -12,7 +12,11 @@ #ifndef GLVIS_LOGO_HPP #define GLVIS_LOGO_HPP +#ifdef GLVIS_USE_LOGO + extern unsigned char logo_rgba[]; extern unsigned int logo_rgba_len; +#endif // GLVIS_USE_LOGO + #endif //__LOGO_HPP__ diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index 069a94d8..c5798b0a 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -450,6 +450,7 @@ void VisualizationScene::Zoom(double factor) } else { + const double M_PI = glm::pi(); double va = ViewAngle * ( M_PI / 360.0 ); ViewAngle = atan( tan( va ) / factor ) * (360.0 / M_PI); } diff --git a/lib/palettes.cpp b/lib/palettes.cpp index b9ff8e80..16584b4e 100644 --- a/lib/palettes.cpp +++ b/lib/palettes.cpp @@ -7400,6 +7400,7 @@ void Init_Palettes() static bool first_init = true; if (first_init) { + const double M_PI = std::atan(1) * 4; // init rainbow palette for (int i = 0; i < RGB_Palette_23_Size; i++) { diff --git a/lib/sdl.cpp b/lib/sdl.cpp index e5571163..4a0bac0f 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -647,6 +647,7 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) platform->RegisterWindow(new_handle.hwnd); } +#ifdef GLVIS_USE_LOGO const int PixelStride = 4; int stride = (int) sqrt(logo_rgba_len / PixelStride); if (unsigned(stride * stride * PixelStride) != logo_rgba_len) @@ -674,6 +675,7 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) PRINT_DEBUG("Unable to set window logo: " << SDL_GetError() << endl); } } +#endif // GLVIS_USE_LOGO // Detect if we are using a high-dpi display and resize the window unless it // was already resized by SDL's underlying backend. diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index 68b64365..912cf361 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -9,6 +9,8 @@ # terms of the BSD-3 license. We welcome feedback and contributions, see file # CONTRIBUTING.md for details. +if(NOT WIN32) + add_custom_command( OUTPUT logo.rgba.bin.cpp COMMAND ${CMAKE_COMMAND} -E copy @@ -20,3 +22,5 @@ add_custom_command( add_library(glvis_logo STATIC ${CMAKE_CURRENT_BINARY_DIR}/logo.rgba.bin.cpp) + +endif(NOT WIN32) From edc74241489dc2dee9e000fe9d1560da5fb993f6 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 13:05:56 -0700 Subject: [PATCH 19/47] Add HiDPI support for Windows --- CMakeLists.txt | 1 + share/windows.manifest | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 share/windows.manifest diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d31d702..7f078205 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,6 +287,7 @@ if(NOT EMSCRIPTEN) if (WIN32) set_target_properties(glvis-exe PROPERTIES WIN32_EXECUTABLE TRUE) + target_sources(glvis-exe PRIVATE "share/windows.manifest") target_link_libraries(glvis-exe PRIVATE SDL2::SDL2main) else() target_link_libraries(glvis-exe PRIVATE glvis_logo) diff --git a/share/windows.manifest b/share/windows.manifest new file mode 100644 index 00000000..f2708ecb --- /dev/null +++ b/share/windows.manifest @@ -0,0 +1,9 @@ + + + + + true + PerMonitorV2 + + + From fc3ad6d6a40dbdb765e8fe76f3b7663cae5ce18c Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 13:16:48 -0700 Subject: [PATCH 20/47] Add #ifdef guards for our custom M_PI --- lib/gl/types.cpp | 2 ++ lib/openglvis.cpp | 2 ++ lib/palettes.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/lib/gl/types.cpp b/lib/gl/types.cpp index b1f69dd9..8917f645 100644 --- a/lib/gl/types.cpp +++ b/lib/gl/types.cpp @@ -24,7 +24,9 @@ void GlDrawable::addCone(float x, float y, float z, float phi = acos(vz/rhos); float theta = atan2 (vy, vx); +#ifndef M_PI const double M_PI = glm::pi(); +#endif glm::mat4 mtx(1.0); mtx = glm::translate(mtx, glm::vec3(x, y, z)); diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index c5798b0a..83a81916 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -450,7 +450,9 @@ void VisualizationScene::Zoom(double factor) } else { +#ifndef M_PI const double M_PI = glm::pi(); +#endif double va = ViewAngle * ( M_PI / 360.0 ); ViewAngle = atan( tan( va ) / factor ) * (360.0 / M_PI); } diff --git a/lib/palettes.cpp b/lib/palettes.cpp index 16584b4e..c6d51a7c 100644 --- a/lib/palettes.cpp +++ b/lib/palettes.cpp @@ -7400,7 +7400,9 @@ void Init_Palettes() static bool first_init = true; if (first_init) { +#ifndef M_PI const double M_PI = std::atan(1) * 4; +#endif // init rainbow palette for (int i = 0; i < RGB_Palette_23_Size; i++) { From 2fb40e920ee2d7afd1064fe79718786324d08360 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 13:34:59 -0700 Subject: [PATCH 21/47] Fix build on gcc 4.9.3 + clean up warnings --- glvis.cpp | 24 ++++++++++++------------ lib/sdl.cpp | 4 ++-- lib/threads.cpp | 40 ++++++++++++++++++++-------------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 2db3ec43..d0aa94d0 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -37,7 +37,6 @@ const char *sol_file = string_none; const char *vec_sol_file = string_none; const char *gfunc_file = string_none; const char *arg_keys = string_none; -int np = 0; int pad_digits = 6; int gf_component = -1; bool keep_attr = false; @@ -81,7 +80,7 @@ void ReadSerial(StreamState& state); void SetGridFunction(StreamState& state); // read the mesh and the solution from multiple files -void ReadParallel(StreamState& state); +void ReadParallel(int np, StreamState& state); int ReadParMeshAndGridFunction(int np, const char *mesh_prefix, const char *sol_prefix, StreamState& state, @@ -912,16 +911,16 @@ struct Session bool StartSavedSession(std::string stream_file) { - ifstream ifs(stream_file); - if (!ifs) + unique_ptr ifs(new ifstream(stream_file)); + if (!(*ifs)) { cout << "Can not open stream file: " << stream_file << endl; return false; } string data_type; - ifs >> data_type >> ws; - ft = state.ReadStream(ifs, data_type); - input_streams.emplace_back(new ifstream{std::move(ifs)}); + *ifs >> data_type >> ws; + ft = state.ReadStream(*ifs, data_type); + input_streams.emplace_back(std::move(ifs)); StartSession(); return true; @@ -933,7 +932,8 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, { std::vector current_sessions; string data_type; - int viscount = 0, nproc = 1, proc = 0; + int viscount = 0; + unsigned int nproc = 1, proc = 0; #ifdef MFEM_USE_GNUTLS unique_ptr state; @@ -1009,7 +1009,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, if (data_type == "parallel") { par_data = 1; - np = 0; + unsigned int np = 0; do { *isock >> nproc >> proc; @@ -1128,7 +1128,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, int main (int argc, char *argv[]) { // variables for command line arguments - bool multi_session = false; // not added as option + int np = 0; bool mac = false; const char *stream_file = string_none; const char *script_file = string_none; @@ -1348,7 +1348,7 @@ int main (int argc, char *argv[]) { if (input & 256) { - ReadParallel(stream_state); + ReadParallel(np, stream_state); } else { @@ -1493,7 +1493,7 @@ void SetGridFunction(StreamState& state) } -void ReadParallel(StreamState& state) +void ReadParallel(int np, StreamState& state) { int err; diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 4a0bac0f..c9a30921 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -167,7 +167,7 @@ struct SdlWindow::MainThread SDL_Event e; while (SDL_PollEvent(&e)) { - unsigned int windowId = -1; + unsigned int windowId = UINT_MAX; bool sendToAll = false; switch (e.type) { @@ -221,7 +221,7 @@ struct SdlWindow::MainThread wnd_events[windowId].emplace_back(e); } } - else if (windowId != -1) + else if (windowId != UINT_MAX) { wnd_events[windowId].emplace_back(e); } diff --git a/lib/threads.cpp b/lib/threads.cpp index c86c44c8..34d9163f 100644 --- a/lib/threads.cpp +++ b/lib/threads.cpp @@ -843,7 +843,7 @@ void communication_thread::execute() *is[0] >> ws >> filename; // all processors sent the screenshot command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'screenshot' *is[i] >> ws >> ident; // filename @@ -861,7 +861,7 @@ void communication_thread::execute() *is[0] >> ws >> keys; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'keys' *is[i] >> ws >> ident; // keys @@ -879,7 +879,7 @@ void communication_thread::execute() *is[0] >> w >> h; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'window_size' *is[i] >> t >> t; @@ -897,7 +897,7 @@ void communication_thread::execute() *is[0] >> x >> y >> w >> h; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'window_geometry' *is[i] >> t >> t >> t >> t; @@ -918,7 +918,7 @@ void communication_thread::execute() getline(*is[0], title, c); // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'window_title' *is[i] >> ws >> c; @@ -940,7 +940,7 @@ void communication_thread::execute() getline(*is[0], caption, c); // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'plot_caption' *is[i] >> ws >> c; @@ -966,7 +966,7 @@ void communication_thread::execute() getline(*is[0], label_z, c); // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'axis_label' *is[i] >> ws >> c; @@ -987,7 +987,7 @@ void communication_thread::execute() else if (ident == "pause") { // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'pause' } @@ -1004,7 +1004,7 @@ void communication_thread::execute() *is[0] >> theta >> phi; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'view' *is[i] >> a >> a; @@ -1022,7 +1022,7 @@ void communication_thread::execute() *is[0] >> factor; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'zoom' *is[i] >> a; @@ -1040,7 +1040,7 @@ void communication_thread::execute() *is[0] >> tot >> bdr; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'subdivisions' *is[i] >> a >> a; @@ -1058,7 +1058,7 @@ void communication_thread::execute() *is[0] >> minv >> maxv; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'valuerange' *is[i] >> a >> a; @@ -1076,7 +1076,7 @@ void communication_thread::execute() *is[0] >> ws >> shd; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'shading' *is[i] >> ws >> ident; @@ -1094,7 +1094,7 @@ void communication_thread::execute() *is[0] >> x >> y; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'viewcenter' *is[i] >> a >> a; @@ -1112,7 +1112,7 @@ void communication_thread::execute() *is[0] >> ws >> mode; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'autoscale' *is[i] >> ws >> ident; @@ -1130,7 +1130,7 @@ void communication_thread::execute() *is[0] >> pal; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'palette' *is[i] >> a; @@ -1148,7 +1148,7 @@ void communication_thread::execute() *is[0] >> n; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'palette_repeat' *is[i] >> a; @@ -1169,7 +1169,7 @@ void communication_thread::execute() } // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'camera' for (int j = 0; j < 9; j++) @@ -1190,7 +1190,7 @@ void communication_thread::execute() *is[0] >> ws >> mode; // all processors sent the command - for (int i = 1; i < is.size(); i++) + for (size_t i = 1; i < is.size(); i++) { *is[i] >> ws >> ident; // 'autopause' *is[i] >> ws >> ident; @@ -1210,7 +1210,7 @@ void communication_thread::execute() cout << "Stream: end of input." << endl; comm_terminate: - for (int i = 0; i < is.size(); i++) + for (size_t i = 0; i < is.size(); i++) { socketstream *isock = dynamic_cast(is[i].get()); if (isock) From 2c286e78529e7010109416a3081824ef55895a10 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 15:28:15 -0700 Subject: [PATCH 22/47] Fix js build by enabling single-threaded mode --- CMakeLists.txt | 2 + lib/aux_js.cpp | 3 +- lib/aux_vis.cpp | 2 +- lib/sdl.cpp | 335 ++++++++++++++++++++++++++---------------------- lib/sdl.hpp | 9 +- 5 files changed, 194 insertions(+), 157 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f078205..a979d0ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,6 +232,8 @@ else(NOT EMSCRIPTEN) # OpenGL, SDL2, and GLEW are provided by the Emscripten runtime # Enable SDL2 list(APPEND _emscripten_opts "-s USE_SDL=2") + list(APPEND _emscripten_opts + "-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0") # Set WebGL options list(APPEND _emscripten_opts diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index e4b3b6e2..25e6930f 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -18,7 +18,8 @@ std::string plot_caption; std::string extra_caption; // used in extern context -mfem::GeometryRefiner GLVisGeometryRefiner; // used in extern context +thread_local mfem::GeometryRefiner +GLVisGeometryRefiner; // used in extern context static VisualizationSceneScalarData * vs = nullptr; StreamState stream_state; diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index ebcf8e83..895e2762 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -468,7 +468,7 @@ bool MainIdleFunc() LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size(); if (IdleFuncs[LastIdleFunc]) { - (*IdleFuncs[LastIdleFunc])(this); + (*IdleFuncs[LastIdleFunc])(); } // Continue executing idle functions sleep = false; diff --git a/lib/sdl.cpp b/lib/sdl.cpp index c9a30921..2e82b523 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -80,16 +80,6 @@ struct SdlWindow::MainThread { public: MainThread() - { - } - - bool SdlInitialized() const { return sdl_init; } - - Uint32 GetCustomEvent() const { return glvis_event_type; } - - // 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) { if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { @@ -110,6 +100,104 @@ struct SdlWindow::MainThread PRINT_DEBUG("Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor << "." << (int)sdl_ver.patch << std::endl); sdl_init = true; + } + + // If called, all SDL window operations are run immediately; this is for + // running in a single-threaded mode. + void SetSingleThread() { sdl_multithread = false; } + + bool SdlInitialized() const { return sdl_init; } + + Uint32 GetCustomEvent() const { return glvis_event_type; } + + // Dequeues all incoming events from SDL, and queues them up to their + // matching windows. + void DispatchSDLEvents() + { + SDL_Event e; + while (SDL_PollEvent(&e)) + { + unsigned int windowId = UINT_MAX; + bool sendToAll = false; + switch (e.type) + { + case SDL_QUIT: + if (server_mode) + { + terminating = exitDialog(); + } + else + { + terminating = true; + } + break; + case SDL_WINDOWEVENT: + windowId = getWindowID(e.window); + break; + case SDL_FINGERDOWN: + fingers.insert(e.tfinger.fingerId); + if (fingers.size() >= 2) { disable_mouse = true; } + break; + case SDL_FINGERUP: + fingers.erase(e.tfinger.fingerId); + if (fingers.size() < 2) { disable_mouse = false; } + break; + case SDL_MULTIGESTURE: + // No window ID is provided with multigesture events. We'll + // have to use focus status within the windows themselves in + // order to resolve the correct target. + sendToAll = true; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + windowId = getWindowID(e.key); + break; + case SDL_TEXTINPUT: + windowId = getWindowID(e.text); + break; + case SDL_MOUSEMOTION: + if (!disable_mouse) { windowId = getWindowID(e.motion); } + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + windowId = getWindowID(e.button); + break; + } + if (sendToAll == true) + { + for (auto wnds : hwnd_to_window) + { + int windowId = wnds.first; + wnd_events[windowId].emplace_back(e); + } + } + else if (windowId != UINT_MAX) + { + wnd_events[windowId].emplace_back(e); + } + } + // Send events to window worker threads + for (auto wnds : hwnd_to_window) + { + int windowId = wnds.first; + SdlWindow* wnd = wnds.second; + if (!wnd_events[windowId].empty()) + { + wnd->queueEvents(std::move(wnd_events[windowId])); + } + else + { + // Wake up the worker thread anyways, to execute onIdle + wnd->queueEvents({}); + } + } + } + + // 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) + { + this->server_mode = server_mode; while (1) { // Process all pending window commands @@ -121,126 +209,14 @@ struct SdlWindow::MainThread for (SdlCtrlCommand& cmd : pending_cmds) { - switch (cmd.type) - { - case SdlCmdType::Create: - createWindowImpl(*cmd.cmd_create); - break; - case SdlCmdType::Delete: - if (cmd.cmd_delete.isInitialized()) - { - Handle to_delete = std::move(cmd.cmd_delete); - if (platform) - { - platform->UnregisterWindow(to_delete.hwnd); - } - int wnd_id = SDL_GetWindowID(to_delete.hwnd); - hwnd_to_window.erase(wnd_id); - wnd_events.erase(wnd_id); - num_windows--; - } - break; - case SdlCmdType::SetTitle: - SDL_SetWindowTitle(cmd.handle->hwnd, cmd.cmd_title.c_str()); - break; - case SdlCmdType::SetSize: - SDL_SetWindowSize(cmd.handle->hwnd, - cmd.cmd_set_size.first, - cmd.cmd_set_size.second); - break; - case SdlCmdType::SetPosition: - SDL_SetWindowPosition(cmd.handle->hwnd, - cmd.cmd_set_position.first, - cmd.cmd_set_position.second); - break; - default: - cerr << "Error in main thread: unknown window control command.\n"; - break; - } + handleWindowCmdImpl(cmd); } if (!server_mode && num_windows == 0) { terminating = true; // all windows closed } - // Dequeue all events from the system window manager - SDL_Event e; - while (SDL_PollEvent(&e)) - { - unsigned int windowId = UINT_MAX; - bool sendToAll = false; - switch (e.type) - { - case SDL_QUIT: - if (server_mode) - { - terminating = exitDialog(); - } - else - { - terminating = true; - } - break; - case SDL_WINDOWEVENT: - windowId = getWindowID(e.window); - break; - case SDL_FINGERDOWN: - fingers.insert(e.tfinger.fingerId); - if (fingers.size() >= 2) { disable_mouse = true; } - break; - case SDL_FINGERUP: - fingers.erase(e.tfinger.fingerId); - if (fingers.size() < 2) { disable_mouse = false; } - break; - case SDL_MULTIGESTURE: - // No window ID is provided with multigesture events. We'll - // have to use focus status within the windows themselves in - // order to resolve the correct target. - sendToAll = true; - break; - case SDL_KEYDOWN: - case SDL_KEYUP: - windowId = getWindowID(e.key); - break; - case SDL_TEXTINPUT: - windowId = getWindowID(e.text); - break; - case SDL_MOUSEMOTION: - if (!disable_mouse) { windowId = getWindowID(e.motion); } - break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - windowId = getWindowID(e.button); - break; - } - if (sendToAll == true) - { - for (auto wnds : hwnd_to_window) - { - int windowId = wnds.first; - wnd_events[windowId].emplace_back(e); - } - } - else if (windowId != UINT_MAX) - { - wnd_events[windowId].emplace_back(e); - } - } - // Send events to window worker threads - for (auto wnds : hwnd_to_window) - { - int windowId = wnds.first; - SdlWindow* wnd = wnds.second; - if (!wnd_events[windowId].empty()) - { - wnd->queueEvents(std::move(wnd_events[windowId])); - } - else - { - // Wake up the worker thread anyways, to execute onIdle - wnd->queueEvents({}); - } - } + DispatchSDLEvents(); if (terminating) { break; } // Wait for the next event (without consuming CPU cycles, if possible) // See also: SdlWindow::signalLoop() @@ -270,13 +246,7 @@ struct SdlWindow::MainThread main_thread_cmd.type = SdlCmdType::Create; main_thread_cmd.cmd_create = &cmd_create; - // Move our create request into the pending queue - { - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_back(std::move(main_thread_cmd)); - } - // Wake up the main thread to create our window - if (platform) { platform->SendEvent(); } + queueWindowEvent(std::move(main_thread_cmd)); Handle out_hnd = res_handle.get(); if (out_hnd.isInitialized()) @@ -374,11 +344,21 @@ struct SdlWindow::MainThread void queueWindowEvent(SdlCtrlCommand cmd) { + if (sdl_multithread) { - lock_guard req_lock{window_cmd_mtx}; - window_cmds.emplace_back(std::move(cmd)); + // 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 + if (platform) { platform->SendEvent(); } + } + else + { + // call the underlying SDL command immediately + handleWindowCmdImpl(cmd); } - if (platform) { platform->SendEvent(); } } bool exitDialog() @@ -417,6 +397,46 @@ struct SdlWindow::MainThread return eventStruct.windowID; } + void handleWindowCmdImpl(SdlCtrlCommand& cmd) + { + switch (cmd.type) + { + case SdlCmdType::Create: + createWindowImpl(*cmd.cmd_create); + break; + case SdlCmdType::Delete: + if (cmd.cmd_delete.isInitialized()) + { + Handle to_delete = std::move(cmd.cmd_delete); + if (platform) + { + platform->UnregisterWindow(to_delete.hwnd); + } + int wnd_id = SDL_GetWindowID(to_delete.hwnd); + hwnd_to_window.erase(wnd_id); + wnd_events.erase(wnd_id); + num_windows--; + } + break; + case SdlCmdType::SetTitle: + SDL_SetWindowTitle(cmd.handle->hwnd, cmd.cmd_title.c_str()); + break; + case SdlCmdType::SetSize: + SDL_SetWindowSize(cmd.handle->hwnd, + cmd.cmd_set_size.first, + cmd.cmd_set_size.second); + break; + case SdlCmdType::SetPosition: + SDL_SetWindowPosition(cmd.handle->hwnd, + cmd.cmd_set_position.first, + cmd.cmd_set_position.second); + break; + default: + cerr << "Error in main thread: unknown window control command.\n"; + break; + } + } + void probeGLContextSupport(bool legacyGlOnly); void getDpi(const Handle& handle, int& wdpi, int& hdpi); @@ -425,6 +445,9 @@ struct SdlWindow::MainThread Uint32 glvis_event_type {(Uint32)-1}; bool sdl_init {false}; + bool sdl_multithread {true}; + + bool server_mode {false}; atomic terminating {false}; @@ -451,7 +474,11 @@ struct SdlWindow::MainThread unique_ptr platform; }; -SdlWindow::MainThread SdlWindow::main_thread{}; +SdlWindow::MainThread& SdlWindow::GetMainThread() +{ + static SdlWindow::MainThread inst; + return inst; +} bool SdlWindow::isGlInitialized() { @@ -462,7 +489,7 @@ SdlWindow::SdlWindow() {} void SdlWindow::StartSDL(bool server_mode) { - main_thread.MainLoop(server_mode); + GetMainThread().MainLoop(server_mode); } // Setup the correct OpenGL context flags in SDL for when we actually open the @@ -744,9 +771,13 @@ void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, bool legacyGlOnly) { +#ifdef __EMSCRIPTEN__ + is_multithreaded = false; + SdlWindow::GetMainThread().SetSingleThread(); +#endif // create a new SDL window - handle = SdlWindow::main_thread.GetHandle(this, title, x, y, w, h, - legacyGlOnly); + handle = SdlWindow::GetMainThread().GetHandle(this, title, x, y, w, h, + legacyGlOnly); // at this point, window should be up if (!handle.isInitialized()) @@ -825,7 +856,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 - SdlWindow::main_thread.DeleteHandle(std::move(handle)); + SdlWindow::GetMainThread().DeleteHandle(std::move(handle)); } void SdlWindow::windowEvent(SDL_WindowEvent& ew) @@ -1000,6 +1031,11 @@ void SdlWindow::multiGestureEvent(SDL_MultiGestureEvent & e) void SdlWindow::mainIter() { + if (!is_multithreaded) + { + // Pull events from GetMainThread() object + SdlWindow::GetMainThread().DispatchSDLEvents(); + } bool events_pending = false; { lock_guard evt_guard{event_mutex}; @@ -1063,23 +1099,16 @@ void SdlWindow::mainIter() } while (keep_going && events_pending); } -#ifndef __EMSCRIPTEN__ else if (onIdle) { bool sleep = onIdle(); - if (sleep) + if (is_multithreaded && sleep) { // Wait for next wakeup event from main event thread unique_lock event_lock{event_mutex}; events_available.wait(event_lock); } } -#else - else if (onIdle) - { - onIdle(); - } -#endif if (wnd_state == RenderState::ExposePending) { #ifdef SDL_VIDEO_DRIVER_COCOA @@ -1132,7 +1161,7 @@ void SdlWindow::mainLoop() void SdlWindow::signalLoop() { // Note: not executed from the main thread - main_thread.SendEvent(); + GetMainThread().SendEvent(); } void SdlWindow::getWindowSize(int& w, int& h) @@ -1210,12 +1239,12 @@ void SdlWindow::setWindowTitle(std::string& title) void SdlWindow::setWindowTitle(const char * title) { - main_thread.SetWindowTitle(handle, title); + GetMainThread().SetWindowTitle(handle, title); } void SdlWindow::setWindowSize(int w, int h) { - main_thread.SetWindowSize(handle, pixel_scale_x*w, pixel_scale_y*h); + GetMainThread().SetWindowSize(handle, pixel_scale_x*w, pixel_scale_y*h); swap_before_expose = true; } @@ -1225,9 +1254,9 @@ void SdlWindow::setWindowPos(int x, int y) SDL_WINDOWPOS_ISCENTERED(x); bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(y) || SDL_WINDOWPOS_ISCENTERED(y); - main_thread.SetWindowPosition(handle, - uc_x ? x : pixel_scale_x*x, - uc_y ? y : pixel_scale_y*y); + GetMainThread().SetWindowPosition(handle, + uc_x ? x : pixel_scale_x*x, + uc_y ? y : pixel_scale_y*y); swap_before_expose = true; } diff --git a/lib/sdl.hpp b/lib/sdl.hpp index f59373af..5078932f 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -73,7 +73,7 @@ class SdlWindow }; struct MainThread; - static MainThread main_thread; + static MainThread& GetMainThread(); Handle handle; std::unique_ptr renderer; @@ -136,6 +136,8 @@ class SdlWindow void keyEvent(char c); void multiGestureEvent(SDL_MultiGestureEvent & e); + bool is_multithreaded{true}; + // Hand off events to the SdlWindow. Intended to be called by the main SDL // thread in MainThread::MainLoop(). void queueEvents(std::vector events) @@ -144,7 +146,10 @@ class SdlWindow std::lock_guard evt_guard{event_mutex}; waiting_events.insert(waiting_events.end(), events.begin(), events.end()); } - events_available.notify_all(); + if (is_multithreaded) + { + events_available.notify_all(); + } } std::string saved_keys; From 588863503af79fd49676375d61867c7d9fd5da22 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 15:50:32 -0700 Subject: [PATCH 23/47] Split SDL main thread class into its own file --- lib/CMakeLists.txt | 2 + lib/sdl.cpp | 695 +-------------------------------------------- lib/sdl.hpp | 6 +- lib/sdl_main.cpp | 621 ++++++++++++++++++++++++++++++++++++++++ lib/sdl_main.hpp | 130 +++++++++ makefile | 11 +- 6 files changed, 769 insertions(+), 696 deletions(-) create mode 100644 lib/sdl_main.cpp create mode 100644 lib/sdl_main.hpp diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 7d51c160..b8a34e7a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -21,6 +21,7 @@ list(APPEND SOURCES palettes.cpp sdl.cpp sdl_helper.cpp + sdl_main.cpp stream_reader.cpp vsdata.cpp vssolution.cpp @@ -44,6 +45,7 @@ list(APPEND HEADERS palettes.hpp sdl.hpp sdl_helper.hpp + sdl_main.hpp sdl_mac.hpp stream_reader.hpp visual.hpp diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 2e82b523..b1cdb577 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -15,21 +15,14 @@ #include #include "threads.hpp" #include "aux_vis.hpp" -#include "logo.hpp" #include "gl/renderer_core.hpp" #include "gl/renderer_ff.hpp" #include "sdl.hpp" +#include "sdl_main.hpp" #ifdef __EMSCRIPTEN__ #include #include #endif -#include "sdl_helper.hpp" -#if defined(SDL_VIDEO_DRIVER_X11) -#include "sdl_x11.hpp" -#endif -#if defined(SDL_VIDEO_DRIVER_COCOA) -#include "sdl_mac.hpp" -#endif using std::cerr; @@ -75,408 +68,9 @@ SdlWindow::Handle::~Handle() } } - -struct SdlWindow::MainThread -{ -public: - MainThread() - { - if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) - { - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) - { - cerr << "FATAL: Failed to initialize SDL: " << SDL_GetError() << endl; - } - SDL_EnableScreenSaver(); - glvis_event_type = SDL_RegisterEvents(1); - if (glvis_event_type == (Uint32)(-1)) - { - cerr << "SDL_RegisterEvents(1) failed: " << SDL_GetError() << endl; - } - } - - SDL_version sdl_ver; - SDL_GetVersion(&sdl_ver); - PRINT_DEBUG("Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor - << "." << (int)sdl_ver.patch << std::endl); - sdl_init = true; - } - - // If called, all SDL window operations are run immediately; this is for - // running in a single-threaded mode. - void SetSingleThread() { sdl_multithread = false; } - - bool SdlInitialized() const { return sdl_init; } - - Uint32 GetCustomEvent() const { return glvis_event_type; } - - // Dequeues all incoming events from SDL, and queues them up to their - // matching windows. - void DispatchSDLEvents() - { - SDL_Event e; - while (SDL_PollEvent(&e)) - { - unsigned int windowId = UINT_MAX; - bool sendToAll = false; - switch (e.type) - { - case SDL_QUIT: - if (server_mode) - { - terminating = exitDialog(); - } - else - { - terminating = true; - } - break; - case SDL_WINDOWEVENT: - windowId = getWindowID(e.window); - break; - case SDL_FINGERDOWN: - fingers.insert(e.tfinger.fingerId); - if (fingers.size() >= 2) { disable_mouse = true; } - break; - case SDL_FINGERUP: - fingers.erase(e.tfinger.fingerId); - if (fingers.size() < 2) { disable_mouse = false; } - break; - case SDL_MULTIGESTURE: - // No window ID is provided with multigesture events. We'll - // have to use focus status within the windows themselves in - // order to resolve the correct target. - sendToAll = true; - break; - case SDL_KEYDOWN: - case SDL_KEYUP: - windowId = getWindowID(e.key); - break; - case SDL_TEXTINPUT: - windowId = getWindowID(e.text); - break; - case SDL_MOUSEMOTION: - if (!disable_mouse) { windowId = getWindowID(e.motion); } - break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - windowId = getWindowID(e.button); - break; - } - if (sendToAll == true) - { - for (auto wnds : hwnd_to_window) - { - int windowId = wnds.first; - wnd_events[windowId].emplace_back(e); - } - } - else if (windowId != UINT_MAX) - { - wnd_events[windowId].emplace_back(e); - } - } - // Send events to window worker threads - for (auto wnds : hwnd_to_window) - { - int windowId = wnds.first; - SdlWindow* wnd = wnds.second; - if (!wnd_events[windowId].empty()) - { - wnd->queueEvents(std::move(wnd_events[windowId])); - } - else - { - // Wake up the worker thread anyways, to execute onIdle - wnd->queueEvents({}); - } - } - } - - // 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) - { - this->server_mode = server_mode; - while (1) - { - // Process all pending window commands - vector pending_cmds; - { - lock_guard cmd_lock{window_cmd_mtx}; - pending_cmds = std::move(window_cmds); - } - - for (SdlCtrlCommand& cmd : pending_cmds) - { - handleWindowCmdImpl(cmd); - } - if (!server_mode && num_windows == 0) - { - terminating = true; // all windows closed - } - - DispatchSDLEvents(); - if (terminating) { break; } - // Wait for the next event (without consuming CPU cycles, if possible) - // See also: SdlWindow::signalLoop() - if (platform) - { - platform->WaitEvent(); - } - else - { - if (!SDL_PollEvent(nullptr)) - { - std::this_thread::sleep_for(std::chrono::milliseconds(8)); - } - } - } - } - - // Executed from a window worker thread. Returns a handle to a new window - // and associated OpenGL context. - Handle GetHandle(SdlWindow* wnd, const std::string& title, - int x, int y, int w, int h, bool legacyGlOnly) - { - CreateWindowCmd cmd_create = { wnd, title, x, y, w, h, legacyGlOnly, false }; - future res_handle = cmd_create.out_handle.get_future(); - - SdlCtrlCommand main_thread_cmd; - main_thread_cmd.type = SdlCmdType::Create; - main_thread_cmd.cmd_create = &cmd_create; - - queueWindowEvent(std::move(main_thread_cmd)); - - Handle out_hnd = res_handle.get(); - if (out_hnd.isInitialized()) - { - // Make the OpenGL context current on the worker thread. - // Since SDL calls aren't guaranteed to be thread-safe, we guard - // the call to SDL_GL_MakeCurrent. - lock_guard ctx_lock{gl_ctx_mtx}; - SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx); - } - return out_hnd; - } - - // Executed from a window worker thread. Deletes a handle to a window and - // the corresponding OpenGL context. - void DeleteHandle(Handle to_delete) - { - SdlCtrlCommand main_thread_cmd; - main_thread_cmd.type = SdlCmdType::Delete; - main_thread_cmd.cmd_delete = std::move(to_delete); - - queueWindowEvent(std::move(main_thread_cmd)); - } - - // Issues a command on the main thread to set the window title. - void SetWindowTitle(const Handle& handle, std::string title) - { - SdlCtrlCommand main_thread_cmd; - main_thread_cmd.type = SdlCmdType::SetTitle; - main_thread_cmd.handle = &handle; - main_thread_cmd.cmd_title = std::move(title); - - queueWindowEvent(std::move(main_thread_cmd)); - } - - // Issues a command on the main thread to set the window size. - void SetWindowSize(const Handle& handle, int w, int h) - { - SdlCtrlCommand main_thread_cmd; - main_thread_cmd.type = SdlCmdType::SetSize; - main_thread_cmd.handle = &handle; - main_thread_cmd.cmd_set_size = {w, h}; - - queueWindowEvent(std::move(main_thread_cmd)); - } - - // Issues a command on the main thread to set the window position. - void SetWindowPosition(const Handle& handle, int x, int y) - { - SdlCtrlCommand main_thread_cmd; - main_thread_cmd.type = SdlCmdType::SetPosition; - main_thread_cmd.handle = &handle; - main_thread_cmd.cmd_set_position = {x, y}; - - queueWindowEvent(std::move(main_thread_cmd)); - } - - void SendEvent() - { - if (platform) { platform->SendEvent(); } - } - -private: - struct CreateWindowCmd - { - SdlWindow* wnd; - std::string title; - int x, y, w, h; - bool legacy_gl_only; - bool window_create_executed; - promise out_handle; - }; - - enum class SdlCmdType - { - None, - Create, - Delete, - SetTitle, - SetSize, - SetPosition - }; - - struct SdlCtrlCommand - { - SdlCmdType type {SdlCmdType::None}; - - const Handle* handle {nullptr}; - CreateWindowCmd* cmd_create; - Handle cmd_delete; - string cmd_title; - pair cmd_set_size; - pair cmd_set_position; - }; - - void queueWindowEvent(SdlCtrlCommand cmd) - { - if (sdl_multithread) - { - // 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 - if (platform) { platform->SendEvent(); } - } - else - { - // call the underlying SDL command immediately - handleWindowCmdImpl(cmd); - } - } - - bool exitDialog() - { - const int ID_Exit = 1; - const int ID_Continue = 2; - const SDL_MessageBoxButtonData buttons[] = - { - {0, ID_Exit, "Yes"}, - {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, ID_Continue, "No"} - }; - - const SDL_MessageBoxData request_box - { - SDL_MESSAGEBOX_INFORMATION, - nullptr, - "GLVis Server", - "Stop GLVis server thread?", - 2, - buttons, - nullptr - }; - - int button_id; - if (SDL_ShowMessageBox(&request_box, &button_id) < 0) - { - return true; - } - - return button_id == ID_Exit; - } - - template - Uint32 getWindowID(const T& eventStruct) - { - return eventStruct.windowID; - } - - void handleWindowCmdImpl(SdlCtrlCommand& cmd) - { - switch (cmd.type) - { - case SdlCmdType::Create: - createWindowImpl(*cmd.cmd_create); - break; - case SdlCmdType::Delete: - if (cmd.cmd_delete.isInitialized()) - { - Handle to_delete = std::move(cmd.cmd_delete); - if (platform) - { - platform->UnregisterWindow(to_delete.hwnd); - } - int wnd_id = SDL_GetWindowID(to_delete.hwnd); - hwnd_to_window.erase(wnd_id); - wnd_events.erase(wnd_id); - num_windows--; - } - break; - case SdlCmdType::SetTitle: - SDL_SetWindowTitle(cmd.handle->hwnd, cmd.cmd_title.c_str()); - break; - case SdlCmdType::SetSize: - SDL_SetWindowSize(cmd.handle->hwnd, - cmd.cmd_set_size.first, - cmd.cmd_set_size.second); - break; - case SdlCmdType::SetPosition: - SDL_SetWindowPosition(cmd.handle->hwnd, - cmd.cmd_set_position.first, - cmd.cmd_set_position.second); - break; - default: - cerr << "Error in main thread: unknown window control command.\n"; - break; - } - } - - void probeGLContextSupport(bool legacyGlOnly); - - void getDpi(const Handle& handle, int& wdpi, int& hdpi); - - void createWindowImpl(CreateWindowCmd& cmd); - - Uint32 glvis_event_type {(Uint32)-1}; - bool sdl_init {false}; - bool sdl_multithread {true}; - - bool server_mode {false}; - - atomic terminating {false}; - - // ------------------------------------------------------------------------- - // Objects for handling passing of window control commands to the main event - // loop. - - mutex window_cmd_mtx; - vector window_cmds; - - int num_windows {-1}; // -1: waiting for window to be created - - // ------------------------------------------------------------------------- - // Objects for handling dispatching events from the main event loop to - // worker threads. - - unordered_map hwnd_to_window; - unordered_map> wnd_events; - std::set fingers; - bool disable_mouse {false}; - - mutex gl_ctx_mtx; - - unique_ptr platform; -}; - -SdlWindow::MainThread& SdlWindow::GetMainThread() +SdlMainThread& GetMainThread() { - static SdlWindow::MainThread inst; + static SdlMainThread inst; return inst; } @@ -492,292 +86,17 @@ void SdlWindow::StartSDL(bool server_mode) GetMainThread().MainLoop(server_mode); } -// Setup the correct OpenGL context flags in SDL for when we actually open the -// window. -void SdlWindow::MainThread::probeGLContextSupport(bool legacyGlOnly) -{ - Uint32 win_flags_hidden = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; -#ifndef __EMSCRIPTEN__ - 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("Testing if OpenGL core profile window can be created..." << flush); - // Try to create core profile context first - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - { - Handle testCore("", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - 100, 100, win_flags_hidden); - if (testCore.isInitialized()) - { - PRINT_DEBUG("success!" << endl); - return; - } - } - - PRINT_DEBUG("failed." << endl); - PRINT_DEBUG("Testing if OpenGL compatibility profile window can be created..." - << flush); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, - SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); - { - Handle testCompat("", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - 100, 100, win_flags_hidden); - if (testCompat.isInitialized()) - { - PRINT_DEBUG("success!" << endl); - return; - } - } - PRINT_DEBUG("failed." << endl); - } - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); - if (GetMultisample() > 0) - { - SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, GetMultisample()); - PRINT_DEBUG("Testing OpenGL with default profile and antialiasing..." << endl); - { - Handle testMsaa("", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - 100, 100, win_flags_hidden); - if (testMsaa.isInitialized()) - { - PRINT_DEBUG("success!" << endl); - return; - } - } - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); - PRINT_DEBUG("failed." << endl); - } - PRINT_DEBUG("Opening OpenGL window with no flags."); -#else - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, - SDL_GL_CONTEXT_PROFILE_ES); - PRINT_DEBUG("Testing for WebGL 2.0 context availability..." << flush); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - { - Handle testWebGl2("", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - 100, 100, win_flags_hidden); - if (testWebGl2.isInitialized()) - { - PRINT_DEBUG("success!" << endl); - return; - } - } - PRINT_DEBUG("failed." << endl); - PRINT_DEBUG("Testing for WebGL 1.0 context availability..." << flush); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - // Note that WebGL 2 support controllable antialiasing, while WebGL 1 only - // supports requesting multisampling at context creation time. The browser - // is free to ignore this flag. - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, GetMultisample()); - { - Handle testWebGl("", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - 100, 100, win_flags_hidden); - if (testWebGl.isInitialized()) - { - PRINT_DEBUG("success!" << endl); - return; - } - } -#endif -} - const int default_dpi = 72; -void SdlWindow::MainThread::getDpi(const Handle& handle, int& w, int& h) -{ - w = default_dpi; - h = default_dpi; - if (!handle.isInitialized()) - { - PRINT_DEBUG("warning: unable to get dpi: handle is null" << endl); - return; - } - int disp = SDL_GetWindowDisplayIndex(handle.hwnd); - if (disp < 0) - { - PRINT_DEBUG("warning: problem getting display index: " << SDL_GetError() - << endl); - PRINT_DEBUG("returning default dpi of " << default_dpi << endl); - return; - } - - float f_w, f_h; - if (SDL_GetDisplayDPI(disp, NULL, &f_w, &f_h)) - { - PRINT_DEBUG("warning: problem getting dpi, setting to " << default_dpi - << ": " << SDL_GetError() << endl); - } - else - { - PRINT_DEBUG("Screen DPI: w = " << f_w << " ppi, h = " << f_h << " ppi"); - w = f_w + 0.5f; - h = f_h + 0.5f; - PRINT_DEBUG(" (" << w << " x " << h << ')' << endl); - } -} -void SdlWindow::MainThread::createWindowImpl(CreateWindowCmd& cmd) -{ - Uint32 win_flags = SDL_WINDOW_OPENGL; - // Hide window until we adjust its size for high-dpi displays - win_flags |= SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN; -#ifndef __EMSCRIPTEN__ - win_flags |= SDL_WINDOW_RESIZABLE; -#endif - SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24); - probeGLContextSupport(cmd.legacy_gl_only); - Handle new_handle(cmd.title, cmd.x, cmd.y, cmd.w, cmd.h, win_flags); - cmd.window_create_executed = true; - - // at this point, window should be up - if (!new_handle.isInitialized()) - { - PRINT_DEBUG("failed." << endl); - cerr << "FATAL: window and/or OpenGL context creation failed." << endl; - return; - } - else - { - PRINT_DEBUG("Handle for window created." << endl); - } - -#ifndef __EMSCRIPTEN__ - SDL_GL_SetSwapInterval(0); - glEnable(GL_DEBUG_OUTPUT); -#endif - - // Register window internally in the main thread so it can receive events - int wnd_id = SDL_GetWindowID(new_handle.hwnd); - hwnd_to_window[wnd_id] = cmd.wnd; - - if (!platform) - { - platform = SdlNativePlatform::Create(new_handle.hwnd); - } - if (platform) - { - platform->RegisterWindow(new_handle.hwnd); - } - -#ifdef GLVIS_USE_LOGO - const int PixelStride = 4; - int stride = (int) sqrt(logo_rgba_len / PixelStride); - if (unsigned(stride * stride * PixelStride) != logo_rgba_len) - { - cerr << "Unable to set window logo: icon size not square" << endl; - } - else - { - SDL_Surface* iconSurf = - SDL_CreateRGBSurfaceFrom(logo_rgba, - stride, stride, // height, width - 8 * PixelStride, // depth - stride * PixelStride, // pitch - 0x000000FF, - 0x0000FF00, - 0x00FF0000, - 0xFF000000); - if (iconSurf) - { - SDL_SetWindowIcon(new_handle.hwnd, iconSurf); - SDL_FreeSurface(iconSurf); - } - else - { - PRINT_DEBUG("Unable to set window logo: " << SDL_GetError() << endl); - } - } -#endif // GLVIS_USE_LOGO - - // Detect if we are using a high-dpi display and resize the window unless it - // was already resized by SDL's underlying backend. - { - SdlWindow* wnd = cmd.wnd; - int scr_w, scr_h, pix_w, pix_h, wdpi, hdpi; - // SDL_GetWindowSize() -- size in "screen coordinates" - SDL_GetWindowSize(new_handle.hwnd, &scr_w, &scr_h); - // SDL_GL_GetDrawableSize() -- size in pixels - SDL_GL_GetDrawableSize(new_handle.hwnd, &pix_w, &pix_h); - wnd->high_dpi = false; - wnd->pixel_scale_x = wnd->pixel_scale_y = 1.0f; - float sdl_pixel_scale_x = 1.0f, sdl_pixel_scale_y = 1.0f; - // If "screen" and "pixel" sizes are different, assume high-dpi and no - // need to scale the window. - if (scr_w == pix_w && scr_h == pix_h) - { - getDpi(new_handle, wdpi, hdpi); - if (std::max(wdpi, hdpi) >= high_dpi_threshold) - { - wnd->high_dpi = true; - wnd->pixel_scale_x = wnd->pixel_scale_y = 2.0f; - // the following two calls use 'pixel_scale_*' - SDL_SetWindowSize(new_handle.hwnd, - wnd->pixel_scale_x*cmd.w, - wnd->pixel_scale_y*cmd.h); - bool uc_x = SDL_WINDOWPOS_ISUNDEFINED(cmd.x) || - SDL_WINDOWPOS_ISCENTERED(cmd.x); - bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(cmd.y) || - SDL_WINDOWPOS_ISCENTERED(cmd.y); - SDL_SetWindowPosition(new_handle.hwnd, - uc_x ? cmd.x : wnd->pixel_scale_x*cmd.x, - uc_y ? cmd.y : wnd->pixel_scale_y*cmd.y); - } - } - else - { - wnd->high_dpi = true; - // keep 'pixel_scale_*' = 1, scaling is done inside SDL - sdl_pixel_scale_x = float(pix_w)/scr_w; - sdl_pixel_scale_y = float(pix_h)/scr_h; - } - if (wnd->high_dpi) - { - cout << "High-dpi display detected: using window scaling: " - << sdl_pixel_scale_x*wnd->pixel_scale_x << " x " - << sdl_pixel_scale_y*wnd->pixel_scale_y << endl; - } - } - - // Unset GL context in this thread - { - lock_guard ctx_lock{gl_ctx_mtx}; - SDL_GL_MakeCurrent(new_handle.hwnd, nullptr); - } - - SDL_ShowWindow(new_handle.hwnd); - if (num_windows == -1) - { - num_windows = 0; - } - num_windows++; - cmd.out_handle.set_value(std::move(new_handle)); -} bool SdlWindow::createWindow(const char* title, int x, int y, int w, int h, bool legacyGlOnly) { #ifdef __EMSCRIPTEN__ is_multithreaded = false; - SdlWindow::GetMainThread().SetSingleThread(); + GetMainThread().SetSingleThread(); #endif // create a new SDL window - handle = SdlWindow::GetMainThread().GetHandle(this, title, x, y, w, h, - legacyGlOnly); + handle = GetMainThread().GetHandle(this, title, x, y, w, h, legacyGlOnly); // at this point, window should be up if (!handle.isInitialized()) @@ -856,7 +175,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 - SdlWindow::GetMainThread().DeleteHandle(std::move(handle)); + GetMainThread().DeleteHandle(std::move(handle)); } void SdlWindow::windowEvent(SDL_WindowEvent& ew) @@ -1034,7 +353,7 @@ void SdlWindow::mainIter() if (!is_multithreaded) { // Pull events from GetMainThread() object - SdlWindow::GetMainThread().DispatchSDLEvents(); + GetMainThread().DispatchSDLEvents(); } bool events_pending = false; { diff --git a/lib/sdl.hpp b/lib/sdl.hpp index 5078932f..95ddbc6f 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -37,9 +37,12 @@ typedef void (*WindowDelegate)(int, int); typedef void (*Delegate)(); typedef bool (*IdleDelegate)(); +class SdlMainThread; + class SdlWindow { private: + friend class SdlMainThread; struct Handle { SDL_Window * hwnd{nullptr}; @@ -71,9 +74,6 @@ class SdlWindow return (hwnd != nullptr && gl_ctx != 0); } }; - struct MainThread; - - static MainThread& GetMainThread(); Handle handle; std::unique_ptr renderer; diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp new file mode 100644 index 00000000..0d9676b4 --- /dev/null +++ b/lib/sdl_main.cpp @@ -0,0 +1,621 @@ +// Copyright (c) 2010-2021, 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 +#include + +#include "sdl_main.hpp" +#include "sdl_helper.hpp" +#include "logo.hpp" + +#ifdef GLVIS_DEBUG +#define PRINT_DEBUG(s) std::cerr << s +#else +#define PRINT_DEBUG(s) {} +#endif + +extern int GetMultisample(); + +struct SdlMainThread::CreateWindowCmd +{ + SdlWindow* wnd; + std::string title; + int x, y, w, h; + bool legacy_gl_only; + bool window_create_executed; + promise out_handle; +}; + +struct SdlMainThread::SdlCtrlCommand +{ + SdlCmdType type {SdlCmdType::None}; + + const Handle* handle {nullptr}; + CreateWindowCmd* cmd_create; + Handle cmd_delete; + string cmd_title; + pair cmd_set_size; + pair cmd_set_position; +}; + + +SdlMainThread::SdlMainThread() +{ + if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) + { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) + { + std::cerr << "FATAL: Failed to initialize SDL: " << SDL_GetError() << endl; + } + SDL_EnableScreenSaver(); + glvis_event_type = SDL_RegisterEvents(1); + if (glvis_event_type == (Uint32)(-1)) + { + cerr << "SDL_RegisterEvents(1) failed: " << SDL_GetError() << endl; + } + } + + SDL_version sdl_ver; + SDL_GetVersion(&sdl_ver); + std::cerr << "Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor + << "." << (int)sdl_ver.patch << std::endl; + sdl_init = true; +} + +SdlMainThread::~SdlMainThread() = default; + +void SdlMainThread::MainLoop(bool server_mode) +{ + this->server_mode = server_mode; + while (1) + { + // Process all pending window commands + vector pending_cmds; + { + lock_guard cmd_lock{window_cmd_mtx}; + pending_cmds = std::move(window_cmds); + } + + for (SdlCtrlCommand& cmd : pending_cmds) + { + handleWindowCmdImpl(cmd); + } + if (!server_mode && num_windows == 0) + { + terminating = true; // all windows closed + } + + DispatchSDLEvents(); + if (terminating) { break; } + // Wait for the next event (without consuming CPU cycles, if possible) + // See also: SdlWindow::signalLoop() + if (platform) + { + platform->WaitEvent(); + } + else + { + if (!SDL_PollEvent(nullptr)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(8)); + } + } + } +} + +void SdlMainThread::DispatchSDLEvents() +{ + SDL_Event e; + while (SDL_PollEvent(&e)) + { + unsigned int windowId = UINT_MAX; + bool sendToAll = false; + switch (e.type) + { + case SDL_QUIT: + if (server_mode) + { + terminating = exitDialog(); + } + else + { + terminating = true; + } + break; + case SDL_WINDOWEVENT: + windowId = getWindowID(e.window); + break; + case SDL_FINGERDOWN: + fingers.insert(e.tfinger.fingerId); + if (fingers.size() >= 2) { disable_mouse = true; } + break; + case SDL_FINGERUP: + fingers.erase(e.tfinger.fingerId); + if (fingers.size() < 2) { disable_mouse = false; } + break; + case SDL_MULTIGESTURE: + // No window ID is provided with multigesture events. We'll + // have to use focus status within the windows themselves in + // order to resolve the correct target. + sendToAll = true; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + windowId = getWindowID(e.key); + break; + case SDL_TEXTINPUT: + windowId = getWindowID(e.text); + break; + case SDL_MOUSEMOTION: + if (!disable_mouse) { windowId = getWindowID(e.motion); } + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + windowId = getWindowID(e.button); + break; + } + if (sendToAll == true) + { + for (auto wnds : hwnd_to_window) + { + int windowId = wnds.first; + wnd_events[windowId].emplace_back(e); + } + } + else if (windowId != UINT_MAX) + { + wnd_events[windowId].emplace_back(e); + } + } + // Send events to window worker threads + for (auto wnds : hwnd_to_window) + { + int windowId = wnds.first; + SdlWindow* wnd = wnds.second; + if (!wnd_events[windowId].empty()) + { + wnd->queueEvents(std::move(wnd_events[windowId])); + } + else + { + // Wake up the worker thread anyways, to execute onIdle + wnd->queueEvents({}); + } + } +} + +SdlMainThread::Handle SdlMainThread::GetHandle(SdlWindow* wnd, + const std::string& title, + int x, int y, int w, int h, bool legacyGlOnly) +{ + CreateWindowCmd cmd_create = { wnd, title, x, y, w, h, legacyGlOnly, false }; + future res_handle = cmd_create.out_handle.get_future(); + + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::Create; + main_thread_cmd.cmd_create = &cmd_create; + + queueWindowEvent(std::move(main_thread_cmd)); + + Handle out_hnd = res_handle.get(); + if (out_hnd.isInitialized()) + { + // Make the OpenGL context current on the worker thread. + // Since SDL calls aren't guaranteed to be thread-safe, we guard + // the call to SDL_GL_MakeCurrent. + lock_guard ctx_lock{gl_ctx_mtx}; + SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx); + } + return out_hnd; +} + +void SdlMainThread::DeleteHandle(Handle to_delete) +{ + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::Delete; + main_thread_cmd.cmd_delete = std::move(to_delete); + + queueWindowEvent(std::move(main_thread_cmd)); +} + +void SdlMainThread::SetWindowTitle(const Handle& handle, std::string title) +{ + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::SetTitle; + main_thread_cmd.handle = &handle; + main_thread_cmd.cmd_title = std::move(title); + + queueWindowEvent(std::move(main_thread_cmd)); +} + +void SdlMainThread::SetWindowSize(const Handle& handle, int w, int h) +{ + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::SetSize; + main_thread_cmd.handle = &handle; + main_thread_cmd.cmd_set_size = {w, h}; + + queueWindowEvent(std::move(main_thread_cmd)); +} + +void SdlMainThread::SetWindowPosition(const Handle& handle, int x, int y) +{ + SdlCtrlCommand main_thread_cmd; + main_thread_cmd.type = SdlCmdType::SetPosition; + main_thread_cmd.handle = &handle; + main_thread_cmd.cmd_set_position = {x, y}; + + queueWindowEvent(std::move(main_thread_cmd)); +} + +void SdlMainThread::queueWindowEvent(SdlCtrlCommand cmd) +{ + if (sdl_multithread) + { + // 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 + if (platform) { platform->SendEvent(); } + } + else + { + // call the underlying SDL command immediately + handleWindowCmdImpl(cmd); + } +} + +bool SdlMainThread::exitDialog() +{ + const int ID_Exit = 1; + const int ID_Continue = 2; + const SDL_MessageBoxButtonData buttons[] = + { + {0, ID_Exit, "Yes"}, + {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, ID_Continue, "No"} + }; + + const SDL_MessageBoxData request_box + { + SDL_MESSAGEBOX_INFORMATION, + nullptr, + "GLVis Server", + "Stop GLVis server thread?", + 2, + buttons, + nullptr + }; + + int button_id; + if (SDL_ShowMessageBox(&request_box, &button_id) < 0) + { + return true; + } + + return button_id == ID_Exit; +} + +void SdlMainThread::handleWindowCmdImpl(SdlCtrlCommand& cmd) +{ + switch (cmd.type) + { + case SdlCmdType::Create: + createWindowImpl(*cmd.cmd_create); + break; + case SdlCmdType::Delete: + if (cmd.cmd_delete.isInitialized()) + { + Handle to_delete = std::move(cmd.cmd_delete); + if (platform) + { + platform->UnregisterWindow(to_delete.hwnd); + } + int wnd_id = SDL_GetWindowID(to_delete.hwnd); + hwnd_to_window.erase(wnd_id); + wnd_events.erase(wnd_id); + num_windows--; + } + break; + case SdlCmdType::SetTitle: + SDL_SetWindowTitle(cmd.handle->hwnd, cmd.cmd_title.c_str()); + break; + case SdlCmdType::SetSize: + SDL_SetWindowSize(cmd.handle->hwnd, + cmd.cmd_set_size.first, + cmd.cmd_set_size.second); + break; + case SdlCmdType::SetPosition: + SDL_SetWindowPosition(cmd.handle->hwnd, + cmd.cmd_set_position.first, + cmd.cmd_set_position.second); + break; + default: + cerr << "Error in main thread: unknown window control command.\n"; + break; + } +} + +void SdlMainThread::probeGLContextSupport(bool legacyGlOnly) +{ + Uint32 win_flags_hidden = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; +#ifndef __EMSCRIPTEN__ + 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("Testing if OpenGL core profile window can be created..." << flush); + // Try to create core profile context first + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + { + Handle testCore("", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 100, 100, win_flags_hidden); + if (testCore.isInitialized()) + { + PRINT_DEBUG("success!" << endl); + return; + } + } + + PRINT_DEBUG("failed." << endl); + PRINT_DEBUG("Testing if OpenGL compatibility profile window can be created..." + << flush); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); + { + Handle testCompat("", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 100, 100, win_flags_hidden); + if (testCompat.isInitialized()) + { + PRINT_DEBUG("success!" << endl); + return; + } + } + PRINT_DEBUG("failed." << endl); + } + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); + if (GetMultisample() > 0) + { + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, GetMultisample()); + PRINT_DEBUG("Testing OpenGL with default profile and antialiasing..." << endl); + { + Handle testMsaa("", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 100, 100, win_flags_hidden); + if (testMsaa.isInitialized()) + { + PRINT_DEBUG("success!" << endl); + return; + } + } + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + PRINT_DEBUG("failed." << endl); + } + PRINT_DEBUG("Opening OpenGL window with no flags."); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_ES); + PRINT_DEBUG("Testing for WebGL 2.0 context availability..." << flush); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + { + Handle testWebGl2("", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 100, 100, win_flags_hidden); + if (testWebGl2.isInitialized()) + { + PRINT_DEBUG("success!" << endl); + return; + } + } + PRINT_DEBUG("failed." << endl); + PRINT_DEBUG("Testing for WebGL 1.0 context availability..." << flush); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + // Note that WebGL 2 support controllable antialiasing, while WebGL 1 only + // supports requesting multisampling at context creation time. The browser + // is free to ignore this flag. + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, GetMultisample()); + { + Handle testWebGl("", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 100, 100, win_flags_hidden); + if (testWebGl.isInitialized()) + { + PRINT_DEBUG("success!" << endl); + return; + } + } +#endif +} + +const int default_dpi = 72; +void SdlMainThread::getDpi(const Handle& handle, int& w, int& h) +{ + w = default_dpi; + h = default_dpi; + if (!handle.isInitialized()) + { + PRINT_DEBUG("warning: unable to get dpi: handle is null" << endl); + return; + } + int disp = SDL_GetWindowDisplayIndex(handle.hwnd); + if (disp < 0) + { + PRINT_DEBUG("warning: problem getting display index: " << SDL_GetError() + << endl); + PRINT_DEBUG("returning default dpi of " << default_dpi << endl); + return; + } + + float f_w, f_h; + if (SDL_GetDisplayDPI(disp, NULL, &f_w, &f_h)) + { + PRINT_DEBUG("warning: problem getting dpi, setting to " << default_dpi + << ": " << SDL_GetError() << endl); + } + else + { + PRINT_DEBUG("Screen DPI: w = " << f_w << " ppi, h = " << f_h << " ppi"); + w = f_w + 0.5f; + h = f_h + 0.5f; + PRINT_DEBUG(" (" << w << " x " << h << ')' << endl); + } +} + +void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd) +{ + Uint32 win_flags = SDL_WINDOW_OPENGL; + // Hide window until we adjust its size for high-dpi displays + win_flags |= SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN; +#ifndef __EMSCRIPTEN__ + win_flags |= SDL_WINDOW_RESIZABLE; +#endif + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24); + probeGLContextSupport(cmd.legacy_gl_only); + Handle new_handle(cmd.title, cmd.x, cmd.y, cmd.w, cmd.h, win_flags); + cmd.window_create_executed = true; + + // at this point, window should be up + if (!new_handle.isInitialized()) + { + PRINT_DEBUG("failed." << endl); + cerr << "FATAL: window and/or OpenGL context creation failed." << endl; + return; + } + else + { + PRINT_DEBUG("Handle for window created." << endl); + } + +#ifndef __EMSCRIPTEN__ + SDL_GL_SetSwapInterval(0); + glEnable(GL_DEBUG_OUTPUT); +#endif + + // Register window internally in the main thread so it can receive events + int wnd_id = SDL_GetWindowID(new_handle.hwnd); + hwnd_to_window[wnd_id] = cmd.wnd; + + if (!platform) + { + platform = SdlNativePlatform::Create(new_handle.hwnd); + } + if (platform) + { + platform->RegisterWindow(new_handle.hwnd); + } + +#ifdef GLVIS_USE_LOGO + const int PixelStride = 4; + int stride = (int) sqrt(logo_rgba_len / PixelStride); + if (unsigned(stride * stride * PixelStride) != logo_rgba_len) + { + cerr << "Unable to set window logo: icon size not square" << endl; + } + else + { + SDL_Surface* iconSurf = + SDL_CreateRGBSurfaceFrom(logo_rgba, + stride, stride, // height, width + 8 * PixelStride, // depth + stride * PixelStride, // pitch + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + if (iconSurf) + { + SDL_SetWindowIcon(new_handle.hwnd, iconSurf); + SDL_FreeSurface(iconSurf); + } + else + { + PRINT_DEBUG("Unable to set window logo: " << SDL_GetError() << endl); + } + } +#endif // GLVIS_USE_LOGO + + // Detect if we are using a high-dpi display and resize the window unless it + // was already resized by SDL's underlying backend. + { + SdlWindow* wnd = cmd.wnd; + int scr_w, scr_h, pix_w, pix_h, wdpi, hdpi; + // SDL_GetWindowSize() -- size in "screen coordinates" + SDL_GetWindowSize(new_handle.hwnd, &scr_w, &scr_h); + // SDL_GL_GetDrawableSize() -- size in pixels + SDL_GL_GetDrawableSize(new_handle.hwnd, &pix_w, &pix_h); + wnd->high_dpi = false; + wnd->pixel_scale_x = wnd->pixel_scale_y = 1.0f; + float sdl_pixel_scale_x = 1.0f, sdl_pixel_scale_y = 1.0f; + // If "screen" and "pixel" sizes are different, assume high-dpi and no + // need to scale the window. + if (scr_w == pix_w && scr_h == pix_h) + { + getDpi(new_handle, wdpi, hdpi); + if (std::max(wdpi, hdpi) >= SdlWindow::high_dpi_threshold) + { + wnd->high_dpi = true; + wnd->pixel_scale_x = wnd->pixel_scale_y = 2.0f; + // the following two calls use 'pixel_scale_*' + SDL_SetWindowSize(new_handle.hwnd, + wnd->pixel_scale_x*cmd.w, + wnd->pixel_scale_y*cmd.h); + bool uc_x = SDL_WINDOWPOS_ISUNDEFINED(cmd.x) || + SDL_WINDOWPOS_ISCENTERED(cmd.x); + bool uc_y = SDL_WINDOWPOS_ISUNDEFINED(cmd.y) || + SDL_WINDOWPOS_ISCENTERED(cmd.y); + SDL_SetWindowPosition(new_handle.hwnd, + uc_x ? cmd.x : wnd->pixel_scale_x*cmd.x, + uc_y ? cmd.y : wnd->pixel_scale_y*cmd.y); + } + } + else + { + wnd->high_dpi = true; + // keep 'pixel_scale_*' = 1, scaling is done inside SDL + sdl_pixel_scale_x = float(pix_w)/scr_w; + sdl_pixel_scale_y = float(pix_h)/scr_h; + } + if (wnd->high_dpi) + { + cout << "High-dpi display detected: using window scaling: " + << sdl_pixel_scale_x*wnd->pixel_scale_x << " x " + << sdl_pixel_scale_y*wnd->pixel_scale_y << endl; + } + } + + // Unset GL context in this thread + { + lock_guard ctx_lock{gl_ctx_mtx}; + SDL_GL_MakeCurrent(new_handle.hwnd, nullptr); + } + + SDL_ShowWindow(new_handle.hwnd); + if (num_windows == -1) + { + num_windows = 0; + } + num_windows++; + cmd.out_handle.set_value(std::move(new_handle)); +} diff --git a/lib/sdl_main.hpp b/lib/sdl_main.hpp new file mode 100644 index 00000000..7210c13b --- /dev/null +++ b/lib/sdl_main.hpp @@ -0,0 +1,130 @@ +// Copyright (c) 2010-2021, 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_SDL_MAIN_HPP +#define GLVIS_SDL_MAIN_HPP + +#include "sdl.hpp" + +class SdlMainThread +{ +private: + using Handle = SdlWindow::Handle; +public: + SdlMainThread(); + ~SdlMainThread(); + + // If called, all SDL window operations are run immediately; this is for + // running in a single-threaded mode. + void SetSingleThread() { sdl_multithread = false; } + + bool SdlInitialized() const { return sdl_init; } + + Uint32 GetCustomEvent() const { return glvis_event_type; } + + // 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); + + // Dequeues all incoming events from SDL, and queues them up to their + // matching windows. Intended to be called only in single-threaded mode. + void DispatchSDLEvents(); + + // Executed from a window worker thread. Returns a handle to a new window + // and associated OpenGL context. + Handle GetHandle(SdlWindow* wnd, const std::string& title, + int x, int y, int w, int h, bool legacyGlOnly); + + // Executed from a window worker thread. Deletes a handle to a window and + // the corresponding OpenGL context. + void DeleteHandle(Handle to_delete); + + // Issues a command on the main thread to set the window title. + void SetWindowTitle(const Handle& handle, std::string title); + + // Issues a command on the main thread to set the window size. + void SetWindowSize(const Handle& handle, int w, int h); + + // Issues a command on the main thread to set the window position. + void SetWindowPosition(const Handle& handle, int x, int y); + + // Wakes up the main thread, if sleeping. + void SendEvent() + { + if (platform) { platform->SendEvent(); } + } + +private: + struct CreateWindowCmd; + struct SdlCtrlCommand; + + enum class SdlCmdType + { + None, + Create, + Delete, + SetTitle, + SetSize, + SetPosition + }; + + void queueWindowEvent(SdlCtrlCommand cmd); + + template + Uint32 getWindowID(const T& eventStruct) + { + return eventStruct.windowID; + } + + bool exitDialog(); + + void handleWindowCmdImpl(SdlCtrlCommand& cmd); + + // Setup the correct OpenGL context flags in SDL for when we actually open the + // window. + void probeGLContextSupport(bool legacyGlOnly); + + void getDpi(const Handle& handle, int& wdpi, int& hdpi); + + void createWindowImpl(CreateWindowCmd& cmd); + + Uint32 glvis_event_type {(Uint32)-1}; + bool sdl_init {false}; + bool sdl_multithread {true}; + + bool server_mode {false}; + + atomic terminating {false}; + + // ------------------------------------------------------------------------- + // Objects for handling passing of window control commands to the main event + // loop. + + mutex window_cmd_mtx; + vector window_cmds; + + int num_windows {-1}; // -1: waiting for window to be created + + // ------------------------------------------------------------------------- + // Objects for handling dispatching events from the main event loop to + // worker threads. + + unordered_map hwnd_to_window; + unordered_map> wnd_events; + std::set fingers; + bool disable_mouse {false}; + + mutex gl_ctx_mtx; + + unique_ptr platform; +}; + +#endif diff --git a/makefile b/makefile index 748a68ae..2b8269ff 100644 --- a/makefile +++ b/makefile @@ -215,8 +215,9 @@ 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/font.cpp \ lib/gl2ps.c lib/material.cpp lib/openglvis.cpp lib/palettes.cpp lib/sdl.cpp \ - lib/sdl_helper.cpp lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp \ - lib/vssolution.cpp lib/vssolution3d.cpp lib/vsvector.cpp lib/vsvector3d.cpp + lib/sdl_helper.cpp lib/sdl_main.cpp lib/stream_reader.cpp lib/threads.cpp \ + lib/vsdata.cpp lib/vssolution.cpp lib/vssolution3d.cpp lib/vsvector.cpp \ + lib/vsvector3d.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 WEB_ONLY_SOURCE_FILES = lib/aux_js.cpp @@ -231,9 +232,9 @@ HEADER_FILES = \ lib/gl/shader.hpp lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp \ lib/gl/types.hpp lib/aux_vis.hpp lib/font.hpp lib/geom_utils.hpp lib/gl2ps.h \ lib/logo.hpp lib/material.hpp lib/openglvis.hpp lib/palettes.hpp lib/sdl.hpp \ - lib/sdl_helper.hpp lib/sdl_mac.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/sdl_helper.hpp lib/sdl_mac.hpp lib/sdl_main.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 DESKTOP_SOURCE_FILES = $(COMMON_SOURCE_FILES) $(DESKTOP_ONLY_SOURCE_FILES) $(LOGO_FILE_CPP) WEB_SOURCE_FILES = $(COMMON_SOURCE_FILES) $(WEB_ONLY_SOURCE_FILES) From a2bbbb1c5571a5458dfe4d23d133c1ea8dad9019 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 16:05:15 -0700 Subject: [PATCH 24/47] Fix build on gcc 4.9.3 again --- lib/sdl_main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp index 0d9676b4..c4061180 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl_main.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "sdl_main.hpp" #include "sdl_helper.hpp" From ef58810a749e288665d93f3355bf338d79c70c84 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 7 Jun 2021 16:17:52 -0700 Subject: [PATCH 25/47] Prune some headers --- lib/sdl.cpp | 4 ---- lib/sdl.hpp | 1 - lib/sdl_main.hpp | 3 +++ 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index b1cdb577..09950c6c 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -10,10 +10,6 @@ // CONTRIBUTING.md for details. #include -#include -#include -#include -#include "threads.hpp" #include "aux_vis.hpp" #include "gl/renderer_core.hpp" #include "gl/renderer_ff.hpp" diff --git a/lib/sdl.hpp b/lib/sdl.hpp index 95ddbc6f..dbe70e71 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/sdl_main.hpp b/lib/sdl_main.hpp index 7210c13b..d26d095f 100644 --- a/lib/sdl_main.hpp +++ b/lib/sdl_main.hpp @@ -12,6 +12,9 @@ #ifndef GLVIS_SDL_MAIN_HPP #define GLVIS_SDL_MAIN_HPP +#include +#include + #include "sdl.hpp" class SdlMainThread From dcca4a572d90923fb7aac6d4ea4a9f8cd31249a5 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Thu, 10 Jun 2021 16:05:11 -0700 Subject: [PATCH 26/47] Make some more variables thread_local --- lib/vssolution3d.cpp | 4 ++-- lib/vsvector.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/vssolution3d.cpp b/lib/vssolution3d.cpp index 27551664..6d7ad3a7 100644 --- a/lib/vssolution3d.cpp +++ b/lib/vssolution3d.cpp @@ -2825,8 +2825,8 @@ void VisualizationSceneSolution3d::PrepareCuttingPlaneLines2() } } -int triangle_counter; -int quad_counter; +thread_local int triangle_counter; +thread_local int quad_counter; void VisualizationSceneSolution3d::DrawTetLevelSurf( gl3::GlDrawable& target, diff --git a/lib/vsvector.cpp b/lib/vsvector.cpp index 65e64125..ae761902 100644 --- a/lib/vsvector.cpp +++ b/lib/vsvector.cpp @@ -839,7 +839,7 @@ void VisualizationSceneVector::PrepareDisplacedMesh() updated_bufs.emplace_back(&displine_buf); } -double new_maxlen; +thread_local double new_maxlen; void VisualizationSceneVector::DrawVector(double px, double py, double vx, double vy, double cval) From 26fb8b7aabfe635e442bd1706b4bb0f55277ec14 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 16 Jun 2021 12:30:13 -0700 Subject: [PATCH 27/47] Fix for non-parallel solution streams --- glvis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glvis.cpp b/glvis.cpp index d0aa94d0..1334b719 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -1109,7 +1109,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, if (!par_data) { new_session.ft = new_session.state.ReadStream(*isock, data_type); - new_session.input_streams.emplace_back(isock); + input_streams.emplace_back(isock); } else { From 5ba0dbc0f1c038061afefe5791d8f51d61232d4b Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 16 Jun 2021 12:33:30 -0700 Subject: [PATCH 28/47] Guard potential data race on platform variable with condition_variable --- lib/sdl_main.cpp | 11 ++++++++--- lib/sdl_main.hpp | 11 ++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp index c4061180..504e63a7 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl_main.cpp @@ -267,7 +267,7 @@ void SdlMainThread::queueWindowEvent(SdlCtrlCommand cmd) window_cmds.emplace_back(std::move(cmd)); } // wake up the main thread to handle our event - if (platform) { platform->SendEvent(); } + SendEvent(); } else { @@ -518,9 +518,14 @@ void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd) int wnd_id = SDL_GetWindowID(new_handle.hwnd); hwnd_to_window[wnd_id] = cmd.wnd; - if (!platform) { - platform = SdlNativePlatform::Create(new_handle.hwnd); + lock_guard platform_lk{event_mtx}; + if (!platform) + { + platform = SdlNativePlatform::Create(new_handle.hwnd); + try_create_platform = true; + } + event_cv.notify_all(); } if (platform) { diff --git a/lib/sdl_main.hpp b/lib/sdl_main.hpp index d26d095f..1ebed573 100644 --- a/lib/sdl_main.hpp +++ b/lib/sdl_main.hpp @@ -14,6 +14,7 @@ #include #include +#include #include "sdl.hpp" @@ -62,7 +63,12 @@ class SdlMainThread // Wakes up the main thread, if sleeping. void SendEvent() { - if (platform) { platform->SendEvent(); } + std::unique_lock platform_lk{event_mtx}; + event_cv.wait(platform_lk, [this]() { return try_create_platform; }); + if (platform) + { + platform->SendEvent(); + } } private: @@ -127,6 +133,9 @@ class SdlMainThread mutex gl_ctx_mtx; + mutex event_mtx; + condition_variable event_cv; + bool try_create_platform{false}; unique_ptr platform; }; From 3752cc2cd58f31fb1bd26a111765413cca39de84 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 16 Jun 2021 12:42:34 -0700 Subject: [PATCH 29/47] Fix thread leaks --- glvis.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 1334b719..8cebd4a1 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -881,13 +881,7 @@ struct Session , ft(other_ft) { } - ~Session() - { - if (handler.joinable()) - { - handler.join(); - } - } + ~Session() = default; Session(Session&& from) = default; Session& operator= (Session&& from) = default; @@ -907,6 +901,7 @@ struct Session }; handler = std::thread {funcThread, std::move(state), ft, std::move(input_streams)}; + handler.detach(); } bool StartSavedSession(std::string stream_file) From 19f2911e0b822010f724cc52c4dcce98cb526cd5 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 16 Jun 2021 12:49:43 -0700 Subject: [PATCH 30/47] Cleanup memory management --- glvis.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 8cebd4a1..36713b77 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -284,7 +284,6 @@ int ScriptReadSolution(istream &scr, StreamState& state) if (!isol) { cout << "Can not open solution file: " << sword << endl; - state.mesh.release(); return 2; } state.grid_f.reset(new GridFunction(state.mesh.get(), isol)); @@ -979,11 +978,11 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, while (1) { - socketstream *isock; + unique_ptr isock; #ifndef MFEM_USE_GNUTLS - isock = new socketstream; + isock.reset(new socketstream); #else - isock = secure ? new socketstream(*params) : new socketstream(false); + isock.reset(secure ? new socketstream(*params) : new socketstream(false)); #endif vector> input_streams; while (server.accept(*isock) < 0) @@ -1043,12 +1042,12 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, mfem_error(); } - input_streams[proc].reset(isock); + input_streams[proc] = std::move(isock); #ifndef MFEM_USE_GNUTLS - isock = new socketstream; + isock.reset(new socketstream); #else - isock = secure ? new socketstream(*params) : - new socketstream(false); + isock.reset(secure ? new socketstream(*params) : + new socketstream(false)); #endif np++; if (np == nproc) @@ -1104,11 +1103,10 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, if (!par_data) { new_session.ft = new_session.state.ReadStream(*isock, data_type); - input_streams.emplace_back(isock); + input_streams.emplace_back(std::move(isock)); } else { - delete isock; new_session.ft = ReadInputStreams(new_session.state, input_streams); } // Pass ownership of input streams into session object @@ -1116,7 +1114,6 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, new_session.StartSession(); } current_sessions.emplace_back(std::move(new_session)); - isock = nullptr; } } From 928813ce9300c990bf55f200a0db8bacb6a9a7ee Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 16 Jun 2021 13:55:34 -0700 Subject: [PATCH 31/47] Fix data race in palette init --- lib/palettes.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/palettes.cpp b/lib/palettes.cpp index c6d51a7c..066bfb71 100644 --- a/lib/palettes.cpp +++ b/lib/palettes.cpp @@ -17,6 +17,7 @@ #include #include #include +#include using namespace std; @@ -7695,6 +7696,8 @@ PaletteState::PaletteState() { } +static std::mutex init_mtx; + void PaletteState::Init() { if (!first_init) @@ -7705,7 +7708,10 @@ void PaletteState::Init() cerr << "Warning: GL_MAX_TEXTURE_SIZE is less than 4096." << endl; } MaxTextureSize = std::min(MaxTextureSize, 4096); - Init_Palettes(); + { + std::lock_guard lk{init_mtx}; + Init_Palettes(); + } GLuint paletteTexIds[Num_RGB_Palettes][2]; GLuint alphaTexId; From abc46abe618f0f026d9204f03d0e2a1424fb0af6 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 16 Jun 2021 15:37:39 -0700 Subject: [PATCH 32/47] Make glvis quit handling more robust --- lib/sdl_main.cpp | 149 +++++++++++++++++++++++++++++++++-------------- lib/sdl_main.hpp | 5 +- 2 files changed, 110 insertions(+), 44 deletions(-) diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp index 504e63a7..bd9b3b80 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl_main.cpp @@ -68,6 +68,7 @@ SdlMainThread::SdlMainThread() SDL_GetVersion(&sdl_ver); std::cerr << "Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor << "." << (int)sdl_ver.patch << std::endl; + sdl_init = true; } @@ -89,13 +90,53 @@ void SdlMainThread::MainLoop(bool server_mode) { handleWindowCmdImpl(cmd); } - if (!server_mode && num_windows == 0) + // Check if all windows have been closed. Exiting via shortcut 'q' won't + // generate an SDL_QUIT event that can be handled in DispatchSDLEvents(). + if (num_windows == 0) { - terminating = true; // all windows closed + terminating = true; } DispatchSDLEvents(); - if (terminating) { break; } + + // If we're running in server mode, prompt to make sure user wants to + // exit out of server process + if (terminating && server_mode) + { + bool queryQuit = exitDialog(); + if (!queryQuit) { terminating = false; } + } + + // At this point, we're ready to start cleaning up + if (terminating) + { + // Signal worker threads of all remaining open windows to terminate + for (auto wnds : hwnd_to_window) + { + SDL_Event sdl_quit; + sdl_quit.type = SDL_QUIT; + wnds.second->queueEvents({sdl_quit}); + } + while (num_windows > 0) + { + // Process pending destroy window commands + vector pending_cmds; + { + lock_guard cmd_lock{window_cmd_mtx}; + pending_cmds = std::move(window_cmds); + } + for (SdlCtrlCommand& cmd : pending_cmds) + { + if (cmd.type == SdlCmdType::Delete) + { + handleWindowCmdImpl(cmd); + } + } + } + // Exit main loop + break; + } + // Wait for the next event (without consuming CPU cycles, if possible) // See also: SdlWindow::signalLoop() if (platform) @@ -122,14 +163,7 @@ void SdlMainThread::DispatchSDLEvents() switch (e.type) { case SDL_QUIT: - if (server_mode) - { - terminating = exitDialog(); - } - else - { - terminating = true; - } + terminating = true; break; case SDL_WINDOWEVENT: windowId = getWindowID(e.window); @@ -278,6 +312,24 @@ void SdlMainThread::queueWindowEvent(SdlCtrlCommand cmd) bool SdlMainThread::exitDialog() { + // Create an empty window so we can raise focus + int flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_ALLOW_HIGHDPI; + SDL_Window* dialog_handle = SDL_CreateWindow("GLVis Server", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 400, + 400, + flags); + + // Destroys dialong_handle at end of scope + unique_ptr + delete_wnd(dialog_handle, SDL_DestroyWindow); + + setWindowIcon(dialog_handle); + + SDL_ShowWindow(dialog_handle); + SDL_RaiseWindow(dialog_handle); + const int ID_Exit = 1; const int ID_Continue = 2; const SDL_MessageBoxButtonData buttons[] = @@ -286,12 +338,17 @@ bool SdlMainThread::exitDialog() {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, ID_Continue, "No"} }; + std::string serverOnly = "Stop GLVis server thread?"; + std::string serverAndWindows = + "Stop GLVis server thread and close all windows?"; + const SDL_MessageBoxData request_box { SDL_MESSAGEBOX_INFORMATION, - nullptr, - "GLVis Server", - "Stop GLVis server thread?", + dialog_handle, + "", + ((num_windows > 0) ? serverAndWindows.c_str() + : serverOnly.c_str()), 2, buttons, nullptr @@ -346,6 +403,39 @@ void SdlMainThread::handleWindowCmdImpl(SdlCtrlCommand& cmd) } } +void SdlMainThread::setWindowIcon(SDL_Window* hwnd) +{ +#ifdef GLVIS_USE_LOGO + const int PixelStride = 4; + int stride = (int) sqrt(logo_rgba_len / PixelStride); + if (unsigned(stride * stride * PixelStride) != logo_rgba_len) + { + cerr << "Unable to set window logo: icon size not square" << endl; + } + else + { + SDL_Surface* iconSurf = + SDL_CreateRGBSurfaceFrom(logo_rgba, + stride, stride, // height, width + 8 * PixelStride, // depth + stride * PixelStride, // pitch + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + if (iconSurf) + { + SDL_SetWindowIcon(hwnd, iconSurf); + SDL_FreeSurface(iconSurf); + } + else + { + PRINT_DEBUG("Unable to set window logo: " << SDL_GetError() << endl); + } + } +#endif // GLVIS_USE_LOGO +} + void SdlMainThread::probeGLContextSupport(bool legacyGlOnly) { Uint32 win_flags_hidden = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; @@ -532,35 +622,7 @@ void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd) platform->RegisterWindow(new_handle.hwnd); } -#ifdef GLVIS_USE_LOGO - const int PixelStride = 4; - int stride = (int) sqrt(logo_rgba_len / PixelStride); - if (unsigned(stride * stride * PixelStride) != logo_rgba_len) - { - cerr << "Unable to set window logo: icon size not square" << endl; - } - else - { - SDL_Surface* iconSurf = - SDL_CreateRGBSurfaceFrom(logo_rgba, - stride, stride, // height, width - 8 * PixelStride, // depth - stride * PixelStride, // pitch - 0x000000FF, - 0x0000FF00, - 0x00FF0000, - 0xFF000000); - if (iconSurf) - { - SDL_SetWindowIcon(new_handle.hwnd, iconSurf); - SDL_FreeSurface(iconSurf); - } - else - { - PRINT_DEBUG("Unable to set window logo: " << SDL_GetError() << endl); - } - } -#endif // GLVIS_USE_LOGO + setWindowIcon(new_handle.hwnd); // Detect if we are using a high-dpi display and resize the window unless it // was already resized by SDL's underlying backend. @@ -618,6 +680,7 @@ void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd) } SDL_ShowWindow(new_handle.hwnd); + SDL_RaiseWindow(new_handle.hwnd); if (num_windows == -1) { num_windows = 0; diff --git a/lib/sdl_main.hpp b/lib/sdl_main.hpp index 1ebed573..62c7f9bf 100644 --- a/lib/sdl_main.hpp +++ b/lib/sdl_main.hpp @@ -95,6 +95,8 @@ class SdlMainThread bool exitDialog(); + void setWindowIcon(SDL_Window* hwnd); + void handleWindowCmdImpl(SdlCtrlCommand& cmd); // Setup the correct OpenGL context flags in SDL for when we actually open the @@ -111,7 +113,8 @@ class SdlMainThread bool server_mode {false}; - atomic terminating {false}; + // A flag indicating whether the main loop will *begin* terminating + bool terminating {false}; // ------------------------------------------------------------------------- // Objects for handling passing of window control commands to the main event From e23658385c30e76a0696c378cc0faca4885c65c7 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 16 Jun 2021 16:10:42 -0700 Subject: [PATCH 33/47] Make captions thread_local --- glvis.cpp | 14 +++++++++++--- lib/vsdata.hpp | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/glvis.cpp b/glvis.cpp index 36713b77..86dcd716 100644 --- a/glvis.cpp +++ b/glvis.cpp @@ -46,8 +46,8 @@ int window_w = 400; int window_h = 350; const char *window_title = string_default; const char *c_plot_caption = string_none; -string plot_caption; -string extra_caption; +thread_local string plot_caption; +thread_local string extra_caption; // Global variables int input = 1; @@ -841,6 +841,10 @@ void PlayScript(istream &scr) { // set the thread-local StreamState stream_state = std::move(local_state); + if (c_plot_caption != string_none) + { + plot_caption = c_plot_caption; + } if (GLVisInitVis((stream_state.grid_f->VectorDim() == 1) ? 0 : 1, {})) { GetAppWindow()->setOnKeyDown(SDLK_SPACE, ScriptControl); @@ -892,6 +896,10 @@ struct Session { // Set thread-local stream state stream_state = std::move(state); + if (c_plot_caption != string_none) + { + plot_caption = c_plot_caption; + } if (GLVisInitVis(ft, std::move(is))) { @@ -1047,7 +1055,7 @@ void GLVisServer(int portnum, bool mac, bool fix_elem_orient, isock.reset(new socketstream); #else isock.reset(secure ? new socketstream(*params) : - new socketstream(false)); + new socketstream(false)); #endif np++; if (np == nproc) diff --git a/lib/vsdata.hpp b/lib/vsdata.hpp index cf26ab38..975de0bb 100644 --- a/lib/vsdata.hpp +++ b/lib/vsdata.hpp @@ -19,8 +19,8 @@ using namespace mfem; -extern std::string plot_caption; // defined in glvis.cpp -extern std::string extra_caption; // defined in glvis.cpp +extern thread_local std::string plot_caption; // defined in glvis.cpp +extern thread_local std::string extra_caption; // defined in glvis.cpp class Plane { From dfb14c1726f2962b4aa71a36be33b9a8d6ea7fa3 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Mon, 21 Jun 2021 11:08:04 -0700 Subject: [PATCH 34/47] Better fix for Cocoa resize issue --- lib/sdl.cpp | 40 ++++++++++++++++++++++------------------ lib/sdl_mac.hpp | 2 ++ lib/sdl_mac.mm | 9 +++++++++ lib/sdl_main.hpp | 2 ++ 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 09950c6c..5c89d281 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -20,6 +20,9 @@ #include #endif +#ifdef SDL_VIDEO_DRIVER_COCOA +#include "sdl_mac.hpp" +#endif using std::cerr; using std::endl; @@ -189,8 +192,25 @@ void SdlWindow::windowEvent(SDL_WindowEvent& ew) break; case SDL_WINDOWEVENT_MOVED: case SDL_WINDOWEVENT_RESIZED: - swap_before_expose = true; - break; +#ifdef SDL_VIDEO_DRIVER_COCOA + { + // On SDL, when the OpenGL context is on a separate thread from the + // main thread, the call to [NSOpenGLContext update] after a resize or + // move event is only scheduled for after the next swap event. Any + // rendering/OpenGL commands occuring before this update will be + // corrupted. + // + // To avoid this issue, we just call [NSOpenGLContext update] + // immediately after the resize/move event. + SdlCocoaPlatform* platform = + dynamic_cast(GetMainThread().GetPlatform()); + if (platform) + { + platform->ContextUpdate(); + } + } +#endif + break; default: break; } @@ -426,22 +446,6 @@ void SdlWindow::mainIter() } if (wnd_state == RenderState::ExposePending) { -#ifdef SDL_VIDEO_DRIVER_COCOA - // There is some weird behavior with SDL on Cocoa, where the swap - // immediately following a resize event causes the most recent onExpose() - // to display incorrectly. I suspect it has something to do with - // [NSOpenGLContext update] getting called after the context flush within - // Cocoa_GL_SwapBuffers(). - // - // To work around this, we do an extra swap before onExpose() so that the - // [NSOpenGLContext update] call is done before we start rendering to the - // back buffer. - if (swap_before_expose) - { - SDL_GL_SwapWindow(handle.hwnd); - swap_before_expose = false; - } -#endif onExpose(); wnd_state = RenderState::SwapPending; } diff --git a/lib/sdl_mac.hpp b/lib/sdl_mac.hpp index edbff026..7fed427c 100644 --- a/lib/sdl_mac.hpp +++ b/lib/sdl_mac.hpp @@ -18,6 +18,8 @@ class SdlCocoaPlatform final : public SdlNativePlatform public: void WaitEvent(); void SendEvent(); + + void ContextUpdate(); }; #endif diff --git a/lib/sdl_mac.mm b/lib/sdl_mac.mm index 0593c677..edde7c8e 100644 --- a/lib/sdl_mac.mm +++ b/lib/sdl_mac.mm @@ -36,3 +36,12 @@ data2:0] atStart:NO]; }} + +void SdlCocoaPlatform::ContextUpdate() +{ @autoreleasepool +{ + // This actually calls [SDLOpenGLContext update], which handles the dispatch + // to the main thread for us. + [[NSOpenGLContext currentContext] update]; +} +} diff --git a/lib/sdl_main.hpp b/lib/sdl_main.hpp index 62c7f9bf..750c3874 100644 --- a/lib/sdl_main.hpp +++ b/lib/sdl_main.hpp @@ -71,6 +71,8 @@ class SdlMainThread } } + SdlNativePlatform* GetPlatform() const { return platform.get(); } + private: struct CreateWindowCmd; struct SdlCtrlCommand; From d10bc970ef73e04be0747cedda32450292b16ac3 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Thu, 24 Jun 2021 14:27:22 -0700 Subject: [PATCH 35/47] Workaround for SDL 2.0.12 --- lib/sdl.cpp | 15 +++++++ lib/sdl_mac.hpp | 6 +++ lib/sdl_mac.mm | 113 +++++++++++++++++++++++++++++++++++++---------- lib/sdl_main.cpp | 36 ++++++++++++++- 4 files changed, 145 insertions(+), 25 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 5c89d281..9b6e39fe 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -465,7 +465,22 @@ void SdlWindow::mainLoop() mainIter(); if (wnd_state == RenderState::SwapPending) { +#ifdef SDL_VIDEO_DRIVER_COCOA + // TODO: Temporary workaround - after merge, everyone should update to + // latest SDL + SdlCocoaPlatform* mac_platform + = dynamic_cast(GetMainThread().GetPlatform()); + if (mac_platform && mac_platform->UseThreadWorkaround()) + { + mac_platform->SwapWindow(); + } + else + { + SDL_GL_SwapWindow(handle.hwnd); + } +#else SDL_GL_SwapWindow(handle.hwnd); +#endif wnd_state = RenderState::Updated; } if (takeScreenshot) diff --git a/lib/sdl_mac.hpp b/lib/sdl_mac.hpp index 7fed427c..f06d210a 100644 --- a/lib/sdl_mac.hpp +++ b/lib/sdl_mac.hpp @@ -20,6 +20,12 @@ class SdlCocoaPlatform final : public SdlNativePlatform void SendEvent(); void ContextUpdate(); + + // TODO: Workaround methods for SDL 2.0.12, delete in follow-up PR + bool UseThreadWorkaround() const; + void ClearCurrentContext(int wnd_id); + void SetCurrentContext(int wnd_id); + void SwapWindow(); }; #endif diff --git a/lib/sdl_mac.mm b/lib/sdl_mac.mm index edde7c8e..6550b94d 100644 --- a/lib/sdl_mac.mm +++ b/lib/sdl_mac.mm @@ -12,36 +12,101 @@ #include "sdl_mac.hpp" #import +#include +#include + +std::unordered_map& GetContextMap() +{ + static std::unordered_map inst; + return inst; +} + +inline bool AtLeastVersion(SDL_version sdl_ver, int major, int minor, int patch) +{ + return ((sdl_ver.major > major) || + (sdl_ver.major == major && sdl_ver.minor > minor) || + (sdl_ver.major == major && sdl_ver.minor == minor && sdl_ver.patch > patch)); +} + void SdlCocoaPlatform::WaitEvent() -{ @autoreleasepool { - [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantFuture] - inMode:NSDefaultRunLoopMode - dequeue:NO]; -}} + @autoreleasepool + { + [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:NO]; + } +} void SdlCocoaPlatform::SendEvent() -{ @autoreleasepool -{ - NSPoint loc = {0., 0.}; - [NSApp postEvent:[NSEvent otherEventWithType:NSEventTypeApplicationDefined - location:loc - modifierFlags:0 - timestamp:0.0 - windowNumber:0 - context:nil - subtype:0 - data1:0 - data2:0] - atStart:NO]; -}} +{ + @autoreleasepool + { + NSPoint loc = {0., 0.}; + [NSApp postEvent:[NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:loc + modifierFlags:0 + timestamp:0.0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0] + atStart:NO]; + } +} + +bool SdlCocoaPlatform::UseThreadWorkaround() const +{ + static bool first_call = true; + static bool value = false; + if (first_call) + { + SDL_version sdl_ver; + SDL_GetVersion(&sdl_ver); + value = !AtLeastVersion(sdl_ver, 2, 0, 14); + first_call = false; + } + return value; +} void SdlCocoaPlatform::ContextUpdate() -{ @autoreleasepool { - // This actually calls [SDLOpenGLContext update], which handles the dispatch - // to the main thread for us. - [[NSOpenGLContext currentContext] update]; + @autoreleasepool + { + NSOpenGLContext* ctx = [NSOpenGLContext currentContext]; + // This calls [SDLOpenGLContext update] defined in SDL_cocoaopengl.m + dispatch_sync(dispatch_get_main_queue(), ^{ [ctx update]; }); + } +} + +void SdlCocoaPlatform::ClearCurrentContext(int wnd_id) +{ + @autoreleasepool + { + NSOpenGLContext* ctx = [NSOpenGLContext currentContext]; + GetContextMap().emplace(wnd_id, ctx); + [NSOpenGLContext clearCurrentContext]; + } } + +void SdlCocoaPlatform::SetCurrentContext(int wnd_id) +{ + @autoreleasepool + { + NSOpenGLContext* ctx = GetContextMap()[wnd_id]; + [ctx makeCurrentContext]; + } +} + +std::mutex swap_mtx; + +void SdlCocoaPlatform::SwapWindow() +{ + @autoreleasepool + { + std::lock_guard lk{swap_mtx}; + [[NSOpenGLContext currentContext] flushBuffer]; + } } diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp index bd9b3b80..add3a38b 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl_main.cpp @@ -17,6 +17,10 @@ #include "sdl_helper.hpp" #include "logo.hpp" +#ifdef SDL_VIDEO_DRIVER_COCOA +#include "sdl_mac.hpp" +#endif + #ifdef GLVIS_DEBUG #define PRINT_DEBUG(s) std::cerr << s #else @@ -47,7 +51,6 @@ struct SdlMainThread::SdlCtrlCommand pair cmd_set_position; }; - SdlMainThread::SdlMainThread() { if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) @@ -247,7 +250,23 @@ SdlMainThread::Handle SdlMainThread::GetHandle(SdlWindow* wnd, // Since SDL calls aren't guaranteed to be thread-safe, we guard // the call to SDL_GL_MakeCurrent. lock_guard ctx_lock{gl_ctx_mtx}; +#ifdef SDL_VIDEO_DRIVER_COCOA + // TODO: Temporary workaround - after merge, everyone should update to + // latest SDL + SdlCocoaPlatform* mac_platform + = dynamic_cast(platform.get()); + if (mac_platform && mac_platform->UseThreadWorkaround()) + { + int wnd_id = SDL_GetWindowID(out_hnd.hwnd); + mac_platform->SetCurrentContext(wnd_id); + } + else + { + SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx); + } +#else SDL_GL_MakeCurrent(out_hnd.hwnd, out_hnd.gl_ctx); +#endif } return out_hnd; } @@ -676,7 +695,22 @@ void SdlMainThread::createWindowImpl(CreateWindowCmd& cmd) // Unset GL context in this thread { lock_guard ctx_lock{gl_ctx_mtx}; +#ifdef SDL_VIDEO_DRIVER_COCOA + // TODO: Temporary workaround - after merge, everyone should update to + // latest SDL + SdlCocoaPlatform* mac_platform + = dynamic_cast(platform.get()); + if (mac_platform && mac_platform->UseThreadWorkaround()) + { + mac_platform->ClearCurrentContext(wnd_id); + } + else + { + SDL_GL_MakeCurrent(new_handle.hwnd, nullptr); + } +#else SDL_GL_MakeCurrent(new_handle.hwnd, nullptr); +#endif } SDL_ShowWindow(new_handle.hwnd); From dacf67cd7547ba47fffdbebd3547c6a56b7fee9c Mon Sep 17 00:00:00 2001 From: Max Yang Date: Tue, 29 Jun 2021 12:56:48 -0700 Subject: [PATCH 36/47] Restore old behavior of server process, use glvis icon in dock with hidden window --- lib/sdl_main.cpp | 44 +++++++++++++++++++++++++++++++++----------- lib/sdl_main.hpp | 3 +++ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp index add3a38b..039ea4cc 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl_main.cpp @@ -80,6 +80,22 @@ SdlMainThread::~SdlMainThread() = default; void SdlMainThread::MainLoop(bool server_mode) { this->server_mode = server_mode; + if (server_mode) + { + // Create a hidden window for dock icon and suppressing SDL_QUIT on last + // vis window closing + int flags = SDL_WINDOW_HIDDEN + | SDL_WINDOW_BORDERLESS + | SDL_WINDOW_ALLOW_HIGHDPI; + SDL_Window* bg_wnd_handle = SDL_CreateWindow("GLVis", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 1, + 1, + flags); + setWindowIcon(bg_wnd_handle); + bg_wnd.reset(bg_wnd_handle); + } while (1) { // Process all pending window commands @@ -93,23 +109,16 @@ void SdlMainThread::MainLoop(bool server_mode) { handleWindowCmdImpl(cmd); } - // Check if all windows have been closed. Exiting via shortcut 'q' won't - // generate an SDL_QUIT event that can be handled in DispatchSDLEvents(). - if (num_windows == 0) + // Check if all windows have been closed, and we're not in server mode. + // Exiting via shortcut 'q' won't generate an SDL_QUIT event that can be + // handled in DispatchSDLEvents(). + if (num_windows == 0 && !server_mode) { terminating = true; } DispatchSDLEvents(); - // If we're running in server mode, prompt to make sure user wants to - // exit out of server process - if (terminating && server_mode) - { - bool queryQuit = exitDialog(); - if (!queryQuit) { terminating = false; } - } - // At this point, we're ready to start cleaning up if (terminating) { @@ -170,6 +179,10 @@ void SdlMainThread::DispatchSDLEvents() break; case SDL_WINDOWEVENT: windowId = getWindowID(e.window); + if (server_mode && (windowId == SDL_GetWindowID(bg_wnd.get()))) + { + handleBackgroundWindowEvent(e.window); + } break; case SDL_FINGERDOWN: fingers.insert(e.tfinger.fingerId); @@ -230,6 +243,15 @@ void SdlMainThread::DispatchSDLEvents() } } +void SdlMainThread::handleBackgroundWindowEvent(SDL_WindowEvent e) +{ + // Background window should always stay hidden. + if (e.event == SDL_WINDOWEVENT_SHOWN) + { + SDL_HideWindow(bg_wnd.get()); + } +} + SdlMainThread::Handle SdlMainThread::GetHandle(SdlWindow* wnd, const std::string& title, int x, int y, int w, int h, bool legacyGlOnly) diff --git a/lib/sdl_main.hpp b/lib/sdl_main.hpp index 750c3874..50fbff64 100644 --- a/lib/sdl_main.hpp +++ b/lib/sdl_main.hpp @@ -109,6 +109,8 @@ class SdlMainThread void createWindowImpl(CreateWindowCmd& cmd); + void handleBackgroundWindowEvent(SDL_WindowEvent e); + Uint32 glvis_event_type {(Uint32)-1}; bool sdl_init {false}; bool sdl_multithread {true}; @@ -117,6 +119,7 @@ class SdlMainThread // A flag indicating whether the main loop will *begin* terminating bool terminating {false}; + unique_ptr bg_wnd{nullptr, SDL_DestroyWindow}; // ------------------------------------------------------------------------- // Objects for handling passing of window control commands to the main event From 9f3fb8bccc68c5c13d7370d6bf2640e83cc1469a Mon Sep 17 00:00:00 2001 From: Max Yang Date: Tue, 29 Jun 2021 13:08:10 -0700 Subject: [PATCH 37/47] Delete quitDialog function --- lib/sdl_main.cpp | 53 ------------------------------------------------ lib/sdl_main.hpp | 2 -- 2 files changed, 55 deletions(-) diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp index 039ea4cc..30295daf 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl_main.cpp @@ -351,59 +351,6 @@ void SdlMainThread::queueWindowEvent(SdlCtrlCommand cmd) } } -bool SdlMainThread::exitDialog() -{ - // Create an empty window so we can raise focus - int flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_ALLOW_HIGHDPI; - SDL_Window* dialog_handle = SDL_CreateWindow("GLVis Server", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - 400, - 400, - flags); - - // Destroys dialong_handle at end of scope - unique_ptr - delete_wnd(dialog_handle, SDL_DestroyWindow); - - setWindowIcon(dialog_handle); - - SDL_ShowWindow(dialog_handle); - SDL_RaiseWindow(dialog_handle); - - const int ID_Exit = 1; - const int ID_Continue = 2; - const SDL_MessageBoxButtonData buttons[] = - { - {0, ID_Exit, "Yes"}, - {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, ID_Continue, "No"} - }; - - std::string serverOnly = "Stop GLVis server thread?"; - std::string serverAndWindows = - "Stop GLVis server thread and close all windows?"; - - const SDL_MessageBoxData request_box - { - SDL_MESSAGEBOX_INFORMATION, - dialog_handle, - "", - ((num_windows > 0) ? serverAndWindows.c_str() - : serverOnly.c_str()), - 2, - buttons, - nullptr - }; - - int button_id; - if (SDL_ShowMessageBox(&request_box, &button_id) < 0) - { - return true; - } - - return button_id == ID_Exit; -} - void SdlMainThread::handleWindowCmdImpl(SdlCtrlCommand& cmd) { switch (cmd.type) diff --git a/lib/sdl_main.hpp b/lib/sdl_main.hpp index 50fbff64..20670206 100644 --- a/lib/sdl_main.hpp +++ b/lib/sdl_main.hpp @@ -95,8 +95,6 @@ class SdlMainThread return eventStruct.windowID; } - bool exitDialog(); - void setWindowIcon(SDL_Window* hwnd); void handleWindowCmdImpl(SdlCtrlCommand& cmd); From 4dac77dee6c35db6c00dc630abc616210850c58f Mon Sep 17 00:00:00 2001 From: Max Yang Date: Tue, 29 Jun 2021 14:03:23 -0700 Subject: [PATCH 38/47] Fix makefile --- makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makefile b/makefile index 2b8269ff..0a011bc6 100644 --- a/makefile +++ b/makefile @@ -125,6 +125,9 @@ GLVIS_MS_LINEWIDTH ?= $(if $(NOTMAC),1.4,1.0) DEFINES = -DGLVIS_MULTISAMPLE=$(GLVIS_MULTISAMPLE)\ -DGLVIS_MS_LINEWIDTH=$(GLVIS_MS_LINEWIDTH)\ -DGLVIS_OGL3 +# Enable logo setting via SDL (disabled on Windows/CMake build) +DEFINES += -DGLVIS_USE_LOGO + GLVIS_FLAGS += $(DEFINES) # We don't want most of the stuff below because Emscripten handles that for us From 37bdedb4883af01d41517d9c05ec21cf6df34905 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 7 Jul 2021 10:36:40 -0700 Subject: [PATCH 39/47] Call [NSOpenGLContext update] after window geometry changes from threads.cpp --- lib/sdl.cpp | 46 +++++++++++++++++++++++++--------------------- lib/sdl.hpp | 2 +- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 9b6e39fe..7f6d7688 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -192,25 +192,8 @@ void SdlWindow::windowEvent(SDL_WindowEvent& ew) break; case SDL_WINDOWEVENT_MOVED: case SDL_WINDOWEVENT_RESIZED: -#ifdef SDL_VIDEO_DRIVER_COCOA - { - // On SDL, when the OpenGL context is on a separate thread from the - // main thread, the call to [NSOpenGLContext update] after a resize or - // move event is only scheduled for after the next swap event. Any - // rendering/OpenGL commands occuring before this update will be - // corrupted. - // - // To avoid this issue, we just call [NSOpenGLContext update] - // immediately after the resize/move event. - SdlCocoaPlatform* platform = - dynamic_cast(GetMainThread().GetPlatform()); - if (platform) - { - platform->ContextUpdate(); - } - } -#endif - break; + update_before_expose = true; + break; default: break; } @@ -446,6 +429,26 @@ void SdlWindow::mainIter() } if (wnd_state == RenderState::ExposePending) { +#ifdef SDL_VIDEO_DRIVER_COCOA + if (update_before_expose) + { + // On SDL, when the OpenGL context is on a separate thread from the + // main thread, the call to [NSOpenGLContext update] after a resize or + // move event is only scheduled for after the next swap event. Any + // rendering/OpenGL commands occuring before this update will be + // corrupted. + // + // To avoid this issue, we just call [NSOpenGLContext update] + // immediately before the expose event. + SdlCocoaPlatform* platform = + dynamic_cast(GetMainThread().GetPlatform()); + if (platform) + { + platform->ContextUpdate(); + } + update_before_expose = false; + } +#endif onExpose(); wnd_state = RenderState::SwapPending; } @@ -579,7 +582,8 @@ void SdlWindow::setWindowTitle(const char * title) void SdlWindow::setWindowSize(int w, int h) { GetMainThread().SetWindowSize(handle, pixel_scale_x*w, pixel_scale_y*h); - swap_before_expose = true; + update_before_expose = true; + } void SdlWindow::setWindowPos(int x, int y) @@ -591,7 +595,7 @@ void SdlWindow::setWindowPos(int x, int y) GetMainThread().SetWindowPosition(handle, uc_x ? x : pixel_scale_x*x, uc_y ? y : pixel_scale_y*y); - swap_before_expose = true; + update_before_expose = true; } void SdlWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m) diff --git a/lib/sdl.hpp b/lib/sdl.hpp index dbe70e71..5f6034b0 100644 --- a/lib/sdl.hpp +++ b/lib/sdl.hpp @@ -120,7 +120,7 @@ class SdlWindow RenderState wnd_state{RenderState::Updated}; - bool swap_before_expose{false}; + bool update_before_expose{false}; //bool requiresExpose; bool takeScreenshot{false}; From e38089270db4aefbb482e415b49f117781929c4a Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 7 Jul 2021 12:24:59 -0700 Subject: [PATCH 40/47] Ensure context is updated when updateLayer is called as well (expose event) --- lib/sdl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 7f6d7688..664ceb09 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -182,6 +182,7 @@ void SdlWindow::windowEvent(SDL_WindowEvent& ew) switch (ew.event) { case SDL_WINDOWEVENT_EXPOSED: + update_before_expose = true; if (onExpose) { wnd_state = RenderState::ExposePending; From 8b27bd8a05f1efc10ce3a724046192582a356b51 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 7 Jul 2021 13:23:15 -0700 Subject: [PATCH 41/47] Fix qutting glvis server with Ctrl-C --- lib/sdl_mac.mm | 4 +++- lib/sdl_x11.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/sdl_mac.mm b/lib/sdl_mac.mm index 6550b94d..0e45898b 100644 --- a/lib/sdl_mac.mm +++ b/lib/sdl_mac.mm @@ -32,8 +32,10 @@ inline bool AtLeastVersion(SDL_version sdl_ver, int major, int minor, int patch) { @autoreleasepool { + // We timeout the poll call after 500ms, just in case a pending SDL_QUIT + // needs to be pumped into the SDL event queue [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantFuture] + untilDate:[NSDate dateWithTimeIntervalSinceNow:0.500] inMode:NSDefaultRunLoopMode dequeue:NO]; } diff --git a/lib/sdl_x11.cpp b/lib/sdl_x11.cpp index bdf316e4..5f382269 100644 --- a/lib/sdl_x11.cpp +++ b/lib/sdl_x11.cpp @@ -109,7 +109,9 @@ void SdlX11Platform::WaitEvent() int nstr; do { - nstr = poll(pfd.data(), pfd.size(), -1); + // We timeout the poll call after 500ms, just in case a pending SDL_QUIT + // needs to be pumped into the SDL event queue + nstr = poll(pfd.data(), pfd.size(), 500); } while (nstr == -1 && errno == EINTR); From 57fbf46edbd8753ea171abd29c728837af52412d Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 7 Jul 2021 13:24:14 -0700 Subject: [PATCH 42/47] Fix version check for workaround --- lib/sdl_mac.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sdl_mac.mm b/lib/sdl_mac.mm index 0e45898b..a884d782 100644 --- a/lib/sdl_mac.mm +++ b/lib/sdl_mac.mm @@ -25,7 +25,7 @@ inline bool AtLeastVersion(SDL_version sdl_ver, int major, int minor, int patch) { return ((sdl_ver.major > major) || (sdl_ver.major == major && sdl_ver.minor > minor) || - (sdl_ver.major == major && sdl_ver.minor == minor && sdl_ver.patch > patch)); + (sdl_ver.major == major && sdl_ver.minor == minor && sdl_ver.patch >= patch)); } void SdlCocoaPlatform::WaitEvent() From 2b843477ee1cd9063ce676ca7ddb7cc86a5c126a Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 7 Jul 2021 13:50:07 -0700 Subject: [PATCH 43/47] Update CHANGELOG --- CHANGELOG | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 7e1a6018..35cf3b97 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,13 @@ Version 4.0.1 (development) =========================== +- Use threads in server mode for window creation and session management + instead of fork(). This resolves some issues with running GLVis in server + mode on macOS. This also allows for closing all open GLVis windows associated + with the server by using Ctrl-C in the terminal. + +- Preliminary support for building natively on Windows via CMake. + - Refactored glvis.cpp to fix a bug in GLVis script handling. - Enabled support for WebGL 2, when available. This enables, among other From 2e0e0a7c20e432a1a56bfd0e9d6f1072407e088f Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 7 Jul 2021 13:51:31 -0700 Subject: [PATCH 44/47] Only print SDL version in debug mode --- lib/sdl_main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/sdl_main.cpp b/lib/sdl_main.cpp index 30295daf..5642579a 100644 --- a/lib/sdl_main.cpp +++ b/lib/sdl_main.cpp @@ -69,8 +69,8 @@ SdlMainThread::SdlMainThread() SDL_version sdl_ver; SDL_GetVersion(&sdl_ver); - std::cerr << "Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor - << "." << (int)sdl_ver.patch << std::endl; + PRINT_DEBUG("Using SDL " << (int)sdl_ver.major << "." << (int)sdl_ver.minor + << "." << (int)sdl_ver.patch << std::endl); sdl_init = true; } From 92f7f4c737cd60bd70dbc8274219add29fc806ef Mon Sep 17 00:00:00 2001 From: Max Yang Date: Wed, 7 Jul 2021 13:53:57 -0700 Subject: [PATCH 45/47] Wrap makefile to 80 cols --- makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makefile b/makefile index 0a011bc6..95b6627e 100644 --- a/makefile +++ b/makefile @@ -222,7 +222,8 @@ ALL_SOURCE_FILES = \ lib/vsdata.cpp lib/vssolution.cpp lib/vssolution3d.cpp lib/vsvector.cpp \ lib/vsvector3d.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 +DESKTOP_ONLY_SOURCE_FILES = \ + lib/gl/renderer_ff.cpp lib/threads.cpp lib/gl2ps.c lib/sdl_x11.cpp WEB_ONLY_SOURCE_FILES = lib/aux_js.cpp LOGO_FILE = share/logo.rgba LOGO_FILE_CPP = $(LOGO_FILE).bin.cpp From f002d95710a5419c271ef40959791a340a7408b8 Mon Sep 17 00:00:00 2001 From: Tom Stitt Date: Wed, 7 Jul 2021 14:57:49 -0700 Subject: [PATCH 46/47] minor js build fix --- lib/aux_js.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index 25e6930f..0ebbd169 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -16,10 +16,10 @@ #include #include -std::string plot_caption; -std::string extra_caption; // used in extern context -thread_local mfem::GeometryRefiner -GLVisGeometryRefiner; // used in extern context +// used in extern context +thread_local std::string plot_caption; +thread_local std::string extra_caption; +thread_local mfem::GeometryRefiner GLVisGeometryRefiner; static VisualizationSceneScalarData * vs = nullptr; StreamState stream_state; From b8e08611624be319ef7288552a89c5ac60f0cfe2 Mon Sep 17 00:00:00 2001 From: Max Yang Date: Thu, 8 Jul 2021 11:12:56 -0700 Subject: [PATCH 47/47] Better workaround for M_PI on windows --- CMakeLists.txt | 2 ++ lib/gl/types.cpp | 4 ---- lib/openglvis.cpp | 3 --- lib/palettes.cpp | 3 --- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a979d0ef..e7d2ad71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,8 @@ list(APPEND _glvis_compile_defs "GLVIS_MULTISAMPLE=${GLVIS_MULTISAMPLE}") list(APPEND _glvis_compile_defs "GLVIS_MS_LINEWIDTH=${GLVIS_MS_LINEWIDTH}") if (NOT WIN32) list(APPEND _glvis_compile_defs "GLVIS_USE_LOGO") +else() + list(APPEND _glvis_compile_defs "_USE_MATH_DEFINES") endif() if (CMAKE_BUILD_TYPE MATCHES "Debug|debug|DEBUG") diff --git a/lib/gl/types.cpp b/lib/gl/types.cpp index 8917f645..a0bef475 100644 --- a/lib/gl/types.cpp +++ b/lib/gl/types.cpp @@ -24,10 +24,6 @@ void GlDrawable::addCone(float x, float y, float z, float phi = acos(vz/rhos); float theta = atan2 (vy, vx); -#ifndef M_PI - const double M_PI = glm::pi(); -#endif - glm::mat4 mtx(1.0); mtx = glm::translate(mtx, glm::vec3(x, y, z)); mtx = glm::rotate(mtx, theta, glm::vec3(0.f, 0.f, 1.f)); diff --git a/lib/openglvis.cpp b/lib/openglvis.cpp index 83a81916..069a94d8 100644 --- a/lib/openglvis.cpp +++ b/lib/openglvis.cpp @@ -450,9 +450,6 @@ void VisualizationScene::Zoom(double factor) } else { -#ifndef M_PI - const double M_PI = glm::pi(); -#endif double va = ViewAngle * ( M_PI / 360.0 ); ViewAngle = atan( tan( va ) / factor ) * (360.0 / M_PI); } diff --git a/lib/palettes.cpp b/lib/palettes.cpp index 066bfb71..363b0268 100644 --- a/lib/palettes.cpp +++ b/lib/palettes.cpp @@ -7401,9 +7401,6 @@ void Init_Palettes() static bool first_init = true; if (first_init) { -#ifndef M_PI - const double M_PI = std::atan(1) * 4; -#endif // init rainbow palette for (int i = 0; i < RGB_Palette_23_Size; i++) {