imgui icon indicating copy to clipboard operation
imgui copied to clipboard

How to handle multiple fonts (e.g. regular, bold, etc.) and an icon font (e.g. FontAwesome)? + 1.92 update

Open SaltyPandaCedric opened this issue 8 months ago • 11 comments

Version/Branch of Dear ImGui:

Version 1.92, Branch: docking

Back-ends:

Custom/Unreal Engine

Compiler, OS:

Windows 10 + MSVC 14.38.33130

Full config/build information:

No response

Details:

Multiple Text Fonts + one Icon Font

Hello there!

I'm trying to understand how one is supposed to handle adding multiple fonts for text, such as Roboto-Regular and Roboto-Bold, while also adding a font for icons, such as FontAwesome.

I do understand how to add a given font:

ImGuiIO& IO = ImGui::GetIO();
const FString RobotoRegularFontPath = FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf");
if (FPaths::FileExists(*RobotoRegularFontPath))
{
	IO.Fonts->AddFontFromFileTTF(TCHAR_TO_UTF8(*RobotoRegularFontPath));
}

I also think I understand, based on some 1.92 update notes, that it's expected to use the MergeMode on the icon font, to merge it into the text font:

// Above this is the RobotoRegular code.

ImFontConfig FontAwesomeFontConfig_Solid = ImFontConfig();
static constexpr ImWchar SolidIconRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };  // Is not needed anymore by now.

FontAwesomeFontConfig_Solid.FontDataOwnedByAtlas = false;
FontAwesomeFontConfig_Solid.FontData = (void*)fa_solid_900;
FontAwesomeFontConfig_Solid.FontDataSize = fa_solid_900_size;
FontAwesomeFontConfig_Solid.SizePixels = 14.0f;
FontAwesomeFontConfig_Solid.GlyphRanges = SolidIconRanges; // Is not needed anymore by now.
FontAwesomeFontConfig_Solid.MergeMode = true;

ImFormatString(FontAwesomeFontConfig_Solid.Name, IM_ARRAYSIZE(FontAwesomeFontConfig_Solid.Name), "%s, %.0fpx", "fa-solid-900.tff", FontAwesomeFontConfig_Solid.SizePixels);

IO.Fonts->AddFont(&FontAwesomeFontConfig_Solid);

(Note: I might still be missing some changes, such as the GlyphRanges not being needed, as I'm still discovering some of the required changes. You can gladly point those out if you notice them.)

However, if I were to add a second text font, such as Roboto-Bold, without also merging the icon font into that one too, I would end up losing access to the icon font when using the Roboto-Bold font via PushFont for some of my text, right?

If that's true, is the idea to define the above FontAwesomeFontConfig_Solid once before adding the Roboto-XYZ fonts, and then always adding the FontAwesomeFontConfig_Solid after each time I add a Roboto font? So basically this:

// FontAwesomeFontConfig_Solid code here.

const FString RobotoRegularFontPath = FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf");
if (FPaths::FileExists(*RobotoRegularFontPath))
{
	IO.Fonts->AddFontFromFileTTF(TCHAR_TO_UTF8(*RobotoRegularFontPath));
	IO.Fonts->AddFont(&FontAwesomeFontConfig_Solid);
}

const FString RobotoBoldFontPath = FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf");
if (FPaths::FileExists(*RobotoBoldFontPath))
{
	IO.Fonts->AddFontFromFileTTF(TCHAR_TO_UTF8(*RobotoBoldFontPath));
	IO.Fonts->AddFont(&FontAwesomeFontConfig_Solid);
}

If that's correct, does that add any overhead, given the icon font is added multiple times?

And if that's not correct, what is the suggested way of handling this?

1.92 Update

I'm still in the process of updating to 1.92, and while going over the update notes, I have problems understanding what some of the notes want to tell me.

Font Merging

font inputs are now scanned in order for the first font input which the desired glyph.

That sentence doesn't read like proper English to me. Either this is a language barrier on my end, or there are mistakes in it.

If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1.

I believe this tries to tell me that if Font Source 1 has glyphs in the same range of glyphs that Font Source 2 covers, we can tell Font Source 1 to exclude those, so they don't end up overlapping?

After the update and when using a new backend, those glyphs may now loaded from Font Source 1!

That part I also can't follow. Does this mean that, if previously there were issues with this overlap of glyph range, this can now be resolved by excluding the glyphs when adding Font Source 1? If I didn't have any issues with this before, then I assume this is all irrelevant to me?

Removed ImFontAtlas function

The update notes say that a lot of the ImFontAtlas functions are now obsolete and have been removed. The ones I was using up until now were:

  • IsBuilt()
  • GetTexDataAsRGBA32()
  • SetTexID()

At the beginning of a frame, before calling ImGui::NewFrame(), I was doing this:

if (!IO.Fonts->IsBuilt() || !FontAtlasTexturePtr.IsValid())
{
	uint8* TextureData;
	int32 TextureWidth, TextureHeight, BytesPerPixel;
	IO.Fonts->GetTexDataAsRGBA32(&TextureData, &TextureWidth, &TextureHeight, &BytesPerPixel);

#if WITH_ENGINE
	const FImageView TextureView(TextureData, TextureWidth, TextureHeight, ERawImageFormat::BGRA8);
	FontAtlasTexturePtr.Reset(FImageUtils::CreateTexture2DFromImage(TextureView));
#else
	FontAtlasTexturePtr = FSlateDynamicImageBrush::CreateWithImageData(
		TEXT("ImGuiFontAtlas"), FVector2D(TextureWidth, TextureHeight),
		TArray(TextureDataRaw, TextureWidth * TextureHeight * BytesPerPixel));
#endif

	IO.Fonts->SetTexID(FontAtlasTexturePtr.Get());
}

You can ignore the #if WITH_ENGINE part, that's Unreal Engine specific.

The update notes state:

The new protocol for backends to handle textures doesn't need them. Kept redirection functions (will obsolete).

Does this refer to the section Textures: added partial texture update protocol. in the update notes? If so, do I understand correctly that I now have to actively handle the different ImTextureStatus values etc.?

I don't think I had to do any of this before. In the current implementation, I have the code that I posted above for the FontAtlasTexturePtr and then code in the rendering section of a Slate (Unreal Engine) Widget, which grabs the TextureID (now TexRef.GetTexID()) and simply uses that for an FSlateDrawElement. Those TextureIDs are usually either UTextures or FSlateBrushs, which are either assets from the project, or already runtime-created within Unreal Engine (user) code.

If I set io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures does this only affect the FontAtlasTexture or will this then also call for the other Textures, such as an asset already loaded by Unreal Engine?

Kind regards Cedric

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

SaltyPandaCedric avatar Jun 28 '25 10:06 SaltyPandaCedric

About the FontAtlasTexture and the ImGuiBackendFlags_RendererHasTextures flow.

I struggle to see how that works with Unreal Engine specifically. Within the Engine itself, most textures will be of type UTexture2D. Those are UObject child classes, and Unreal Engine has automatic Garbage Collection for those.

Currently, I'm keeping track of the FontAtlasTexture as a TStrongObjectPtr<UTexture2D>, to ensure Unreal Engine doesn't garbage collect the texture. The new flow seems to potentially call for more than just the FontAtlasTexture and simply pushing the created UTexture2D pointer into the ImTextureData as TexID won't be enough for it not to be garbage collected.

I can keep a general TArray<TStrongObjectPtr<UTexture2D>> to which I add all created ones, but I'm unsure what this flow will all call for. E.g., if I use ImGui::Image(...), I would end up passing in a UTexture2D which is already constructed, and its lifetime may be tied to whatever I retrieved that from. Will this cause any of the ImTextureStatus_ to be set to something other than Ok? I can't be blindly adding UTexture2D objects to the array, as it would keep the object alive longer than I would want.

EDIT: I also just noticed that this is all now handled in static callbacks. I don't even have access to what I was originally storing the FontAtlasTexture pointer in :/

SaltyPandaCedric avatar Jun 28 '25 10:06 SaltyPandaCedric

Still fixing up other errors, but I updated my logic now to do the following. I removed the manual tracking of the references and redefined ImTextureID to the actual types that should make sure nothing gets garbage collected.

#if WITH_ENGINE
#define ImTextureID TStrongObjectPtr<UTexture>
#define ImTextureID_Invalid TStrongObjectPtr<UTexture>(nullptr)
#else
#define ImTextureID TSharedPtr<FSlateBrush>
#define ImTextureID_Invalid nullptr
#endif
static void ImGui_UpdateTexture(ImTextureData* TextureData)
{
	switch (TextureData->Status)
	{
		case ImTextureStatus_WantCreate:
		{
#if WITH_ENGINE
			const FImageView TextureView(TextureData->Pixels, TextureData->Width, TextureData->Height, ERawImageFormat::BGRA8);
			TStrongObjectPtr<UTexture> const NewTexture = TStrongObjectPtr(FImageUtils::CreateTexture2DFromImage(TextureView));
#else
			TSharedPtr<FSlateBrush> NewTexture = FSlateDynamicImageBrush::CreateWithImageData(
				TEXT("ImGuiTexture"), FVector2D(TextureData->Width, TextureData->Height),
				TArray(TextureData->Pixels, TextureData->Width * TextureData->Height * TextureData->BytesPerPixel));
#endif

			TextureData->SetTexID(NewTexture);
			TextureData->SetStatus(ImTextureStatus_OK);

			break;
		}
		case ImTextureStatus_WantUpdates:
		{
			TextureData->SetStatus(ImTextureStatus_OK);

			break;
		}
		case ImTextureStatus_WantDestroy:
		{
			TextureData->GetTexID().Reset();
			TextureData->SetTexID(ImTextureID_Invalid);
			TextureData->SetStatus(ImTextureStatus_Destroyed);

			break;
		}
		default: break;
	}
}

I'm not entirely sure what I want to do within WantUpdates though.

	if (Context)
	{
		ImGuiPlatformIO& PlatformIO = ImGui::GetPlatformIO();
		for (ImTextureData* const TextureData : PlatformIO.Textures)
		{
			if (TextureData == nullptr)
			{
				continue;
			}

			if (TextureData->RefCount == 1)
			{
				TextureData->GetTexID().Reset();
				TextureData->SetTexID(ImTextureID_Invalid);
				TextureData->SetStatus(ImTextureStatus_Destroyed);
				TextureData->BackendUserData = nullptr;
			}
		}

		ImGui::DestroyContext(Context);
		Context = nullptr;
	}

Cleanup on shutdown.

static void ImGui_RenderWindow(ImGuiViewport* Viewport, void* UserData)
{
	if (Viewport->DrawData->Textures != nullptr)
	{
		for (ImTextureData* TextureData : *Viewport->DrawData->Textures)
		{
			if (TextureData->Status != ImTextureStatus_OK)
			{
				ImGui_UpdateTexture(TextureData);
			}
		}
	}
}

I'm not actively handling rendering the Window here. That has to do with the inner Unreal Engine loop. I still placed the UpdateTextures call here though, as per docs.

Haven't been able to compile yet, so not sure if this even works. Need to check updates for external dependencies first.

SaltyPandaCedric avatar Jun 28 '25 12:06 SaltyPandaCedric

However, if I were to add a second text font, such as Roboto-Bold, without also merging the icon font into that one too, I would end up losing access to the icon font when using the Roboto-Bold font via PushFont for some of my text, right?

Right. So you'd currently need to add it to the bold variant as well. This hasn't changed.

If that's true, is the idea to define the above FontAwesomeFontConfig_Solid once before adding the Roboto-XYZ fonts, and then always adding the FontAwesomeFontConfig_Solid after each time I add a Roboto font? So basically this: [...] And if that's not correct, what is the suggested way of handling this?

Currently the "same" font source in two fonts won't be merged/reused, which is in theory a waste - but in practice it is already miles better than 1.92. I believe it will be done 1.92.1 in 1.92.2. It is better in the current version if you preload the file data and use AddFontFromMemoryXXX so the TTF data is not loaded twice. Its indeed a flaw in 1.92.0 but which will be addressed soon normally.

" font inputs are now scanned in order for the first font input which the desired glyph. " That sentence doesn't read like proper English to me. Either this is a language barrier on my end, or there are mistakes in it.

Right, my bad. I have reworded it as:

  - When searching for a glyph in multiple merged fonts: we search for the FIRST font source
    which contains the desired glyph. Because the user doesn't need to provide glyph ranges
    any more, it is possible that a glyph that you expected to fetch from a secondary/merged
    icon font may be erroneously fetched from the primary font.

That part I also can't follow. Does this mean that, if previously there were issues with this overlap of glyph range, this can now be resolved by excluding the glyphs when adding Font Source 1? If I didn't have any issues with this before, then I assume this is all irrelevant to me?

GlyphExcludeRanges[] didn't exist before 1.92. We added it as a counter measure for the fact that we don't need to specify input ranges anymore.

Does this refer to the section Textures: added partial texture update protocol. in the update notes? If so, do I understand correctly that I now have to actively handle the different ImTextureStatus values etc.?

Yes. This is described in https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md#rendering-adding-support-for-imguibackendflags_rendererhastextures-192.

I think you should commit the commit/diff for a few of the backends and you will understand this better (there are linked from this link above)

I don't think I had to do any of this before.

Well yes, it is new.

If I set io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures does this only affect the FontAtlasTexture or will this then also call for the other Textures, such as an asset already loaded by Unreal Engine?

Dear ImGui doesn't know anything about Unreal Engine, it cannot have an effect on it.

It seems dangerous that you store fancy pointers in ImTextureID, since Dear ImGui doesn't call constructor/destructors.

I'm not actively handling rendering the Window here. That has to do with the inner Unreal Engine loop. I still placed the UpdateTextures call here though, as per docs.

You can do it anytime between ImGui::Render() and your rendering. The array will be the same in all viewports and it is expected that the first one which process the textures will handle all requests, so subsequent viewports rendering (if you support it) will have nothing to do.

ocornut avatar Jun 28 '25 15:06 ocornut

Thanks for the info about the Font situation.

I think you should commit the commit/diff for a few of the backends and you will understand this better (there are linked from this link above)

Will do, thanks!

Well yes, it is new.

Right, wasn't sure if "new" could still refer to something added in a recent previous version while being optional still.

Dear ImGui doesn't know anything about Unreal Engine, it cannot have an effect on it.

I think I worded that badly. What I meant was if any of the Assets that are potentially passed into functions such as ImGui::Image end up in that Textures array. I should probably have asked what Textures/TextureData actually goes into the Textures Array. By now I assume it's probably only the FontAtlas, or?

It seems dangerous that you store fancy pointers in ImTextureID, since Dear ImGui doesn't call constructor/destructors.

Yeah, spoiler, it crashed. I went back and changed the type to just UTexture* and continue storing them in my own managed Array.

I'm currently facing the problem that the FontAtlas seems to only have a handful of icons and "Debug" as glyphs. I'm probably doing something wrong still somewhere, so will investigate this a bit further.

You can do it anytime between ImGui::Render() and your rendering. The array will be the same in all viewports and it is expected that the first one which process the textures will handle all requests, so subsequent viewports rendering (if you support it) will have nothing to do.

Yeah, that's all working. I'm mainly upgrading to 1.92 at the moment. I only added that comment to avoid any confusion as to why I'm only updating the Textures there and not more.

SaltyPandaCedric avatar Jun 28 '25 16:06 SaltyPandaCedric

I think I worded that badly. What I meant was if any of the Assets that are potentially passed into functions such as ImGui::Image end up in that Textures array. I should probably have asked what Textures/TextureData actually goes into the Textures Array. By now I assume it's probably only the FontAtlas, or?

Correct, it would be the font atlas and “possibly” later other things such as textures created using high-lever helpers. But any texture that you created yourself and provide the ImTextureId of won’t be added to this array.

I'm currently facing the problem that the FontAtlas seems to only have a handful of icons and "Debug" as glyphs. I'm probably doing something wrong still somewhere, so will investigate this a bit further.

It could be that initial creation works, and they are the only glyphes uploaded during frame 0, but that subsequent updates are not processed.

ocornut avatar Jun 28 '25 16:06 ocornut

Right, I did notice that it does trigger a breakpoint in there, so I tried to update it now, and it partially worked haha.

case ImTextureStatus_WantUpdates:
{
	ENQUEUE_RENDER_COMMAND(UpdateImTextureData)
	([TextureData](FRHICommandListImmediate &RHICmdList) -> void
	{
		UTexture* const Texture = TextureData->GetTexID();

		for (const ImTextureRect& Update : TextureData->Updates)
		{
			FUpdateTextureRegion2D Region(Update.x, Update.y, 0, 0, Update.w, Update.h);
			RHICmdList.UpdateTexture2D(Texture->GetResource()->GetTexture2DRHI(), 0, Region, TextureData->GetPitch(), (uint8*)TextureData->GetPixelsAt(Update.x, Update.y));
		}
	});

	TextureData->SetStatus(ImTextureStatus_OK);

	break;
}

I need to figure out if I have to run this through UE's RenderThread, given that the UTexture can be created outside of it. It went from only having a few icons and "Debug" now to this:

Image

Image

That looks pretty broken still. Will have to further investigate.

EDIT: I see now that it only adds the glyphs to the FontAtlasTexture when it needs them. Saw it adding to the Texture while displaying it cause I opened a different window that had some "new" glyphs needed.

EDIT2: It seems like the initial window that I open (which is a Command Palette), has all its glyphs (minus FontAwesome, cause I commented that out), but any other window opened after that only has the initial glyphs from that window available.

SaltyPandaCedric avatar Jun 28 '25 16:06 SaltyPandaCedric

I think I got it to work now. I had to ensure the Updates array is copied. I'm not doing multithreaded stuff often enough to have a good feeling for what data I can capture and what not.

I guess the Updates array got (partially?) cleared on the GameThread before the RenderThread could actually utilize it. Noticed that only because using the UpdateRect directly caused it to crash.

case ImTextureStatus_WantUpdates:
{
	ENQUEUE_RENDER_COMMAND(UpdateImTextureData)
	([Updates = TextureData->Updates, TextureData](FRHICommandListImmediate &RHICmdList) -> void
	{
		UTexture* const Texture = TextureData->GetTexID();

		for (const ImTextureRect& UpdateRect : Updates)
		{
			FUpdateTextureRegion2D Region(UpdateRect.x, UpdateRect.y, 0, 0, UpdateRect.w, UpdateRect.h);
			RHICmdList.UpdateTexture2D(Texture->GetResource()->GetTexture2DRHI(), 0, Region, TextureData->GetPitch(), (uint8*)TextureData->GetPixelsAt(UpdateRect.x, UpdateRect.y));
		}
	});

	TextureData->SetStatus(ImTextureStatus_OK);

	break;
}

It does make me wonder, however, if I also have to copy the Pixels, as they could also have changed before being accessed. I don't think it would break anything, as long as I don't resize the texture, as the pixels should still be allocated. Only thing that could happen is that an update already partially applies a change to the texture that a future update will queue anyway.

EDIT: Celebrated too early. Clicking on the "Font Sizes" tree entry in the ImGui demo crashes still.

EDIT2: Seems like the TextureData not being copied is indeed an issue, because ImGui can ask it to be destroyed before the RenderCommand is executed, resulting in the TextureData being fully or partially invalid. Adding checks against that now, but I presume I should do this differently.

SaltyPandaCedric avatar Jun 28 '25 17:06 SaltyPandaCedric

Alright, had a look through Unreal Engine's source code a bit, to see how others handle this, and it seems like I can call FlushRenderCommands(), which waits for the RenderCommand to finish before continuing. That way, I can remove the copying of the Updates array and also don't have to worry about the TextureData being invalidated. And on top of that, I can ensure that the status of the TextureData is only set to OK once it's actually updated.

case ImTextureStatus_WantUpdates:
{
	ENQUEUE_RENDER_COMMAND(UpdateImTextureData)
	([TextureData](FRHICommandListImmediate &RHICmdList) -> void
	{
		UTexture* const Texture = TextureData->GetTexID();
		const FTextureResource* const TextureResource = Texture->GetResource();
		const int TexturePitch = TextureData->GetPitch();

		for (const auto& [Left, Top, Width, Height] : TextureData->Updates)
		{
			const FUpdateTextureRegion2D Region(Left, Top, 0, 0, Width, Height);
			RHICmdList.UpdateTexture2D(TextureResource->GetTexture2DRHI(), 0, Region, TexturePitch, static_cast<uint8*>(TextureData->GetPixelsAt(Left, Top)));
		}
	});

	FlushRenderingCommands();

	TextureData->SetStatus(ImTextureStatus_OK);

	break;
}

I assume the earlier crash I had was when the number of glyphs exceeded the space available on the FontAtlas texture, so it got recreated. How does this actually work @ocornut , does it throw away old glyphs that are currently not being used? And what happens if this Atlas is exceeded in terms of size, but all glyphs are needed, does it destroy this one and ask to create a bigger one in width/height?

Also looks quite funky (in a good way) seeing the FontAtlas updated in real-time. Not entirely sure why it sometimes flashes white. Maybe it still wants to draw it, but the data is already reset? I don't think I will be looking at the FontAtlas texture a lo,t and the rest of the UI seems to behave normally, but it would still be nice to understand if there is still a small "bug" in how I handle things.

https://github.com/user-attachments/assets/4b5bcc90-0f24-44c7-8747-c73dba5cc986

SaltyPandaCedric avatar Jun 28 '25 17:06 SaltyPandaCedric

Not entirely sure why it sometimes flashes white. Maybe it still wants to draw it, but the data is already reset? I don't think I will be looking at the FontAtlas texture a lo,t and the rest of the UI seems to behave normally, but it would still be nice to understand if there is still a small "bug" in how I handle things.

Does the view of the same texture inside Demo is correct ?

Two possible reasons:

(1) How are you submitting this "Font Atlas Texture" code? If you were to extract the ImTextureID and pass this to Image() as a imTextureRef this is going to be invalid for one frame, and perhaps that's the result it. See https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-what-are-imtextureidimtextureref

If you want to display the font atlas you need to use ImFontAtlas::TexRef which is the same as atlas->TexData->GetTexRef(). If you use atlas->TexData->GetTexID() you get a lossy conversion which will affect you during the first frame. You would get a vaid imTextureData* pointer stored in the TexRef, but downcasting to TexID would get a 0 (== ImTextureID_Invalid).

Normally during the render loop, ImDrawCmd::GetTexID() would assert for this but you might have asserts disabled?

// Using an indirection to avoid patching ImDrawCmd after a SetTexID() call (but this could be an alternative solution too)
inline ImTextureID ImDrawCmd::GetTexID() const
{
    // If you are getting this assert: A renderer backend with support for ImGuiBackendFlags_RendererHasTextures (1.92)
    // must iterate and handle ImTextureData requests stored in ImDrawData::Textures[].
    ImTextureID tex_id = TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID; // == TexRef.GetTexID() above.
    if (TexRef._TexData != NULL)
        IM_ASSERT(tex_id != ImTextureID_Invalid && "ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexID() after handling ImTextureStatus_WantCreate request!");
    return tex_id;
}

(2) It may be the opposite: you may be destroying textures too earlier, and staged rendering is using a texture which was already destroying and somehow the code is being nice with it. Standard backends are doing:

    if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
        ImGui_ImplDX11_DestroyTexture(tex);

You may want to use e.g. tex->UnusedFrames > 2 if somehow texture destruction needs to be deferred with your rendering pipeline.

ocornut avatar Jul 08 '25 13:07 ocornut

So, after quite some time, I got back to this. The frame where it shows a white square was likely due to rendering an invalid SlateBrush. UE5.6 crashed on this by now, and resolving the crash also resolves the white square.

The last thing I was wondering is, if it's expected to have a frame with 2 FontAtlas textures, when the old texture is "full" and a new one gets created. I breakpointed the code that handles ImTextureStatus_WantCreate and ImTextureStatus_WantDestroy, and for me, WantCreate calls in EngineFrame X, while WantDestroy calls in EngineFrame X+1.

This doesn't seem to break anything, and it might even somewhat make sense to ensure we don't end up with no FontAtlas texture, but I still feel like I should ask if this is expected or if something in my setup might be wrong.

EDIT:

Looked into it a bit, and it seems to be expected:

// Queue old as to destroy next frame
old_tex->WantDestroyNextFrame = true;

Just to wrap this up: Is there a reason we can't create and destroy in the same frame? Is it a matter of Create and Destroy not being guaranteed to happen in an order that we always have a FontAtlas texture, or why do we delay the destroy to the next frame?

EDIT2:

I think I will just use the Status and the WantsDestroyNextFrame boolean before drawing the texture to filter it out for now.

EDIT3:

Never mind, ImGui still accounts for the texture, even if it's WantsDestroyNextFrame, so the FontAtlas texture is still in use. Guess I'll ignore this and see it as "intended".

eXifreXi avatar Aug 20 '25 09:08 eXifreXi

ImTextureStatus_WantCreate and ImTextureStatus_WantDestroy, and for me, WantCreate calls in EngineFrame X, while WantDestroy calls in EngineFrame X+1.

That’s expected.

Vertices have already been output using the old texture.

ocornut avatar Aug 20 '25 10:08 ocornut