Skip to content

Commit

Permalink
editor_pane: Workaround for empty ImGui context issue on Windows
Browse files Browse the repository at this point in the history
On Windows, when I've used dedicated thread for ImGui rendering, and
used TLS for GImGui, there's still a bug:

When I move mouse into editor window, plugin crashes. GDB shows that it
crashes when ImGui's GLFW callback function is accessing backend data
via ImGui_ImplGlfw_GetBackendData() (returns current ImGui context).

This is because GLFW queries window events in main thread, not in
drawing thread. Since the two threads have their own GImGui instance,
GImGui will be null in main thread.

@bear24rw (Max Thurn) provided a workaround: register our own glfw
callbacks to ensure the imgui context is set correctly before fowarding
the call to the imgui backend.

This workaround doesn't aim at multithreading, but works well for me!

Reference: ocornut/imgui#3934 (comment)
  • Loading branch information
AnClark committed Sep 5, 2022
1 parent abec5f7 commit 843eb45
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 1 deletion.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ if (CONFIG_VST_IMGUI)
src/ImGui/mainwindow.cpp
src/ImGui/subwindow.cpp
src/ImGui/editor_pane.cpp
src/ImGui/glfw_callbacks.cpp
)
target_include_directories (${PROJECT_NAME}_vst_imgui PRIVATE
${CMAKE_SOURCE_DIR}/vendor
Expand Down
6 changes: 5 additions & 1 deletion src/ImGui/editor_pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,13 @@ int ImguiEditor::setupImGui()
//ImGui::StyleColorsClassic();

// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplGlfw_InitForOpenGL(window, false); // Do not register callbacks automatically
ImGui_ImplOpenGL2_Init();

// Register my own callbacks
// See glfw_callbacks.cpp for more details.
_setMyGLFWCallbacks();

/* The font is loaded from generated/font.h. The font file is in generated by the
* binary_to_source utility included in Dear ImGui, this util is built and run by
* CMake when generating the make files. Default font is Roboto
Expand Down
9 changes: 9 additions & 0 deletions src/ImGui/editor_pane.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,15 @@ class ImguiEditor
// Window / panel
void _AmsynthWindow_Main();
void _AmsynthWindow_Preset();

// GLFW Callbacks
void _setMyGLFWCallbacks();

void _charCallback(unsigned int c);
void _cursorEnterCallback(int entered);
void _mouseButtonCallback(int button, int action, int mods);
void _scrollCallback(double xoffset, double yoffset);
void _keyCallback(int key, int scancode, int action, int mods);
};

// The drawing thread function
Expand Down
110 changes: 110 additions & 0 deletions src/ImGui/glfw_callbacks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* glfw_callbacks.cpp
*
* Copyright (c) 2021 AnClark Liu
*
* This file is part of amsynth.
*
* amsynth is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* amsynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with amsynth. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* Workaround for empty ImGui context issue on Windows
*
* On Windows, when I've used dedicated thread for ImGui rendering, and
* used TLS for GImGui, there's still a bug:
*
* When I move mouse into editor window, plugin crashes. GDB shows that it
* crashes when ImGui's GLFW callback function is accessing backend data
* via ImGui_ImplGlfw_GetBackendData() (returns current ImGui context).
*
* This is because GLFW queries window events in main thread, not in
* drawing thread. Since the two threads have their own GImGui instance,
* GImGui will be null in main thread.
*
* @bear24rw (Max Thurn) provided a workaround: register our own glfw
* callbacks to ensure the imgui context is set correctly before fowarding
* the call to the imgui backend.
*
* This workaround doesn't aim at multithreading, but works well for me!
*
* Reference: https://github.com/ocornut/imgui/pull/3934#issuecomment-873213161
*/

#include "editor_pane.h"

void ImguiEditor::_setMyGLFWCallbacks()
{
// Define intermediate callback functions.
// Those intermediates will execute callbacks defined in ImguiEditor's instance.
auto char_callback_func = [](GLFWwindow *w, unsigned int c) {
static_cast<ImguiEditor *>(glfwGetWindowUserPointer(w))->_charCallback(c);
};
auto cursor_enter_callback_func = [](GLFWwindow *w, int entered) {
static_cast<ImguiEditor *>(glfwGetWindowUserPointer(w))->_cursorEnterCallback(entered);
};
auto mouse_button_callback_func = [](GLFWwindow *w, int button, int action, int mods) {
static_cast<ImguiEditor *>(glfwGetWindowUserPointer(w))->_mouseButtonCallback(button, action, mods);
};
auto scroll_callback_func = [](GLFWwindow *w, double xoffset, double yoffset) {
static_cast<ImguiEditor *>(glfwGetWindowUserPointer(w))->_scrollCallback(xoffset, yoffset);
};
auto key_callback_func = [](GLFWwindow *w, int key, int scancode, int action, int mods) {
static_cast<ImguiEditor *>(glfwGetWindowUserPointer(w))->_keyCallback(key, scancode, action, mods);
};

// Register my own callbacks
glfwSetCharCallback(window, char_callback_func);
glfwSetCursorEnterCallback(window, cursor_enter_callback_func);
glfwSetMouseButtonCallback(window, mouse_button_callback_func);
glfwSetScrollCallback(window, scroll_callback_func);
glfwSetKeyCallback(window, key_callback_func);

// Set window user pointer to ImguiEditor's current instance
glfwSetWindowUserPointer(window, this);
}

// ---------- CALLBACKS ----------
// These callbacks will first set correct ImGui context, then invoke ImGui's own callbacks.
// This can prevent GLFW from accessing wrong ImGui instance.

void ImguiEditor::_charCallback(unsigned int c)
{
ImGui::SetCurrentContext(this->myImGuiContext);
ImGui_ImplGlfw_CharCallback(this->window, c);
}

void ImguiEditor::_cursorEnterCallback(int entered)
{
ImGui::SetCurrentContext(this->myImGuiContext);
ImGui_ImplGlfw_CursorEnterCallback(this->window, entered);
}

void ImguiEditor::_mouseButtonCallback(int button, int action, int mods)
{
ImGui::SetCurrentContext(this->myImGuiContext);
ImGui_ImplGlfw_MouseButtonCallback(this->window, button, action, mods);
}

void ImguiEditor::_scrollCallback(double xoffset, double yoffset)
{
ImGui::SetCurrentContext(this->myImGuiContext);
ImGui_ImplGlfw_ScrollCallback(window, xoffset, yoffset);
}

void ImguiEditor::_keyCallback(int key, int scancode, int action, int mods)
{
ImGui::SetCurrentContext(this->myImGuiContext);
ImGui_ImplGlfw_KeyCallback(this->window, key, scancode, action, mods);
}

0 comments on commit 843eb45

Please sign in to comment.