Skip to content

Commit 0430c55

Browse files
committed
Backends: OpenGL2: added ImGuiBackendFlags_RendererHasTextures support.
Removed ImGui_ImplOpenGL2_CreateFontsTexture() and ImGui_ImplOpenGL2_DestroyFontsTexture().
1 parent dbb91a5 commit 0430c55

File tree

2 files changed

+78
-51
lines changed

2 files changed

+78
-51
lines changed

backends/imgui_impl_opengl2.cpp

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
33

44
// Implemented features:
5-
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
5+
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
6+
// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures).
67
// Missing features or Issues:
78
// [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
89

@@ -24,6 +25,7 @@
2425

2526
// CHANGELOG
2627
// (minor and older changes stripped away, please see git history for details)
28+
// 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL2_CreateFontsTexture() and ImGui_ImplOpenGL2_DestroyFontsTexture().
2729
// 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap.
2830
// 2024-06-28: OpenGL: ImGui_ImplOpenGL2_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL2_DestroyFontsTexture(). (#7748)
2931
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
@@ -81,8 +83,6 @@
8183
// OpenGL data
8284
struct ImGui_ImplOpenGL2_Data
8385
{
84-
GLuint FontTexture;
85-
8686
ImGui_ImplOpenGL2_Data() { memset((void*)this, 0, sizeof(*this)); }
8787
};
8888

@@ -104,6 +104,7 @@ bool ImGui_ImplOpenGL2_Init()
104104
ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)();
105105
io.BackendRendererUserData = (void*)bd;
106106
io.BackendRendererName = "imgui_impl_opengl2";
107+
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
107108

108109
return true;
109110
}
@@ -117,18 +118,15 @@ void ImGui_ImplOpenGL2_Shutdown()
117118
ImGui_ImplOpenGL2_DestroyDeviceObjects();
118119
io.BackendRendererName = nullptr;
119120
io.BackendRendererUserData = nullptr;
121+
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasTextures);
120122
IM_DELETE(bd);
121123
}
122124

123125
void ImGui_ImplOpenGL2_NewFrame()
124126
{
125127
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
126128
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL2_Init()?");
127-
128-
if (!bd->FontTexture)
129-
ImGui_ImplOpenGL2_CreateDeviceObjects();
130-
if (!bd->FontTexture)
131-
ImGui_ImplOpenGL2_CreateFontsTexture();
129+
IM_UNUSED(bd);
132130
}
133131

134132
static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height)
@@ -186,6 +184,13 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data)
186184
if (fb_width == 0 || fb_height == 0)
187185
return;
188186

187+
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
188+
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
189+
if (draw_data->Textures != nullptr)
190+
for (ImTextureData* tex : *draw_data->Textures)
191+
if (tex->Status != ImTextureStatus_OK)
192+
ImGui_ImplOpenGL2_UpdateTexture(tex);
193+
189194
// Backup GL state
190195
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
191196
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
@@ -259,57 +264,77 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data)
259264
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, last_tex_env_mode);
260265
}
261266

262-
bool ImGui_ImplOpenGL2_CreateFontsTexture()
263-
{
264-
// Build texture atlas
265-
ImGuiIO& io = ImGui::GetIO();
266-
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
267-
unsigned char* pixels;
268-
int width, height;
269-
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.
270-
271-
// Upload texture to graphics system
272-
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
273-
GLint last_texture;
274-
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
275-
glGenTextures(1, &bd->FontTexture);
276-
glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
277-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
278-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
279-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
280-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
281-
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
282-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
283-
284-
// Store our identifier
285-
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
286-
287-
// Restore state
288-
glBindTexture(GL_TEXTURE_2D, last_texture);
289-
290-
return true;
291-
}
292-
293-
void ImGui_ImplOpenGL2_DestroyFontsTexture()
267+
void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex)
294268
{
295-
ImGuiIO& io = ImGui::GetIO();
296-
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
297-
if (bd->FontTexture)
269+
if (tex->Status == ImTextureStatus_WantCreate)
270+
{
271+
// Create and upload new texture to graphics system
272+
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
273+
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
274+
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
275+
const void* pixels = tex->GetPixels();
276+
GLuint gl_texture_id = 0;
277+
278+
// Upload texture to graphics system
279+
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
280+
GLint last_texture;
281+
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
282+
GL_CALL(glGenTextures(1, &gl_texture_id));
283+
GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_texture_id));
284+
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
285+
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
286+
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
287+
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
288+
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
289+
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
290+
291+
// Store identifiers
292+
tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id);
293+
tex->SetStatus(ImTextureStatus_OK);
294+
295+
// Restore state
296+
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture));
297+
}
298+
else if (tex->Status == ImTextureStatus_WantUpdates)
299+
{
300+
// Update selected blocks. We only ever write to textures regions which have never been used before!
301+
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
302+
GLint last_texture;
303+
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
304+
305+
GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;
306+
GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_tex_id));
307+
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width));
308+
for (ImTextureRect& r : tex->Updates)
309+
GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y)));
310+
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
311+
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); // Restore state
312+
tex->SetStatus(ImTextureStatus_OK);
313+
}
314+
else if (tex->Status == ImTextureStatus_WantDestroy)
298315
{
299-
glDeleteTextures(1, &bd->FontTexture);
300-
io.Fonts->SetTexID(0);
301-
bd->FontTexture = 0;
316+
GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;
317+
glDeleteTextures(1, &gl_tex_id);
318+
319+
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
320+
tex->SetTexID(ImTextureID_Invalid);
321+
tex->SetStatus(ImTextureStatus_Destroyed);
302322
}
303323
}
304324

305325
bool ImGui_ImplOpenGL2_CreateDeviceObjects()
306326
{
307-
return ImGui_ImplOpenGL2_CreateFontsTexture();
327+
return true;
308328
}
309329

310330
void ImGui_ImplOpenGL2_DestroyDeviceObjects()
311331
{
312-
ImGui_ImplOpenGL2_DestroyFontsTexture();
332+
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
333+
if (tex->RefCount == 1)
334+
{
335+
tex->SetStatus(ImTextureStatus_WantDestroy);
336+
ImGui_ImplOpenGL2_UpdateTexture(tex);
337+
}
313338
}
314339

315340
//-----------------------------------------------------------------------------

backends/imgui_impl_opengl2.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
33

44
// Implemented features:
5-
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
5+
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
6+
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
67
// Missing features or Issues:
78
// [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
89

@@ -33,9 +34,10 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame();
3334
IMGUI_IMPL_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data);
3435

3536
// Called by Init/NewFrame/Shutdown
36-
IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture();
37-
IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture();
3837
IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects();
3938
IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects();
4039

40+
// (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.
41+
IMGUI_IMPL_API void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex);
42+
4143
#endif // #ifndef IMGUI_DISABLE

0 commit comments

Comments
 (0)