Skip to content

Commit e538883

Browse files
committed
Backends: SDL_Renderer3: added ImGuiBackendFlags_RendererHasTextures support.
1 parent 9fa65cd commit e538883

File tree

2 files changed

+66
-45
lines changed

2 files changed

+66
-45
lines changed

backends/imgui_impl_sdlrenderer3.cpp

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
// and it might be difficult to step out of those boundaries.
1111

1212
// Implemented features:
13-
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
13+
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
1414
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
15+
// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures).
1516
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
1617

1718
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@@ -23,6 +24,7 @@
2324
// - Introduction, links and more at the top of imgui.cpp
2425

2526
// CHANGELOG
27+
// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer3_CreateFontsTexture() and ImGui_ImplSDLRenderer3_DestroyFontsTexture().
2628
// 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color.
2729
// 2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
2830
// 2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009).
@@ -51,7 +53,6 @@
5153
struct ImGui_ImplSDLRenderer3_Data
5254
{
5355
SDL_Renderer* Renderer; // Main viewport's renderer
54-
SDL_Texture* FontTexture;
5556
ImVector<SDL_FColor> ColorBuffer;
5657

5758
ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); }
@@ -77,6 +78,7 @@ bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer)
7778
io.BackendRendererUserData = (void*)bd;
7879
io.BackendRendererName = "imgui_impl_sdlrenderer3";
7980
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
81+
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
8082

8183
bd->Renderer = renderer;
8284

@@ -93,7 +95,7 @@ void ImGui_ImplSDLRenderer3_Shutdown()
9395

9496
io.BackendRendererName = nullptr;
9597
io.BackendRendererUserData = nullptr;
96-
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
98+
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
9799
IM_DELETE(bd);
98100
}
99101

@@ -109,9 +111,7 @@ void ImGui_ImplSDLRenderer3_NewFrame()
109111
{
110112
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
111113
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer3_Init()?");
112-
113-
if (!bd->FontTexture)
114-
ImGui_ImplSDLRenderer3_CreateDeviceObjects();
114+
IM_UNUSED(bd);
115115
}
116116

117117
// https://github.com/libsdl-org/SDL/issues/9009
@@ -152,6 +152,13 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
152152
if (fb_width == 0 || fb_height == 0)
153153
return;
154154

155+
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
156+
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
157+
if (draw_data->Textures != nullptr)
158+
for (ImTextureData* tex : *draw_data->Textures)
159+
if (tex->Status != ImTextureStatus_OK)
160+
ImGui_ImplSDLRenderer3_UpdateTexture(tex);
161+
155162
// Backup SDL_Renderer state that will be modified to restore it afterwards
156163
struct BackupSDLRendererState
157164
{
@@ -235,55 +242,67 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
235242
SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr);
236243
}
237244

238-
// Called by Init/NewFrame/Shutdown
239-
bool ImGui_ImplSDLRenderer3_CreateFontsTexture()
245+
void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex)
240246
{
241-
ImGuiIO& io = ImGui::GetIO();
242247
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
243248

244-
// Build texture atlas
245-
unsigned char* pixels;
246-
int width, height;
247-
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
248-
249-
// Upload texture to graphics system
250-
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
251-
bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height);
252-
if (bd->FontTexture == nullptr)
249+
if (tex->Status == ImTextureStatus_WantCreate)
253250
{
254-
SDL_Log("error creating texture");
255-
return false;
251+
// Create and upload new texture to graphics system
252+
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
253+
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
254+
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
255+
256+
// Create texture
257+
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
258+
SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height);
259+
IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!");
260+
SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch());
261+
SDL_SetTextureBlendMode(sdl_texture, SDL_BLENDMODE_BLEND);
262+
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_LINEAR);
263+
264+
// Store identifiers
265+
tex->SetTexID((ImTextureID)(intptr_t)sdl_texture);
266+
tex->SetStatus(ImTextureStatus_OK);
256267
}
257-
SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width);
258-
SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND);
259-
SDL_SetTextureScaleMode(bd->FontTexture, SDL_SCALEMODE_LINEAR);
260-
261-
// Store our identifier
262-
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
263-
264-
return true;
265-
}
266-
267-
void ImGui_ImplSDLRenderer3_DestroyFontsTexture()
268-
{
269-
ImGuiIO& io = ImGui::GetIO();
270-
ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
271-
if (bd->FontTexture)
268+
else if (tex->Status == ImTextureStatus_WantUpdates)
272269
{
273-
io.Fonts->SetTexID(0);
274-
SDL_DestroyTexture(bd->FontTexture);
275-
bd->FontTexture = nullptr;
270+
// Update selected blocks. We only ever write to textures regions which have never been used before!
271+
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
272+
SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID;
273+
for (ImTextureRect& r : tex->Updates)
274+
{
275+
SDL_Rect sdl_r = { r.x, r.y, r.w, r.h };
276+
SDL_UpdateTexture(sdl_texture, &sdl_r, tex->GetPixelsAt(r.x, r.y), tex->GetPitch());
277+
}
278+
tex->SetStatus(ImTextureStatus_OK);
279+
}
280+
else if (tex->Status == ImTextureStatus_WantDestroy)
281+
{
282+
SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID;
283+
if (sdl_texture == nullptr)
284+
return;
285+
SDL_DestroyTexture(sdl_texture);
286+
287+
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
288+
tex->SetTexID(ImTextureID_Invalid);
289+
tex->SetStatus(ImTextureStatus_Destroyed);
276290
}
277291
}
278292

279-
bool ImGui_ImplSDLRenderer3_CreateDeviceObjects()
293+
void ImGui_ImplSDLRenderer3_CreateDeviceObjects()
280294
{
281-
return ImGui_ImplSDLRenderer3_CreateFontsTexture();
282295
}
283296

284297
void ImGui_ImplSDLRenderer3_DestroyDeviceObjects()
285298
{
286-
ImGui_ImplSDLRenderer3_DestroyFontsTexture();
299+
// Destroy all textures
300+
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
301+
if (tex->RefCount == 1)
302+
{
303+
tex->SetStatus(ImTextureStatus_WantDestroy);
304+
ImGui_ImplSDLRenderer3_UpdateTexture(tex);
305+
}
287306
}
288307

289308
//-----------------------------------------------------------------------------

backends/imgui_impl_sdlrenderer3.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
// and it might be difficult to step out of those boundaries.
1111

1212
// Implemented features:
13-
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
13+
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
1414
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
15+
// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures).
1516
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
1617

1718
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@@ -35,11 +36,12 @@ IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_NewFrame();
3536
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer);
3637

3738
// Called by Init/NewFrame/Shutdown
38-
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateFontsTexture();
39-
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyFontsTexture();
40-
IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_CreateDeviceObjects();
39+
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_CreateDeviceObjects();
4140
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
4241

42+
// (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.
43+
IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex);
44+
4345
// [BETA] Selected render state data shared with callbacks.
4446
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplSDLRenderer3_RenderDrawData() call.
4547
// (Please open an issue if you feel you need access to more data)

0 commit comments

Comments
 (0)