diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 28afa44e2..49bb6e63d 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -47,6 +47,14 @@ struct ImGui_ImplDX9_Data int VertexBufferSize; int IndexBufferSize; + // Direct3D 9 programmable rendering pipeline implementation. + // Nearly all graphic card released after 2007 support Shader Model 2. + // So theoretically, this feature is supported on almost all current devices. + bool IsShaderSupported; + IDirect3DVertexDeclaration9* pInputLayout; + IDirect3DVertexShader9* pVertexShader; + IDirect3DPixelShader9* pPixelShader; + ImGui_ImplDX9_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } }; @@ -71,6 +79,311 @@ static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplDX9_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } +// Shared +static D3DMATRIX ImGui_ImplDX9_BuildProjectionMatrix(ImDrawData* draw_data) +{ + // Orthographic projection matrix + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + // Being agnostic of whether or can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH() + float L = draw_data->DisplayPos.x + 0.5f; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f; + float T = draw_data->DisplayPos.y + 0.5f; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f; + const D3DMATRIX mat_projection = + { { { + 2.0f/(R-L), 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f/(T-B), 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + (L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f, + } } }; + return mat_projection; +} + +// Programmable render pipeline +static bool ImGui_ImplDX9WithShader_CreateDeviceObjects() +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + + D3DCAPS9 caps; ZeroMemory(&caps, sizeof(D3DCAPS9)); + if (D3D_OK != bd->pd3dDevice->GetDeviceCaps(&caps)) + return false; + if (caps.VertexShaderVersion < D3DVS_VERSION(2,0) || caps.PixelShaderVersion < D3DPS_VERSION(2, 0)) + return false; + + // Input layout of default ImDrawVert + static const D3DVERTEXELEMENT9 InputLayout[4] = { + { 0, 0, D3DDECLTYPE_FLOAT2 , D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + { 0, 8, D3DDECLTYPE_FLOAT2 , D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, + { 0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR , 0 }, + D3DDECL_END(), + }; + if (D3D_OK != bd->pd3dDevice->CreateVertexDeclaration(InputLayout, &bd->pInputLayout)) + return false; + + // Shaders + /* + float4x4 mvp : register(c0); + struct VS_Input + { + float2 pos : POSITION0; + float2 uv : TEXCOORD0; + float4 col : COLOR0; + }; + struct VS_Output + { + float4 pos : POSITION; + float2 uv : TEXCOORD0; + float4 col : COLOR0; + }; + VS_Output main(VS_Input input) + { + VS_Output output; + output.pos = mul(mvp, float4(input.pos.x, input.pos.y, 0.0f, 1.0f)); + output.uv = input.uv; + output.col = input.col; + return output; + }; + */ + // Precompile with Visual Studio HLSL compiler, vs_2_0 + static const BYTE VertexShaderByteCode[244] = { 0, 2, 254, 255, 254, 255, 30, 0, 67, 84, 65, 66, 28, 0, 0, 0, 75, 0, 0, 0, 0, 2, 254, 255, 1, 0, 0, 0, 28, 0, 0, 0, 0, 1, 0, 0, 68, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 4, 0, 2, 0, 52, 0, 0, 0, 0, 0, 0, 0, 109, 118, 112, 0, 3, 0, 3, 0, 4, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 118, 115, 95, 50, 95, 48, 0, 77, 105, 99, 114, 111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32, 83, 104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105, 108, 101, 114, 32, 49, 48, 46, 49, 0, 171, 31, 0, 0, 2, 0, 0, 0, 128, 0, 0, 15, 144, 31, 0, 0, 2, 5, 0, 0, 128, 1, 0, 15, 144, 31, 0, 0, 2, 10, 0, 0, 128, 2, 0, 15, 144, 5, 0, 0, 3, 0, 0, 15, 128, 0, 0, 85, 144, 1, 0, 228, 160, 4, 0, 0, 4, 0, 0, 15, 128, 0, 0, 228, 160, 0, 0, 0, 144, 0, 0, 228, 128, 2, 0, 0, 3, 0, 0, 15, 192, 0, 0, 228, 128, 3, 0, 228, 160, 1, 0, 0, 2, 0, 0, 3, 224, 1, 0, 228, 144, 1, 0, 0, 2, 0, 0, 15, 208, 2, 0, 228, 144, 255, 255, 0, 0 }; + /* + sampler tex0 : register(s0); + struct PS_Input + { + float4 pos : VPOS; + float2 uv : TEXCOORD0; + float4 col : COLOR0; + }; + struct PS_Output + { + float4 col : COLOR; + }; + PS_Output main(PS_Input input) + { + PS_Output output; + output.col = input.col * tex2D(tex0, input.uv); + return output; + }; + */ + // Precompile with Visual Studio HLSL compiler, ps_2_0 + static const BYTE PixelShaderByteCode[216] = { 0, 2, 255, 255, 254, 255, 31, 0, 67, 84, 65, 66, 28, 0, 0, 0, 79, 0, 0, 0, 0, 2, 255, 255, 1, 0, 0, 0, 28, 0, 0, 0, 0, 1, 0, 0, 72, 0, 0, 0, 48, 0, 0, 0, 3, 0, 0, 0, 1, 0, 2, 0, 56, 0, 0, 0, 0, 0, 0, 0, 116, 101, 120, 48, 0, 171, 171, 171, 4, 0, 12, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 112, 115, 95, 50, 95, 48, 0, 77, 105, 99, 114, 111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32, 83, 104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105, 108, 101, 114, 32, 49, 48, 46, 49, 0, 171, 31, 0, 0, 2, 0, 0, 0, 128, 0, 0, 3, 176, 31, 0, 0, 2, 0, 0, 0, 128, 0, 0, 15, 144, 31, 0, 0, 2, 0, 0, 0, 144, 0, 8, 15, 160, 66, 0, 0, 3, 0, 0, 15, 128, 0, 0, 228, 176, 0, 8, 228, 160, 5, 0, 0, 3, 0, 0, 15, 128, 0, 0, 228, 128, 0, 0, 228, 144, 1, 0, 0, 2, 0, 8, 15, 128, 0, 0, 228, 128, 255, 255, 0, 0 }; + if (D3D_OK != bd->pd3dDevice->CreateVertexShader((DWORD*)VertexShaderByteCode, &bd->pVertexShader)) + return false; + if (D3D_OK != bd->pd3dDevice->CreatePixelShader((DWORD*)PixelShaderByteCode, &bd->pPixelShader)) + return false; + + bd->IsShaderSupported = true; + ImGui::GetIO().BackendRendererName = "imgui_impl_dx9 (shader)"; + return true; +} + +static void ImGui_ImplDX9WithShader_InvalidateDeviceObjects() +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = nullptr; } + if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = nullptr; } + if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = nullptr; } + bd->IsShaderSupported = false; +} + +static bool ImGui_ImplDX9WithShader_SetRenderState(ImDrawData* draw_data) +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + if (!bd->IsShaderSupported) + return false; + + IDirect3DDevice9* ctx = bd->pd3dDevice; + D3DMATRIX mat_projection = ImGui_ImplDX9_BuildProjectionMatrix(draw_data); + D3DVIEWPORT9 viewport = { 0, 0, (DWORD)draw_data->DisplaySize.x, (DWORD)draw_data->DisplaySize.y, 0.0f, 1.0f }; + + // [IA Stage] + ctx->SetVertexDeclaration(bd->pInputLayout); + ctx->SetStreamSource(0, bd->pVB, 0, sizeof(ImDrawVert)); + ctx->SetStreamSourceFreq(0, 1); // no instantiated drawing + ctx->SetIndices(bd->pIB); + // [VS Stage] + ctx->SetVertexShaderConstantF(0, (float*)&mat_projection, 4); // constant buffer: float4x4 matrix + ctx->SetVertexShader(bd->pVertexShader); + // [RS Stage] + ctx->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); // rasterizer state + ctx->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + ctx->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + ctx->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, FALSE); + ctx->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, FALSE); + ctx->SetViewport(&viewport); + // [PS Stage] + ctx->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); // sampler state + ctx->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + ctx->SetSamplerState(0, D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP); + ctx->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + ctx->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + ctx->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + ctx->SetPixelShader(bd->pPixelShader); + // [OM Stage] + ctx->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); // depth stencil state + ctx->SetRenderState(D3DRS_STENCILENABLE, FALSE); + ctx->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); // blend state + ctx->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); + ctx->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + ctx->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + ctx->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + ctx->SetRenderState(D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD); + ctx->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE); + ctx->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA); + ctx->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA); + + // [Fixed Pipeline] disable these features, especially lighting + ctx->SetRenderState(D3DRS_CLIPPING, FALSE); + ctx->SetRenderState(D3DRS_CLIPPLANEENABLE, 0); + ctx->SetRenderState(D3DRS_LIGHTING, FALSE); + ctx->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + ctx->SetRenderState(D3DRS_POINTSPRITEENABLE, FALSE); + ctx->SetRenderState(D3DRS_FOGENABLE, FALSE); + ctx->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE); + ctx->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + + return true; +} + +static bool ImGui_ImplDX9WithShader_UploadBuffers(ImDrawData* draw_data) +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + + // Create or resize buffers if needed + const DWORD usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; + const D3DFORMAT format = sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32; + if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) + { + if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } + while (bd->VertexBufferSize < draw_data->TotalVtxCount) { bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; } + if (D3D_OK != bd->pd3dDevice->CreateVertexBuffer(bd->VertexBufferSize * sizeof(ImDrawVert), usage, 0, D3DPOOL_DEFAULT, &bd->pVB, nullptr)) + return false; + } + if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) + { + if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } + while (bd->IndexBufferSize < draw_data->TotalIdxCount) { bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; } + if (D3D_OK != bd->pd3dDevice->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), usage, format, D3DPOOL_DEFAULT, &bd->pIB, nullptr)) + return false; + } + + // Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format. + // FIXME-OPT: This is a minor waste of resource, the ideal is to: + // 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR + + // Copy vertex buffer + ImDrawVert* vtx_dst = nullptr; + if (D3D_OK != bd->pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(ImDrawVert)), (void**)&vtx_dst, D3DLOCK_DISCARD)) + return false; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; +#ifndef IMGUI_USE_BGRA_PACKED_COLOR + ImDrawVert* vtx_src = cmd_list->VtxBuffer.Data; + for (int i = 0; i < cmd_list->VtxBuffer.Size; i++) + { + vtx_dst->pos = vtx_src->pos; + vtx_dst->uv = vtx_src->uv; + vtx_dst->col = IMGUI_COL_TO_DX9_ARGB(vtx_src->col); + vtx_src++; + vtx_dst++; + } +#else + memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); + vtx_dst += cmd_list->VtxBuffer.Size; +#endif + } + if (D3D_OK != bd->pVB->Unlock()) + return false; + + // Copy index buffer + ImDrawIdx* idx_dst = nullptr; + if (D3D_OK != bd->pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD)) + return false; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + idx_dst += cmd_list->IdxBuffer.Size; + } + if (D3D_OK != bd->pIB->Unlock()) + return false; + + return true; +} + +static bool ImGui_ImplDX9WithShader_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized + if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) + return true; + + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + + if (!ImGui_ImplDX9WithShader_UploadBuffers(draw_data)) + return false; + + // Backup the DX9 state + IDirect3DStateBlock9* d3d9_state_block = nullptr; + if (D3D_OK != bd->pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block)) + return false; + if (D3D_OK != d3d9_state_block->Capture()) + { + d3d9_state_block->Release(); + return false; + } + D3DMATRIX last_float4x4; + bd->pd3dDevice->GetVertexShaderConstantF(0, (float*)&last_float4x4, 4); + + // Setup desired DX state + ImGui_ImplDX9WithShader_SetRenderState(draw_data); + + // Render command lists + // (Because we merged all buffers into a single one, we maintain our own offset into them) + int global_vtx_offset = 0; + int global_idx_offset = 0; + ImVec2 clip_off = draw_data->DisplayPos; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != nullptr) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplDX9WithShader_SetRenderState(draw_data); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply Scissor/clipping rectangle, Bind texture, Draw + const RECT rect = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; + bd->pd3dDevice->SetScissorRect(&rect); + bd->pd3dDevice->SetTexture(0, (IDirect3DTexture9*)pcmd->GetTexID()); + bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3); + } + } + global_idx_offset += cmd_list->IdxBuffer.Size; + global_vtx_offset += cmd_list->VtxBuffer.Size; + } + + // Restore the DX9 state + bd->pd3dDevice->SetVertexShaderConstantF(0, (float*)&last_float4x4, 4); + d3d9_state_block->Apply(); + d3d9_state_block->Release(); + return true; +} + // Functions static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) { @@ -120,25 +433,11 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); // Setup orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - // Being agnostic of whether or can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH() - { - float L = draw_data->DisplayPos.x + 0.5f; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f; - float T = draw_data->DisplayPos.y + 0.5f; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f; - D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } }; - D3DMATRIX mat_projection = - { { { - 2.0f/(R-L), 0.0f, 0.0f, 0.0f, - 0.0f, 2.0f/(T-B), 0.0f, 0.0f, - 0.0f, 0.0f, 0.5f, 0.0f, - (L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f - } } }; - bd->pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity); - bd->pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity); - bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection); - } + D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } }; + D3DMATRIX mat_projection = ImGui_ImplDX9_BuildProjectionMatrix(draw_data); + bd->pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity); + bd->pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity); + bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection); } // Render function. @@ -148,8 +447,15 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - // Create and grow buffers if needed + // If available, render via programmable render pipeline ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + if (bd->IsShaderSupported) + { + ImGui_ImplDX9WithShader_RenderDrawData(draw_data); + return; + } + + // Create and grow buffers if needed if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } @@ -357,6 +663,7 @@ bool ImGui_ImplDX9_CreateDeviceObjects() return false; if (!ImGui_ImplDX9_CreateFontsTexture()) return false; + ImGui_ImplDX9WithShader_CreateDeviceObjects(); return true; } @@ -365,6 +672,7 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); if (!bd || !bd->pd3dDevice) return; + ImGui_ImplDX9WithShader_InvalidateDeviceObjects(); if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } if (bd->FontTexture) { bd->FontTexture->Release(); bd->FontTexture = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.