Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for feedback: dynamic fonts & texture updates (v1.92) #8465

Open
ocornut opened this issue Mar 5, 2025 · 18 comments
Open

Request for feedback: dynamic fonts & texture updates (v1.92) #8465

ocornut opened this issue Mar 5, 2025 · 18 comments

Comments

@ocornut
Copy link
Owner

ocornut commented Mar 5, 2025

Pushed an experimental features/dynamic_fonts branch.

I am looking for feedback / early adopters to try this as we will inevitably find bugs or use cases that are not well supported.
The API are not 100% stabilized but it is expected they stay relatively close.

As of today, I only recommend experienced users to try this. Things should be simplified and clarified over the upcoming month.

WHEN YOU SUBMIT FEEDBACK PLEASE TRY TO INCLUDE RELEVANT CONTEXT. Things may look simple in the surface but there are lots of parameters under the hood.

Warning: Your first impression may be very dry: (1) the examples application are using the default bitmap fonts and are still not DPI aware. (but it is now trivial to scale the font dynamically) (2) we are not exposing a global "scale" for frustratingly subtle reasons which will evolve. But if you tinker with font loading and map a DragFloat() to io.FontGlobalScale you can see some of it more easily, e.g. ImGui::DragFloat("io.FontGlobalScale", &ImGui::GetIO().FontGlobalScale, 0.05f, 0.5f, 5.0f);. It's going to make more sense in an established application. If you are using multiple fonts, large fonts, icon fonts, non-English languages, this will likely make things easier.

TL;DR; Add a font once, use it as any size.

2025-01-27.font.resize.mp4

If you want to help testing this:

  • Read this :)
  • Branch your project and update to this features/dynamic_fonts branch of imgui.
  • Make changes (in required/desirable) in your branch.
  • If you find any issue you can report/discuss and return to your main branch. Your feedback will help improve the code but also comments/docs/examples.
  • If you start using this, please try to update and feedback regularly as we work on finalizing this.

I am interested in different situations:

  • Updating to latest code and using an old standard backend. Do you see any issue/breakage?
  • Updating to latest code and using the latest standard backend.
  • Updating to latest code and using a custom backend. Do you see any issue/breakage? How's your experience figuring out how to update to support ImGuiBackendFlags_RendererHasTextures ?
  • Users for AddCustomRectRegular() / AddCustomRectFontGlyph() api: I am creating a dedicated topic Request for feedback: custom rect / custom glyphs / custom loaders with dynamic_fonts branch. #8466 for feedback on those.

The API we are replacing to interface with renderer backends has been here since January 2015 (!), so is more 10 years old. I want to ensure that the new API will also last long.

THE PLAN

My hope is that I can finish and merge this to master in 1~2 months but it will depends on amount of feedback/bugs, and the remaining work on more "General Scaling".

  1. I am pushing a public branch today. It is temporarily based over docking.
  2. Now gathering a first wave of feedback and issues from early enthusiasts.
    I am creating two topics:
  3. The commit history will be reworked/sculpted, rebased and force-pushed for a little while, as I am treating this as short-term feature branch. So you will likely need to checkout when you update (regular merging will be harder). For now the commits are intentionally labelled "WIP - xxxx". At some point when I am convinced enough that the commits are a good, I will stop sculpting history and start only adding to it.
  4. When this is deemed mature enough, it will be rebased on master and pushed as 1.92. We will open new topics if the current topics have had too many posts and many already solved issues.
  5. My expectation is that before 1.92 I will have also finished and added more features related to "general scaling" (current code only has "font scaling").

USEFUL REFERENCES COMMITS/DIFF

Public API

Backends:

Third Party Extensions

@ocornut
Copy link
Owner Author

ocornut commented Mar 5, 2025

GENERAL OVERVIEW OF THE CHANGES

As outlined in the '10 years of Dear ImGui' article there are many changes coming related to the font system and text shaping.

For the font system, I have decided to split the work in 2 distinct steps:

  • Step 1: dynamic font atlas & backend support for texture updates.
    • Fonts may be used at any size.
    • Backends can create, update, destroy textures.
    • Getting the low-level support working. As little user facing change as possible.
  • Step 2: rework and cleanup all high-level fonts api and general scaling system.
    • General scaling system (e.g. scaling style sizes).
    • Maybe revamp font loading API. Surface more features.

The code I am publishing today is mostly step 1.

What is part of what I am publishing (step 1)

  • Generally reworked the low-level font atlas system toward being more dynamic. Provided the backend support ImGuiBackendFlags_RendererHasTextures:
    • Fonts may be added and removed at any time.
    • One font may be used at any size.
    • Glyphs are added, and rasterized and packed on demand.
    • Atlas may be repacked, compacted etc.
    • A bunch of under the hood machinery ensure this is mostly transparent and efficient. Great care has been put at reducing the cost on lower-level text functions, but a general overall 1-2% slowdown may be expected.
  • Intentionally trying to keep user-facing changes to a strict minimum!
    • As such, many features are not exposed or surfaced in high-level API yet. (e.g. while it is now easy to use a font at any size, we don't yet provide a standard mechanism for general scaling of everything.) e.g. I very shyly added a minimum set of ImFontFlags values required for feature we need know, but it is intentionally buried in ImFontConfig struct until font loading API are redesigned better.
  • Added API for backends to support arbitrary texture creation and destruction and partial texture updates.
    • The backend API for this has not changed since v1.30 (February 2015!). We are changing it for the first time in ten years!
    • Hopefully the new API should last for a long time!
    • The old system required the Renderer Backend to build and upload texture during the backend NewFrame() call. The atlas texture identifier was locked past this point and used in draw commands.
    • The new system provides a list of textures for the backend to process at the end of the frame when it does its rendering. Low-level texture identifiers are not generally known until end of the the frame.
    • Textures blocks that have never been used may be written with new texels. Textures blocks that have been used will never be rewritten, which simplify needs for synchronization in rendering backends.
    • When the texture is out of capacity, a new texture is created. Previous glyphs are packed into it. Size variations that are not being used may be garbage collected. The texture may be larger or the same size, depending on which font size may be unused and were evicted. The previous texture may be freed by the backend after in-flight renderings are done.
    • During a frame where a new texture is created, some UI elements will use the old texture and some will use the new texture. This should be completely transparent to the user.
    • If you need fixed/known memory usage, you may decide to setup atlas texture to a fixed size by writing equal values to the atlas->TexMinWidth/TexMaxWidth, TexMinHeight/TexMaxHeight.
  • Internals have been rewritten, making it easier to later experiment with font backends others than stb_truetype or Freetype, and different ways to render fonts. Custom font loaders may be used.
  • Effective benefits, today:
    • Users of icons, Asian and non-English languages do not need to pre-build all glyphs ahead of time. Saving on loading time, memory, and also reducing issues with missing glyphs.
    • Specifying glyph ranges is not needed anymore.
    • **PushFontSize() may be used anytime. Adding/removing fonts is easier (but will be more easier later). **
    • It is easier to dynamically adjust variety font setting and change the font rendering backend at runtime while seeing result immediately.(Hard to see now as I didn't push many tools yet)
    • Packing custom rectangles is more convenient as pixels may be written to immediately.
    • Any update to fonts previously required backend specific calls to re-upload the texture, and said calls were not portable across backend. It is now possible to scale fonts etc. in a way that doesn't require you to make backend specific calls.
    • It is possible to plug a custom loader/backend to any font source.
    • General refactor should facilitate further experiments and improvements.

What is not part of this update yet (step 2, will come next)

Your first impression may be very dry: The examples are using the default bitmap fonts and are still not DPI aware.

  • Adding a more general scaling system that apply to e.g. style sizes.
  • Refactoring high-level fonts API: loading api, loading flags, sharing font sources and data between fonts, fonts fallbacks etc.
  • Reevaluating many other fonts features.
  • This is one of the backbone for better DPI and multi-DPI support, but still need to make all examples DPI aware.
  • Examples are still using the default pixelated font, "ProggyClean", also informally referred to as the Marmite/Vegemite of UI fonts: much loved and much hated.
  • Font sizes and many other aspects of layout are still pixel aligned. (1858, 791) This might become optional, primarily to allow smoother zooming. Without the rounding it's possible to get incredibly smooth zooming. Note that Freetype's great Font Hinting feature by nature does align character widths to pixel, but we are aiming to design a system when animated/slow zooming could temporarily disable hinting.
  • CustomRects are already better as you can write your pixels immediately after registering a rectangle. But the whole system is still in flux as better multi-dpi support will likely need us to further redesign those API. Custom font glyphs are better handled by registering a custom ImFontLoader.
  • When the system is widely adopted, this should enable imgui code to easily bake custom symbols, shapes, icons and many other things on the fly. Because I am eager to be able to take advantage of this fully, my expectation is that we will accelerate phasing out support for old backends (normally we phase things out in 2 years, I may make this shorter).

Generally speaking, the design puzzle here has been to push toward improvements while providing a reasonable path for old code to function without major issues. I have iterated on this for countless hours to find what I hope is the best solution, but it's expected that people will run into issues.

@ocornut
Copy link
Owner Author

ocornut commented Mar 5, 2025

KNOWN ISSUES

  • General Scaling system is not yet solved/pushed. This only provides FONT SCALING. I am working on this, and the final solution may alter some details about font scaling.
  • I haven't yet exposed many fonts features in examples and demo.
    • I have NOT YET exposed the general satisfying "scale all the UI" widget in public code, because I want this to be tied to general scaling rather than font scaling.
    • But if you are curious you may bind io.FontGlobalScale to an interactive item and immediately see this in action: ImGui::DragFloat("io.FontGlobalScale", &ImGui::GetIO().FontGlobalScale, 0.05f, 0.5f, 5.0f);
    • It's incredibly frustrating that this is not the first thing you see in Demo, but not exposing it is a necessary fence until I finish design work on general scaling.
  • Examples are still not technically DPI aware yet (but it's much easier now). As I am still working on providing a transparent solution for general scaling, how much responsibility may be left to user is this undecided and being explored.
  • Important: use of legacy io.FontGlobalScale, ImGuiConfigFlags_DpiEnableScaleFonts and SetWindowFontScale() should now all appear to work nicely. But be warned they can lead to confusion and feedback loop when using e.g. GetFontSize() as a base to request another size. This will be clarified and improved as I finish work on general scaling.
  • Using multiple atlas simultaneously in the same context has not been tested yet.
  • Sharing a same atlas between two contexts is possible, with the following constraints:
    • Contexts sharing a same atlas must not be doing work in parallel.
    • Contexts sharing a same atlas must be using the same rendering backend.
  • No attempt to merge UpdateRects[] provided to backends yet. Should be easy.
  • ImFont::AddRemapChar() doesn't work and may be obsoleted as I'm not sure anyone is using this.
  • Backends: WGPU: Missing support (PR welcome!)
  • Backends: DX12, Vulkan: due to some legacy constructs, we are currently needlessly requiring for texture uploads to complete during rendering, which is not optimal. It may be hard to notice as texture uploads are rare. However, we can and should improve the backends to remove those blocking waits.
  • Binding generators for C and other languages (dear_bindings, cimgui) may need to come up with a way to automatically turn ImTextureUserID into ImTextureID.

@ocornut
Copy link
Owner Author

ocornut commented Mar 5, 2025

CHANGELOG

Breaking Changes

  • Optional compile-time #define ImTextureID has been renamed to ImTextureUserID.
    • Will compile-time error and redirect you to the new name if we find ImTextureID to be defined, as we cannot redirect the #define.
    • ImTextureID is now a small composite structure which may be constructed from a ImTextureUserID. Best efforts have been made for this to be transparent for most C++ users, but in some cases you may run into casting issues.
    • As 100% of users calls will want to construct a ImTextureID from a ImTextureUserID, we suggest that C users and any higher-level language bindings generators may automatically convert this in some way. This design may evolve as we test the branch.
    • ImDrawCmd::GetTexID() renamed to ImDrawCmd::GetTexUserID(), now returns a ImTextureUserID, which is what makes sense for backends. Backends older than 1.83 may be reading directly in the structure, please switch to use GetTexUserID(). Kept inline redirection function called GetTexID().
  • Backends: removed ImGui_ImplXXXX_CreateFontsTexture()/ImGui_ImplXXXX_DestroyFontsTexture() for all backends that had them. They should not be necessary any more.
  • Fonts: obsoleted ImFontAtlas::GetTexDataAsRGBA32(), GetTexDataAsAlpha8(), Build(), SetTexID() and IsBuilt() functions. The new protocol for backends to handle textures doesn't need them. Kept redirection functions (will obsolete).
    • A majority of old backends should still work with new code (behaving like they did before).
    • Calling ImFontAtlas::Build() before initializing new backends will erroneously trigger preloading all glyphs. Will be detected with an assertion after the backend is initialized.
  • Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (327) (it vaguely made sense with the old system as if unspecified textures width maxxed up to 4096 but that limit isn't necessary anymore, and Renderer_TextureMaxWidth covers this). However you may set ``atlas->TexMinWidth == atlas->TexMaxWidth` for the same effect.
  • Fonts: the return type of GetCustomRectByIndex() has been changed from ImFontAtlasCustomRect* to ImTextureRect with the following renamed fields:
    • ImFontAtlasCustomRect::X --> ImTextureRect::x
    • ImFontAtlasCustomRect::Y --> ImTextureRect::y
    • ImFontAtlasCustomRect::Width --> ImTextureRect::w
    • ImFontAtlasCustomRect::Height --> ImTextureRect::h
      We added a redirecting typedef but haven't attempted to magically redirect the field names, as this API is rarely used and the fix is simple.
  • Fonts: obsoleted AddCustomRectFontGlyph() as the API does not make sense for scalable fonts.
    • Kept existing function which uses the font "default size" (Sources[0]->SizePixels).
    • Added a helper AddCustomRectFontGlyphForSize() which is immediately marked obsolete, but can facilitate transitioning old code.
    • Prefer adding a font source (ImFontConfig) using a custom/procedural loader.
    • You may use ImFontFlags_LockBakedSizes to limit an existing font to known sizes:
        ImFont* myfont = io.Fonts->AddFontFromFileTTF(....);
        myfont->GetFontBaked(16.0f);
        myfont->Flags |= ImFontFlags_LockBakedSizes;
  • Fonts: ImFontConfig::OversampleH/OversampleV defaults to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling.
  • Fonts: specifying glyph ranges is now unnecessary.
    • The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends.
    • All GetGlyphRangesXXXX() functions will be removed in a subsequent updates:
      • GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GlyphRangesVietnamese().
  • Fonts: generally reworked internals of ImFontAtlas and ImFont. While efforts were made to not affect most users, it is probable that some users wlll be affected. Among other things:
    • ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[].
    • ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourceCount.
    • Things moved from ImFont to ImFontBaked:
      • ImFont::IndexAdvanceX[] -> ImFontBaked::IndexAdvanceX[]
      • ImFont::Glyphs[] -> ImFontBaked::Glyphs[]
      • ImFont::Ascent, Descent -> ImFontBaked::Ascent, Descent
      • Widget code using the currently bound font, aka ImGui::GetFont() == g.Font,
        may use g.FontBaked which is == g.Font->GetFontBaked(g.FontSize).
    • ImFont::CalcWordWrapPositionA() has been renamed to ImFont::CalcWordWrapPosition(), and the leading 'float scale' parameters was changed to 'float size'. This was unfortunately necessary as 'scale' is assuming standard font size which is a concept we aim to eliminate. Kept inline redirection function.
      Please report if you are affected!

Other Changes

  • Fonts: added partial texture update protocol.
    • The Renderer Backend needs to set io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures.
    • New structs: ImTextureData, ImTextureRect.
    • New enums: ImTextureStatus, ImTextureFormat.
    • During its ImGui_ImplXXXX_RenderDrawData() call, the backend can now access a texture list in platform_io.Textures[]. Textures may have four distinct states:
      • ImTextureStatus_WantCreate: requesting backend to create a texture.
      • ImTextureStatus_WantUpdates: requesting backend to copy given blocks from the CPU side copy of the texture to your graphics pipeline.
        A tex->Updates[] list of update is provided as well as a single tex->UpdatesRect bounding box.
      • ImTextureStatus_WantDestroy: requesting backend to destroy the texture. An int UnusedFrames value is provided to conveniently defer destroying.
      • ImTextureStatus_OK: nothing to do.
    • Almost all standard backends have been updated to support this.
    • Backends are allowed to destroy textures at any time if they desire so.
    • Both stb_truetype and Freetype backends have been updated to work with the new system, and they now share more code than before.
    • Added #define IMGUI_HAS_TEXTURES to facilitate compile-time checks for third-party extensions until this is merged with a definitive version number to check.
  • Fonts: added user-overridden #define ImTextureUserID_Invalid 0 if you need 0 to be a valid texture identifier.
  • Fonts: added ImFontAtlas::FontBackendName (which is surfaced in the "About Dear ImGui" window and other locations).
  • Fonts: ImFontAtlas::AddFontXXX() functions may be called at any time during the frame.
  • Fonts: ImFontAtlas::AddFontXXX() can fail more gracefully if error handling is configured to not assert (this will be better exposed via future font flags).
  • Fonts: added ImGui::PushFontSize()/PopFontSize() functions.
  • Fonts: added optional font_size parameter to ImGui::PushFont() function. THE MEANING OF IT MAY EVOLVE
  • Fonts: added ImFontAtlas::RemoveFont() function.
  • Fonts: added ImFontAtlas::CompactCache() function.
  • Fonts: added ImFontAtlas::TexDesiredFormat field (default to ImTextureFormat_RGBA32, may be changed to ImTextureFormat_Alpha8).
  • Fonts: added ImFontAtlas::TexMinWidth, TexMinHeight, TexMaxWidth, TexMaxHeight fields.
  • Fonts: added ImFontConfig::PixelSnapV to align scaled GlyphOffset.y to pixel boundaries.
  • Fonts: added ImFontConfig::GlyphExcludeRanges[], which behave similarly to ImFontConfig::GlyphRanges[] but has the opposite meaning, and is tailored but situation where merged fonts have overlapping characters.
  • Fonts: the general design has changed toward meaning that a ImFont doesn't have a specific size: it may be bound and drawn with any size.
    • We store ImFontBaked structures internally, which are a cache of information for a given size being drawn.
    • ImFontBaked structures may be cleaned up between frames when unused.
      -Added `ImFontBaked::IsGlyphLoaded().
  • Fonts: packing has generally been reworked. (CustomRect API problems (AddCustomRectRegular) #8107, feat: API for Styling glyphs #7962, Make font atlas padding between packed glyphs configurable #1282)
    • AddCustomRectRegular()/AddCustomRectFontGlyph() function will immediately return a packed rectangle and you can write your pixels anytime. This is also the case when using a legacy backend.
    • Custom packed rectangles may be moved during a texture change. Always refer to latest position returned by GetCustomRectByIndex().
  • Fonts: texture is now stored in a single format CPU side (save ~25% when using RGBA).
  • Fonts: changing current font to one from a different atlas is supported. (IM_ASSERT(font->ContainerAtlas->TexID == _CmdHeader.TextureId); #8080).
  • Fonts: automatic baking of an "..." ellipsis works better with monospace fonts.
  • Fonts: each ImFontConfig font source may provide a custom backend/loader.
  • Fonts: added platform_io.Renderer_TextureMaxWidth/ Renderer_TextureMaxHeight fields for Renderer Backend to specify if there is a maximum accepted texture size (not yet used). MAY REMOE
  • Debug Tools: Fonts section: add font preview, add "Remove" button, add "Load all glyphs" button, add selection to change font backend when both are compiled in.
  • Backends: DX9/DX10/DX11/DX12, Vulkan, OpenGL2/3, Metal, SDLGPU3, SDLRenderer2/3, Allegro5:

@ocornut
Copy link
Owner Author

ocornut commented Mar 6, 2025

As well as various small fixes/addition, I have temporarily added a scale widget + direction in the demo to make experimenting less dry.
Image

@bratpilz
Copy link

bratpilz commented Mar 7, 2025

This is really great! It lets me delete a lot of the code I had written to deal with creating and using fonts of different sizes, and it's much faster and more flexible on top of that.

So far I've noticed only two issues (for which I can also create separate GitHub issues if you prefer):

  1. I create normal/bold/italic/bold+italic versions of all the fonts I use. This makes it so that I can quickly do e.g. PushFont(active_fonts.bold); [...] PopFont(); to have a heading text be bold. Right now this resets the font size back to what I initially specified when creating the font though.
PushFont(active_fonts.regular, 16.0f);
[...] // Later, in a nested function call:
PushFont(active_fonts.bold);
Text("This text gets shown with size 14.0f, which I created the font with originally.");
PopFont();
[...]
PopFont();

I saw that this is because of a temporary backwards compatibility measure in PushFont:
font_size = font->Sources[0].SizePixels; // g.FontSize;
If I change this line to font_size = g.FontSize;, the bold text is also shown at the new size (i.e. 16.0f in the example above).

  1. I have multiple OS windows, each with its own ImGuiContext, and I'm sharing the font atlas between all of these contexts. When I close one of the windows, I destroy its context and also call ImGui_ImplOpenGL3_Shutdown. This then deletes all the textures that are still in use in the other OS windows' contexts, which leads to the following assertion:
ImTextureUserID ImDrawCmd::GetTexUserID() const: Assertion `tex_id != ((ImTextureUserID)0) && "ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexUserID()!"' failed.

with the following stack:

[...]
#5  0x0000000001854a00 in ImDrawCmd::GetTexUserID (this=0x784a810) at imgui.h:3829
#6  0x00000000018fb67e in ImGui_ImplOpenGL3_RenderDrawData (draw_data=0x7b56bd0) at imgui_impl_opengl3.cpp:656
[...]

If I comment out the call to ImGui_ImplOpenGL3_Shutdown, the problem disappears, but then I'm leaking resources on the GPU.

@ocornut
Copy link
Owner Author

ocornut commented Mar 7, 2025

Thank you @bratpilz for your feedback.

(1) I haven't figured out the right design for this yet. To be forward-facing, it seems evident that PushFont(xxx) by default should preserve current font size, but this creates a problem with legacy code which defacto often used this to change font for the purpose of changing size.
We can distinguish font_size ==0 from font_size < 0 to allow for two different behavior, but again the tricky part is to decide on the default one. I'll work on this.

(2) I think we could solve this by adding a reference count inside ImFontAtlas and having backend check this. I'll work on this too.

@ocornut
Copy link
Owner Author

ocornut commented Mar 7, 2025

I have pushed 3eefa4e which should solve the problem with sharing an atlas between context. For simplicity I had to take a shortcut of assuming that when multiple contexts are created, they all have their backend initialized/bound to the context.

@ocornut ocornut pinned this issue Mar 7, 2025
@ocornut ocornut changed the title Request for feedback: dynamic fonts & texture update protocol. Request for feedback: dynamic fonts & texture updates (v1.92) Mar 7, 2025
@ZingBallyhoo
Copy link

Question r.e supporting in custom backends, would it be possible for the pixel data to be kept alive until the backend agrees the texture is destroyed? Currently the data is freed as soon as the texture enters the ImTextureStatus_WantDestroy state, but its possible that the data could still be in use asynchronously (obviously relies on the whole "Textures blocks that have been used will never be rewritten" guarantee). Also fine if not! Just requires buffering

@ocornut
Copy link
Owner Author

ocornut commented Mar 7, 2025

Question r.e supporting in custom backends, would it be possible for the pixel data to be kept alive until the backend agrees the texture is destroyed? Currently the data is freed as soon as the texture enters the ImTextureStatus_WantDestroy state, but its possible that the data could still be in use asynchronously (obviously relies on the whole "Textures blocks that have been used will never be rewritten" guarantee). Also fine if not! Just requires buffering

Out of curiosity could you describe your use case? Dx12/Vulkan tend to use upload buffer which would require a copy anyhow.
Trying to get as much data as possible to take on better decision (eg should this be opt-in for certain backends, or not).

@PathogenDavid
Copy link
Contributor

but this creates a problem with legacy code which defacto often used this to change font for the purpose of changing size.

Instead of modifying PushFont to not change the font size, maybe it could be done from the font configuration end instead? ImFontConfig::SizePixels is more of a default size now, right? Maybe if a font has a size of 0 that means it doesn't have one and it should use whatever is current.

As far as the default font size goes: Maybe a new style variable can be added for that? Or maybe there's just enforcement that the default font must always have an explicit size. (Specifying font size to AddFontFromFileTTF feels legacy with dynamic fonts so the former might make the most sense IMO.)

@PathogenDavid
Copy link
Contributor

Out of curiosity could you describe your use case?

I'm not @ZingBallyhoo and none of these situations are important to me personally, but I can imagine at least a few scenarios where someone might want the texture data to stick around after it's marked as WantDestroy:

  • Support for UMA GPU architectures (where the separate upload buffer can be skipped, EG: using ID3D12Resource::WriteToSubresource)
    • (Examples of UMA GPUs are integrated GPUs, consoles, modern MacBooks, and I think most/all embedded GPUs-EG: phones/tablets)
  • Engines where uploading a texture kicks off a job (and the copying to the upload buffer is done asynchronously)
  • Engines which keep the CPU-side memory around for reuploads (EG: to recover from device removal or as part of a manual memory eviction strategy)

(That being said I don't think it's totally unreasonable to tell backends that they don't own the texture memory and therefore shouldn't hold onto it, but it also currently looks like it wouldn't be that hard to say it's an (opt-in?) responsibility of backends to call ImTextureData::DestroyPixels.)

@ZingBallyhoo
Copy link

ZingBallyhoo commented Mar 8, 2025

Out of curiosity could you describe your use case?

Main thread -> render thread syncronization. Only the render thread can manage GPU resources, so in order to upload data it either needs to be buffered into per-frame allocs or be (effectively) immutable & outlive the lifetime of the render frame. Buffering is fine of course and happens everywhere (e.g imgui vtx/idx data), but the setup is very close to not requiring it which is why I ask.

it wouldn't be that hard to say it's an (opt-in?) responsibility of backends to call ImTextureData::DestroyPixels.)

My thought was to just move it to be deleted upon reaching the Destroyed state (decided by the backed) instead of the WantDestroy state (decided by imgui usually). Most backends will be immediately setting textures to that state when requested anyway so no semantic change there.

@the-goodies
Copy link

the-goodies commented Mar 9, 2025

Here's my function (Go language) to load a text font merged with an icons font. Icons font is given a smaller font size, but it ignores it and uses the text font size. Both text and icons being the same font size, makes icons too big when used within the same text line.

//go:embed embed/fonts/RobotoCondensed-Medium.ttf
var embededFontRegularBytes []byte

//go:embed embed/fonts/fa-light.ttf
var embededIconsFontBytes []byte

func createFonts() {
	fontSize := float32(20)
	iconsFontSize := fontSize * 0.6

	// Create font config, which will merge icons font with a previous text font
	fontIconsConfig := NewFontConfig()
	fontIconsConfig.SetMergeMode(true)
	fontIconsConfig.SetName("Font Awesome 5 Icons")
	defer fontIconsConfig.Delete()

	imguiIO.Fonts().AddFontFromMemoryTTFV(embededFontRegularBytes, fontSize, 0)
	imguiIO.Fonts().AddFontFromMemoryTTFV(embededIconsFontBytes, iconsFontSize, fontIconsConfig)
}

@ocornut
Copy link
Owner Author

ocornut commented Mar 9, 2025

@PathogenDavid

Instead of modifying PushFont to not change the font size, maybe it could be done from the font configuration end instead? ImFontConfig::SizePixels is more of a default size now, right? Maybe if a font has a size of 0 that means it doesn't have one and it should use whatever is current.

Merged fonts may need different base scale so that needs to be expressed in some way of another (it could be a scale factor relative to main font). That's exactly the case in the issue that just got reported. I'll come up with something.

@ZingBallyhoo
I'll find a solution to your problem.

@the-goodies:

Here's my function (Go language) to load a text font merged with an icons font. Icons font is given a smaller font size, but it ignores it and uses the text font size. Both text and icons being the same font size, makes icons too big when used within the same text line.

Thank you for reporting this. It's indeed a bug that happened during a rework last month. I've pushed a quick fix 24be548 now (I will rework that fix and merge it into earlier commits later).

@bratpilz
Copy link

I have pushed 3eefa4e which should solve the problem with sharing an atlas between context. For simplicity I had to take a shortcut of assuming that when multiple contexts are created, they all have their backend initialized/bound to the context.

Thanks! It works without asserting now.

@ocornut
Copy link
Owner Author

ocornut commented Mar 10, 2025

@ZingBallyhoo I have confirmed that I see no issue deferring destruction of the pixel buffer to after backend has acknowledged it, so pushed that change 0f7c7bc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants