From 30224fdde0e8846d38ca175b4355fb3c369b56c5 Mon Sep 17 00:00:00 2001 From: rokups Date: Fri, 25 Jun 2021 11:43:30 +0300 Subject: [PATCH 1/9] Examples: example_apple_opengl2: Fix keys remaining stuck in pressed state when using shortcuts with CMD. (#4253) Fix follows event capture scheme of example_apple_metal, where this issue is not present. --- docs/CHANGELOG.txt | 2 ++ examples/example_apple_opengl2/main.mm | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fb6304db..a6d6cbd3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -60,6 +60,8 @@ Other Changes: - Backends: OpenGL3: Handle GL_CLIP_ORIGIN on <4.5 contexts if "GL_ARB_clip_control" extension is detected. (#4170, #3998) - Backends: OpenGL3: Destroy vertex/fragment shader objects right after they are linked into main shader. (#4244) [@Crowbarous] - Backends: OSX: Added a fix for shortcuts using CTRL key instead of CMD key. (#4253) [@rokups] +- Examples: OSX+OpenGL2: Fix event forwarding (fix key remaining stuck when using shortcuts with Cmd/Super key). + Other OSX examples were not affected. (#4253, #1873) [@rokups] - Examples: Updated all .vcxproj to VS2015 (toolset v140) to facilitate usage with vcpkg. - Examples: SDL2: Accomodate for vcpkg install having headers in SDL2/SDL.h vs SDL.h. diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 9af396b6..75484ec0 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -140,9 +140,6 @@ } // Forward Mouse/Keyboard events to Dear ImGui OSX backend. --(void)keyUp:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } --(void)keyDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } --(void)flagsChanged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)mouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)rightMouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)otherMouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } @@ -155,7 +152,6 @@ -(void)mouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)rightMouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)otherMouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } --(void)scrollWheel:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } @end @@ -243,6 +239,15 @@ if ([view openGLContext] == nil) NSLog(@"No OpenGL Context!"); + // Some events do not raise callbacks of ImGuiExampleView in some circumstances (for example when CMD key is held down). + // This monitor taps into global event stream and captures these events. + NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged | NSEventTypeScrollWheel; + [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) + { + ImGui_ImplOSX_HandleEvent(event, view); + return event; + }]; + // Setup Dear ImGui context // FIXME: This example doesn't have proper cleanup... IMGUI_CHECKVERSION(); From 8ced41570e4692b0d556503683ae45f92416f5f1 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 25 Jun 2021 22:06:38 +1200 Subject: [PATCH 2/9] Backends: DX9: explicitely disable texture state stages after >= 1. (#4268) imgui only uses texture stage state for stage 0 But any state set in stages >= 1 will affect rendering Set stage 1 COLOROP to DISABLED, to deactivate stages >= 1 --- backends/imgui_impl_dx9.cpp | 3 +++ docs/CHANGELOG.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index fd20e6b6..959b9398 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -12,6 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-25: DirectX9: Explicitly disable texture state stages after >= 1. // 2021-05-19: DirectX9: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states. // 2021-03-18: DirectX9: Calling IDirect3DStateBlock9::Capture() after CreateStateBlock() as a workaround for state restoring issues (see #3857). @@ -95,6 +96,8 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + g_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a6d6cbd3..a02d7084 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,7 @@ Other Changes: - Backends: Win32: Rework to handle certains Windows 8.1/10 features without a manifest. (#4200, #4191) - ImGui_ImplWin32_GetDpiScaleForMonitor() will handle per-monitor DPI on Windows 10 without a manifest. - ImGui_ImplWin32_EnableDpiAwareness() will call SetProcessDpiAwareness() fallback on Windows 8.1 without a manifest. +- Backends: DX9: Explicitly disable texture state stages after >= 1. (#4268) [@NZJenkins] - Backends: DX12: Fix texture casting crash on 32-bit systems (introduced on 2021/05/19 and v1.83) + added comments about building on 32-bit systems. (#4225) [@kingofthebongo2008] - Backends: OpenGL3: Handle GL_CLIP_ORIGIN on <4.5 contexts if "GL_ARB_clip_control" extension is detected. (#4170, #3998) From 38165f420f235357516c9fbc79f4713c50d65048 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 25 Jun 2021 15:44:14 +0200 Subject: [PATCH 3/9] Fonts: Prefer using U+FFFD character for fallback instead of '?'. Use U+FF0E dot character to construct an ellipsis if U+002E '.' is not available. (#4269) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- imgui.h | 3 ++- imgui_draw.cpp | 54 +++++++++++++++++++++++++++++----------------- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a02d7084..736cc5bf 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,8 @@ Other Changes: - TabBar: Fixed using more than 32 KB-worth of tab names. (#4176) - Fixed printf-style format checks on non-MinGW flavors. (#4183, #3592) - Fonts: Functions with a 'float size_pixels' parameter can accept zero if it is set in ImFontSize::SizePixels. +- Fonts: Prefer using U+FFFD character for fallback instead of '?', if available. (#4269) +- Fonts: Use U+FF0E dot character to construct an ellipsis if U+002E '.' is not available. (#4269) - Demo: Fixed requirement in 1.83 to link with imgui_demo.cpp if IMGUI_DISABLE_METRICS_WINDOW is not set. (#4171) Normally the right way to disable compiling the demo is to set IMGUI_DISABLE_DEMO_WINDOWS, but we want to avoid implying that the file is required. diff --git a/imgui.cpp b/imgui.cpp index f381c673..daf21721 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2786,7 +2786,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con int ellipsis_char_count = 1; if (ellipsis_char == (ImWchar)-1) { - ellipsis_char = (ImWchar)'.'; + ellipsis_char = font->DotChar; ellipsis_char_count = 3; } const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char); diff --git a/imgui.h b/imgui.h index 2166d659..5596eb56 100644 --- a/imgui.h +++ b/imgui.h @@ -2709,7 +2709,8 @@ struct ImFont const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. ImWchar FallbackChar; // 2 // in // = '?' // Replacement character if a glyph isn't found. Only set via SetFallbackChar() - ImWchar EllipsisChar; // 2 // out // = -1 // Character used for ellipsis rendering. + ImWchar EllipsisChar; // 2 // out // = '...' // Character used for ellipsis rendering. + ImWchar DotChar; // 2 // out // = '.' // Dot character used for fallback ellipsis rendering. bool DirtyLookupTables; // 1 // out // float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] diff --git a/imgui_draw.cpp b/imgui_draw.cpp index a026ce4b..b0e3f777 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2795,23 +2795,6 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) for (int i = 0; i < atlas->Fonts.Size; i++) if (atlas->Fonts[i]->DirtyLookupTables) atlas->Fonts[i]->BuildLookupTable(); - - // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). - // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. - // FIXME: Also note that 0x2026 is currently seldom included in our font ranges. Because of this we are more likely to use three individual dots. - for (int i = 0; i < atlas->Fonts.size(); i++) - { - ImFont* font = atlas->Fonts[i]; - if (font->EllipsisChar != (ImWchar)-1) - continue; - const ImWchar ellipsis_variants[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; - for (int j = 0; j < IM_ARRAYSIZE(ellipsis_variants); j++) - if (font->FindGlyphNoFallback(ellipsis_variants[j]) != NULL) // Verify glyph exists - { - font->EllipsisChar = ellipsis_variants[j]; - break; - } - } } // Retrieve list of range (2 int per range, values are inclusive) @@ -3110,8 +3093,9 @@ ImFont::ImFont() { FontSize = 0.0f; FallbackAdvanceX = 0.0f; - FallbackChar = (ImWchar)'?'; + FallbackChar = (ImWchar)-1; EllipsisChar = (ImWchar)-1; + DotChar = (ImWchar)-1; FallbackGlyph = NULL; ContainerAtlas = NULL; ConfigData = NULL; @@ -3142,6 +3126,14 @@ void ImFont::ClearOutputData() MetricsTotalSurface = 0; } +static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count) +{ + for (int n = 0; n < candidate_chars_count; n++) + if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL) + return candidate_chars[n]; + return (ImWchar)-1; +} + void ImFont::BuildLookupTable() { int max_codepoint = 0; @@ -3184,9 +3176,31 @@ void ImFont::BuildLookupTable() SetGlyphVisible((ImWchar)' ', false); SetGlyphVisible((ImWchar)'\t', false); - // Setup fall-backs + // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). + // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. + // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots. + const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; + const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E }; + if (EllipsisChar == (ImWchar)-1) + EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars)); + if (DotChar == (ImWchar)-1) + DotChar = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars)); + + // Setup fallback character + const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' }; FallbackGlyph = FindGlyphNoFallback(FallbackChar); - FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f; + if (FallbackGlyph == NULL) + { + FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars)); + FallbackGlyph = FindGlyphNoFallback(FallbackChar); + if (FallbackGlyph == NULL) + { + FallbackGlyph = &Glyphs.back(); + FallbackChar = FallbackGlyph->Codepoint; + } + } + + FallbackAdvanceX = FallbackGlyph->AdvanceX; for (int i = 0; i < max_codepoint + 1; i++) if (IndexAdvanceX[i] < 0.0f) IndexAdvanceX[i] = FallbackAdvanceX; From 9440974f66422d63b2ee6c6288e942ecabd6a521 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 25 Jun 2021 15:47:42 +0200 Subject: [PATCH 4/9] Fonts: Add U+FFFD ("replacement character") to default asian glyphs ranges. Remove SetFallbackChar() (amend 0ffd99d31). (#4269) --- docs/CHANGELOG.txt | 1 + imgui.h | 5 ++--- imgui_draw.cpp | 14 ++++++-------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 736cc5bf..0cd04241 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,6 +51,7 @@ Other Changes: - Fonts: Functions with a 'float size_pixels' parameter can accept zero if it is set in ImFontSize::SizePixels. - Fonts: Prefer using U+FFFD character for fallback instead of '?', if available. (#4269) - Fonts: Use U+FF0E dot character to construct an ellipsis if U+002E '.' is not available. (#4269) +- Fonts: Add U+FFFD ("replacement character") to default asian glyphs ranges. (#4269) - Demo: Fixed requirement in 1.83 to link with imgui_demo.cpp if IMGUI_DISABLE_METRICS_WINDOW is not set. (#4171) Normally the right way to disable compiling the demo is to set IMGUI_DISABLE_DEMO_WINDOWS, but we want to avoid implying that the file is required. diff --git a/imgui.h b/imgui.h index 5596eb56..9eeabbe9 100644 --- a/imgui.h +++ b/imgui.h @@ -2708,9 +2708,9 @@ struct ImFont ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. - ImWchar FallbackChar; // 2 // in // = '?' // Replacement character if a glyph isn't found. Only set via SetFallbackChar() + ImWchar FallbackChar; // 2 // out // = FFFD/'?' // Character used if a glyph isn't found. ImWchar EllipsisChar; // 2 // out // = '...' // Character used for ellipsis rendering. - ImWchar DotChar; // 2 // out // = '.' // Dot character used for fallback ellipsis rendering. + ImWchar DotChar; // 2 // out // = '.' // Character used for ellipsis rendering (if a single '...' character isn't found) bool DirtyLookupTables; // 1 // out // float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] @@ -2740,7 +2740,6 @@ struct ImFont IMGUI_API void AddGlyph(const ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. IMGUI_API void SetGlyphVisible(ImWchar c, bool visible); - IMGUI_API void SetFallbackChar(ImWchar c); IMGUI_API bool IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last); }; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index b0e3f777..f2ce627f 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2815,6 +2815,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesKorean() 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3131, 0x3163, // Korean alphabets 0xAC00, 0xD7A3, // Korean characters + 0xFFFD, 0xFFFD, // Invalid 0, }; return &ranges[0]; @@ -2829,6 +2830,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD, // Invalid 0x4e00, 0x9FAF, // CJK Ideograms 0, }; @@ -2905,7 +2907,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() 0x2000, 0x206F, // General Punctuation 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters + 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; if (!full_ranges[0]) @@ -2994,7 +2997,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters + 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; if (!full_ranges[0]) @@ -3225,12 +3229,6 @@ void ImFont::SetGlyphVisible(ImWchar c, bool visible) glyph->Visible = visible ? 1 : 0; } -void ImFont::SetFallbackChar(ImWchar c) -{ - FallbackChar = c; - BuildLookupTable(); -} - void ImFont::GrowIndex(int new_size) { IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size); From 2ab7f96f211bea8223bdd22ff100722d50cafa78 Mon Sep 17 00:00:00 2001 From: Chaoya Li Date: Fri, 25 Jun 2021 10:55:32 +0800 Subject: [PATCH 5/9] Backends: OpenGL3: Enable OES_vertex_array_object extension for emscripten/wasm build (#4266, #4267) --- backends/imgui_impl_opengl3.cpp | 32 +++++++++++++++++++++++++------- docs/CHANGELOG.txt | 1 + 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 2048603c..4b9b2e00 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state. // 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader. // 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version. // 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) @@ -94,6 +95,12 @@ // GL includes #if defined(IMGUI_IMPL_OPENGL_ES2) #include +#if defined(__EMSCRIPTEN__) +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES +#endif +#include +#endif #elif defined(IMGUI_IMPL_OPENGL_ES3) #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) #include // Use GL ES 3 @@ -132,6 +139,17 @@ using namespace gl; #endif #endif +// Vertex arrays are not supported on ES2/WebGL1 unless Emscripten which uses an extension +#ifndef IMGUI_IMPL_OPENGL_ES2 +#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY +#elif defined(__EMSCRIPTEN__) +#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY +#define glBindVertexArray glBindVertexArrayOES +#define glGenVertexArrays glGenVertexArraysOES +#define glDeleteVertexArrays glDeleteVertexArraysOES +#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES +#endif + // Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have. #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2) #define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET @@ -323,7 +341,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid #endif (void)vertex_array_object; -#ifndef IMGUI_IMPL_OPENGL_ES2 +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glBindVertexArray(vertex_array_object); #endif @@ -358,7 +376,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) GLuint last_sampler; if (g_GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; } #endif GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer); -#ifndef IMGUI_IMPL_OPENGL_ES2 +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object); #endif #ifdef GL_POLYGON_MODE @@ -385,7 +403,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. GLuint vertex_array_object = 0; -#ifndef IMGUI_IMPL_OPENGL_ES2 +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glGenVertexArrays(1, &vertex_array_object); #endif ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); @@ -443,7 +461,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) } // Destroy the temporary VAO -#ifndef IMGUI_IMPL_OPENGL_ES2 +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glDeleteVertexArrays(1, &vertex_array_object); #endif @@ -455,7 +473,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glBindSampler(0, last_sampler); #endif glActiveTexture(last_active_texture); -#ifndef IMGUI_IMPL_OPENGL_ES2 +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glBindVertexArray(last_vertex_array_object); #endif glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); @@ -559,7 +577,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() GLint last_texture, last_array_buffer; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); -#ifndef IMGUI_IMPL_OPENGL_ES2 +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); #endif @@ -732,7 +750,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() // Restore modified GL state glBindTexture(GL_TEXTURE_2D, last_texture); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); -#ifndef IMGUI_IMPL_OPENGL_ES2 +#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY glBindVertexArray(last_vertex_array); #endif diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0cd04241..3b18dc01 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -63,6 +63,7 @@ Other Changes: about building on 32-bit systems. (#4225) [@kingofthebongo2008] - Backends: OpenGL3: Handle GL_CLIP_ORIGIN on <4.5 contexts if "GL_ARB_clip_control" extension is detected. (#4170, #3998) - Backends: OpenGL3: Destroy vertex/fragment shader objects right after they are linked into main shader. (#4244) [@Crowbarous] +- Backends: OpenGL3: Use OES_vertex_array extension on Emscripten + backup/restore current state. (#4266, #4267) [@harry75369] - Backends: OSX: Added a fix for shortcuts using CTRL key instead of CMD key. (#4253) [@rokups] - Examples: OSX+OpenGL2: Fix event forwarding (fix key remaining stuck when using shortcuts with Cmd/Super key). Other OSX examples were not affected. (#4253, #1873) [@rokups] From 95db098477a1a8c3e758577177975a46ca433bf2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 24 Jun 2021 17:34:23 +0200 Subject: [PATCH 6/9] ImFontAtlas: move implicit AddFontDefault call to Build() function + remove unnecessary asserts in backend. --- backends/imgui_impl_android.cpp | 1 - backends/imgui_impl_glfw.cpp | 1 - backends/imgui_impl_opengl2.cpp | 2 +- backends/imgui_impl_sdl.cpp | 3 +-- backends/imgui_impl_win32.cpp | 1 - imgui.cpp | 3 +-- imgui_draw.cpp | 8 ++++---- 7 files changed, 7 insertions(+), 12 deletions(-) diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 8730c876..aae8e6b8 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -157,7 +157,6 @@ void ImGui_ImplAndroid_Shutdown() void ImGui_ImplAndroid_NewFrame() { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); // Process queued key events // FIXME: This is a workaround for multiple key event actions occurring at once (see above) and can be removed once we use upcoming input queue. diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 2818e697..860486ec 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -354,7 +354,6 @@ static void ImGui_ImplGlfw_UpdateGamepads() void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); // Setup display size (every frame to accommodate for window resizing) int w, h; diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 0ec530d7..6ecc2a93 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -4,7 +4,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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 diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 9eb1d8b6..2135f81d 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -11,7 +11,7 @@ // Missing features: // [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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 @@ -351,7 +351,6 @@ static void ImGui_ImplSDL2_UpdateGamepads() void ImGui_ImplSDL2_NewFrame(SDL_Window* window) { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); // Setup display size (every frame to accommodate for window resizing) int w, h; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index bd4eddfd..6800b12a 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -267,7 +267,6 @@ static void ImGui_ImplWin32_UpdateGamepads() void ImGui_ImplWin32_NewFrame() { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); // Setup display size (every frame to accommodate for window resizing) RECT rect = { 0, 0, 0, 0 }; diff --git a/imgui.cpp b/imgui.cpp index daf21721..514028a5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7173,8 +7173,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); - IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); - IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); + IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()"); IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations diff --git a/imgui_draw.cpp b/imgui_draw.cpp index f2ce627f..6bc0357d 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2033,11 +2033,7 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid { // Build atlas on demand if (TexPixelsAlpha8 == NULL) - { - if (ConfigData.empty()) - AddFontDefault(); Build(); - } *out_pixels = TexPixelsAlpha8; if (out_width) *out_width = TexWidth; @@ -2258,6 +2254,10 @@ bool ImFontAtlas::Build() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); + // Default font is none are specified + if (ConfigData.Size == 0) + AddFontDefault(); + // 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 From 0aeb978e616b641d489362898add71b0971249e8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 28 Jun 2021 11:16:43 +0200 Subject: [PATCH 7/9] Comments + adding spacing in headers because VS IDE parser display blocks so badly. Add helper in internal's ImGuiInputTextState. (#4275) --- imgui.cpp | 3 +++ imgui.h | 27 ++++++++++++++++++++------- imgui_internal.h | 3 +++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 514028a5..f7a72d44 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4459,6 +4459,9 @@ void ImGui::EndFrame() CallContextHooks(&g, ImGuiContextHookType_EndFramePost); } +// Prepare the data for rendering so you can call GetDrawData() +// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all: +// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend) void ImGui::Render() { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 9eeabbe9..925a7936 100644 --- a/imgui.h +++ b/imgui.h @@ -343,7 +343,8 @@ namespace ImGui IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) - // Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). + // Window manipulation + // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints. @@ -355,7 +356,7 @@ namespace ImGui IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus(). - IMGUI_API void SetWindowFontScale(float scale); // set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). + IMGUI_API void SetWindowFontScale(float scale); // [OBSOLETE] set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state @@ -405,6 +406,7 @@ namespace ImGui IMGUI_API void PopTextWrapPos(); // Style read access + // - Use the style editor (ShowStyleEditor() function) to interactively see what the colors are) IMGUI_API ImFont* GetFont(); // get current font IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API @@ -445,11 +447,15 @@ namespace ImGui IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) // ID stack/scopes - // - Read the FAQ for more details about how ID are handled in dear imgui. If you are creating widgets in a loop you most - // likely want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. - // - The resulting ID are hashes of the entire stack. + // Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui. + // - Those questions are answered and impacted by understanding of the ID stack system: + // - "Q: Why is my widget not reacting when I click on it?" + // - "Q: How can I have widgets with an empty label?" + // - "Q: How can I have multiple widgets with the same label?" + // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely + // want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. - // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed and used as an ID, + // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID, // whereas "str_id" denote a string that is only used as an ID and not normally displayed. IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). @@ -654,12 +660,14 @@ namespace ImGui // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). // - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack. // This is sometimes leading to confusing mistakes. May rework this in the future. + // Popups: begin/end functions // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window. // - BeginPopupModal(): block every interactions behind the window, cannot be closed by user, add a dimming background, has a title bar. IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it. IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! + // Popups: open/close functions // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. @@ -671,6 +679,7 @@ namespace ImGui IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. + // Popups: open+begin combined functions helpers // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. // - They are convenient to easily create context menus, hence the name. @@ -679,6 +688,7 @@ namespace ImGui IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows). + // Popups: query functions // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack. @@ -715,6 +725,7 @@ namespace ImGui IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. + // Tables: Headers & Columns declaration // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc. // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column. @@ -727,6 +738,7 @@ namespace ImGui IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) + // Tables: Sorting // - Call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. // - When 'SpecsDirty == true' you should sort your data. It will be true when sorting specs have changed @@ -734,6 +746,7 @@ namespace ImGui // wastefully sort your data every frame! // - Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable(). IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). + // Tables: Miscellaneous functions // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index. IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable) @@ -744,7 +757,7 @@ namespace ImGui IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) IMGUI_API void TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details. - // Legacy Columns API (2020: prefer using Tables!) + // Legacy Columns API (prefer using Tables!) // - You can also use SameLine(pos_x) to mimic simplified columns. IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished diff --git a/imgui_internal.h b/imgui_internal.h index 7fe1ab3f..dc480ebf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1078,6 +1078,9 @@ struct IMGUI_API ImGuiInputTextState void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } bool HasSelection() const { return Stb.select_start != Stb.select_end; } void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } + int GetCursorPos() const { return Stb.cursor; } + int GetSelectionStart() const { return Stb.select_start; } + int GetSelectionEnd() const { return Stb.select_end; } void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } }; From 88f4c1321a4030120d364023f58c0d76815bb3cf Mon Sep 17 00:00:00 2001 From: rokups Date: Thu, 12 Nov 2020 13:47:31 +0200 Subject: [PATCH 8/9] Examples: Bring code of example_apple_metal and example_apple_opengl2 closer to each other. (#1873, #3543) --- examples/example_apple_metal/main.mm | 326 +++++++++++-------------- examples/example_apple_opengl2/main.mm | 203 +++++++-------- 2 files changed, 239 insertions(+), 290 deletions(-) diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm index 273811d3..5d4b7710 100644 --- a/examples/example_apple_metal/main.mm +++ b/examples/example_apple_metal/main.mm @@ -15,26 +15,28 @@ #include "imgui.h" #include "imgui_impl_metal.h" - #if TARGET_OS_OSX #include "imgui_impl_osx.h" - -@interface ViewController : NSViewController +@interface AppViewController : NSViewController @end #else -@interface ViewController : UIViewController +@interface AppViewController : UIViewController @end #endif -@interface ViewController () +@interface AppViewController () @property (nonatomic, readonly) MTKView *mtkView; @property (nonatomic, strong) id device; @property (nonatomic, strong) id commandQueue; @end -@implementation ViewController +//----------------------------------------------------------------------------------- +// AppViewController +//----------------------------------------------------------------------------------- -- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil +@implementation AppViewController + +-(instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; @@ -80,17 +82,17 @@ return self; } -- (MTKView *)mtkView +-(MTKView *)mtkView { return (MTKView *)self.view; } -- (void)loadView +-(void)loadView { self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 720)]; } -- (void)viewDidLoad +-(void)viewDidLoad { [super viewDidLoad]; @@ -110,7 +112,7 @@ // we receive events for all controls, not just Dear ImGui widgets. If we had native controls in our // window, we'd want to be much more careful than just ingesting the complete event stream. // To match the behavior of other backends, we pass every event down to the OS. - NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged | NSEventTypeScrollWheel; + NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged; [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) { ImGui_ImplOSX_HandleEvent(event, self.view); @@ -122,53 +124,120 @@ #endif } -#pragma mark - Interaction +-(void)drawInMTKView:(MTKView*)view +{ + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize.x = view.bounds.size.width; + io.DisplaySize.y = view.bounds.size.height; + +#if TARGET_OS_OSX + CGFloat framebufferScale = view.window.screen.backingScaleFactor ?: NSScreen.mainScreen.backingScaleFactor; +#else + CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale; +#endif + io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale); + + io.DeltaTime = 1 / float(view.preferredFramesPerSecond ?: 60); + + id commandBuffer = [self.commandQueue commandBuffer]; + + MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; + if (renderPassDescriptor == nil) + { + [commandBuffer commit]; + return; + } + + // Start the Dear ImGui frame + ImGui_ImplMetal_NewFrame(renderPassDescriptor); +#if TARGET_OS_OSX + ImGui_ImplOSX_NewFrame(view); +#endif + ImGui::NewFrame(); + + // Our state (make them static = more or less global) as a convenience to keep the example terse. + 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); + + // 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(); + } + + // Rendering + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + [renderEncoder pushDebugGroup:@"Dear ImGui rendering"]; + ImGui_ImplMetal_RenderDrawData(draw_data, commandBuffer, renderEncoder); + [renderEncoder popDebugGroup]; + [renderEncoder endEncoding]; + + // Present + [commandBuffer presentDrawable:view.currentDrawable]; + [commandBuffer commit]; +} + +-(void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size +{ +} + +//----------------------------------------------------------------------------------- +// Input processing +//----------------------------------------------------------------------------------- #if TARGET_OS_OSX -- (void)mouseMoved:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)mouseDown:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)rightMouseDown:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)otherMouseDown:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)mouseUp:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)rightMouseUp:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)otherMouseUp:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)mouseDragged:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)rightMouseDragged:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)otherMouseDragged:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} - -- (void)scrollWheel:(NSEvent *)event { - ImGui_ImplOSX_HandleEvent(event, self.view); -} +// Forward Mouse/Keyboard events to Dear ImGui OSX backend. +// Other events are registered via addLocalMonitorForEventsMatchingMask() +-(void)mouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)rightMouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)otherMouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)mouseUp:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)rightMouseUp:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)otherMouseUp:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)mouseMoved:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)mouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)rightMouseMoved:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)rightMouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)otherMouseMoved:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)otherMouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } +-(void)scrollWheel:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } #else @@ -177,7 +246,7 @@ // multitouch correctly at all. This causes the "cursor" to behave very erratically // when there are multiple active touches. But for demo purposes, single-touch // interaction actually works surprisingly well. -- (void)updateIOWithTouchEvent:(UIEvent *)event +-(void)updateIOWithTouchEvent:(UIEvent *)event { UITouch *anyTouch = event.allTouches.anyObject; CGPoint touchLocation = [anyTouch locationInView:self.view]; @@ -196,127 +265,18 @@ io.MouseDown[0] = hasActiveTouch; } -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - [self updateIOWithTouchEvent:event]; -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -{ - [self updateIOWithTouchEvent:event]; -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event -{ - [self updateIOWithTouchEvent:event]; -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ - [self updateIOWithTouchEvent:event]; -} +-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self updateIOWithTouchEvent:event]; } +-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [self updateIOWithTouchEvent:event]; } +-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [self updateIOWithTouchEvent:event]; } +-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [self updateIOWithTouchEvent:event]; } #endif -#pragma mark - MTKViewDelegate - -- (void)drawInMTKView:(MTKView*)view -{ - ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize.x = view.bounds.size.width; - io.DisplaySize.y = view.bounds.size.height; - -#if TARGET_OS_OSX - CGFloat framebufferScale = view.window.screen.backingScaleFactor ?: NSScreen.mainScreen.backingScaleFactor; -#else - CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale; -#endif - io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale); - - io.DeltaTime = 1 / float(view.preferredFramesPerSecond ?: 60); - - id commandBuffer = [self.commandQueue commandBuffer]; - - // Our state (make them static = more or less global) as a convenience to keep the example terse. - static bool show_demo_window = true; - static bool show_another_window = false; - static float clear_color[4] = { 0.28f, 0.36f, 0.5f, 1.0f }; - - MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; - if (renderPassDescriptor != nil) - { - renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color[0] * clear_color[3], clear_color[1] * clear_color[3], clear_color[2] * clear_color[3], clear_color[3]); - - // Here, you could do additional rendering work, including other passes as necessary. - - id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; - [renderEncoder pushDebugGroup:@"ImGui demo"]; - - // Start the Dear ImGui frame - ImGui_ImplMetal_NewFrame(renderPassDescriptor); -#if TARGET_OS_OSX - ImGui_ImplOSX_NewFrame(view); -#endif - 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(); - } - - // Rendering - ImGui::Render(); - ImDrawData* draw_data = ImGui::GetDrawData(); - ImGui_ImplMetal_RenderDrawData(draw_data, commandBuffer, renderEncoder); - - [renderEncoder popDebugGroup]; - [renderEncoder endEncoding]; - - [commandBuffer presentDrawable:view.currentDrawable]; - } - - [commandBuffer commit]; -} - -- (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size -{ -} - @end -#pragma mark - Application Delegate +//----------------------------------------------------------------------------------- +// AppDelegate +//----------------------------------------------------------------------------------- #if TARGET_OS_OSX @@ -326,11 +286,16 @@ @implementation AppDelegate -- (instancetype)init +-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender +{ + return YES; +} + +-(instancetype)init { if (self = [super init]) { - NSViewController *rootViewController = [[ViewController alloc] initWithNibName:nil bundle:nil]; + NSViewController *rootViewController = [[AppViewController alloc] initWithNibName:nil bundle:nil]; self.window = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable backing:NSBackingStoreBuffered @@ -343,11 +308,6 @@ return self; } -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender -{ - return YES; -} - @end #else @@ -358,10 +318,10 @@ @implementation AppDelegate -- (BOOL)application:(UIApplication *)application +-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - UIViewController *rootViewController = [[ViewController alloc] init]; + UIViewController *rootViewController = [[AppViewController alloc] init]; self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; @@ -372,7 +332,9 @@ #endif -#pragma mark - main() +//----------------------------------------------------------------------------------- +// Application main() function +//----------------------------------------------------------------------------------- #if TARGET_OS_OSX diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 75484ec0..825c8a8f 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -2,30 +2,25 @@ // 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 -#include "imgui.h" -#include "../../backends/imgui_impl_osx.h" -#include "../../backends/imgui_impl_opengl2.h" -#include #import #import #import +#include "imgui.h" +#include "imgui_impl_opengl2.h" +#include "imgui_impl_osx.h" + //----------------------------------------------------------------------------------- -// ImGuiExampleView +// AppView //----------------------------------------------------------------------------------- -@interface ImGuiExampleView : NSOpenGLView +@interface AppView : NSOpenGLView { NSTimer* animationTimer; } @end -@implementation ImGuiExampleView - --(void)animationTimerFired:(NSTimer*)timer -{ - [self setNeedsDisplay:YES]; -} +@implementation AppView -(void)prepareOpenGL { @@ -39,11 +34,54 @@ #endif } +-(void)initialize +{ + // Some events do not raise callbacks of AppView in some circumstances (for example when CMD key is held down). + // This monitor taps into global event stream and captures these events. + NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged; + [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) + { + ImGui_ImplOSX_HandleEvent(event, self); + return event; + }]; + + // Setup Dear ImGui context + // FIXME: This example doesn't have proper cleanup... + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + // Setup Platform/Renderer backends + ImGui_ImplOSX_Init(); + ImGui_ImplOpenGL2_Init(); + + // 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.txt' 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 \\ ! + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != NULL); +} + -(void)updateAndDrawDemoView { // Start the Dear ImGui frame - ImGui_ImplOpenGL2_NewFrame(); - ImGui_ImplOSX_NewFrame(self); + ImGui_ImplOpenGL2_NewFrame(); + ImGui_ImplOSX_NewFrame(self); ImGui::NewFrame(); // Our state (make them static = more or less global) as a convenience to keep the example terse. @@ -88,18 +126,18 @@ ImGui::End(); } - // Rendering - ImGui::Render(); - [[self openGLContext] makeCurrentContext]; - + // Rendering + ImGui::Render(); ImDrawData* draw_data = ImGui::GetDrawData(); + + [[self openGLContext] makeCurrentContext]; GLsizei width = (GLsizei)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); GLsizei height = (GLsizei)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); glViewport(0, 0, width, height); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); - glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL2_RenderDrawData(draw_data); + ImGui_ImplOpenGL2_RenderDrawData(draw_data); // Present [[self openGLContext] flushBuffer]; @@ -108,38 +146,20 @@ animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES]; } --(void)reshape -{ - [[self openGLContext] update]; - [self updateAndDrawDemoView]; -} +-(void)reshape { [[self openGLContext] update]; [self updateAndDrawDemoView]; } +-(void)drawRect:(NSRect)bounds { [self updateAndDrawDemoView]; } +-(void)animationTimerFired:(NSTimer*)timer { [self setNeedsDisplay:YES]; } +-(BOOL)acceptsFirstResponder { return (YES); } +-(BOOL)becomeFirstResponder { return (YES); } +-(BOOL)resignFirstResponder { return (YES); } +-(void)dealloc { animationTimer = nil; } --(void)drawRect:(NSRect)bounds -{ - [self updateAndDrawDemoView]; -} - --(BOOL)acceptsFirstResponder -{ - return (YES); -} - --(BOOL)becomeFirstResponder -{ - return (YES); -} - --(BOOL)resignFirstResponder -{ - return (YES); -} - --(void)dealloc -{ - animationTimer = nil; -} +//----------------------------------------------------------------------------------- +// Input processing +//----------------------------------------------------------------------------------- // Forward Mouse/Keyboard events to Dear ImGui OSX backend. +// Other events are registered via addLocalMonitorForEventsMatchingMask() -(void)mouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)rightMouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)otherMouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } @@ -147,23 +167,24 @@ -(void)rightMouseUp:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)otherMouseUp:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)mouseMoved:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } --(void)rightMouseMoved:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } --(void)otherMouseMoved:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)mouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } +-(void)rightMouseMoved:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)rightMouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } +-(void)otherMouseMoved:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } -(void)otherMouseDragged:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } +-(void)scrollWheel:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self); } @end //----------------------------------------------------------------------------------- -// ImGuiExampleAppDelegate +// AppDelegate //----------------------------------------------------------------------------------- -@interface ImGuiExampleAppDelegate : NSObject +@interface AppDelegate : NSObject @property (nonatomic, readonly) NSWindow* window; @end -@implementation ImGuiExampleAppDelegate +@implementation AppDelegate @synthesize window = _window; -(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication @@ -189,7 +210,7 @@ -(void)setupMenu { - NSMenu* mainMenuBar = [[NSMenu alloc] init]; + NSMenu* mainMenuBar = [[NSMenu alloc] init]; NSMenu* appMenu; NSMenuItem* menuItem; @@ -213,11 +234,11 @@ -(void)applicationDidFinishLaunching:(NSNotification *)aNotification { - // Make the application a foreground application (else it won't receive keyboard events) - ProcessSerialNumber psn = {0, kCurrentProcess}; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); + // Make the application a foreground application (else it won't receive keyboard events) + ProcessSerialNumber psn = {0, kCurrentProcess}; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); - // Menu + // Menu [self setupMenu]; NSOpenGLPixelFormatAttribute attrs[] = @@ -228,7 +249,7 @@ }; NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; - ImGuiExampleView* view = [[ImGuiExampleView alloc] initWithFrame:self.window.frame pixelFormat:format]; + AppView* view = [[AppView alloc] initWithFrame:self.window.frame pixelFormat:format]; format = nil; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) @@ -239,57 +260,23 @@ if ([view openGLContext] == nil) NSLog(@"No OpenGL Context!"); - // Some events do not raise callbacks of ImGuiExampleView in some circumstances (for example when CMD key is held down). - // This monitor taps into global event stream and captures these events. - NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged | NSEventTypeScrollWheel; - [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) - { - ImGui_ImplOSX_HandleEvent(event, view); - return event; - }]; - - // Setup Dear ImGui context - // FIXME: This example doesn't have proper cleanup... - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls - - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - //ImGui::StyleColorsClassic(); - - // Setup Platform/Renderer backends - ImGui_ImplOSX_Init(); - ImGui_ImplOpenGL2_Init(); - - // 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.txt' 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 \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); - //IM_ASSERT(font != NULL); + [view initialize]; } @end +//----------------------------------------------------------------------------------- +// Application main() function +//----------------------------------------------------------------------------------- + int main(int argc, const char* argv[]) { - @autoreleasepool - { - NSApp = [NSApplication sharedApplication]; - ImGuiExampleAppDelegate* delegate = [[ImGuiExampleAppDelegate alloc] init]; - [[NSApplication sharedApplication] setDelegate:delegate]; - [NSApp run]; - } - return NSApplicationMain(argc, argv); + @autoreleasepool + { + NSApp = [NSApplication sharedApplication]; + AppDelegate* delegate = [[AppDelegate alloc] init]; + [[NSApplication sharedApplication] setDelegate:delegate]; + [NSApp run]; + } + return NSApplicationMain(argc, argv); } From 70c603854e8817770c1a4eeb047929eb1b7313a3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 28 Jun 2021 16:52:10 +0200 Subject: [PATCH 9/9] Backends: moved global to a data structure to facilitate support for multi-contexts. (#586, #1851, #2004, #3012, #3934, #4141) This is NOT enable multi-contexts for any backends - in order to make this commit as harmless as possible, while containing all the cruft/renaming - --- backends/imgui_impl_allegro5.cpp | 122 ++++++++------ backends/imgui_impl_dx10.cpp | 181 +++++++++++--------- backends/imgui_impl_dx11.cpp | 190 ++++++++++++--------- backends/imgui_impl_dx12.cpp | 153 ++++++++++------- backends/imgui_impl_dx9.cpp | 196 +++++++++++++--------- backends/imgui_impl_glfw.cpp | 180 +++++++++++--------- backends/imgui_impl_opengl2.cpp | 46 ++++-- backends/imgui_impl_opengl3.cpp | 172 +++++++++++-------- backends/imgui_impl_sdl.cpp | 121 ++++++++------ backends/imgui_impl_vulkan.cpp | 274 ++++++++++++++++++------------- backends/imgui_impl_win32.cpp | 110 ++++++++----- docs/CHANGELOG.txt | 2 + 12 files changed, 1042 insertions(+), 705 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index b5de39e4..ac2fa2d9 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -9,13 +9,14 @@ // [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. // [ ] Platform: Missing gamepad support. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: Renderer: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: Change blending equation to preserve alpha in output buffer. // 2020-08-10: Inputs: Fixed horizontal mouse wheel direction. @@ -53,13 +54,24 @@ #pragma warning (disable: 4127) // condition expression is constant #endif -// Data -static ALLEGRO_DISPLAY* g_Display = NULL; -static ALLEGRO_BITMAP* g_Texture = NULL; -static double g_Time = 0.0; -static ALLEGRO_MOUSE_CURSOR* g_MouseCursorInvisible = NULL; -static ALLEGRO_VERTEX_DECL* g_VertexDecl = NULL; -static char* g_ClipboardTextData = NULL; +// Allegro Data +struct ImGui_ImplAllegro5_Data +{ + ALLEGRO_DISPLAY* Display; + ALLEGRO_BITMAP* Texture; + double Time; + ALLEGRO_MOUSE_CURSOR* MouseCursorInvisible; + ALLEGRO_VERTEX_DECL* VertexDecl; + char* ClipboardTextData; + + ImGui_ImplAllegro5_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplAllegro5_Data* g_Data; +static ImGui_ImplAllegro5_Data* ImGui_ImplAllegro5_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplAllegro5_Data); return g_Data; } +static ImGui_ImplAllegro5_Data* ImGui_ImplAllegro5_GetBackendData() { return ImGui::GetCurrentContext() != NULL ? g_Data : NULL; } +static void ImGui_ImplAllegro5_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } struct ImDrawVertAllegro { @@ -96,6 +108,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) return; // Backup Allegro state that will be modified + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); ALLEGRO_TRANSFORM last_transform = *al_get_current_transform(); ALLEGRO_TRANSFORM last_projection_transform = *al_get_current_projection_transform(); int last_clip_x, last_clip_y, last_clip_w, last_clip_h; @@ -161,7 +174,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) // Draw ALLEGRO_BITMAP* texture = (ALLEGRO_BITMAP*)pcmd->GetTexID(); al_set_clipping_rectangle(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y, pcmd->ClipRect.z - pcmd->ClipRect.x, pcmd->ClipRect.w - pcmd->ClipRect.y); - al_draw_prim(&vertices[0], g_VertexDecl, texture, idx_offset, idx_offset + pcmd->ElemCount, ALLEGRO_PRIM_TRIANGLE_LIST); + al_draw_prim(&vertices[0], bd->VertexDecl, texture, idx_offset, idx_offset + pcmd->ElemCount, ALLEGRO_PRIM_TRIANGLE_LIST); } idx_offset += pcmd->ElemCount; } @@ -177,6 +190,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) bool ImGui_ImplAllegro5_CreateDeviceObjects() { // Build texture atlas + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); unsigned char* pixels; int width, height; @@ -210,12 +224,12 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects() // Store our identifier io.Fonts->SetTexID((void*)cloned_img); - g_Texture = cloned_img; + bd->Texture = cloned_img; // Create an invisible mouse cursor // Because al_hide_mouse_cursor() seems to mess up with the actual inputs.. ALLEGRO_BITMAP* mouse_cursor = al_create_bitmap(8, 8); - g_MouseCursorInvisible = al_create_mouse_cursor(mouse_cursor, 0, 0); + bd->MouseCursorInvisible = al_create_mouse_cursor(mouse_cursor, 0, 0); al_destroy_bitmap(mouse_cursor); return true; @@ -223,41 +237,48 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects() void ImGui_ImplAllegro5_InvalidateDeviceObjects() { - if (g_Texture) + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + if (bd->Texture) { - ImGuiIO& io = ImGui::GetIO(); io.Fonts->SetTexID(NULL); - al_destroy_bitmap(g_Texture); - g_Texture = NULL; + al_destroy_bitmap(bd->Texture); + bd->Texture = NULL; } - if (g_MouseCursorInvisible) + if (bd->MouseCursorInvisible) { - al_destroy_mouse_cursor(g_MouseCursorInvisible); - g_MouseCursorInvisible = NULL; + al_destroy_mouse_cursor(bd->MouseCursorInvisible); + bd->MouseCursorInvisible = NULL; } } #if ALLEGRO_HAS_CLIPBOARD static const char* ImGui_ImplAllegro5_GetClipboardText(void*) { - if (g_ClipboardTextData) - al_free(g_ClipboardTextData); - g_ClipboardTextData = al_get_clipboard_text(g_Display); - return g_ClipboardTextData; + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + if (bd->ClipboardTextData) + al_free(bd->ClipboardTextData); + bd->ClipboardTextData = al_get_clipboard_text(bd->Display); + return bd->ClipboardTextData; } static void ImGui_ImplAllegro5_SetClipboardText(void*, const char* text) { - al_set_clipboard_text(g_Display, text); + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + al_set_clipboard_text(bd->Display, text); } #endif bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) { - g_Display = display; + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_CreateBackendData(); + bd->Display = display; // Setup backend capabilities flags - ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererUserData = (void*)bd; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendPlatformName = io.BackendRendererName = "imgui_impl_allegro5"; @@ -271,7 +292,7 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) { ALLEGRO_PRIM_COLOR_ATTR, 0, IM_OFFSETOF(ImDrawVertAllegro, col) }, { 0, 0, 0 } }; - g_VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro)); + bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro)); io.KeyMap[ImGuiKey_Tab] = ALLEGRO_KEY_TAB; io.KeyMap[ImGuiKey_LeftArrow] = ALLEGRO_KEY_LEFT; @@ -308,18 +329,20 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) void ImGui_ImplAllegro5_Shutdown() { + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); ImGui_ImplAllegro5_InvalidateDeviceObjects(); - g_Display = NULL; - g_Time = 0.0; + bd->Display = NULL; + bd->Time = 0.0; - if (g_VertexDecl) - al_destroy_vertex_decl(g_VertexDecl); - g_VertexDecl = NULL; + if (bd->VertexDecl) + al_destroy_vertex_decl(bd->VertexDecl); + bd->VertexDecl = NULL; - if (g_ClipboardTextData) - al_free(g_ClipboardTextData); - g_ClipboardTextData = NULL; + if (bd->ClipboardTextData) + al_free(bd->ClipboardTextData); + bd->ClipboardTextData = NULL; + ImGui_ImplAllegro5_DestroyBackendData(); } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. @@ -329,11 +352,12 @@ void ImGui_ImplAllegro5_Shutdown() bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev) { ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); switch (ev->type) { case ALLEGRO_EVENT_MOUSE_AXES: - if (ev->mouse.display == g_Display) + if (ev->mouse.display == bd->Display) { io.MouseWheel += ev->mouse.dz; io.MouseWheelH -= ev->mouse.dw; @@ -342,31 +366,31 @@ bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev) return true; case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: case ALLEGRO_EVENT_MOUSE_BUTTON_UP: - if (ev->mouse.display == g_Display && ev->mouse.button <= 5) + if (ev->mouse.display == bd->Display && ev->mouse.button <= 5) io.MouseDown[ev->mouse.button - 1] = (ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN); return true; case ALLEGRO_EVENT_TOUCH_MOVE: - if (ev->touch.display == g_Display) + if (ev->touch.display == bd->Display) io.MousePos = ImVec2(ev->touch.x, ev->touch.y); return true; case ALLEGRO_EVENT_TOUCH_BEGIN: case ALLEGRO_EVENT_TOUCH_END: case ALLEGRO_EVENT_TOUCH_CANCEL: - if (ev->touch.display == g_Display && ev->touch.primary) + if (ev->touch.display == bd->Display && ev->touch.primary) io.MouseDown[0] = (ev->type == ALLEGRO_EVENT_TOUCH_BEGIN); return true; case ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY: - if (ev->mouse.display == g_Display) + if (ev->mouse.display == bd->Display) io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); return true; case ALLEGRO_EVENT_KEY_CHAR: - if (ev->keyboard.display == g_Display) + if (ev->keyboard.display == bd->Display) if (ev->keyboard.unichar != 0) io.AddInputCharacter((unsigned int)ev->keyboard.unichar); return true; case ALLEGRO_EVENT_KEY_DOWN: case ALLEGRO_EVENT_KEY_UP: - if (ev->keyboard.display == g_Display) + if (ev->keyboard.display == bd->Display) io.KeysDown[ev->keyboard.keycode] = (ev->type == ALLEGRO_EVENT_KEY_DOWN); return true; } @@ -379,11 +403,12 @@ static void ImGui_ImplAllegro5_UpdateMouseCursor() if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return; + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - al_set_mouse_cursor(g_Display, g_MouseCursorInvisible); + al_set_mouse_cursor(bd->Display, bd->MouseCursorInvisible); } else { @@ -398,27 +423,28 @@ static void ImGui_ImplAllegro5_UpdateMouseCursor() case ImGuiMouseCursor_ResizeNWSE: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NW; break; case ImGuiMouseCursor_NotAllowed: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_UNAVAILABLE; break; } - al_set_system_mouse_cursor(g_Display, cursor_id); + al_set_system_mouse_cursor(bd->Display, cursor_id); } } void ImGui_ImplAllegro5_NewFrame() { - if (!g_Texture) + ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + if (!bd->Texture) ImGui_ImplAllegro5_CreateDeviceObjects(); ImGuiIO& io = ImGui::GetIO(); // Setup display size (every frame to accommodate for window resizing) int w, h; - w = al_get_display_width(g_Display); - h = al_get_display_height(g_Display); + w = al_get_display_width(bd->Display); + h = al_get_display_height(bd->Display); io.DisplaySize = ImVec2((float)w, (float)h); // Setup time step double current_time = al_get_time(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); - g_Time = current_time; + io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); + bd->Time = current_time; // Setup inputs ALLEGRO_KEYBOARD_STATE keys; diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 61a8f583..760f36f0 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -5,13 +5,14 @@ // [X] Renderer: User texture backend. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: DirectX10: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: DirectX10: Change blending equation to preserve alpha in output buffer. // 2019-07-21: DirectX10: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData(). @@ -40,28 +41,43 @@ #endif // DirectX data -static ID3D10Device* g_pd3dDevice = NULL; -static IDXGIFactory* g_pFactory = NULL; -static ID3D10Buffer* g_pVB = NULL; -static ID3D10Buffer* g_pIB = NULL; -static ID3D10VertexShader* g_pVertexShader = NULL; -static ID3D10InputLayout* g_pInputLayout = NULL; -static ID3D10Buffer* g_pVertexConstantBuffer = NULL; -static ID3D10PixelShader* g_pPixelShader = NULL; -static ID3D10SamplerState* g_pFontSampler = NULL; -static ID3D10ShaderResourceView*g_pFontTextureView = NULL; -static ID3D10RasterizerState* g_pRasterizerState = NULL; -static ID3D10BlendState* g_pBlendState = NULL; -static ID3D10DepthStencilState* g_pDepthStencilState = NULL; -static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; +struct ImGui_ImplDX10_Data +{ + ID3D10Device* pd3dDevice; + IDXGIFactory* pFactory; + ID3D10Buffer* pVB; + ID3D10Buffer* pIB; + ID3D10VertexShader* pVertexShader; + ID3D10InputLayout* pInputLayout; + ID3D10Buffer* pVertexConstantBuffer; + ID3D10PixelShader* pPixelShader; + ID3D10SamplerState* pFontSampler; + ID3D10ShaderResourceView* pFontTextureView; + ID3D10RasterizerState* pRasterizerState; + ID3D10BlendState* pBlendState; + ID3D10DepthStencilState* pDepthStencilState; + int VertexBufferSize; + int IndexBufferSize; + + ImGui_ImplDX10_Data() { memset(this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplDX10_Data* g_Data; +static ImGui_ImplDX10_Data* ImGui_ImplDX10_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplDX10_Data); return g_Data; } +static ImGui_ImplDX10_Data* ImGui_ImplDX10_GetBackendData() { return ImGui::GetCurrentContext() != NULL ? g_Data : NULL; } +static void ImGui_ImplDX10_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } struct VERTEX_CONSTANT_BUFFER { float mvp[4][4]; }; +// Functions static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx) { + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + // Setup viewport D3D10_VIEWPORT vp; memset(&vp, 0, sizeof(D3D10_VIEWPORT)); @@ -75,21 +91,21 @@ static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* // Bind shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); unsigned int offset = 0; - ctx->IASetInputLayout(g_pInputLayout); - ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); - ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); + ctx->IASetInputLayout(bd->pInputLayout); + ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset); + ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ctx->VSSetShader(g_pVertexShader); - ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); - ctx->PSSetShader(g_pPixelShader); - ctx->PSSetSamplers(0, 1, &g_pFontSampler); + ctx->VSSetShader(bd->pVertexShader); + ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); + ctx->PSSetShader(bd->pPixelShader); + ctx->PSSetSamplers(0, 1, &bd->pFontSampler); ctx->GSSetShader(NULL); // Setup render state const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; - ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); - ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); - ctx->RSSetState(g_pRasterizerState); + ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); + ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); + ctx->RSSetState(bd->pRasterizerState); } // Render function @@ -99,43 +115,44 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - ID3D10Device* ctx = g_pd3dDevice; + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + ID3D10Device* ctx = bd->pd3dDevice; // Create and grow vertex/index buffers if needed - if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) + if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - g_VertexBufferSize = draw_data->TotalVtxCount + 5000; + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D10_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D10_BUFFER_DESC)); desc.Usage = D3D10_USAGE_DYNAMIC; - desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); + desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - if (ctx->CreateBuffer(&desc, NULL, &g_pVB) < 0) + if (ctx->CreateBuffer(&desc, NULL, &bd->pVB) < 0) return; } - if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) + if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) { - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - g_IndexBufferSize = draw_data->TotalIdxCount + 10000; + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D10_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D10_BUFFER_DESC)); desc.Usage = D3D10_USAGE_DYNAMIC; - desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); + desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D10_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; - if (ctx->CreateBuffer(&desc, NULL, &g_pIB) < 0) + if (ctx->CreateBuffer(&desc, NULL, &bd->pIB) < 0) return; } // Copy and convert all vertices into a single contiguous buffer ImDrawVert* vtx_dst = NULL; ImDrawIdx* idx_dst = NULL; - g_pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst); - g_pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst); + bd->pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst); + bd->pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst); for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -144,14 +161,14 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) vtx_dst += cmd_list->VtxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size; } - g_pVB->Unmap(); - g_pIB->Unmap(); + bd->pVB->Unmap(); + bd->pIB->Unmap(); // 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). DisplayPos is (0,0) for single viewport apps. { void* mapped_resource; - if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) + if (bd->pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource; float L = draw_data->DisplayPos.x; @@ -166,7 +183,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, }; memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); - g_pVertexConstantBuffer->Unmap(); + bd->pVertexConstantBuffer->Unmap(); } // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) @@ -270,6 +287,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) static void ImGui_ImplDX10_CreateFontsTexture() { // Build texture atlas + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); unsigned char* pixels; int width, height; @@ -294,7 +312,7 @@ static void ImGui_ImplDX10_CreateFontsTexture() subResource.pSysMem = pixels; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); IM_ASSERT(pTexture != NULL); // Create texture view @@ -304,12 +322,12 @@ static void ImGui_ImplDX10_CreateFontsTexture() srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; srv_desc.Texture2D.MipLevels = desc.MipLevels; srv_desc.Texture2D.MostDetailedMip = 0; - g_pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &g_pFontTextureView); + bd->pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &bd->pFontTextureView); pTexture->Release(); } // Store our identifier - io.Fonts->SetTexID((ImTextureID)g_pFontTextureView); + io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView); // Create texture sampler { @@ -323,15 +341,16 @@ static void ImGui_ImplDX10_CreateFontsTexture() desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS; desc.MinLOD = 0.f; desc.MaxLOD = 0.f; - g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); } } bool ImGui_ImplDX10_CreateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + if (!bd->pd3dDevice) return false; - if (g_pFontSampler) + if (bd->pFontSampler) ImGui_ImplDX10_InvalidateDeviceObjects(); // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) @@ -373,7 +392,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() ID3DBlob* vertexShaderBlob; if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL))) return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (g_pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &g_pVertexShader) != S_OK) + if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pVertexShader) != S_OK) { vertexShaderBlob->Release(); return false; @@ -386,7 +405,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D10_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D10_INPUT_PER_VERTEX_DATA, 0 }, }; - if (g_pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) + if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK) { vertexShaderBlob->Release(); return false; @@ -401,7 +420,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); + bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer); } } @@ -426,7 +445,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() ID3DBlob* pixelShaderBlob; if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL))) return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (g_pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), &g_pPixelShader) != S_OK) + if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), &bd->pPixelShader) != S_OK) { pixelShaderBlob->Release(); return false; @@ -447,7 +466,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; - g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); + bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState); } // Create the rasterizer state @@ -458,7 +477,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() desc.CullMode = D3D10_CULL_NONE; desc.ScissorEnable = true; desc.DepthClipEnable = true; - g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); + bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState); } // Create depth-stencil State @@ -472,7 +491,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS; desc.BackFace = desc.FrontFace; - g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); + bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } ImGui_ImplDX10_CreateFontsTexture(); @@ -482,27 +501,31 @@ bool ImGui_ImplDX10_CreateDeviceObjects() void ImGui_ImplDX10_InvalidateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + if (!bd->pd3dDevice) return; - if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } - if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - - if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } - if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } - if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } - if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } - if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } - if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } - if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } + if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; } + if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; } + if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; } + if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; } + if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; } + if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; } + if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; } + if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; } } bool ImGui_ImplDX10_Init(ID3D10Device* device) { - // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_CreateBackendData(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx10"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. @@ -510,30 +533,36 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) IDXGIDevice* pDXGIDevice = NULL; IDXGIAdapter* pDXGIAdapter = NULL; IDXGIFactory* pFactory = NULL; - if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK) if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) { - g_pd3dDevice = device; - g_pFactory = pFactory; + bd->pd3dDevice = device; + bd->pFactory = pFactory; } if (pDXGIDevice) pDXGIDevice->Release(); if (pDXGIAdapter) pDXGIAdapter->Release(); - g_pd3dDevice->AddRef(); + bd->pd3dDevice->AddRef(); return true; } void ImGui_ImplDX10_Shutdown() { + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + ImGui_ImplDX10_InvalidateDeviceObjects(); - if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } - if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } + if (bd->pFactory) { bd->pFactory->Release(); bd->pFactory = NULL; } + if (bd->pd3dDevice) { bd->pd3dDevice->Release(); bd->pd3dDevice = NULL; } + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + ImGui_ImplDX10_DestroyBackendData(); } void ImGui_ImplDX10_NewFrame() { - if (!g_pFontSampler) + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + if (!bd->pFontSampler) ImGui_ImplDX10_CreateDeviceObjects(); } diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index da97ef09..867fb7ac 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -5,13 +5,14 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer. // 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled). @@ -39,30 +40,45 @@ #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif -// DirectX data -static ID3D11Device* g_pd3dDevice = NULL; -static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; -static IDXGIFactory* g_pFactory = NULL; -static ID3D11Buffer* g_pVB = NULL; -static ID3D11Buffer* g_pIB = NULL; -static ID3D11VertexShader* g_pVertexShader = NULL; -static ID3D11InputLayout* g_pInputLayout = NULL; -static ID3D11Buffer* g_pVertexConstantBuffer = NULL; -static ID3D11PixelShader* g_pPixelShader = NULL; -static ID3D11SamplerState* g_pFontSampler = NULL; -static ID3D11ShaderResourceView*g_pFontTextureView = NULL; -static ID3D11RasterizerState* g_pRasterizerState = NULL; -static ID3D11BlendState* g_pBlendState = NULL; -static ID3D11DepthStencilState* g_pDepthStencilState = NULL; -static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; +// DirectX11 data +struct ImGui_ImplDX11_Data +{ + ID3D11Device* pd3dDevice; + ID3D11DeviceContext* pd3dDeviceContext; + IDXGIFactory* pFactory; + ID3D11Buffer* pVB; + ID3D11Buffer* pIB; + ID3D11VertexShader* pVertexShader; + ID3D11InputLayout* pInputLayout; + ID3D11Buffer* pVertexConstantBuffer; + ID3D11PixelShader* pPixelShader; + ID3D11SamplerState* pFontSampler; + ID3D11ShaderResourceView* pFontTextureView; + ID3D11RasterizerState* pRasterizerState; + ID3D11BlendState* pBlendState; + ID3D11DepthStencilState* pDepthStencilState; + int VertexBufferSize; + int IndexBufferSize; + + ImGui_ImplDX11_Data() { memset(this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplDX11_Data* g_Data; +static ImGui_ImplDX11_Data* ImGui_ImplDX11_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplDX11_Data); return g_Data; } +static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData() { return ImGui::GetCurrentContext() != NULL ? g_Data : NULL; } +static void ImGui_ImplDX11_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } struct VERTEX_CONSTANT_BUFFER { float mvp[4][4]; }; +// Functions static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx) { + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + // Setup viewport D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(D3D11_VIEWPORT)); @@ -76,14 +92,14 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC // Setup shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); unsigned int offset = 0; - ctx->IASetInputLayout(g_pInputLayout); - ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); - ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); + ctx->IASetInputLayout(bd->pInputLayout); + ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset); + ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ctx->VSSetShader(g_pVertexShader, NULL, 0); - ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); - ctx->PSSetShader(g_pPixelShader, NULL, 0); - ctx->PSSetSamplers(0, 1, &g_pFontSampler); + ctx->VSSetShader(bd->pVertexShader, NULL, 0); + ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); + ctx->PSSetShader(bd->pPixelShader, NULL, 0); + ctx->PSSetSamplers(0, 1, &bd->pFontSampler); ctx->GSSetShader(NULL, NULL, 0); ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. @@ -91,9 +107,9 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC // Setup blend state const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; - ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); - ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); - ctx->RSSetState(g_pRasterizerState); + ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); + ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); + ctx->RSSetState(bd->pRasterizerState); } // Render function @@ -103,42 +119,43 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - ID3D11DeviceContext* ctx = g_pd3dDeviceContext; + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + ID3D11DeviceContext* ctx = bd->pd3dDeviceContext; // Create and grow vertex/index buffers if needed - if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) + if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - g_VertexBufferSize = draw_data->TotalVtxCount + 5000; + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); + desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0) + if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVB) < 0) return; } - if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) + if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) { - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - g_IndexBufferSize = draw_data->TotalIdxCount + 10000; + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); + desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0) + if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pIB) < 0) return; } // Upload vertex/index data into a single contiguous GPU buffer D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; - if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) + if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) return; - if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) + if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) return; ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; @@ -150,14 +167,14 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) vtx_dst += cmd_list->VtxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size; } - ctx->Unmap(g_pVB, 0); - ctx->Unmap(g_pIB, 0); + ctx->Unmap(bd->pVB, 0); + ctx->Unmap(bd->pIB, 0); // 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). DisplayPos is (0,0) for single viewport apps. { D3D11_MAPPED_SUBRESOURCE mapped_resource; - if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) + if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; float L = draw_data->DisplayPos.x; @@ -172,7 +189,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, }; memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); - ctx->Unmap(g_pVertexConstantBuffer, 0); + ctx->Unmap(bd->pVertexConstantBuffer, 0); } // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) @@ -283,6 +300,7 @@ static void ImGui_ImplDX11_CreateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); @@ -306,7 +324,7 @@ static void ImGui_ImplDX11_CreateFontsTexture() subResource.pSysMem = pixels; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); IM_ASSERT(pTexture != NULL); // Create texture view @@ -316,12 +334,12 @@ static void ImGui_ImplDX11_CreateFontsTexture() srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; - g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); + bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView); pTexture->Release(); } // Store our identifier - io.Fonts->SetTexID((ImTextureID)g_pFontTextureView); + io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView); // Create texture sampler { @@ -335,15 +353,16 @@ static void ImGui_ImplDX11_CreateFontsTexture() desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; desc.MinLOD = 0.f; desc.MaxLOD = 0.f; - g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); } } bool ImGui_ImplDX11_CreateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pd3dDevice) return false; - if (g_pFontSampler) + if (bd->pFontSampler) ImGui_ImplDX11_InvalidateDeviceObjects(); // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) @@ -385,7 +404,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() ID3DBlob* vertexShaderBlob; if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL))) return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (g_pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK) + if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &bd->pVertexShader) != S_OK) { vertexShaderBlob->Release(); return false; @@ -398,7 +417,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; - if (g_pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) + if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK) { vertexShaderBlob->Release(); return false; @@ -413,7 +432,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); + bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer); } } @@ -438,7 +457,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() ID3DBlob* pixelShaderBlob; if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL))) return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (g_pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK) + if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &bd->pPixelShader) != S_OK) { pixelShaderBlob->Release(); return false; @@ -459,7 +478,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); + bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState); } // Create the rasterizer state @@ -470,7 +489,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.CullMode = D3D11_CULL_NONE; desc.ScissorEnable = true; desc.DepthClipEnable = true; - g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); + bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState); } // Create depth-stencil State @@ -484,7 +503,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; desc.BackFace = desc.FrontFace; - g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); + bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } ImGui_ImplDX11_CreateFontsTexture(); @@ -494,27 +513,31 @@ bool ImGui_ImplDX11_CreateDeviceObjects() void ImGui_ImplDX11_InvalidateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pd3dDevice) return; - if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } - if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - - if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } - if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } - if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } - if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } - if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } - if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } - if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } + if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; } + if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; } + if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; } + if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; } + if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; } + if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; } + if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; } + if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; } } bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) { - // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_CreateBackendData(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx11"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. @@ -527,28 +550,35 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) { - g_pd3dDevice = device; - g_pd3dDeviceContext = device_context; - g_pFactory = pFactory; + bd->pd3dDevice = device; + bd->pd3dDeviceContext = device_context; + bd->pFactory = pFactory; } if (pDXGIDevice) pDXGIDevice->Release(); if (pDXGIAdapter) pDXGIAdapter->Release(); - g_pd3dDevice->AddRef(); - g_pd3dDeviceContext->AddRef(); + bd->pd3dDevice->AddRef(); + bd->pd3dDeviceContext->AddRef(); return true; } void ImGui_ImplDX11_Shutdown() { + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + ImGui_ImplDX11_InvalidateDeviceObjects(); - if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } - if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } - if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } + if (bd->pFactory) { bd->pFactory->Release(); bd->pFactory = NULL; } + if (bd->pd3dDevice) { bd->pd3dDevice->Release(); bd->pd3dDevice = NULL; } + if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); bd->pd3dDeviceContext = NULL; } + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + ImGui_ImplDX11_DestroyBackendData(); } void ImGui_ImplDX11_NewFrame() { - if (!g_pFontSampler) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pFontSampler) ImGui_ImplDX11_CreateDeviceObjects(); } diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index fc88b8c9..76b4a982 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer. // 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically. @@ -48,14 +49,6 @@ #endif // DirectX data -static ID3D12Device* g_pd3dDevice = NULL; -static ID3D12RootSignature* g_pRootSignature = NULL; -static ID3D12PipelineState* g_pPipelineState = NULL; -static DXGI_FORMAT g_RTVFormat = DXGI_FORMAT_UNKNOWN; -static ID3D12Resource* g_pFontTextureResource = NULL; -static D3D12_CPU_DESCRIPTOR_HANDLE g_hFontSrvCpuDescHandle = {}; -static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {}; - struct FrameResources { ID3D12Resource* IndexBuffer; @@ -63,9 +56,29 @@ struct FrameResources int IndexBufferSize; int VertexBufferSize; }; -static FrameResources* g_pFrameResources = NULL; -static UINT g_numFramesInFlight = 0; -static UINT g_frameIndex = UINT_MAX; + +struct ImGui_ImplDX12_Data +{ + ID3D12Device* pd3dDevice; + ID3D12RootSignature* pRootSignature; + ID3D12PipelineState* pPipelineState; + DXGI_FORMAT RTVFormat; + ID3D12Resource* pFontTextureResource; + D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle; + D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle; + + FrameResources* pFrameResources; + UINT numFramesInFlight; + UINT frameIndex; + + ImGui_ImplDX12_Data() { memset(this, 0, sizeof(*this)); frameIndex = UINT_MAX; } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplDX12_Data* g_Data; +static ImGui_ImplDX12_Data* ImGui_ImplDX12_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplDX12_Data); return g_Data; } +static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData() { return ImGui::GetCurrentContext() != NULL ? g_Data : NULL; } +static void ImGui_ImplDX12_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } template static void SafeRelease(T*& res) @@ -82,6 +95,8 @@ struct VERTEX_CONSTANT_BUFFER static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, FrameResources* fr) { + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + // 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). VERTEX_CONSTANT_BUFFER vertex_constant_buffer; @@ -126,8 +141,8 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; ctx->IASetIndexBuffer(&ibv); ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ctx->SetPipelineState(g_pPipelineState); - ctx->SetGraphicsRootSignature(g_pRootSignature); + ctx->SetPipelineState(bd->pPipelineState); + ctx->SetGraphicsRootSignature(bd->pRootSignature); ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0); // Setup blend factor @@ -144,8 +159,9 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL // 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]; + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + bd->frameIndex = bd->frameIndex + 1; + FrameResources* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight]; // Create and grow vertex/index buffers if needed if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) @@ -168,7 +184,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL desc.SampleDesc.Count = 1; desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.Flags = D3D12_RESOURCE_FLAG_NONE; - if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->VertexBuffer)) < 0) + if (bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->VertexBuffer)) < 0) return; } if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount) @@ -191,7 +207,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL desc.SampleDesc.Count = 1; desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.Flags = D3D12_RESOURCE_FLAG_NONE; - if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->IndexBuffer)) < 0) + if (bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->IndexBuffer)) < 0) return; } @@ -262,6 +278,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); @@ -289,7 +306,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() desc.Flags = D3D12_RESOURCE_FLAG_NONE; ID3D12Resource* pTexture = NULL; - g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, + bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&pTexture)); UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); @@ -311,7 +328,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; ID3D12Resource* uploadBuffer = NULL; - HRESULT hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, + HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer)); IM_ASSERT(SUCCEEDED(hr)); @@ -346,7 +363,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; ID3D12Fence* fence = NULL; - hr = g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); + hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); IM_ASSERT(SUCCEEDED(hr)); HANDLE event = CreateEvent(0, 0, 0, 0); @@ -358,15 +375,15 @@ static void ImGui_ImplDX12_CreateFontsTexture() queueDesc.NodeMask = 1; ID3D12CommandQueue* cmdQueue = NULL; - hr = g_pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue)); + hr = bd->pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue)); IM_ASSERT(SUCCEEDED(hr)); ID3D12CommandAllocator* cmdAlloc = NULL; - hr = g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); + hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); IM_ASSERT(SUCCEEDED(hr)); ID3D12GraphicsCommandList* cmdList = NULL; - hr = g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList)); + hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList)); IM_ASSERT(SUCCEEDED(hr)); cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL); @@ -397,9 +414,9 @@ static void ImGui_ImplDX12_CreateFontsTexture() srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, g_hFontSrvCpuDescHandle); - SafeRelease(g_pFontTextureResource); - g_pFontTextureResource = pTexture; + bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, bd->hFontSrvCpuDescHandle); + SafeRelease(bd->pFontTextureResource); + bd->pFontTextureResource = pTexture; } // Store our identifier @@ -410,15 +427,18 @@ static void ImGui_ImplDX12_CreateFontsTexture() // [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. // [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) // [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file) - static_assert(sizeof(ImTextureID) >= sizeof(g_hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); - io.Fonts->SetTexID((ImTextureID)g_hFontSrvGpuDescHandle.ptr); + static_assert(sizeof(ImTextureID) >= sizeof(bd->hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); + io.Fonts->SetTexID((ImTextureID)bd->hFontSrvGpuDescHandle.ptr); } bool ImGui_ImplDX12_CreateDeviceObjects() { - if (!g_pd3dDevice) + if (ImGui::GetCurrentContext() == NULL) return false; - if (g_pPipelineState) + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + if (!bd->pd3dDevice) + return false; + if (bd->pPipelineState) ImGui_ImplDX12_InvalidateDeviceObjects(); // Create the root signature @@ -499,7 +519,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, NULL) != S_OK) return false; - g_pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&g_pRootSignature)); + bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature)); blob->Release(); } @@ -513,10 +533,10 @@ bool ImGui_ImplDX12_CreateDeviceObjects() memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC)); psoDesc.NodeMask = 1; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - psoDesc.pRootSignature = g_pRootSignature; + psoDesc.pRootSignature = bd->pRootSignature; psoDesc.SampleMask = UINT_MAX; psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = g_RTVFormat; + psoDesc.RTVFormats[0] = bd->RTVFormat; psoDesc.SampleDesc.Count = 1; psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; @@ -635,7 +655,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() desc.BackFace = desc.FrontFace; } - HRESULT result_pipeline_state = g_pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&g_pPipelineState)); + HRESULT result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState)); vertexShaderBlob->Release(); pixelShaderBlob->Release(); if (result_pipeline_state != S_OK) @@ -648,19 +668,21 @@ bool ImGui_ImplDX12_CreateDeviceObjects() void ImGui_ImplDX12_InvalidateDeviceObjects() { - if (!g_pd3dDevice) + if (ImGui::GetCurrentContext() == NULL) + return; + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + if (!bd->pd3dDevice) return; - SafeRelease(g_pRootSignature); - SafeRelease(g_pPipelineState); - SafeRelease(g_pFontTextureResource); + SafeRelease(bd->pRootSignature); + SafeRelease(bd->pPipelineState); + SafeRelease(bd->pFontTextureResource); + io.Fonts->SetTexID(NULL); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. - ImGuiIO& io = ImGui::GetIO(); - io.Fonts->SetTexID(NULL); // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - - for (UINT i = 0; i < g_numFramesInFlight; i++) + for (UINT i = 0; i < bd->numFramesInFlight; i++) { - FrameResources* fr = &g_pFrameResources[i]; + FrameResources* fr = &bd->pFrameResources[i]; SafeRelease(fr->IndexBuffer); SafeRelease(fr->VertexBuffer); } @@ -669,24 +691,28 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) { - // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_CreateBackendData(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx12"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - g_pd3dDevice = device; - g_RTVFormat = rtv_format; - g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; - g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle; - g_pFrameResources = new FrameResources[num_frames_in_flight]; - g_numFramesInFlight = num_frames_in_flight; - g_frameIndex = UINT_MAX; + bd->pd3dDevice = device; + bd->RTVFormat = rtv_format; + bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; + bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle; + bd->pFrameResources = new FrameResources[num_frames_in_flight]; + bd->numFramesInFlight = num_frames_in_flight; + bd->frameIndex = UINT_MAX; IM_UNUSED(cbv_srv_heap); // Unused in master branch (will be used by multi-viewports) // 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]; + FrameResources* fr = &bd->pFrameResources[i]; fr->IndexBuffer = NULL; fr->VertexBuffer = NULL; fr->IndexBufferSize = 10000; @@ -698,18 +724,25 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO void ImGui_ImplDX12_Shutdown() { + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + ImGui_ImplDX12_InvalidateDeviceObjects(); - delete[] g_pFrameResources; - g_pFrameResources = NULL; - g_pd3dDevice = NULL; - g_hFontSrvCpuDescHandle.ptr = 0; - g_hFontSrvGpuDescHandle.ptr = 0; - g_numFramesInFlight = 0; - g_frameIndex = UINT_MAX; + delete[] bd->pFrameResources; + bd->pFrameResources = NULL; + bd->pd3dDevice = NULL; + bd->hFontSrvCpuDescHandle.ptr = 0; + bd->hFontSrvGpuDescHandle.ptr = 0; + bd->numFramesInFlight = 0; + bd->frameIndex = UINT_MAX; + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + ImGui_ImplDX12_DestroyBackendData(); } void ImGui_ImplDX12_NewFrame() { - if (!g_pPipelineState) + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + if (!bd->pPipelineState) ImGui_ImplDX12_CreateDeviceObjects(); } diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 959b9398..a2d426db 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -12,6 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-06-25: DirectX9: Explicitly disable texture state stages after >= 1. // 2021-05-19: DirectX9: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states. @@ -36,11 +37,23 @@ #include // DirectX data -static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; -static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; -static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL; -static LPDIRECT3DTEXTURE9 g_FontTexture = NULL; -static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; +struct ImGui_ImplDX9_Data +{ + LPDIRECT3DDEVICE9 pd3dDevice; + LPDIRECT3DVERTEXBUFFER9 pVB; + LPDIRECT3DINDEXBUFFER9 pIB; + LPDIRECT3DTEXTURE9 FontTexture; + int VertexBufferSize; + int IndexBufferSize; + + ImGui_ImplDX9_Data() { memset(this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplDX9_Data* g_Data; +static ImGui_ImplDX9_Data* ImGui_ImplDX9_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplDX9_Data); return g_Data; } +static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData() { return ImGui::GetCurrentContext() != NULL ? g_Data : NULL; } +static void ImGui_ImplDX9_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } struct CUSTOMVERTEX { @@ -56,8 +69,11 @@ struct CUSTOMVERTEX #define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16)) #endif +// Functions static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) { + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + // Setup viewport D3DVIEWPORT9 vp; vp.X = vp.Y = 0; @@ -65,41 +81,41 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) vp.Height = (DWORD)draw_data->DisplaySize.y; vp.MinZ = 0.0f; vp.MaxZ = 1.0f; - g_pd3dDevice->SetViewport(&vp); + bd->pd3dDevice->SetViewport(&vp); // Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient) - g_pd3dDevice->SetPixelShader(NULL); - g_pd3dDevice->SetVertexShader(NULL); - g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); - g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); - g_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); - g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); - g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE); - g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); - g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - g_pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); - g_pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE); - g_pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA); - g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); - g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); - g_pd3dDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE); - g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); - g_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); - g_pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE); - g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); - g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); - g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); - g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); - g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); - g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); - g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - g_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + bd->pd3dDevice->SetPixelShader(NULL); + bd->pd3dDevice->SetVertexShader(NULL); + bd->pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + bd->pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + bd->pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + bd->pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + bd->pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + bd->pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE); + bd->pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + bd->pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + bd->pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + bd->pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + bd->pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); + bd->pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE); + bd->pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA); + bd->pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + bd->pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + bd->pd3dDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE); + bd->pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + bd->pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); + bd->pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE); + bd->pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); + bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + bd->pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + bd->pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); // Setup orthographic projection matrix // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. @@ -117,9 +133,9 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) 0.0f, 0.0f, 0.5f, 0.0f, (L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f } } }; - g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity); - g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity); - g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection); + bd->pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity); + bd->pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity); + bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection); } } @@ -131,24 +147,25 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) return; // Create and grow buffers if needed - if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - g_VertexBufferSize = draw_data->TotalVtxCount + 5000; - if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) < 0) + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; + if (bd->pd3dDevice->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, NULL) < 0) return; } - if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) + if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) { - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - g_IndexBufferSize = draw_data->TotalIdxCount + 10000; - if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &g_pIB, NULL) < 0) + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; + if (bd->pd3dDevice->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, NULL) < 0) return; } // Backup the DX9 state IDirect3DStateBlock9* d3d9_state_block = NULL; - if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0) + if (bd->pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0) return; if (d3d9_state_block->Capture() < 0) { @@ -158,21 +175,21 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) // Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to) D3DMATRIX last_world, last_view, last_projection; - g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world); - g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view); - g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection); + bd->pd3dDevice->GetTransform(D3DTS_WORLD, &last_world); + bd->pd3dDevice->GetTransform(D3DTS_VIEW, &last_view); + bd->pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection); // Allocate buffers CUSTOMVERTEX* vtx_dst; ImDrawIdx* idx_dst; - if (g_pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0) + if (bd->pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0) { d3d9_state_block->Release(); return; } - if (g_pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0) + if (bd->pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0) { - g_pVB->Unlock(); + bd->pVB->Unlock(); d3d9_state_block->Release(); return; } @@ -199,11 +216,11 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); idx_dst += cmd_list->IdxBuffer.Size; } - g_pVB->Unlock(); - g_pIB->Unlock(); - g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX)); - g_pd3dDevice->SetIndices(g_pIB); - g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); + bd->pVB->Unlock(); + bd->pIB->Unlock(); + bd->pd3dDevice->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX)); + bd->pd3dDevice->SetIndices(bd->pIB); + bd->pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); // Setup desired DX state ImGui_ImplDX9_SetupRenderState(draw_data); @@ -232,9 +249,9 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) { const RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID(); - g_pd3dDevice->SetTexture(0, texture); - g_pd3dDevice->SetScissorRect(&r); - g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3); + bd->pd3dDevice->SetTexture(0, texture); + bd->pd3dDevice->SetScissorRect(&r); + bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3); } } global_idx_offset += cmd_list->IdxBuffer.Size; @@ -242,9 +259,9 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) } // Restore the DX9 transform - g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world); - g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view); - g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection); + bd->pd3dDevice->SetTransform(D3DTS_WORLD, &last_world); + bd->pd3dDevice->SetTransform(D3DTS_VIEW, &last_view); + bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection); // Restore the DX9 state d3d9_state_block->Apply(); @@ -253,26 +270,38 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) { - // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_CreateBackendData(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx9"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - g_pd3dDevice = device; - g_pd3dDevice->AddRef(); + bd->pd3dDevice = device; + bd->pd3dDevice->AddRef(); + return true; } void ImGui_ImplDX9_Shutdown() { + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + ImGui_ImplDX9_InvalidateDeviceObjects(); - if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } + if (bd->pd3dDevice) { bd->pd3dDevice->Release(); bd->pd3dDevice = NULL; } + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + ImGui_ImplDX9_DestroyBackendData(); } static bool ImGui_ImplDX9_CreateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); unsigned char* pixels; int width, height, bytes_per_pixel; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel); @@ -289,18 +318,18 @@ static bool ImGui_ImplDX9_CreateFontsTexture() #endif // Upload texture to graphics system - g_FontTexture = NULL; - if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0) + bd->FontTexture = NULL; + if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture, NULL) < 0) return false; D3DLOCKED_RECT tex_locked_rect; - if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK) + if (bd->FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK) return false; for (int y = 0; y < height; y++) memcpy((unsigned char*)tex_locked_rect.pBits + (size_t)tex_locked_rect.Pitch * y, pixels + (size_t)width * bytes_per_pixel * y, (size_t)width * bytes_per_pixel); - g_FontTexture->UnlockRect(0); + bd->FontTexture->UnlockRect(0); // Store our identifier - io.Fonts->SetTexID((ImTextureID)g_FontTexture); + io.Fonts->SetTexID((ImTextureID)bd->FontTexture); #ifndef IMGUI_USE_BGRA_PACKED_COLOR if (io.Fonts->TexPixelsUseColors) @@ -312,7 +341,8 @@ static bool ImGui_ImplDX9_CreateFontsTexture() bool ImGui_ImplDX9_CreateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + if (!bd || !bd->pd3dDevice) return false; if (!ImGui_ImplDX9_CreateFontsTexture()) return false; @@ -321,15 +351,17 @@ bool ImGui_ImplDX9_CreateDeviceObjects() void ImGui_ImplDX9_InvalidateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + if (!bd || !bd->pd3dDevice) return; - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - if (g_FontTexture) { g_FontTexture->Release(); g_FontTexture = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + if (bd->FontTexture) { bd->FontTexture->Release(); bd->FontTexture = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. } void ImGui_ImplDX9_NewFrame() { - if (!g_FontTexture) + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + if (!bd->FontTexture) ImGui_ImplDX9_CreateDeviceObjects(); } diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 860486ec..8e87c62d 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -9,13 +9,14 @@ // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. // 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). // 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. @@ -59,26 +60,39 @@ #define GLFW_HAS_NEW_CURSORS (0) #endif -// Data +// GLFW data enum GlfwClientApi { GlfwClientApi_Unknown, GlfwClientApi_OpenGL, GlfwClientApi_Vulkan }; -static GLFWwindow* g_Window = NULL; // Main window -static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; -static double g_Time = 0.0; -static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; -static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; -static bool g_InstalledCallbacks = false; -// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. -static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; -static GLFWscrollfun g_PrevUserCallbackScroll = NULL; -static GLFWkeyfun g_PrevUserCallbackKey = NULL; -static GLFWcharfun g_PrevUserCallbackChar = NULL; +struct ImGui_ImplGlfw_Data +{ + GLFWwindow* Window; + GlfwClientApi ClientApi; + double Time; + bool MouseJustPressed[ImGuiMouseButton_COUNT]; + GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + bool InstalledCallbacks; + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + GLFWmousebuttonfun PrevUserCallbackMousebutton; + GLFWscrollfun PrevUserCallbackScroll; + GLFWkeyfun PrevUserCallbackKey; + GLFWcharfun PrevUserCallbackChar; + + ImGui_ImplGlfw_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplGlfw_Data* g_Data; +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplGlfw_Data); return g_Data; } +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() { return ImGui::GetCurrentContext() != NULL ? g_Data : NULL; } +static void ImGui_ImplGlfw_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } + +// Functions static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) { return glfwGetClipboardString((GLFWwindow*)user_data); @@ -91,17 +105,19 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { - if (g_PrevUserCallbackMousebutton != NULL) - g_PrevUserCallbackMousebutton(window, button, action, mods); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackMousebutton != NULL) + bd->PrevUserCallbackMousebutton(window, button, action, mods); - if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) - g_MouseJustPressed[button] = true; + if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(bd->MouseJustPressed)) + bd->MouseJustPressed[button] = true; } void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { - if (g_PrevUserCallbackScroll != NULL) - g_PrevUserCallbackScroll(window, xoffset, yoffset); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackScroll != NULL) + bd->PrevUserCallbackScroll(window, xoffset, yoffset); ImGuiIO& io = ImGui::GetIO(); io.MouseWheelH += (float)xoffset; @@ -110,8 +126,9 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { - if (g_PrevUserCallbackKey != NULL) - g_PrevUserCallbackKey(window, key, scancode, action, mods); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackKey != NULL) + bd->PrevUserCallbackKey(window, key, scancode, action, mods); ImGuiIO& io = ImGui::GetIO(); if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)) @@ -135,8 +152,9 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int a void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) { - if (g_PrevUserCallbackChar != NULL) - g_PrevUserCallbackChar(window, c); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackChar != NULL) + bd->PrevUserCallbackChar(window, c); ImGuiIO& io = ImGui::GetIO(); io.AddInputCharacter(c); @@ -144,14 +162,18 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { - g_Window = window; - g_Time = 0.0; + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_CreateBackendData(); + bd->Window = window; + bd->Time = 0.0; // Setup backend capabilities flags - ImGuiIO& io = ImGui::GetIO(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_glfw"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - io.BackendPlatformName = "imgui_impl_glfw"; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; @@ -179,9 +201,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; - io.ClipboardUserData = g_Window; + io.ClipboardUserData = bd->Window; #if defined(_WIN32) - io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window); + io.ImeWindowHandle = (void*)glfwGetWin32Window(bd->Window); #endif // Create mouse cursors @@ -189,39 +211,39 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. // Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.) GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL); - g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); #if GLFW_HAS_NEW_CURSORS - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); #else - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); #endif glfwSetErrorCallback(prev_error_callback); // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. - g_PrevUserCallbackMousebutton = NULL; - g_PrevUserCallbackScroll = NULL; - g_PrevUserCallbackKey = NULL; - g_PrevUserCallbackChar = NULL; + bd->PrevUserCallbackMousebutton = NULL; + bd->PrevUserCallbackScroll = NULL; + bd->PrevUserCallbackKey = NULL; + bd->PrevUserCallbackChar = NULL; if (install_callbacks) { - g_InstalledCallbacks = true; - g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + bd->InstalledCallbacks = true; + bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); } - g_ClientApi = client_api; + bd->ClientApi = client_api; return true; } @@ -242,32 +264,40 @@ bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) void ImGui_ImplGlfw_Shutdown() { - if (g_InstalledCallbacks) + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + + if (bd->InstalledCallbacks) { - glfwSetMouseButtonCallback(g_Window, g_PrevUserCallbackMousebutton); - glfwSetScrollCallback(g_Window, g_PrevUserCallbackScroll); - glfwSetKeyCallback(g_Window, g_PrevUserCallbackKey); - glfwSetCharCallback(g_Window, g_PrevUserCallbackChar); - g_InstalledCallbacks = false; + glfwSetMouseButtonCallback(bd->Window, bd->PrevUserCallbackMousebutton); + glfwSetScrollCallback(bd->Window, bd->PrevUserCallbackScroll); + glfwSetKeyCallback(bd->Window, bd->PrevUserCallbackKey); + glfwSetCharCallback(bd->Window, bd->PrevUserCallbackChar); + bd->InstalledCallbacks = false; } for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) { - glfwDestroyCursor(g_MouseCursors[cursor_n]); - g_MouseCursors[cursor_n] = NULL; + glfwDestroyCursor(bd->MouseCursors[cursor_n]); + bd->MouseCursors[cursor_n] = NULL; } - g_ClientApi = GlfwClientApi_Unknown; + + io.BackendPlatformName = NULL; + io.BackendPlatformUserData = NULL; + ImGui_ImplGlfw_DestroyBackendData(); } static void ImGui_ImplGlfw_UpdateMousePosAndButtons() { + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + // Update buttons ImGuiIO& io = ImGui::GetIO(); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; + io.MouseDown[i] = bd->MouseJustPressed[i] || glfwGetMouseButton(bd->Window, i) != 0; + bd->MouseJustPressed[i] = false; } // Update mouse position @@ -276,18 +306,18 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() #ifdef __EMSCRIPTEN__ const bool focused = true; // Emscripten #else - const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0; + const bool focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0; #endif if (focused) { if (io.WantSetMousePos) { - glfwSetCursorPos(g_Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); + glfwSetCursorPos(bd->Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); } else { double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); + glfwGetCursorPos(bd->Window, &mouse_x, &mouse_y); io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); } } @@ -296,21 +326,22 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() static void ImGui_ImplGlfw_UpdateMouseCursor() { ImGuiIO& io = ImGui::GetIO(); - if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) return; ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); } else { // Show OS mouse cursor // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. - glfwSetCursor(g_Window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetCursor(bd->Window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } } @@ -354,20 +385,21 @@ static void ImGui_ImplGlfw_UpdateGamepads() void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); // Setup display size (every frame to accommodate for window resizing) int w, h; int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); + glfwGetWindowSize(bd->Window, &w, &h); + glfwGetFramebufferSize(bd->Window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); if (w > 0 && h > 0) io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); // Setup time step double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); - g_Time = current_time; + io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); + bd->Time = current_time; ImGui_ImplGlfw_UpdateMousePosAndButtons(); ImGui_ImplGlfw_UpdateMouseCursor(); diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 6ecc2a93..e45c0423 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -19,6 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-01-03: OpenGL: Backup, setup and restore GL_SHADE_MODEL state, disable GL_STENCIL_TEST and disable GL_NORMAL_ARRAY client state to increase compatibility with legacy OpenGL applications. // 2020-01-23: OpenGL: Backup, setup and restore GL_TEXTURE_ENV to increase compatibility with legacy OpenGL applications. @@ -55,26 +56,47 @@ #include #endif -// OpenGL Data -static GLuint g_FontTexture = 0; +struct ImGui_ImplOpenGL2_Data +{ + GLuint FontTexture; + + ImGui_ImplOpenGL2_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplOpenGL2_Data* g_Data; +static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplOpenGL2_Data); return g_Data; } +static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData() { return ImGui::GetCurrentContext() != NULL ? g_Data : NULL; } +static void ImGui_ImplOpenGL2_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } // Functions bool ImGui_ImplOpenGL2_Init() { - // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_CreateBackendData(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_opengl2"; + return true; } void ImGui_ImplOpenGL2_Shutdown() { + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL2_DestroyDeviceObjects(); + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + ImGui_ImplOpenGL2_DestroyBackendData(); } void ImGui_ImplOpenGL2_NewFrame() { - if (!g_FontTexture) + ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); + if (!bd->FontTexture) ImGui_ImplOpenGL2_CreateDeviceObjects(); } @@ -215,6 +237,7 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. @@ -222,15 +245,15 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture() // Upload texture to graphics system GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glGenTextures(1, &bd->FontTexture); + glBindTexture(GL_TEXTURE_2D, bd->FontTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontTexture); + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); // Restore state glBindTexture(GL_TEXTURE_2D, last_texture); @@ -240,12 +263,13 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture() void ImGui_ImplOpenGL2_DestroyFontsTexture() { - if (g_FontTexture) + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); + if (bd->FontTexture) { - ImGuiIO& io = ImGui::GetIO(); - glDeleteTextures(1, &g_FontTexture); + glDeleteTextures(1, &bd->FontTexture); io.Fonts->SetTexID(0); - g_FontTexture = 0; + bd->FontTexture = 0; } } diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 4b9b2e00..411a226b 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state. // 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader. // 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version. @@ -171,18 +172,37 @@ using namespace gl; #endif // OpenGL Data -static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2) -static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings. -static GLuint g_FontTexture = 0; -static GLuint g_ShaderHandle = 0; -static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location -static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location -static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; -static bool g_HasClipOrigin = false; +struct ImGui_ImplOpenGL3_Data +{ + GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2) + char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings. + GLuint FontTexture; + GLuint ShaderHandle; + GLint AttribLocationTex; // Uniforms location + GLint AttribLocationProjMtx; + GLuint AttribLocationVtxPos; // Vertex attributes location + GLuint AttribLocationVtxUV; + GLuint AttribLocationVtxColor; + unsigned int VboHandle, ElementsHandle; + bool HasClipOrigin; + + ImGui_ImplOpenGL3_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplOpenGL3_Data* g_Data; +static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplOpenGL3_Data); return g_Data; } +static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData() { IM_ASSERT(ImGui::GetCurrentContext() != NULL); return g_Data; } +static void ImGui_ImplOpenGL3_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } // Functions bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_CreateBackendData(); + // Query for GL version (e.g. 320 for GL 3.2) #if !defined(IMGUI_IMPL_OPENGL_ES2) GLint major = 0; @@ -195,16 +215,16 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) const char* gl_version = (const char*)glGetString(GL_VERSION); sscanf(gl_version, "%d.%d", &major, &minor); } - g_GlVersion = (GLuint)(major * 100 + minor * 10); + bd->GlVersion = (GLuint)(major * 100 + minor * 10); #else - g_GlVersion = 200; // GLES 2 + bd->GlVersion = 200; // GLES 2 #endif // Setup backend capabilities flags - ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_opengl3"; #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET - if (g_GlVersion >= 320) + if (bd->GlVersion >= 320) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. #endif @@ -223,9 +243,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) if (glsl_version == NULL) glsl_version = "#version 130"; #endif - IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); - strcpy(g_GlslVersionString, glsl_version); - strcat(g_GlslVersionString, "\n"); + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString)); + strcpy(bd->GlslVersionString, glsl_version); + strcat(bd->GlslVersionString, "\n"); // Debugging construct to make it easily visible in the IDE and debugger which GL loader has been selected. // The code actually never uses the 'gl_loader' variable! It is only here so you can read it! @@ -259,7 +279,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); // Detect extensions we support - g_HasClipOrigin = (g_GlVersion >= 450); + bd->HasClipOrigin = (bd->GlVersion >= 450); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS GLint num_extensions = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); @@ -267,7 +287,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); if (extension != NULL && strcmp(extension, "GL_ARB_clip_control") == 0) - g_HasClipOrigin = true; + bd->HasClipOrigin = true; } #endif @@ -276,17 +296,24 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) void ImGui_ImplOpenGL3_Shutdown() { + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL3_DestroyDeviceObjects(); + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + ImGui_ImplOpenGL3_DestroyBackendData(); } void ImGui_ImplOpenGL3_NewFrame() { - if (!g_ShaderHandle) + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (!bd->ShaderHandle) ImGui_ImplOpenGL3_CreateDeviceObjects(); } static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) { + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); @@ -296,7 +323,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid glDisable(GL_STENCIL_TEST); glEnable(GL_SCISSOR_TEST); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART - if (g_GlVersion >= 310) + if (bd->GlVersion >= 310) glDisable(GL_PRIMITIVE_RESTART); #endif #ifdef GL_POLYGON_MODE @@ -306,7 +333,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) #if defined(GL_CLIP_ORIGIN) bool clip_origin_lower_left = true; - if (g_HasClipOrigin) + if (bd->HasClipOrigin) { GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin); if (current_clip_origin == GL_UPPER_LEFT) @@ -331,12 +358,12 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid { 0.0f, 0.0f, -1.0f, 0.0f }, { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, }; - glUseProgram(g_ShaderHandle); - glUniform1i(g_AttribLocationTex, 0); - glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); + glUseProgram(bd->ShaderHandle); + glUniform1i(bd->AttribLocationTex, 0); + glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - if (g_GlVersion >= 330) + if (bd->GlVersion >= 330) glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. #endif @@ -346,14 +373,14 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid #endif // Bind vertex/index buffers and setup attributes for ImDrawVert - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); - glEnableVertexAttribArray(g_AttribLocationVtxPos); - glEnableVertexAttribArray(g_AttribLocationVtxUV); - glEnableVertexAttribArray(g_AttribLocationVtxColor); - glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); - glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); - glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); + glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle); + glEnableVertexAttribArray(bd->AttribLocationVtxPos); + glEnableVertexAttribArray(bd->AttribLocationVtxUV); + glEnableVertexAttribArray(bd->AttribLocationVtxColor); + glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); + glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); + glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); } // OpenGL3 Render function. @@ -367,13 +394,15 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (fb_width <= 0 || fb_height <= 0) return; + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + // Backup GL state GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); glActiveTexture(GL_TEXTURE0); GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program); GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - GLuint last_sampler; if (g_GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; } + GLuint last_sampler; if (bd->GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; } #endif GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer); #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY @@ -396,7 +425,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST); GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART - GLboolean last_enable_primitive_restart = (g_GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE; + GLboolean last_enable_primitive_restart = (bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE; #endif // Setup desired GL state @@ -450,7 +479,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // Bind texture, Draw glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET - if (g_GlVersion >= 320) + if (bd->GlVersion >= 320) glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset); else #endif @@ -469,7 +498,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glUseProgram(last_program); glBindTexture(GL_TEXTURE_2D, last_texture); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - if (g_GlVersion >= 330) + if (bd->GlVersion >= 330) glBindSampler(0, last_sampler); #endif glActiveTexture(last_active_texture); @@ -485,7 +514,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST); if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART - if (g_GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); } + if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); } #endif #ifdef GL_POLYGON_MODE @@ -497,8 +526,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) bool ImGui_ImplOpenGL3_CreateFontsTexture() { - // Build texture atlas ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + + // Build texture atlas unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. @@ -506,8 +537,8 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() // Upload texture to graphics system GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glGenTextures(1, &bd->FontTexture); + glBindTexture(GL_TEXTURE_2D, bd->FontTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); #ifdef GL_UNPACK_ROW_LENGTH @@ -516,7 +547,7 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontTexture); + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); // Restore state glBindTexture(GL_TEXTURE_2D, last_texture); @@ -526,23 +557,25 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() void ImGui_ImplOpenGL3_DestroyFontsTexture() { - if (g_FontTexture) + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (bd->FontTexture) { - ImGuiIO& io = ImGui::GetIO(); - glDeleteTextures(1, &g_FontTexture); + glDeleteTextures(1, &bd->FontTexture); io.Fonts->SetTexID(0); - g_FontTexture = 0; + bd->FontTexture = 0; } } // If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. static bool CheckShader(GLuint handle, const char* desc) { + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); GLint status = 0, log_length = 0; glGetShaderiv(handle, GL_COMPILE_STATUS, &status); glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); if ((GLboolean)status == GL_FALSE) - fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString); if (log_length > 1) { ImVector buf; @@ -556,11 +589,12 @@ static bool CheckShader(GLuint handle, const char* desc) // If you get an error please report on GitHub. You may try different GL context version or GLSL version. static bool CheckProgram(GLuint handle, const char* desc) { + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); GLint status = 0, log_length = 0; glGetProgramiv(handle, GL_LINK_STATUS, &status); glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); if ((GLboolean)status == GL_FALSE) - fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString); + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString); if (log_length > 1) { ImVector buf; @@ -573,6 +607,8 @@ static bool CheckProgram(GLuint handle, const char* desc) bool ImGui_ImplOpenGL3_CreateDeviceObjects() { + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + // Backup GL state GLint last_texture, last_array_buffer; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); @@ -584,7 +620,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() // Parse GLSL version string int glsl_version = 130; - sscanf(g_GlslVersionString, "#version %d", &glsl_version); + sscanf(bd->GlslVersionString, "#version %d", &glsl_version); const GLchar* vertex_shader_glsl_120 = "uniform mat4 ProjMtx;\n" @@ -711,39 +747,39 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() } // Create shaders - const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader }; + const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader }; GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL); glCompileShader(vert_handle); CheckShader(vert_handle, "vertex shader"); - const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader }; + const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader }; GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL); glCompileShader(frag_handle); CheckShader(frag_handle, "fragment shader"); // Link - g_ShaderHandle = glCreateProgram(); - glAttachShader(g_ShaderHandle, vert_handle); - glAttachShader(g_ShaderHandle, frag_handle); - glLinkProgram(g_ShaderHandle); - CheckProgram(g_ShaderHandle, "shader program"); + bd->ShaderHandle = glCreateProgram(); + glAttachShader(bd->ShaderHandle, vert_handle); + glAttachShader(bd->ShaderHandle, frag_handle); + glLinkProgram(bd->ShaderHandle); + CheckProgram(bd->ShaderHandle, "shader program"); - glDetachShader(g_ShaderHandle, vert_handle); - glDetachShader(g_ShaderHandle, frag_handle); + glDetachShader(bd->ShaderHandle, vert_handle); + glDetachShader(bd->ShaderHandle, frag_handle); glDeleteShader(vert_handle); glDeleteShader(frag_handle); - g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); - g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); - g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position"); - g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV"); - g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color"); + bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture"); + bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx"); + bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position"); + bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV"); + bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color"); // Create buffers - glGenBuffers(1, &g_VboHandle); - glGenBuffers(1, &g_ElementsHandle); + glGenBuffers(1, &bd->VboHandle); + glGenBuffers(1, &bd->ElementsHandle); ImGui_ImplOpenGL3_CreateFontsTexture(); @@ -759,9 +795,9 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() void ImGui_ImplOpenGL3_DestroyDeviceObjects() { - if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; } - if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; } - if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; } - + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; } + if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; } + if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; } ImGui_ImplOpenGL3_DestroyFontsTexture(); } diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 2135f81d..9392216f 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950) // 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends. // 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2). @@ -59,20 +60,32 @@ #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) -// Data -static SDL_Window* g_Window = NULL; -static Uint64 g_Time = 0; -static bool g_MousePressed[3] = { false, false, false }; -static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; -static char* g_ClipboardTextData = NULL; -static bool g_MouseCanUseGlobalState = true; +struct ImGui_ImplSDL2_Data +{ + SDL_Window* Window; + Uint64 Time; + bool MousePressed[3]; + SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; + char* ClipboardTextData; + bool MouseCanUseGlobalState; + ImGui_ImplSDL2_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Wrapping access to backend data data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplSDL2_Data* g_Data; +static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplSDL2_Data); return g_Data; } +static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData() { return ImGui::GetCurrentContext() != NULL ? g_Data : NULL; } +static void ImGui_ImplSDL2_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } + +// Functions static const char* ImGui_ImplSDL2_GetClipboardText(void*) { - if (g_ClipboardTextData) - SDL_free(g_ClipboardTextData); - g_ClipboardTextData = SDL_GetClipboardText(); - return g_ClipboardTextData; + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + if (bd->ClipboardTextData) + SDL_free(bd->ClipboardTextData); + bd->ClipboardTextData = SDL_GetClipboardText(); + return bd->ClipboardTextData; } static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) @@ -88,6 +101,8 @@ static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + switch (event->type) { case SDL_MOUSEWHEEL: @@ -100,9 +115,9 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_MOUSEBUTTONDOWN: { - if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true; - if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true; - if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true; + if (event->button.button == SDL_BUTTON_LEFT) { bd->MousePressed[0] = true; } + if (event->button.button == SDL_BUTTON_RIGHT) { bd->MousePressed[1] = true; } + if (event->button.button == SDL_BUTTON_MIDDLE) { bd->MousePressed[2] = true; } return true; } case SDL_TEXTINPUT: @@ -132,13 +147,17 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) static bool ImGui_ImplSDL2_Init(SDL_Window* window) { - g_Window = window; + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_CreateBackendData(); + bd->Window = window; // Setup backend capabilities flags - ImGuiIO& io = ImGui::GetIO(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_sdl"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - io.BackendPlatformName = "imgui_impl_sdl"; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; @@ -169,24 +188,24 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) io.ClipboardUserData = NULL; // Load mouse cursors - g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); - g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); - g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); - g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); - g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); - g_MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); + bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); + bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); // Check and store if we are on a SDL backend that supports global mouse position // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) const char* sdl_backend = SDL_GetCurrentVideoDriver(); const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; - g_MouseCanUseGlobalState = false; + bd->MouseCanUseGlobalState = false; for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) - g_MouseCanUseGlobalState = true; + bd->MouseCanUseGlobalState = true; #ifdef _WIN32 SDL_SysWMinfo wmInfo; @@ -202,7 +221,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { - (void)sdl_gl_context; // Viewport branch will need this. + IM_UNUSED(sdl_gl_context); // Viewport branch will need this. return ImGui_ImplSDL2_Init(window); } @@ -229,41 +248,48 @@ bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window) void ImGui_ImplSDL2_Shutdown() { - g_Window = NULL; + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + bd->Window = NULL; // Destroy last known clipboard data - if (g_ClipboardTextData) - SDL_free(g_ClipboardTextData); - g_ClipboardTextData = NULL; + if (bd->ClipboardTextData) + SDL_free(bd->ClipboardTextData); + bd->ClipboardTextData = NULL; // Destroy SDL mouse cursors for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) - SDL_FreeCursor(g_MouseCursors[cursor_n]); - memset(g_MouseCursors, 0, sizeof(g_MouseCursors)); + SDL_FreeCursor(bd->MouseCursors[cursor_n]); + memset(bd->MouseCursors, 0, sizeof(bd->MouseCursors)); + + io.BackendPlatformName = NULL; + io.BackendPlatformUserData = NULL; + ImGui_ImplSDL2_DestroyBackendData(); } static void ImGui_ImplSDL2_UpdateMousePosAndButtons() { ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) if (io.WantSetMousePos) - SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y); + SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y); else io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); int mx, my; Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); - io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; - io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; - g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; + io.MouseDown[0] = bd->MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[1] = bd->MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; + io.MouseDown[2] = bd->MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; + bd->MousePressed[0] = bd->MousePressed[1] = bd->MousePressed[2] = false; #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) SDL_Window* focused_window = SDL_GetKeyboardFocus(); - if (g_Window == focused_window) + if (bd->Window == focused_window) { - if (g_MouseCanUseGlobalState) + if (bd->MouseCanUseGlobalState) { // SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?) // The creation of a new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally. @@ -282,7 +308,7 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() bool any_mouse_button_down = ImGui::IsAnyMouseDown(); SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); #else - if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) + if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) io.MousePos = ImVec2((float)mx, (float)my); #endif } @@ -292,6 +318,7 @@ static void ImGui_ImplSDL2_UpdateMouseCursor() ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return; + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) @@ -302,7 +329,7 @@ static void ImGui_ImplSDL2_UpdateMouseCursor() else { // Show OS mouse cursor - SDL_SetCursor(g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); + SDL_SetCursor(bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); SDL_ShowCursor(SDL_TRUE); } } @@ -351,6 +378,8 @@ static void ImGui_ImplSDL2_UpdateGamepads() void ImGui_ImplSDL2_NewFrame(SDL_Window* window) { ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + IM_ASSERT(bd->Window == window); // FIXME: Should remove parameter from ImGui_ImplSDL2_NewFrame() // Setup display size (every frame to accommodate for window resizing) int w, h; @@ -366,8 +395,8 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) static Uint64 frequency = SDL_GetPerformanceFrequency(); Uint64 current_time = SDL_GetPerformanceCounter(); - io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f); - g_Time = current_time; + io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); + bd->Time = current_time; ImGui_ImplSDL2_UpdateMousePosAndButtons(); ImGui_ImplSDL2_UpdateMouseCursor(); diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 4f5b5a0e..f5d63b24 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -6,7 +6,7 @@ // Missing features: // [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this backend! See https://github.com/ocornut/imgui/pull/914 -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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 @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. // 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. // 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions(). @@ -77,33 +78,43 @@ struct ImGui_ImplVulkanH_WindowRenderBuffers }; // Vulkan data -static ImGui_ImplVulkan_InitInfo g_VulkanInitInfo = {}; -static VkRenderPass g_RenderPass = VK_NULL_HANDLE; -static VkDeviceSize g_BufferMemoryAlignment = 256; -static VkPipelineCreateFlags g_PipelineCreateFlags = 0x00; -static VkDescriptorSetLayout g_DescriptorSetLayout = VK_NULL_HANDLE; -static VkPipelineLayout g_PipelineLayout = VK_NULL_HANDLE; -static VkDescriptorSet g_DescriptorSet = VK_NULL_HANDLE; -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 +struct ImGui_ImplVulkan_Data +{ + ImGui_ImplVulkan_InitInfo VulkanInitInfo; + VkRenderPass RenderPass; + VkDeviceSize BufferMemoryAlignment; + VkPipelineCreateFlags PipelineCreateFlags; + VkDescriptorSetLayout DescriptorSetLayout; + VkPipelineLayout PipelineLayout; + VkDescriptorSet DescriptorSet; + VkPipeline Pipeline; + uint32_t Subpass; + VkShaderModule ShaderModuleVert; + VkShaderModule ShaderModuleFrag; -// Font data -static VkSampler g_FontSampler = VK_NULL_HANDLE; -static VkDeviceMemory g_FontMemory = VK_NULL_HANDLE; -static VkImage g_FontImage = VK_NULL_HANDLE; -static VkImageView g_FontView = VK_NULL_HANDLE; -static VkDeviceMemory g_UploadBufferMemory = VK_NULL_HANDLE; -static VkBuffer g_UploadBuffer = VK_NULL_HANDLE; + // Font data + VkSampler FontSampler; + VkDeviceMemory FontMemory; + VkImage FontImage; + VkImageView FontView; + VkDeviceMemory UploadBufferMemory; + VkBuffer UploadBuffer; -// Render buffers -static ImGui_ImplVulkanH_WindowRenderBuffers g_MainWindowRenderBuffers; + // Render buffers + ImGui_ImplVulkanH_WindowRenderBuffers MainWindowRenderBuffers; + + ImGui_ImplVulkan_Data() + { + memset(this, 0, sizeof(*this)); + BufferMemoryAlignment = 256; + } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplVulkan_Data* g_Data; +static ImGui_ImplVulkan_Data* ImGui_ImplVulkan_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplVulkan_Data); return g_Data; } +static ImGui_ImplVulkan_Data* ImGui_ImplVulkan_GetBackendData() { return ImGui::GetCurrentContext() ? g_Data : NULL; } +static void ImGui_ImplVulkan_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } // Forward Declarations bool ImGui_ImplVulkan_CreateDeviceObjects(); @@ -118,6 +129,11 @@ void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_devi // Vulkan prototypes for use with custom loaders // (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h #ifdef VK_NO_PROTOTYPES +static bool g_FunctionsLoaded = false; +#else +static bool g_FunctionsLoaded = true; +#endif +#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) \ @@ -299,7 +315,8 @@ static uint32_t __glsl_shader_frag_spv[] = static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, uint32_t type_bits) { - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkPhysicalDeviceMemoryProperties prop; vkGetPhysicalDeviceMemoryProperties(v->PhysicalDevice, &prop); for (uint32_t i = 0; i < prop.memoryTypeCount; i++) @@ -310,21 +327,25 @@ static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, ui static void check_vk_result(VkResult err) { - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (!bd) + return; + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; if (v->CheckVkResultFn) v->CheckVkResultFn(err); } static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& p_buffer_size, size_t new_size, VkBufferUsageFlagBits usage) { - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err; if (buffer != VK_NULL_HANDLE) vkDestroyBuffer(v->Device, buffer, v->Allocator); if (buffer_memory != VK_NULL_HANDLE) vkFreeMemory(v->Device, buffer_memory, v->Allocator); - VkDeviceSize vertex_buffer_size_aligned = ((new_size - 1) / g_BufferMemoryAlignment + 1) * g_BufferMemoryAlignment; + VkDeviceSize vertex_buffer_size_aligned = ((new_size - 1) / bd->BufferMemoryAlignment + 1) * bd->BufferMemoryAlignment; VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = vertex_buffer_size_aligned; @@ -335,7 +356,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory VkMemoryRequirements req; vkGetBufferMemoryRequirements(v->Device, buffer, &req); - g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; + bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; @@ -350,11 +371,13 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) { + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + // Bind pipeline and descriptor sets: { vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - VkDescriptorSet desc_set[1] = { g_DescriptorSet }; - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_PipelineLayout, 0, 1, desc_set, 0, NULL); + VkDescriptorSet desc_set[1] = { bd->DescriptorSet }; + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, NULL); } // Bind Vertex And Index Buffer: @@ -387,8 +410,8 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline float translate[2]; translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; - vkCmdPushConstants(command_buffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); - vkCmdPushConstants(command_buffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); + vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); + vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); } } @@ -401,12 +424,13 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (fb_width <= 0 || fb_height <= 0) return; - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; if (pipeline == VK_NULL_HANDLE) - pipeline = g_Pipeline; + pipeline = bd->Pipeline; // Allocate array to store enough vertex/index buffers - ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &g_MainWindowRenderBuffers; + ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers; if (wrb->FrameRenderBuffers == NULL) { wrb->Index = 0; @@ -519,8 +543,9 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) { - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; unsigned char* pixels; int width, height; @@ -545,17 +570,17 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - err = vkCreateImage(v->Device, &info, v->Allocator, &g_FontImage); + err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage); check_vk_result(err); VkMemoryRequirements req; - vkGetImageMemoryRequirements(v->Device, g_FontImage, &req); + vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &g_FontMemory); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory); check_vk_result(err); - err = vkBindImageMemory(v->Device, g_FontImage, g_FontMemory, 0); + err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0); check_vk_result(err); } @@ -563,25 +588,25 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) { VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.image = g_FontImage; + info.image = bd->FontImage; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; - err = vkCreateImageView(v->Device, &info, v->Allocator, &g_FontView); + err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView); check_vk_result(err); } // Update the Descriptor Set: { VkDescriptorImageInfo desc_image[1] = {}; - desc_image[0].sampler = g_FontSampler; - desc_image[0].imageView = g_FontView; + desc_image[0].sampler = bd->FontSampler; + desc_image[0].imageView = bd->FontView; desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet write_desc[1] = {}; write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_desc[0].dstSet = g_DescriptorSet; + write_desc[0].dstSet = bd->DescriptorSet; write_desc[0].descriptorCount = 1; write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write_desc[0].pImageInfo = desc_image; @@ -595,34 +620,34 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) buffer_info.size = upload_size; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &g_UploadBuffer); + err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &bd->UploadBuffer); check_vk_result(err); VkMemoryRequirements req; - vkGetBufferMemoryRequirements(v->Device, g_UploadBuffer, &req); - g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; + vkGetBufferMemoryRequirements(v->Device, bd->UploadBuffer, &req); + bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &g_UploadBufferMemory); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->UploadBufferMemory); check_vk_result(err); - err = vkBindBufferMemory(v->Device, g_UploadBuffer, g_UploadBufferMemory, 0); + err = vkBindBufferMemory(v->Device, bd->UploadBuffer, bd->UploadBufferMemory, 0); check_vk_result(err); } // Upload to Buffer: { char* map = NULL; - err = vkMapMemory(v->Device, g_UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); + err = vkMapMemory(v->Device, bd->UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); check_vk_result(err); memcpy(map, pixels, upload_size); VkMappedMemoryRange range[1] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - range[0].memory = g_UploadBufferMemory; + range[0].memory = bd->UploadBufferMemory; range[0].size = upload_size; err = vkFlushMappedMemoryRanges(v->Device, 1, range); check_vk_result(err); - vkUnmapMemory(v->Device, g_UploadBufferMemory); + vkUnmapMemory(v->Device, bd->UploadBufferMemory); } // Copy to Image: @@ -634,7 +659,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barrier[0].image = g_FontImage; + copy_barrier[0].image = bd->FontImage; copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_barrier[0].subresourceRange.levelCount = 1; copy_barrier[0].subresourceRange.layerCount = 1; @@ -646,7 +671,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) region.imageExtent.width = width; region.imageExtent.height = height; region.imageExtent.depth = 1; - vkCmdCopyBufferToImage(command_buffer, g_UploadBuffer, g_FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + vkCmdCopyBufferToImage(command_buffer, bd->UploadBuffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); VkImageMemoryBarrier use_barrier[1] = {}; use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -656,7 +681,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - use_barrier[0].image = g_FontImage; + use_barrier[0].image = bd->FontImage; use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; use_barrier[0].subresourceRange.levelCount = 1; use_barrier[0].subresourceRange.layerCount = 1; @@ -664,7 +689,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) } // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontImage); + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontImage); return true; } @@ -672,29 +697,31 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) { // Create the shader modules - if (g_ShaderModuleVert == NULL) + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (bd->ShaderModuleVert == NULL) { VkShaderModuleCreateInfo vert_info = {}; vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; vert_info.codeSize = sizeof(__glsl_shader_vert_spv); vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; - VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &g_ShaderModuleVert); + VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &bd->ShaderModuleVert); check_vk_result(err); } - if (g_ShaderModuleFrag == NULL) + if (bd->ShaderModuleFrag == NULL) { VkShaderModuleCreateInfo frag_info = {}; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; frag_info.codeSize = sizeof(__glsl_shader_frag_spv); frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; - VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &g_ShaderModuleFrag); + VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &bd->ShaderModuleFrag); check_vk_result(err); } } static void ImGui_ImplVulkan_CreateFontSampler(VkDevice device, const VkAllocationCallbacks* allocator) { - if (g_FontSampler) + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (bd->FontSampler) return; VkSamplerCreateInfo info = {}; @@ -708,17 +735,18 @@ static void ImGui_ImplVulkan_CreateFontSampler(VkDevice device, const VkAllocati info.minLod = -1000; info.maxLod = 1000; info.maxAnisotropy = 1.0f; - VkResult err = vkCreateSampler(device, &info, allocator, &g_FontSampler); + VkResult err = vkCreateSampler(device, &info, allocator, &bd->FontSampler); check_vk_result(err); } static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDevice device, const VkAllocationCallbacks* allocator) { - if (g_DescriptorSetLayout) + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (bd->DescriptorSetLayout) return; ImGui_ImplVulkan_CreateFontSampler(device, allocator); - VkSampler sampler[1] = { g_FontSampler }; + VkSampler sampler[1] = { bd->FontSampler }; VkDescriptorSetLayoutBinding binding[1] = {}; binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binding[0].descriptorCount = 1; @@ -728,13 +756,14 @@ static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDevice device, const Vk info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; info.bindingCount = 1; info.pBindings = binding; - VkResult err = vkCreateDescriptorSetLayout(device, &info, allocator, &g_DescriptorSetLayout); + VkResult err = vkCreateDescriptorSetLayout(device, &info, allocator, &bd->DescriptorSetLayout); check_vk_result(err); } static void ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device, const VkAllocationCallbacks* allocator) { - if (g_PipelineLayout) + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (bd->PipelineLayout) return; // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix @@ -743,29 +772,30 @@ static void ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device, const VkAlloc push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; push_constants[0].offset = sizeof(float) * 0; push_constants[0].size = sizeof(float) * 4; - VkDescriptorSetLayout set_layout[1] = { g_DescriptorSetLayout }; + VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout }; VkPipelineLayoutCreateInfo layout_info = {}; layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layout_info.setLayoutCount = 1; layout_info.pSetLayouts = set_layout; layout_info.pushConstantRangeCount = 1; layout_info.pPushConstantRanges = push_constants; - VkResult err = vkCreatePipelineLayout(device, &layout_info, allocator, &g_PipelineLayout); + VkResult err = vkCreatePipelineLayout(device, &layout_info, allocator, &bd->PipelineLayout); check_vk_result(err); } static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) { + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_CreateShaderModules(device, allocator); VkPipelineShaderStageCreateInfo stage[2] = {}; stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - stage[0].module = g_ShaderModuleVert; + stage[0].module = bd->ShaderModuleVert; stage[0].pName = "main"; stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - stage[1].module = g_ShaderModuleFrag; + stage[1].module = bd->ShaderModuleFrag; stage[1].pName = "main"; VkVertexInputBindingDescription binding_desc[1] = {}; @@ -841,7 +871,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC VkGraphicsPipelineCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - info.flags = g_PipelineCreateFlags; + info.flags = bd->PipelineCreateFlags; info.stageCount = 2; info.pStages = stage; info.pVertexInputState = &vertex_info; @@ -852,7 +882,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC info.pDepthStencilState = &depth_info; info.pColorBlendState = &blend_info; info.pDynamicState = &dynamic_state; - info.layout = g_PipelineLayout; + info.layout = bd->PipelineLayout; info.renderPass = renderPass; info.subpass = subpass; VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); @@ -861,10 +891,11 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC bool ImGui_ImplVulkan_CreateDeviceObjects() { - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err; - if (!g_FontSampler) + if (!bd->FontSampler) { VkSamplerCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; @@ -877,13 +908,13 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() info.minLod = -1000; info.maxLod = 1000; info.maxAnisotropy = 1.0f; - err = vkCreateSampler(v->Device, &info, v->Allocator, &g_FontSampler); + err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->FontSampler); check_vk_result(err); } - if (!g_DescriptorSetLayout) + if (!bd->DescriptorSetLayout) { - VkSampler sampler[1] = {g_FontSampler}; + VkSampler sampler[1] = {bd->FontSampler}; VkDescriptorSetLayoutBinding binding[1] = {}; binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binding[0].descriptorCount = 1; @@ -893,7 +924,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; info.bindingCount = 1; info.pBindings = binding; - err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, &g_DescriptorSetLayout); + err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, &bd->DescriptorSetLayout); check_vk_result(err); } @@ -903,64 +934,66 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = v->DescriptorPool; alloc_info.descriptorSetCount = 1; - alloc_info.pSetLayouts = &g_DescriptorSetLayout; - err = vkAllocateDescriptorSets(v->Device, &alloc_info, &g_DescriptorSet); + alloc_info.pSetLayouts = &bd->DescriptorSetLayout; + err = vkAllocateDescriptorSets(v->Device, &alloc_info, &bd->DescriptorSet); check_vk_result(err); } - if (!g_PipelineLayout) + if (!bd->PipelineLayout) { // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix VkPushConstantRange push_constants[1] = {}; push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; push_constants[0].offset = sizeof(float) * 0; push_constants[0].size = sizeof(float) * 4; - VkDescriptorSetLayout set_layout[1] = { g_DescriptorSetLayout }; + VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout }; VkPipelineLayoutCreateInfo layout_info = {}; layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layout_info.setLayoutCount = 1; layout_info.pSetLayouts = set_layout; layout_info.pushConstantRangeCount = 1; layout_info.pPushConstantRanges = push_constants; - err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &g_PipelineLayout); + err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &bd->PipelineLayout); check_vk_result(err); } - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, g_RenderPass, v->MSAASamples, &g_Pipeline, g_Subpass); + ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, bd->RenderPass, v->MSAASamples, &bd->Pipeline, bd->Subpass); return true; } void ImGui_ImplVulkan_DestroyFontUploadObjects() { - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; - if (g_UploadBuffer) + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (bd->UploadBuffer) { - vkDestroyBuffer(v->Device, g_UploadBuffer, v->Allocator); - g_UploadBuffer = VK_NULL_HANDLE; + vkDestroyBuffer(v->Device, bd->UploadBuffer, v->Allocator); + bd->UploadBuffer = VK_NULL_HANDLE; } - if (g_UploadBufferMemory) + if (bd->UploadBufferMemory) { - vkFreeMemory(v->Device, g_UploadBufferMemory, v->Allocator); - g_UploadBufferMemory = VK_NULL_HANDLE; + vkFreeMemory(v->Device, bd->UploadBufferMemory, v->Allocator); + bd->UploadBufferMemory = VK_NULL_HANDLE; } } void ImGui_ImplVulkan_DestroyDeviceObjects() { - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &g_MainWindowRenderBuffers, v->Allocator); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); ImGui_ImplVulkan_DestroyFontUploadObjects(); - if (g_ShaderModuleVert) { vkDestroyShaderModule(v->Device, g_ShaderModuleVert, v->Allocator); g_ShaderModuleVert = VK_NULL_HANDLE; } - if (g_ShaderModuleFrag) { vkDestroyShaderModule(v->Device, g_ShaderModuleFrag, v->Allocator); g_ShaderModuleFrag = VK_NULL_HANDLE; } - if (g_FontView) { vkDestroyImageView(v->Device, g_FontView, v->Allocator); g_FontView = VK_NULL_HANDLE; } - if (g_FontImage) { vkDestroyImage(v->Device, g_FontImage, v->Allocator); g_FontImage = VK_NULL_HANDLE; } - if (g_FontMemory) { vkFreeMemory(v->Device, g_FontMemory, v->Allocator); g_FontMemory = VK_NULL_HANDLE; } - if (g_FontSampler) { vkDestroySampler(v->Device, g_FontSampler, v->Allocator); g_FontSampler = VK_NULL_HANDLE; } - if (g_DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, g_DescriptorSetLayout, v->Allocator); g_DescriptorSetLayout = VK_NULL_HANDLE; } - if (g_PipelineLayout) { vkDestroyPipelineLayout(v->Device, g_PipelineLayout, v->Allocator); g_PipelineLayout = VK_NULL_HANDLE; } - if (g_Pipeline) { vkDestroyPipeline(v->Device, g_Pipeline, v->Allocator); g_Pipeline = VK_NULL_HANDLE; } + if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } + if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } + if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; } + if (bd->FontImage) { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; } + if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; } + if (bd->FontSampler) { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; } + if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } + if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } + if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } } bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) @@ -988,8 +1021,12 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend { 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(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_CreateBackendData(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_vulkan"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. @@ -1002,9 +1039,9 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend IM_ASSERT(info->ImageCount >= info->MinImageCount); IM_ASSERT(render_pass != VK_NULL_HANDLE); - g_VulkanInitInfo = *info; - g_RenderPass = render_pass; - g_Subpass = info->Subpass; + bd->VulkanInitInfo = *info; + bd->RenderPass = render_pass; + bd->Subpass = info->Subpass; ImGui_ImplVulkan_CreateDeviceObjects(); @@ -1013,7 +1050,11 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend void ImGui_ImplVulkan_Shutdown() { + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_DestroyDeviceObjects(); + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + ImGui_ImplVulkan_DestroyBackendData(); } void ImGui_ImplVulkan_NewFrame() @@ -1022,15 +1063,16 @@ void ImGui_ImplVulkan_NewFrame() void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) { + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); IM_ASSERT(min_image_count >= 2); - if (g_VulkanInitInfo.MinImageCount == min_image_count) + if (bd->VulkanInitInfo.MinImageCount == min_image_count) return; - ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err = vkDeviceWaitIdle(v->Device); check_vk_result(err); - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &g_MainWindowRenderBuffers, v->Allocator); - g_VulkanInitInfo.MinImageCount = min_image_count; + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + bd->VulkanInitInfo.MinImageCount = min_image_count; } @@ -1300,7 +1342,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // We do not create a pipeline by default as this is also used by examples' main.cpp, // but secondary viewport in multi-viewport mode may want to create one with: - //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, g_Subpass); + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, bd->Subpass); } // Create The Image Views @@ -1357,7 +1399,7 @@ void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevic void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator) { vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) - //vkQueueWaitIdle(g_Queue); + //vkQueueWaitIdle(bd->Queue); for (uint32_t i = 0; i < wd->ImageCount; i++) { diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 6800b12a..edd6635a 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -33,6 +33,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-06-08: Fix ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1). // 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS). // 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi). @@ -63,35 +64,54 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set. -// Win32 Data -static HWND g_hWnd = NULL; -static INT64 g_Time = 0; -static INT64 g_TicksPerSecond = 0; -static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; -static bool g_HasGamepad = false; -static bool g_WantUpdateHasGamepad = true; +struct ImGui_ImplWin32_Data +{ + HWND hWnd; + INT64 Time; + INT64 TicksPerSecond; + ImGuiMouseCursor LastMouseCursor; + bool HasGamepad; + bool WantUpdateHasGamepad; -// XInput DLL and functions #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -static HMODULE g_XInputDLL = NULL; -static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL; -static PFN_XInputGetState g_XInputGetState = NULL; + HMODULE XInputDLL; + PFN_XInputGetCapabilities XInputGetCapabilities; + PFN_XInputGetState XInputGetState; #endif + ImGui_ImplWin32_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Wrapping access to backend data (to facilitate multiple-contexts stored in io.BackendPlatformUserData) +static ImGui_ImplWin32_Data* g_Data; +static ImGui_ImplWin32_Data* ImGui_ImplWin32_CreateBackendData() { IM_ASSERT(g_Data == NULL); g_Data = IM_NEW(ImGui_ImplWin32_Data); return g_Data; } +static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() { return ImGui::GetCurrentContext() ? g_Data : NULL; } +static void ImGui_ImplWin32_DestroyBackendData() { IM_DELETE(g_Data); g_Data = NULL; } + // Functions bool ImGui_ImplWin32_Init(void* hwnd) { - if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&g_TicksPerSecond)) + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + + INT64 perf_frequency, perf_counter; + if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&perf_frequency)) return false; - if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time)) + if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter)) return false; + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_CreateBackendData(); + bd->hWnd = (HWND)hwnd; + bd->WantUpdateHasGamepad = true; + bd->TicksPerSecond = perf_frequency; + bd->Time = perf_counter; + bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + // Setup backend capabilities flags - g_hWnd = (HWND)hwnd; - ImGuiIO& io = ImGui::GetIO(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_win32"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - io.BackendPlatformName = "imgui_impl_win32"; io.ImeWindowHandle = hwnd; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. @@ -131,9 +151,9 @@ bool ImGui_ImplWin32_Init(void* hwnd) for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++) if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) { - g_XInputDLL = dll; - g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); - g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); + bd->XInputDLL = dll; + bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); + bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); break; } #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD @@ -143,21 +163,18 @@ bool ImGui_ImplWin32_Init(void* hwnd) void ImGui_ImplWin32_Shutdown() { + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - if (g_XInputDLL) - ::FreeLibrary(g_XInputDLL); - g_XInputDLL = NULL; - g_XInputGetCapabilities = NULL; - g_XInputGetState = NULL; + if (bd->XInputDLL) + ::FreeLibrary(bd->XInputDLL); #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; + io.BackendPlatformName = NULL; + io.BackendPlatformUserData = NULL; + ImGui_ImplWin32_DestroyBackendData(); } static bool ImGui_ImplWin32_UpdateMouseCursor() @@ -196,13 +213,14 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() static void ImGui_ImplWin32_UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(g_hWnd != 0); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd->hWnd != 0); // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) if (io.WantSetMousePos) { POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - if (::ClientToScreen(g_hWnd, &pos)) + if (::ClientToScreen(bd->hWnd, &pos)) ::SetCursorPos(pos.x, pos.y); } @@ -210,8 +228,8 @@ static void ImGui_ImplWin32_UpdateMousePos() io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); POINT pos; if (HWND active_window = ::GetForegroundWindow()) - if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd)) - if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos)) + if (active_window == bd->hWnd || ::IsChild(active_window, bd->hWnd)) + if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos)) io.MousePos = ImVec2((float)pos.x, (float)pos.y); } @@ -220,22 +238,23 @@ static void ImGui_ImplWin32_UpdateGamepads() { #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); 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) + if (bd->WantUpdateHasGamepad) { XINPUT_CAPABILITIES caps; - g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; - g_WantUpdateHasGamepad = false; + bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; + bd->WantUpdateHasGamepad = false; } io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; XINPUT_STATE xinput_state; - if (g_HasGamepad && g_XInputGetState && g_XInputGetState(0, &xinput_state) == ERROR_SUCCESS) + if (bd->HasGamepad && bd->XInputGetState && bd->XInputGetState(0, &xinput_state) == ERROR_SUCCESS) { const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; io.BackendFlags |= ImGuiBackendFlags_HasGamepad; @@ -267,17 +286,18 @@ static void ImGui_ImplWin32_UpdateGamepads() void ImGui_ImplWin32_NewFrame() { ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); // Setup display size (every frame to accommodate for window resizing) RECT rect = { 0, 0, 0, 0 }; - ::GetClientRect(g_hWnd, &rect); + ::GetClientRect(bd->hWnd, &rect); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); // Setup time step INT64 current_time = 0; ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); - io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; - g_Time = current_time; + io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; + bd->Time = current_time; // Read keyboard modifiers inputs io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; @@ -291,9 +311,9 @@ void ImGui_ImplWin32_NewFrame() // Update OS mouse cursor with the cursor requested by imgui ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (g_LastMouseCursor != mouse_cursor) + if (bd->LastMouseCursor != mouse_cursor) { - g_LastMouseCursor = mouse_cursor; + bd->LastMouseCursor = mouse_cursor; ImGui_ImplWin32_UpdateMouseCursor(); } @@ -327,6 +347,8 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + switch (msg) { case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: @@ -389,7 +411,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; case WM_DEVICECHANGE: if ((UINT)wParam == DBT_DEVNODES_CHANGED) - g_WantUpdateHasGamepad = true; + bd->WantUpdateHasGamepad = true; return 0; } return 0; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3b18dc01..995307e2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,8 @@ Other Changes: - Demo: Fixed requirement in 1.83 to link with imgui_demo.cpp if IMGUI_DISABLE_METRICS_WINDOW is not set. (#4171) Normally the right way to disable compiling the demo is to set IMGUI_DISABLE_DEMO_WINDOWS, but we want to avoid implying that the file is required. +- Backends: Reorganized most backends (Win32, SDL, GLFW, OpenGL2/3, DX9/10/11/12, Vulkan, Allegro) to pull data + from a single structure to facilitate usage with multiple-contexts. (#586, #1851, #2004, #3012, #3934, #4141) - Backends: Win32: Rework to handle certains Windows 8.1/10 features without a manifest. (#4200, #4191) - ImGui_ImplWin32_GetDpiScaleForMonitor() will handle per-monitor DPI on Windows 10 without a manifest. - ImGui_ImplWin32_EnableDpiAwareness() will call SetProcessDpiAwareness() fallback on Windows 8.1 without a manifest.