From f144c67676802ad98c4db3edff6f7d2585b0b7b4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Jan 2021 14:58:52 +0100 Subject: [PATCH 01/17] ImDrawList: fixed AddCircle, AddCircleFilled buffer read overflow with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) Amend 051ce076. Maximum cached count becomes 63 instead of 64. --- docs/CHANGELOG.txt | 12 ++++++++++++ imgui.cpp | 2 +- imgui.h | 2 +- imgui_draw.cpp | 6 +++--- imgui_internal.h | 3 ++- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4c928b7b..1b66f1b9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -31,10 +31,22 @@ HOW TO UPDATE? - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.8X (In Progress) +----------------------------------------------------------------------- + +Other Changes: + +- ImDrawList: fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) + Would lead to a buffer read overflow. + + ----------------------------------------------------------------------- VERSION 1.80 (Released 2021-01-21) ----------------------------------------------------------------------- +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.80 + Breaking Changes: - Added imgui_tables.cpp file! Manually constructed project files will need the new file added! (#3740) diff --git a/imgui.cpp b/imgui.cpp index b3c42dac..cd1be7b2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1130,7 +1130,7 @@ void ImGuiIO::ClearInputCharacters() ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) { - IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau() + IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau() ImVec2 p_last = p1; ImVec2 p_closest; float p_closest_dist2 = FLT_MAX; diff --git a/imgui.h b/imgui.h index 99f03dec..3ce67a54 100644 --- a/imgui.h +++ b/imgui.h @@ -59,7 +59,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.80" -#define IMGUI_VERSION_NUM 18000 +#define IMGUI_VERSION_NUM 18001 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 8a675664..5f2a20c0 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -377,7 +377,7 @@ void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error) CircleSegmentMaxError = max_error; for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++) { - const float radius = i + 1.0f; + const float radius = (float)i; const int segment_count = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError); CircleSegmentCounts[i] = (ImU8)ImMin(segment_count, 255); } @@ -1278,7 +1278,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu if (num_segments <= 0) { // Automatic segment count - const int radius_idx = (int)radius - 1; + const int radius_idx = (int)radius; if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value else @@ -1308,7 +1308,7 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, if (num_segments <= 0) { // Automatic segment count - const int radius_idx = (int)radius - 1; + const int radius_idx = (int)radius; if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value else diff --git a/imgui_internal.h b/imgui_internal.h index dff845a6..bcfb675f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -610,6 +610,7 @@ struct IMGUI_API ImChunkStream //----------------------------------------------------------------------------- // ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value. +// FIXME: the minimum number of auto-segment may be undesirably high for very small radiuses (e.g. 1.0f) #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN 12 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX 512 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR) ImClamp((int)((IM_PI * 2.0f) / ImAcos(((_RAD) - (_MAXERROR)) / (_RAD))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) @@ -633,7 +634,7 @@ struct IMGUI_API ImDrawListSharedData // [Internal] Lookup tables ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas - ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead) + ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead) const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas ImDrawListSharedData(); From 695a4bd1a96982a3b6e5ae370b766b329101b124 Mon Sep 17 00:00:00 2001 From: Belinsky-L-V Date: Fri, 22 Jan 2021 17:04:54 +0100 Subject: [PATCH 02/17] Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) --- backends/imgui_impl_metal.mm | 3 ++- docs/CHANGELOG.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 8a6f7e7d..d368daad 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-25: Metal: Fixed texture storage mode when building on Mac Catalyst. // 2019-05-29: Metal: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2019-04-30: Metal: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. // 2019-02-11: Metal: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. @@ -238,7 +239,7 @@ void ImGui_ImplMetal_DestroyDeviceObjects() height:(NSUInteger)height mipmapped:NO]; textureDescriptor.usage = MTLTextureUsageShaderRead; -#if TARGET_OS_OSX +#if TARGET_OS_OSX || TARGET_OS_MACCATALYST textureDescriptor.storageMode = MTLStorageModeManaged; #else textureDescriptor.storageMode = MTLStorageModeShared; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1b66f1b9..b03fe7c3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,8 +37,9 @@ HOW TO UPDATE? Other Changes: -- ImDrawList: fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) +- ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) Would lead to a buffer read overflow. +- Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) [@Belinsky-L-V] ----------------------------------------------------------------------- From 2ed47e582241fbe5309fb20b72927b20bd74b653 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Jan 2021 16:30:37 +0100 Subject: [PATCH 03/17] Version 1.81 WIP --- docs/CHANGELOG.txt | 2 +- imgui.cpp | 2 +- imgui.h | 4 ++-- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b03fe7c3..0bc4f694 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -32,7 +32,7 @@ HOW TO UPDATE? ----------------------------------------------------------------------- - VERSION 1.8X (In Progress) + VERSION 1.81 WIP (In Progress) ----------------------------------------------------------------------- Other Changes: diff --git a/imgui.cpp b/imgui.cpp index cd1be7b2..78fc2fcf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.81 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 3ce67a54..e87caaff 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.81 WIP // (headers) // Help: @@ -58,7 +58,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.80" +#define IMGUI_VERSION "1.81 WIP" #define IMGUI_VERSION_NUM 18001 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a19d4a00..83ee4efd 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.81 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 5f2a20c0..d7b0b073 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.81 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index bcfb675f..7e48c1c6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.81 WIP // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 19fa625d..ac38185f 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.81 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 931e8ec1..b765701f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.81 WIP // (widgets code) /* From 633d1033afddf7617d70e517faf39b99d79471ad Mon Sep 17 00:00:00 2001 From: Kuanlan Date: Sat, 23 Jan 2021 13:23:59 +0800 Subject: [PATCH 04/17] Backends: Win32: dynamically load XInput library (#3646, #3645, #3248, #2716) --- backends/imgui_impl_win32.cpp | 83 +++++++++++++++++++++++++++-------- backends/imgui_impl_win32.h | 3 +- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index f73d11e8..f0bb7295 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -19,15 +19,11 @@ #include #include -// Using XInput library for gamepad (with recent Windows SDK this may leads to executables which won't run on Windows 7) +// Using XInput library for gamepad #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #include -#else -#define IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT -#endif -#if defined(_MSC_VER) && !defined(IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT) -#pragma comment(lib, "xinput") -//#pragma comment(lib, "Xinput9_1_0") +typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); +typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); #endif // CHANGELOG @@ -65,6 +61,14 @@ static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; static bool g_HasGamepad = false; static bool g_WantUpdateHasGamepad = true; +// XInput Data +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD +static XINPUT_STATE g_XInputState; +static HMODULE g_hXInputDLL = NULL; +static PFN_XInputGetCapabilities g_fXInputGetCapabilities = NULL; +static PFN_XInputGetState g_fXInputGetState = NULL; +#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + // Functions bool ImGui_ImplWin32_Init(void* hwnd) { @@ -104,13 +108,51 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.KeyMap[ImGuiKey_X] = 'X'; io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Z] = 'Z'; - + + // Gamepad, dynamically load XInput library +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + const wchar_t* _XinputLibraryName[] = { + L"xinput1_4.dll", // Windows 8+ + L"xinput1_3.dll", // DirectX SDK + L"xinput9_1_0.dll", // Windows Vista, Windows 7 + L"xinput1_2.dll", // DirectX SDK + L"xinput1_1.dll", // DirectX SDK + NULL, + }; + for (size_t idx = 0; _XinputLibraryName[idx]; idx += 1) + { + if (HMODULE dll = ::LoadLibraryW(_XinputLibraryName[idx])) + { + g_hXInputDLL = dll; + g_fXInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); + g_fXInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); + break; + } + } + ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); +#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + return true; } void ImGui_ImplWin32_Shutdown() { - g_hWnd = (HWND)0; + // Gamepad, unload XInput library +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + if (g_hXInputDLL) + ::FreeLibrary(g_hXInputDLL); + ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); + g_hXInputDLL = NULL; + g_fXInputGetCapabilities = NULL; + g_fXInputGetState = NULL; +#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + + g_hWnd = NULL; + g_Time = 0; + g_TicksPerSecond = 0; + g_LastMouseCursor = ImGuiMouseCursor_COUNT; + g_HasGamepad = false; + g_WantUpdateHasGamepad = true; } static bool ImGui_ImplWin32_UpdateMouseCursor() @@ -175,25 +217,30 @@ static void ImGui_ImplWin32_UpdateGamepads() memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; - + // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. if (g_WantUpdateHasGamepad) { - XINPUT_CAPABILITIES caps; - g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); g_WantUpdateHasGamepad = false; + XINPUT_CAPABILITIES caps; + if (g_fXInputGetCapabilities) + g_HasGamepad = (g_fXInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); + else + g_HasGamepad = false; + if (!g_HasGamepad) + ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); // clear if no gamepad } - - XINPUT_STATE xinput_state; + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; - if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS) + if (g_HasGamepad && g_fXInputGetState && (g_fXInputGetState(0, &g_XInputState) == ERROR_SUCCESS)) { - const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; + const XINPUT_GAMEPAD& gamepad = g_XInputState.Gamepad; io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } - #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } + #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0);\ + if (vn > 1.0f) vn = 1.0f;\ + if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 4919f54a..0721d3e9 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -19,9 +19,8 @@ IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); // Configuration -// - Disable gamepad support or linking with xinput.lib +// - Disable gamepad support //#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -//#define IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT // Win32 message handler your application need to call. // - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. From f13984675099cf66485b6b5099f8a2028a542496 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Jan 2021 16:19:43 +0100 Subject: [PATCH 05/17] Backends: Win32: dynamically load XInput library (amends) (#3646, #3645, #3248, #2716) --- backends/imgui_impl_win32.cpp | 81 ++++++++++++++++------------------- docs/CHANGELOG.txt | 2 + 2 files changed, 38 insertions(+), 45 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index f0bb7295..848dfab9 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -19,7 +19,7 @@ #include #include -// Using XInput library for gamepad +// Using XInput for gamepad (will load DLL dynamically) #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #include typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); @@ -28,6 +28,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-25: Inputs: Dynamically loading XInput DLL. // 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed. // 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs) // 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions. @@ -61,13 +62,12 @@ static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; static bool g_HasGamepad = false; static bool g_WantUpdateHasGamepad = true; -// XInput Data +// XInput DLL and functions #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -static XINPUT_STATE g_XInputState; -static HMODULE g_hXInputDLL = NULL; -static PFN_XInputGetCapabilities g_fXInputGetCapabilities = NULL; -static PFN_XInputGetState g_fXInputGetState = NULL; -#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD +static HMODULE g_XInputDLL = NULL; +static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL; +static PFN_XInputGetState g_XInputGetState = NULL; +#endif // Functions bool ImGui_ImplWin32_Init(void* hwnd) @@ -108,28 +108,25 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.KeyMap[ImGuiKey_X] = 'X'; io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Z] = 'Z'; - - // Gamepad, dynamically load XInput library + + // Dynamically load XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - const wchar_t* _XinputLibraryName[] = { - L"xinput1_4.dll", // Windows 8+ - L"xinput1_3.dll", // DirectX SDK - L"xinput9_1_0.dll", // Windows Vista, Windows 7 - L"xinput1_2.dll", // DirectX SDK - L"xinput1_1.dll", // DirectX SDK - NULL, - }; - for (size_t idx = 0; _XinputLibraryName[idx]; idx += 1) + const char* xinput_dll_names[] = { - if (HMODULE dll = ::LoadLibraryW(_XinputLibraryName[idx])) + "xinput1_4.dll", // Windows 8+ + "xinput1_3.dll", // DirectX SDK + "xinput9_1_0.dll", // Windows Vista, Windows 7 + "xinput1_2.dll", // DirectX SDK + "xinput1_1.dll" // DirectX SDK + }; + for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++) + if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) { - g_hXInputDLL = dll; - g_fXInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); - g_fXInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); + g_XInputDLL = dll; + g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); + g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); break; } - } - ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD return true; @@ -137,14 +134,13 @@ bool ImGui_ImplWin32_Init(void* hwnd) void ImGui_ImplWin32_Shutdown() { - // Gamepad, unload XInput library + // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - if (g_hXInputDLL) - ::FreeLibrary(g_hXInputDLL); - ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); - g_hXInputDLL = NULL; - g_fXInputGetCapabilities = NULL; - g_fXInputGetState = NULL; + if (g_XInputDLL) + ::FreeLibrary(g_XInputDLL); + g_XInputDLL = NULL; + g_XInputGetCapabilities = NULL; + g_XInputGetState = NULL; #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD g_hWnd = NULL; @@ -217,30 +213,25 @@ static void ImGui_ImplWin32_UpdateGamepads() memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; - + // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. if (g_WantUpdateHasGamepad) { - g_WantUpdateHasGamepad = false; XINPUT_CAPABILITIES caps; - if (g_fXInputGetCapabilities) - g_HasGamepad = (g_fXInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); - else - g_HasGamepad = false; - if (!g_HasGamepad) - ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); // clear if no gamepad + g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; + g_WantUpdateHasGamepad = false; } - + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; - if (g_HasGamepad && g_fXInputGetState && (g_fXInputGetState(0, &g_XInputState) == ERROR_SUCCESS)) + XINPUT_STATE xinput_state; + if (g_HasGamepad && g_XInputGetState && g_XInputGetState(0, &xinput_state) == ERROR_SUCCESS) { - const XINPUT_GAMEPAD& gamepad = g_XInputState.Gamepad; + const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } - #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0);\ - if (vn > 1.0f) vn = 1.0f;\ - if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } + #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0bc4f694..f2d3463e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,8 @@ Other Changes: - ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) Would lead to a buffer read overflow. +- Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with + old WindowSDK versions or running on Windows 7. (#3646, #3645, #3248, #2716) [@Demonese] - Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) [@Belinsky-L-V] From f3f2578e8f9d89742635f286728ec6998b8e3559 Mon Sep 17 00:00:00 2001 From: Sammy Fatnassi Date: Tue, 26 Jan 2021 15:14:56 +0100 Subject: [PATCH 06/17] Internals: Added context hook removal support (#3580, #3626, #3753) --- imgui.cpp | 22 ++++++++++++++++++++-- imgui_internal.h | 7 +++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 78fc2fcf..8e64e341 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3346,11 +3346,23 @@ void ImGui::DestroyContext(ImGuiContext* ctx) } // No specific ordering/dependency support, will see as needed -void ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) +ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) { ImGuiContext& g = *ctx; - IM_ASSERT(hook->Callback != NULL); + IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); g.Hooks.push_back(*hook); + g.Hooks.back().HookId = ++g.HookIdNext; + return g.HookIdNext; +} + +// Deferred removal, avoiding issue with changing vector while iterating it +void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) +{ + ImGuiContext& g = *ctx; + IM_ASSERT(hook_id != 0); + for (int n = 0; n < g.Hooks.Size; n++) + if (g.Hooks[n].HookId == hook_id) + g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; } // Call context hooks (used by e.g. test engine) @@ -3780,6 +3792,12 @@ void ImGui::NewFrame() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); ImGuiContext& g = *GImGui; + + // Remove pending delete hooks before frame start. + // This deferred removal avoid issues of removal while iterating the hook vector + for (int n = g.Hooks.Size - 1; n >= 0; n--) + if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_) + g.Hooks.erase(&g.Hooks[n]); CallContextHooks(&g, ImGuiContextHookType_NewFramePre); diff --git a/imgui_internal.h b/imgui_internal.h index 7e48c1c6..15c10f5e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1223,10 +1223,11 @@ struct IMGUI_API ImGuiStackSizes //----------------------------------------------------------------------------- typedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook); -enum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown }; +enum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ }; struct ImGuiContextHook { + ImGuiID HookId; // A unique ID assigned by AddContextHook() ImGuiContextHookType Type; ImGuiID Owner; ImGuiContextHookCallback Callback; @@ -1454,6 +1455,7 @@ struct ImGuiContext ImChunkStream SettingsWindows; // ImGuiWindow .ini settings entries ImChunkStream SettingsTables; // ImGuiTable .ini settings entries ImVector Hooks; // Hooks for extensions (e.g. test engine) + ImGuiID HookIdNext; // Next available HookId // Capture/Logging bool LogEnabled; // Currently capturing @@ -2185,7 +2187,8 @@ namespace ImGui IMGUI_API void UpdateMouseMovingWindowEndFrame(); // Generic context hooks - IMGUI_API void AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook); + IMGUI_API ImGuiID AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook); + IMGUI_API void RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove); IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); // Settings From a8d2d8ee976b8d73038559e2a004b236df8f3a83 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 26 Jan 2021 15:26:41 +0100 Subject: [PATCH 07/17] Fixed uninitialized variable, amend f3f2578. (#3753) --- imgui_internal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui_internal.h b/imgui_internal.h index 15c10f5e..e0f821bd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1609,6 +1609,7 @@ struct ImGuiContext SettingsLoaded = false; SettingsDirtyTimer = 0.0f; + HookIdNext = 0; LogEnabled = false; LogType = ImGuiLogType_None; From 6b32d0ebc79ccb6b748bf7ebfb8f4ca794c1e20a Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 26 Jan 2021 16:07:41 +0100 Subject: [PATCH 08/17] Fixed minor title bar text clipping issue when FramePadding is small/zero and there are no close button in the window. (#3731) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f2d3463e..0e468e60 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,8 @@ HOW TO UPDATE? Other Changes: +- Window: Fixed minor title bar text clipping issue when FramePadding is small/zero and there are no + close button in the window. (#3731) - ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) Would lead to a buffer read overflow. - Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with diff --git a/imgui.cpp b/imgui.cpp index 8e64e341..7e192fe7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5511,7 +5511,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, - // while uncentered title text will still reach edges correct. + // while uncentered title text will still reach edges correctly. if (pad_l > style.FramePadding.x) pad_l += g.Style.ItemInnerSpacing.x; if (pad_r > style.FramePadding.x) @@ -5525,8 +5525,9 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl } ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); - ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y); - //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] + ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y); + //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] + //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); if (flags & ImGuiWindowFlags_UnsavedDocument) { @@ -6118,7 +6119,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) - RenderWindowTitleBarContents(window, title_bar_rect, name, p_open); + RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open); // Clear hit test shape every frame window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; From 9417acc20f485d6157da9c59d485bc0d8ce9f7d0 Mon Sep 17 00:00:00 2001 From: Louis Schnellbach Date: Tue, 12 Jan 2021 09:24:51 +0100 Subject: [PATCH 09/17] Fonts: if IMGUI_ENABLE_FREETYPE, use library by default for font rasterization Also renamed IMGUI_DISABLE_STB_TRUETYPE to IMGUI_ENABLE_STB_TRUETYPE --- docs/CHANGELOG.txt | 3 +++ imconfig.h | 10 +++++++-- imgui.h | 1 + imgui_draw.cpp | 53 +++++++++++++++++++++++++--------------------- imgui_internal.h | 5 +++++ 5 files changed, 46 insertions(+), 26 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0e468e60..8197fbfa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -142,6 +142,9 @@ Other Changes: - Style: Changed default style.WindowRounding value to 0.0f (matches default for multi-viewports). - Style: Reduced the size of the resizing grip, made alpha less prominent. - Style: Classic: Increase the default alpha value of WindowBg to be closer to other styles. +- Fonts: Ease Freetype integration with IMGUI_ENABLE_FREETYPE: When enabled, it Will use the library to rasterize font by default. + Also renamed IMGUI_DISABLE_STB_TRUETYPE to IMGUI_ENABLE_STB_TRUETYPE, and making it the default rasterizer + if IMGUI_ENABLE_FREETYPE is not defined. [@Xipiryon] - Demo: Clarify usage of right-aligned items in Demo>Layout>Widgets Width. - Backends: OpenGL3: Use glGetString(GL_VERSION) query instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x). (#3530) [@xndcn] diff --git a/imconfig.h b/imconfig.h index e17c5a90..6a71e01e 100644 --- a/imconfig.h +++ b/imconfig.h @@ -56,13 +56,19 @@ // By default the embedded implementations are declared static and not available outside of imgui cpp files. //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" -//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION -//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION //---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) // Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. // #define IMGUI_USE_STB_SPRINTF +//---- Use Freetype to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) +// Requires Freetype headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the Freetype library (not provided) +//#define IMGUI_ENABLE_FREETYPE + +//---- Use stb_truetype to build and rasterize the font atlas (this is enabled by default) +// The only purpose of this this define is if you want force compilation of the stb_truetype backend ALONG with the Freetype backend. +//#define IMGUI_ENABLE_STB_TRUETYPE + //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. // This will be inlined as part of ImVec2 and ImVec4 class declarations. /* diff --git a/imgui.h b/imgui.h index e87caaff..920b676b 100644 --- a/imgui.h +++ b/imgui.h @@ -2634,6 +2634,7 @@ struct ImFontAtlas ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0. + const char* Builder; // Select font builder/rasterizer. Default to "stb_truetype". Set to "freetype" to enable Freetype (default if IMGUI_ENABLE_FREETYPE is defined). // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index d7b0b073..972950fb 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -32,7 +32,11 @@ Index of this file: #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif + #include "imgui_internal.h" +#ifdef IMGUI_ENABLE_FREETYPE +#include "misc/freetype/imgui_freetype.h" +#endif #include // vsnprintf, sscanf, printf #if !defined(alloca) @@ -131,6 +135,7 @@ namespace IMGUI_STB_NAMESPACE #endif #endif +#ifdef IMGUI_ENABLE_STB_TRUETYPE #ifndef STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) #ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION #define STBTT_malloc(x,u) ((void)(u), IM_ALLOC(x)) @@ -153,6 +158,7 @@ namespace IMGUI_STB_NAMESPACE #include "imstb_truetype.h" #endif #endif +#endif #if defined(__GNUC__) #pragma GCC diagnostic pop @@ -1710,25 +1716,13 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve ImFontConfig::ImFontConfig() { - FontData = NULL; - FontDataSize = 0; + memset(this, 0, sizeof(*this)); FontDataOwnedByAtlas = true; - FontNo = 0; - SizePixels = 0.0f; OversampleH = 3; // FIXME: 2 may be a better default? OversampleV = 1; - PixelSnapH = false; - GlyphExtraSpacing = ImVec2(0.0f, 0.0f); - GlyphOffset = ImVec2(0.0f, 0.0f); - GlyphRanges = NULL; - GlyphMinAdvanceX = 0.0f; GlyphMaxAdvanceX = FLT_MAX; - MergeMode = false; - RasterizerFlags = 0x00; RasterizerMultiply = 1.0f; EllipsisChar = (ImWchar)-1; - memset(Name, 0, sizeof(Name)); - DstFont = NULL; } //----------------------------------------------------------------------------- @@ -1785,18 +1779,17 @@ static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3 ImFontAtlas::ImFontAtlas() { - Locked = false; - Flags = ImFontAtlasFlags_None; - TexID = (ImTextureID)NULL; - TexDesiredWidth = 0; + memset(this, 0, sizeof(*this)); TexGlyphPadding = 1; - - TexPixelsAlpha8 = NULL; - TexPixelsRGBA32 = NULL; - TexWidth = TexHeight = 0; - TexUvScale = ImVec2(0.0f, 0.0f); - TexUvWhitePixel = ImVec2(0.0f, 0.0f); PackIdMouseCursors = PackIdLines = -1; + +#ifdef IMGUI_ENABLE_FREETYPE + Builder = "freetype"; +#else +# ifdef IMGUI_ENABLE_STB_TRUETYPE + Builder = "stb_truetype"; +# endif +#endif } ImFontAtlas::~ImFontAtlas() @@ -2081,7 +2074,17 @@ bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* ou bool ImFontAtlas::Build() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - return ImFontAtlasBuildWithStbTruetype(this); +#ifdef IMGUI_ENABLE_FREETYPE + if (strcmp(Builder, "freetype") == 0) + return ImGuiFreeType::BuildFontAtlas(this, 0); +#endif + // Not doing "#else" here, since we could have both + // IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE defined. +#ifdef IMGUI_ENABLE_STB_TRUETYPE + if (strcmp(Builder, "stb_truetype") == 0) + return ImFontAtlasBuildWithStbTruetype(this); +#endif + return false; } void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor) @@ -2101,6 +2104,7 @@ void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsig data[i] = table[data[i]]; } +#ifdef IMGUI_ENABLE_STB_TRUETYPE // Temporary data for one source font (multiple source fonts can be merged into one destination ImFont) // (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.) struct ImFontBuildSrcData @@ -2390,6 +2394,7 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) ImFontAtlasBuildFinish(atlas); return true; } +#endif void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) { diff --git a/imgui_internal.h b/imgui_internal.h index e0f821bd..f87e8289 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -83,6 +83,11 @@ Index of this file: #error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #endif +// Enable stb_truetype by default unless Freetype is enable. User can compile both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE +#ifndef IMGUI_ENABLE_FREETYPE +#define IMGUI_ENABLE_STB_TRUETYPE +#endif + //----------------------------------------------------------------------------- // [SECTION] Forward declarations //----------------------------------------------------------------------------- From 3867c6c5f0eace22ce2c59ac299bae285bb84ec4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Jan 2021 17:57:12 +0100 Subject: [PATCH 10/17] Fonts: (Breaking) Rename ImGuiFreeType:: flags to ImGuiFreeTypeBuilderFlags_XXX. Remove ImGuiFreeType::BuildFontAtlas() flags. Rename ImFontConfig::RasterizerFlags to FontBuilderFlags. Add ImFontBuilderIO (opaque). Amend 53d59f3 with a dozen of small fixes. --- docs/CHANGELOG.txt | 14 +++- imconfig.h | 13 ++-- imgui.cpp | 3 + imgui.h | 8 +- imgui_draw.cpp | 53 +++++++------ imgui_internal.h | 37 ++++++--- misc/freetype/README.md | 119 ++--------------------------- misc/freetype/imgui_freetype.cpp | 124 ++++++++++++++++++------------- misc/freetype/imgui_freetype.h | 62 +++++++++------- 9 files changed, 196 insertions(+), 237 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8197fbfa..195cb25b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,10 +35,21 @@ HOW TO UPDATE? VERSION 1.81 WIP (In Progress) ----------------------------------------------------------------------- +Breaking Changes: + +- imgui_freetype: Removed ImGuiFreeType::BuildFontAtlas() extra flags, now stored in ImFontAtlas::FontBuilderFlags. +- imgui_freetype: Renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags. +- imgui_freetyoe: Renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. + Other Changes: - Window: Fixed minor title bar text clipping issue when FramePadding is small/zero and there are no close button in the window. (#3731) +- imgui_freetype: Facilitated using FreeType integration: [@Xipiryon, @ocornut] + - Use '#define IMGUI_ENABLE_FREETYPE' in imconfig.h should make it work with no other modifications + other than compiling misc/freetype/imgui_freetype.cpp and linking with FreeType. + - Use '#define IMGUI_ENABLE_STB_TRUETYPE' if you somehow need the stb_truetype rasterizer to be + compiled in along with the FreeType one, otherwise it is enabled by default. - ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) Would lead to a buffer read overflow. - Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with @@ -142,9 +153,6 @@ Other Changes: - Style: Changed default style.WindowRounding value to 0.0f (matches default for multi-viewports). - Style: Reduced the size of the resizing grip, made alpha less prominent. - Style: Classic: Increase the default alpha value of WindowBg to be closer to other styles. -- Fonts: Ease Freetype integration with IMGUI_ENABLE_FREETYPE: When enabled, it Will use the library to rasterize font by default. - Also renamed IMGUI_DISABLE_STB_TRUETYPE to IMGUI_ENABLE_STB_TRUETYPE, and making it the default rasterizer - if IMGUI_ENABLE_FREETYPE is not defined. [@Xipiryon] - Demo: Clarify usage of right-aligned items in Demo>Layout>Widgets Width. - Backends: OpenGL3: Use glGetString(GL_VERSION) query instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x). (#3530) [@xndcn] diff --git a/imconfig.h b/imconfig.h index 6a71e01e..c5e1650a 100644 --- a/imconfig.h +++ b/imconfig.h @@ -53,20 +53,23 @@ //#define IMGUI_USE_WCHAR32 //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version -// By default the embedded implementations are declared static and not available outside of imgui cpp files. +// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" +//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION //---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) // Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. // #define IMGUI_USE_STB_SPRINTF -//---- Use Freetype to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) -// Requires Freetype headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the Freetype library (not provided) +//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) +// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). +// On Windows you may use vcpkg with 'vcpkg install freetype' + 'vcpkg integrate install'. //#define IMGUI_ENABLE_FREETYPE -//---- Use stb_truetype to build and rasterize the font atlas (this is enabled by default) -// The only purpose of this this define is if you want force compilation of the stb_truetype backend ALONG with the Freetype backend. +//---- Use stb_truetype to build and rasterize the font atlas (default) +// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. //#define IMGUI_ENABLE_STB_TRUETYPE //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. diff --git a/imgui.cpp b/imgui.cpp index 7e192fe7..b48cff73 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -373,6 +373,9 @@ CODE When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2021/01/26 (1.81) - imgui_freetype: removed ImGuiFreeType::BuildFontAtlas() extra flags, now stored in ImFontAtlas::FontBuilderFlags. + - imgui_freetype: renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags. + - imgui_freetype: renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018): - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit(). - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg diff --git a/imgui.h b/imgui.h index 920b676b..8f210de1 100644 --- a/imgui.h +++ b/imgui.h @@ -125,6 +125,7 @@ struct ImDrawListSplitter; // Helper to split a draw list into differen struct ImDrawVert; // A single vertex (pos + uv + col = 20 bytes by default. Override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) struct ImFont; // Runtime data for a single font within a parent ImFontAtlas struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader +struct ImFontBuilderIO; // Opaque interface to a font builder (stb_truetype or FreeType). struct ImFontConfig; // Configuration data when adding a font or merging fonts struct ImFontGlyph; // A single font glyph (code point + coordinates within in ImFontAtlas + offset) struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data @@ -2488,7 +2489,7 @@ struct ImFontConfig float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. - unsigned int RasterizerFlags; // 0x00 // Settings for custom font rasterizer (e.g. ImGuiFreeType). Leave as zero if you aren't using one. + unsigned int FontBuilderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. float RasterizerMultiply; // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. ImWchar EllipsisChar; // -1 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. @@ -2634,7 +2635,6 @@ struct ImFontAtlas ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0. - const char* Builder; // Select font builder/rasterizer. Default to "stb_truetype". Set to "freetype" to enable Freetype (default if IMGUI_ENABLE_FREETYPE is defined). // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. @@ -2649,6 +2649,10 @@ struct ImFontAtlas ImVector ConfigData; // Configuration data ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines + // [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). + unsigned int FontBuilderFlags; // Shared flags (for all fonts) for custom font builder. THIS IS BUILD IMPLEMENTATION DEPENDENT. Per-font override is also available in ImFontConfig. + // [Internal] Packing data int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 972950fb..06cf06d9 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -122,7 +122,7 @@ namespace IMGUI_STB_NAMESPACE #endif #ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) -#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in another compilation unit #define STBRP_STATIC #define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0) #define STBRP_SORT ImQsort @@ -137,7 +137,7 @@ namespace IMGUI_STB_NAMESPACE #ifdef IMGUI_ENABLE_STB_TRUETYPE #ifndef STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) -#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in another compilation unit #define STBTT_malloc(x,u) ((void)(u), IM_ALLOC(x)) #define STBTT_free(x,u) ((void)(u), IM_FREE(x)) #define STBTT_assert(x) do { IM_ASSERT(x); } while(0) @@ -158,7 +158,7 @@ namespace IMGUI_STB_NAMESPACE #include "imstb_truetype.h" #endif #endif -#endif +#endif // IMGUI_ENABLE_STB_TRUETYPE #if defined(__GNUC__) #pragma GCC diagnostic pop @@ -1782,14 +1782,6 @@ ImFontAtlas::ImFontAtlas() memset(this, 0, sizeof(*this)); TexGlyphPadding = 1; PackIdMouseCursors = PackIdLines = -1; - -#ifdef IMGUI_ENABLE_FREETYPE - Builder = "freetype"; -#else -# ifdef IMGUI_ENABLE_STB_TRUETYPE - Builder = "stb_truetype"; -# endif -#endif } ImFontAtlas::~ImFontAtlas() @@ -2074,17 +2066,26 @@ bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* ou bool ImFontAtlas::Build() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); + + // Select builder + // - Note that we do not reassign to atlas->FontBuilderIO, since it is likely to point to static data which + // may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are + // using a hot-reloading scheme that messes up static data, store your own instance of ImFontBuilderIO somewhere + // and point to it instead of pointing directly to return value of the GetBuilderXXX functions. + const ImFontBuilderIO* builder_io = FontBuilderIO; + if (builder_io == NULL) + { #ifdef IMGUI_ENABLE_FREETYPE - if (strcmp(Builder, "freetype") == 0) - return ImGuiFreeType::BuildFontAtlas(this, 0); -#endif - // Not doing "#else" here, since we could have both - // IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE defined. -#ifdef IMGUI_ENABLE_STB_TRUETYPE - if (strcmp(Builder, "stb_truetype") == 0) - return ImFontAtlasBuildWithStbTruetype(this); + builder_io = ImGuiFreeType::GetBuilderForFreeType(); +#elif defined(IMGUI_ENABLE_STB_TRUETYPE) + builder_io = ImFontAtlasGetBuilderForStbTruetype(); +#else + IM_ASSERT(0); // Invalid Build function #endif - return false; + } + + // Build + return builder_io->FontBuilder_Build(this); } void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor) @@ -2142,7 +2143,7 @@ static void UnpackBitVectorToFlatIndexList(const ImBitVector* in, ImVector* out->push_back((int)(((it - it_begin) << 5) + bit_n)); } -bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) +static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) { IM_ASSERT(atlas->ConfigData.Size > 0); @@ -2394,7 +2395,15 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) ImFontAtlasBuildFinish(atlas); return true; } -#endif + +const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype() +{ + static ImFontBuilderIO io; + io.FontBuilder_Build = ImFontAtlasBuildWithStbTruetype; + return &io; +} + +#endif // IMGUI_ENABLE_STB_TRUETYPE void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) { diff --git a/imgui_internal.h b/imgui_internal.h index f87e8289..ed07501b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -29,7 +29,8 @@ Index of this file: // [SECTION] ImGuiWindowTempData, ImGuiWindow // [SECTION] Tab bar, Tab item support // [SECTION] Table support -// [SECTION] Internal API +// [SECTION] ImGui internal API +// [SECTION] ImFontAtlas internal API // [SECTION] Test Engine specific hooks (imgui_test_engine) */ @@ -83,7 +84,8 @@ Index of this file: #error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #endif -// Enable stb_truetype by default unless Freetype is enable. User can compile both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE +// Enable stb_truetype by default unless FreeType is enabled. +// You can compile with both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE together. #ifndef IMGUI_ENABLE_FREETYPE #define IMGUI_ENABLE_STB_TRUETYPE #endif @@ -2144,7 +2146,7 @@ struct ImGuiTableSettings #endif // #ifdef IMGUI_HAS_TABLE //----------------------------------------------------------------------------- -// [SECTION] Internal API +// [SECTION] ImGui internal API // No guarantee of forward compatibility here! //----------------------------------------------------------------------------- @@ -2490,15 +2492,26 @@ namespace ImGui } // namespace ImGui -// ImFontAtlas internals -IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); -IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); -IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildRender1bppRectFromString(ImFontAtlas* atlas, int atlas_x, int atlas_y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value); -IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); -IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); + +//----------------------------------------------------------------------------- +// [SECTION] ImFontAtlas internal API +//----------------------------------------------------------------------------- + +// This structure is likely to evolve as we add support for incremental atlas updates +struct ImFontBuilderIO +{ + bool (*FontBuilder_Build)(ImFontAtlas* atlas); +}; + +// Helper for font builder +IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype(); +IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); +IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); +IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildRender1bppRectFromString(ImFontAtlas* atlas, int atlas_x, int atlas_y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value); +IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); +IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); //----------------------------------------------------------------------------- // [SECTION] Test Engine specific hooks (imgui_test_engine) diff --git a/misc/freetype/README.md b/misc/freetype/README.md index e565097a..440fe859 100644 --- a/misc/freetype/README.md +++ b/misc/freetype/README.md @@ -1,131 +1,24 @@ # imgui_freetype -Build font atlases using FreeType instead of stb_truetype (which is the default font rasterizer in Dear ImGui). +Build font atlases using FreeType instead of stb_truetype (which is the default font rasterizer).
by @vuhdo, @mikesart, @ocornut. ### Usage 1. Get latest FreeType binaries or build yourself (under Windows you may use vcpkg with `vcpkg install freetype`, `vcpkg integrate install`). -2. Add imgui_freetype.h/cpp alongside your imgui sources. -3. Include imgui_freetype.h after imgui.h. -4. Call `ImGuiFreeType::BuildFontAtlas()` *BEFORE* calling `ImFontAtlas::GetTexDataAsRGBA32()` or `ImFontAtlas::Build()` (so normal Build() won't be called): +2. Add imgui_freetype.h/cpp alongside your project files. +3. Add `#define IMGUI_ENABLE_FREETYPE` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file -```cpp -// See ImGuiFreeType::RasterizationFlags -unsigned int flags = ImGuiFreeType::NoHinting; -ImGuiFreeType::BuildFontAtlas(io.Fonts, flags); -io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); -``` - -### Gamma Correct Blending +### About Gamma Correct Blending FreeType assumes blending in linear space rather than gamma space. See FreeType note for [FT_Render_Glyph](https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph). For correct results you need to be using sRGB and convert to linear space in the pixel shader output. The default Dear ImGui styles will be impacted by this change (alpha values will need tweaking). -### Test code Usage -```cpp -#include "misc/freetype/imgui_freetype.h" -#include "misc/freetype/imgui_freetype.cpp" - -// Load various small fonts -ImGuiIO& io = ImGui::GetIO(); -io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 13.0f); -io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 13.0f); -io.Fonts->AddFontDefault(); - -FreeTypeTest freetype_test; - -// Main Loop -while (true) -{ - if (freetype_test.UpdateRebuild()) - { - // REUPLOAD FONT TEXTURE TO GPU - ImGui_ImplXXX_DestroyDeviceObjects(); - ImGui_ImplXXX_CreateDeviceObjects(); - } - ImGui::NewFrame(); - freetype_test.ShowFreetypeOptionsWindow(); - ... -} -``` - -### Test code -```cpp -#include "misc/freetype/imgui_freetype.h" -#include "misc/freetype/imgui_freetype.cpp" - -struct FreeTypeTest -{ - enum FontBuildMode - { - FontBuildMode_FreeType, - FontBuildMode_Stb - }; - - FontBuildMode BuildMode; - bool WantRebuild; - float FontsMultiply; - int FontsPadding; - unsigned int FontsFlags; - - FreeTypeTest() - { - BuildMode = FontBuildMode_FreeType; - WantRebuild = true; - FontsMultiply = 1.0f; - FontsPadding = 1; - FontsFlags = 0; - } - - // Call _BEFORE_ NewFrame() - bool UpdateRebuild() - { - if (!WantRebuild) - return false; - ImGuiIO& io = ImGui::GetIO(); - io.Fonts->TexGlyphPadding = FontsPadding; - for (int n = 0; n < io.Fonts->ConfigData.Size; n++) - { - ImFontConfig* font_config = (ImFontConfig*)&io.Fonts->ConfigData[n]; - font_config->RasterizerMultiply = FontsMultiply; - font_config->RasterizerFlags = (BuildMode == FontBuildMode_FreeType) ? FontsFlags : 0x00; - } - if (BuildMode == FontBuildMode_FreeType) - ImGuiFreeType::BuildFontAtlas(io.Fonts, FontsFlags); - else if (BuildMode == FontBuildMode_Stb) - io.Fonts->Build(); - WantRebuild = false; - return true; - } +### Testbed for toying with settings (for developers) - // Call to draw interface - void ShowFreetypeOptionsWindow() - { - ImGui::Begin("FreeType Options"); - ImGui::ShowFontSelector("Fonts"); - WantRebuild |= ImGui::RadioButton("FreeType", (int*)&BuildMode, FontBuildMode_FreeType); - ImGui::SameLine(); - WantRebuild |= ImGui::RadioButton("Stb (Default)", (int*)&BuildMode, FontBuildMode_Stb); - WantRebuild |= ImGui::DragFloat("Multiply", &FontsMultiply, 0.001f, 0.0f, 2.0f); - WantRebuild |= ImGui::DragInt("Padding", &FontsPadding, 0.1f, 0, 16); - if (BuildMode == FontBuildMode_FreeType) - { - WantRebuild |= ImGui::CheckboxFlags("NoHinting", &FontsFlags, ImGuiFreeType::NoHinting); - WantRebuild |= ImGui::CheckboxFlags("NoAutoHint", &FontsFlags, ImGuiFreeType::NoAutoHint); - WantRebuild |= ImGui::CheckboxFlags("ForceAutoHint", &FontsFlags, ImGuiFreeType::ForceAutoHint); - WantRebuild |= ImGui::CheckboxFlags("LightHinting", &FontsFlags, ImGuiFreeType::LightHinting); - WantRebuild |= ImGui::CheckboxFlags("MonoHinting", &FontsFlags, ImGuiFreeType::MonoHinting); - WantRebuild |= ImGui::CheckboxFlags("Bold", &FontsFlags, ImGuiFreeType::Bold); - WantRebuild |= ImGui::CheckboxFlags("Oblique", &FontsFlags, ImGuiFreeType::Oblique); - WantRebuild |= ImGui::CheckboxFlags("Monochrome", &FontsFlags, ImGuiFreeType::Monochrome); - } - ImGui::End(); - } -}; -``` +See https://gist.github.com/ocornut/b3a9ecf13502fd818799a452969649ad ### Known issues - `cfg.OversampleH`, `OversampleV` are ignored (but perhaps not so necessary with this rasterizer). diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 412411a9..e96559bf 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -1,25 +1,30 @@ -// dear imgui: wrapper to use FreeType (instead of stb_truetype) +// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder) +// (code) + // Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype -// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained and v0.60+ by @ocornut. - -// Changelog: -// - v0.50: (2017/08/16) imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks. -// - v0.51: (2017/08/26) cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply. -// - v0.52: (2017/09/26) fixes for imgui internal changes. -// - v0.53: (2017/10/22) minor inconsequential change to match change in master (removed an unnecessary statement). -// - v0.54: (2018/01/22) fix for addition of ImFontAtlas::TexUvscale member. -// - v0.55: (2018/02/04) moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club) -// - v0.56: (2018/06/08) added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX. -// - v0.60: (2019/01/10) re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding. -// - v0.61: (2019/01/15) added support for imgui allocators + added FreeType only override function SetAllocatorFunctions(). -// - v0.62: (2019/02/09) added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!) -// - v0.63: (2020/06/04) fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails. - -// Gamma Correct Blending: -// FreeType assumes blending in linear space rather than gamma space. -// See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph -// For correct results you need to be using sRGB and convert to linear space in the pixel shader output. -// The default imgui styles will be impacted by this change (alpha values will need tweaking). +// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut. + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. +// renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas(). +// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails. +// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!) +// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions(). +// 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding. +// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX. +// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club) +// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member. +// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement). +// 2017/09/26: fixes for imgui internal changes. +// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply. +// 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks. + +// About Gamma Correct Blending: +// - FreeType assumes blending in linear space rather than gamma space. +// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph +// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output. +// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking). // FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer). @@ -41,6 +46,23 @@ #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #endif +//------------------------------------------------------------------------- +// Data +//------------------------------------------------------------------------- + +// Default memory allocators +static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); } +static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); } + +// Current memory allocators +static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc; +static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc; +static void* GImGuiFreeTypeAllocatorUserData = NULL; + +//------------------------------------------------------------------------- +// Code +//------------------------------------------------------------------------- + namespace { // Glyph metrics: @@ -118,7 +140,7 @@ namespace // From SDL_ttf: Handy routines for converting from fixed point #define FT_CEIL(X) (((X + 63) & -64) / 64) - bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags) + bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags) { FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face); if (error != 0) @@ -131,22 +153,22 @@ namespace SetPixelHeight((uint32_t)cfg.SizePixels); // Convert to FreeType flags (NB: Bold and Oblique are processed separately) - UserFlags = cfg.RasterizerFlags | extra_user_flags; + UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags; LoadFlags = FT_LOAD_NO_BITMAP; - if (UserFlags & ImGuiFreeType::NoHinting) + if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting) LoadFlags |= FT_LOAD_NO_HINTING; - if (UserFlags & ImGuiFreeType::NoAutoHint) + if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint) LoadFlags |= FT_LOAD_NO_AUTOHINT; - if (UserFlags & ImGuiFreeType::ForceAutoHint) + if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint) LoadFlags |= FT_LOAD_FORCE_AUTOHINT; - if (UserFlags & ImGuiFreeType::LightHinting) + if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting) LoadFlags |= FT_LOAD_TARGET_LIGHT; - else if (UserFlags & ImGuiFreeType::MonoHinting) + else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting) LoadFlags |= FT_LOAD_TARGET_MONO; else LoadFlags |= FT_LOAD_TARGET_NORMAL; - if (UserFlags & ImGuiFreeType::Monochrome) + if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome) RenderMode = FT_RENDER_MODE_MONO; else RenderMode = FT_RENDER_MODE_NORMAL; @@ -200,9 +222,9 @@ namespace IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE); // Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting) - if (UserFlags & ImGuiFreeType::Bold) + if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold) FT_GlyphSlot_Embolden(slot); - if (UserFlags & ImGuiFreeType::Oblique) + if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique) { FT_GlyphSlot_Oblique(slot); //FT_BBox bbox; @@ -320,7 +342,7 @@ struct ImFontBuildDstDataFT ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font. }; -bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags) +bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags) { IM_ASSERT(atlas->ConfigData.Size > 0); @@ -605,50 +627,41 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns return true; } -// Default memory allocators -static void* ImFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); } -static void ImFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); } - -// Current memory allocators -static void* (*GImFreeTypeAllocFunc)(size_t size, void* user_data) = ImFreeTypeDefaultAllocFunc; -static void (*GImFreeTypeFreeFunc)(void* ptr, void* user_data) = ImFreeTypeDefaultFreeFunc; -static void* GImFreeTypeAllocatorUserData = NULL; - // FreeType memory allocation callbacks static void* FreeType_Alloc(FT_Memory /*memory*/, long size) { - return GImFreeTypeAllocFunc((size_t)size, GImFreeTypeAllocatorUserData); + return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData); } static void FreeType_Free(FT_Memory /*memory*/, void* block) { - GImFreeTypeFreeFunc(block, GImFreeTypeAllocatorUserData); + GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); } static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block) { // Implement realloc() as we don't ask user to provide it. if (block == NULL) - return GImFreeTypeAllocFunc((size_t)new_size, GImFreeTypeAllocatorUserData); + return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); if (new_size == 0) { - GImFreeTypeFreeFunc(block, GImFreeTypeAllocatorUserData); + GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); return NULL; } if (new_size > cur_size) { - void* new_block = GImFreeTypeAllocFunc((size_t)new_size, GImFreeTypeAllocatorUserData); + void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData); memcpy(new_block, block, (size_t)cur_size); - GImFreeTypeFreeFunc(block, GImFreeTypeAllocatorUserData); + GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData); return new_block; } return block; } -bool ImGuiFreeType::BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags) +static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas) { // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html FT_MemoryRec_ memory_rec = {}; @@ -666,15 +679,22 @@ bool ImGuiFreeType::BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags) // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator. FT_Add_Default_Modules(ft_library); - bool ret = ImFontAtlasBuildWithFreeType(ft_library, atlas, extra_flags); + bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags); FT_Done_Library(ft_library); return ret; } +const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType() +{ + static ImFontBuilderIO io; + io.FontBuilder_Build = ImFontAtlasBuildWithFreeType; + return &io; +} + void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) { - GImFreeTypeAllocFunc = alloc_func; - GImFreeTypeFreeFunc = free_func; - GImFreeTypeAllocatorUserData = user_data; + GImGuiFreeTypeAllocFunc = alloc_func; + GImGuiFreeTypeFreeFunc = free_func; + GImGuiFreeTypeAllocatorUserData = user_data; } diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h index 619735c4..a108d4fb 100644 --- a/misc/freetype/imgui_freetype.h +++ b/misc/freetype/imgui_freetype.h @@ -1,36 +1,42 @@ -// dear imgui: wrapper to use FreeType (instead of stb_truetype) -// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype -// Original code by @Vuhdo (Aleksei Skriabin), maintained by @ocornut +// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder) +// (headers) #pragma once -#include "imgui.h" // IMGUI_API, ImFontAtlas +#include "imgui.h" // IMGUI_API -namespace ImGuiFreeType -{ - // Hinting greatly impacts visuals (and glyph sizes). - // When disabled, FreeType generates blurrier glyphs, more or less matches the stb's output. - // The Default hinting mode usually looks good, but may distort glyphs in an unusual way. - // The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer. +// Forward declarations +struct ImFontAtlas; +struct ImFontBuilderIO; - // You can set those flags on a per font basis in ImFontConfig::RasterizerFlags. - // Use the 'extra_flags' parameter of BuildFontAtlas() to force a flag on all your fonts. - enum RasterizerFlags - { - // By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter. - NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes. - NoAutoHint = 1 << 1, // Disable auto-hinter. - ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter. - LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text. - MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output. - Bold = 1 << 5, // Styling: Should we artificially embolden the font? - Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style? - Monochrome = 1 << 7 // Disable anti-aliasing. Combine this with MonoHinting for best results! - }; +// Hinting greatly impacts visuals (and glyph sizes). +// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter. +// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h +// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way. +// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer. +// You can set those flags globaly in ImFontAtlas::FontBuilderFlags +// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags +enum ImGuiFreeTypeBuilderFlags +{ + ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes. + ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter. + ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter. + ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text. + ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output. + ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font? + ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style? + ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7 // Disable anti-aliasing. Combine this with MonoHinting for best results! +}; - IMGUI_API bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags = 0); +namespace ImGuiFreeType +{ + // This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'. + // If you need to dynamically select between multiple builders: + // - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' + // - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data. + IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); - // By default ImGuiFreeType will use IM_ALLOC()/IM_FREE(). - // However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired: - IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL); + // Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE() + // However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired. + IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL); } From e28b51786eae60f32c18214658c15952639085a2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 27 Jan 2021 12:18:48 +0100 Subject: [PATCH 11/17] Tables: Fixed PopItemWidth() or multi-components items not restoring per-colum ItemWidth correctly. (#3760) rework local stacks to facilitate modifying current value without altering the stack. May consider doing the same for ItemFlags and moving to g.ItemFlags... --- docs/CHANGELOG.txt | 1 + imgui.cpp | 14 ++++++++------ imgui.h | 4 ++-- imgui_internal.h | 8 ++++---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 195cb25b..4958d1e7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,7 @@ Breaking Changes: Other Changes: +- Tables: Fixed PopItemWidth() or multi-components items not restoring per-colum ItemWidth correctly. (#3760) - Window: Fixed minor title bar text clipping issue when FramePadding is small/zero and there are no close button in the window. (#3731) - imgui_freetype: Facilitated using FreeType integration: [@Xipiryon, @ocornut] diff --git a/imgui.cpp b/imgui.cpp index b48cff73..b4e9574a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6428,15 +6428,15 @@ void ImGui::PopButtonRepeat() void ImGui::PushTextWrapPos(float wrap_pos_x) { ImGuiWindow* window = GetCurrentWindow(); + window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos); window->DC.TextWrapPos = wrap_pos_x; - window->DC.TextWrapPosStack.push_back(wrap_pos_x); } void ImGui::PopTextWrapPos() { ImGuiWindow* window = GetCurrentWindow(); + window->DC.TextWrapPos = window->DC.TextWrapPosStack.back(); window->DC.TextWrapPosStack.pop_back(); - window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back(); } bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) @@ -7393,12 +7393,13 @@ void ImGui::SetNextItemWidth(float item_width) g.NextItemData.Width = item_width; } +// FIXME: Remove the == 0.0f behavior? void ImGui::PushItemWidth(float item_width) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); - window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; } @@ -7409,18 +7410,19 @@ void ImGui::PushMultiItemsWidths(int components, float w_full) const ImGuiStyle& style = g.Style; const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); + window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width window->DC.ItemWidthStack.push_back(w_item_last); - for (int i = 0; i < components - 1; i++) + for (int i = 0; i < components - 2; i++) window->DC.ItemWidthStack.push_back(w_item_one); - window->DC.ItemWidth = window->DC.ItemWidthStack.back(); + window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one; g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; } void ImGui::PopItemWidth() { ImGuiWindow* window = GetCurrentWindow(); + window->DC.ItemWidth = window->DC.ItemWidthStack.back(); window->DC.ItemWidthStack.pop_back(); - window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); } // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(). diff --git a/imgui.h b/imgui.h index 8f210de1..6bfe7532 100644 --- a/imgui.h +++ b/imgui.h @@ -59,7 +59,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.81 WIP" -#define IMGUI_VERSION_NUM 18001 +#define IMGUI_VERSION_NUM 18002 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -375,7 +375,7 @@ namespace ImGui IMGUI_API void PopButtonRepeat(); // Parameters stacks (current window) - IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). 0.0f = default to ~2/3 of windows width, + IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). IMGUI_API void PopItemWidth(); IMGUI_API void SetNextItemWidth(float item_width); // set width of the _next_ common large "item+label" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side) IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions. diff --git a/imgui_internal.h b/imgui_internal.h index ed07501b..0747b7cd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1692,10 +1692,10 @@ struct IMGUI_API ImGuiWindowTempData // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. ImGuiItemFlags ItemFlags; // == g.ItemFlagsStack.back() - float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window - float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] - ImVector ItemWidthStack; - ImVector TextWrapPosStack; + float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). + float TextWrapPos; // Current text wrap pos. + ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) + ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting }; From 066406b9be78b69ca82b0dc25bf7c6c7f419857a Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 27 Jan 2021 13:24:52 +0200 Subject: [PATCH 12/17] Backends: OSX: Fix mouse position not being reported when mouse buttons other than left one are down. (#3762) # Conflicts: # docs/CHANGELOG.txt --- backends/imgui_impl_osx.mm | 3 ++- docs/CHANGELOG.txt | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 4c137f6c..62bba023 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-27: Inputs: Added a fix for mouse position not being reported when mouse buttons other than left one are down. // 2020-10-28: Inputs: Added a fix for handling keypad-enter key. // 2020-05-25: Inputs: Added a fix for missing trackpad clicks when done with "soft tap". // 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. @@ -226,7 +227,7 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) return io.WantCaptureMouse; } - if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged) + if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged) { NSPoint mousePoint = event.locationInWindow; mousePoint = [view convertPoint:mousePoint fromView:nil]; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4958d1e7..b472c939 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,24 +38,25 @@ HOW TO UPDATE? Breaking Changes: - imgui_freetype: Removed ImGuiFreeType::BuildFontAtlas() extra flags, now stored in ImFontAtlas::FontBuilderFlags. -- imgui_freetype: Renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags. -- imgui_freetyoe: Renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. +- imgui_freetype: Renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags. +- imgui_freetyoe: Renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. Other Changes: - Tables: Fixed PopItemWidth() or multi-components items not restoring per-colum ItemWidth correctly. (#3760) -- Window: Fixed minor title bar text clipping issue when FramePadding is small/zero and there are no +- Window: Fixed minor title bar text clipping issue when FramePadding is small/zero and there are no close button in the window. (#3731) - imgui_freetype: Facilitated using FreeType integration: [@Xipiryon, @ocornut] - - Use '#define IMGUI_ENABLE_FREETYPE' in imconfig.h should make it work with no other modifications + - Use '#define IMGUI_ENABLE_FREETYPE' in imconfig.h should make it work with no other modifications other than compiling misc/freetype/imgui_freetype.cpp and linking with FreeType. - Use '#define IMGUI_ENABLE_STB_TRUETYPE' if you somehow need the stb_truetype rasterizer to be compiled in along with the FreeType one, otherwise it is enabled by default. - ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) Would lead to a buffer read overflow. -- Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with +- Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with old WindowSDK versions or running on Windows 7. (#3646, #3645, #3248, #2716) [@Demonese] - Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) [@Belinsky-L-V] +- Backends: OSX: Fixed mouse position not being reported when mouse buttons other than left one are down. (#3762) [@rokups] ----------------------------------------------------------------------- @@ -87,7 +88,7 @@ Breaking Changes: - If you were still using the old names, while you are cleaning up, considering enabling IMGUI_DISABLE_OBSOLETE_FUNCTIONS in imconfig.h even temporarily to have a pass at finding and removing up old API calls, if any remaining. -- Internals: Columns: renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* to reduce +- Internals: Columns: renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* to reduce confusion with Tables API. Keep redirection enums (will obsolete). (#125, #513, #913, #1204, #1444, #2142, #2707) - Renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature now applies to other data structures. (#2636) @@ -97,14 +98,14 @@ Other Changes: - Tables: added new Tables Beta API as a replacement for old Columns. (#3740, #2957, #125) Check out 'Demo->Tables' for many demos. Read API comments in imgui.h for details. Read extra commentary in imgui_tables.cpp. - - Added 16 functions: + - Added 16 functions: - BeginTable(), EndTable() - TableNextRow(), TableNextColumn(), TableSetColumnIndex() - TableSetupColumn(), TableSetupScrollFreeze() - TableHeadersRow(), TableHeader() - TableGetRowIndex(), TableGetColumnCount(), TableGetColumnIndex(), TableGetColumnName(), TableGetColumnFlags() - TableGetSortSpecs(), TableSetBgColor() - - Added 3 flags sets: + - Added 3 flags sets: - ImGuiTableFlags (29 flags for: features, decorations, sizing policies, padding, clipping, scrolling, sorting etc.) - ImGuiTableColumnFlags (24 flags for: width policies, default settings, sorting options, indentation options etc.) - ImGuiTableRowFlags (1 flag for: header row) @@ -157,7 +158,7 @@ Other Changes: - Demo: Clarify usage of right-aligned items in Demo>Layout>Widgets Width. - Backends: OpenGL3: Use glGetString(GL_VERSION) query instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x). (#3530) [@xndcn] -- Backends: OpenGL2: Backup and restore GL_SHADE_MODEL and disable GL_NORMAL_ARRAY state to increase +- Backends: OpenGL2: Backup and restore GL_SHADE_MODEL and disable GL_NORMAL_ARRAY state to increase compatibility with legacy code. (#3671) - Backends: OpenGL3: Backup and restore GL_PRIMITIVE_RESTART state. (#3544) [@Xipiryon] - Backends: OpenGL2, OpenGL3: Backup and restore GL_STENCIL_TEST enable state. (#3668) From 6001c54598c98dd056e2e19891cb7dbbcd6d3e4b Mon Sep 17 00:00:00 2001 From: Hossein Noroozpour Date: Tue, 26 Jan 2021 21:04:12 +0330 Subject: [PATCH 13/17] Backends: Vulkan: Support for custom function/symbol loader (#3759, #3227) - It adds an optional feature to support dynamic linkage of Vulkan instead of using default linkage. - It is now possible to have several potentially working implementation and whenever the Vulkan library was available it can work. --- backends/imgui_impl_vulkan.cpp | 79 +++++++++++++++++++++++++++++++++- backends/imgui_impl_vulkan.h | 45 +++++++++++++------ 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 46062390..c0c65bc3 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -48,10 +48,75 @@ // 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources. // 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active. -#include "imgui.h" #include "imgui_impl_vulkan.h" #include +#ifdef IMGUI_IMPL_VULKAN_NO_PROTOTYPES +#define IMGUI_VULKAN_FUNCTIONS_DEF(func) static PFN_##func func; +#define IMGUI_VULKAN_FUNCTIONS_MAP(IMGUI_VULKAN_FUNCTIONS_MAP_MACRO) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkAllocateCommandBuffers) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkAllocateDescriptorSets) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkAllocateMemory) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkBindBufferMemory) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkBindImageMemory) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdBindDescriptorSets) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdBindIndexBuffer) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdBindPipeline) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdBindVertexBuffers) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdCopyBufferToImage) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdDrawIndexed) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdPipelineBarrier) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdPushConstants) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdSetScissor) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdSetViewport) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateBuffer) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateCommandPool) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateDescriptorSetLayout) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateFence) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateFramebuffer) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateGraphicsPipelines) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateImage) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateImageView) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreatePipelineLayout) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateRenderPass) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateSampler) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateSemaphore) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateShaderModule) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateSwapchainKHR) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyBuffer) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyCommandPool) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyDescriptorSetLayout) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyFence) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyFramebuffer) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyImage) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyImageView) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyPipeline) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyPipelineLayout) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyRenderPass) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroySampler) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroySemaphore) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyShaderModule) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroySurfaceKHR) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroySwapchainKHR) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDeviceWaitIdle) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkFlushMappedMemoryRanges) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkFreeCommandBuffers) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkFreeMemory) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetBufferMemoryRequirements) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetImageMemoryRequirements) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetSwapchainImagesKHR) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkMapMemory) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkUnmapMemory) \ + IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkUpdateDescriptorSets) + +IMGUI_VULKAN_FUNCTIONS_MAP(IMGUI_VULKAN_FUNCTIONS_DEF) +#undef IMGUI_VULKAN_FUNCTIONS_DEF +#endif + // Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData() // [Please zero-clear before use!] struct ImGui_ImplVulkanH_FrameRenderBuffers @@ -888,6 +953,18 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass) { + +#ifdef IMGUI_IMPL_VULKAN_NO_PROTOTYPES + IM_ASSERT(info->GetVulkanProcAddressFn != NULL); +#define IMGUI_VULKAN_FUNCTIONS_LOAD(func) \ + func = reinterpret_cast(info->GetVulkanProcAddressFn(info->user_data, #func)); \ + if(NULL == func) { \ + return false; \ + } + IMGUI_VULKAN_FUNCTIONS_MAP(IMGUI_VULKAN_FUNCTIONS_LOAD) +#undef IMGUI_VULKAN_FUNCTIONS_LOAD +#endif + // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendRendererName = "imgui_impl_vulkan"; diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 1e984b39..99158722 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -22,25 +22,44 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API + +// In order to be able to use a customized Vulkan loader instead of the default one, you can uncomment the +// underlying definition in here (not in your code) or define it as a compilation flag in your build system. +// After enabling this, the user has to provide 'GetVulkanProcAddressFn' (and 'user_data' if it was needed) in +// the 'ImGui_ImplVulkan_InitInfo' so the implementation can resolve function addresses with that. +// If you have no idea what it is, leave it alone! +//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES + +#ifdef IMGUI_IMPL_VULKAN_NO_PROTOTYPES +#define VK_NO_PROTOTYPES 1 +#endif #include // Initialization data, for ImGui_ImplVulkan_Init() // [Please zero-clear before use!] struct ImGui_ImplVulkan_InitInfo { - VkInstance Instance; - VkPhysicalDevice PhysicalDevice; - VkDevice Device; - uint32_t QueueFamily; - VkQueue Queue; - VkPipelineCache PipelineCache; - VkDescriptorPool DescriptorPool; - uint32_t Subpass; - uint32_t MinImageCount; // >= 2 - uint32_t ImageCount; // >= MinImageCount - VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT - const VkAllocationCallbacks* Allocator; - void (*CheckVkResultFn)(VkResult err); + VkInstance Instance; + VkPhysicalDevice PhysicalDevice; + VkDevice Device; + uint32_t QueueFamily; + VkQueue Queue; + VkPipelineCache PipelineCache; + VkDescriptorPool DescriptorPool; + uint32_t Subpass; + uint32_t MinImageCount; // >= 2 + uint32_t ImageCount; // >= MinImageCount + VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT + const VkAllocationCallbacks* Allocator; + void (*CheckVkResultFn)(VkResult err); +#ifdef IMGUI_IMPL_VULKAN_NO_PROTOTYPES + // This function pointer is needed when the default Vulkan loader is disabled(not applicable). + // For more information look at comments before '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' + PFN_vkVoidFunction (*GetVulkanProcAddressFn)(void *user_data, const char* pName); + // This pointer is going to be fed to the 'GetVulkanProcAddressFn', you can use it for + // accessing contex/object that is maybe needed for the function call. + void *user_data; +#endif }; // Called by user code From 6487860aae967f8e36996a658db54363ae6cd347 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 27 Jan 2021 13:23:28 +0100 Subject: [PATCH 14/17] Backends: Vulkan: Rework support for custom function/symbol loader (#3759, #3227), add ImGui_ImplVulkan_LoadFunctions (amend 6001c54) Making it a separate function allows to use/test this with our examples or any code using the VulkanH helper called before ImGui_ImplVulkan_Init() --- backends/imgui_impl_vulkan.cpp | 177 +++++++++++++++++++-------------- backends/imgui_impl_vulkan.h | 57 +++++------ docs/CHANGELOG.txt | 2 + 3 files changed, 131 insertions(+), 105 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index c0c65bc3..bae3b2b3 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -22,6 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions(). // 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. // 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init). // 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices. @@ -51,72 +52,6 @@ #include "imgui_impl_vulkan.h" #include -#ifdef IMGUI_IMPL_VULKAN_NO_PROTOTYPES -#define IMGUI_VULKAN_FUNCTIONS_DEF(func) static PFN_##func func; -#define IMGUI_VULKAN_FUNCTIONS_MAP(IMGUI_VULKAN_FUNCTIONS_MAP_MACRO) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkAllocateCommandBuffers) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkAllocateDescriptorSets) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkAllocateMemory) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkBindBufferMemory) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkBindImageMemory) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdBindDescriptorSets) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdBindIndexBuffer) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdBindPipeline) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdBindVertexBuffers) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdCopyBufferToImage) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdDrawIndexed) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdPipelineBarrier) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdPushConstants) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdSetScissor) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCmdSetViewport) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateBuffer) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateCommandPool) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateDescriptorSetLayout) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateFence) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateFramebuffer) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateGraphicsPipelines) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateImage) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateImageView) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreatePipelineLayout) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateRenderPass) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateSampler) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateSemaphore) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateShaderModule) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkCreateSwapchainKHR) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyBuffer) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyCommandPool) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyDescriptorSetLayout) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyFence) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyFramebuffer) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyImage) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyImageView) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyPipeline) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyPipelineLayout) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyRenderPass) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroySampler) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroySemaphore) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroyShaderModule) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroySurfaceKHR) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDestroySwapchainKHR) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkDeviceWaitIdle) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkFlushMappedMemoryRanges) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkFreeCommandBuffers) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkFreeMemory) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetBufferMemoryRequirements) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetImageMemoryRequirements) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkGetSwapchainImagesKHR) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkMapMemory) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkUnmapMemory) \ - IMGUI_VULKAN_FUNCTIONS_MAP_MACRO(vkUpdateDescriptorSets) - -IMGUI_VULKAN_FUNCTIONS_MAP(IMGUI_VULKAN_FUNCTIONS_DEF) -#undef IMGUI_VULKAN_FUNCTIONS_DEF -#endif - // Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData() // [Please zero-clear before use!] struct ImGui_ImplVulkanH_FrameRenderBuffers @@ -150,6 +85,11 @@ static VkPipeline g_Pipeline = VK_NULL_HANDLE; static uint32_t g_Subpass = 0; static VkShaderModule g_ShaderModuleVert; static VkShaderModule g_ShaderModuleFrag; +#ifdef VK_NO_PROTOTYPES +static bool g_FunctionsLoaded = false; +#else +static bool g_FunctionsLoaded = true; +#endif // Font data static VkSampler g_FontSampler = VK_NULL_HANDLE; @@ -172,6 +112,75 @@ void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVul void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); +// Vulkan prototypes for use with custom loaders +// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h +#ifdef VK_NO_PROTOTYPES +#define IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_MAP_MACRO) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindBufferMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindImageMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindIndexBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindPipeline) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindVertexBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdCopyBufferToImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdDrawIndexed) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPipelineBarrier) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPushConstants) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetScissor) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFence) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateGraphicsPipelines) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImageView) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreatePipelineLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateRenderPass) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSampler) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSemaphore) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateShaderModule) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImageView) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipeline) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipelineLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyRenderPass) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySampler) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySemaphore) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyShaderModule) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySurfaceKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySwapchainKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDeviceWaitIdle) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFlushMappedMemoryRanges) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeCommandBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetBufferMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetImageMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetSwapchainImagesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkMapMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) + +// Define function pointers +#define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func; +IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_DEF) +#undef IMGUI_VULKAN_FUNC_DEF +#endif // VK_NO_PROTOTYPES + //----------------------------------------------------------------------------- // SHADERS //----------------------------------------------------------------------------- @@ -951,19 +960,30 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() if (g_Pipeline) { vkDestroyPipeline(v->Device, g_Pipeline, v->Allocator); g_Pipeline = VK_NULL_HANDLE; } } -bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass) +bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) { - -#ifdef IMGUI_IMPL_VULKAN_NO_PROTOTYPES - IM_ASSERT(info->GetVulkanProcAddressFn != NULL); -#define IMGUI_VULKAN_FUNCTIONS_LOAD(func) \ - func = reinterpret_cast(info->GetVulkanProcAddressFn(info->user_data, #func)); \ - if(NULL == func) { \ - return false; \ - } - IMGUI_VULKAN_FUNCTIONS_MAP(IMGUI_VULKAN_FUNCTIONS_LOAD) -#undef IMGUI_VULKAN_FUNCTIONS_LOAD + // Load function pointers + // You can use the default Vulkan loader using: + // ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); }); + // But this would be equivalent to not setting VK_NO_PROTOTYPES. +#ifdef VK_NO_PROTOTYPES +#define IMGUI_VULKAN_FUNC_LOAD(func) \ + func = reinterpret_cast(loader_func(#func, user_data)); \ + if (func == NULL) \ + return false; + IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_LOAD) +#undef IMGUI_VULKAN_FUNC_LOAD +#else + IM_UNUSED(loader_func); + IM_UNUSED(user_data); #endif + g_FunctionsLoaded = true; + return true; +} + +bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); @@ -1029,6 +1049,7 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space) { + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); IM_ASSERT(request_formats != NULL); IM_ASSERT(request_formats_count > 0); @@ -1073,6 +1094,7 @@ VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physic VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count) { + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); IM_ASSERT(request_modes != NULL); IM_ASSERT(request_modes_count > 0); @@ -1323,6 +1345,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // Create or resize window void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) { + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); (void)instance; ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 99158722..9f9010e5 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -23,15 +23,21 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API -// In order to be able to use a customized Vulkan loader instead of the default one, you can uncomment the -// underlying definition in here (not in your code) or define it as a compilation flag in your build system. -// After enabling this, the user has to provide 'GetVulkanProcAddressFn' (and 'user_data' if it was needed) in -// the 'ImGui_ImplVulkan_InitInfo' so the implementation can resolve function addresses with that. -// If you have no idea what it is, leave it alone! +// [Configuration] in order to use a custom Vulkan function loader: +// (1) You'll need to disable default Vulkan function prototypes. +// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag. +// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit: +// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file +// - Or as a compilation flag in your build system +// - Or uncomment here (not recommended because you'd be modifying imgui sources!) +// - Do not simply add it in a .cpp file! +// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function. +// If you have no idea what this is, leave it alone! //#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES -#ifdef IMGUI_IMPL_VULKAN_NO_PROTOTYPES -#define VK_NO_PROTOTYPES 1 +// Vulkan includes +#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES) +#define VK_NO_PROTOTYPES #endif #include @@ -39,27 +45,19 @@ // [Please zero-clear before use!] struct ImGui_ImplVulkan_InitInfo { - VkInstance Instance; - VkPhysicalDevice PhysicalDevice; - VkDevice Device; - uint32_t QueueFamily; - VkQueue Queue; - VkPipelineCache PipelineCache; - VkDescriptorPool DescriptorPool; - uint32_t Subpass; - uint32_t MinImageCount; // >= 2 - uint32_t ImageCount; // >= MinImageCount - VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT - const VkAllocationCallbacks* Allocator; - void (*CheckVkResultFn)(VkResult err); -#ifdef IMGUI_IMPL_VULKAN_NO_PROTOTYPES - // This function pointer is needed when the default Vulkan loader is disabled(not applicable). - // For more information look at comments before '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' - PFN_vkVoidFunction (*GetVulkanProcAddressFn)(void *user_data, const char* pName); - // This pointer is going to be fed to the 'GetVulkanProcAddressFn', you can use it for - // accessing contex/object that is maybe needed for the function call. - void *user_data; -#endif + VkInstance Instance; + VkPhysicalDevice PhysicalDevice; + VkDevice Device; + uint32_t QueueFamily; + VkQueue Queue; + VkPipelineCache PipelineCache; + VkDescriptorPool DescriptorPool; + uint32_t Subpass; + uint32_t MinImageCount; // >= 2 + uint32_t ImageCount; // >= MinImageCount + VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT + const VkAllocationCallbacks* Allocator; + void (*CheckVkResultFn)(VkResult err); }; // Called by user code @@ -71,6 +69,9 @@ IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer comm IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) +// Optional: load Vulkan functions with a custom function loader +// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES +IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = NULL); //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b472c939..209f3be1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,8 @@ Other Changes: Would lead to a buffer read overflow. - Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with old WindowSDK versions or running on Windows 7. (#3646, #3645, #3248, #2716) [@Demonese] +- Backends: Vulkan: Add support for custom Vulkan function loader and VK_NO_PROTOTYPES. (#3759, #3227) [@Hossein-Noroozpour] + User needs to call ImGui_ImplVulkan_LoadFunctions() with their custom loader prior to other functions. - Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) [@Belinsky-L-V] - Backends: OSX: Fixed mouse position not being reported when mouse buttons other than left one are down. (#3762) [@rokups] From 5853fbd68bc2deb8ca82fd37708fc3027f0e8fda Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Thu, 28 Jan 2021 11:37:34 +0100 Subject: [PATCH 15/17] Backends, Examples: Added support for WebGPU and corresponding example (#3632) (Squashed 11 commits) --- .github/workflows/build.yml | 7 + backends/imgui_impl_wgpu.cpp | 783 ++++++++++++++++++ backends/imgui_impl_wgpu.h | 26 + docs/BACKENDS.md | 1 + docs/CHANGELOG.txt | 1 + docs/EXAMPLES.md | 4 + examples/example_emscripten_wgpu/Makefile | 80 ++ examples/example_emscripten_wgpu/README.md | 10 + .../example_emscripten_wgpu.html | 80 ++ examples/example_emscripten_wgpu/main.cpp | 257 ++++++ 10 files changed, 1249 insertions(+) create mode 100644 backends/imgui_impl_wgpu.cpp create mode 100644 backends/imgui_impl_wgpu.h create mode 100644 examples/example_emscripten_wgpu/Makefile create mode 100644 examples/example_emscripten_wgpu/README.md create mode 100644 examples/example_emscripten_wgpu/example_emscripten_wgpu.html create mode 100644 examples/example_emscripten_wgpu/main.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index adec4998..50a6133c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -400,6 +400,13 @@ jobs: popd make -C examples/example_emscripten_opengl3 + - name: Build example_emscripten_wgpu + run: | + pushd emsdk-master + source ./emsdk_env.sh + popd + make -C examples/example_emscripten_wgpu + Discord-CI: runs-on: ubuntu-18.04 if: always() diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp new file mode 100644 index 00000000..23605dd6 --- /dev/null +++ b/backends/imgui_impl_wgpu.cpp @@ -0,0 +1,783 @@ +// dear imgui: Renderer for WebGPU +// This needs to be used along with a Platform Binding (e.g. GLFW) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) + +#include "imgui.h" +#include "imgui_impl_wgpu.h" + +// CRT +#include + +// WebGPU +#include + +// ImGui prototypes +ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0); + +// WebGPU data +static WGPUDevice g_wgpuDevice = NULL; +static WGPUTextureFormat g_renderTargetFormat = WGPUTextureFormat_Undefined; +static WGPURenderPipeline g_pipelineState = NULL; + +struct RenderResources +{ + // Font texture + WGPUTexture FontTexture; + + // Texture view for font texture + WGPUTextureView FontTextureView; + + // Sampler for the font texture + WGPUSampler Sampler; + + // Shader uniforms + WGPUBuffer Uniforms; + + // Resources bind-group to bind the common resources to pipeline + WGPUBindGroup CommonBindGroup; + + // Bind group layout for image textures + WGPUBindGroupLayout ImageBindGroupLayout; + + // Resources bind-group to bind the font/image resources to pipeline + ImGuiStorage ImageBindGroups; + + // Default font-resource of ImGui + WGPUBindGroup ImageBindGroup; +}; +static RenderResources g_resources; + +struct FrameResources +{ + WGPUBuffer IndexBuffer; + WGPUBuffer VertexBuffer; + ImDrawIdx* IndexBufferHost; + ImDrawVert* VertexBufferHost; + int IndexBufferSize; + int VertexBufferSize; +}; +static FrameResources* g_pFrameResources = NULL; +static unsigned int g_numFramesInFlight = 0; +static unsigned int g_frameIndex = UINT_MAX; + +struct Uniforms +{ + float MVP[4][4]; +}; + +//----------------------------------------------------------------------------- +// SHADERS +//----------------------------------------------------------------------------- + +// glsl_shader.vert, compiled with: +// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert +/* +#version 450 core +layout(location = 0) in vec2 aPos; +layout(location = 1) in vec2 aUV; +layout(location = 2) in vec4 aColor; +layout(set=0, binding = 0) uniform transform { mat4 mvp; }; + +out gl_PerVertex { vec4 gl_Position; }; +layout(location = 0) out struct { vec4 Color; vec2 UV; } Out; + +void main() +{ + Out.Color = aColor; + Out.UV = aUV; + gl_Position = mvp * vec4(aPos, 0, 1); +} +*/ +static uint32_t __glsl_shader_vert_spv[] = +{ + 0x07230203,0x00010000,0x00080007,0x0000002c,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015, + 0x0000001b,0x00000023,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, + 0x00000000,0x00030005,0x00000009,0x00000000,0x00050006,0x00000009,0x00000000,0x6f6c6f43, + 0x00000072,0x00040006,0x00000009,0x00000001,0x00005655,0x00030005,0x0000000b,0x0074754f, + 0x00040005,0x0000000f,0x6c6f4361,0x0000726f,0x00030005,0x00000015,0x00565561,0x00060005, + 0x00000019,0x505f6c67,0x65567265,0x78657472,0x00000000,0x00060006,0x00000019,0x00000000, + 0x505f6c67,0x7469736f,0x006e6f69,0x00030005,0x0000001b,0x00000000,0x00050005,0x0000001d, + 0x6e617274,0x726f6673,0x0000006d,0x00040006,0x0000001d,0x00000000,0x0070766d,0x00030005, + 0x0000001f,0x00000000,0x00040005,0x00000023,0x736f5061,0x00000000,0x00040047,0x0000000b, + 0x0000001e,0x00000000,0x00040047,0x0000000f,0x0000001e,0x00000002,0x00040047,0x00000015, + 0x0000001e,0x00000001,0x00050048,0x00000019,0x00000000,0x0000000b,0x00000000,0x00030047, + 0x00000019,0x00000002,0x00040048,0x0000001d,0x00000000,0x00000005,0x00050048,0x0000001d, + 0x00000000,0x00000023,0x00000000,0x00050048,0x0000001d,0x00000000,0x00000007,0x00000010, + 0x00030047,0x0000001d,0x00000002,0x00040047,0x0000001f,0x00000022,0x00000000,0x00040047, + 0x0000001f,0x00000021,0x00000000,0x00040047,0x00000023,0x0000001e,0x00000000,0x00020013, + 0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017, + 0x00000007,0x00000006,0x00000004,0x00040017,0x00000008,0x00000006,0x00000002,0x0004001e, + 0x00000009,0x00000007,0x00000008,0x00040020,0x0000000a,0x00000003,0x00000009,0x0004003b, + 0x0000000a,0x0000000b,0x00000003,0x00040015,0x0000000c,0x00000020,0x00000001,0x0004002b, + 0x0000000c,0x0000000d,0x00000000,0x00040020,0x0000000e,0x00000001,0x00000007,0x0004003b, + 0x0000000e,0x0000000f,0x00000001,0x00040020,0x00000011,0x00000003,0x00000007,0x0004002b, + 0x0000000c,0x00000013,0x00000001,0x00040020,0x00000014,0x00000001,0x00000008,0x0004003b, + 0x00000014,0x00000015,0x00000001,0x00040020,0x00000017,0x00000003,0x00000008,0x0003001e, + 0x00000019,0x00000007,0x00040020,0x0000001a,0x00000003,0x00000019,0x0004003b,0x0000001a, + 0x0000001b,0x00000003,0x00040018,0x0000001c,0x00000007,0x00000004,0x0003001e,0x0000001d, + 0x0000001c,0x00040020,0x0000001e,0x00000002,0x0000001d,0x0004003b,0x0000001e,0x0000001f, + 0x00000002,0x00040020,0x00000020,0x00000002,0x0000001c,0x0004003b,0x00000014,0x00000023, + 0x00000001,0x0004002b,0x00000006,0x00000025,0x00000000,0x0004002b,0x00000006,0x00000026, + 0x3f800000,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005, + 0x0004003d,0x00000007,0x00000010,0x0000000f,0x00050041,0x00000011,0x00000012,0x0000000b, + 0x0000000d,0x0003003e,0x00000012,0x00000010,0x0004003d,0x00000008,0x00000016,0x00000015, + 0x00050041,0x00000017,0x00000018,0x0000000b,0x00000013,0x0003003e,0x00000018,0x00000016, + 0x00050041,0x00000020,0x00000021,0x0000001f,0x0000000d,0x0004003d,0x0000001c,0x00000022, + 0x00000021,0x0004003d,0x00000008,0x00000024,0x00000023,0x00050051,0x00000006,0x00000027, + 0x00000024,0x00000000,0x00050051,0x00000006,0x00000028,0x00000024,0x00000001,0x00070050, + 0x00000007,0x00000029,0x00000027,0x00000028,0x00000025,0x00000026,0x00050091,0x00000007, + 0x0000002a,0x00000022,0x00000029,0x00050041,0x00000011,0x0000002b,0x0000001b,0x0000000d, + 0x0003003e,0x0000002b,0x0000002a,0x000100fd,0x00010038 +}; + +// glsl_shader.frag, compiled with: +// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag +/* +#version 450 core +layout(location = 0) out vec4 fColor; +layout(set=0, binding=1) uniform sampler s; +layout(set=1, binding=0) uniform texture2D t; +layout(location = 0) in struct { vec4 Color; vec2 UV; } In; +void main() +{ + fColor = In.Color * texture(sampler2D(t, s), In.UV.st); +} +*/ +static uint32_t __glsl_shader_frag_spv[] = +{ + 0x07230203,0x00010000,0x00080007,0x00000023,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010, + 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, + 0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000, + 0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001, + 0x00005655,0x00030005,0x0000000d,0x00006e49,0x00030005,0x00000015,0x00000074,0x00030005, + 0x00000019,0x00000073,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d, + 0x0000001e,0x00000000,0x00040047,0x00000015,0x00000022,0x00000001,0x00040047,0x00000015, + 0x00000021,0x00000000,0x00040047,0x00000019,0x00000022,0x00000000,0x00040047,0x00000019, + 0x00000021,0x00000001,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016, + 0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008, + 0x00000003,0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040017,0x0000000a, + 0x00000006,0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,0x0000000c, + 0x00000001,0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,0x0000000e, + 0x00000020,0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,0x00000010, + 0x00000001,0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,0x00000000, + 0x00000000,0x00000001,0x00000000,0x00040020,0x00000014,0x00000000,0x00000013,0x0004003b, + 0x00000014,0x00000015,0x00000000,0x0002001a,0x00000017,0x00040020,0x00000018,0x00000000, + 0x00000017,0x0004003b,0x00000018,0x00000019,0x00000000,0x0003001b,0x0000001b,0x00000013, + 0x0004002b,0x0000000e,0x0000001d,0x00000001,0x00040020,0x0000001e,0x00000001,0x0000000a, + 0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x00050041, + 0x00000010,0x00000011,0x0000000d,0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011, + 0x0004003d,0x00000013,0x00000016,0x00000015,0x0004003d,0x00000017,0x0000001a,0x00000019, + 0x00050056,0x0000001b,0x0000001c,0x00000016,0x0000001a,0x00050041,0x0000001e,0x0000001f, + 0x0000000d,0x0000001d,0x0004003d,0x0000000a,0x00000020,0x0000001f,0x00050057,0x00000007, + 0x00000021,0x0000001c,0x00000020,0x00050085,0x00000007,0x00000022,0x00000012,0x00000021, + 0x0003003e,0x00000009,0x00000022,0x000100fd,0x00010038 +}; + +static void SafeRelease(ImDrawIdx*& res) +{ + if (res) + delete[] res; + res = NULL; +} +static void SafeRelease(ImDrawVert*& res) +{ + if (res) + delete[] res; + res = NULL; +} +static void SafeRelease(WGPUBindGroupLayout& res) +{ + if (res) + wgpuBindGroupLayoutRelease(res); + res = NULL; +} +static void SafeRelease(WGPUBindGroup& res) +{ + if (res) + wgpuBindGroupRelease(res); + res = NULL; +} +static void SafeRelease(WGPUBuffer& res) +{ + if (res) + wgpuBufferRelease(res); + res = NULL; +} +static void SafeRelease(WGPURenderPipeline& res) +{ + if (res) + wgpuRenderPipelineRelease(res); + res = NULL; +} +static void SafeRelease(WGPUSampler& res) +{ + if (res) + wgpuSamplerRelease(res); + res = NULL; +} +static void SafeRelease(WGPUShaderModule& res) +{ + if (res) + wgpuShaderModuleRelease(res); + res = NULL; +} +static void SafeRelease(WGPUTextureView& res) +{ + if (res) + wgpuTextureViewRelease(res); + res = NULL; +} +static void SafeRelease(WGPUTexture& res) +{ + if (res) + wgpuTextureRelease(res); + res = NULL; +} + +static void SafeRelease(RenderResources& res) +{ + SafeRelease(res.FontTexture); + SafeRelease(res.FontTextureView); + SafeRelease(res.Sampler); + SafeRelease(res.Uniforms); + SafeRelease(res.CommonBindGroup); + SafeRelease(res.ImageBindGroupLayout); + SafeRelease(res.ImageBindGroup); +}; + +static void SafeRelease(FrameResources& res) +{ + SafeRelease(res.IndexBuffer); + SafeRelease(res.VertexBuffer); + SafeRelease(res.IndexBufferHost); + SafeRelease(res.VertexBufferHost); +} + +static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(uint32_t* binary_data, uint32_t binary_data_size) +{ + WGPUShaderModuleSPIRVDescriptor spirv_desc = {}; + spirv_desc.chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor; + spirv_desc.codeSize = binary_data_size; + spirv_desc.code = binary_data; + + WGPUShaderModuleDescriptor desc; + desc.nextInChain = reinterpret_cast(&spirv_desc); + + WGPUProgrammableStageDescriptor stage_desc = {}; + stage_desc.module = wgpuDeviceCreateShaderModule(g_wgpuDevice, &desc); + stage_desc.entryPoint = "main"; + return stage_desc; +} + +static WGPUBindGroup ImGui_ImplWGPU_CreateImageBindGroup(WGPUBindGroupLayout layout, WGPUTextureView texture) +{ + WGPUBindGroupEntry image_bg_entries[] = { + { 0, 0, 0, 0, 0, texture }, + }; + + WGPUBindGroupDescriptor image_bg_descriptor = {}; + image_bg_descriptor.layout = layout; + image_bg_descriptor.entryCount = sizeof(image_bg_entries) / sizeof(WGPUBindGroupEntry); + image_bg_descriptor.entries = image_bg_entries; + return wgpuDeviceCreateBindGroup(g_wgpuDevice, &image_bg_descriptor); +} + +static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPassEncoder ctx, FrameResources* fr) +{ + // Setup orthographic projection matrix into our constant buffer + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). + { + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + float mvp[4][4] = + { + { 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 }, + { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + }; + wgpuQueueWriteBuffer(wgpuDeviceGetDefaultQueue(g_wgpuDevice), g_resources.Uniforms, 0, mvp, sizeof(mvp)); + } + + // Setup viewport + wgpuRenderPassEncoderSetViewport(ctx, 0, 0, draw_data->DisplaySize.x, draw_data->DisplaySize.y, 0, 1); + + // Bind shader and vertex buffers + unsigned int stride = sizeof(ImDrawVert); + unsigned int offset = 0; + wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, offset, fr->VertexBufferSize * stride); + wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, fr->IndexBufferSize * sizeof(ImDrawIdx)); + wgpuRenderPassEncoderSetPipeline(ctx, g_pipelineState); + wgpuRenderPassEncoderSetBindGroup(ctx, 0, g_resources.CommonBindGroup, 0, NULL); + + // Setup blend factor + WGPUColor blend_color = { 0.f, 0.f, 0.f, 0.f }; + wgpuRenderPassEncoderSetBlendColor(ctx, &blend_color); +} + +// Render function +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder) +{ + // Avoid rendering when minimized + if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) + return; + + // FIXME: I'm assuming that this only gets called once per frame! + // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. + g_frameIndex = g_frameIndex + 1; + FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight]; + + // Create and grow vertex/index buffers if needed + if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) + { + SafeRelease(fr->VertexBuffer); + SafeRelease(fr->VertexBufferHost); + fr->VertexBufferSize = draw_data->TotalVtxCount + 5000; + + WGPUBufferDescriptor vb_desc = { + nullptr, + "IMGUI Vertex buffer", + WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, + fr->VertexBufferSize * sizeof(ImDrawVert), + false + }; + fr->VertexBuffer = wgpuDeviceCreateBuffer(g_wgpuDevice, &vb_desc); + if (!fr->VertexBuffer) + return; + + fr->VertexBufferHost = new ImDrawVert[fr->VertexBufferSize]; + } + if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount) + { + SafeRelease(fr->IndexBuffer); + SafeRelease(fr->IndexBufferHost); + fr->IndexBufferSize = draw_data->TotalIdxCount + 10000; + + WGPUBufferDescriptor ib_desc = { + nullptr, + "IMGUI Index buffer", + WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, + fr->IndexBufferSize * sizeof(ImDrawIdx), + false + }; + fr->IndexBuffer = wgpuDeviceCreateBuffer(g_wgpuDevice, &ib_desc); + if (!fr->IndexBuffer) + return; + + fr->IndexBufferHost = new ImDrawIdx[fr->IndexBufferSize]; + } + + // Upload vertex/index data into a single contiguous GPU buffer + ImDrawVert* vtx_dst = (ImDrawVert*)fr->VertexBufferHost; + ImDrawIdx* idx_dst = (ImDrawIdx*)fr->IndexBufferHost; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + vtx_dst += cmd_list->VtxBuffer.Size; + idx_dst += cmd_list->IdxBuffer.Size; + } + int64_t vb_write_size = ((char*)vtx_dst - (char*)fr->VertexBufferHost + 3) & ~3; + int64_t ib_write_size = ((char*)idx_dst - (char*)fr->IndexBufferHost + 3) & ~3; + wgpuQueueWriteBuffer(wgpuDeviceGetDefaultQueue(g_wgpuDevice), fr->VertexBuffer, 0, fr->VertexBufferHost, vb_write_size); + wgpuQueueWriteBuffer(wgpuDeviceGetDefaultQueue(g_wgpuDevice), fr->IndexBuffer, 0, fr->IndexBufferHost, ib_write_size); + + // Setup desired render state + ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); + + // 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 != NULL) + { + // 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_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Bind custom texture + auto bind_group = g_resources.ImageBindGroups.GetVoidPtr(ImHashData(&pcmd->TextureId, sizeof(ImTextureID))); + if (bind_group) { + wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, NULL); + } + else { + WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(g_resources.ImageBindGroupLayout, (WGPUTextureView) pcmd->TextureId); + g_resources.ImageBindGroups.SetVoidPtr(ImHashData(&pcmd->TextureId, sizeof(ImTextureID)), image_bind_group); + + wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, image_bind_group, 0, NULL); + } + + // Apply Scissor, Bind texture, Draw + uint32_t clip_rect[4]; + clip_rect[0] = static_cast(pcmd->ClipRect.x - clip_off.x); + clip_rect[1] = static_cast(pcmd->ClipRect.y - clip_off.y); + clip_rect[2] = static_cast(pcmd->ClipRect.z - clip_off.x); + clip_rect[3] = static_cast(pcmd->ClipRect.w - clip_off.y); + wgpuRenderPassEncoderSetScissorRect(pass_encoder, clip_rect[0], clip_rect[1], clip_rect[2] - clip_rect[0], clip_rect[3] - clip_rect[1]); + wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); + } + } + global_idx_offset += cmd_list->IdxBuffer.Size; + global_vtx_offset += cmd_list->VtxBuffer.Size; + } +} + +static WGPUBuffer ImGui_ImplWGPU_CreateBufferFromData(const WGPUDevice& device, const void* data, uint64_t size, WGPUBufferUsage usage) +{ + WGPUBufferDescriptor descriptor = {}; + descriptor.size = size; + descriptor.usage = usage | WGPUBufferUsage_CopyDst; + WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); + + WGPUQueue queue = wgpuDeviceGetDefaultQueue(g_wgpuDevice); + wgpuQueueWriteBuffer(queue, buffer, 0, data, size); + return buffer; +} + + +static void ImGui_ImplWGPU_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height, size_pp; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &size_pp); + + // Upload texture to graphics system + { + WGPUTextureDescriptor tex_desc = {}; + tex_desc.label = "IMGUI Font Texture"; + tex_desc.dimension = WGPUTextureDimension_2D; + tex_desc.size.width = width; + tex_desc.size.height = height; + tex_desc.size.depth = 1; + tex_desc.sampleCount = 1; + tex_desc.format = WGPUTextureFormat_RGBA8Unorm; + tex_desc.mipLevelCount = 1; + tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_Sampled; + g_resources.FontTexture = wgpuDeviceCreateTexture(g_wgpuDevice, &tex_desc); + + WGPUTextureViewDescriptor tex_view_desc = {}; + tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm; + tex_view_desc.dimension = WGPUTextureViewDimension_2D; + tex_view_desc.baseMipLevel = 0; + tex_view_desc.mipLevelCount = 1; + tex_view_desc.baseArrayLayer = 0; + tex_view_desc.arrayLayerCount = 1; + tex_view_desc.aspect = WGPUTextureAspect_All; + g_resources.FontTextureView = wgpuTextureCreateView(g_resources.FontTexture, &tex_view_desc); + } + + // Upload texture data + { + WGPUBuffer staging_buffer = ImGui_ImplWGPU_CreateBufferFromData(g_wgpuDevice, pixels, static_cast(width * size_pp * height), WGPUBufferUsage_CopySrc); + + WGPUBufferCopyView bufferCopyView = {}; + bufferCopyView.buffer = staging_buffer; + bufferCopyView.layout.offset = 0; + bufferCopyView.layout.bytesPerRow = width * size_pp; + bufferCopyView.layout.rowsPerImage = height; + + WGPUTextureCopyView textureCopyView = {}; + textureCopyView.texture = g_resources.FontTexture; + textureCopyView.mipLevel = 0; + textureCopyView.origin = { 0, 0, 0 }; +#ifndef __EMSCRIPTEN__ + textureCopyView.aspect = WGPUTextureAspect_All; +#endif + + WGPUExtent3D copySize = { static_cast(width), static_cast(height), 1 }; + + WGPUCommandEncoderDescriptor enc_desc = {}; + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(g_wgpuDevice, &enc_desc); + wgpuCommandEncoderCopyBufferToTexture(encoder, &bufferCopyView, &textureCopyView, ©Size); + WGPUCommandBufferDescriptor cmd_buf_desc = {}; + WGPUCommandBuffer copy = wgpuCommandEncoderFinish(encoder, &cmd_buf_desc); + WGPUQueue queue = wgpuDeviceGetDefaultQueue(g_wgpuDevice); + wgpuQueueSubmit(queue, 1, ©); + + wgpuCommandEncoderRelease(encoder); + wgpuBufferRelease(staging_buffer); + } + + // Create the associated sampler + { + WGPUSamplerDescriptor sampler_desc = {}; + sampler_desc.minFilter = WGPUFilterMode_Linear; + sampler_desc.magFilter = WGPUFilterMode_Linear; + sampler_desc.mipmapFilter = WGPUFilterMode_Linear; + sampler_desc.addressModeU = WGPUAddressMode_Repeat; + sampler_desc.addressModeV = WGPUAddressMode_Repeat; + sampler_desc.addressModeW = WGPUAddressMode_Repeat; + g_resources.Sampler = wgpuDeviceCreateSampler(g_wgpuDevice, &sampler_desc); + } + + // Store our identifier + static_assert(sizeof(ImTextureID) >= sizeof(g_resources.FontTexture), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); + io.Fonts->TexID = (ImTextureID)g_resources.FontTextureView; +} + +static void ImGui_ImplWGPU_CreateUniformBuffer() +{ + WGPUBufferDescriptor ub_desc = { + nullptr, + "IMGUI Uniform buffer", + WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, + sizeof(Uniforms), + false + }; + g_resources.Uniforms = wgpuDeviceCreateBuffer(g_wgpuDevice, &ub_desc); +} + +bool ImGui_ImplWGPU_CreateDeviceObjects() +{ + if (!g_wgpuDevice) + return false; + if (g_pipelineState) + ImGui_ImplWGPU_InvalidateDeviceObjects(); + + // Create render pipeline + WGPURenderPipelineDescriptor graphics_pipeline_desc = {}; + + graphics_pipeline_desc.primitiveTopology = WGPUPrimitiveTopology_TriangleList; + graphics_pipeline_desc.sampleCount = 1; + graphics_pipeline_desc.sampleMask = UINT_MAX; + + + WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {}; + common_bg_layout_entries[0].binding = 0; + common_bg_layout_entries[0].visibility = WGPUShaderStage_Vertex; + common_bg_layout_entries[0].type = WGPUBindingType_UniformBuffer; + common_bg_layout_entries[1].binding = 1; + common_bg_layout_entries[1].visibility = WGPUShaderStage_Fragment; + common_bg_layout_entries[1].type = WGPUBindingType_Sampler; + + WGPUBindGroupLayoutEntry image_bg_layout_entries[1] = {}; + image_bg_layout_entries[0].binding = 0; + image_bg_layout_entries[0].visibility = WGPUShaderStage_Fragment; + image_bg_layout_entries[0].type = WGPUBindingType_SampledTexture; + + WGPUBindGroupLayoutDescriptor common_bg_layout_desc = {}; + common_bg_layout_desc.entryCount = 2; + common_bg_layout_desc.entries = common_bg_layout_entries; + + WGPUBindGroupLayoutDescriptor image_bg_layout_desc = {}; + image_bg_layout_desc.entryCount = 1; + image_bg_layout_desc.entries = image_bg_layout_entries; + + WGPUBindGroupLayout bg_layouts[2]; + bg_layouts[0] = wgpuDeviceCreateBindGroupLayout(g_wgpuDevice, &common_bg_layout_desc); + bg_layouts[1] = wgpuDeviceCreateBindGroupLayout(g_wgpuDevice, &image_bg_layout_desc); + + WGPUPipelineLayoutDescriptor layout_desc = {}; + layout_desc.bindGroupLayoutCount = 2; + layout_desc.bindGroupLayouts = bg_layouts; + graphics_pipeline_desc.layout = wgpuDeviceCreatePipelineLayout(g_wgpuDevice, &layout_desc); + + // Create the vertex shader + WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__glsl_shader_vert_spv, sizeof(__glsl_shader_vert_spv) / sizeof(uint32_t)); + graphics_pipeline_desc.vertexStage = vertex_shader_desc; + + // Vertex input configuration + WGPUVertexAttributeDescriptor attribute_binding_desc[] = { + { WGPUVertexFormat_Float2, (uint64_t)IM_OFFSETOF(ImDrawVert, pos), 0 }, + { WGPUVertexFormat_Float2, (uint64_t)IM_OFFSETOF(ImDrawVert, uv), 1 }, + { WGPUVertexFormat_UChar4Norm, (uint64_t)IM_OFFSETOF(ImDrawVert, col), 2 }, + }; + + WGPUVertexBufferLayoutDescriptor buffer_binding_desc; + buffer_binding_desc.arrayStride = sizeof(ImDrawVert); + buffer_binding_desc.stepMode = WGPUInputStepMode_Vertex; + buffer_binding_desc.attributeCount = 3; + buffer_binding_desc.attributes = attribute_binding_desc; + + WGPUVertexStateDescriptor vertex_state_desc = {}; + vertex_state_desc.indexFormat = WGPUIndexFormat_Undefined; + vertex_state_desc.vertexBufferCount = 1; + vertex_state_desc.vertexBuffers = &buffer_binding_desc; + + graphics_pipeline_desc.vertexState = &vertex_state_desc; + + // Create the pixel shader + WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__glsl_shader_frag_spv, sizeof(__glsl_shader_frag_spv) / sizeof(uint32_t)); + graphics_pipeline_desc.fragmentStage = &pixel_shader_desc; + + // Create the blending setup + WGPUColorStateDescriptor color_state = {}; + { + color_state.format = g_renderTargetFormat; + color_state.alphaBlend.operation = WGPUBlendOperation_Add; + color_state.alphaBlend.srcFactor = WGPUBlendFactor_OneMinusSrcAlpha; + color_state.alphaBlend.dstFactor = WGPUBlendFactor_Zero; + color_state.colorBlend.operation = WGPUBlendOperation_Add; + color_state.colorBlend.srcFactor = WGPUBlendFactor_SrcAlpha; + color_state.colorBlend.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; + color_state.writeMask = WGPUColorWriteMask_All; + + graphics_pipeline_desc.colorStateCount = 1; + graphics_pipeline_desc.colorStates = &color_state; + graphics_pipeline_desc.alphaToCoverageEnabled = false; + } + + // Create the rasterizer state + WGPURasterizationStateDescriptor raster_desc = {}; + { + raster_desc.cullMode = WGPUCullMode_None; + raster_desc.frontFace = WGPUFrontFace_CW; + raster_desc.depthBias = 0; + raster_desc.depthBiasClamp = 0; + raster_desc.depthBiasSlopeScale = 0; + graphics_pipeline_desc.rasterizationState = &raster_desc; + } + + // Create depth-stencil State + WGPUDepthStencilStateDescriptor depth_desc = {}; + { + // Configure disabled state + depth_desc.format = WGPUTextureFormat_Undefined; + + depth_desc.depthWriteEnabled = true; + depth_desc.depthCompare = WGPUCompareFunction_Always; + depth_desc.stencilReadMask = 0; + depth_desc.stencilWriteMask = 0; + depth_desc.stencilBack.compare = WGPUCompareFunction_Always; + depth_desc.stencilBack.failOp = WGPUStencilOperation_Keep; + depth_desc.stencilBack.depthFailOp = WGPUStencilOperation_Keep; + depth_desc.stencilBack.passOp = WGPUStencilOperation_Keep; + depth_desc.stencilFront.compare = WGPUCompareFunction_Always; + depth_desc.stencilFront.failOp = WGPUStencilOperation_Keep; + depth_desc.stencilFront.depthFailOp = WGPUStencilOperation_Keep; + depth_desc.stencilFront.passOp = WGPUStencilOperation_Keep; + + // No depth buffer corresponds to no configuration + graphics_pipeline_desc.depthStencilState = nullptr; + } + + g_pipelineState = wgpuDeviceCreateRenderPipeline(g_wgpuDevice, &graphics_pipeline_desc); + + ImGui_ImplWGPU_CreateFontsTexture(); + ImGui_ImplWGPU_CreateUniformBuffer(); + + // Create resource bind group + WGPUBindGroupEntry common_bg_entries[] = { + { 0, g_resources.Uniforms, 0, sizeof(Uniforms), 0, 0 }, + { 1, 0, 0, 0, g_resources.Sampler, 0 }, + }; + + WGPUBindGroupDescriptor common_bg_descriptor = {}; + common_bg_descriptor.layout = bg_layouts[0]; + common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry); + common_bg_descriptor.entries = common_bg_entries; + g_resources.CommonBindGroup = wgpuDeviceCreateBindGroup(g_wgpuDevice, &common_bg_descriptor); + g_resources.ImageBindGroupLayout = bg_layouts[1]; + + WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bg_layouts[1], g_resources.FontTextureView); + g_resources.ImageBindGroup = image_bind_group; + g_resources.ImageBindGroups.SetVoidPtr(ImHashData(&g_resources.FontTextureView, sizeof(ImTextureID)), image_bind_group); + + SafeRelease(vertex_shader_desc.module); + SafeRelease(pixel_shader_desc.module); + SafeRelease(bg_layouts[0]); + + return true; +} + +void ImGui_ImplWGPU_InvalidateDeviceObjects() +{ + if (!g_wgpuDevice) + return; + + SafeRelease(g_pipelineState); + SafeRelease(g_resources); + + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->TexID = NULL; // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + + for (unsigned int i = 0; i < g_numFramesInFlight; i++) + { + SafeRelease(g_pFrameResources[i]); + } +} + +bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format) +{ + // Setup back-end capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_webgpu"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + g_wgpuDevice = device; + g_renderTargetFormat = rt_format; + g_pFrameResources = new FrameResources[num_frames_in_flight]; + g_numFramesInFlight = num_frames_in_flight; + g_frameIndex = UINT_MAX; + + g_resources.FontTexture = NULL; + g_resources.FontTextureView = NULL; + g_resources.Sampler = NULL; + g_resources.Uniforms = NULL; + g_resources.CommonBindGroup = NULL; + g_resources.ImageBindGroupLayout = NULL; + g_resources.ImageBindGroups.Data.reserve(100); + g_resources.ImageBindGroup = NULL; + + // Create buffers with a default size (they will later be grown as needed) + for (int i = 0; i < num_frames_in_flight; i++) + { + FrameResources* fr = &g_pFrameResources[i]; + fr->IndexBuffer = NULL; + fr->VertexBuffer = NULL; + fr->IndexBufferHost = NULL; + fr->VertexBufferHost = NULL; + fr->IndexBufferSize = 10000; + fr->VertexBufferSize = 5000; + } + + return true; +} + +void ImGui_ImplWGPU_Shutdown() +{ + ImGui_ImplWGPU_InvalidateDeviceObjects(); + delete[] g_pFrameResources; + g_pFrameResources = NULL; + g_wgpuDevice = NULL; + g_numFramesInFlight = 0; + g_frameIndex = UINT_MAX; +} + +void ImGui_ImplWGPU_NewFrame() +{ + if (!g_pipelineState) + ImGui_ImplWGPU_CreateDeviceObjects(); +} diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h new file mode 100644 index 00000000..92365b5f --- /dev/null +++ b/backends/imgui_impl_wgpu.h @@ -0,0 +1,26 @@ +// dear imgui: Renderer for WebGPU +// This needs to be used along with a Platform Binding (e.g. GLFW) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API + +// WebGPU +#include + +// cmd_list is the command list that the implementation will use to render imgui draw lists. +IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format); +IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder); + +// Use if you want to reset your rendering device without losing Dear ImGui state. +IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects(); +IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects(); diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 2463ce79..099d71b8 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -68,6 +68,7 @@ List of Renderer Backends: imgui_impl_opengl2.cpp ; OpenGL 2 (legacy, fixed pipeline <- don't use with modern OpenGL context) imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2, OpenGL ES 3 (modern programmable pipeline) imgui_impl_vulkan.cpp ; Vulkan + imgui_impl_wgpu.cpp ; WebGPU List of high-level Frameworks Backends (combining Platform + Renderer): diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 209f3be1..f2a38393 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -94,6 +94,7 @@ Breaking Changes: confusion with Tables API. Keep redirection enums (will obsolete). (#125, #513, #913, #1204, #1444, #2142, #2707) - Renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature now applies to other data structures. (#2636) +- Backends: WebGPU: Added backend for WebGPU support in imgui_impl_wgpu [@bfierz] Other Changes: diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 932f4604..35d4bec3 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -98,6 +98,10 @@ Emcripten + SDL2 + OpenGL3+/ES2/ES3 example.
Note that other examples based on SDL or GLFW + OpenGL could easily be modified to work with Emscripten. We provide this to make the Emscripten differences obvious, and have them not pollute all other examples. +[example_emscripten_wgpu/](https://github.com/ocornut/imgui/blob/master/examples/example_emscripten_wgpu/)
+Emcripten + GLFW + WebGPU example.
+= main.cpp + imgui_impl_glfw.cpp + imgui_impl_wgpu.cpp + [example_glfw_metal/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_metal/)
GLFW (Mac) + Metal example.
= main.mm + imgui_impl_glfw.cpp + imgui_impl_metal.mm diff --git a/examples/example_emscripten_wgpu/Makefile b/examples/example_emscripten_wgpu/Makefile new file mode 100644 index 00000000..44e2b8be --- /dev/null +++ b/examples/example_emscripten_wgpu/Makefile @@ -0,0 +1,80 @@ +# +# Makefile to use with emscripten +# See https://emscripten.org/docs/getting_started/downloads.html +# for installation instructions. +# +# This Makefile assumes you have loaded emscripten's environment. +# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead) +# +# Running `make` will produce two files: +# - example_emscripten_wgpu.js +# - example_emscripten_wgpu.wasm +# +# All three are needed to run the demo. + +CC = emcc +CXX = em++ +EXE = example_emscripten_wgpu.js +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_wgpu.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) + +##--------------------------------------------------------------------- +## EMSCRIPTEN OPTIONS +##--------------------------------------------------------------------- + +EMS += -s USE_GLFW=3 -s USE_WEBGPU=1 -s WASM=1 +EMS += -s ALLOW_MEMORY_GROWTH=1 +EMS += -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=0 +EMS += -s ASSERTIONS=1 + +# Emscripten allows preloading a file or folder to be accessible at runtime. +# The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts" +# See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html +# (Default value is 0. Set to 1 to enable file-system and include the misc/fonts/ folder as part of the build.) +USE_FILE_SYSTEM ?= 0 +ifeq ($(USE_FILE_SYSTEM), 0) +EMS += -s NO_FILESYSTEM=1 -DIMGUI_DISABLE_FILE_FUNCTIONS +endif +ifeq ($(USE_FILE_SYSTEM), 1) +LDFLAGS += --no-heap-copy --preload-file ../../misc/fonts@/fonts +endif + +##--------------------------------------------------------------------- +## FINAL BUILD FLAGS +##--------------------------------------------------------------------- + +CPPFLAGS += -I$(IMGUI_DIR)/ -I$(IMGUI_DIR)/backends +#CPPFLAGS += -g +CPPFLAGS += -Wall -Wformat -Os +CPPFLAGS += $(EMS) +LIBS += $(EMS) +#LDFLAGS += --shell-file shell_minimal.html + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +%.o:../%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(EXE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(LIBS) $(LDFLAGS) + +clean: + rm -f $(EXE) $(OBJS) *.js *.wasm *.wasm.pre diff --git a/examples/example_emscripten_wgpu/README.md b/examples/example_emscripten_wgpu/README.md new file mode 100644 index 00000000..73fabddc --- /dev/null +++ b/examples/example_emscripten_wgpu/README.md @@ -0,0 +1,10 @@ + +# How to Build + +- You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions + +- Depending on your configuration, in Windows you may need to run `emsdk/emsdk_env.bat` in your console to access the Emscripten command-line tools. + +- Then build using `make` while in the `example_emscripten_wgpu/` directory. + +- Requires Emscripten 2.0.10 (December 2020) due to GLFW adaptations diff --git a/examples/example_emscripten_wgpu/example_emscripten_wgpu.html b/examples/example_emscripten_wgpu/example_emscripten_wgpu.html new file mode 100644 index 00000000..796844b6 --- /dev/null +++ b/examples/example_emscripten_wgpu/example_emscripten_wgpu.html @@ -0,0 +1,80 @@ + + + + + + Dear ImGui Emscripten example + + + + + + + diff --git a/examples/example_emscripten_wgpu/main.cpp b/examples/example_emscripten_wgpu/main.cpp new file mode 100644 index 00000000..4ff634cd --- /dev/null +++ b/examples/example_emscripten_wgpu/main.cpp @@ -0,0 +1,257 @@ +// Dear ImGui: standalone example application for Emscripten, using GLFW + WebGPU +// (Emscripten is a C++-to-javascript compiler, used to publish executables for the web. See https://emscripten.org/) +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +// This is mostly the same code as the SDL2 + OpenGL3 example, simply with the modifications needed to run on Emscripten. +// It is possible to combine both code into a single source file that will compile properly on Desktop and using Emscripten. +// See https://github.com/ocornut/imgui/pull/2492 as an example on how to do just that. + +#include "imgui.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_wgpu.h" +#include +#include +#include +#include +#include +#include +#include + +// Global WebGPU required states +static WGPUDevice wgpu_device = NULL; +static WGPUSurface wgpu_surface = NULL; +static WGPUSwapChain wgpu_swap_chain = NULL; + +static int wgpu_swap_chain_width = 0; +static int wgpu_swap_chain_height = 0; + +// States tracked across render frames +static bool show_demo_window = true; +static bool show_another_window = false; +static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + +// Forward declartions +bool init_wgpu(); +void main_loop(void* window); +void print_glfw_error(int error, const char* description); +void print_wgpu_error(WGPUErrorType error_type, const char* message, void*); + +int main(int, char**) +{ + glfwSetErrorCallback(print_glfw_error); + if (!glfwInit()) + return 1; + + // Make sure GLFW does not initialize any graphics context. + // This needs to be done explicitly later + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+WebGPU example", NULL, NULL); + if (!window) { + glfwTerminate(); + return 1; + } + + // Initialize the WebGPU environment + if (!init_wgpu()) { + if (window) + glfwDestroyWindow(window); + glfwTerminate(); + return 1; + } + glfwShowWindow(window); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file. + // You may manually call LoadIniSettingsFromMemory() to load settings from your own storage. + io.IniFilename = NULL; + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + // Setup Platform/Renderer backends + ImGui_ImplGlfw_InitForVulkan(window, true); + ImGui_ImplWGPU_Init(wgpu_device, 3, WGPUTextureFormat_RGBA8Unorm); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Read 'docs/FONTS.md' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + // - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details. + //io.Fonts->AddFontDefault(); +#ifndef IMGUI_DISABLE_FILE_FUNCTIONS + io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf", 15.0f); + //io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("fonts/ProggyTiny.ttf", 10.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != NULL); +#endif + + // This function will directly return and extit he main function. + // Make sure that no required objects get cleaned up. + // This way we can use the browsers 'requestAnimationFrame' to control the rendering. + emscripten_set_main_loop_arg(main_loop, window, 0, false); + + return 0; +} + +bool init_wgpu() +{ + wgpu_device = emscripten_webgpu_get_device(); + if (!wgpu_device) + return false; + + wgpuDeviceSetUncapturedErrorCallback(wgpu_device, print_wgpu_error, nullptr); + + // Use C++ wrapper due to malbehaviour in Emscripten. + // Some offset computation for wgpuInstanceCreateSurface in JavaScript + // seem to be inline with struct alignments in the C++ structure + wgpu::SurfaceDescriptorFromCanvasHTMLSelector html_surface_desc{}; + html_surface_desc.selector = "#canvas"; + + wgpu::SurfaceDescriptor surface_desc{}; + surface_desc.nextInChain = &html_surface_desc; + + // Use 'null' instance + wgpu::Instance instance{}; + wgpu_surface = instance.CreateSurface(&surface_desc).Release(); + + return true; +} + +void main_loop(void* window) +{ + glfwPollEvents(); + + int width, height; + glfwGetFramebufferSize((GLFWwindow*) window, &width, &height); + + // React to changes in screen size + if (width != wgpu_swap_chain_width && height != wgpu_swap_chain_height) + { + ImGui_ImplWGPU_InvalidateDeviceObjects(); + + if (wgpu_swap_chain) + wgpuSwapChainRelease(wgpu_swap_chain); + + wgpu_swap_chain_width = width; + wgpu_swap_chain_height = height; + + WGPUSwapChainDescriptor swap_chain_desc = {}; + swap_chain_desc.usage = WGPUTextureUsage_OutputAttachment; + swap_chain_desc.format = WGPUTextureFormat_RGBA8Unorm; + swap_chain_desc.width = width; + swap_chain_desc.height = height; + swap_chain_desc.presentMode = WGPUPresentMode_Fifo; + wgpu_swap_chain = wgpuDeviceCreateSwapChain(wgpu_device, wgpu_surface, &swap_chain_desc); + + ImGui_ImplWGPU_CreateDeviceObjects(); + } + + // Start the Dear ImGui frame + ImGui_ImplWGPU_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Render the generated ImGui frame + ImGui::Render(); + + WGPURenderPassColorAttachmentDescriptor color_attachments = {}; + color_attachments.loadOp = WGPULoadOp_Clear; + color_attachments.storeOp = WGPUStoreOp_Store; + color_attachments.clearColor = { clear_color.x, clear_color.y, clear_color.z, clear_color.w }; + color_attachments.attachment = wgpuSwapChainGetCurrentTextureView(wgpu_swap_chain); + WGPURenderPassDescriptor render_pass_desc = {}; + render_pass_desc.colorAttachmentCount = 1; + render_pass_desc.colorAttachments = &color_attachments; + render_pass_desc.depthStencilAttachment = nullptr; + + WGPUCommandEncoderDescriptor enc_desc = {}; + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(wgpu_device, &enc_desc); + + WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc); + ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass); + wgpuRenderPassEncoderEndPass(pass); + + WGPUCommandBufferDescriptor cmd_buffer_desc = {}; + WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc); + WGPUQueue queue = wgpuDeviceGetDefaultQueue(wgpu_device); + wgpuQueueSubmit(queue, 1, &cmd_buffer); +} + +void print_glfw_error(int error, const char* description) +{ + printf("Glfw Error %d: %s\n", error, description); +} + +void print_wgpu_error(WGPUErrorType error_type, const char* message, void*) +{ + const char* error_type_lbl = ""; + switch (error_type) { + case WGPUErrorType_Validation: + error_type_lbl = "Validation"; + break; + case WGPUErrorType_OutOfMemory: + error_type_lbl = "Out of memory"; + break; + case WGPUErrorType_Unknown: + error_type_lbl = "Unknown"; + break; + case WGPUErrorType_DeviceLost: + error_type_lbl = "Device lost"; + break; + default: + error_type_lbl = "Unknown"; + } + + printf("%s error: %s\n", error_type_lbl, message); +} From dff0044d4e04d4663919f7d57494e0f26e0eeac4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 28 Jan 2021 12:11:26 +0100 Subject: [PATCH 16/17] Backends, Examples: Added support for WebGPU and corresponding example. Amend 5853fbd (#3632) --- .gitignore | 1 + backends/imgui_impl_glfw.cpp | 10 +- backends/imgui_impl_glfw.h | 3 +- backends/imgui_impl_wgpu.cpp | 105 +++++++----------- backends/imgui_impl_wgpu.h | 6 +- docs/BACKENDS.md | 30 +---- docs/CHANGELOG.txt | 7 +- docs/README.md | 4 +- examples/example_emscripten_opengl3/main.cpp | 4 +- examples/example_emscripten_wgpu/Makefile | 29 +++-- examples/example_emscripten_wgpu/main.cpp | 68 +++++------- .../index.html} | 4 +- 12 files changed, 114 insertions(+), 157 deletions(-) rename examples/example_emscripten_wgpu/{example_emscripten_wgpu.html => web/index.html} (96%) diff --git a/.gitignore b/.gitignore index d1fdd53e..8d720199 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ examples/*.o.tmp examples/*.out.js examples/*.out.wasm examples/example_emscripten_opengl3/web/* +examples/example_emscripten_wgpu/web/* ## JetBrains IDE artifacts .idea diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 74a266e1..0b9ac216 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -1,5 +1,5 @@ // dear imgui: Platform Backend for GLFW -// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (Requires: GLFW 3.1+) @@ -63,7 +63,8 @@ enum GlfwClientApi { GlfwClientApi_Unknown, GlfwClientApi_OpenGL, - GlfwClientApi_Vulkan + GlfwClientApi_Vulkan, + GlfwClientApi_WebGPU }; static GLFWwindow* g_Window = NULL; // Main window static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; @@ -231,6 +232,11 @@ bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); } +bool ImGui_ImplGlfw_InitForWebGPU(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_WebGPU); +} + void ImGui_ImplGlfw_Shutdown() { if (g_InstalledCallbacks) diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 6abb4056..f04ba728 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -1,5 +1,5 @@ // dear imgui: Platform Backend for GLFW -// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // Implemented features: @@ -23,6 +23,7 @@ struct GLFWwindow; IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForWebGPU(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 23605dd6..b18a5345 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -1,5 +1,6 @@ // dear imgui: Renderer for WebGPU // This needs to be used along with a Platform Binding (e.g. GLFW) +// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.) // Implemented features: // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! @@ -11,51 +12,33 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-28: Initial version. #include "imgui.h" #include "imgui_impl_wgpu.h" - -// CRT #include - -// WebGPU #include -// ImGui prototypes -ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0); +// Dear ImGui prototypes from imgui_internal.h +extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0); // WebGPU data -static WGPUDevice g_wgpuDevice = NULL; -static WGPUTextureFormat g_renderTargetFormat = WGPUTextureFormat_Undefined; -static WGPURenderPipeline g_pipelineState = NULL; +static WGPUDevice g_wgpuDevice = NULL; +static WGPUTextureFormat g_renderTargetFormat = WGPUTextureFormat_Undefined; +static WGPURenderPipeline g_pipelineState = NULL; struct RenderResources { - // Font texture - WGPUTexture FontTexture; - - // Texture view for font texture - WGPUTextureView FontTextureView; - - // Sampler for the font texture - WGPUSampler Sampler; - - // Shader uniforms - WGPUBuffer Uniforms; - - // Resources bind-group to bind the common resources to pipeline - WGPUBindGroup CommonBindGroup; - - // Bind group layout for image textures - WGPUBindGroupLayout ImageBindGroupLayout; - - // Resources bind-group to bind the font/image resources to pipeline - ImGuiStorage ImageBindGroups; - - // Default font-resource of ImGui - WGPUBindGroup ImageBindGroup; + WGPUTexture FontTexture; // Font texture + WGPUTextureView FontTextureView; // Texture view for font texture + WGPUSampler Sampler; // Sampler for the font texture + WGPUBuffer Uniforms; // Shader uniforms + WGPUBindGroup CommonBindGroup; // Resources bind-group to bind the common resources to pipeline + WGPUBindGroupLayout ImageBindGroupLayout; // Bind group layout for image textures + ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map) + WGPUBindGroup ImageBindGroup; // Default font-resource of Dear ImGui }; -static RenderResources g_resources; +static RenderResources g_resources; struct FrameResources { @@ -285,9 +268,7 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(uint32_ static WGPUBindGroup ImGui_ImplWGPU_CreateImageBindGroup(WGPUBindGroupLayout layout, WGPUTextureView texture) { - WGPUBindGroupEntry image_bg_entries[] = { - { 0, 0, 0, 0, 0, texture }, - }; + WGPUBindGroupEntry image_bg_entries[] = { { 0, 0, 0, 0, 0, texture } }; WGPUBindGroupDescriptor image_bg_descriptor = {}; image_bg_descriptor.layout = layout; @@ -339,7 +320,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - // FIXME: I'm assuming that this only gets called once per frame! + // FIXME: Assuming that this only gets called once per frame! // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. g_frameIndex = g_frameIndex + 1; FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight]; @@ -351,9 +332,10 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder SafeRelease(fr->VertexBufferHost); fr->VertexBufferSize = draw_data->TotalVtxCount + 5000; - WGPUBufferDescriptor vb_desc = { - nullptr, - "IMGUI Vertex buffer", + WGPUBufferDescriptor vb_desc = + { + NULL, + "Dear ImGui Vertex buffer", WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, fr->VertexBufferSize * sizeof(ImDrawVert), false @@ -370,9 +352,10 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder SafeRelease(fr->IndexBufferHost); fr->IndexBufferSize = draw_data->TotalIdxCount + 10000; - WGPUBufferDescriptor ib_desc = { - nullptr, - "IMGUI Index buffer", + WGPUBufferDescriptor ib_desc = + { + NULL, + "Dear ImGui Index buffer", WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, fr->IndexBufferSize * sizeof(ImDrawIdx), false @@ -427,13 +410,14 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder { // Bind custom texture auto bind_group = g_resources.ImageBindGroups.GetVoidPtr(ImHashData(&pcmd->TextureId, sizeof(ImTextureID))); - if (bind_group) { + if (bind_group) + { wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, NULL); } - else { - WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(g_resources.ImageBindGroupLayout, (WGPUTextureView) pcmd->TextureId); + else + { + WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(g_resources.ImageBindGroupLayout, (WGPUTextureView)pcmd->TextureId); g_resources.ImageBindGroups.SetVoidPtr(ImHashData(&pcmd->TextureId, sizeof(ImTextureID)), image_bind_group); - wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, image_bind_group, 0, NULL); } @@ -464,7 +448,6 @@ static WGPUBuffer ImGui_ImplWGPU_CreateBufferFromData(const WGPUDevice& device, return buffer; } - static void ImGui_ImplWGPU_CreateFontsTexture() { // Build texture atlas @@ -476,7 +459,7 @@ static void ImGui_ImplWGPU_CreateFontsTexture() // Upload texture to graphics system { WGPUTextureDescriptor tex_desc = {}; - tex_desc.label = "IMGUI Font Texture"; + tex_desc.label = "Dear ImGui Font Texture"; tex_desc.dimension = WGPUTextureDimension_2D; tex_desc.size.width = width; tex_desc.size.height = height; @@ -500,7 +483,7 @@ static void ImGui_ImplWGPU_CreateFontsTexture() // Upload texture data { - WGPUBuffer staging_buffer = ImGui_ImplWGPU_CreateBufferFromData(g_wgpuDevice, pixels, static_cast(width * size_pp * height), WGPUBufferUsage_CopySrc); + WGPUBuffer staging_buffer = ImGui_ImplWGPU_CreateBufferFromData(g_wgpuDevice, pixels, (uint32_t)(width * size_pp * height), WGPUBufferUsage_CopySrc); WGPUBufferCopyView bufferCopyView = {}; bufferCopyView.buffer = staging_buffer; @@ -516,7 +499,7 @@ static void ImGui_ImplWGPU_CreateFontsTexture() textureCopyView.aspect = WGPUTextureAspect_All; #endif - WGPUExtent3D copySize = { static_cast(width), static_cast(height), 1 }; + WGPUExtent3D copySize = { (uint32_t)width, (uint32_t)height, 1 }; WGPUCommandEncoderDescriptor enc_desc = {}; WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(g_wgpuDevice, &enc_desc); @@ -549,9 +532,10 @@ static void ImGui_ImplWGPU_CreateFontsTexture() static void ImGui_ImplWGPU_CreateUniformBuffer() { - WGPUBufferDescriptor ub_desc = { - nullptr, - "IMGUI Uniform buffer", + WGPUBufferDescriptor ub_desc = + { + NULL, + "Dear ImGui Uniform buffer", WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, sizeof(Uniforms), false @@ -568,12 +552,10 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() // Create render pipeline WGPURenderPipelineDescriptor graphics_pipeline_desc = {}; - graphics_pipeline_desc.primitiveTopology = WGPUPrimitiveTopology_TriangleList; graphics_pipeline_desc.sampleCount = 1; graphics_pipeline_desc.sampleMask = UINT_MAX; - WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {}; common_bg_layout_entries[0].binding = 0; common_bg_layout_entries[0].visibility = WGPUShaderStage_Vertex; @@ -609,7 +591,8 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() graphics_pipeline_desc.vertexStage = vertex_shader_desc; // Vertex input configuration - WGPUVertexAttributeDescriptor attribute_binding_desc[] = { + WGPUVertexAttributeDescriptor attribute_binding_desc[] = + { { WGPUVertexFormat_Float2, (uint64_t)IM_OFFSETOF(ImDrawVert, pos), 0 }, { WGPUVertexFormat_Float2, (uint64_t)IM_OFFSETOF(ImDrawVert, uv), 1 }, { WGPUVertexFormat_UChar4Norm, (uint64_t)IM_OFFSETOF(ImDrawVert, col), 2 }, @@ -665,7 +648,6 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() { // Configure disabled state depth_desc.format = WGPUTextureFormat_Undefined; - depth_desc.depthWriteEnabled = true; depth_desc.depthCompare = WGPUCompareFunction_Always; depth_desc.stencilReadMask = 0; @@ -680,7 +662,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() depth_desc.stencilFront.passOp = WGPUStencilOperation_Keep; // No depth buffer corresponds to no configuration - graphics_pipeline_desc.depthStencilState = nullptr; + graphics_pipeline_desc.depthStencilState = NULL; } g_pipelineState = wgpuDeviceCreateRenderPipeline(g_wgpuDevice, &graphics_pipeline_desc); @@ -689,7 +671,8 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() ImGui_ImplWGPU_CreateUniformBuffer(); // Create resource bind group - WGPUBindGroupEntry common_bg_entries[] = { + WGPUBindGroupEntry common_bg_entries[] = + { { 0, g_resources.Uniforms, 0, sizeof(Uniforms), 0, 0 }, { 1, 0, 0, 0, g_resources.Sampler, 0 }, }; @@ -724,9 +707,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects() io.Fonts->TexID = NULL; // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. for (unsigned int i = 0; i < g_numFramesInFlight; i++) - { SafeRelease(g_pFrameResources[i]); - } } bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format) diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index 92365b5f..f6c61528 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -1,5 +1,6 @@ // dear imgui: Renderer for WebGPU // This needs to be used along with a Platform Binding (e.g. GLFW) +// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.) // Implemented features: // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! @@ -10,12 +11,9 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs #pragma once -#include "imgui.h" // IMGUI_IMPL_API - -// WebGPU +#include "imgui.h" // IMGUI_IMPL_API #include -// cmd_list is the command list that the implementation will use to render imgui draw lists. IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format); IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame(); diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 099d71b8..7a723cc7 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -80,35 +80,7 @@ The [example_emscripten_opengl3](https://github.com/ocornut/imgui/tree/master/ex ### Backends for third-party frameworks, graphics API or other languages -See https://github.com/ocornut/imgui/wiki/Bindings -- AGS/Adventure Game Studio -- Amethyst -- bsf -- Cinder -- Cocos2d-x -- Diligent Engine -- Flexium, -- GML/Game Maker Studio2 -- GTK3+OpenGL3 -- Irrlicht Engine -- LÖVE+LUA -- Magnum -- NanoRT -- Nim Game Lib, -- Ogre -- openFrameworks -- OSG/OpenSceneGraph -- Orx -- px_render -- Qt/QtDirect3D -- SFML -- Sokol -- Unity -- Unreal Engine 4 -- vtk -- Win32 GDI -etc. - +See https://github.com/ocornut/imgui/wiki/Bindings for the full list. ### Recommended Backends diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f2a38393..3e51e2dd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -59,6 +59,12 @@ Other Changes: User needs to call ImGui_ImplVulkan_LoadFunctions() with their custom loader prior to other functions. - Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) [@Belinsky-L-V] - Backends: OSX: Fixed mouse position not being reported when mouse buttons other than left one are down. (#3762) [@rokups] +- Backends: WebGPU: Added enderer backend for WebGPU support (imgui_impl_wgpu.cpp) (#3632) [@bfierz] + Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break. +- Examples: WebGPU: Added Emscripten+WebGPU example. (#3632) [@bfierz] +- Backends: GLFW: Added ImGui_ImplGlfw_InitForWebGPU() init point. It currently has strictly no effect on anything, + but because some multi-viewport renderers require knowledge of the render stack in the Platform back-end, we're + adding it for consistency. (#3632) ----------------------------------------------------------------------- @@ -94,7 +100,6 @@ Breaking Changes: confusion with Tables API. Keep redirection enums (will obsolete). (#125, #513, #913, #1204, #1444, #2142, #2707) - Renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature now applies to other data structures. (#2636) -- Backends: WebGPU: Added backend for WebGPU support in imgui_impl_wgpu [@bfierz] Other Changes: diff --git a/docs/README.md b/docs/README.md index 7c088556..7c8ea360 100644 --- a/docs/README.md +++ b/docs/README.md @@ -115,13 +115,13 @@ On most platforms and when using C++, **you should be able to use a combination Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading one texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that. If you are an experienced programmer at ease with those concepts, it should take you less than two hours to integrate Dear ImGui in your custom engine. **Make sure to spend time reading the [FAQ](https://www.dearimgui.org/faq), comments, and some of the examples/ application!** Officially maintained backends/bindings (in repository): -- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, OpenGL (legacy), OpenGL3/ES/ES2 (modern), Vulkan, Metal. +- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL (legacy), OpenGL3/ES/ES2 (modern), Vulkan, WebGPU. - Platforms: GLFW, SDL2, Win32, Glut, OSX. - Frameworks: Emscripten, Allegro5, Marmalade. [Third-party backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) wiki page: - Languages: C, C# and: Beef, ChaiScript, Crystal, D, Go, Haskell, Haxe/hxcpp, Java, JavaScript, Julia, Kotlin, Lobster, Lua, Odin, Pascal, PureBasic, Python, Ruby, Rust, Swift... -- Frameworks: AGS/Adventure Game Studio, Amethyst, bsf, Cinder, Cocos2d-x, Diligent Engine, Flexium, GML/Game Maker Studio2, Godot, GTK3+OpenGL3, Irrlicht Engine, LÖVE+LUA, Magnum, NanoRT, nCine, Nim Game Lib, Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, SFML, Sokol, Unity, Unreal Engine 4, vtk, Win32 GDI, WxWidgets. +- Frameworks: AGS/Adventure Game Studio, Amethyst, Blender, bsf, Cinder, Cocos2d-x, Diligent Engine, Flexium, GML/Game Maker Studio2, GLEQ, Godot, GTK3+OpenGL3, Irrlicht Engine, LÖVE+LUA, Magnum, Monogame, NanoRT, nCine, Nim Game Lib, Nintendo 3DS & Switch (homebrew), Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, SDL_Renderer, SFML, Sokol, Unity, Unreal Engine 4, vtk, VulkanHpp, VulkanSceneGraph, Win32 GDI, WxWidgets. - Note that C bindings ([cimgui](https://github.com/cimgui/cimgui)) are auto-generated, you can use its json/lua output to generate bindings for other languages. [Useful widgets and extensions](https://github.com/ocornut/imgui/wiki/Useful-Widgets) wiki page: diff --git a/examples/example_emscripten_opengl3/main.cpp b/examples/example_emscripten_opengl3/main.cpp index 13f90ede..0c926660 100644 --- a/examples/example_emscripten_opengl3/main.cpp +++ b/examples/example_emscripten_opengl3/main.cpp @@ -21,7 +21,7 @@ SDL_Window* g_Window = NULL; SDL_GLContext g_GLContext = NULL; // For clarity, our main loop code is declared at the end. -void main_loop(void*); +static void main_loop(void*); int main(int, char**) { @@ -99,7 +99,7 @@ int main(int, char**) emscripten_set_main_loop_arg(main_loop, NULL, 0, true); } -void main_loop(void* arg) +static void main_loop(void* arg) { ImGuiIO& io = ImGui::GetIO(); IM_UNUSED(arg); // We can pass this argument as the second parameter of emscripten_set_main_loop_arg(), but we don't use that. diff --git a/examples/example_emscripten_wgpu/Makefile b/examples/example_emscripten_wgpu/Makefile index 44e2b8be..de1792fe 100644 --- a/examples/example_emscripten_wgpu/Makefile +++ b/examples/example_emscripten_wgpu/Makefile @@ -6,18 +6,20 @@ # This Makefile assumes you have loaded emscripten's environment. # (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead) # -# Running `make` will produce two files: -# - example_emscripten_wgpu.js -# - example_emscripten_wgpu.wasm +# Running `make` will produce three files: +# - web/index.html (current stored in the repository) +# - web/index.js +# - web/index.wasm # # All three are needed to run the demo. CC = emcc CXX = em++ -EXE = example_emscripten_wgpu.js +WEB_DIR = web +EXE = $(WEB_DIR)/index.js IMGUI_DIR = ../.. SOURCES = main.cpp -SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_wgpu.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) @@ -47,7 +49,7 @@ endif ## FINAL BUILD FLAGS ##--------------------------------------------------------------------- -CPPFLAGS += -I$(IMGUI_DIR)/ -I$(IMGUI_DIR)/backends +CPPFLAGS = -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends #CPPFLAGS += -g CPPFLAGS += -Wall -Wformat -Os CPPFLAGS += $(EMS) @@ -61,9 +63,6 @@ LIBS += $(EMS) %.o:%.cpp $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< -%.o:../%.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< - %.o:$(IMGUI_DIR)/%.cpp $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< @@ -73,8 +72,14 @@ LIBS += $(EMS) all: $(EXE) @echo Build complete for $(EXE) -$(EXE): $(OBJS) - $(CXX) -o $@ $^ $(LIBS) $(LDFLAGS) +$(WEB_DIR): + mkdir $@ + +serve: all + python3 -m http.server -d $(WEB_DIR) + +$(EXE): $(OBJS) $(WEB_DIR) + $(CXX) -o $@ $(OBJS) $(LIBS) $(LDFLAGS) clean: - rm -f $(EXE) $(OBJS) *.js *.wasm *.wasm.pre + rm -f $(EXE) $(OBJS) $(WEB_DIR)/*.js $(WEB_DIR)/*.wasm $(WEB_DIR)/*.wasm.pre diff --git a/examples/example_emscripten_wgpu/main.cpp b/examples/example_emscripten_wgpu/main.cpp index 4ff634cd..94f20142 100644 --- a/examples/example_emscripten_wgpu/main.cpp +++ b/examples/example_emscripten_wgpu/main.cpp @@ -3,10 +3,6 @@ // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs -// This is mostly the same code as the SDL2 + OpenGL3 example, simply with the modifications needed to run on Emscripten. -// It is possible to combine both code into a single source file that will compile properly on Desktop and using Emscripten. -// See https://github.com/ocornut/imgui/pull/2492 as an example on how to do just that. - #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_wgpu.h" @@ -22,7 +18,6 @@ static WGPUDevice wgpu_device = NULL; static WGPUSurface wgpu_surface = NULL; static WGPUSwapChain wgpu_swap_chain = NULL; - static int wgpu_swap_chain_width = 0; static int wgpu_swap_chain_height = 0; @@ -31,11 +26,11 @@ static bool show_demo_window = true; static bool show_another_window = false; static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); -// Forward declartions -bool init_wgpu(); -void main_loop(void* window); -void print_glfw_error(int error, const char* description); -void print_wgpu_error(WGPUErrorType error_type, const char* message, void*); +// Forward declarations +static bool init_wgpu(); +static void main_loop(void* window); +static void print_glfw_error(int error, const char* description); +static void print_wgpu_error(WGPUErrorType error_type, const char* message, void*); int main(int, char**) { @@ -48,13 +43,15 @@ int main(int, char**) glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+WebGPU example", NULL, NULL); - if (!window) { + if (!window) + { glfwTerminate(); return 1; } // Initialize the WebGPU environment - if (!init_wgpu()) { + if (!init_wgpu()) + { if (window) glfwDestroyWindow(window); glfwTerminate(); @@ -99,7 +96,7 @@ int main(int, char**) //IM_ASSERT(font != NULL); #endif - // This function will directly return and extit he main function. + // This function will directly return and exit the main function. // Make sure that no required objects get cleaned up. // This way we can use the browsers 'requestAnimationFrame' to control the rendering. emscripten_set_main_loop_arg(main_loop, window, 0, false); @@ -107,31 +104,31 @@ int main(int, char**) return 0; } -bool init_wgpu() +static bool init_wgpu() { wgpu_device = emscripten_webgpu_get_device(); if (!wgpu_device) return false; - wgpuDeviceSetUncapturedErrorCallback(wgpu_device, print_wgpu_error, nullptr); + wgpuDeviceSetUncapturedErrorCallback(wgpu_device, print_wgpu_error, NULL); - // Use C++ wrapper due to malbehaviour in Emscripten. + // Use C++ wrapper due to misbehavior in Emscripten. // Some offset computation for wgpuInstanceCreateSurface in JavaScript // seem to be inline with struct alignments in the C++ structure - wgpu::SurfaceDescriptorFromCanvasHTMLSelector html_surface_desc{}; + wgpu::SurfaceDescriptorFromCanvasHTMLSelector html_surface_desc = {}; html_surface_desc.selector = "#canvas"; - wgpu::SurfaceDescriptor surface_desc{}; + wgpu::SurfaceDescriptor surface_desc = {}; surface_desc.nextInChain = &html_surface_desc; // Use 'null' instance - wgpu::Instance instance{}; + wgpu::Instance instance = {}; wgpu_surface = instance.CreateSurface(&surface_desc).Release(); return true; } -void main_loop(void* window) +static void main_loop(void* window) { glfwPollEvents(); @@ -202,7 +199,7 @@ void main_loop(void* window) ImGui::End(); } - // Render the generated ImGui frame + // Rendering ImGui::Render(); WGPURenderPassColorAttachmentDescriptor color_attachments = {}; @@ -213,7 +210,7 @@ void main_loop(void* window) WGPURenderPassDescriptor render_pass_desc = {}; render_pass_desc.colorAttachmentCount = 1; render_pass_desc.colorAttachments = &color_attachments; - render_pass_desc.depthStencilAttachment = nullptr; + render_pass_desc.depthStencilAttachment = NULL; WGPUCommandEncoderDescriptor enc_desc = {}; WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(wgpu_device, &enc_desc); @@ -228,30 +225,21 @@ void main_loop(void* window) wgpuQueueSubmit(queue, 1, &cmd_buffer); } -void print_glfw_error(int error, const char* description) +static void print_glfw_error(int error, const char* description) { printf("Glfw Error %d: %s\n", error, description); } -void print_wgpu_error(WGPUErrorType error_type, const char* message, void*) +static void print_wgpu_error(WGPUErrorType error_type, const char* message, void*) { const char* error_type_lbl = ""; - switch (error_type) { - case WGPUErrorType_Validation: - error_type_lbl = "Validation"; - break; - case WGPUErrorType_OutOfMemory: - error_type_lbl = "Out of memory"; - break; - case WGPUErrorType_Unknown: - error_type_lbl = "Unknown"; - break; - case WGPUErrorType_DeviceLost: - error_type_lbl = "Device lost"; - break; - default: - error_type_lbl = "Unknown"; + switch (error_type) + { + case WGPUErrorType_Validation: error_type_lbl = "Validation"; break; + case WGPUErrorType_OutOfMemory: error_type_lbl = "Out of memory"; break; + case WGPUErrorType_Unknown: error_type_lbl = "Unknown"; break; + case WGPUErrorType_DeviceLost: error_type_lbl = "Device lost"; break; + default: error_type_lbl = "Unknown"; } - printf("%s error: %s\n", error_type_lbl, message); } diff --git a/examples/example_emscripten_wgpu/example_emscripten_wgpu.html b/examples/example_emscripten_wgpu/web/index.html similarity index 96% rename from examples/example_emscripten_wgpu/example_emscripten_wgpu.html rename to examples/example_emscripten_wgpu/web/index.html index 796844b6..82b1c422 100644 --- a/examples/example_emscripten_wgpu/example_emscripten_wgpu.html +++ b/examples/example_emscripten_wgpu/web/index.html @@ -3,7 +3,7 @@ - Dear ImGui Emscripten example + Dear ImGui Emscripten+WebGPU example