From 377f5beca03a1ccb253559c5a87364fc0f027436 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Jan 2025 10:38:11 +0100 Subject: [PATCH] WIP - Backends: Allegro5: added ImGuiBackendFlags_RendererHasTextures support. --- backends/imgui_impl_allegro5.cpp | 132 +++++++++++++++++++------------ backends/imgui_impl_allegro5.h | 6 +- 2 files changed, 87 insertions(+), 51 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index b990aa8f6..aab24e57d 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/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 system (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'. @@ -138,6 +139,11 @@ 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 will an OK status, aka nothing to do. + for (ImTextureData* tex : ImGui::GetPlatformIO().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(); @@ -233,43 +239,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.. @@ -280,16 +250,81 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects() return true; } +void ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex) +{ + 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->Status = ImTextureStatus_OK; + } + else if (tex->Status == ImTextureStatus_WantUpdates) + { + // 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->Status = 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->Status = ImTextureStatus_Destroyed; + } +} + void ImGui_ImplAllegro5_InvalidateDeviceObjects() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); - if (bd->Texture) - { - io.Fonts->SetTexID(0); - al_destroy_bitmap(bd->Texture); - bd->Texture = nullptr; - } + + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + { + tex->Status = ImTextureStatus_WantDestroy; + ImGui_ImplAllegro5_UpdateTexture(tex); + } + + // Destroy mouse cursor if (bd->MouseCursorInvisible) { al_destroy_mouse_cursor(bd->MouseCursorInvisible); @@ -440,6 +475,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; @@ -479,7 +515,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); } @@ -609,12 +645,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); diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h index e67ffe509..406253405 100644 --- a/backends/imgui_impl_allegro5.h +++ b/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 system (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'. @@ -37,4 +38,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 want to start updating textures after ImGui::Render() without waiting for the RenderDrawData call. +IMGUI_IMPL_API void ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex); + #endif // #ifndef IMGUI_DISABLE