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
-
This commit is contained in:
ocornut 2021-06-28 16:52:10 +02:00
parent 88f4c1321a
commit 70c603854e
12 changed files with 1042 additions and 705 deletions

View File

@ -9,13 +9,14 @@
// [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. // [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually.
// [ ] Platform: Missing gamepad support. // [ ] 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. // 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. // 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 // Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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. // 2021-02-18: Change blending equation to preserve alpha in output buffer.
// 2020-08-10: Inputs: Fixed horizontal mouse wheel direction. // 2020-08-10: Inputs: Fixed horizontal mouse wheel direction.
@ -53,13 +54,24 @@
#pragma warning (disable: 4127) // condition expression is constant #pragma warning (disable: 4127) // condition expression is constant
#endif #endif
// Data // Allegro Data
static ALLEGRO_DISPLAY* g_Display = NULL; struct ImGui_ImplAllegro5_Data
static ALLEGRO_BITMAP* g_Texture = NULL; {
static double g_Time = 0.0; ALLEGRO_DISPLAY* Display;
static ALLEGRO_MOUSE_CURSOR* g_MouseCursorInvisible = NULL; ALLEGRO_BITMAP* Texture;
static ALLEGRO_VERTEX_DECL* g_VertexDecl = NULL; double Time;
static char* g_ClipboardTextData = NULL; 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 struct ImDrawVertAllegro
{ {
@ -96,6 +108,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
return; return;
// Backup Allegro state that will be modified // 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_transform = *al_get_current_transform();
ALLEGRO_TRANSFORM last_projection_transform = *al_get_current_projection_transform(); ALLEGRO_TRANSFORM last_projection_transform = *al_get_current_projection_transform();
int last_clip_x, last_clip_y, last_clip_w, last_clip_h; 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 // Draw
ALLEGRO_BITMAP* texture = (ALLEGRO_BITMAP*)pcmd->GetTexID(); 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_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; idx_offset += pcmd->ElemCount;
} }
@ -177,6 +190,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
bool ImGui_ImplAllegro5_CreateDeviceObjects() bool ImGui_ImplAllegro5_CreateDeviceObjects()
{ {
// Build texture atlas // Build texture atlas
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels; unsigned char* pixels;
int width, height; int width, height;
@ -210,12 +224,12 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects()
// Store our identifier // Store our identifier
io.Fonts->SetTexID((void*)cloned_img); io.Fonts->SetTexID((void*)cloned_img);
g_Texture = cloned_img; bd->Texture = cloned_img;
// Create an invisible mouse cursor // Create an invisible mouse cursor
// Because al_hide_mouse_cursor() seems to mess up with the actual inputs.. // Because al_hide_mouse_cursor() seems to mess up with the actual inputs..
ALLEGRO_BITMAP* mouse_cursor = al_create_bitmap(8, 8); 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); al_destroy_bitmap(mouse_cursor);
return true; return true;
@ -223,41 +237,48 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects()
void ImGui_ImplAllegro5_InvalidateDeviceObjects() 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); io.Fonts->SetTexID(NULL);
al_destroy_bitmap(g_Texture); al_destroy_bitmap(bd->Texture);
g_Texture = NULL; bd->Texture = NULL;
} }
if (g_MouseCursorInvisible) if (bd->MouseCursorInvisible)
{ {
al_destroy_mouse_cursor(g_MouseCursorInvisible); al_destroy_mouse_cursor(bd->MouseCursorInvisible);
g_MouseCursorInvisible = NULL; bd->MouseCursorInvisible = NULL;
} }
} }
#if ALLEGRO_HAS_CLIPBOARD #if ALLEGRO_HAS_CLIPBOARD
static const char* ImGui_ImplAllegro5_GetClipboardText(void*) static const char* ImGui_ImplAllegro5_GetClipboardText(void*)
{ {
if (g_ClipboardTextData) ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
al_free(g_ClipboardTextData); if (bd->ClipboardTextData)
g_ClipboardTextData = al_get_clipboard_text(g_Display); al_free(bd->ClipboardTextData);
return g_ClipboardTextData; bd->ClipboardTextData = al_get_clipboard_text(bd->Display);
return bd->ClipboardTextData;
} }
static void ImGui_ImplAllegro5_SetClipboardText(void*, const char* text) 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 #endif
bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) 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 // Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO(); io.BackendRendererUserData = (void*)bd;
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendPlatformName = io.BackendRendererName = "imgui_impl_allegro5"; 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) }, { ALLEGRO_PRIM_COLOR_ATTR, 0, IM_OFFSETOF(ImDrawVertAllegro, col) },
{ 0, 0, 0 } { 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_Tab] = ALLEGRO_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = ALLEGRO_KEY_LEFT; io.KeyMap[ImGuiKey_LeftArrow] = ALLEGRO_KEY_LEFT;
@ -308,18 +329,20 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display)
void ImGui_ImplAllegro5_Shutdown() void ImGui_ImplAllegro5_Shutdown()
{ {
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
ImGui_ImplAllegro5_InvalidateDeviceObjects(); ImGui_ImplAllegro5_InvalidateDeviceObjects();
g_Display = NULL; bd->Display = NULL;
g_Time = 0.0; bd->Time = 0.0;
if (g_VertexDecl) if (bd->VertexDecl)
al_destroy_vertex_decl(g_VertexDecl); al_destroy_vertex_decl(bd->VertexDecl);
g_VertexDecl = NULL; bd->VertexDecl = NULL;
if (g_ClipboardTextData) if (bd->ClipboardTextData)
al_free(g_ClipboardTextData); al_free(bd->ClipboardTextData);
g_ClipboardTextData = NULL; 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. // 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) bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
switch (ev->type) switch (ev->type)
{ {
case ALLEGRO_EVENT_MOUSE_AXES: case ALLEGRO_EVENT_MOUSE_AXES:
if (ev->mouse.display == g_Display) if (ev->mouse.display == bd->Display)
{ {
io.MouseWheel += ev->mouse.dz; io.MouseWheel += ev->mouse.dz;
io.MouseWheelH -= ev->mouse.dw; io.MouseWheelH -= ev->mouse.dw;
@ -342,31 +366,31 @@ bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev)
return true; return true;
case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
case ALLEGRO_EVENT_MOUSE_BUTTON_UP: 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); io.MouseDown[ev->mouse.button - 1] = (ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN);
return true; return true;
case ALLEGRO_EVENT_TOUCH_MOVE: 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); io.MousePos = ImVec2(ev->touch.x, ev->touch.y);
return true; return true;
case ALLEGRO_EVENT_TOUCH_BEGIN: case ALLEGRO_EVENT_TOUCH_BEGIN:
case ALLEGRO_EVENT_TOUCH_END: case ALLEGRO_EVENT_TOUCH_END:
case ALLEGRO_EVENT_TOUCH_CANCEL: 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); io.MouseDown[0] = (ev->type == ALLEGRO_EVENT_TOUCH_BEGIN);
return true; return true;
case ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY: 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); io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
return true; return true;
case ALLEGRO_EVENT_KEY_CHAR: case ALLEGRO_EVENT_KEY_CHAR:
if (ev->keyboard.display == g_Display) if (ev->keyboard.display == bd->Display)
if (ev->keyboard.unichar != 0) if (ev->keyboard.unichar != 0)
io.AddInputCharacter((unsigned int)ev->keyboard.unichar); io.AddInputCharacter((unsigned int)ev->keyboard.unichar);
return true; return true;
case ALLEGRO_EVENT_KEY_DOWN: case ALLEGRO_EVENT_KEY_DOWN:
case ALLEGRO_EVENT_KEY_UP: 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); io.KeysDown[ev->keyboard.keycode] = (ev->type == ALLEGRO_EVENT_KEY_DOWN);
return true; return true;
} }
@ -379,11 +403,12 @@ static void ImGui_ImplAllegro5_UpdateMouseCursor()
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return; return;
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
{ {
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor // 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 else
{ {
@ -398,27 +423,28 @@ static void ImGui_ImplAllegro5_UpdateMouseCursor()
case ImGuiMouseCursor_ResizeNWSE: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NW; break; case ImGuiMouseCursor_ResizeNWSE: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NW; break;
case ImGuiMouseCursor_NotAllowed: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_UNAVAILABLE; 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() void ImGui_ImplAllegro5_NewFrame()
{ {
if (!g_Texture) ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
if (!bd->Texture)
ImGui_ImplAllegro5_CreateDeviceObjects(); ImGui_ImplAllegro5_CreateDeviceObjects();
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing) // Setup display size (every frame to accommodate for window resizing)
int w, h; int w, h;
w = al_get_display_width(g_Display); w = al_get_display_width(bd->Display);
h = al_get_display_height(g_Display); h = al_get_display_height(bd->Display);
io.DisplaySize = ImVec2((float)w, (float)h); io.DisplaySize = ImVec2((float)w, (float)h);
// Setup time step // Setup time step
double current_time = al_get_time(); double current_time = al_get_time();
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
g_Time = current_time; bd->Time = current_time;
// Setup inputs // Setup inputs
ALLEGRO_KEYBOARD_STATE keys; ALLEGRO_KEYBOARD_STATE keys;

View File

@ -5,13 +5,14 @@
// [X] Renderer: User texture backend. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [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. // [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. // 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. // 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 // Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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. // 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(). // 2019-07-21: DirectX10: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData().
@ -40,28 +41,43 @@
#endif #endif
// DirectX data // DirectX data
static ID3D10Device* g_pd3dDevice = NULL; struct ImGui_ImplDX10_Data
static IDXGIFactory* g_pFactory = NULL; {
static ID3D10Buffer* g_pVB = NULL; ID3D10Device* pd3dDevice;
static ID3D10Buffer* g_pIB = NULL; IDXGIFactory* pFactory;
static ID3D10VertexShader* g_pVertexShader = NULL; ID3D10Buffer* pVB;
static ID3D10InputLayout* g_pInputLayout = NULL; ID3D10Buffer* pIB;
static ID3D10Buffer* g_pVertexConstantBuffer = NULL; ID3D10VertexShader* pVertexShader;
static ID3D10PixelShader* g_pPixelShader = NULL; ID3D10InputLayout* pInputLayout;
static ID3D10SamplerState* g_pFontSampler = NULL; ID3D10Buffer* pVertexConstantBuffer;
static ID3D10ShaderResourceView*g_pFontTextureView = NULL; ID3D10PixelShader* pPixelShader;
static ID3D10RasterizerState* g_pRasterizerState = NULL; ID3D10SamplerState* pFontSampler;
static ID3D10BlendState* g_pBlendState = NULL; ID3D10ShaderResourceView* pFontTextureView;
static ID3D10DepthStencilState* g_pDepthStencilState = NULL; ID3D10RasterizerState* pRasterizerState;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; 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 struct VERTEX_CONSTANT_BUFFER
{ {
float mvp[4][4]; float mvp[4][4];
}; };
// Functions
static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx) static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx)
{ {
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
// Setup viewport // Setup viewport
D3D10_VIEWPORT vp; D3D10_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D10_VIEWPORT)); memset(&vp, 0, sizeof(D3D10_VIEWPORT));
@ -75,21 +91,21 @@ static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device*
// Bind shader and vertex buffers // Bind shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert); unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0; unsigned int offset = 0;
ctx->IASetInputLayout(g_pInputLayout); ctx->IASetInputLayout(bd->pInputLayout);
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(g_pVertexShader); ctx->VSSetShader(bd->pVertexShader);
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
ctx->PSSetShader(g_pPixelShader); ctx->PSSetShader(bd->pPixelShader);
ctx->PSSetSamplers(0, 1, &g_pFontSampler); ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
ctx->GSSetShader(NULL); ctx->GSSetShader(NULL);
// Setup render state // Setup render state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
ctx->RSSetState(g_pRasterizerState); ctx->RSSetState(bd->pRasterizerState);
} }
// Render function // 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) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return; return;
ID3D10Device* ctx = g_pd3dDevice; ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
ID3D10Device* ctx = bd->pd3dDevice;
// Create and grow vertex/index buffers if needed // 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; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000; bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D10_BUFFER_DESC desc; D3D10_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC)); memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
desc.Usage = D3D10_USAGE_DYNAMIC; desc.Usage = D3D10_USAGE_DYNAMIC;
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D10_BIND_VERTEX_BUFFER; desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
desc.MiscFlags = 0; desc.MiscFlags = 0;
if (ctx->CreateBuffer(&desc, NULL, &g_pVB) < 0) if (ctx->CreateBuffer(&desc, NULL, &bd->pVB) < 0)
return; 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; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000; bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D10_BUFFER_DESC desc; D3D10_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC)); memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
desc.Usage = D3D10_USAGE_DYNAMIC; desc.Usage = D3D10_USAGE_DYNAMIC;
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D10_BIND_INDEX_BUFFER; desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
if (ctx->CreateBuffer(&desc, NULL, &g_pIB) < 0) if (ctx->CreateBuffer(&desc, NULL, &bd->pIB) < 0)
return; return;
} }
// Copy and convert all vertices into a single contiguous buffer // Copy and convert all vertices into a single contiguous buffer
ImDrawVert* vtx_dst = NULL; ImDrawVert* vtx_dst = NULL;
ImDrawIdx* idx_dst = NULL; ImDrawIdx* idx_dst = NULL;
g_pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst); bd->pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst);
g_pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst); bd->pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
for (int n = 0; n < draw_data->CmdListsCount; n++) for (int n = 0; n < draw_data->CmdListsCount; n++)
{ {
const ImDrawList* cmd_list = draw_data->CmdLists[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; vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size;
} }
g_pVB->Unmap(); bd->pVB->Unmap();
g_pIB->Unmap(); bd->pIB->Unmap();
// Setup orthographic projection matrix into our constant buffer // 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. // 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; 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; return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource;
float L = draw_data->DisplayPos.x; 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 }, { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
}; };
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); 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!) // 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() static void ImGui_ImplDX10_CreateFontsTexture()
{ {
// Build texture atlas // Build texture atlas
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels; unsigned char* pixels;
int width, height; int width, height;
@ -294,7 +312,7 @@ static void ImGui_ImplDX10_CreateFontsTexture()
subResource.pSysMem = pixels; subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4; subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0; subResource.SysMemSlicePitch = 0;
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
IM_ASSERT(pTexture != NULL); IM_ASSERT(pTexture != NULL);
// Create texture view // Create texture view
@ -304,12 +322,12 @@ static void ImGui_ImplDX10_CreateFontsTexture()
srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MipLevels = desc.MipLevels; srv_desc.Texture2D.MipLevels = desc.MipLevels;
srv_desc.Texture2D.MostDetailedMip = 0; srv_desc.Texture2D.MostDetailedMip = 0;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &g_pFontTextureView); bd->pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &bd->pFontTextureView);
pTexture->Release(); pTexture->Release();
} }
// Store our identifier // Store our identifier
io.Fonts->SetTexID((ImTextureID)g_pFontTextureView); io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
// Create texture sampler // Create texture sampler
{ {
@ -323,15 +341,16 @@ static void ImGui_ImplDX10_CreateFontsTexture()
desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS; desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
desc.MinLOD = 0.f; desc.MinLOD = 0.f;
desc.MaxLOD = 0.f; desc.MaxLOD = 0.f;
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
} }
} }
bool ImGui_ImplDX10_CreateDeviceObjects() bool ImGui_ImplDX10_CreateDeviceObjects()
{ {
if (!g_pd3dDevice) ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
if (!bd->pd3dDevice)
return false; return false;
if (g_pFontSampler) if (bd->pFontSampler)
ImGui_ImplDX10_InvalidateDeviceObjects(); ImGui_ImplDX10_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) // By using D3DCompile() from <d3dcompiler.h> / 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; ID3DBlob* vertexShaderBlob;
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL))) 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! 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(); vertexShaderBlob->Release();
return false; 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 }, { "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 }, { "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(); vertexShaderBlob->Release();
return false; return false;
@ -401,7 +420,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects()
desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER; desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
desc.MiscFlags = 0; 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; ID3DBlob* pixelShaderBlob;
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL))) 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! 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(); pixelShaderBlob->Release();
return false; return false;
@ -447,7 +466,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects()
desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState);
} }
// Create the rasterizer state // Create the rasterizer state
@ -458,7 +477,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects()
desc.CullMode = D3D10_CULL_NONE; desc.CullMode = D3D10_CULL_NONE;
desc.ScissorEnable = true; desc.ScissorEnable = true;
desc.DepthClipEnable = true; desc.DepthClipEnable = true;
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState);
} }
// Create depth-stencil State // 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.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS; desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace; desc.BackFace = desc.FrontFace;
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
} }
ImGui_ImplDX10_CreateFontsTexture(); ImGui_ImplDX10_CreateFontsTexture();
@ -482,27 +501,31 @@ bool ImGui_ImplDX10_CreateDeviceObjects()
void ImGui_ImplDX10_InvalidateDeviceObjects() void ImGui_ImplDX10_InvalidateDeviceObjects()
{ {
if (!g_pd3dDevice) ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
if (!bd->pd3dDevice)
return; return;
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->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 (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 (g_pIB) { g_pIB->Release(); g_pIB = NULL; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; }
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; }
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; }
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; }
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; }
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; }
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; }
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
} }
bool ImGui_ImplDX10_Init(ID3D10Device* device) bool ImGui_ImplDX10_Init(ID3D10Device* device)
{ {
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO(); 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.BackendRendererName = "imgui_impl_dx10";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. 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; IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL; IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL; IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK) if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{ {
g_pd3dDevice = device; bd->pd3dDevice = device;
g_pFactory = pFactory; bd->pFactory = pFactory;
} }
if (pDXGIDevice) pDXGIDevice->Release(); if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release(); if (pDXGIAdapter) pDXGIAdapter->Release();
g_pd3dDevice->AddRef(); bd->pd3dDevice->AddRef();
return true; return true;
} }
void ImGui_ImplDX10_Shutdown() void ImGui_ImplDX10_Shutdown()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
ImGui_ImplDX10_InvalidateDeviceObjects(); ImGui_ImplDX10_InvalidateDeviceObjects();
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } if (bd->pFactory) { bd->pFactory->Release(); bd->pFactory = NULL; }
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_ImplDX10_DestroyBackendData();
} }
void ImGui_ImplDX10_NewFrame() void ImGui_ImplDX10_NewFrame()
{ {
if (!g_pFontSampler) ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
if (!bd->pFontSampler)
ImGui_ImplDX10_CreateDeviceObjects(); ImGui_ImplDX10_CreateDeviceObjects();
} }

View File

@ -5,13 +5,14 @@
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [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. // [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. // 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. // 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 // Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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. // 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). // 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. #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
#endif #endif
// DirectX data // DirectX11 data
static ID3D11Device* g_pd3dDevice = NULL; struct ImGui_ImplDX11_Data
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; {
static IDXGIFactory* g_pFactory = NULL; ID3D11Device* pd3dDevice;
static ID3D11Buffer* g_pVB = NULL; ID3D11DeviceContext* pd3dDeviceContext;
static ID3D11Buffer* g_pIB = NULL; IDXGIFactory* pFactory;
static ID3D11VertexShader* g_pVertexShader = NULL; ID3D11Buffer* pVB;
static ID3D11InputLayout* g_pInputLayout = NULL; ID3D11Buffer* pIB;
static ID3D11Buffer* g_pVertexConstantBuffer = NULL; ID3D11VertexShader* pVertexShader;
static ID3D11PixelShader* g_pPixelShader = NULL; ID3D11InputLayout* pInputLayout;
static ID3D11SamplerState* g_pFontSampler = NULL; ID3D11Buffer* pVertexConstantBuffer;
static ID3D11ShaderResourceView*g_pFontTextureView = NULL; ID3D11PixelShader* pPixelShader;
static ID3D11RasterizerState* g_pRasterizerState = NULL; ID3D11SamplerState* pFontSampler;
static ID3D11BlendState* g_pBlendState = NULL; ID3D11ShaderResourceView* pFontTextureView;
static ID3D11DepthStencilState* g_pDepthStencilState = NULL; ID3D11RasterizerState* pRasterizerState;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; 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 struct VERTEX_CONSTANT_BUFFER
{ {
float mvp[4][4]; float mvp[4][4];
}; };
// Functions
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx) static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
{ {
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
// Setup viewport // Setup viewport
D3D11_VIEWPORT vp; D3D11_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D11_VIEWPORT)); memset(&vp, 0, sizeof(D3D11_VIEWPORT));
@ -76,14 +92,14 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC
// Setup shader and vertex buffers // Setup shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert); unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0; unsigned int offset = 0;
ctx->IASetInputLayout(g_pInputLayout); ctx->IASetInputLayout(bd->pInputLayout);
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(g_pVertexShader, NULL, 0); ctx->VSSetShader(bd->pVertexShader, NULL, 0);
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
ctx->PSSetShader(g_pPixelShader, NULL, 0); ctx->PSSetShader(bd->pPixelShader, NULL, 0);
ctx->PSSetSamplers(0, 1, &g_pFontSampler); ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
ctx->GSSetShader(NULL, NULL, 0); ctx->GSSetShader(NULL, NULL, 0);
ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. 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.. 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 // Setup blend state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
ctx->RSSetState(g_pRasterizerState); ctx->RSSetState(bd->pRasterizerState);
} }
// Render function // 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) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return; return;
ID3D11DeviceContext* ctx = g_pd3dDeviceContext; ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
ID3D11DeviceContext* ctx = bd->pd3dDeviceContext;
// Create and grow vertex/index buffers if needed // 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; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000; bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D11_BUFFER_DESC desc; D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC; desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0; desc.MiscFlags = 0;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0) if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVB) < 0)
return; 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; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000; bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D11_BUFFER_DESC desc; D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC; desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 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; return;
} }
// Upload vertex/index data into a single contiguous GPU buffer // Upload vertex/index data into a single contiguous GPU buffer
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; 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; 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; return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_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; vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size;
} }
ctx->Unmap(g_pVB, 0); ctx->Unmap(bd->pVB, 0);
ctx->Unmap(g_pIB, 0); ctx->Unmap(bd->pIB, 0);
// Setup orthographic projection matrix into our constant buffer // 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. // 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; 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; return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
float L = draw_data->DisplayPos.x; 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 }, { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
}; };
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); 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!) // 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 // Build texture atlas
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
unsigned char* pixels; unsigned char* pixels;
int width, height; int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
@ -306,7 +324,7 @@ static void ImGui_ImplDX11_CreateFontsTexture()
subResource.pSysMem = pixels; subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4; subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0; subResource.SysMemSlicePitch = 0;
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
IM_ASSERT(pTexture != NULL); IM_ASSERT(pTexture != NULL);
// Create texture view // Create texture view
@ -316,12 +334,12 @@ static void ImGui_ImplDX11_CreateFontsTexture()
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Texture2D.MostDetailedMip = 0;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView);
pTexture->Release(); pTexture->Release();
} }
// Store our identifier // Store our identifier
io.Fonts->SetTexID((ImTextureID)g_pFontTextureView); io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
// Create texture sampler // Create texture sampler
{ {
@ -335,15 +353,16 @@ static void ImGui_ImplDX11_CreateFontsTexture()
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
desc.MinLOD = 0.f; desc.MinLOD = 0.f;
desc.MaxLOD = 0.f; desc.MaxLOD = 0.f;
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
} }
} }
bool ImGui_ImplDX11_CreateDeviceObjects() bool ImGui_ImplDX11_CreateDeviceObjects()
{ {
if (!g_pd3dDevice) ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
if (!bd->pd3dDevice)
return false; return false;
if (g_pFontSampler) if (bd->pFontSampler)
ImGui_ImplDX11_InvalidateDeviceObjects(); ImGui_ImplDX11_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) // By using D3DCompile() from <d3dcompiler.h> / 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; ID3DBlob* vertexShaderBlob;
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL))) 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! 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(); vertexShaderBlob->Release();
return false; 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 }, { "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 }, { "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(); vertexShaderBlob->Release();
return false; return false;
@ -413,7 +432,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0; 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; ID3DBlob* pixelShaderBlob;
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL))) 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! 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(); pixelShaderBlob->Release();
return false; return false;
@ -459,7 +478,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; 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 // Create the rasterizer state
@ -470,7 +489,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
desc.CullMode = D3D11_CULL_NONE; desc.CullMode = D3D11_CULL_NONE;
desc.ScissorEnable = true; desc.ScissorEnable = true;
desc.DepthClipEnable = true; desc.DepthClipEnable = true;
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState);
} }
// Create depth-stencil State // 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.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace; desc.BackFace = desc.FrontFace;
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
} }
ImGui_ImplDX11_CreateFontsTexture(); ImGui_ImplDX11_CreateFontsTexture();
@ -494,27 +513,31 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
void ImGui_ImplDX11_InvalidateDeviceObjects() void ImGui_ImplDX11_InvalidateDeviceObjects()
{ {
if (!g_pd3dDevice) ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
if (!bd->pd3dDevice)
return; return;
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->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 (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 (g_pIB) { g_pIB->Release(); g_pIB = NULL; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; }
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; }
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; }
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; }
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; }
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; }
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; }
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
} }
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
{ {
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO(); 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.BackendRendererName = "imgui_impl_dx11";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. 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 (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{ {
g_pd3dDevice = device; bd->pd3dDevice = device;
g_pd3dDeviceContext = device_context; bd->pd3dDeviceContext = device_context;
g_pFactory = pFactory; bd->pFactory = pFactory;
} }
if (pDXGIDevice) pDXGIDevice->Release(); if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release(); if (pDXGIAdapter) pDXGIAdapter->Release();
g_pd3dDevice->AddRef(); bd->pd3dDevice->AddRef();
g_pd3dDeviceContext->AddRef(); bd->pd3dDeviceContext->AddRef();
return true; return true;
} }
void ImGui_ImplDX11_Shutdown() void ImGui_ImplDX11_Shutdown()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
ImGui_ImplDX11_InvalidateDeviceObjects(); ImGui_ImplDX11_InvalidateDeviceObjects();
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } if (bd->pFactory) { bd->pFactory->Release(); bd->pFactory = NULL; }
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } if (bd->pd3dDevice) { bd->pd3dDevice->Release(); bd->pd3dDevice = NULL; }
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); bd->pd3dDeviceContext = NULL; }
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
ImGui_ImplDX11_DestroyBackendData();
} }
void ImGui_ImplDX11_NewFrame() void ImGui_ImplDX11_NewFrame()
{ {
if (!g_pFontSampler) ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
if (!bd->pFontSampler)
ImGui_ImplDX11_CreateDeviceObjects(); ImGui_ImplDX11_CreateDeviceObjects();
} }

View File

@ -20,6 +20,7 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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-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. // 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically.
@ -48,14 +49,6 @@
#endif #endif
// DirectX data // 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 struct FrameResources
{ {
ID3D12Resource* IndexBuffer; ID3D12Resource* IndexBuffer;
@ -63,9 +56,29 @@ struct FrameResources
int IndexBufferSize; int IndexBufferSize;
int VertexBufferSize; int VertexBufferSize;
}; };
static FrameResources* g_pFrameResources = NULL;
static UINT g_numFramesInFlight = 0; struct ImGui_ImplDX12_Data
static UINT g_frameIndex = UINT_MAX; {
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<typename T> template<typename T>
static void SafeRelease(T*& res) 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) 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 // 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). // 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; 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; ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
ctx->IASetIndexBuffer(&ibv); ctx->IASetIndexBuffer(&ibv);
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->SetPipelineState(g_pPipelineState); ctx->SetPipelineState(bd->pPipelineState);
ctx->SetGraphicsRootSignature(g_pRootSignature); ctx->SetGraphicsRootSignature(bd->pRootSignature);
ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0); ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
// Setup blend factor // 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! // 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. // 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; ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight]; bd->frameIndex = bd->frameIndex + 1;
FrameResources* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight];
// Create and grow vertex/index buffers if needed // Create and grow vertex/index buffers if needed
if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) 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.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE; 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; return;
} }
if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount) 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.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE; 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; return;
} }
@ -262,6 +278,7 @@ static void ImGui_ImplDX12_CreateFontsTexture()
{ {
// Build texture atlas // Build texture atlas
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
unsigned char* pixels; unsigned char* pixels;
int width, height; int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
@ -289,7 +306,7 @@ static void ImGui_ImplDX12_CreateFontsTexture()
desc.Flags = D3D12_RESOURCE_FLAG_NONE; desc.Flags = D3D12_RESOURCE_FLAG_NONE;
ID3D12Resource* pTexture = NULL; 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)); 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); 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; props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
ID3D12Resource* uploadBuffer = NULL; 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)); D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer));
IM_ASSERT(SUCCEEDED(hr)); IM_ASSERT(SUCCEEDED(hr));
@ -346,7 +363,7 @@ static void ImGui_ImplDX12_CreateFontsTexture()
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
ID3D12Fence* fence = NULL; 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)); IM_ASSERT(SUCCEEDED(hr));
HANDLE event = CreateEvent(0, 0, 0, 0); HANDLE event = CreateEvent(0, 0, 0, 0);
@ -358,15 +375,15 @@ static void ImGui_ImplDX12_CreateFontsTexture()
queueDesc.NodeMask = 1; queueDesc.NodeMask = 1;
ID3D12CommandQueue* cmdQueue = NULL; 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)); IM_ASSERT(SUCCEEDED(hr));
ID3D12CommandAllocator* cmdAlloc = NULL; 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)); IM_ASSERT(SUCCEEDED(hr));
ID3D12GraphicsCommandList* cmdList = NULL; 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)); IM_ASSERT(SUCCEEDED(hr));
cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL); cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL);
@ -397,9 +414,9 @@ static void ImGui_ImplDX12_CreateFontsTexture()
srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, g_hFontSrvCpuDescHandle); bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, bd->hFontSrvCpuDescHandle);
SafeRelease(g_pFontTextureResource); SafeRelease(bd->pFontTextureResource);
g_pFontTextureResource = pTexture; bd->pFontTextureResource = pTexture;
} }
// Store our identifier // 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 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 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) // [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."); static_assert(sizeof(ImTextureID) >= sizeof(bd->hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet.");
io.Fonts->SetTexID((ImTextureID)g_hFontSrvGpuDescHandle.ptr); io.Fonts->SetTexID((ImTextureID)bd->hFontSrvGpuDescHandle.ptr);
} }
bool ImGui_ImplDX12_CreateDeviceObjects() bool ImGui_ImplDX12_CreateDeviceObjects()
{ {
if (!g_pd3dDevice) if (ImGui::GetCurrentContext() == NULL)
return false; return false;
if (g_pPipelineState) ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
if (!bd->pd3dDevice)
return false;
if (bd->pPipelineState)
ImGui_ImplDX12_InvalidateDeviceObjects(); ImGui_ImplDX12_InvalidateDeviceObjects();
// Create the root signature // Create the root signature
@ -499,7 +519,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, NULL) != S_OK) if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, NULL) != S_OK)
return false; 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(); blob->Release();
} }
@ -513,10 +533,10 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC)); memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
psoDesc.NodeMask = 1; psoDesc.NodeMask = 1;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.pRootSignature = g_pRootSignature; psoDesc.pRootSignature = bd->pRootSignature;
psoDesc.SampleMask = UINT_MAX; psoDesc.SampleMask = UINT_MAX;
psoDesc.NumRenderTargets = 1; psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = g_RTVFormat; psoDesc.RTVFormats[0] = bd->RTVFormat;
psoDesc.SampleDesc.Count = 1; psoDesc.SampleDesc.Count = 1;
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
@ -635,7 +655,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
desc.BackFace = desc.FrontFace; 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(); vertexShaderBlob->Release();
pixelShaderBlob->Release(); pixelShaderBlob->Release();
if (result_pipeline_state != S_OK) if (result_pipeline_state != S_OK)
@ -648,19 +668,21 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
void ImGui_ImplDX12_InvalidateDeviceObjects() 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; return;
SafeRelease(g_pRootSignature); SafeRelease(bd->pRootSignature);
SafeRelease(g_pPipelineState); SafeRelease(bd->pPipelineState);
SafeRelease(g_pFontTextureResource); 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(); for (UINT i = 0; i < bd->numFramesInFlight; i++)
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++)
{ {
FrameResources* fr = &g_pFrameResources[i]; FrameResources* fr = &bd->pFrameResources[i];
SafeRelease(fr->IndexBuffer); SafeRelease(fr->IndexBuffer);
SafeRelease(fr->VertexBuffer); 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, 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) 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(); 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.BackendRendererName = "imgui_impl_dx12";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
g_pd3dDevice = device; bd->pd3dDevice = device;
g_RTVFormat = rtv_format; bd->RTVFormat = rtv_format;
g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle; bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
g_pFrameResources = new FrameResources[num_frames_in_flight]; bd->pFrameResources = new FrameResources[num_frames_in_flight];
g_numFramesInFlight = num_frames_in_flight; bd->numFramesInFlight = num_frames_in_flight;
g_frameIndex = UINT_MAX; bd->frameIndex = UINT_MAX;
IM_UNUSED(cbv_srv_heap); // Unused in master branch (will be used by multi-viewports) 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) // Create buffers with a default size (they will later be grown as needed)
for (int i = 0; i < num_frames_in_flight; i++) for (int i = 0; i < num_frames_in_flight; i++)
{ {
FrameResources* fr = &g_pFrameResources[i]; FrameResources* fr = &bd->pFrameResources[i];
fr->IndexBuffer = NULL; fr->IndexBuffer = NULL;
fr->VertexBuffer = NULL; fr->VertexBuffer = NULL;
fr->IndexBufferSize = 10000; fr->IndexBufferSize = 10000;
@ -698,18 +724,25 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
void ImGui_ImplDX12_Shutdown() void ImGui_ImplDX12_Shutdown()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
ImGui_ImplDX12_InvalidateDeviceObjects(); ImGui_ImplDX12_InvalidateDeviceObjects();
delete[] g_pFrameResources; delete[] bd->pFrameResources;
g_pFrameResources = NULL; bd->pFrameResources = NULL;
g_pd3dDevice = NULL; bd->pd3dDevice = NULL;
g_hFontSrvCpuDescHandle.ptr = 0; bd->hFontSrvCpuDescHandle.ptr = 0;
g_hFontSrvGpuDescHandle.ptr = 0; bd->hFontSrvGpuDescHandle.ptr = 0;
g_numFramesInFlight = 0; bd->numFramesInFlight = 0;
g_frameIndex = UINT_MAX; bd->frameIndex = UINT_MAX;
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
ImGui_ImplDX12_DestroyBackendData();
} }
void ImGui_ImplDX12_NewFrame() void ImGui_ImplDX12_NewFrame()
{ {
if (!g_pPipelineState) ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
if (!bd->pPipelineState)
ImGui_ImplDX12_CreateDeviceObjects(); ImGui_ImplDX12_CreateDeviceObjects();
} }

View File

@ -12,6 +12,7 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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-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-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states.
@ -36,11 +37,23 @@
#include <d3d9.h> #include <d3d9.h>
// DirectX data // DirectX data
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; struct ImGui_ImplDX9_Data
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; {
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL; LPDIRECT3DDEVICE9 pd3dDevice;
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL; LPDIRECT3DVERTEXBUFFER9 pVB;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; 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 struct CUSTOMVERTEX
{ {
@ -56,8 +69,11 @@ struct CUSTOMVERTEX
#define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16)) #define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16))
#endif #endif
// Functions
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
{ {
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
// Setup viewport // Setup viewport
D3DVIEWPORT9 vp; D3DVIEWPORT9 vp;
vp.X = vp.Y = 0; 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.Height = (DWORD)draw_data->DisplaySize.y;
vp.MinZ = 0.0f; vp.MinZ = 0.0f;
vp.MaxZ = 1.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) // Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
g_pd3dDevice->SetPixelShader(NULL); bd->pd3dDevice->SetPixelShader(NULL);
g_pd3dDevice->SetVertexShader(NULL); bd->pd3dDevice->SetVertexShader(NULL);
g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); bd->pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); bd->pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
g_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); bd->pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); bd->pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); bd->pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE); bd->pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); bd->pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); bd->pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); bd->pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); bd->pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); bd->pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE); bd->pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA); bd->pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); bd->pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); bd->pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE); bd->pd3dDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); bd->pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); bd->pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE); bd->pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); bd->pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); bd->pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); bd->pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Setup orthographic projection matrix // 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. // 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, 0.0f, 0.0f, 0.5f, 0.0f,
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f (L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
} } }; } } };
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity); bd->pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity); bd->pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection); bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
} }
} }
@ -131,24 +147,25 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
return; return;
// Create and grow buffers if needed // 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; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000; bd->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->pd3dDevice->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, NULL) < 0)
return; 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; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000; bd->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->pd3dDevice->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, NULL) < 0)
return; return;
} }
// Backup the DX9 state // Backup the DX9 state
IDirect3DStateBlock9* d3d9_state_block = NULL; 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; return;
if (d3d9_state_block->Capture() < 0) 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) // 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; D3DMATRIX last_world, last_view, last_projection;
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world); bd->pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view); bd->pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection); bd->pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
// Allocate buffers // Allocate buffers
CUSTOMVERTEX* vtx_dst; CUSTOMVERTEX* vtx_dst;
ImDrawIdx* idx_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(); d3d9_state_block->Release();
return; 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(); d3d9_state_block->Release();
return; 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)); memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
idx_dst += cmd_list->IdxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size;
} }
g_pVB->Unlock(); bd->pVB->Unlock();
g_pIB->Unlock(); bd->pIB->Unlock();
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX)); bd->pd3dDevice->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetIndices(g_pIB); bd->pd3dDevice->SetIndices(bd->pIB);
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); bd->pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
// Setup desired DX state // Setup desired DX state
ImGui_ImplDX9_SetupRenderState(draw_data); 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 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(); const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID();
g_pd3dDevice->SetTexture(0, texture); bd->pd3dDevice->SetTexture(0, texture);
g_pd3dDevice->SetScissorRect(&r); bd->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->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
} }
} }
global_idx_offset += cmd_list->IdxBuffer.Size; global_idx_offset += cmd_list->IdxBuffer.Size;
@ -242,9 +259,9 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
} }
// Restore the DX9 transform // Restore the DX9 transform
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world); bd->pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view); bd->pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection); bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
// Restore the DX9 state // Restore the DX9 state
d3d9_state_block->Apply(); d3d9_state_block->Apply();
@ -253,26 +270,38 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
{ {
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO(); 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.BackendRendererName = "imgui_impl_dx9";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
g_pd3dDevice = device; bd->pd3dDevice = device;
g_pd3dDevice->AddRef(); bd->pd3dDevice->AddRef();
return true; return true;
} }
void ImGui_ImplDX9_Shutdown() void ImGui_ImplDX9_Shutdown()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
ImGui_ImplDX9_InvalidateDeviceObjects(); 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() static bool ImGui_ImplDX9_CreateFontsTexture()
{ {
// Build texture atlas // Build texture atlas
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
unsigned char* pixels; unsigned char* pixels;
int width, height, bytes_per_pixel; int width, height, bytes_per_pixel;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel); io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
@ -289,18 +318,18 @@ static bool ImGui_ImplDX9_CreateFontsTexture()
#endif #endif
// Upload texture to graphics system // Upload texture to graphics system
g_FontTexture = NULL; bd->FontTexture = NULL;
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0) if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture, NULL) < 0)
return false; return false;
D3DLOCKED_RECT tex_locked_rect; 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; return false;
for (int y = 0; y < height; y++) 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); 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 // Store our identifier
io.Fonts->SetTexID((ImTextureID)g_FontTexture); io.Fonts->SetTexID((ImTextureID)bd->FontTexture);
#ifndef IMGUI_USE_BGRA_PACKED_COLOR #ifndef IMGUI_USE_BGRA_PACKED_COLOR
if (io.Fonts->TexPixelsUseColors) if (io.Fonts->TexPixelsUseColors)
@ -312,7 +341,8 @@ static bool ImGui_ImplDX9_CreateFontsTexture()
bool ImGui_ImplDX9_CreateDeviceObjects() bool ImGui_ImplDX9_CreateDeviceObjects()
{ {
if (!g_pd3dDevice) ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
if (!bd || !bd->pd3dDevice)
return false; return false;
if (!ImGui_ImplDX9_CreateFontsTexture()) if (!ImGui_ImplDX9_CreateFontsTexture())
return false; return false;
@ -321,15 +351,17 @@ bool ImGui_ImplDX9_CreateDeviceObjects()
void ImGui_ImplDX9_InvalidateDeviceObjects() void ImGui_ImplDX9_InvalidateDeviceObjects()
{ {
if (!g_pd3dDevice) ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
if (!bd || !bd->pd3dDevice)
return; return;
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } if (bd->pIB) { bd->pIB->Release(); bd->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->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() void ImGui_ImplDX9_NewFrame()
{ {
if (!g_FontTexture) ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
if (!bd->FontTexture)
ImGui_ImplDX9_CreateDeviceObjects(); ImGui_ImplDX9_CreateDeviceObjects();
} }

View File

@ -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: 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). // [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. // 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. // 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 // Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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. // 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-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. // 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown.
@ -59,26 +60,39 @@
#define GLFW_HAS_NEW_CURSORS (0) #define GLFW_HAS_NEW_CURSORS (0)
#endif #endif
// Data // GLFW data
enum GlfwClientApi enum GlfwClientApi
{ {
GlfwClientApi_Unknown, GlfwClientApi_Unknown,
GlfwClientApi_OpenGL, GlfwClientApi_OpenGL,
GlfwClientApi_Vulkan 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. struct ImGui_ImplGlfw_Data
static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; {
static GLFWscrollfun g_PrevUserCallbackScroll = NULL; GLFWwindow* Window;
static GLFWkeyfun g_PrevUserCallbackKey = NULL; GlfwClientApi ClientApi;
static GLFWcharfun g_PrevUserCallbackChar = NULL; 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) static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
{ {
return glfwGetClipboardString((GLFWwindow*)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) void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{ {
if (g_PrevUserCallbackMousebutton != NULL) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
g_PrevUserCallbackMousebutton(window, button, action, mods); if (bd->PrevUserCallbackMousebutton != NULL)
bd->PrevUserCallbackMousebutton(window, button, action, mods);
if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(bd->MouseJustPressed))
g_MouseJustPressed[button] = true; bd->MouseJustPressed[button] = true;
} }
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{ {
if (g_PrevUserCallbackScroll != NULL) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
g_PrevUserCallbackScroll(window, xoffset, yoffset); if (bd->PrevUserCallbackScroll != NULL)
bd->PrevUserCallbackScroll(window, xoffset, yoffset);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.MouseWheelH += (float)xoffset; 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) void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{ {
if (g_PrevUserCallbackKey != NULL) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
g_PrevUserCallbackKey(window, key, scancode, action, mods); if (bd->PrevUserCallbackKey != NULL)
bd->PrevUserCallbackKey(window, key, scancode, action, mods);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)) 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) void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
{ {
if (g_PrevUserCallbackChar != NULL) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
g_PrevUserCallbackChar(window, c); if (bd->PrevUserCallbackChar != NULL)
bd->PrevUserCallbackChar(window, c);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.AddInputCharacter(c); 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) static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
{ {
g_Window = window; ImGuiIO& io = ImGui::GetIO();
g_Time = 0.0; 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 // 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_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) 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. // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; 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.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
io.ClipboardUserData = g_Window; io.ClipboardUserData = bd->Window;
#if defined(_WIN32) #if defined(_WIN32)
io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window); io.ImeWindowHandle = (void*)glfwGetWin32Window(bd->Window);
#endif #endif
// Create mouse cursors // 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. // 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.) // Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.)
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL); GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL);
g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
#if GLFW_HAS_NEW_CURSORS #if GLFW_HAS_NEW_CURSORS
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR);
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR);
#else #else
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
#endif #endif
glfwSetErrorCallback(prev_error_callback); glfwSetErrorCallback(prev_error_callback);
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
g_PrevUserCallbackMousebutton = NULL; bd->PrevUserCallbackMousebutton = NULL;
g_PrevUserCallbackScroll = NULL; bd->PrevUserCallbackScroll = NULL;
g_PrevUserCallbackKey = NULL; bd->PrevUserCallbackKey = NULL;
g_PrevUserCallbackChar = NULL; bd->PrevUserCallbackChar = NULL;
if (install_callbacks) if (install_callbacks)
{ {
g_InstalledCallbacks = true; bd->InstalledCallbacks = true;
g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
} }
g_ClientApi = client_api; bd->ClientApi = client_api;
return true; return true;
} }
@ -242,32 +264,40 @@ bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks)
void ImGui_ImplGlfw_Shutdown() 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); glfwSetMouseButtonCallback(bd->Window, bd->PrevUserCallbackMousebutton);
glfwSetScrollCallback(g_Window, g_PrevUserCallbackScroll); glfwSetScrollCallback(bd->Window, bd->PrevUserCallbackScroll);
glfwSetKeyCallback(g_Window, g_PrevUserCallbackKey); glfwSetKeyCallback(bd->Window, bd->PrevUserCallbackKey);
glfwSetCharCallback(g_Window, g_PrevUserCallbackChar); glfwSetCharCallback(bd->Window, bd->PrevUserCallbackChar);
g_InstalledCallbacks = false; bd->InstalledCallbacks = false;
} }
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
{ {
glfwDestroyCursor(g_MouseCursors[cursor_n]); glfwDestroyCursor(bd->MouseCursors[cursor_n]);
g_MouseCursors[cursor_n] = NULL; bd->MouseCursors[cursor_n] = NULL;
} }
g_ClientApi = GlfwClientApi_Unknown;
io.BackendPlatformName = NULL;
io.BackendPlatformUserData = NULL;
ImGui_ImplGlfw_DestroyBackendData();
} }
static void ImGui_ImplGlfw_UpdateMousePosAndButtons() static void ImGui_ImplGlfw_UpdateMousePosAndButtons()
{ {
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
// Update buttons // Update buttons
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) 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. // 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; io.MouseDown[i] = bd->MouseJustPressed[i] || glfwGetMouseButton(bd->Window, i) != 0;
g_MouseJustPressed[i] = false; bd->MouseJustPressed[i] = false;
} }
// Update mouse position // Update mouse position
@ -276,18 +306,18 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons()
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
const bool focused = true; // Emscripten const bool focused = true; // Emscripten
#else #else
const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0; const bool focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0;
#endif #endif
if (focused) if (focused)
{ {
if (io.WantSetMousePos) 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 else
{ {
double mouse_x, mouse_y; 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); io.MousePos = ImVec2((float)mouse_x, (float)mouse_y);
} }
} }
@ -296,21 +326,22 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons()
static void ImGui_ImplGlfw_UpdateMouseCursor() static void ImGui_ImplGlfw_UpdateMouseCursor()
{ {
ImGuiIO& io = ImGui::GetIO(); 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; return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
{ {
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor // 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 else
{ {
// Show OS mouse cursor // Show OS mouse cursor
// FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. // 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]); glfwSetCursor(bd->Window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
} }
} }
@ -354,20 +385,21 @@ static void ImGui_ImplGlfw_UpdateGamepads()
void ImGui_ImplGlfw_NewFrame() void ImGui_ImplGlfw_NewFrame()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
// Setup display size (every frame to accommodate for window resizing) // Setup display size (every frame to accommodate for window resizing)
int w, h; int w, h;
int display_w, display_h; int display_w, display_h;
glfwGetWindowSize(g_Window, &w, &h); glfwGetWindowSize(bd->Window, &w, &h);
glfwGetFramebufferSize(g_Window, &display_w, &display_h); glfwGetFramebufferSize(bd->Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h); io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0) if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
// Setup time step // Setup time step
double current_time = glfwGetTime(); double current_time = glfwGetTime();
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
g_Time = current_time; bd->Time = current_time;
ImGui_ImplGlfw_UpdateMousePosAndButtons(); ImGui_ImplGlfw_UpdateMousePosAndButtons();
ImGui_ImplGlfw_UpdateMouseCursor(); ImGui_ImplGlfw_UpdateMouseCursor();

View File

@ -19,6 +19,7 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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. // 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. // 2020-01-23: OpenGL: Backup, setup and restore GL_TEXTURE_ENV to increase compatibility with legacy OpenGL applications.
@ -55,26 +56,47 @@
#include <GL/gl.h> #include <GL/gl.h>
#endif #endif
// OpenGL Data struct ImGui_ImplOpenGL2_Data
static GLuint g_FontTexture = 0; {
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 // Functions
bool ImGui_ImplOpenGL2_Init() bool ImGui_ImplOpenGL2_Init()
{ {
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO(); 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"; io.BackendRendererName = "imgui_impl_opengl2";
return true; return true;
} }
void ImGui_ImplOpenGL2_Shutdown() void ImGui_ImplOpenGL2_Shutdown()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL2_DestroyDeviceObjects(); ImGui_ImplOpenGL2_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
ImGui_ImplOpenGL2_DestroyBackendData();
} }
void ImGui_ImplOpenGL2_NewFrame() void ImGui_ImplOpenGL2_NewFrame()
{ {
if (!g_FontTexture) ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
if (!bd->FontTexture)
ImGui_ImplOpenGL2_CreateDeviceObjects(); ImGui_ImplOpenGL2_CreateDeviceObjects();
} }
@ -215,6 +237,7 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture()
{ {
// Build texture atlas // Build texture atlas
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
unsigned char* pixels; unsigned char* pixels;
int width, height; 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. 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 // Upload texture to graphics system
GLint last_texture; GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture); glGenTextures(1, &bd->FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture); glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier // Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontTexture); io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
// Restore state // Restore state
glBindTexture(GL_TEXTURE_2D, last_texture); glBindTexture(GL_TEXTURE_2D, last_texture);
@ -240,12 +263,13 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture()
void ImGui_ImplOpenGL2_DestroyFontsTexture() 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, &bd->FontTexture);
glDeleteTextures(1, &g_FontTexture);
io.Fonts->SetTexID(0); io.Fonts->SetTexID(0);
g_FontTexture = 0; bd->FontTexture = 0;
} }
} }

View File

@ -14,6 +14,7 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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-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-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 #endif
// OpenGL Data // 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) struct ImGui_ImplOpenGL3_Data
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings. {
static GLuint g_FontTexture = 0; GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
static GLuint g_ShaderHandle = 0; char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings.
static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location GLuint FontTexture;
static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location GLuint ShaderHandle;
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; GLint AttribLocationTex; // Uniforms location
static bool g_HasClipOrigin = false; 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 // Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version) 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) // Query for GL version (e.g. 320 for GL 3.2)
#if !defined(IMGUI_IMPL_OPENGL_ES2) #if !defined(IMGUI_IMPL_OPENGL_ES2)
GLint major = 0; GLint major = 0;
@ -195,16 +215,16 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
const char* gl_version = (const char*)glGetString(GL_VERSION); const char* gl_version = (const char*)glGetString(GL_VERSION);
sscanf(gl_version, "%d.%d", &major, &minor); sscanf(gl_version, "%d.%d", &major, &minor);
} }
g_GlVersion = (GLuint)(major * 100 + minor * 10); bd->GlVersion = (GLuint)(major * 100 + minor * 10);
#else #else
g_GlVersion = 200; // GLES 2 bd->GlVersion = 200; // GLES 2
#endif #endif
// Setup backend capabilities flags // Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO(); io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_opengl3"; io.BackendRendererName = "imgui_impl_opengl3";
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET #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. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif #endif
@ -223,9 +243,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
if (glsl_version == NULL) if (glsl_version == NULL)
glsl_version = "#version 130"; glsl_version = "#version 130";
#endif #endif
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString));
strcpy(g_GlslVersionString, glsl_version); strcpy(bd->GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n"); strcat(bd->GlslVersionString, "\n");
// Debugging construct to make it easily visible in the IDE and debugger which GL loader has been selected. // 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! // 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, &current_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
// Detect extensions we support // Detect extensions we support
g_HasClipOrigin = (g_GlVersion >= 450); bd->HasClipOrigin = (bd->GlVersion >= 450);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
GLint num_extensions = 0; GLint num_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); 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); const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i);
if (extension != NULL && strcmp(extension, "GL_ARB_clip_control") == 0) if (extension != NULL && strcmp(extension, "GL_ARB_clip_control") == 0)
g_HasClipOrigin = true; bd->HasClipOrigin = true;
} }
#endif #endif
@ -276,17 +296,24 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
void ImGui_ImplOpenGL3_Shutdown() void ImGui_ImplOpenGL3_Shutdown()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_DestroyDeviceObjects(); ImGui_ImplOpenGL3_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
ImGui_ImplOpenGL3_DestroyBackendData();
} }
void ImGui_ImplOpenGL3_NewFrame() void ImGui_ImplOpenGL3_NewFrame()
{ {
if (!g_ShaderHandle) ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (!bd->ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects(); ImGui_ImplOpenGL3_CreateDeviceObjects();
} }
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) 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 // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD); glBlendEquation(GL_FUNC_ADD);
@ -296,7 +323,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (g_GlVersion >= 310) if (bd->GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART); glDisable(GL_PRIMITIVE_RESTART);
#endif #endif
#ifdef GL_POLYGON_MODE #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) // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
#if defined(GL_CLIP_ORIGIN) #if defined(GL_CLIP_ORIGIN)
bool clip_origin_lower_left = true; bool clip_origin_lower_left = true;
if (g_HasClipOrigin) if (bd->HasClipOrigin)
{ {
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin); GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin);
if (current_clip_origin == GL_UPPER_LEFT) 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 }, { 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
}; };
glUseProgram(g_ShaderHandle); glUseProgram(bd->ShaderHandle);
glUniform1i(g_AttribLocationTex, 0); glUniform1i(bd->AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER #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. glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif #endif
@ -346,14 +373,14 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
#endif #endif
// Bind vertex/index buffers and setup attributes for ImDrawVert // Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos); glEnableVertexAttribArray(bd->AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV); glEnableVertexAttribArray(bd->AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor); glEnableVertexAttribArray(bd->AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); glVertexAttribPointer(bd->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(bd->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)); glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
} }
// OpenGL3 Render function. // OpenGL3 Render function.
@ -367,13 +394,15 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (fb_width <= 0 || fb_height <= 0) if (fb_width <= 0 || fb_height <= 0)
return; return;
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Backup GL state // Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program); GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture); GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER #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 #endif
GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer); GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY #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_stencil_test = glIsEnabled(GL_STENCIL_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART #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 #endif
// Setup desired GL state // Setup desired GL state
@ -450,7 +479,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
// Bind texture, Draw // Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET #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); 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 else
#endif #endif
@ -469,7 +498,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
glUseProgram(last_program); glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture); glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
if (g_GlVersion >= 330) if (bd->GlVersion >= 330)
glBindSampler(0, last_sampler); glBindSampler(0, last_sampler);
#endif #endif
glActiveTexture(last_active_texture); 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_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); if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART #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 #endif
#ifdef GL_POLYGON_MODE #ifdef GL_POLYGON_MODE
@ -497,8 +526,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
bool ImGui_ImplOpenGL3_CreateFontsTexture() bool ImGui_ImplOpenGL3_CreateFontsTexture()
{ {
// Build texture atlas
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Build texture atlas
unsigned char* pixels; unsigned char* pixels;
int width, height; 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. 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 // Upload texture to graphics system
GLint last_texture; GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture); glGenTextures(1, &bd->FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture); glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH #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); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier // Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontTexture); io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
// Restore state // Restore state
glBindTexture(GL_TEXTURE_2D, last_texture); glBindTexture(GL_TEXTURE_2D, last_texture);
@ -526,23 +557,25 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture()
void ImGui_ImplOpenGL3_DestroyFontsTexture() 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, &bd->FontTexture);
glDeleteTextures(1, &g_FontTexture);
io.Fonts->SetTexID(0); 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. // 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) static bool CheckShader(GLuint handle, const char* desc)
{ {
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLint status = 0, log_length = 0; GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status); glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE) 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) if (log_length > 1)
{ {
ImVector<char> buf; ImVector<char> 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. // 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) static bool CheckProgram(GLuint handle, const char* desc)
{ {
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLint status = 0, log_length = 0; GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status); glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE) 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) if (log_length > 1)
{ {
ImVector<char> buf; ImVector<char> buf;
@ -573,6 +607,8 @@ static bool CheckProgram(GLuint handle, const char* desc)
bool ImGui_ImplOpenGL3_CreateDeviceObjects() bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{ {
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Backup GL state // Backup GL state
GLint last_texture, last_array_buffer; GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
@ -584,7 +620,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
// Parse GLSL version string // Parse GLSL version string
int glsl_version = 130; int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version); sscanf(bd->GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 = const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n" "uniform mat4 ProjMtx;\n"
@ -711,39 +747,39 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
} }
// Create shaders // 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); GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL); glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL);
glCompileShader(vert_handle); glCompileShader(vert_handle);
CheckShader(vert_handle, "vertex shader"); 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); GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL); glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL);
glCompileShader(frag_handle); glCompileShader(frag_handle);
CheckShader(frag_handle, "fragment shader"); CheckShader(frag_handle, "fragment shader");
// Link // Link
g_ShaderHandle = glCreateProgram(); bd->ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, vert_handle); glAttachShader(bd->ShaderHandle, vert_handle);
glAttachShader(g_ShaderHandle, frag_handle); glAttachShader(bd->ShaderHandle, frag_handle);
glLinkProgram(g_ShaderHandle); glLinkProgram(bd->ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program"); CheckProgram(bd->ShaderHandle, "shader program");
glDetachShader(g_ShaderHandle, vert_handle); glDetachShader(bd->ShaderHandle, vert_handle);
glDetachShader(g_ShaderHandle, frag_handle); glDetachShader(bd->ShaderHandle, frag_handle);
glDeleteShader(vert_handle); glDeleteShader(vert_handle);
glDeleteShader(frag_handle); glDeleteShader(frag_handle);
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position"); bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position");
g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV"); bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV");
g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color"); bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color");
// Create buffers // Create buffers
glGenBuffers(1, &g_VboHandle); glGenBuffers(1, &bd->VboHandle);
glGenBuffers(1, &g_ElementsHandle); glGenBuffers(1, &bd->ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture(); ImGui_ImplOpenGL3_CreateFontsTexture();
@ -759,9 +795,9 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
void ImGui_ImplOpenGL3_DestroyDeviceObjects() void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{ {
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; } ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; } if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; } if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture(); ImGui_ImplOpenGL3_DestroyFontsTexture();
} }

View File

@ -18,6 +18,7 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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) // 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-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). // 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_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4)
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
// Data struct ImGui_ImplSDL2_Data
static SDL_Window* g_Window = NULL; {
static Uint64 g_Time = 0; SDL_Window* Window;
static bool g_MousePressed[3] = { false, false, false }; Uint64 Time;
static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; bool MousePressed[3];
static char* g_ClipboardTextData = NULL; SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
static bool g_MouseCanUseGlobalState = true; 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*) static const char* ImGui_ImplSDL2_GetClipboardText(void*)
{ {
if (g_ClipboardTextData) ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
SDL_free(g_ClipboardTextData); if (bd->ClipboardTextData)
g_ClipboardTextData = SDL_GetClipboardText(); SDL_free(bd->ClipboardTextData);
return g_ClipboardTextData; bd->ClipboardTextData = SDL_GetClipboardText();
return bd->ClipboardTextData;
} }
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) 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) bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
switch (event->type) switch (event->type)
{ {
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
@ -100,9 +115,9 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
} }
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
{ {
if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true; if (event->button.button == SDL_BUTTON_LEFT) { bd->MousePressed[0] = true; }
if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true; if (event->button.button == SDL_BUTTON_RIGHT) { bd->MousePressed[1] = true; }
if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true; if (event->button.button == SDL_BUTTON_MIDDLE) { bd->MousePressed[2] = true; }
return true; return true;
} }
case SDL_TEXTINPUT: case SDL_TEXTINPUT:
@ -132,13 +147,17 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
static bool ImGui_ImplSDL2_Init(SDL_Window* window) 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 // 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_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) 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. // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
@ -169,24 +188,24 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window)
io.ClipboardUserData = NULL; io.ClipboardUserData = NULL;
// Load mouse cursors // Load mouse cursors
g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); 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 // 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) // ("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* sdl_backend = SDL_GetCurrentVideoDriver();
const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; 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++) 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) if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
g_MouseCanUseGlobalState = true; bd->MouseCanUseGlobalState = true;
#ifdef _WIN32 #ifdef _WIN32
SDL_SysWMinfo wmInfo; 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) 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); return ImGui_ImplSDL2_Init(window);
} }
@ -229,41 +248,48 @@ bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
void ImGui_ImplSDL2_Shutdown() 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 // Destroy last known clipboard data
if (g_ClipboardTextData) if (bd->ClipboardTextData)
SDL_free(g_ClipboardTextData); SDL_free(bd->ClipboardTextData);
g_ClipboardTextData = NULL; bd->ClipboardTextData = NULL;
// Destroy SDL mouse cursors // Destroy SDL mouse cursors
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
SDL_FreeCursor(g_MouseCursors[cursor_n]); SDL_FreeCursor(bd->MouseCursors[cursor_n]);
memset(g_MouseCursors, 0, sizeof(g_MouseCursors)); memset(bd->MouseCursors, 0, sizeof(bd->MouseCursors));
io.BackendPlatformName = NULL;
io.BackendPlatformUserData = NULL;
ImGui_ImplSDL2_DestroyBackendData();
} }
static void ImGui_ImplSDL2_UpdateMousePosAndButtons() static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
{ {
ImGuiIO& io = ImGui::GetIO(); 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) // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos) 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 else
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
int mx, my; int mx, my;
Uint32 mouse_buttons = SDL_GetMouseState(&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[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] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; io.MouseDown[1] = bd->MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; io.MouseDown[2] = bd->MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; 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) #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS)
SDL_Window* focused_window = SDL_GetKeyboardFocus(); 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(?) // 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. // 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(); bool any_mouse_button_down = ImGui::IsAnyMouseDown();
SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE);
#else #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); io.MousePos = ImVec2((float)mx, (float)my);
#endif #endif
} }
@ -292,6 +318,7 @@ static void ImGui_ImplSDL2_UpdateMouseCursor()
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return; return;
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
@ -302,7 +329,7 @@ static void ImGui_ImplSDL2_UpdateMouseCursor()
else else
{ {
// Show OS mouse cursor // 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); SDL_ShowCursor(SDL_TRUE);
} }
} }
@ -351,6 +378,8 @@ static void ImGui_ImplSDL2_UpdateGamepads()
void ImGui_ImplSDL2_NewFrame(SDL_Window* window) void ImGui_ImplSDL2_NewFrame(SDL_Window* window)
{ {
ImGuiIO& io = ImGui::GetIO(); 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) // Setup display size (every frame to accommodate for window resizing)
int w, h; 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) // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
static Uint64 frequency = SDL_GetPerformanceFrequency(); static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 current_time = SDL_GetPerformanceCounter(); Uint64 current_time = SDL_GetPerformanceCounter();
io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f); io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
g_Time = current_time; bd->Time = current_time;
ImGui_ImplSDL2_UpdateMousePosAndButtons(); ImGui_ImplSDL2_UpdateMousePosAndButtons();
ImGui_ImplSDL2_UpdateMouseCursor(); ImGui_ImplSDL2_UpdateMouseCursor();

View File

@ -6,7 +6,7 @@
// Missing features: // Missing features:
// [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this backend! See https://github.com/ocornut/imgui/pull/914 // [ ] 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. // 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. // 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 // Read online: https://github.com/ocornut/imgui/tree/master/docs
@ -23,6 +23,7 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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-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(). // 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 // Vulkan data
static ImGui_ImplVulkan_InitInfo g_VulkanInitInfo = {}; struct ImGui_ImplVulkan_Data
static VkRenderPass g_RenderPass = VK_NULL_HANDLE; {
static VkDeviceSize g_BufferMemoryAlignment = 256; ImGui_ImplVulkan_InitInfo VulkanInitInfo;
static VkPipelineCreateFlags g_PipelineCreateFlags = 0x00; VkRenderPass RenderPass;
static VkDescriptorSetLayout g_DescriptorSetLayout = VK_NULL_HANDLE; VkDeviceSize BufferMemoryAlignment;
static VkPipelineLayout g_PipelineLayout = VK_NULL_HANDLE; VkPipelineCreateFlags PipelineCreateFlags;
static VkDescriptorSet g_DescriptorSet = VK_NULL_HANDLE; VkDescriptorSetLayout DescriptorSetLayout;
static VkPipeline g_Pipeline = VK_NULL_HANDLE; VkPipelineLayout PipelineLayout;
static uint32_t g_Subpass = 0; VkDescriptorSet DescriptorSet;
static VkShaderModule g_ShaderModuleVert; VkPipeline Pipeline;
static VkShaderModule g_ShaderModuleFrag; uint32_t Subpass;
#ifdef VK_NO_PROTOTYPES VkShaderModule ShaderModuleVert;
static bool g_FunctionsLoaded = false; VkShaderModule ShaderModuleFrag;
#else
static bool g_FunctionsLoaded = true;
#endif
// Font data // Font data
static VkSampler g_FontSampler = VK_NULL_HANDLE; VkSampler FontSampler;
static VkDeviceMemory g_FontMemory = VK_NULL_HANDLE; VkDeviceMemory FontMemory;
static VkImage g_FontImage = VK_NULL_HANDLE; VkImage FontImage;
static VkImageView g_FontView = VK_NULL_HANDLE; VkImageView FontView;
static VkDeviceMemory g_UploadBufferMemory = VK_NULL_HANDLE; VkDeviceMemory UploadBufferMemory;
static VkBuffer g_UploadBuffer = VK_NULL_HANDLE; VkBuffer UploadBuffer;
// Render buffers // Render buffers
static ImGui_ImplVulkanH_WindowRenderBuffers g_MainWindowRenderBuffers; 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 // Forward Declarations
bool ImGui_ImplVulkan_CreateDeviceObjects(); bool ImGui_ImplVulkan_CreateDeviceObjects();
@ -118,6 +129,11 @@ void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_devi
// Vulkan prototypes for use with custom loaders // Vulkan prototypes for use with custom loaders
// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h // (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h
#ifdef VK_NO_PROTOTYPES #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) \ #define IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_MAP_MACRO) \
IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \
IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateDescriptorSets) \ 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) 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; VkPhysicalDeviceMemoryProperties prop;
vkGetPhysicalDeviceMemoryProperties(v->PhysicalDevice, &prop); vkGetPhysicalDeviceMemoryProperties(v->PhysicalDevice, &prop);
for (uint32_t i = 0; i < prop.memoryTypeCount; i++) 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) 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) if (v->CheckVkResultFn)
v->CheckVkResultFn(err); v->CheckVkResultFn(err);
} }
static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& p_buffer_size, size_t new_size, VkBufferUsageFlagBits usage) 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; VkResult err;
if (buffer != VK_NULL_HANDLE) if (buffer != VK_NULL_HANDLE)
vkDestroyBuffer(v->Device, buffer, v->Allocator); vkDestroyBuffer(v->Device, buffer, v->Allocator);
if (buffer_memory != VK_NULL_HANDLE) if (buffer_memory != VK_NULL_HANDLE)
vkFreeMemory(v->Device, buffer_memory, v->Allocator); 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 = {}; VkBufferCreateInfo buffer_info = {};
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = vertex_buffer_size_aligned; buffer_info.size = vertex_buffer_size_aligned;
@ -335,7 +356,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory
VkMemoryRequirements req; VkMemoryRequirements req;
vkGetBufferMemoryRequirements(v->Device, buffer, &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 = {}; VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = req.size; 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) 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: // Bind pipeline and descriptor sets:
{ {
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDescriptorSet desc_set[1] = { g_DescriptorSet }; VkDescriptorSet desc_set[1] = { bd->DescriptorSet };
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_PipelineLayout, 0, 1, desc_set, 0, NULL); vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, NULL);
} }
// Bind Vertex And Index Buffer: // Bind Vertex And Index Buffer:
@ -387,8 +410,8 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
float translate[2]; float translate[2];
translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0];
translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; 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, bd->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) * 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) if (fb_width <= 0 || fb_height <= 0)
return; 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) if (pipeline == VK_NULL_HANDLE)
pipeline = g_Pipeline; pipeline = bd->Pipeline;
// Allocate array to store enough vertex/index buffers // Allocate array to store enough vertex/index buffers
ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &g_MainWindowRenderBuffers; ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers;
if (wrb->FrameRenderBuffers == NULL) if (wrb->FrameRenderBuffers == NULL)
{ {
wrb->Index = 0; wrb->Index = 0;
@ -519,8 +543,9 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer)
{ {
ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo;
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
unsigned char* pixels; unsigned char* pixels;
int width, height; 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.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 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); check_vk_result(err);
VkMemoryRequirements req; VkMemoryRequirements req;
vkGetImageMemoryRequirements(v->Device, g_FontImage, &req); vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req);
VkMemoryAllocateInfo alloc_info = {}; VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = req.size; alloc_info.allocationSize = req.size;
alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); 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); 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); check_vk_result(err);
} }
@ -563,25 +588,25 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer)
{ {
VkImageViewCreateInfo info = {}; VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_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.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_R8G8B8A8_UNORM; info.format = VK_FORMAT_R8G8B8A8_UNORM;
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.levelCount = 1; info.subresourceRange.levelCount = 1;
info.subresourceRange.layerCount = 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); check_vk_result(err);
} }
// Update the Descriptor Set: // Update the Descriptor Set:
{ {
VkDescriptorImageInfo desc_image[1] = {}; VkDescriptorImageInfo desc_image[1] = {};
desc_image[0].sampler = g_FontSampler; desc_image[0].sampler = bd->FontSampler;
desc_image[0].imageView = g_FontView; desc_image[0].imageView = bd->FontView;
desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkWriteDescriptorSet write_desc[1] = {}; VkWriteDescriptorSet write_desc[1] = {};
write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 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].descriptorCount = 1;
write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_desc[0].pImageInfo = desc_image; write_desc[0].pImageInfo = desc_image;
@ -595,34 +620,34 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer)
buffer_info.size = upload_size; buffer_info.size = upload_size;
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 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); check_vk_result(err);
VkMemoryRequirements req; VkMemoryRequirements req;
vkGetBufferMemoryRequirements(v->Device, g_UploadBuffer, &req); vkGetBufferMemoryRequirements(v->Device, bd->UploadBuffer, &req);
g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment;
VkMemoryAllocateInfo alloc_info = {}; VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = req.size; alloc_info.allocationSize = req.size;
alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); 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); 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); check_vk_result(err);
} }
// Upload to Buffer: // Upload to Buffer:
{ {
char* map = NULL; 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); check_vk_result(err);
memcpy(map, pixels, upload_size); memcpy(map, pixels, upload_size);
VkMappedMemoryRange range[1] = {}; VkMappedMemoryRange range[1] = {};
range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
range[0].memory = g_UploadBufferMemory; range[0].memory = bd->UploadBufferMemory;
range[0].size = upload_size; range[0].size = upload_size;
err = vkFlushMappedMemoryRanges(v->Device, 1, range); err = vkFlushMappedMemoryRanges(v->Device, 1, range);
check_vk_result(err); check_vk_result(err);
vkUnmapMemory(v->Device, g_UploadBufferMemory); vkUnmapMemory(v->Device, bd->UploadBufferMemory);
} }
// Copy to Image: // 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].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
copy_barrier[0].dstQueueFamilyIndex = 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.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_barrier[0].subresourceRange.levelCount = 1; copy_barrier[0].subresourceRange.levelCount = 1;
copy_barrier[0].subresourceRange.layerCount = 1; copy_barrier[0].subresourceRange.layerCount = 1;
@ -646,7 +671,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer)
region.imageExtent.width = width; region.imageExtent.width = width;
region.imageExtent.height = height; region.imageExtent.height = height;
region.imageExtent.depth = 1; region.imageExtent.depth = 1;
vkCmdCopyBufferToImage(command_buffer, g_UploadBuffer, g_FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region); vkCmdCopyBufferToImage(command_buffer, bd->UploadBuffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
VkImageMemoryBarrier use_barrier[1] = {}; VkImageMemoryBarrier use_barrier[1] = {};
use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; 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].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
use_barrier[0].dstQueueFamilyIndex = 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.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
use_barrier[0].subresourceRange.levelCount = 1; use_barrier[0].subresourceRange.levelCount = 1;
use_barrier[0].subresourceRange.layerCount = 1; use_barrier[0].subresourceRange.layerCount = 1;
@ -664,7 +689,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer)
} }
// Store our identifier // Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontImage); io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontImage);
return true; return true;
} }
@ -672,29 +697,31 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer)
static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator)
{ {
// Create the shader modules // Create the shader modules
if (g_ShaderModuleVert == NULL) ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->ShaderModuleVert == NULL)
{ {
VkShaderModuleCreateInfo vert_info = {}; VkShaderModuleCreateInfo vert_info = {};
vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
vert_info.codeSize = sizeof(__glsl_shader_vert_spv); vert_info.codeSize = sizeof(__glsl_shader_vert_spv);
vert_info.pCode = (uint32_t*)__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); check_vk_result(err);
} }
if (g_ShaderModuleFrag == NULL) if (bd->ShaderModuleFrag == NULL)
{ {
VkShaderModuleCreateInfo frag_info = {}; VkShaderModuleCreateInfo frag_info = {};
frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
frag_info.codeSize = sizeof(__glsl_shader_frag_spv); frag_info.codeSize = sizeof(__glsl_shader_frag_spv);
frag_info.pCode = (uint32_t*)__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); check_vk_result(err);
} }
} }
static void ImGui_ImplVulkan_CreateFontSampler(VkDevice device, const VkAllocationCallbacks* allocator) static void ImGui_ImplVulkan_CreateFontSampler(VkDevice device, const VkAllocationCallbacks* allocator)
{ {
if (g_FontSampler) ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->FontSampler)
return; return;
VkSamplerCreateInfo info = {}; VkSamplerCreateInfo info = {};
@ -708,17 +735,18 @@ static void ImGui_ImplVulkan_CreateFontSampler(VkDevice device, const VkAllocati
info.minLod = -1000; info.minLod = -1000;
info.maxLod = 1000; info.maxLod = 1000;
info.maxAnisotropy = 1.0f; info.maxAnisotropy = 1.0f;
VkResult err = vkCreateSampler(device, &info, allocator, &g_FontSampler); VkResult err = vkCreateSampler(device, &info, allocator, &bd->FontSampler);
check_vk_result(err); check_vk_result(err);
} }
static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDevice device, const VkAllocationCallbacks* allocator) static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDevice device, const VkAllocationCallbacks* allocator)
{ {
if (g_DescriptorSetLayout) ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->DescriptorSetLayout)
return; return;
ImGui_ImplVulkan_CreateFontSampler(device, allocator); ImGui_ImplVulkan_CreateFontSampler(device, allocator);
VkSampler sampler[1] = { g_FontSampler }; VkSampler sampler[1] = { bd->FontSampler };
VkDescriptorSetLayoutBinding binding[1] = {}; VkDescriptorSetLayoutBinding binding[1] = {};
binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
binding[0].descriptorCount = 1; 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.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
info.bindingCount = 1; info.bindingCount = 1;
info.pBindings = binding; info.pBindings = binding;
VkResult err = vkCreateDescriptorSetLayout(device, &info, allocator, &g_DescriptorSetLayout); VkResult err = vkCreateDescriptorSetLayout(device, &info, allocator, &bd->DescriptorSetLayout);
check_vk_result(err); check_vk_result(err);
} }
static void ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device, const VkAllocationCallbacks* allocator) static void ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device, const VkAllocationCallbacks* allocator)
{ {
if (g_PipelineLayout) ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->PipelineLayout)
return; return;
// Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix // 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].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constants[0].offset = sizeof(float) * 0; push_constants[0].offset = sizeof(float) * 0;
push_constants[0].size = sizeof(float) * 4; push_constants[0].size = sizeof(float) * 4;
VkDescriptorSetLayout set_layout[1] = { g_DescriptorSetLayout }; VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout };
VkPipelineLayoutCreateInfo layout_info = {}; VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1; layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = set_layout; layout_info.pSetLayouts = set_layout;
layout_info.pushConstantRangeCount = 1; layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants; 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); 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) 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); ImGui_ImplVulkan_CreateShaderModules(device, allocator);
VkPipelineShaderStageCreateInfo stage[2] = {}; VkPipelineShaderStageCreateInfo stage[2] = {};
stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
stage[0].module = g_ShaderModuleVert; stage[0].module = bd->ShaderModuleVert;
stage[0].pName = "main"; stage[0].pName = "main";
stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
stage[1].module = g_ShaderModuleFrag; stage[1].module = bd->ShaderModuleFrag;
stage[1].pName = "main"; stage[1].pName = "main";
VkVertexInputBindingDescription binding_desc[1] = {}; VkVertexInputBindingDescription binding_desc[1] = {};
@ -841,7 +871,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC
VkGraphicsPipelineCreateInfo info = {}; VkGraphicsPipelineCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
info.flags = g_PipelineCreateFlags; info.flags = bd->PipelineCreateFlags;
info.stageCount = 2; info.stageCount = 2;
info.pStages = stage; info.pStages = stage;
info.pVertexInputState = &vertex_info; info.pVertexInputState = &vertex_info;
@ -852,7 +882,7 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC
info.pDepthStencilState = &depth_info; info.pDepthStencilState = &depth_info;
info.pColorBlendState = &blend_info; info.pColorBlendState = &blend_info;
info.pDynamicState = &dynamic_state; info.pDynamicState = &dynamic_state;
info.layout = g_PipelineLayout; info.layout = bd->PipelineLayout;
info.renderPass = renderPass; info.renderPass = renderPass;
info.subpass = subpass; info.subpass = subpass;
VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); 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() 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; VkResult err;
if (!g_FontSampler) if (!bd->FontSampler)
{ {
VkSamplerCreateInfo info = {}; VkSamplerCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
@ -877,13 +908,13 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
info.minLod = -1000; info.minLod = -1000;
info.maxLod = 1000; info.maxLod = 1000;
info.maxAnisotropy = 1.0f; 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); check_vk_result(err);
} }
if (!g_DescriptorSetLayout) if (!bd->DescriptorSetLayout)
{ {
VkSampler sampler[1] = {g_FontSampler}; VkSampler sampler[1] = {bd->FontSampler};
VkDescriptorSetLayoutBinding binding[1] = {}; VkDescriptorSetLayoutBinding binding[1] = {};
binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
binding[0].descriptorCount = 1; binding[0].descriptorCount = 1;
@ -893,7 +924,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
info.bindingCount = 1; info.bindingCount = 1;
info.pBindings = binding; 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); check_vk_result(err);
} }
@ -903,64 +934,66 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = v->DescriptorPool; alloc_info.descriptorPool = v->DescriptorPool;
alloc_info.descriptorSetCount = 1; alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &g_DescriptorSetLayout; alloc_info.pSetLayouts = &bd->DescriptorSetLayout;
err = vkAllocateDescriptorSets(v->Device, &alloc_info, &g_DescriptorSet); err = vkAllocateDescriptorSets(v->Device, &alloc_info, &bd->DescriptorSet);
check_vk_result(err); 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 // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
VkPushConstantRange push_constants[1] = {}; VkPushConstantRange push_constants[1] = {};
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constants[0].offset = sizeof(float) * 0; push_constants[0].offset = sizeof(float) * 0;
push_constants[0].size = sizeof(float) * 4; push_constants[0].size = sizeof(float) * 4;
VkDescriptorSetLayout set_layout[1] = { g_DescriptorSetLayout }; VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout };
VkPipelineLayoutCreateInfo layout_info = {}; VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1; layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = set_layout; layout_info.pSetLayouts = set_layout;
layout_info.pushConstantRangeCount = 1; layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants; 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); 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; return true;
} }
void ImGui_ImplVulkan_DestroyFontUploadObjects() void ImGui_ImplVulkan_DestroyFontUploadObjects()
{ {
ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (g_UploadBuffer) ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
if (bd->UploadBuffer)
{ {
vkDestroyBuffer(v->Device, g_UploadBuffer, v->Allocator); vkDestroyBuffer(v->Device, bd->UploadBuffer, v->Allocator);
g_UploadBuffer = VK_NULL_HANDLE; bd->UploadBuffer = VK_NULL_HANDLE;
} }
if (g_UploadBufferMemory) if (bd->UploadBufferMemory)
{ {
vkFreeMemory(v->Device, g_UploadBufferMemory, v->Allocator); vkFreeMemory(v->Device, bd->UploadBufferMemory, v->Allocator);
g_UploadBufferMemory = VK_NULL_HANDLE; bd->UploadBufferMemory = VK_NULL_HANDLE;
} }
} }
void ImGui_ImplVulkan_DestroyDeviceObjects() void ImGui_ImplVulkan_DestroyDeviceObjects()
{ {
ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &g_MainWindowRenderBuffers, v->Allocator); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator);
ImGui_ImplVulkan_DestroyFontUploadObjects(); ImGui_ImplVulkan_DestroyFontUploadObjects();
if (g_ShaderModuleVert) { vkDestroyShaderModule(v->Device, g_ShaderModuleVert, v->Allocator); g_ShaderModuleVert = VK_NULL_HANDLE; } if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; }
if (g_ShaderModuleFrag) { vkDestroyShaderModule(v->Device, g_ShaderModuleFrag, v->Allocator); g_ShaderModuleFrag = VK_NULL_HANDLE; } if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; }
if (g_FontView) { vkDestroyImageView(v->Device, g_FontView, v->Allocator); g_FontView = VK_NULL_HANDLE; } if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; }
if (g_FontImage) { vkDestroyImage(v->Device, g_FontImage, v->Allocator); g_FontImage = VK_NULL_HANDLE; } if (bd->FontImage) { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; }
if (g_FontMemory) { vkFreeMemory(v->Device, g_FontMemory, v->Allocator); g_FontMemory = VK_NULL_HANDLE; } if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; }
if (g_FontSampler) { vkDestroySampler(v->Device, g_FontSampler, v->Allocator); g_FontSampler = VK_NULL_HANDLE; } if (bd->FontSampler) { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; }
if (g_DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, g_DescriptorSetLayout, v->Allocator); g_DescriptorSetLayout = VK_NULL_HANDLE; } if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; }
if (g_PipelineLayout) { vkDestroyPipelineLayout(v->Device, g_PipelineLayout, v->Allocator); g_PipelineLayout = VK_NULL_HANDLE; } if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; }
if (g_Pipeline) { vkDestroyPipeline(v->Device, g_Pipeline, v->Allocator); g_Pipeline = 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) 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!"); 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(); 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.BackendRendererName = "imgui_impl_vulkan";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. 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(info->ImageCount >= info->MinImageCount);
IM_ASSERT(render_pass != VK_NULL_HANDLE); IM_ASSERT(render_pass != VK_NULL_HANDLE);
g_VulkanInitInfo = *info; bd->VulkanInitInfo = *info;
g_RenderPass = render_pass; bd->RenderPass = render_pass;
g_Subpass = info->Subpass; bd->Subpass = info->Subpass;
ImGui_ImplVulkan_CreateDeviceObjects(); ImGui_ImplVulkan_CreateDeviceObjects();
@ -1013,7 +1050,11 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend
void ImGui_ImplVulkan_Shutdown() void ImGui_ImplVulkan_Shutdown()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplVulkan_DestroyDeviceObjects(); ImGui_ImplVulkan_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
ImGui_ImplVulkan_DestroyBackendData();
} }
void ImGui_ImplVulkan_NewFrame() void ImGui_ImplVulkan_NewFrame()
@ -1022,15 +1063,16 @@ void ImGui_ImplVulkan_NewFrame()
void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count)
{ {
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
IM_ASSERT(min_image_count >= 2); IM_ASSERT(min_image_count >= 2);
if (g_VulkanInitInfo.MinImageCount == min_image_count) if (bd->VulkanInitInfo.MinImageCount == min_image_count)
return; return;
ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
VkResult err = vkDeviceWaitIdle(v->Device); VkResult err = vkDeviceWaitIdle(v->Device);
check_vk_result(err); check_vk_result(err);
ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &g_MainWindowRenderBuffers, v->Allocator); ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator);
g_VulkanInitInfo.MinImageCount = min_image_count; 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, // 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: // 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 // 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) 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) 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++) for (uint32_t i = 0; i < wd->ImageCount; i++)
{ {

View File

@ -33,6 +33,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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-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-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). // 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. // 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. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
// Win32 Data struct ImGui_ImplWin32_Data
static HWND g_hWnd = NULL; {
static INT64 g_Time = 0; HWND hWnd;
static INT64 g_TicksPerSecond = 0; INT64 Time;
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; INT64 TicksPerSecond;
static bool g_HasGamepad = false; ImGuiMouseCursor LastMouseCursor;
static bool g_WantUpdateHasGamepad = true; bool HasGamepad;
bool WantUpdateHasGamepad;
// XInput DLL and functions
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
static HMODULE g_XInputDLL = NULL; HMODULE XInputDLL;
static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL; PFN_XInputGetCapabilities XInputGetCapabilities;
static PFN_XInputGetState g_XInputGetState = NULL; PFN_XInputGetState XInputGetState;
#endif #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 // Functions
bool ImGui_ImplWin32_Init(void* hwnd) 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; return false;
if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time)) if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter))
return false; 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 // Setup backend capabilities flags
g_hWnd = (HWND)hwnd; io.BackendPlatformUserData = (void*)bd;
ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = "imgui_impl_win32";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_win32";
io.ImeWindowHandle = hwnd; 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. // 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++) for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++)
if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n]))
{ {
g_XInputDLL = dll; bd->XInputDLL = dll;
g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities");
g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState");
break; break;
} }
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
@ -143,21 +163,18 @@ bool ImGui_ImplWin32_Init(void* hwnd)
void ImGui_ImplWin32_Shutdown() void ImGui_ImplWin32_Shutdown()
{ {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
// Unload XInput library // Unload XInput library
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
if (g_XInputDLL) if (bd->XInputDLL)
::FreeLibrary(g_XInputDLL); ::FreeLibrary(bd->XInputDLL);
g_XInputDLL = NULL;
g_XInputGetCapabilities = NULL;
g_XInputGetState = NULL;
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
g_hWnd = NULL; io.BackendPlatformName = NULL;
g_Time = 0; io.BackendPlatformUserData = NULL;
g_TicksPerSecond = 0; ImGui_ImplWin32_DestroyBackendData();
g_LastMouseCursor = ImGuiMouseCursor_COUNT;
g_HasGamepad = false;
g_WantUpdateHasGamepad = true;
} }
static bool ImGui_ImplWin32_UpdateMouseCursor() static bool ImGui_ImplWin32_UpdateMouseCursor()
@ -196,13 +213,14 @@ static bool ImGui_ImplWin32_UpdateMouseCursor()
static void ImGui_ImplWin32_UpdateMousePos() static void ImGui_ImplWin32_UpdateMousePos()
{ {
ImGuiIO& io = ImGui::GetIO(); 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) // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos) if (io.WantSetMousePos)
{ {
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; 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); ::SetCursorPos(pos.x, pos.y);
} }
@ -210,8 +228,8 @@ static void ImGui_ImplWin32_UpdateMousePos()
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
POINT pos; POINT pos;
if (HWND active_window = ::GetForegroundWindow()) if (HWND active_window = ::GetForegroundWindow())
if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd)) if (active_window == bd->hWnd || ::IsChild(active_window, bd->hWnd))
if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos)) if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos))
io.MousePos = ImVec2((float)pos.x, (float)pos.y); io.MousePos = ImVec2((float)pos.x, (float)pos.y);
} }
@ -220,22 +238,23 @@ static void ImGui_ImplWin32_UpdateGamepads()
{ {
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
memset(io.NavInputs, 0, sizeof(io.NavInputs)); memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return; return;
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
if (g_WantUpdateHasGamepad) if (bd->WantUpdateHasGamepad)
{ {
XINPUT_CAPABILITIES caps; XINPUT_CAPABILITIES caps;
g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false;
g_WantUpdateHasGamepad = false; bd->WantUpdateHasGamepad = false;
} }
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
XINPUT_STATE xinput_state; 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; const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad; io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
@ -267,17 +286,18 @@ static void ImGui_ImplWin32_UpdateGamepads()
void ImGui_ImplWin32_NewFrame() void ImGui_ImplWin32_NewFrame()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
// Setup display size (every frame to accommodate for window resizing) // Setup display size (every frame to accommodate for window resizing)
RECT rect = { 0, 0, 0, 0 }; 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)); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
// Setup time step // Setup time step
INT64 current_time = 0; INT64 current_time = 0;
::QueryPerformanceCounter((LARGE_INTEGER*)&current_time); ::QueryPerformanceCounter((LARGE_INTEGER*)&current_time);
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond;
g_Time = current_time; bd->Time = current_time;
// Read keyboard modifiers inputs // Read keyboard modifiers inputs
io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; 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 // Update OS mouse cursor with the cursor requested by imgui
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); 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(); ImGui_ImplWin32_UpdateMouseCursor();
} }
@ -327,6 +347,8 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
return 0; return 0;
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
switch (msg) switch (msg)
{ {
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
@ -389,7 +411,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
return 0; return 0;
case WM_DEVICECHANGE: case WM_DEVICECHANGE:
if ((UINT)wParam == DBT_DEVNODES_CHANGED) if ((UINT)wParam == DBT_DEVNODES_CHANGED)
g_WantUpdateHasGamepad = true; bd->WantUpdateHasGamepad = true;
return 0; return 0;
} }
return 0; return 0;

View File

@ -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) - 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 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. 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) - 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_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. - ImGui_ImplWin32_EnableDpiAwareness() will call SetProcessDpiAwareness() fallback on Windows 8.1 without a manifest.