Repository: bwrsandman/imgui-flame-graph Branch: master Commit: aae0bd9665d1 Files: 9 Total size: 26.2 KB Directory structure: gitextract_wviadsdu/ ├── .github/ │ └── workflows/ │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── example/ │ ├── CMakeLists.txt │ └── main.cpp ├── imgui_widget_flamegraph.cpp └── imgui_widget_flamegraph.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: push: pull_request: schedule: # * is a special character in YAML so you have to quote this string - cron: '45 8 * * *' jobs: build: strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] configuration: [Debug, Release] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Install OpenGL (Ubuntu) if: matrix.os == 'ubuntu-latest' run: | sudo apt-get update -qq sudo apt-get install -y --no-install-recommends libxrandr-dev libxi-dev libxxf86vm-dev libsdl2-dev - name: Build Sample run: | cmake -B_build -S${{ github.workspace }} -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} cmake --build _build --target imgui-flame-graph-example ================================================ FILE: .gitignore ================================================ # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # Clion project files .idea/ cmake-build-*/ _build/ ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.14 FATAL_ERROR) cmake_policy(SET CMP0076 NEW) project(imgui-flame-graph VERSION 1.02) option(IMGUI_FLAME_GRAPH_BUILD_EXAMPLE "Build an example using flame graph." ON) list(APPEND IMGUI_FLAME_GRAPH_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}) add_library(${PROJECT_NAME} INTERFACE) target_sources(${PROJECT_NAME} INTERFACE $ $) target_include_directories(${PROJECT_NAME} INTERFACE $) target_compile_definitions(${PROJECT_NAME} INTERFACE IMGUI_DEFINE_MATH_OPERATORS) if(IMGUI_FLAME_GRAPH_BUILD_EXAMPLE) add_subdirectory(example/) endif() ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Sandy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Dear ImGui Flame Graph Widget [![Build](https://github.com/bwrsandman/imgui-flame-graph/actions/workflows/build.yml/badge.svg)](https://github.com/bwrsandman/imgui-flame-graph/actions/workflows/build.yml) [![Download](https://img.shields.io/badge/master-v1.02-green.svg)](https://github.com/bwrsandman/imgui-flame-graph/releases/tag/v1.02) [![License](https://img.shields.io/github/license/bwrsandman/imgui-flame-graph)](LICENSE.md) [![GitHub Stars](https://img.shields.io/github/stars/bwrsandman/imgui-flame-graph?logo=github)](https://github.com/bwrsandman/imgui-flame-graph/stargazers) [![DOI](https://zenodo.org/badge/216259198.svg)](https://zenodo.org/badge/latestdoi/216259198) A Dear ImGui Widget for displaying Flame Graphs. ![flame-graph](https://user-images.githubusercontent.com/1013356/67159752-54a87880-f349-11e9-9df5-628594b3a745.gif) ### Example Running the `imgui-flame-graph-example` example will display an example window showcasing the flame graph widget. The code is available for reference in `example/main.cpp`. ![imgui-widget-flame-graph](https://user-images.githubusercontent.com/1013356/67151773-d907e680-f2ca-11e9-87dc-c1842cb0f796.png) ================================================ FILE: example/CMakeLists.txt ================================================ include(FetchContent) FetchContent_Declare( imgui GIT_REPOSITORY https://github.com/ocornut/imgui.git GIT_TAG master ) FetchContent_MakeAvailable(imgui) FetchContent_Declare( SDL2 URL https://www.libsdl.org/release/SDL2-2.26.5.tar.gz URL_HASH MD5=47f22c109070431ecccd90abd3c0ab6e ) FetchContent_MakeAvailable(SDL2) FetchContent_Declare( gl3w GIT_REPOSITORY https://github.com/bwrsandman/gl3w.git GIT_TAG master ) FetchContent_MakeAvailable(gl3w) file(GLOB EXTERNAL_SOURCE ${imgui_SOURCE_DIR}/*.cpp ${imgui_SOURCE_DIR}/backends/imgui_impl_sdl2.cpp ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp ) add_executable(imgui-flame-graph-example main.cpp ${EXTERNAL_SOURCE}) target_include_directories(imgui-flame-graph-example PRIVATE ${imgui_SOURCE_DIR} ${imgui_SOURCE_DIR}/backends ${imgui_SOURCE_DIR}/examples ${gl3w_BINARY_DIR}/include ) target_link_libraries(imgui-flame-graph-example PRIVATE gl3w SDL2 imgui-flame-graph ${CMAKE_DL_LIBS}) target_compile_definitions(imgui-flame-graph-example PRIVATE IMGUI_IMPL_OPENGL_LOADER_GL3W) target_compile_features(imgui-flame-graph-example PRIVATE cxx_constexpr) ================================================ FILE: example/main.cpp ================================================ // Dear ImGui: standalone example application for SDL2 + OpenGL // (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs #include "imgui.h" #include "imgui_impl_sdl2.h" #include "imgui_impl_opengl3.h" #include #define SDL_MAIN_HANDLED #include #if defined(IMGUI_IMPL_OPENGL_ES2) #include #else #include #endif #include #include #include class Profiler { public: enum Stage { SdlInput, Plot, NewFrame, DemoWindow, SampleWindow, AnotherWindow, ProfilerWindow, ImGuiRender, OpenGL, ImGuiRenderOpenGL, SwapWindow, Rendering, _StageCount, }; struct Scope { ImU8 _level; std::chrono::system_clock::time_point _start; std::chrono::system_clock::time_point _end; bool _finalized = false; }; struct Entry { std::chrono::system_clock::time_point _frameStart; std::chrono::system_clock::time_point _frameEnd; std::array _stages; }; void Frame() { auto& prevEntry = _entries[_currentEntry]; _currentEntry = (_currentEntry + 1) % _bufferSize; prevEntry._frameEnd = _entries[_currentEntry]._frameStart = std::chrono::system_clock::now(); } void Begin(Stage stage) { assert(_currentLevel < 255); auto& entry = _entries[_currentEntry]._stages[stage]; entry._level = _currentLevel; _currentLevel++; entry._start = std::chrono::system_clock::now(); entry._finalized = false; } void End(Stage stage) { assert(_currentLevel > 0); auto& entry = _entries[_currentEntry]._stages[stage]; assert(!entry._finalized); _currentLevel--; assert(entry._level == _currentLevel); entry._end = std::chrono::system_clock::now(); entry._finalized = true; } ImU8 GetCurrentEntryIndex() const { return (_currentEntry + _bufferSize - 1) % _bufferSize; } static const ImU8 _bufferSize = 100; std::array _entries; private: ImU8 _currentEntry = _bufferSize - 1; ImU8 _currentLevel = 0; }; static const std::array stageNames = { "SDL Input", "Plot", "New Frame", "Demo Window", "Sample Window", "Another Window", "Profiler Window", "ImGui::Render", "OpenGL", "ImGui_ImplOpenGL3_RenderDrawData", "SDL_GL_SwapWindow", "Rendering", }; static void ProfilerValueGetter(float* startTimestamp, float* endTimestamp, ImU8* level, const char** caption, const void* data, int idx) { auto entry = reinterpret_cast(data); auto& stage = entry->_stages[idx]; if (startTimestamp) { std::chrono::duration fltStart = stage._start - entry->_frameStart; *startTimestamp = fltStart.count(); } if (endTimestamp) { *endTimestamp = stage._end.time_since_epoch().count() / 1000000.0f; std::chrono::duration fltEnd = stage._end - entry->_frameStart; *endTimestamp = fltEnd.count(); } if (level) { *level = stage._level; } if (caption) { *caption = stageNames[idx]; } } // Main code int main(int, char**) { // Setup SDL // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!) 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 // 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); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); SDL_GLContext gl_context = SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SetSwapInterval(1); // Enable vsync // 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::StyleColorsClassic(); // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'docs/FONTS.md' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); //IM_ASSERT(font != NULL); // Our state bool show_demo_window = true; bool show_another_window = false; bool show_profiler_window = true; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop bool done = false; bool firstFrame = true; Profiler profiler; while (!done) { profiler.Frame(); // 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. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. SDL_Event event; profiler.Begin(Profiler::Stage::SdlInput); while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; } profiler.End(Profiler::Stage::SdlInput); // Start the Dear ImGui frame profiler.Begin(Profiler::Stage::Plot); profiler.Begin(Profiler::Stage::NewFrame); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); profiler.End(Profiler::Stage::NewFrame); // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). profiler.Begin(Profiler::Stage::DemoWindow); if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); profiler.End(Profiler::Stage::DemoWindow); // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. profiler.Begin(Profiler::Stage::SampleWindow); { static float f = 0.0f; static int counter = 0; ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) counter++; ImGui::SameLine(); ImGui::Text("counter = %d", counter); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::End(); } profiler.End(Profiler::Stage::SampleWindow); // 3. Show another simple window. profiler.Begin(Profiler::Stage::AnotherWindow); 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(); } profiler.End(Profiler::Stage::AnotherWindow); // 4. Show Profiler profiler.Begin(Profiler::Stage::ProfilerWindow); if (show_profiler_window && !firstFrame) { ImGui::Begin("Profiler Window", &show_profiler_window); auto& entry = profiler._entries[profiler.GetCurrentEntryIndex()]; ImGuiWidgetFlameGraph::PlotFlame("CPU", &ProfilerValueGetter, &entry, Profiler::_StageCount, 0, "Main Thread", FLT_MAX, FLT_MAX, ImVec2(400, 0)); ImGui::End(); } profiler.End(Profiler::Stage::ProfilerWindow); profiler.End(Profiler::Stage::Plot); // Rendering profiler.Begin(Profiler::Stage::Rendering); profiler.Begin(Profiler::Stage::ImGuiRender); ImGui::Render(); profiler.Begin(Profiler::Stage::OpenGL); 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); profiler.Begin(Profiler::Stage::ImGuiRenderOpenGL); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); profiler.End(Profiler::Stage::ImGuiRenderOpenGL); profiler.End(Profiler::Stage::OpenGL); profiler.End(Profiler::Stage::ImGuiRender); profiler.Begin(Profiler::Stage::SwapWindow); SDL_GL_SwapWindow(window); profiler.End(Profiler::Stage::SwapWindow); profiler.End(Profiler::Stage::Rendering); firstFrame = false; } // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); SDL_GL_DeleteContext(gl_context); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ================================================ FILE: imgui_widget_flamegraph.cpp ================================================ // The MIT License(MIT) // // Copyright(c) 2019 Sandy Carter // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include "imgui.h" #include "imgui_internal.h" void ImGuiWidgetFlameGraph::PlotFlame(const char* label, void (*values_getter)(float* start, float* end, ImU8* level, const char** caption, const void* data, int idx), const void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) { ImGuiWindow* window = ImGui::GetCurrentWindow(); if (window->SkipItems) return; ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; // Find the maximum depth ImU8 maxDepth = 0; for (int i = values_offset; i < values_count; ++i) { ImU8 depth; values_getter(nullptr, nullptr, &depth, nullptr, data, i); maxDepth = ImMax(maxDepth, depth); } const auto blockHeight = ImGui::GetTextLineHeight() + (style.FramePadding.y * 2); const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); if (graph_size.x == 0.0f) graph_size.x = ImGui::CalcItemWidth(); if (graph_size.y == 0.0f) graph_size.y = label_size.y + (style.FramePadding.y * 3) + blockHeight * (maxDepth + 1); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + graph_size); const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); ImGui::ItemSize(total_bb, style.FramePadding.y); if (!ImGui::ItemAdd(total_bb, 0, &frame_bb)) return; // Determine scale from values if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) { float v_min = FLT_MAX; float v_max = -FLT_MAX; for (int i = values_offset; i < values_count; i++) { float v_start, v_end; values_getter(&v_start, &v_end, nullptr, nullptr, data, i); if (v_start == v_start) // Check non-NaN values v_min = ImMin(v_min, v_start); if (v_end == v_end) // Check non-NaN values v_max = ImMax(v_max, v_end); } if (scale_min == FLT_MAX) scale_min = v_min; if (scale_max == FLT_MAX) scale_max = v_max; } ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); bool any_hovered = false; if (values_count - values_offset >= 1) { const ImU32 col_base = ImGui::GetColorU32(ImGuiCol_PlotHistogram) & 0x77FFFFFF; const ImU32 col_hovered = ImGui::GetColorU32(ImGuiCol_PlotHistogramHovered) & 0x77FFFFFF; const ImU32 col_outline_base = ImGui::GetColorU32(ImGuiCol_PlotHistogram) & 0x7FFFFFFF; const ImU32 col_outline_hovered = ImGui::GetColorU32(ImGuiCol_PlotHistogramHovered) & 0x7FFFFFFF; for (int i = values_offset; i < values_count; ++i) { float stageStart, stageEnd; ImU8 depth; const char* caption; values_getter(&stageStart, &stageEnd, &depth, &caption, data, i); auto duration = scale_max - scale_min; if (duration == 0) { return; } auto start = stageStart - scale_min; auto end = stageEnd - scale_min; auto startX = static_cast(start / (double)duration); auto endX = static_cast(end / (double)duration); float width = inner_bb.Max.x - inner_bb.Min.x; float height = blockHeight * (maxDepth - depth + 1) - style.FramePadding.y; auto pos0 = inner_bb.Min + ImVec2(startX * width, height); auto pos1 = inner_bb.Min + ImVec2(endX * width, height + blockHeight); bool v_hovered = false; if (ImGui::IsMouseHoveringRect(pos0, pos1)) { ImGui::SetTooltip("%s: %8.4g", caption, stageEnd - stageStart); v_hovered = true; any_hovered = v_hovered; } window->DrawList->AddRectFilled(pos0, pos1, v_hovered ? col_hovered : col_base); window->DrawList->AddRect(pos0, pos1, v_hovered ? col_outline_hovered : col_outline_base); auto textSize = ImGui::CalcTextSize(caption); auto boxSize = (pos1 - pos0); auto textOffset = ImVec2(0.0f, 0.0f); if (textSize.x < boxSize.x) { textOffset = ImVec2(0.5f, 0.5f) * (boxSize - textSize); ImGui::RenderText(pos0 + textOffset, caption); } } // Text overlay if (overlay_text) ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f)); if (label_size.x > 0.0f) ImGui::RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); } if (!any_hovered && ImGui::IsItemHovered()) { ImGui::SetTooltip("Total: %8.4g", scale_max - scale_min); } } ================================================ FILE: imgui_widget_flamegraph.h ================================================ // https://github.com/bwrsandman/imgui-flame-graph // v 1.00 // // The MIT License(MIT) // // Copyright(c) 2019 Sandy Carter // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // History : // 2019-10-22 First version #pragma once #include #include namespace ImGuiWidgetFlameGraph { IMGUI_API void PlotFlame(const char* label, void (*values_getter)(float* start, float* end, ImU8* level, const char** caption, const void* data, int idx), const void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); }