Browse Source

WIP Backends: SDLRenderer3: adding support for multi-viewports. (broken: see 2 FIXMES)

ocornut 1 year ago
parent
commit
3b2b53887f

+ 84 - 3
backends/imgui_impl_sdlrenderer3.cpp

@@ -13,8 +13,9 @@
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
-// Missing features:
-//  [ ] Renderer: Multi-viewport support (multiple windows).
+//  [X] Renderer: Multi-viewport support (multiple windows).
+//    FIXME: Missing way to share textures betweens renderers: https://github.com/libsdl-org/SDL/issues/6742
+//    FIXME: Missing way to specify a projection matrix, so our vertices in absolute coordinates are not displayed correctly in multi-viewports mode.
 
 
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@@ -25,6 +26,7 @@
 // - Introduction, links and more at the top of imgui.cpp
 // - Introduction, links and more at the top of imgui.cpp
 
 
 // CHANGELOG
 // CHANGELOG
+//  2024-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. (#5835)
 //  2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
 //  2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009).
 //  2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009).
 //  2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
 //  2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
@@ -65,6 +67,10 @@ static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData()
     return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
     return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
 }
 }
 
 
+// Forward Declarations
+static void ImGui_ImplSDLRenderer3_InitMultiViewportSupport();
+static void ImGui_ImplSDLRenderer3_ShutdownMultiViewportSupport();
+
 // Functions
 // Functions
 bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer)
 bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer)
 {
 {
@@ -78,9 +84,12 @@ bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer)
     io.BackendRendererUserData = (void*)bd;
     io.BackendRendererUserData = (void*)bd;
     io.BackendRendererName = "imgui_impl_sdlrenderer3";
     io.BackendRendererName = "imgui_impl_sdlrenderer3";
     io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
     io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
+    io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports;  // We can create multi-viewports on the Renderer side (optional)
 
 
     bd->Renderer = renderer;
     bd->Renderer = renderer;
 
 
+    ImGui_ImplSDLRenderer3_InitMultiViewportSupport();
+
     return true;
     return true;
 }
 }
 
 
@@ -90,11 +99,12 @@ void ImGui_ImplSDLRenderer3_Shutdown()
     IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
     IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
     ImGuiIO& io = ImGui::GetIO();
     ImGuiIO& io = ImGui::GetIO();
 
 
+    ImGui_ImplSDLRenderer3_ShutdownMultiViewportSupport();
     ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
     ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
 
 
     io.BackendRendererName = nullptr;
     io.BackendRendererName = nullptr;
     io.BackendRendererUserData = nullptr;
     io.BackendRendererUserData = nullptr;
-    io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
+    io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports);
     IM_DELETE(bd);
     IM_DELETE(bd);
 }
 }
 
 
@@ -287,6 +297,77 @@ void ImGui_ImplSDLRenderer3_DestroyDeviceObjects()
     ImGui_ImplSDLRenderer3_DestroyFontsTexture();
     ImGui_ImplSDLRenderer3_DestroyFontsTexture();
 }
 }
 
 
+//--------------------------------------------------------------------------------------------------------
+// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
+// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
+// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
+//--------------------------------------------------------------------------------------------------------
+
+// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
+struct ImGui_ImplSDLRenderer3_ViewportData
+{
+    SDL_Renderer*                   Renderer;
+
+    ImGui_ImplSDLRenderer3_ViewportData()   { Renderer = nullptr; }
+    ~ImGui_ImplSDLRenderer3_ViewportData()  { IM_ASSERT(Renderer == nullptr); }
+};
+
+static void ImGui_ImplSDLRenderer3_CreateWindow(ImGuiViewport* viewport)
+{
+    //ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
+    ImGui_ImplSDLRenderer3_ViewportData* vd = IM_NEW(ImGui_ImplSDLRenderer3_ViewportData)();
+    viewport->RendererUserData = vd;
+
+    SDL_Window* window = (SDL_Window*)viewport->PlatformHandle;
+    vd->Renderer = SDL_CreateRenderer(window, nullptr);
+    SDL_SetRenderVSync(vd->Renderer, 0);
+    IM_ASSERT(vd->Renderer != NULL);
+}
+
+static void ImGui_ImplSDLRenderer3_DestroyWindow(ImGuiViewport* viewport)
+{
+    // The main viewport (owned by the application) will always have RendererUserData == nullptr since we didn't create the data for it.
+    if (ImGui_ImplSDLRenderer3_ViewportData* vd = (ImGui_ImplSDLRenderer3_ViewportData*)viewport->RendererUserData)
+    {
+        SDL_DestroyRenderer(vd->Renderer);
+        vd->Renderer = nullptr;
+        IM_DELETE(vd);
+    }
+    viewport->RendererUserData = nullptr;
+}
+
+static void ImGui_ImplSDLRenderer3_RenderWindow(ImGuiViewport* viewport, void*)
+{
+    ImGui_ImplSDLRenderer3_ViewportData* vd = (ImGui_ImplSDLRenderer3_ViewportData*)viewport->RendererUserData;
+    if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
+    {
+        ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
+        SDL_SetRenderDrawColor(vd->Renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255));
+        SDL_RenderClear(vd->Renderer);
+    }
+    ImGui_ImplSDLRenderer3_RenderDrawData(viewport->DrawData, vd->Renderer);
+}
+
+static void ImGui_ImplSDLRenderer3_SwapBuffers(ImGuiViewport* viewport, void*)
+{
+    ImGui_ImplSDLRenderer3_ViewportData* vd = (ImGui_ImplSDLRenderer3_ViewportData*)viewport->RendererUserData;
+    SDL_RenderPresent(vd->Renderer);
+}
+
+static void ImGui_ImplSDLRenderer3_InitMultiViewportSupport()
+{
+    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
+    platform_io.Renderer_CreateWindow = ImGui_ImplSDLRenderer3_CreateWindow;
+    platform_io.Renderer_DestroyWindow = ImGui_ImplSDLRenderer3_DestroyWindow;
+    platform_io.Renderer_RenderWindow = ImGui_ImplSDLRenderer3_RenderWindow;
+    platform_io.Renderer_SwapBuffers = ImGui_ImplSDLRenderer3_SwapBuffers;
+}
+
+static void ImGui_ImplSDLRenderer3_ShutdownMultiViewportSupport()
+{
+    ImGui::DestroyPlatformWindows();
+}
+
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
 #if defined(__clang__)
 #if defined(__clang__)

+ 3 - 2
backends/imgui_impl_sdlrenderer3.h

@@ -13,8 +13,9 @@
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
 //  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
-// Missing features:
-//  [ ] Renderer: Multi-viewport support (multiple windows).
+//  [X] Renderer: Multi-viewport support (multiple windows).
+//    FIXME: Missing way to share textures betweens renderers: https://github.com/libsdl-org/SDL/issues/6742
+//    FIXME: Missing way to specify a projection matrix, so our vertices in absolute coordinates are not displayed correctly in multi-viewports mode.
 
 
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
 // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.

+ 14 - 1
examples/example_sdl3_sdlrenderer3/main.cpp

@@ -32,13 +32,17 @@ int main(int, char**)
     }
     }
 
 
     // Create window with SDL_Renderer graphics context
     // Create window with SDL_Renderer graphics context
-    Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN;
+    Uint32 window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN;
     SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+SDL_Renderer example", 1280, 720, window_flags);
     SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+SDL_Renderer example", 1280, 720, window_flags);
     if (window == nullptr)
     if (window == nullptr)
     {
     {
         printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
         printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
         return -1;
         return -1;
     }
     }
+
+    for (int n = 0; n < SDL_GetNumRenderDrivers(); n++)
+        printf("- render driver %d: %s\n", n, SDL_GetRenderDriver(n));
+
     SDL_Renderer* renderer = SDL_CreateRenderer(window, nullptr);
     SDL_Renderer* renderer = SDL_CreateRenderer(window, nullptr);
     SDL_SetRenderVSync(renderer, 1);
     SDL_SetRenderVSync(renderer, 1);
     if (renderer == nullptr)
     if (renderer == nullptr)
@@ -56,6 +60,7 @@ int main(int, char**)
     io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
     io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
     io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
     io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
     io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Docking
     io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Docking
+    io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // Enable Multi-Viewport / Platform Windows
 
 
     // Setup Dear ImGui style
     // Setup Dear ImGui style
     ImGui::StyleColorsDark();
     ImGui::StyleColorsDark();
@@ -166,6 +171,14 @@ int main(int, char**)
         SDL_SetRenderDrawColorFloat(renderer, clear_color.x, clear_color.y, clear_color.z, clear_color.w);
         SDL_SetRenderDrawColorFloat(renderer, clear_color.x, clear_color.y, clear_color.z, clear_color.w);
         SDL_RenderClear(renderer);
         SDL_RenderClear(renderer);
         ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer);
         ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer);
+
+        // Update and Render additional Platform Windows
+        if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+        {
+            ImGui::UpdatePlatformWindows();
+            ImGui::RenderPlatformWindowsDefault();
+        }
+
         SDL_RenderPresent(renderer);
         SDL_RenderPresent(renderer);
     }
     }
 #ifdef __EMSCRIPTEN__
 #ifdef __EMSCRIPTEN__