Explorar el Código

Backends: Allegro5: added ImGuiBackendFlags_RendererHasTextures support.

ocornut hace 8 meses
padre
commit
ee8941e0de
Se han modificado 2 ficheros con 90 adiciones y 51 borrados
  1. 85 50
      backends/imgui_impl_allegro5.cpp
  2. 5 1
      backends/imgui_impl_allegro5.h

+ 85 - 50
backends/imgui_impl_allegro5.cpp

@@ -2,7 +2,8 @@
 // (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.)
 
 // Implemented features:
-//  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
+//  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
+//  [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Clipboard support (from Allegro 5.1.12).
 //  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
@@ -20,6 +21,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLGPU3_CreateFontsTexture() and ImGui_ImplSDLGPU3_DestroyFontsTexture().
 //  2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support.
 //  2025-01-06: Avoid calling al_set_mouse_cursor() repeatedly since it appears to leak on on X11 (#8256).
 //  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
@@ -137,6 +139,13 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
     if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
         return;
 
+    // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
+    // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
+    if (draw_data->Textures != nullptr)
+        for (ImTextureData* tex : *draw_data->Textures)
+            if (tex->Status != ImTextureStatus_OK)
+                ImGui_ImplAllegro5_UpdateTexture(tex);
+
     // Backup Allegro state that will be modified
     ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
     ALLEGRO_TRANSFORM last_transform = *al_get_current_transform();
@@ -232,43 +241,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
 
 bool ImGui_ImplAllegro5_CreateDeviceObjects()
 {
-    // Build texture atlas
     ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
-    ImGuiIO& io = ImGui::GetIO();
-    unsigned char* pixels;
-    int width, height;
-    io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
-
-    // Create texture
-    // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
-    int flags = al_get_new_bitmap_flags();
-    int fmt = al_get_new_bitmap_format();
-    al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP | ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR);
-    al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE);
-    ALLEGRO_BITMAP* img = al_create_bitmap(width, height);
-    al_set_new_bitmap_flags(flags);
-    al_set_new_bitmap_format(fmt);
-    if (!img)
-        return false;
-
-    ALLEGRO_LOCKED_REGION* locked_img = al_lock_bitmap(img, al_get_bitmap_format(img), ALLEGRO_LOCK_WRITEONLY);
-    if (!locked_img)
-    {
-        al_destroy_bitmap(img);
-        return false;
-    }
-    memcpy(locked_img->data, pixels, sizeof(int) * width * height);
-    al_unlock_bitmap(img);
-
-    // Convert software texture to hardware texture.
-    ALLEGRO_BITMAP* cloned_img = al_clone_bitmap(img);
-    al_destroy_bitmap(img);
-    if (!cloned_img)
-        return false;
-
-    // Store our identifier
-    io.Fonts->SetTexID((ImTextureID)(intptr_t)cloned_img);
-    bd->Texture = cloned_img;
 
     // Create an invisible mouse cursor
     // Because al_hide_mouse_cursor() seems to mess up with the actual inputs..
@@ -279,16 +252,81 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects()
     return true;
 }
 
-void ImGui_ImplAllegro5_InvalidateDeviceObjects()
+void ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex)
 {
-    ImGuiIO& io = ImGui::GetIO();
-    ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
-    if (bd->Texture)
+    if (tex->Status == ImTextureStatus_WantCreate)
+    {
+        // Create and upload new texture to graphics system
+        //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
+        IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
+        IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
+
+        // Create texture
+        // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
+        const int new_bitmap_flags = al_get_new_bitmap_flags();
+        int new_bitmap_format = al_get_new_bitmap_format();
+        al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP | ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR);
+        al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE);
+        ALLEGRO_BITMAP* cpu_bitmap = al_create_bitmap(tex->Width, tex->Height);
+        al_set_new_bitmap_flags(new_bitmap_flags);
+        al_set_new_bitmap_format(new_bitmap_format);
+        IM_ASSERT(cpu_bitmap != nullptr && "Backend failed to create texture!");
+
+        // Upload pixels
+        ALLEGRO_LOCKED_REGION* locked_region = al_lock_bitmap(cpu_bitmap, al_get_bitmap_format(cpu_bitmap), ALLEGRO_LOCK_WRITEONLY);
+        IM_ASSERT(locked_region != nullptr && "Backend failed to create texture!");
+        memcpy(locked_region->data, tex->GetPixels(), tex->GetSizeInBytes());
+        al_unlock_bitmap(cpu_bitmap);
+
+        // Convert software texture to hardware texture.
+        ALLEGRO_BITMAP* gpu_bitmap = al_clone_bitmap(cpu_bitmap);
+        al_destroy_bitmap(cpu_bitmap);
+        IM_ASSERT(gpu_bitmap != nullptr && "Backend failed to create texture!");
+
+        // Store identifiers
+        tex->SetTexID((ImTextureID)(intptr_t)gpu_bitmap);
+        tex->SetStatus(ImTextureStatus_OK);
+    }
+    else if (tex->Status == ImTextureStatus_WantUpdates)
     {
-        io.Fonts->SetTexID(0);
-        al_destroy_bitmap(bd->Texture);
-        bd->Texture = nullptr;
+        // Update selected blocks. We only ever write to textures regions which have never been used before!
+        // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
+        ImTextureRect r_bb = tex->UpdateRect; // Bounding box encompassing all individual updates
+        ALLEGRO_BITMAP* gpu_bitmap = (ALLEGRO_BITMAP*)(intptr_t)tex->TexID;
+        ALLEGRO_LOCKED_REGION* locked_region = al_lock_bitmap_region(gpu_bitmap, r_bb.x, r_bb.y, r_bb.w, r_bb.h, al_get_bitmap_format(gpu_bitmap), ALLEGRO_LOCK_WRITEONLY);
+        IM_ASSERT(locked_region && "Backend failed to update texture!");
+        for (ImTextureRect& r : tex->Updates)
+            for (int y = 0; y < r.h; y++)
+                memcpy((unsigned char*)locked_region->data + locked_region->pitch * (r.y - r_bb.y + y) + (r.x - r_bb.x) * tex->BytesPerPixel, // dst
+                    tex->GetPixelsAt(r.x, r.y + y), r.w * tex->BytesPerPixel); // src, block pitch
+        al_unlock_bitmap(gpu_bitmap);
+        tex->SetStatus(ImTextureStatus_OK);
+    }
+    else if (tex->Status == ImTextureStatus_WantDestroy)
+    {
+        ALLEGRO_BITMAP* backend_tex = (ALLEGRO_BITMAP*)(intptr_t)tex->TexID;
+        if (backend_tex)
+            al_destroy_bitmap(backend_tex);
+
+        // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
+        tex->SetTexID(ImTextureID_Invalid);
+        tex->SetStatus(ImTextureStatus_Destroyed);
     }
+}
+
+void ImGui_ImplAllegro5_InvalidateDeviceObjects()
+{
+    ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
+
+    // Destroy all textures
+    for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
+        if (tex->RefCount == 1)
+        {
+            tex->SetStatus(ImTextureStatus_WantDestroy);
+            ImGui_ImplAllegro5_UpdateTexture(tex);
+        }
+
+    // Destroy mouse cursor
     if (bd->MouseCursorInvisible)
     {
         al_destroy_mouse_cursor(bd->MouseCursorInvisible);
@@ -439,6 +477,7 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display)
     io.BackendPlatformUserData = (void*)bd;
     io.BackendPlatformName = io.BackendRendererName = "imgui_impl_allegro5";
     io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;       // We can honor GetMouseCursor() values (optional)
+    io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures;   // We can honor ImGuiPlatformIO::Textures[] requests during render.
 
     bd->Display = display;
     bd->LastCursor = ALLEGRO_SYSTEM_MOUSE_CURSOR_NONE;
@@ -478,7 +517,7 @@ void ImGui_ImplAllegro5_Shutdown()
 
     io.BackendPlatformName = io.BackendRendererName = nullptr;
     io.BackendPlatformUserData = nullptr;
-    io.BackendFlags &= ~ImGuiBackendFlags_HasMouseCursors;
+    io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_RendererHasTextures);
     IM_DELETE(bd);
 }
 
@@ -608,12 +647,8 @@ void ImGui_ImplAllegro5_NewFrame()
     ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
     IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplAllegro5_Init()?");
 
-    if (!bd->Texture)
-        ImGui_ImplAllegro5_CreateDeviceObjects();
-
-    ImGuiIO& io = ImGui::GetIO();
-
     // Setup display size (every frame to accommodate for window resizing)
+    ImGuiIO& io = ImGui::GetIO();
     int w, h;
     w = al_get_display_width(bd->Display);
     h = al_get_display_height(bd->Display);

+ 5 - 1
backends/imgui_impl_allegro5.h

@@ -2,7 +2,8 @@
 // (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.)
 
 // Implemented features:
-//  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
+//  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
+//  [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
 //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
 //  [X] Platform: Clipboard support (from Allegro 5.1.12).
 //  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
@@ -36,4 +37,7 @@ IMGUI_IMPL_API bool     ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* event);
 IMGUI_IMPL_API bool     ImGui_ImplAllegro5_CreateDeviceObjects();
 IMGUI_IMPL_API void     ImGui_ImplAllegro5_InvalidateDeviceObjects();
 
+// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
+IMGUI_IMPL_API void     ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex);
+
 #endif // #ifndef IMGUI_DISABLE