imgui
imgui copied to clipboard
Multi-viewports with SRGB framebuffer
Version/Branch of Dear ImGui:
Version: 1.87 Branch: docking
Back-end/Renderer/Compiler/OS
Back-ends: imgui_impl_opengl3.cpp + imgui_impl_glfw.cpp Compiler: MSVC2022 Operating System: XXX
My Issue/Question:
I have enabled SRGB framebuffer by calling glEnable(GL_FRAMEBUFFER_SRGB);
when creating the default window context.
But when I drag an imgui window out of my main viewport, a new framebuffer seems to get created, and my SRGB image is gone.
- Is there a chance I can initialize each opengl context (by calling some GL functions somewhere) when a new viewport window is created?
- Also, I have some input bindings bound to the default window, so I want to bind those to newly created viewport windows. How can I achieve this? Thanks a lot!
When window is detached from main viewport, new window is created by ImGui_ImplGlfw_CreateWindow()
. You probably need to enable sRGB after glfwCreateWindow()
call.
glEnable(GL_FRAMEBUFFER_SRGB);
is a global setting already, which applies during blending, so you have full control over it.
If you have problem I imagine it may be that OpenGL requires adjustment when creating the framebuffer format, see: https://community.khronos.org/t/setting-up-srgb/75216
glEnable(GL_FRAMEBUFFER_SRGB);
is a global setting already, which applies during blending, so you have full control over it.If you have problem I imagine it may be that OpenGL requires adjustment when creating the framebuffer format, see: https://community.khronos.org/t/setting-up-srgb/75216
Sadly, it's not true. I have enabled SRGB after main window creation. But this does not work for me.
You can see the following gif, the whole color changes(including my checkerboard sphere and imgui window background) after dragging out of the main window. You can also notice that the imgui window rounding at the top corner disappears after that.
I have enabled SRGB after main window creation
The link I provided suggest you also need to call glfwWindowHint(GL_FRAMEBUFFER_SRGB_EXT, GL_TRUE);
prior to window creation.
I have enabled SRGB after main window creation
The link I provided suggest you also need to call
glfwWindowHint(GL_FRAMEBUFFER_SRGB_EXT, GL_TRUE);
prior to window creation.
Thanks. But I have already enabled that before my main window creation. I have also tried glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE);
which does not work either...
Whatever you do on your main viewport needs to be done on secondary viewport, try to find out what's the difference.. You may need to redirect platform_io.Platform_CreateWindow() or platform_io.Renderer_CreateWindow() to your own function to add extra code before/after window creation and then call the previous platform/renderer handler.
Whatever you do on your main viewport needs to be done on secondary viewport, try to find out what's the difference.. You may need to redirect platform_io.Platform_CreateWindow() or platform_io.Renderer_CreateWindow() to your own function to add extra code before/after window creation and then call the previous platform/renderer handler.
So I have to modify imgui_impl_opengl3.cpp and imgui_impl_glfw.cpp right? Or could you please provide some callbacks so that I could add logic in my own code because I actually do not want to modify imgui code...
So I have to modify imgui_impl_opengl3.cpp and imgui_impl_glfw.cpp right?
No.
Or could you please provide some callbacks
The callbacks are platform_io.Platform_CreateWindow and platform_io.Renderer_CreateWindow etc you can modify/redirect them. Remember old function, change callback to yours, and have your function call the old one.
So I have to modify imgui_impl_opengl3.cpp and imgui_impl_glfw.cpp right?
No.
Or could you please provide some callbacks
The callbacks are platform_io.Platform_CreateWindow and platform_io.Renderer_CreateWindow etc you can modify/redirect them. Remember old function, change callback to yours, and have your function call the old one.
Oh I did not realize that! I will have a try! Thanks
So I have to modify imgui_impl_opengl3.cpp and imgui_impl_glfw.cpp right?
No.
Or could you please provide some callbacks
The callbacks are platform_io.Platform_CreateWindow and platform_io.Renderer_CreateWindow etc you can modify/redirect them. Remember old function, change callback to yours, and have your function call the old one.
After digging into ImGui_ImplGlfw_CreateWindow()
in imgui_impl_glfw.cpp, I find that I cannot easily copy the whole function into my own code as there are many things defined static in that cpp file. I want to copy those instead of creating my own because I do not want to modify that implementation, in fact, I just want to add some extra OpenGL init code and some window callbacks into that... What should I do? Thanks a lot!
I just want to add some extra OpenGL init code and some window callbacks into that...
Which exactly?
I just want to add some extra OpenGL init code and some window callbacks into that... Which exactly?
static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)();
viewport->PlatformUserData = vd;
// GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED
// With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem
glfwWindowHint(GLFW_VISIBLE, false);
glfwWindowHint(GLFW_FOCUSED, false);
#if GLFW_HAS_FOCUS_ON_SHOW
glfwWindowHint(GLFW_FOCUS_ON_SHOW, false);
#endif
glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true);
#if GLFW_HAS_WINDOW_TOPMOST
glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false);
#endif
GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : NULL;
vd->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window);
vd->WindowOwned = true;
viewport->PlatformHandle = (void*)vd->Window;
#ifdef _WIN32
viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window);
#endif
glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y);
///////////////////////////////////////////////////////////////////
/// glEnable(GL_FRAMEBUFFER_SRGB);
/// And bind my own glfw callbacks here
//////////////////////////////////////////////////////////////////
// Install GLFW callbacks for secondary viewports
glfwSetWindowFocusCallback(vd->Window, ImGui_ImplGlfw_WindowFocusCallback);
glfwSetCursorEnterCallback(vd->Window, ImGui_ImplGlfw_CursorEnterCallback);
glfwSetCursorPosCallback(vd->Window, ImGui_ImplGlfw_CursorPosCallback);
glfwSetMouseButtonCallback(vd->Window, ImGui_ImplGlfw_MouseButtonCallback);
glfwSetScrollCallback(vd->Window, ImGui_ImplGlfw_ScrollCallback);
glfwSetKeyCallback(vd->Window, ImGui_ImplGlfw_KeyCallback);
glfwSetCharCallback(vd->Window, ImGui_ImplGlfw_CharCallback);
glfwSetWindowCloseCallback(vd->Window, ImGui_ImplGlfw_WindowCloseCallback);
glfwSetWindowPosCallback(vd->Window, ImGui_ImplGlfw_WindowPosCallback);
glfwSetWindowSizeCallback(vd->Window, ImGui_ImplGlfw_WindowSizeCallback);
if (bd->ClientApi == GlfwClientApi_OpenGL)
{
glfwMakeContextCurrent(vd->Window);
glfwSwapInterval(0);
}
}
I just want to keep the original CreateWindow implementation and insert my own code (see the above commented lines) into it. But when I copy the content, some symbols are defined in imgui_impl_glfw.cpp file which I cannot reference in my own code.
Omar isn't saying you should clone ImGui_ImplGlfw_CreateWindow
entirely. You should add your own callback which chains to it. For example:
static void (*OldCreateWindow)(ImGuiViewport*);
static void Hls333555CreateWindowShim(ImGuiViewport* viewport)
{
OldCreateWindow(viewport);
GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle;
// Your manipulations of the GLFW window go here.
}
int main(int, char**)
{
// ... skipped most GLFW and Dear ImGui initialization ...
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
OldCreateWindow = platform_io.Platform_CreateWindow;
platform_io.Platform_CreateWindow = &Hls333555CreateWindowShim;
// ... skipped everything else ...
}
If you need to replace callbacks that the GLFW backend is already using, you should chain them as well. (All glfwSet***Callback
functions return the previous callback if there was one. The GLFW backend uses this to chain to existing user callbacks, look for references to PrevUserCallbackWindowFocus
in imgui_impl_glfw.cpp
if you for an example.)
Omar isn't saying you should clone
ImGui_ImplGlfw_CreateWindow
entirely. You should add your own callback which chains to it. For example:static void (*OldCreateWindow)(ImGuiViewport*); static void Hls333555CreateWindowShim(ImGuiViewport* viewport) { OldCreateWindow(viewport); GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; // Your manipulations of the GLFW window go here. } int main(int, char**) { // ... skipped most GLFW and Dear ImGui initialization ... // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); OldCreateWindow = platform_io.Platform_CreateWindow; platform_io.Platform_CreateWindow = &Hls333555CreateWindowShim; // ... skipped everything else ... }
If you need to replace callbacks that the GLFW backend is already using, you should chain them as well. (All
glfwSet***Callback
functions return the previous callback if there was one. The GLFW backend uses this to chain to existing user callbacks, look for references toPrevUserCallbackWindowFocus
inimgui_impl_glfw.cpp
if you for an example.)
Thansk for you reply! It's very helpful for me! However, I have found the last two glfw callbacks which cannot be chained and those callbacks are cpp-static:
glfwSetWindowCloseCallback(vd->Window, ImGui_ImplGlfw_WindowCloseCallback);
glfwSetWindowSizeCallback(vd->Window, ImGui_ImplGlfw_WindowSizeCallback);
How to solve this, thanks a lot!
First of all, it is unclear why you would need to chain those callbacks and how they have anything to do with srgb.
Secondly, if you actually needed to chain those (which I doubt so) you can call the glfw function to retrieve the current function pointer.
I suspect you misunderstood David’s answer, all you need are the few lines in their post: https://github.com/ocornut/imgui/issues/5397#issuecomment-1159477757
First of all, it is unclear why you would need to chain those callbacks and how they have anything to do with srgb.
Secondly, if you actually needed to chain those (which I doubt so) you can call the glfw function to retrieve the current function pointer.
I suspect you misunderstood David’s answer, all you need are the few lines in their post: #5397 (comment)
OK, I may misunderstood what this mean:
All glfwSet***Callback functions return the previous callback if there was one.
I'll try it again.
Please read my previous message again. You should not read those field nor call static functions. You can obtain the callback value via the glfw set functions.
Please read my previous message again. You should not read those field nor call static functions. You can obtain the callback value via the glfw set functions.
That works perfectly now! Thanks. However, there is some other issues related to multi-viewport:
-
The window rounding will disappear when a new viewport window is created(you can see this in my previous gif, note the upper corner has a rounding size of 8).
-
The mouse cursor cannot be disabled explicitly in the created viewport window (I want this feature as I will manipulate camera in that viewport window and cursor should be disabled in that situation): I see the code in imgui_impl_glfw.cpp that will force set input mode every frame for every viewport and I find that it will only return if main viewport's cursor is disabled but not the other viewports':
static void ImGui_ImplGlfw_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
for (int n = 0; n < platform_io.Viewports.Size; n++)
{
GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle;
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
}
else
{
// Show OS mouse cursor
// FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.
glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
}
}
I can fix this by adding if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) continue;
into the for loop as a workaround.
The window rounding will disappear when a new viewport window is created
That's expected and is why the examples disable window rounding when viewports are enabled.
The mouse cursor cannot be disabled explicitly in the created viewport window
It'd probably be easier to disable the mouse cursor via ImGui::SetMouseCursor(ImGuiMouseCursor_None);
so you don't have to fight it.
The window rounding will disappear when a new viewport window is created
That's expected and is why the examples disable window rounding when viewports are enabled.
So this is impossible to have both rounding enabled right?
The mouse cursor cannot be disabled explicitly in the created viewport window
It'd probably be easier to disable the mouse cursor via
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
so you don't have to fight it.
This method indeed hide the cursor, but it does not achieve the glfw way of "locking" the cursor to its position. And I in fact need to "disable" the mouse cursor. The glfw doc says:
If you wish to implement mouse motion based camera controls or other input schemes that require unlimited mouse movement, set the cursor mode to GLFW_CURSOR_DISABLED.
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
This will hide the cursor and lock it to the specified window. GLFW will then take care of all the details of cursor re-centering and offset calculation and providing the application with a virtual cursor position. This virtual position is provided normally via both the cursor position callback and through polling.
So this is impossible to have both rounding enabled right?
Correct, supporting window rounding on every platform for every backend is non-trivial.
This method indeed hide the cursor, but it does not achieve the glfw way of "locking" the cursor to its position.
Ah, sorry. I missed the distinction between GLFW_CURSOR_DISABLED
and GLFW_CURSOR_HIDDEN
.
In that case you should just set the ImGuiConfigFlags_NoMouseCursorChange
flag in ImGuiIO::ConfigFlags
when you have the cursor locked.
This method indeed hide the cursor, but it does not achieve the glfw way of "locking" the cursor to its position.
Ah, sorry. I missed the distinction between
GLFW_CURSOR_DISABLED
andGLFW_CURSOR_HIDDEN
.In that case you should just set the
ImGuiConfigFlags_NoMouseCursorChange
flag inImGuiIO::ConfigFlags
when you have the cursor locked.
This solution works quite nice! Thanks!