Skip to content

Commit dbb91a5

Browse files
committed
Backends: OpenGL3: added ImGuiBackendFlags_RendererHasTextures support.
+ Removed ImGui_ImplOpenGL3_CreateFontsTexture() and ImGui_ImplOpenGL3_DestroyFontsTexture().
1 parent eefe5d5 commit dbb91a5

File tree

3 files changed

+103
-48
lines changed

3 files changed

+103
-48
lines changed

backends/imgui_impl_opengl3.cpp

Lines changed: 91 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
55

66
// Implemented features:
7-
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
7+
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
88
// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]
9+
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
910

1011
// About WebGL/ES:
1112
// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
@@ -22,6 +23,7 @@
2223

2324
// CHANGELOG
2425
// (minor and older changes stripped away, please see git history for details)
26+
// 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL3_CreateFontsTexture() and ImGui_ImplOpenGL3_DestroyFontsTexture().
2527
// 2025-06-04: OpenGL: Made GLES 3.20 contexts not access GL_CONTEXT_PROFILE_MASK nor GL_PRIMITIVE_RESTART. (#8664)
2628
// 2025-02-18: OpenGL: Lazily reinitialize embedded GL loader for when calling backend from e.g. other DLL boundaries. (#8406)
2729
// 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap.
@@ -231,7 +233,7 @@ struct ImGui_ImplOpenGL3_Data
231233
bool GlProfileIsES3;
232234
bool GlProfileIsCompat;
233235
GLint GlProfileMask;
234-
GLuint FontTexture;
236+
GLint MaxTextureSize;
235237
GLuint ShaderHandle;
236238
GLint AttribLocationTex; // Uniforms location
237239
GLint AttribLocationProjMtx;
@@ -244,6 +246,7 @@ struct ImGui_ImplOpenGL3_Data
244246
bool HasPolygonMode;
245247
bool HasClipOrigin;
246248
bool UseBufferSubData;
249+
ImVector<char> TempBuffer;
247250

248251
ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
249252
};
@@ -326,6 +329,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
326329
if (major == 0 && minor == 0)
327330
sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
328331
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
332+
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &bd->MaxTextureSize);
329333

330334
#if defined(IMGUI_IMPL_OPENGL_ES3)
331335
bd->GlProfileIsES3 = true;
@@ -359,6 +363,10 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
359363
if (bd->GlVersion >= 320)
360364
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
361365
#endif
366+
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
367+
368+
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
369+
platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = (int)bd->MaxTextureSize;
362370

363371
// Store GLSL version string so we can refer to it later in case we recreate shaders.
364372
// Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure.
@@ -411,7 +419,7 @@ void ImGui_ImplOpenGL3_Shutdown()
411419
ImGui_ImplOpenGL3_DestroyDeviceObjects();
412420
io.BackendRendererName = nullptr;
413421
io.BackendRendererUserData = nullptr;
414-
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
422+
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
415423
IM_DELETE(bd);
416424
}
417425

@@ -424,8 +432,6 @@ void ImGui_ImplOpenGL3_NewFrame()
424432

425433
if (!bd->ShaderHandle)
426434
ImGui_ImplOpenGL3_CreateDeviceObjects();
427-
if (!bd->FontTexture)
428-
ImGui_ImplOpenGL3_CreateFontsTexture();
429435
}
430436

431437
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
@@ -517,6 +523,13 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
517523

518524
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
519525

526+
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
527+
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
528+
if (draw_data->Textures != nullptr)
529+
for (ImTextureData* tex : *draw_data->Textures)
530+
if (tex->Status != ImTextureStatus_OK)
531+
ImGui_ImplOpenGL3_UpdateTexture(tex);
532+
520533
// Backup GL state
521534
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
522535
glActiveTexture(GL_TEXTURE0);
@@ -685,50 +698,82 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
685698
(void)bd; // Not all compilation paths use this
686699
}
687700

688-
bool ImGui_ImplOpenGL3_CreateFontsTexture()
701+
static void ImGui_ImplOpenGL3_DestroyTexture(ImTextureData* tex)
689702
{
690-
ImGuiIO& io = ImGui::GetIO();
691-
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
703+
GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;
704+
glDeleteTextures(1, &gl_tex_id);
705+
706+
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
707+
tex->SetTexID(ImTextureID_Invalid);
708+
tex->SetStatus(ImTextureStatus_Destroyed);
709+
}
692710

693-
// Build texture atlas
694-
unsigned char* pixels;
695-
int width, height;
696-
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.
697-
698-
// Upload texture to graphics system
699-
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
700-
GLint last_texture;
701-
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
702-
GL_CALL(glGenTextures(1, &bd->FontTexture));
703-
GL_CALL(glBindTexture(GL_TEXTURE_2D, bd->FontTexture));
704-
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
705-
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
706-
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
707-
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
711+
void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex)
712+
{
713+
if (tex->Status == ImTextureStatus_WantCreate)
714+
{
715+
// Create and upload new texture to graphics system
716+
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
717+
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
718+
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
719+
const void* pixels = tex->GetPixels();
720+
GLuint gl_texture_id = 0;
721+
722+
// Upload texture to graphics system
723+
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
724+
GLint last_texture;
725+
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
726+
GL_CALL(glGenTextures(1, &gl_texture_id));
727+
GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_texture_id));
728+
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
729+
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
730+
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
731+
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
708732
#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
709-
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
733+
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
710734
#endif
711-
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
712-
713-
// Store identifier
714-
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
735+
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
715736

716-
// Restore state
717-
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture));
718-
719-
return true;
720-
}
737+
// Store identifiers
738+
tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id);
739+
tex->SetStatus(ImTextureStatus_OK);
721740

722-
void ImGui_ImplOpenGL3_DestroyFontsTexture()
723-
{
724-
ImGuiIO& io = ImGui::GetIO();
725-
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
726-
if (bd->FontTexture)
741+
// Restore state
742+
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture));
743+
}
744+
else if (tex->Status == ImTextureStatus_WantUpdates)
727745
{
728-
glDeleteTextures(1, &bd->FontTexture);
729-
io.Fonts->SetTexID(0);
730-
bd->FontTexture = 0;
746+
// Update selected blocks. We only ever write to textures regions which have never been used before!
747+
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
748+
GLint last_texture;
749+
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
750+
751+
GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;
752+
GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_tex_id));
753+
#if 0// GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
754+
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width));
755+
for (ImTextureRect& r : tex->Updates)
756+
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)));
757+
GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
758+
#else
759+
// GL ES doesn't have GL_UNPACK_ROW_LENGTH, so we need to (A) copy to a contiguous buffer or (B) upload line by line.
760+
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
761+
for (ImTextureRect& r : tex->Updates)
762+
{
763+
const int src_pitch = r.w * tex->BytesPerPixel;
764+
bd->TempBuffer.resize(r.h * src_pitch);
765+
char* out_p = bd->TempBuffer.Data;
766+
for (int y = 0; y < r.h; y++, out_p += src_pitch)
767+
memcpy(out_p, tex->GetPixelsAt(r.x, r.y + y), src_pitch);
768+
IM_ASSERT(out_p == bd->TempBuffer.end());
769+
GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, bd->TempBuffer.Data));
770+
}
771+
#endif
772+
tex->SetStatus(ImTextureStatus_OK);
773+
GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); // Restore state
731774
}
775+
else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
776+
ImGui_ImplOpenGL3_DestroyTexture(tex);
732777
}
733778

734779
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
@@ -951,8 +996,6 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
951996
glGenBuffers(1, &bd->VboHandle);
952997
glGenBuffers(1, &bd->ElementsHandle);
953998

954-
ImGui_ImplOpenGL3_CreateFontsTexture();
955-
956999
// Restore modified GL state
9571000
glBindTexture(GL_TEXTURE_2D, last_texture);
9581001
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
@@ -972,7 +1015,11 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects()
9721015
if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
9731016
if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
9741017
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
975-
ImGui_ImplOpenGL3_DestroyFontsTexture();
1018+
1019+
// Destroy all textures
1020+
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
1021+
if (tex->RefCount == 1)
1022+
ImGui_ImplOpenGL3_DestroyTexture(tex);
9761023
}
9771024

9781025
//-----------------------------------------------------------------------------

backends/imgui_impl_opengl3.h

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

66
// Implemented features:
7-
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
7+
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
88
// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]
9+
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
910

1011
// About WebGL/ES:
1112
// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
@@ -36,11 +37,12 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
3637
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
3738

3839
// (Optional) Called by Init/NewFrame/Shutdown
39-
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
40-
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
4140
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
4241
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
4342

43+
// (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.
44+
IMGUI_IMPL_API void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex);
45+
4446
// Configuration flags to add in your imconfig file:
4547
//#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten)
4648
//#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android)

backends/imgui_impl_opengl3_loader.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ typedef khronos_uint8_t GLubyte;
167167
#define GL_SCISSOR_TEST 0x0C11
168168
#define GL_UNPACK_ROW_LENGTH 0x0CF2
169169
#define GL_PACK_ALIGNMENT 0x0D05
170+
#define GL_MAX_TEXTURE_SIZE 0x0D33
170171
#define GL_TEXTURE_2D 0x0DE1
171172
#define GL_UNSIGNED_BYTE 0x1401
172173
#define GL_UNSIGNED_SHORT 0x1403
@@ -224,11 +225,13 @@ typedef khronos_float_t GLclampf;
224225
typedef double GLclampd;
225226
#define GL_TEXTURE_BINDING_2D 0x8069
226227
typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices);
228+
typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
227229
typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
228230
typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures);
229231
typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
230232
#ifdef GL_GLEXT_PROTOTYPES
231233
GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
234+
GLAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
232235
GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture);
233236
GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);
234237
GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
@@ -478,7 +481,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
478481

479482
/* gl3w internal state */
480483
union ImGL3WProcs {
481-
GL3WglProc ptr[59];
484+
GL3WglProc ptr[60];
482485
struct {
483486
PFNGLACTIVETEXTUREPROC ActiveTexture;
484487
PFNGLATTACHSHADERPROC AttachShader;
@@ -534,6 +537,7 @@ union ImGL3WProcs {
534537
PFNGLSHADERSOURCEPROC ShaderSource;
535538
PFNGLTEXIMAGE2DPROC TexImage2D;
536539
PFNGLTEXPARAMETERIPROC TexParameteri;
540+
PFNGLTEXSUBIMAGE2DPROC TexSubImage2D;
537541
PFNGLUNIFORM1IPROC Uniform1i;
538542
PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv;
539543
PFNGLUSEPROGRAMPROC UseProgram;
@@ -599,6 +603,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs;
599603
#define glShaderSource imgl3wProcs.gl.ShaderSource
600604
#define glTexImage2D imgl3wProcs.gl.TexImage2D
601605
#define glTexParameteri imgl3wProcs.gl.TexParameteri
606+
#define glTexSubImage2D imgl3wProcs.gl.TexSubImage2D
602607
#define glUniform1i imgl3wProcs.gl.Uniform1i
603608
#define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv
604609
#define glUseProgram imgl3wProcs.gl.UseProgram
@@ -894,6 +899,7 @@ static const char *proc_names[] = {
894899
"glShaderSource",
895900
"glTexImage2D",
896901
"glTexParameteri",
902+
"glTexSubImage2D",
897903
"glUniform1i",
898904
"glUniformMatrix4fv",
899905
"glUseProgram",

0 commit comments

Comments
 (0)