Browse Source

Fonts: Fixed/improved support for legacy backend. SetTexID() writes into our ImTextureData to keep the indirection, clear TexIsBuilt.

The idea is that a legacy backend can somehow add a if (!atlas->IsBuilt()) ImGui_ImplXXXXX_CreateFontsTexture() call _after_ Render() and some features are supported.
ocornut 7 months ago
parent
commit
2bf6552f2f
3 changed files with 40 additions and 31 deletions
  1. 4 12
      imgui.cpp
  2. 16 13
      imgui.h
  3. 20 6
      imgui_draw.cpp

+ 4 - 12
imgui.cpp

@@ -5178,13 +5178,16 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos)
     io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
 }
 
+// FIXME-NEWATLAS-V2: If we aim to support multiple atlases used by same context: how to reach/target all atlases?
 static void ImGui::UpdateTexturesNewFrame()
 {
-    // FIXME-NEWATLAS-V2: If we aim to support multiple atlases used by same context: how to reach/target all atlases?
     ImGuiContext& g = *GImGui;
     ImFontAtlas* atlas = g.IO.Fonts;
     if (g.FontAtlasOwnedByContext)
+    {
+        atlas->RendererHasTextures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
         ImFontAtlasUpdateNewFrame(atlas);
+    }
 }
 
 // Build a single texture list
@@ -5240,13 +5243,6 @@ void ImGui::NewFrame()
 
     CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
 
-    // Check that font atlas was built or backend support texture reload in which case we can build now
-    ImFontAtlas* atlas = g.IO.Fonts;
-    if (!atlas->TexIsBuilt && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures))
-        ImFontAtlasBuildMain(atlas);
-    else // Legacy backend
-        IM_ASSERT(atlas->TexIsBuilt && "Backend does not support ImGuiBackendFlags_RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8().");
-
     // Check and assert for various common IO and Configuration mistakes
     ErrorCheckNewFrameSanityChecks();
 
@@ -8573,11 +8569,7 @@ void ImGui::UpdateFontsNewFrame()
 {
     ImGuiContext& g = *GImGui;
     if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
-    {
         g.IO.Fonts->Locked = true;
-        for (ImFont* font : g.IO.Fonts->Fonts)
-            font->LockDisableLoading = true;
-    }
     SetCurrentFont(GetDefaultFont());
     IM_ASSERT(g.Font->IsLoaded());
 }

+ 16 - 13
imgui.h

@@ -3332,7 +3332,7 @@ struct ImDrawData
 };
 
 //-----------------------------------------------------------------------------
-// [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureDataUpdate, ImTextureData
+// [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData
 //-----------------------------------------------------------------------------
 
 // We intentionally support a limited amount of texture formats to limit burden on CPU-side code and extension.
@@ -3517,17 +3517,20 @@ struct ImFontAtlas
     IMGUI_API void              ClearTexData();             // [OBSOLETE] Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory.
 
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-    // Build atlas, retrieve pixel data.
-    // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().
-    // The pitch is always = Width * BytesPerPixels (1 or 4)
-    // Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into
-    // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste.
-    IMGUI_API bool              Build();                    // Build pixels data. This is called automatically for you by the GetTexData*** functions.
-    IMGUI_API void              GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel
-    IMGUI_API void              GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel
-    void                        SetTexID(ImTextureID id)    { TexRef._TexData = NULL; TexRef._TexID = id; } // Called by legacy backends.
-    void                        SetTexID(ImTextureRef id)   { TexRef = id; }                                // Called by legacy backends.
-    bool                        IsBuilt() const             { return Fonts.Size > 0 && TexIsBuilt; }        // Bit ambiguous: used to detect when user didn't build texture but effectively we should check TexID != 0 except that would be backend dependent...
+    // Legacy path for build atlas + retrieving pixel data.
+    // - User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().
+    // - The pitch is always = Width * BytesPerPixels (1 or 4)
+    // - Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into
+    //   the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste.
+    // - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures:
+    //   - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed.
+    //   - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation.
+    IMGUI_API bool  Build();                    // Build pixels data. This is called automatically for you by the GetTexData*** functions.
+    IMGUI_API void  GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel
+    IMGUI_API void  GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel
+    void            SetTexID(ImTextureID id)    { IM_ASSERT(TexRef._TexID == ImTextureID_Invalid); TexRef._TexData->TexID = id; }                               // Called by legacy backends. May be called before texture creation.
+    void            SetTexID(ImTextureRef id)   { IM_ASSERT(TexRef._TexID == ImTextureID_Invalid && id._TexData == NULL); TexRef._TexData->TexID = id._TexID; } // Called by legacy backends.
+    bool            IsBuilt() const { return Fonts.Size > 0 && TexIsBuilt; } // Bit ambiguous: used to detect when user didn't build texture but effectively we should check TexID != 0 except that would be backend dependent..
 #endif
 
     //-------------------------------------------
@@ -3588,7 +3591,7 @@ struct ImFontAtlas
     ImVector<ImTextureData*>    TexList;            // Texture list (most often TexList.Size == 1). TexData is always == TexList.back(). DO NOT USE DIRECTLY, USE GetDrawData().Textures[]/GetPlatformIO().Textures[] instead!
     bool                        Locked;             // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert.
     bool                        RendererHasTextures;// Copy of (BackendFlags & ImGuiBackendFlags_RendererHasTextures) from supporting context.
-    bool                        TexIsBuilt;         // Set when texture was built matching current font input
+    bool                        TexIsBuilt;         // Set when texture was built matching current font input. Mostly useful for legacy IsBuilt() call.
     bool                        TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format or conversion process.
     ImVec2                      TexUvScale;         // = (1.0f/TexData->TexWidth, 1.0f/TexData->TexHeight)
     ImVec2                      TexUvWhitePixel;    // Texture coordinates to a white pixel

+ 20 - 6
imgui_draw.cpp

@@ -2485,6 +2485,7 @@ void ImTextureData::DestroyPixels()
 // - ImFontAtlas::CalcCustomRectUV()
 // - ImFontAtlasGetMouseCursorTexData()
 //-----------------------------------------------------------------------------
+// - ImFontAtlasBuildMain()
 // - ImFontAtlasBuildSetupFontLoader()
 // - ImFontAtlasBuildPreloadAllGlyphRanges()
 // - ImFontAtlasBuildUpdatePointers()
@@ -2685,15 +2686,24 @@ static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* at
 }
 
 // Called by NewFrame(). When multiple context own the atlas, only the first one calls this.
+// If you are calling this yourself, ensure atlas->RendererHasTexUpdates is et.
 void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas)
 {
-    if (atlas->TexIsBuilt && atlas->Builder->PreloadedAllGlyphsRanges)
+    // Check that font atlas was built or backend support texture reload in which case we can build now
+    if (atlas->RendererHasTextures)
+    {
+        atlas->TexIsBuilt = true;
+        if (atlas->Builder == NULL) // This will only happen if fonts were not already loaded.
+            ImFontAtlasBuildMain(atlas);
+    }
+    else // Legacy backend
     {
-        ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas);
-        IM_ASSERT_USER_ERROR(atlas->RendererHasTextures == false,
-            "Called ImFontAtlas::Build() before ImGuiBackendFlags_RendererHasTextures got set! With new backends: you don't need to call Build().");
+        IM_ASSERT_USER_ERROR(atlas->TexIsBuilt, "Backend does not support ImGuiBackendFlags_RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8().");
     }
+    if (atlas->TexIsBuilt && atlas->Builder->PreloadedAllGlyphsRanges)
+        IM_ASSERT_USER_ERROR(atlas->RendererHasTextures == false, "Called ImFontAtlas::Build() before ImGuiBackendFlags_RendererHasTextures got set! With new backends: you don't need to call Build().");
 
+    // Update texture status
     for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
     {
         ImTextureData* tex = atlas->TexList[tex_n];
@@ -2865,6 +2875,7 @@ void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex,
     tex->UpdateRect.y = ImMin(tex->UpdateRect.y, req.y);
     tex->UpdateRect.w = (unsigned short)(new_x1 - tex->UpdateRect.x);
     tex->UpdateRect.h = (unsigned short)(new_y1 - tex->UpdateRect.y);
+    atlas->TexIsBuilt = false;
 
     // No need to queue if status is _WantCreate
     if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates)
@@ -3206,6 +3217,7 @@ bool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor curso
     return true;
 }
 
+// When atlas->RendererHasTexUpdates == true, this is only called if no font were loaded.
 void ImFontAtlasBuildMain(ImFontAtlas* atlas)
 {
     IM_ASSERT(!atlas->Locked && "Cannot modify a locked ImFontAtlas!");
@@ -3589,6 +3601,7 @@ void ImFontAtlasBuildReloadFont(ImFontAtlas* atlas, ImFont* font)
             atlas->FontLoader->FontSrcInit(atlas, src);
 
         ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, src); // Technically this is called for each source sub-font, tho 99.9% of the time the first one fills everything.
+        atlas->TexIsBuilt = false;
     }
 
     // Notify external systems
@@ -3684,6 +3697,7 @@ ImTextureData* ImFontAtlasBuildAddTexture(ImFontAtlas* atlas, int w, int h)
 
     new_tex->Create(atlas->TexDesiredFormat, w, h);
     new_tex->Status = ImTextureStatus_WantCreate;
+    atlas->TexIsBuilt = false;
 
     ImFontAtlasBuildSetTexture(atlas, new_tex);
 
@@ -4037,12 +4051,12 @@ ImFontAtlasRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id
 
 ImFontGlyph* ImFont::BuildLoadGlyph(ImWchar codepoint)
 {
-    if (LockDisableLoading)
+    ImFontAtlas* atlas = ContainerAtlas;
+    if (LockDisableLoading || atlas->Locked)
         return NULL;
 
     //char utf8_buf[5];
     //IMGUI_DEBUG_LOG("[font] BuildAddGlyph U+%04X (%s)\n", (unsigned int)codepoint, ImTextCharToUtf8(utf8_buf, (unsigned int)codepoint));
-    ImFontAtlas* atlas = ContainerAtlas;
 
     // Load from single source or all sources?
     int srcs_count = (LockSingleSrcConfigIdx != -1) ? 1 : SourcesCount;