为何ImGuiLayer可获取有效ImGuiContext,SandboxLayer却返回空?
GImGui上下文结果不同的原因 问题描述
我有两个继承自同一基类layer的Layer,二者均重写了虚函数virtual void on_imgui_render() {},并被添加至同一个vector中依次遍历调用。在imgui_layer里,代码ImGuiContext& g = *GImGui;能获取到有效的ImGuiContext,但在sandbox_layer中该代码返回空指针。调用流程为:先执行m_imgui_layer->begin(),再遍历调用所有Layer的on_imgui_render(),最后执行m_imgui_layer->end()。为何同一调用流程下,一个能拿到有效上下文,另一个不行?
完整代码
基类layer定义
class FL_API layer { public: explicit layer(const std::string& name = "Layer"); virtual ~layer() = default; layer(const layer& other) = delete; layer(layer&& other) noexcept = delete; layer& operator=(const layer& other) = delete; layer& operator=(layer&& other) noexcept = delete; virtual void on_attach() {} virtual void on_detach() {} virtual void on_update() {} virtual void on_render() {} virtual void on_imgui_render() {} virtual void on_event(event& event) {} protected: std::string m_debug_name; };
imgui_layer的on_imgui_render实现
void imgui_layer::on_imgui_render() { ImGuiContext& g = *GImGui; fl::b8 within_frame_scope = g.WithinFrameScope; (void) within_frame_scope; }
sandbox_layer的on_imgui_render实现
void sandbox_layer::on_imgui_render() { ImGuiContext& g = *GImGui; fl::b8 within_frame_scope = g.WithinFrameScope; (void) within_frame_scope; }
调用流程代码
m_imgui_layer->begin(); std::ranges::for_each(m_layerstack, [](layer* layer) { layer->on_imgui_render(); }); m_imgui_layer->end();
imgui_layer的begin和end实现
void imgui_layer::begin() { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); } void imgui_layer::end() { ImGuiIO& io = ImGui::GetIO(); io.DisplaySize = ImVec2(static_cast<f32>(application::instance()->get_window().get_width()), static_cast<f32>(application::instance()->get_window().get_height())); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { GLFWwindow* backup_current_context = glfwGetCurrentContext(); ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); glfwMakeContextCurrent(backup_current_context); } }
问题原因分析
核心问题集中在**GImGui的作用域有效性和头文件包含差异**:
头文件包含不统一
GImGui是ImGui定义的全局线程局部变量,必须在包含imgui.h后才能正确识别其定义。如果sandbox_layer的实现文件未包含imgui.h,或包含时机晚于类定义,编译器会将GImGui视为未定义符号,运行时会访问到空指针;而imgui_layer因正确包含头文件,能正常获取上下文。线程局部存储的线程一致性
GImGui是线程局部变量,仅在调用ImGui::NewFrame()的线程中有效。如果sandbox_layer的on_imgui_render()被其他线程调用,跨线程访问会直接得到空指针。从调用流程看是单线程执行,但需排查是否有隐藏的线程切换逻辑。上下文激活状态异常
若存在手动创建多个ImGui上下文的逻辑,且未在调用on_imgui_render()前激活默认上下文,也会导致GImGui为空。不过从代码看未涉及手动上下文管理,此可能性较低。
解决建议
- 检查
sandbox_layer的实现文件,确保在类定义前包含imgui.h,且与imgui_layer的包含方式一致。 - 替换直接解引用
GImGui的写法,改用更安全的ImGui::GetCurrentContext()获取上下文:ImGuiContext* ctx = ImGui::GetCurrentContext(); if (ctx) { fl::b8 within_frame_scope = ctx->WithinFrameScope; (void) within_frame_scope; } - 确认调用所有
on_imgui_render()的线程与执行ImGui::NewFrame()的线程为同一线程,无隐性线程切换。
内容的提问来源于stack exchange,提问作者Milan Dierick

