Skip to content

Commit

Permalink
WIP. Crude dynamic font texture update.
Browse files Browse the repository at this point in the history
  • Loading branch information
thedmd committed Apr 30, 2021
1 parent b58b3aa commit ff825b0
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 40 deletions.
124 changes: 89 additions & 35 deletions backends/imgui_impl_dx9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
static int g_FontTextureWidth = 0;
static int g_FontTextureHeight = 0;
static ImVector<LPDIRECT3DTEXTURE9> g_FontTextures;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;

struct CUSTOMVERTEX
Expand All @@ -56,6 +54,8 @@ struct CUSTOMVERTEX
#define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16))
#endif

static LPDIRECT3DTEXTURE9 ImGui_ImplDX9_UpdateTexture(const ImTextureData& texture_data);

static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
{
// Setup viewport
Expand Down Expand Up @@ -259,19 +259,17 @@ void ImGui_ImplDX9_Shutdown()
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
}

static bool ImGui_ImplDX9_UpdateFontsTexture()
static LPDIRECT3DTEXTURE9 ImGui_ImplDX9_UpdateTexture(const ImTextureData& texture_data)
{
ImGuiIO& io = ImGui::GetIO();

// Build texture atlas
unsigned char* pixels;
int width, height, bytes_per_pixel;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
io.Fonts->MarkClean();
LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)texture_data.GetTexID();
unsigned char* pixels = (unsigned char*)texture_data.TexPixels;
int width = texture_data.TexWidth;
int height = texture_data.TexHeight;
int bytes_per_pixel = 4;

// Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
#ifndef IMGUI_USE_BGRA_PACKED_COLOR
if (io.Fonts->TexData.TexFormat == ImTextureFormat_RGBA32)
if (texture_data.TexFormat == ImTextureFormat_RGBA32)
{
ImU32* dst_start = (ImU32*)ImGui::MemAlloc(width * height * bytes_per_pixel);
for (ImU32* src = (ImU32*)pixels, *dst = dst_start, *dst_end = dst_start + width * height; dst < dst_end; src++, dst++)
Expand All @@ -280,33 +278,31 @@ static bool ImGui_ImplDX9_UpdateFontsTexture()
}
#endif

D3DSURFACE_DESC surface_desc = {};
if (texture)
texture->GetLevelDesc(0, &surface_desc);

// Upload texture to graphics system
if ((!g_FontTexture) || (g_FontTextureWidth != width) || (g_FontTextureHeight != height))
if ((!texture) || (surface_desc.Width != width) || (surface_desc.Height != height))
{
// (Re-)create texture
if (g_FontTexture)
g_FontTexture->Release();
io.Fonts->SetTexID(NULL);
g_FontTexture = NULL;
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
// Create texture
texture = NULL;
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture, NULL) < 0)
return false;

// Store size
g_FontTextureWidth = width;
g_FontTextureHeight = height;
surface_desc.Width = width;
surface_desc.Height = height;
}

// Store our identifier
io.Fonts->SetTexID((ImTextureID)g_FontTexture);

{
D3DLOCKED_RECT tex_locked_rect;
RECT dirty_rect;
dirty_rect.left = 0;
dirty_rect.right = width;
dirty_rect.top = 0;
dirty_rect.bottom = height;
if (g_FontTexture->LockRect(0, &tex_locked_rect, &dirty_rect, 0) != D3D_OK)
if (texture->LockRect(0, &tex_locked_rect, &dirty_rect, 0) != D3D_OK)
return false;

if (tex_locked_rect.Pitch == (width * 4))
Expand All @@ -329,24 +325,23 @@ static bool ImGui_ImplDX9_UpdateFontsTexture()
read_ptr += src_stride;
}
}
g_FontTexture->UnlockRect(0);
texture->UnlockRect(0);
}

// Upload the dirty region
#ifndef IMGUI_USE_BGRA_PACKED_COLOR
if (io.Fonts->TexData.TexFormat == ImTextureFormat_RGBA32)
if (texture_data.TexFormat == ImTextureFormat_RGBA32)
ImGui::MemFree(pixels);
#endif

return true;
return texture;
}

bool ImGui_ImplDX9_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (!ImGui_ImplDX9_UpdateFontsTexture())
return false;

return true;
}

Expand All @@ -356,16 +351,75 @@ void ImGui_ImplDX9_InvalidateDeviceObjects()
return;
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
if (g_FontTexture) { g_FontTexture->Release(); g_FontTexture = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.

for (int i = 0; i < g_FontTextures.Size; ++i)
g_FontTextures[i]->Release();
g_FontTextures.resize(0);
}

void ImGui_ImplDX9_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();

if (!g_FontTexture)
ImGui_ImplDX9_CreateDeviceObjects();
if (g_FontTextures.empty())
{
if (io.Fonts->TexData.TexPixels == NULL || io.Fonts->IsDirty())
{
if (io.Fonts->ConfigData.empty())
io.Fonts->AddFontDefault();
io.Fonts->Build();
}
}
}

static void ImGui_ImplDX9_DeleteTextures(ImVector<LPDIRECT3DTEXTURE9>& textures)
{
if (textures.empty())
return;

for (int i = 0; i < textures.Size; ++i)
textures[i]->Release();
}

static void ImGui_ImplDX9_UpdateTextures(ImVector<LPDIRECT3DTEXTURE9>& textures, const ImVector<ImTextureData*>& textures_data)
{
ImVector<LPDIRECT3DTEXTURE9> discarded = textures;

bool recreate_all = textures.empty();

for (int i = 0; i < textures_data.Size; ++i)
{
ImTextureData* texture_data = textures_data[i];

LPDIRECT3DTEXTURE9 current_texture = (LPDIRECT3DTEXTURE9)texture_data->GetTexID();
if (current_texture == NULL || recreate_all)
{
texture_data->EnsureFormat(ImTextureFormat_RGBA32);

LPDIRECT3DTEXTURE9 new_texture = ImGui_ImplDX9_UpdateTexture(*texture_data);
if (current_texture != NULL && new_texture != current_texture)
textures.find_erase_unsorted(current_texture);

if (new_texture != NULL && new_texture != current_texture)
textures.push_back(new_texture);

discarded.find_erase_unsorted(new_texture);

texture_data->SetTexID(new_texture);
}
else if (current_texture)
discarded.find_erase_unsorted(current_texture);
}

ImGui_ImplDX9_DeleteTextures(discarded);

for (int i = 0; i < discarded.Size; ++i)
textures.find_erase_unsorted(discarded[i]);
}

void ImGui_ImplDX9_UpdateTextures()
{
ImTextureUpdateData texture_update_data = ImGui::GetTextureUpdateData();

if (io.Fonts->IsDirty())
ImGui_ImplDX9_UpdateFontsTexture(); // We ignore the return value because if it fails that almost certainly means the device is invalid, and the font will get reinitialised by ImGui_ImplDX9_CreateDeviceObjects() later
ImGui_ImplDX9_UpdateTextures(g_FontTextures, texture_update_data.Textures);
}
1 change: 1 addition & 0 deletions backends/imgui_impl_dx9.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct IDirect3DDevice9;
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device);
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX9_UpdateTextures();
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data);

// Use if you want to reset your rendering device without losing Dear ImGui state.
Expand Down
3 changes: 1 addition & 2 deletions examples/example_win32_directx9/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ int main(int, char**)
if (done)
break;

ImGui::UpdateFontDemo();

// Start the Dear ImGui frame
ImGui_ImplDX9_NewFrame();
ImGui_ImplWin32_NewFrame();
Expand Down Expand Up @@ -151,6 +149,7 @@ int main(int, char**)
if (g_pd3dDevice->BeginScene() >= 0)
{
ImGui::Render();
ImGui_ImplDX9_UpdateTextures();
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
g_pd3dDevice->EndScene();
}
Expand Down
7 changes: 7 additions & 0 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3442,6 +3442,12 @@ ImDrawData* ImGui::GetDrawData()
return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
}

ImTextureUpdateData ImGui::GetTextureUpdateData()
{
ImGuiContext& g = *GImGui;
return g.IO.Fonts->GetTextureUpdateData();
}

double ImGui::GetTime()
{
return GImGui->Time;
Expand Down Expand Up @@ -3922,6 +3928,7 @@ void ImGui::NewFrame()

// Setup current font and draw list shared data
g.IO.Fonts->Locked = true;
g.IO.Fonts->ClearTransientTextures();
SetCurrentFont(GetDefaultFont());
IM_ASSERT(g.Font->IsLoaded());
ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
Expand Down
23 changes: 22 additions & 1 deletion imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ struct ImGuiTextBuffer; // Helper to hold and append into a text buf
struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]")
struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor
struct ImTexture;
struct ImTextureUpdateData;

// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
Expand Down Expand Up @@ -274,6 +275,7 @@ namespace ImGui
IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all!
IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData().
IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render.
IMGUI_API ImTextureUpdateData GetTextureUpdateData();

// Demo, Debug, Information
IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application!
Expand Down Expand Up @@ -2623,6 +2625,7 @@ struct ImTextureData
{
IMGUI_API ImTextureData();
IMGUI_API ~ImTextureData();
IMGUI_API void Reset();
IMGUI_API void AllocatePixels(int width, int height, ImTextureFormat format, bool clear = true);
IMGUI_API void DiscardPixels();
IMGUI_API void EnsureFormat(ImTextureFormat format);
Expand All @@ -2637,6 +2640,11 @@ struct ImTextureData
int TexHeight;
};

struct ImTextureUpdateData
{
ImVector<ImTextureData*> Textures;
};

struct ImFontAtlas
{
IMGUI_API ImFontAtlas();
Expand All @@ -2661,6 +2669,8 @@ struct ImFontAtlas
IMGUI_API bool IsDirty(); // Returns true if the font needs to be (re-)built. User code should call GetTexData*** and update either the whole texture or dirty region as required.
IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel
IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel
IMGUI_API void BuildTextureUpdateData(ImTextureUpdateData* texture_update_data);
IMGUI_API ImTextureUpdateData GetTextureUpdateData();
bool IsBuilt() const { return Fonts.Size > 0 && (TexData.TexPixels != NULL); }
void SetTexID(ImTextureID id) { TexData.TexID = id; }
ImTextureID GetTexID() const { return TexData.TexID; }
Expand Down Expand Up @@ -2701,6 +2711,8 @@ struct ImFontAtlas
IMGUI_API bool GetMouseCursorTexData(ImGuiMouseCursor cursor, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]);
IMGUI_API void MarkDirty(); // Mark atlas texture as dirty
IMGUI_API void MarkClean(); // Mark the whole texture as clean. Should be called after uploading texture.
IMGUI_API void PushTexPage();
IMGUI_API void ClearTransientTextures();

//-------------------------------------------
// Members
Expand All @@ -2721,6 +2733,7 @@ struct ImFontAtlas
ImVector<ImFontAtlasCustomRect> CustomRects; // Rectangles for packing custom texture data into the atlas.
ImVector<ImFontConfig> ConfigData; // Configuration data
ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines
ImVector<ImTextureData> TexPages;

// [Internal] Font builder
const ImFontBuilderIO* FontBuilderIO; // Opaque interface to a font builder (default to stb_truetype, can be changed to use FreeType by defining IMGUI_ENABLE_FREETYPE).
Expand Down Expand Up @@ -2845,7 +2858,15 @@ inline ImTexture::ImTexture(ImTextureID texture_id)

inline ImTextureID ImTexture::GetID() const
{
return Type == ImTextureType_Atlas ? FontAtlas->TexData.TexID : TextureId;
if (Type == ImTextureType_Atlas)
{
if (FontAtlasPage < FontAtlas->TexPages.Size)
return FontAtlas->TexPages[FontAtlasPage].GetTexID();
else
return FontAtlas->TexData.GetTexID();
}
else
return TextureId;
}

//-----------------------------------------------------------------------------
Expand Down
12 changes: 10 additions & 2 deletions imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7690,6 +7690,8 @@ void ShowExampleAppDocuments(bool* p_open)
ImGui::End();
}

#include "imgui_internal.h"

static const char* font_path_prefix = "../../misc/fonts/";
static const char* fonts[] =
{
Expand All @@ -7716,6 +7718,8 @@ void ImGui::UpdateFontDemo()
if (selected_font_size < 0)
selected_font_size = font_size;

io.Fonts->Locked = false; // #thedmd: remove this
io.Fonts->PushTexPage();
io.Fonts->Clear();
if (selected_font_index == 0)
{
Expand All @@ -7735,7 +7739,9 @@ void ImGui::UpdateFontDemo()
selected_font_index = -1;
selected_font_size = -1.0f;

//io.Fonts->Build();
io.Fonts->Build();
io.Fonts->Locked = true; // #thedmd: remove this
ImGui::SetCurrentFont(ImGui::GetDefaultFont());
}
}

Expand Down Expand Up @@ -7774,10 +7780,12 @@ void ImGui::ShowFontDemoWindow()
{
ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
ImGui::Image(atlas->GetTexID(), ImVec2((float)atlas->TexData.TexWidth, (float)atlas->TexData.TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col);
ImGui::Image(ImTexture(atlas), ImVec2((float)atlas->TexData.TexWidth, (float)atlas->TexData.TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col);
//ImGui::TreePop();
}

UpdateFontDemo();

ImGui::End();
}
}
Expand Down
Loading

0 comments on commit ff825b0

Please sign in to comment.