|
@@ -2,7 +2,8 @@
|
|
|
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
|
|
|
|
|
// Implemented features:
|
|
|
-// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
|
|
+// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
|
|
+// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures).
|
|
|
// Missing features or Issues:
|
|
|
// [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
|
|
|
|
@@ -24,6 +25,7 @@
|
|
|
|
|
|
// CHANGELOG
|
|
|
// (minor and older changes stripped away, please see git history for details)
|
|
|
+// 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL2_CreateFontsTexture() and ImGui_ImplOpenGL2_DestroyFontsTexture().
|
|
|
// 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap.
|
|
|
// 2024-06-28: OpenGL: ImGui_ImplOpenGL2_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL2_DestroyFontsTexture(). (#7748)
|
|
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
|
@@ -81,8 +83,6 @@
|
|
|
// OpenGL data
|
|
|
struct ImGui_ImplOpenGL2_Data
|
|
|
{
|
|
|
- GLuint FontTexture;
|
|
|
-
|
|
|
ImGui_ImplOpenGL2_Data() { memset((void*)this, 0, sizeof(*this)); }
|
|
|
};
|
|
|
|
|
@@ -104,6 +104,7 @@ bool ImGui_ImplOpenGL2_Init()
|
|
|
ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)();
|
|
|
io.BackendRendererUserData = (void*)bd;
|
|
|
io.BackendRendererName = "imgui_impl_opengl2";
|
|
|
+ io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -117,6 +118,7 @@ void ImGui_ImplOpenGL2_Shutdown()
|
|
|
ImGui_ImplOpenGL2_DestroyDeviceObjects();
|
|
|
io.BackendRendererName = nullptr;
|
|
|
io.BackendRendererUserData = nullptr;
|
|
|
+ io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasTextures);
|
|
|
IM_DELETE(bd);
|
|
|
}
|
|
|
|
|
@@ -124,11 +126,7 @@ void ImGui_ImplOpenGL2_NewFrame()
|
|
|
{
|
|
|
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
|
|
|
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL2_Init()?");
|
|
|
-
|
|
|
- if (!bd->FontTexture)
|
|
|
- ImGui_ImplOpenGL2_CreateDeviceObjects();
|
|
|
- if (!bd->FontTexture)
|
|
|
- ImGui_ImplOpenGL2_CreateFontsTexture();
|
|
|
+ IM_UNUSED(bd);
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
if (fb_width == 0 || fb_height == 0)
|
|
|
return;
|
|
|
|
|
|
+ // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
|
|
+ // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
|
|
+ if (draw_data->Textures != nullptr)
|
|
|
+ for (ImTextureData* tex : *draw_data->Textures)
|
|
|
+ if (tex->Status != ImTextureStatus_OK)
|
|
|
+ ImGui_ImplOpenGL2_UpdateTexture(tex);
|
|
|
+
|
|
|
// Backup GL state
|
|
|
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
|
|
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
|
@@ -259,57 +264,77 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data)
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, last_tex_env_mode);
|
|
|
}
|
|
|
|
|
|
-bool ImGui_ImplOpenGL2_CreateFontsTexture()
|
|
|
-{
|
|
|
- // Build texture atlas
|
|
|
- ImGuiIO& io = ImGui::GetIO();
|
|
|
- ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
|
|
|
- unsigned char* pixels;
|
|
|
- int width, height;
|
|
|
- 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.
|
|
|
-
|
|
|
- // Upload texture to graphics system
|
|
|
- // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
|
|
- GLint last_texture;
|
|
|
- glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
|
|
- glGenTextures(1, &bd->FontTexture);
|
|
|
- glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
|
|
|
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
|
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
|
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
|
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
-
|
|
|
- // Store our identifier
|
|
|
- io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
|
|
|
-
|
|
|
- // Restore state
|
|
|
- glBindTexture(GL_TEXTURE_2D, last_texture);
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-void ImGui_ImplOpenGL2_DestroyFontsTexture()
|
|
|
+void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex)
|
|
|
{
|
|
|
- ImGuiIO& io = ImGui::GetIO();
|
|
|
- ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
|
|
|
- if (bd->FontTexture)
|
|
|
+ if (tex->Status == ImTextureStatus_WantCreate)
|
|
|
+ {
|
|
|
+ // Create and upload new texture to graphics system
|
|
|
+ //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
|
|
+ IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
|
|
|
+ IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
|
|
+ const void* pixels = tex->GetPixels();
|
|
|
+ GLuint gl_texture_id = 0;
|
|
|
+
|
|
|
+ // Upload texture to graphics system
|
|
|
+ // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
|
|
+ GLint last_texture;
|
|
|
+ GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
|
|
|
+ GL_CALL(glGenTextures(1, &gl_texture_id));
|
|
|
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_texture_id));
|
|
|
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
|
|
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
|
|
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
|
|
|
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
|
|
|
+ GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
|
|
|
+ GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
|
|
|
+
|
|
|
+ // Store identifiers
|
|
|
+ tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id);
|
|
|
+ tex->SetStatus(ImTextureStatus_OK);
|
|
|
+
|
|
|
+ // Restore state
|
|
|
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture));
|
|
|
+ }
|
|
|
+ else if (tex->Status == ImTextureStatus_WantUpdates)
|
|
|
+ {
|
|
|
+ // Update selected blocks. We only ever write to textures regions which have never been used before!
|
|
|
+ // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
|
|
|
+ GLint last_texture;
|
|
|
+ GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
|
|
|
+
|
|
|
+ GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;
|
|
|
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_tex_id));
|
|
|
+ GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width));
|
|
|
+ for (ImTextureRect& r : tex->Updates)
|
|
|
+ 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)));
|
|
|
+ GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
|
|
|
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); // Restore state
|
|
|
+ tex->SetStatus(ImTextureStatus_OK);
|
|
|
+ }
|
|
|
+ else if (tex->Status == ImTextureStatus_WantDestroy)
|
|
|
{
|
|
|
- glDeleteTextures(1, &bd->FontTexture);
|
|
|
- io.Fonts->SetTexID(0);
|
|
|
- bd->FontTexture = 0;
|
|
|
+ GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;
|
|
|
+ glDeleteTextures(1, &gl_tex_id);
|
|
|
+
|
|
|
+ // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
|
|
+ tex->SetTexID(ImTextureID_Invalid);
|
|
|
+ tex->SetStatus(ImTextureStatus_Destroyed);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool ImGui_ImplOpenGL2_CreateDeviceObjects()
|
|
|
{
|
|
|
- return ImGui_ImplOpenGL2_CreateFontsTexture();
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
void ImGui_ImplOpenGL2_DestroyDeviceObjects()
|
|
|
{
|
|
|
- ImGui_ImplOpenGL2_DestroyFontsTexture();
|
|
|
+ for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
|
|
+ if (tex->RefCount == 1)
|
|
|
+ {
|
|
|
+ tex->SetStatus(ImTextureStatus_WantDestroy);
|
|
|
+ ImGui_ImplOpenGL2_UpdateTexture(tex);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|