diff --git a/CHANGELOG.md b/CHANGELOG.md index 69b604b820..b2bf935626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Inconsistent behavior on ECS queries on symmetric self-relations (**@RiscadoA**). - Undefined behavior on ECS entity removal due to creating tables while iterating over tables (#1363, **@RiscadoA**). - Made canvas draw calls sorted by layer in order to prevent undeterministic behavior when drawing elements with transparency (**@mkuritsu**). +- Crash when using ImGui across DLL boundaries (#1144, **@RiscadoA**). ## [v0.4.0] - 2024-10-13 diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 1af0123c58..f34fe3e83e 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -27,6 +27,7 @@ set(CUBOS_ENGINE_SOURCE "src/imgui/plugin.cpp" "src/imgui/imgui.cpp" "src/imgui/data_inspector.cpp" + "src/imgui/context.cpp" "src/transform/plugin.cpp" "src/transform/child_of.cpp" diff --git a/engine/include/cubos/engine/imgui/context.hpp b/engine/include/cubos/engine/imgui/context.hpp new file mode 100644 index 0000000000..57fc3885f7 --- /dev/null +++ b/engine/include/cubos/engine/imgui/context.hpp @@ -0,0 +1,36 @@ +/// @file +/// @brief Resource @ref cubos::engine::ImGuiContextHolder. +/// @ingroup imgui-plugin + +#pragma once + +#include + +#include + +#include + +namespace cubos::engine +{ + /// @brief Resource which stores a pointer to the ImGui context created by the ImGui plugin. + /// + /// This resource is inserted by the @ref imgui-plugin during @ref imguiInitTag. + /// + /// When linking dynamically the engine library from an application or another library, although this plugin + /// initializes the context, it might not be set automatically on the client side - particularly, this happens on + /// Windows, as global variables are not shared between DLLs and applications. + /// + /// So, if you're using ImGui in your application or library, to ensure dynamic linking capability in all platforms, + /// you should call `ImGui::SetCurrentContext(ImGuiContextHolder::context)` before using any ImGui functions. + /// + /// This resource is not named ImGuiContext because ImGui already has a type with that name. + /// + /// @ingroup imgui-plugin + struct CUBOS_ENGINE_API ImGuiContextHolder + { + CUBOS_REFLECT; + + /// @brief Pointer to the ImGui context. + ImGuiContext* context; + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/imgui/plugin.hpp b/engine/include/cubos/engine/imgui/plugin.hpp index 3d844c1b1d..84069308fa 100644 --- a/engine/include/cubos/engine/imgui/plugin.hpp +++ b/engine/include/cubos/engine/imgui/plugin.hpp @@ -7,6 +7,7 @@ #pragma once +#include #include namespace cubos::engine diff --git a/engine/samples/imgui/main.cpp b/engine/samples/imgui/main.cpp index f09eebe14d..762d68dfda 100644 --- a/engine/samples/imgui/main.cpp +++ b/engine/samples/imgui/main.cpp @@ -96,6 +96,12 @@ int main(int argc, char** argv) cubos.plugin(imguiPlugin); /// [Adding the plugin] + /// [ImGui initialization] + cubos.startupSystem("set ImGui context").after(imguiInitTag).call([](ImGuiContextHolder& holder) { + ImGui::SetCurrentContext(holder.context); + }); + /// [ImGui initialization] + /// [ImGui Demo] cubos.system("show ImGui demo").tagged(imguiTag).call([]() { ImGui::Begin("Dear ImGui + Cubos"); diff --git a/engine/samples/imgui/page.md b/engine/samples/imgui/page.md index c5ae5a0c15..59a97eaba5 100644 --- a/engine/samples/imgui/page.md +++ b/engine/samples/imgui/page.md @@ -16,7 +16,13 @@ Then, make sure to add the plugin. @snippet imgui/main.cpp Adding the plugin -Once the ImGui plugin is added, you can create systems to display ImGui windows and widgets. Here's a system which opens an ImGui window, and its demo. +When you're using ImGui directly in an application or a library, you must also make sure to initialize the ImGui context on it. +To do so, you can add a startup system just like the one below: + +@snippet imgui/main.cpp ImGui initialization + +You can read more about this in the documentation of @ref cubos::engine::ImGuiContextHolder. +Now, you can create systems to display ImGui windows and widgets. Here's a system which opens an ImGui window, and its demo. @snippet imgui/main.cpp ImGui Demo diff --git a/engine/src/imgui/context.cpp b/engine/src/imgui/context.cpp new file mode 100644 index 0000000000..9ab78d3aa8 --- /dev/null +++ b/engine/src/imgui/context.cpp @@ -0,0 +1,8 @@ +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::ImGuiContextHolder) +{ + return core::ecs::TypeBuilder("cubos::engine::ImGuiContextHolder").build(); +} diff --git a/engine/src/imgui/plugin.cpp b/engine/src/imgui/plugin.cpp index 4e7b313d2c..dfbc317617 100644 --- a/engine/src/imgui/plugin.cpp +++ b/engine/src/imgui/plugin.cpp @@ -1,3 +1,6 @@ +#include + +#include #include #include #include @@ -22,6 +25,7 @@ void cubos::engine::imguiPlugin(Cubos& cubos) cubos.depends(renderTargetPlugin); cubos.resource(); + cubos.uninitResource(); cubos.startupTag(imguiInitTag).after(windowInitTag); @@ -29,10 +33,14 @@ void cubos::engine::imguiPlugin(Cubos& cubos) cubos.tag(imguiEndTag).before(windowRenderTag).after(imguiBeginTag); cubos.tag(imguiTag).after(imguiBeginTag).before(imguiEndTag); - cubos.startupSystem("initialize ImGui").tagged(imguiInitTag).call([](const Window& window, Settings& settings) { - float dpiScale = static_cast(settings.getDouble("imgui.scale", window->contentScale())); - imguiInitialize(window, dpiScale); - }); + cubos.startupSystem("initialize ImGui") + .tagged(imguiInitTag) + .call([](Commands cmds, const Window& window, Settings& settings) { + float dpiScale = static_cast(settings.getDouble("imgui.scale", window->contentScale())); + imguiInitialize(window, dpiScale); + + cmds.insertResource(ImGuiContextHolder{ImGui::GetCurrentContext()}); + }); cubos.system("begin ImGui frame").tagged(imguiBeginTag).call([](EventReader events) { // Pass window events to ImGui. diff --git a/tools/tesseratos/src/tesseratos/main.cpp b/tools/tesseratos/src/tesseratos/main.cpp index 1b6d697877..2c43462cb2 100644 --- a/tools/tesseratos/src/tesseratos/main.cpp +++ b/tools/tesseratos/src/tesseratos/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -54,5 +55,9 @@ int main(int argc, char** argv) input.bind(*bindings); }); + cubos.startupSystem("set ImGui context").after(imguiInitTag).call([](ImGuiContextHolder& holder) { + ImGui::SetCurrentContext(holder.context); + }); + cubos.run(); }