Created
March 20, 2024 14:13
-
-
Save sphaero/0e957a1e10dfa7a380eaa70824019c95 to your computer and use it in GitHub Desktop.
SDL OpenGL Dear ImGui Example with libuv integration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Dear ImGui: SDL2 + OpenGL + libuv | |
#include "imgui/imgui.h" | |
#include "imgui/backends/imgui_impl_sdl2.h" | |
#include "imgui/backends/imgui_impl_opengl3.h" | |
#include <stdio.h> | |
#include <uv.h> | |
#include <SDL.h> | |
#if defined(IMGUI_IMPL_OPENGL_ES2) | |
#include <SDL_opengles2.h> | |
#else | |
#include <SDL_opengl.h> | |
#endif | |
uv_loop_t *loop; | |
SDL_Window* window; | |
uint32_t syncts; | |
bool terminate; | |
// Our state | |
bool show_demo_window = true; | |
bool show_another_window = false; | |
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); | |
void run_timer2(uv_timer_t *handle) | |
{ | |
printf("helloe\n"); | |
if (terminate) | |
uv_timer_stop(handle); | |
} | |
void run_ui(uv_timer_t *handle) | |
{ | |
// Poll and handle events (inputs, window resize, etc.) | |
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. | |
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. | |
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. | |
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. | |
SDL_Event event; | |
while (SDL_PollEvent(&event)) | |
{ | |
ImGui_ImplSDL2_ProcessEvent(&event); | |
if (event.type == SDL_QUIT) | |
terminate = true; | |
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) | |
terminate = true; | |
} | |
if (terminate) | |
uv_timer_stop(handle); | |
// Start the Dear ImGui frame | |
ImGui_ImplOpenGL3_NewFrame(); | |
ImGui_ImplSDL2_NewFrame(); | |
ImGui::NewFrame(); | |
ImGuiIO& io = ImGui::GetIO(); (void)io; | |
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). | |
if (show_demo_window) | |
ImGui::ShowDemoWindow(&show_demo_window); | |
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. | |
{ | |
static float f = 0.0f; | |
static int counter = 0; | |
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. | |
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) | |
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state | |
ImGui::Checkbox("Another Window", &show_another_window); | |
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f | |
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color | |
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) | |
counter++; | |
ImGui::SameLine(); | |
ImGui::Text("counter = %d", counter); | |
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); | |
ImGui::End(); | |
} | |
// 3. Show another simple window. | |
if (show_another_window) | |
{ | |
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) | |
ImGui::Text("Hello from another window!"); | |
if (ImGui::Button("Close Me")) | |
show_another_window = false; | |
ImGui::End(); | |
} | |
// Rendering | |
ImGui::Render(); | |
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); | |
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); | |
glClear(GL_COLOR_BUFFER_BIT); | |
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | |
int a = SDL_GetTicks(); | |
SDL_GL_SwapWindow(window); | |
syncts = SDL_GetTicks(); | |
printf("swap duration: %ims, next sync in: %ims\n", syncts-a, (a+16)-syncts); | |
if (uv_timer_get_due_in(handle) < 15) | |
uv_timer_again(handle); //try to sync the timer with the vsync | |
} | |
// Main code | |
int main(int, char**) | |
{ | |
// init libuv | |
loop = uv_default_loop(); | |
syncts = 0; | |
// Setup SDL | |
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) | |
{ | |
printf("Error: %s\n", SDL_GetError()); | |
return -1; | |
} | |
// Decide GL+GLSL versions | |
#if defined(IMGUI_IMPL_OPENGL_ES2) | |
// GL ES 2.0 + GLSL 100 | |
const char* glsl_version = "#version 100"; | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); | |
#elif defined(__APPLE__) | |
// GL 3.2 Core + GLSL 150 | |
const char* glsl_version = "#version 150"; | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); | |
#else | |
// GL 3.0 + GLSL 130 | |
const char* glsl_version = "#version 130"; | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); | |
#endif | |
// From 2.0.18: Enable native IME. | |
#ifdef SDL_HINT_IME_SHOW_UI | |
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); | |
#endif | |
// Create window with graphics context | |
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | |
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | |
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); | |
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | |
window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); | |
if (window == nullptr) | |
{ | |
printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); | |
return -1; | |
} | |
SDL_GLContext gl_context = SDL_GL_CreateContext(window); | |
SDL_GL_MakeCurrent(window, gl_context); | |
int rcsync = SDL_GL_SetSwapInterval(-1); // Enable adaptive sync | |
if (rcsync == -1) | |
SDL_GL_SetSwapInterval(1); | |
// Setup Dear ImGui context | |
IMGUI_CHECKVERSION(); | |
ImGui::CreateContext(); | |
ImGuiIO& io = ImGui::GetIO(); (void)io; | |
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls | |
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls | |
// Setup Dear ImGui style | |
ImGui::StyleColorsDark(); | |
//ImGui::StyleColorsLight(); | |
// Setup Platform/Renderer backends | |
ImGui_ImplSDL2_InitForOpenGL(window, gl_context); | |
ImGui_ImplOpenGL3_Init(glsl_version); | |
uv_timer_t timer; | |
uv_timer_init(loop, &timer); | |
// run ui once so we now the sync timestamp (syncts) | |
run_ui(&timer); | |
int nextsyncts = (syncts+16)-SDL_GetTicks(); | |
assert(nextsyncts>0); | |
uv_timer_start(&timer, run_ui, nextsyncts, nextsyncts); | |
uv_timer_t timer2; | |
uv_timer_init(loop, &timer2); | |
uv_timer_start(&timer2, run_timer2, 0, 1); | |
int rc = uv_run(loop, UV_RUN_DEFAULT); | |
uv_loop_close(uv_default_loop()); | |
// Cleanup | |
ImGui_ImplOpenGL3_Shutdown(); | |
ImGui_ImplSDL2_Shutdown(); | |
ImGui::DestroyContext(); | |
SDL_GL_DeleteContext(gl_context); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return rc; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It tries to schedule at Vsync. This could probably be done differently. I'm not sure libuv can schedule that accurately.