Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash when using ImGui across DLL boundaries on Windows #1376

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
36 changes: 36 additions & 0 deletions engine/include/cubos/engine/imgui/context.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/// @file
/// @brief Resource @ref cubos::engine::ImGuiContextHolder.
/// @ingroup imgui-plugin

#pragma once

#include <imgui.h>

#include <cubos/core/reflection/reflect.hpp>

#include <cubos/engine/api.hpp>

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
1 change: 1 addition & 0 deletions engine/include/cubos/engine/imgui/plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#pragma once

#include <cubos/engine/imgui/context.hpp>
#include <cubos/engine/prelude.hpp>

namespace cubos::engine
Expand Down
6 changes: 6 additions & 0 deletions engine/samples/imgui/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
8 changes: 7 additions & 1 deletion engine/samples/imgui/page.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 8 additions & 0 deletions engine/src/imgui/context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <cubos/core/ecs/reflection.hpp>

#include <cubos/engine/imgui/context.hpp>

CUBOS_REFLECT_IMPL(cubos::engine::ImGuiContextHolder)
{
return core::ecs::TypeBuilder<ImGuiContextHolder>("cubos::engine::ImGuiContextHolder").build();
}
16 changes: 12 additions & 4 deletions engine/src/imgui/plugin.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#include <imgui.h>

#include <cubos/engine/imgui/context.hpp>
#include <cubos/engine/imgui/data_inspector.hpp>
#include <cubos/engine/imgui/plugin.hpp>
#include <cubos/engine/render/target/plugin.hpp>
Expand All @@ -22,17 +25,22 @@ void cubos::engine::imguiPlugin(Cubos& cubos)
cubos.depends(renderTargetPlugin);

cubos.resource<DataInspector>();
cubos.uninitResource<ImGuiContextHolder>();

cubos.startupTag(imguiInitTag).after(windowInitTag);

cubos.tag(imguiBeginTag).after(windowPollTag);
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<float>(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<float>(settings.getDouble("imgui.scale", window->contentScale()));
imguiInitialize(window, dpiScale);

cmds.insertResource<ImGuiContextHolder>(ImGuiContextHolder{ImGui::GetCurrentContext()});
});

cubos.system("begin ImGui frame").tagged(imguiBeginTag).call([](EventReader<WindowEvent> events) {
// Pass window events to ImGui.
Expand Down
5 changes: 5 additions & 0 deletions tools/tesseratos/src/tesseratos/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <cubos/engine/assets/plugin.hpp>
#include <cubos/engine/defaults/plugin.hpp>
#include <cubos/engine/imgui/plugin.hpp>
#include <cubos/engine/input/input.hpp>
#include <cubos/engine/input/plugin.hpp>
#include <cubos/engine/settings/plugin.hpp>
Expand Down Expand Up @@ -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();
}
Loading