Skip to content

Instantly share code, notes, and snippets.

@RichardGale
Created May 16, 2017 22:15
Show Gist options
  • Save RichardGale/6e2b74bc42b3005e08397236e4be0fd0 to your computer and use it in GitHub Desktop.
Save RichardGale/6e2b74bc42b3005e08397236e4be0fd0 to your computer and use it in GitHub Desktop.
imgui+bgfx
// ImGui BFFX binding
// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown().
// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#include <imgui.h>
#include "imgui_impl_bgfx.h"
// BGFX/BX
#include "bgfx/bgfx.h"
#include "bgfx/embedded_shader.h"
#include "bx/fpumath.h"
#include "bx/timer.h"
// Data
static uint8_t g_View = 255;
static int64_t g_Time = 0;
static bgfx::TextureHandle g_FontTexture = BGFX_INVALID_HANDLE;
static bgfx::ProgramHandle g_ShaderHandle = BGFX_INVALID_HANDLE;
static bgfx::UniformHandle g_AttribLocationTex = BGFX_INVALID_HANDLE;
static bgfx::VertexDecl g_VertexDecl;
// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
// If text or lines are blurry when integrating ImGui in your engine:
// - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
void ImGui_Implbgfx_RenderDrawLists(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
ImGuiIO& io = ImGui::GetIO();
int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x);
int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y);
if (fb_width == 0 || fb_height == 0)
return;
draw_data->ScaleClipRects(io.DisplayFramebufferScale);
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
uint64_t state = BGFX_STATE_RGB_WRITE | BGFX_STATE_ALPHA_WRITE | BGFX_STATE_MSAA | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA);
// Setup viewport, orthographic projection matrix
float ortho[16];
bx::mtxOrtho(ortho, 0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, 1.0f);
bgfx::setViewTransform(g_View, NULL, ortho);
bgfx::setViewRect(g_View, 0, 0, (uint16_t)fb_width, (uint16_t)fb_height);
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
uint32_t idx_buffer_offset = 0;
bgfx::TransientVertexBuffer tvb;
bgfx::TransientIndexBuffer tib;
uint32_t numVertices = (uint32_t)cmd_list->VtxBuffer.size();
uint32_t numIndices = (uint32_t)cmd_list->IdxBuffer.size();
if ((numVertices != bgfx::getAvailTransientVertexBuffer(numVertices, g_VertexDecl)) ||
(numIndices != bgfx::getAvailTransientIndexBuffer(numIndices)))
{
// not enough space in transient buffer just quit drawing the rest...
break;
}
bgfx::allocTransientVertexBuffer(&tvb, numVertices, g_VertexDecl);
bgfx::allocTransientIndexBuffer(&tib, numIndices);
ImDrawVert* verts = (ImDrawVert*)tvb.data;
memcpy(verts, cmd_list->VtxBuffer.begin(), numVertices * sizeof(ImDrawVert));
ImDrawIdx* indices = (ImDrawIdx*)tib.data;
memcpy(indices, cmd_list->IdxBuffer.begin(), numIndices * sizeof(ImDrawIdx));
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
const uint16_t xx = (uint16_t)bx::fmax(pcmd->ClipRect.x, 0.0f);
const uint16_t yy = (uint16_t)bx::fmax(pcmd->ClipRect.y, 0.0f);
bgfx::setScissor(xx, yy, (uint16_t)bx::fmin(pcmd->ClipRect.z, 65535.0f)-xx, (uint16_t)bx::fmin(pcmd->ClipRect.w, 65535.0f)-yy);
bgfx::setState(state);
bgfx::TextureHandle texture = { (uint16_t)((intptr_t)pcmd->TextureId&0xffff) };
bgfx::setTexture(0, g_AttribLocationTex, texture);
bgfx::setVertexBuffer(&tvb, 0, numVertices);
bgfx::setIndexBuffer(&tib, idx_buffer_offset, pcmd->ElemCount);
bgfx::submit(g_View, g_ShaderHandle);
}
idx_buffer_offset += pcmd->ElemCount;
}
}
}
bool ImGui_Implbgfx_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
g_FontTexture = bgfx::createTexture2D((uint16_t)width,
(uint16_t)height,
false,
1,
bgfx::TextureFormat::BGRA8,
0,
bgfx::copy(pixels, width*height*4));
// Store our identifier
io.Fonts->TexID = (void *)(intptr_t)g_FontTexture.idx;
return true;
}
#include "vs_imgui.bin.h"
#include "fs_imgui.bin.h"
static const bgfx::EmbeddedShader s_embeddedShaders[] =
{
BGFX_EMBEDDED_SHADER(vs_ocornut_imgui),
BGFX_EMBEDDED_SHADER(fs_ocornut_imgui),
BGFX_EMBEDDED_SHADER_END()
};
bool ImGui_Implbgfx_CreateDeviceObjects()
{
bgfx::RendererType::Enum type = bgfx::getRendererType();
g_ShaderHandle = bgfx::createProgram(bgfx::createEmbeddedShader(s_embeddedShaders, type, "vs_ocornut_imgui"),
bgfx::createEmbeddedShader(s_embeddedShaders, type, "fs_ocornut_imgui"),
true);
g_VertexDecl.begin()
.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float)
.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true)
.end();
g_AttribLocationTex = bgfx::createUniform("g_AttribLocationTex", bgfx::UniformType::Int1);
ImGui_Implbgfx_CreateFontsTexture();
return true;
}
void ImGui_Implbgfx_InvalidateDeviceObjects()
{
bgfx::destroyUniform(g_AttribLocationTex);
bgfx::destroyProgram(g_ShaderHandle);
if (isValid(g_FontTexture))
{
bgfx::destroyTexture(g_FontTexture);
ImGui::GetIO().Fonts->TexID = 0;
g_FontTexture.idx = bgfx::invalidHandle;
}
}
void ImGui_Implbgfx_Init(int view)
{
g_View = (uint8_t)(view&0xff);
ImGuiIO& io = ImGui::GetIO();
// io.KeyMap[ImGuiKey_Tab] = (int)entry::Key::Tab;
// io.KeyMap[ImGuiKey_LeftArrow] = (int)entry::Key::Left;
// io.KeyMap[ImGuiKey_RightArrow] = (int)entry::Key::Right;
// io.KeyMap[ImGuiKey_UpArrow] = (int)entry::Key::Up;
// io.KeyMap[ImGuiKey_DownArrow] = (int)entry::Key::Down;
// io.KeyMap[ImGuiKey_Home] = (int)entry::Key::Home;
// io.KeyMap[ImGuiKey_End] = (int)entry::Key::End;
// io.KeyMap[ImGuiKey_Delete] = (int)entry::Key::Delete;
// io.KeyMap[ImGuiKey_Backspace] = (int)entry::Key::Backspace;
// io.KeyMap[ImGuiKey_Enter] = (int)entry::Key::Return;
// io.KeyMap[ImGuiKey_Escape] = (int)entry::Key::Esc;
// io.KeyMap[ImGuiKey_A] = (int)entry::Key::KeyA;
// io.KeyMap[ImGuiKey_C] = (int)entry::Key::KeyC;
// io.KeyMap[ImGuiKey_V] = (int)entry::Key::KeyV;
// io.KeyMap[ImGuiKey_X] = (int)entry::Key::KeyX;
// io.KeyMap[ImGuiKey_Y] = (int)entry::Key::KeyY;
// io.KeyMap[ImGuiKey_Z] = (int)entry::Key::KeyZ;
io.RenderDrawListsFn = ImGui_Implbgfx_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer.
g_Time = bx::getHPCounter();
}
void ImGui_Implbgfx_Shutdown()
{
ImGui_Implbgfx_InvalidateDeviceObjects();
ImGui::Shutdown();
}
void ImGui_Implbgfx_NewFrame(float width,
float height,
float mouse_x,
float mouse_y,
bool mousePressed0,
bool mousePressed1,
bool mousePressed2,
float mouseWheel,
int key)
{
if (!isValid(g_FontTexture))
ImGui_Implbgfx_CreateDeviceObjects();
ImGuiIO& io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing)
io.DisplaySize = ImVec2(width, height);
// Setup time step
int64_t current_time = bx::getHPCounter();
const double freq = (double)bx::getHPFrequency();
io.DeltaTime = (float)((current_time - g_Time)/freq);
g_Time = current_time;
// Setup inputs
io.MousePos = ImVec2(mouse_x, mouse_y);
io.MouseDown[0] = mousePressed0;
io.MouseDown[1] = mousePressed1;
io.MouseDown[2] = mousePressed2;
io.MouseWheel = mouseWheel;
if (key < 0x7f)
{
io.AddInputCharacter(key);
}
// Start the frame
ImGui::NewFrame();
}
// ImGui BGFX binding
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown().
// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
void ImGui_Implbgfx_Init(int view);
void ImGui_Implbgfx_Shutdown();
void ImGui_Implbgfx_NewFrame(float width,
float height,
float mouse_x,
float mouse_y,
bool mousePressed0,
bool mousePressed1,
bool mousePressed2,
float mouseWheel,
int key);
// Use if you want to reset your rendering device without losing ImGui state.
void ImGui_Implbgfx_InvalidateDeviceObjects();
bool ImGui_Implbgfx_CreateDeviceObjects();
@pr0g
Copy link

pr0g commented Aug 14, 2022

I just tested this locally and you're 100% correct. I could see the visual glitch before and then saw it disappear after. I've updated my fork of this Gist here with the changes. Thanks for reporting it! 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment