ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
60.44k stars 10.21k forks source link

Timing of CreateDeviceObjects in Metal and Vulkan backends #3314

Open thatoddmailbox opened 4 years ago

thatoddmailbox commented 4 years ago

Version/Branch of Dear ImGui:

Version: v1.76 Branch: master

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_metal.mm + imgui_impl_vulkan.cpp Operating System: macOS 10.15.4, but affects everything

My Issue/Question:

I was working on porting an application that uses Dear ImGui from OpenGL (imgui_impl_opengl3.cpp) to Metal (imgui_impl_metal.mm). I was using my own fonts, and I was loading these fonts after initializing ImGui and the respective backend. This worked with OpenGL, but not in Metal. (in Metal, nothing relating to imgui would render at all)

What I found out is that the Metal backend does its CreateDeviceObjects() call when you initialize it, while the OpenGL backend does its CreateDeviceObjects() on the first NewFrame(). Since this includes the font texture, and on Metal this happened before fonts were loaded, the correct font texture was only being created with OpenGL, hence the weird behavior.

While I fixed this in my app by loading fonts before initializing imgui and its backend, I'm not sure if this is something that should be fixed so that it's consistent. I went through all the backends, and I found that the majority of them (allegro5, dx9, dx10, dx11, dx12, marmalade, opengl2, opengl3) create objects on the first frame, while only two (metal, vulkan) create objects on init. I haven't been able to test the Vulkan backend with my application but I assume the same issue would come up.

Is this something that should be fixed? Or should users be expected to load fonts before initialization of imgui and its backend, and therefore this is just an implementation detail of the backend?

I think the reason the Metal backend does this is because creating the device objects requires access to the MTLDevice, which isn't available in ImGui_ImplMetal_NewFrame. (although I suppose you could hold on to the id<MTLDevice> in g_sharedMetalContext when you call ImGui_ImplMetal_Init, and then use it on the first ImGui_ImplMetal_NewFrame?)

Screenshots/Video

I can attach one if you want...but it's really just a blank window.

Standalone, minimal, complete and verifiable example: (see https://github.com/ocornut/imgui/issues/2261)

// in your initialization
ImGui::Init();
#ifdef USE_METAL
ImGui_ImplMetal_Init(MTLCreateSystemDefaultDevice());
#else
ImGui_ImplOpenGL3_Init("#version 100");
#endif 

// now load fonts from disk
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("some_font_file.ttf", 14);
io.Fonts->Build();

// ....
// ....
// ....
// later, when you render...
ImGui::Begin("Example Bug");
ImGui::Text("This doesn't show up when using Metal (or presumably Vulkan)");
ImGui::End();
ocornut commented 4 years ago

Hello,

Thanks for posting this issue. I agree we should aim to make the change you proposed.

I would appreciate a pull request making the change in Metal back-end to perform that upload in the first call to NewFrame().

For Vulkan back-ends, it's a little different because the texture build and upload is triggered by ImGui_ImplVulkan_CreateFontsTexture() which is called manually, so user has control of the timing already.

We have ongoing (unpublished) work to redesign the texture uploading structure for back-ends to be able to reupload partially changed texture, so considering that and the specific complications of Vulkan I wouldn't suggest making a change to the Vulkan back-end now.