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

Shared font atlas issue with multiple ImGui contexes #8288

Open
bear24rw opened this issue Jan 3, 2025 · 2 comments
Open

Shared font atlas issue with multiple ImGui contexes #8288

bear24rw opened this issue Jan 3, 2025 · 2 comments

Comments

@bear24rw
Copy link
Contributor

bear24rw commented Jan 3, 2025

Version/Branch of Dear ImGui:

master/docking

Back-ends:

imgui_impl_glfw.cpp + imgui_impl_metal.cpp/imgui_impl_opengl3.cpp

Compiler, OS:

macOS + Clang

Details:

A shared font atlas has some incompatibility with the rendering backend being destroyed for a context. In the example below if you close one of the two windows the imgui widgets in the other window becomes black and the following message is printed:

UNSUPPORTED (log once): POSSIBLE ISSUE: unit 0 GLD_TEXTURE_INDEX_2D is unloadable and bound to sampler type (Float) - using zero texture because texture unloadable

If you do not use a shared font atlas everything works as expected. The metal backend also exhibits similar behavior. I believe it might have something to do with backend context specific data being stored in the globally shared Fonts structure:

io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);

Minimal, Complete and Verifiable Example code:

#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include <memory>
#include <stdio.h>
#include <vector>

static ImFontAtlas* s_font_atlas = nullptr;

class Window {
public:
    Window(const char* title)
    {
        m_window = glfwCreateWindow(1280, 720, title, nullptr, nullptr);
        m_context = ImGui::CreateContext(s_font_atlas);
        glfwMakeContextCurrent(m_window);
        ImGui::SetCurrentContext(m_context);
        ImGui_ImplGlfw_InitForOpenGL(m_window, true);
        ImGui_ImplOpenGL3_Init("#version 150");
    }

    ~Window()
    {
        ImGui::SetCurrentContext(m_context);
        glfwMakeContextCurrent(m_window);
        ImGui_ImplOpenGL3_Shutdown();
        ImGui_ImplGlfw_Shutdown();
        ImGui::DestroyContext(m_context);
        glfwDestroyWindow(m_window);
    }

    void render()
    {
        ImGui::SetCurrentContext(m_context);
        glfwMakeContextCurrent(m_window);
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
        ImGui::ShowDemoWindow(nullptr);
        ImGui::Render();
        int display_w, display_h;
        glfwGetFramebufferSize(m_window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        glfwSwapBuffers(m_window);
    }

    bool should_close()
    {
        return glfwWindowShouldClose(m_window);
    }

private:
    GLFWwindow* m_window = nullptr;
    ImGuiContext* m_context = nullptr;
};

int main(int, char**)
{
    glfwInit();

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    IMGUI_CHECKVERSION();

    // Comment this out to disable shared font atlas and it will work
    s_font_atlas = IM_NEW(ImFontAtlas)();
    s_font_atlas->AddFontDefault();

    std::vector<std::unique_ptr<Window>> windows;
    windows.emplace_back(new Window("1"));
    windows.emplace_back(new Window("2"));

    while (!windows.empty()) {
        glfwPollEvents();
        for (auto& window : windows)
            window->render();
        windows.erase(std::remove_if(windows.begin(), windows.end(), [](const auto& window) { return window->should_close(); }), windows.end());
    }

    glfwTerminate();

    return 0;
}
@ocornut
Copy link
Owner

ocornut commented Jan 8, 2025

Regardless of sharing an atlas, many of default backends don't support multi-context at all, I don't even think the GLFW one does.

On your specific issue: I am not going to aim to look for an immediate fix right now, because my work-in-progress rework of the atlas system doesn't have this issue. You can share an atlas between contexts as long as the same backends are used for all.
For now I suggest you use two atlases.

@bear24rw
Copy link
Contributor Author

bear24rw commented Jan 8, 2025

I don't even think the GLFW one does.

You added it for me a few years ago :)

4cec3a0

For now I suggest you use two atlases.

This is what I'm doing now, works fine.

The only reason I didn't hit this issue years ago is because I was not ever shutting down the backends when closing windows, but now ImGui::DestroyContext asserts that the backend is shutdown.

I think an alternative would be to use a single imgui context with multi viewports but I believe I would need these to be figured out first?

#3680
#3350

My application needs to be able to programmatically open multiple glfw windows with their native host decorations and be able to close/open them in any order (aka there is no "main" one). Being able to drag imgui windows between the two glfw windows would be awesome but I don't want the glfw windows to be able to be merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants