From ead05a20a7d7e7ca2d6f49c6c4e0d48161d03327 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Sun, 20 Jun 2021 19:26:46 +0200 Subject: [PATCH 01/13] Add GPU-accelerated heatmaps --- backends/implot_opengl3.cpp | 294 ++++++++++++++++++++++++++++++++++++ implot_internal.h | 17 +++ implot_items.cpp | 35 ++++- 3 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 backends/implot_opengl3.cpp diff --git a/backends/implot_opengl3.cpp b/backends/implot_opengl3.cpp new file mode 100644 index 0000000..c9c74f6 --- /dev/null +++ b/backends/implot_opengl3.cpp @@ -0,0 +1,294 @@ +#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION + +#include "../implot.h" +#include "../implot_internal.h" + +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include +// About Desktop OpenGL function loaders: +// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) +#include // Initialize with gl3wInit() +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) +#include // Initialize with glewInit() +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) +#include // Initialize with gladLoadGL() +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) +#include // Initialize with gladLoadGL(...) or gladLoaderLoadGL() +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) +#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. +#include // Initialize with glbinding::Binding::initialize() +#include +using namespace gl; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) +#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. +#include // Initialize with glbinding::initialize() +#include +using namespace gl; +#else +#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM +#endif + +namespace ImPlot { +namespace Backends { + +struct HeatmapData +{ + GLuint HeatmapTexID; + GLuint ColormapTexID; + float MinValue; + float MaxValue; +}; + +struct OpenGLContextData +{ + GLuint g_ShaderProgram = 0; ///< Shader ID for the heatmap shader + GLuint g_AttribLocationHeatmapSampler = 0; ///< Attribute location for the heatmap texture + GLuint g_AttribLocationColormapSampler = 0; ///< Attribute location for the colormap texture + GLuint g_AttribLocationProjection = 0; ///< Attribute location for the projection matrix uniform + GLuint g_AttribLocationMinValue = 0; ///< Attribute location for the minimum value uniform + GLuint g_AttribLocationMaxValue = 0; ///< Attribute location for the maximum value uniform + + GLuint g_AttribLocationImGuiProjection = 0; ///< Attribute location for the projection matrix uniform (ImGui default shader) + + ImVector HeatmapDataList; ///< Array of heatmap data + ImGuiStorage PlotIDs; ///< PlotID <-> Heatmap array index table +}; + +static OpenGLContextData Context; + +static void CreateShader(const ImDrawList*, const ImDrawCmd*) +{ + GLuint CurrentShader; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&CurrentShader); + + Context.g_AttribLocationImGuiProjection = glGetUniformLocation(CurrentShader, "ProjMtx"); + + GLuint g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(CurrentShader, "Position"); + GLuint g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(CurrentShader, "UV"); + GLuint g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(CurrentShader, "Color"); + + const GLchar* VertexShaderCode_t = + "#version 330 core\n" + "precision mediump float;\n" + "layout (location = %d) in vec2 Position;\n" + "layout (location = %d) in vec2 UV;\n" + "layout (location = %d) in vec4 Color;\n" + "\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0.0f, 1.0f);\n" + "}\n"; + + const GLchar* FragmentShaderCode = + "#version 330 core\n" + "precision mediump float;\n" + "\n" + "in vec2 Frag_UV;\n" + "out vec4 Out_Color;\n" + "\n" + "uniform sampler1D colormap;\n" + "uniform sampler2D heatmap;\n" + "uniform float min_val;\n" + "uniform float max_val;\n" + "\n" + "void main()\n" + "{\n" + " float offset = (texture(heatmap, Frag_UV).r - min_val) / (max_val - min_val);\n" + " Out_Color = texture(colormap, clamp(offset, 0.0f, 1.0f));\n" + "}\n"; + + GLchar* VertexShaderCode = new GLchar[512]; + snprintf(VertexShaderCode, 512, VertexShaderCode_t, g_AttribLocationVtxPos, g_AttribLocationVtxUV, g_AttribLocationVtxColor); + + GLuint g_VertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(g_VertexShader, 1, &VertexShaderCode, nullptr); + glCompileShader(g_VertexShader); + + GLuint g_FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(g_FragmentShader, 1, &FragmentShaderCode, nullptr); + glCompileShader(g_FragmentShader); + + Context.g_ShaderProgram = glCreateProgram(); + glAttachShader(Context.g_ShaderProgram, g_VertexShader); + glAttachShader(Context.g_ShaderProgram, g_FragmentShader); + glLinkProgram(Context.g_ShaderProgram); + + glDetachShader(Context.g_ShaderProgram, g_VertexShader); + glDetachShader(Context.g_ShaderProgram, g_FragmentShader); + glDeleteShader(g_VertexShader); + glDeleteShader(g_FragmentShader); + + Context.g_AttribLocationHeatmapSampler = glGetUniformLocation(Context.g_ShaderProgram, "heatmap"); + Context.g_AttribLocationColormapSampler = glGetUniformLocation(Context.g_ShaderProgram, "colormap"); + Context.g_AttribLocationProjection = glGetUniformLocation(Context.g_ShaderProgram, "ProjMtx"); + Context.g_AttribLocationMinValue = glGetUniformLocation(Context.g_ShaderProgram, "min_val"); + Context.g_AttribLocationMaxValue = glGetUniformLocation(Context.g_ShaderProgram, "max_val"); + + glUseProgram(Context.g_ShaderProgram); + glUniform1i(Context.g_AttribLocationHeatmapSampler, 0); // Set texture slot of heatmap texture + glUniform1i(Context.g_AttribLocationColormapSampler, 1); // Set texture slot of colormap texture + + delete[] VertexShaderCode; +} + +static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd) +{ + int plotID = (int)(intptr_t)cmd->UserCallbackData; + int plotIdx = Context.PlotIDs.GetInt(plotID, -1); + HeatmapData& data = Context.HeatmapDataList[plotIdx]; + + GLuint CurrentShader; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&CurrentShader); + + // Get projection matrix of current shader + float OrthoProjection[4][4]; + glGetUniformfv(CurrentShader, Context.g_AttribLocationImGuiProjection, &OrthoProjection[0][0]); + + // Enable our shader + glUseProgram(Context.g_ShaderProgram); + + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, data.HeatmapTexID); // Set texture ID of data + glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_1D, data.ColormapTexID); // Set texture ID of colormap + + glUniformMatrix4fv(Context.g_AttribLocationProjection, 1, GL_FALSE, &OrthoProjection[0][0]); + glUniform1f(Context.g_AttribLocationMinValue, data.MinValue); // Set minimum range + glUniform1f(Context.g_AttribLocationMaxValue, data.MaxValue); // Set maximum range +} + +static void UnbindTexture(const ImDrawList*, const ImDrawCmd*) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); +} + +static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei cols, GLenum type) +{ + int idx = Context.PlotIDs.GetInt(plotID, -1); + GLuint texID = Context.HeatmapDataList[idx].HeatmapTexID; + + // Set heatmap data + glBindTexture(GL_TEXTURE_2D, texID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cols, rows, 0, GL_RED, type, data); +} + +void OpenGL3_AddColormap(int* texID, const ImU32* keys, int count, bool qual) +{ + GLuint* colormapID = (GLuint*)texID; + glGenTextures(1, colormapID); + glBindTexture(GL_TEXTURE_1D, *colormapID); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, count, 0, GL_RGBA, GL_UNSIGNED_BYTE, keys); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, qual ? GL_NEAREST : GL_LINEAR); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, qual ? GL_NEAREST : GL_LINEAR); + glBindTexture(GL_TEXTURE_1D, 0); +} + +static GLuint CreateHeatmapTexture() +{ + GLuint textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + return textureID; +} + +void OpenGL3_SetHeatmapData(int plotID, const ImS8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_BYTE); } +void OpenGL3_SetHeatmapData(int plotID, const ImU8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_BYTE); } +void OpenGL3_SetHeatmapData(int plotID, const ImS16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_SHORT); } +void OpenGL3_SetHeatmapData(int plotID, const ImU16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_SHORT); } +void OpenGL3_SetHeatmapData(int plotID, const ImS32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_INT); } +void OpenGL3_SetHeatmapData(int plotID, const ImU32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_INT); } +void OpenGL3_SetHeatmapData(int plotID, const float* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_FLOAT); } + +void OpenGL3_SetHeatmapData(int plotID, const double* values, int rows, int cols) +{ + float* values2 = new float[rows*cols]; + + for(int i = 0; i < rows*cols; i++) + values2[i] = (float)values[i]; + + SetTextureData(plotID, values2, rows, cols, GL_FLOAT); + + delete[] values2; +} + +void OpenGL3_SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) +{ + ImS32* values2 = new ImS32[rows*cols]; + + for(int i = 0; i < rows*cols; i++) + values2[i] = (ImS32)values[i]; + + SetTextureData(plotID, values2, rows, cols, GL_INT); + + delete[] values2; +} + +void OpenGL3_SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) +{ + ImU32* values2 = new ImU32[rows*cols]; + + for(int i = 0; i < rows*cols; i++) + values2[i] = (ImU32)values[i]; + + SetTextureData(plotID, values2, rows, cols, GL_UNSIGNED_INT); + + delete[] values2; +} + +void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) +{ + ImPlotContext& gp = *GImPlot; + int idx = Context.PlotIDs.GetInt(plotID, -1); + + if(idx < 0) + { + // New entry + HeatmapData data; + data.HeatmapTexID = CreateHeatmapTexture(); + data.ColormapTexID = gp.ColormapData.TextureIDs[colormap]; + data.MinValue = scale_min; + data.MaxValue = scale_max; + + Context.PlotIDs.SetInt(plotID, Context.HeatmapDataList.Size); + Context.HeatmapDataList.push_back(data); + } + else + { + HeatmapData& data = Context.HeatmapDataList[idx]; + data.ColormapTexID = gp.ColormapData.TextureIDs[colormap]; + data.MinValue = scale_min; + data.MaxValue = scale_max; + } + + if(Context.g_ShaderProgram == 0) + DrawList.AddCallback(CreateShader, nullptr); + + DrawList.AddCallback(RenderCallback, (void*)(intptr_t)plotID); + DrawList.PrimReserve(6, 4); + DrawList.PrimRectUV(bounds_min, bounds_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), 0); + DrawList.AddCallback(UnbindTexture, nullptr); + DrawList.AddCallback(ImDrawCallback_ResetRenderState, nullptr); +} + +void OpenGL3_BustPlotCache() +{ + Context.HeatmapDataList.clear(); + Context.PlotIDs.Clear(); +} + +} +} + +#endif diff --git a/implot_internal.h b/implot_internal.h index 994331c..57c7512 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -42,6 +42,16 @@ #error Must include implot.h before implot_internal.h #endif +#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION +namespace ImPlot { +namespace Backends { + +void OpenGL3_AddColormap(int* texID, const ImU32* keys, int count, bool qual); + +} +} +#endif + //----------------------------------------------------------------------------- // [SECTION] Constants //----------------------------------------------------------------------------- @@ -358,6 +368,9 @@ struct ImPlotColormapData { ImVector Quals; ImGuiStorage Map; int Count; +#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION + ImVector TextureIDs; +#endif ImPlotColormapData() { Count = 0; } @@ -376,6 +389,10 @@ struct ImPlotColormapData { int idx = Count++; Map.SetInt(id,idx); _AppendTable(idx); +#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION + TextureIDs.push_back(0); + ImPlot::Backends::OpenGL3_AddColormap(&TextureIDs[idx], keys, count, qual); +#endif return idx; } diff --git a/implot_items.cpp b/implot_items.cpp index 332cf12..2483566 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -53,6 +53,26 @@ #endif +#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION +namespace ImPlot { +namespace Backends { + void OpenGL3_SetHeatmapData(int texID, const ImS8* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const ImU8* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const ImS16* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const ImU16* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const ImS32* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const ImU32* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const ImS64* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const ImU64* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const float* values, int rows, int cols); + void OpenGL3_SetHeatmapData(int texID, const double* values, int rows, int cols); + + void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap); +} +} +#endif + + namespace ImPlot { //----------------------------------------------------------------------------- @@ -1890,13 +1910,22 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value } const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; +#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION + // NOTE: Order is important! + ImVec2 bmin = transformer(bounds_min); + ImVec2 bmax = transformer(bounds_max); + + Backends::OpenGL3_RenderHeatmap(gp.CurrentPlot->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap); + Backends::OpenGL3_SetHeatmapData(gp.CurrentPlot->ID, values, rows, cols); +#else GetterHeatmap getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); switch (GetCurrentScale()) { case ImPlotScale_LinLin: RenderPrimitives(RectRenderer, TransformerLinLin>(getter, TransformerLinLin()), DrawList, gp.CurrentPlot->PlotRect); break; - case ImPlotScale_LogLin: RenderPrimitives(RectRenderer, TransformerLogLin>(getter, TransformerLogLin()), DrawList, gp.CurrentPlot->PlotRect); break;; - case ImPlotScale_LinLog: RenderPrimitives(RectRenderer, TransformerLinLog>(getter, TransformerLinLog()), DrawList, gp.CurrentPlot->PlotRect); break;; - case ImPlotScale_LogLog: RenderPrimitives(RectRenderer, TransformerLogLog>(getter, TransformerLogLog()), DrawList, gp.CurrentPlot->PlotRect); break;; + case ImPlotScale_LogLin: RenderPrimitives(RectRenderer, TransformerLogLin>(getter, TransformerLogLin()), DrawList, gp.CurrentPlot->PlotRect); break; + case ImPlotScale_LinLog: RenderPrimitives(RectRenderer, TransformerLinLog>(getter, TransformerLinLog()), DrawList, gp.CurrentPlot->PlotRect); break; + case ImPlotScale_LogLog: RenderPrimitives(RectRenderer, TransformerLogLog>(getter, TransformerLogLog()), DrawList, gp.CurrentPlot->PlotRect); break; } +#endif if (fmt != NULL) { const double w = (bounds_max.x - bounds_min.x) / cols; const double h = (bounds_max.y - bounds_min.y) / rows; From 7b840d1a44c83b99f1edf99e66bc5badbbcb31f6 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Sun, 20 Jun 2021 21:19:48 +0200 Subject: [PATCH 02/13] Add BustPlotCache() and other small changes --- backends/implot_opengl3.cpp | 19 ++++++++++++------- implot.cpp | 14 +++++++++++++- implot_internal.h | 8 ++------ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/backends/implot_opengl3.cpp b/backends/implot_opengl3.cpp index c9c74f6..59e69ad 100644 --- a/backends/implot_opengl3.cpp +++ b/backends/implot_opengl3.cpp @@ -54,6 +54,7 @@ struct OpenGLContextData GLuint g_AttribLocationImGuiProjection = 0; ///< Attribute location for the projection matrix uniform (ImGui default shader) ImVector HeatmapDataList; ///< Array of heatmap data + ImVector ColormapIDs; ///< Texture IDs of the colormap textures ImGuiStorage PlotIDs; ///< PlotID <-> Heatmap array index table }; @@ -178,17 +179,19 @@ static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei c glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cols, rows, 0, GL_RED, type, data); } -void OpenGL3_AddColormap(int* texID, const ImU32* keys, int count, bool qual) +void OpenGL3_AddColormap(const ImU32* keys, int count, bool qual) { - GLuint* colormapID = (GLuint*)texID; - glGenTextures(1, colormapID); - glBindTexture(GL_TEXTURE_1D, *colormapID); + GLuint texID; + glGenTextures(1, &texID); + glBindTexture(GL_TEXTURE_1D, texID); glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, count, 0, GL_RGBA, GL_UNSIGNED_BYTE, keys); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, qual ? GL_NEAREST : GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, qual ? GL_NEAREST : GL_LINEAR); glBindTexture(GL_TEXTURE_1D, 0); + + Context.ColormapIDs.push_back(texID); } static GLuint CreateHeatmapTexture() @@ -249,7 +252,6 @@ void OpenGL3_SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) { - ImPlotContext& gp = *GImPlot; int idx = Context.PlotIDs.GetInt(plotID, -1); if(idx < 0) @@ -257,7 +259,7 @@ void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bound // New entry HeatmapData data; data.HeatmapTexID = CreateHeatmapTexture(); - data.ColormapTexID = gp.ColormapData.TextureIDs[colormap]; + data.ColormapTexID = Context.ColormapIDs[colormap]; data.MinValue = scale_min; data.MaxValue = scale_max; @@ -267,7 +269,7 @@ void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bound else { HeatmapData& data = Context.HeatmapDataList[idx]; - data.ColormapTexID = gp.ColormapData.TextureIDs[colormap]; + data.ColormapTexID = Context.ColormapIDs[colormap]; data.MinValue = scale_min; data.MaxValue = scale_max; } @@ -284,6 +286,9 @@ void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bound void OpenGL3_BustPlotCache() { + for(const HeatmapData& data : Context.HeatmapDataList) + glDeleteTextures(1, &data.HeatmapTexID); + Context.HeatmapDataList.clear(); Context.PlotIDs.Clear(); } diff --git a/implot.cpp b/implot.cpp index 837edee..e3374f7 100644 --- a/implot.cpp +++ b/implot.cpp @@ -88,6 +88,16 @@ You can read releases logs https://github.com/epezent/implot/releases for more d #define GetBufSize GetSize // A little bit ugly since 'GetBufSize' could technically be used elsewhere (but currently isn't). Could use a proxy define if needed. #endif +#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION +namespace ImPlot { +namespace Backends { + +void OpenGL3_BustPlotCache(); + +} +} +#endif + // Global plot context ImPlotContext* GImPlot = NULL; @@ -435,7 +445,6 @@ void Initialize(ImPlotContext* ctx) { IMPLOT_APPEND_CMAP(PiYG, false); IMPLOT_APPEND_CMAP(Spectral, false); IMPLOT_APPEND_CMAP(Greys, false); - } void Reset(ImPlotContext* ctx) { @@ -489,6 +498,9 @@ ImPlotPlot* GetCurrentPlot() { void BustPlotCache() { GImPlot->Plots.Clear(); +#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION + Backends::OpenGL3_BustPlotCache(); +#endif } void PushLinkedAxis(ImPlotAxis& axis) { diff --git a/implot_internal.h b/implot_internal.h index 57c7512..cc2f4b6 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -46,7 +46,7 @@ namespace ImPlot { namespace Backends { -void OpenGL3_AddColormap(int* texID, const ImU32* keys, int count, bool qual); +void OpenGL3_AddColormap(const ImU32* keys, int count, bool qual); } } @@ -368,9 +368,6 @@ struct ImPlotColormapData { ImVector Quals; ImGuiStorage Map; int Count; -#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION - ImVector TextureIDs; -#endif ImPlotColormapData() { Count = 0; } @@ -390,8 +387,7 @@ struct ImPlotColormapData { Map.SetInt(id,idx); _AppendTable(idx); #ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION - TextureIDs.push_back(0); - ImPlot::Backends::OpenGL3_AddColormap(&TextureIDs[idx], keys, count, qual); + ImPlot::Backends::OpenGL3_AddColormap(keys, count, qual); #endif return idx; } From 2816b0cbd92b2b48cd4eefe3133e7008d2c38f56 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Mon, 21 Jun 2021 10:26:46 +0200 Subject: [PATCH 03/13] Fixupside-down heatmaps --- backends/implot_opengl3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/implot_opengl3.cpp b/backends/implot_opengl3.cpp index 59e69ad..ad7b3f9 100644 --- a/backends/implot_opengl3.cpp +++ b/backends/implot_opengl3.cpp @@ -279,7 +279,7 @@ void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bound DrawList.AddCallback(RenderCallback, (void*)(intptr_t)plotID); DrawList.PrimReserve(6, 4); - DrawList.PrimRectUV(bounds_min, bounds_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), 0); + DrawList.PrimRectUV(bounds_min, bounds_max, ImVec2(0.0f, 1.0f), ImVec2(1.0f, 0.0f), 0); DrawList.AddCallback(UnbindTexture, nullptr); DrawList.AddCallback(ImDrawCallback_ResetRenderState, nullptr); } From aa716726da99bde5cb6af16a38e5af104c18eb14 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Mon, 21 Jun 2021 17:32:11 +0200 Subject: [PATCH 04/13] Prevent new/delete on each call to PlotHeatmap() on certain cases --- backends/implot_opengl3.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/backends/implot_opengl3.cpp b/backends/implot_opengl3.cpp index ad7b3f9..be70f1b 100644 --- a/backends/implot_opengl3.cpp +++ b/backends/implot_opengl3.cpp @@ -56,6 +56,10 @@ struct OpenGLContextData ImVector HeatmapDataList; ///< Array of heatmap data ImVector ColormapIDs; ///< Texture IDs of the colormap textures ImGuiStorage PlotIDs; ///< PlotID <-> Heatmap array index table + + ImVector temp1; + ImVector temp2; + ImVector temp3; }; static OpenGLContextData Context; @@ -216,38 +220,35 @@ void OpenGL3_SetHeatmapData(int plotID, const float* values, int rows, int cols) void OpenGL3_SetHeatmapData(int plotID, const double* values, int rows, int cols) { - float* values2 = new float[rows*cols]; + if(Context.temp1.Size < rows * cols) + Context.temp1.resize(rows * cols); for(int i = 0; i < rows*cols; i++) - values2[i] = (float)values[i]; + Context.temp1[i] = (float)values[i]; - SetTextureData(plotID, values2, rows, cols, GL_FLOAT); - - delete[] values2; + SetTextureData(plotID, Context.temp1.Data, rows, cols, GL_FLOAT); } void OpenGL3_SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) { - ImS32* values2 = new ImS32[rows*cols]; + if(Context.temp2.Size < rows * cols) + Context.temp2.resize(rows * cols); for(int i = 0; i < rows*cols; i++) - values2[i] = (ImS32)values[i]; + Context.temp2[i] = (ImS32)values[i]; - SetTextureData(plotID, values2, rows, cols, GL_INT); - - delete[] values2; + SetTextureData(plotID, Context.temp2.Data, rows, cols, GL_INT); } void OpenGL3_SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) { - ImU32* values2 = new ImU32[rows*cols]; + if(Context.temp3.Size < rows * cols) + Context.temp3.resize(rows * cols); for(int i = 0; i < rows*cols; i++) - values2[i] = (ImU32)values[i]; + Context.temp3[i] = (ImU32)values[i]; - SetTextureData(plotID, values2, rows, cols, GL_UNSIGNED_INT); - - delete[] values2; + SetTextureData(plotID, Context.temp3.Data, rows, cols, GL_UNSIGNED_INT); } void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) From 718351c6f57c486ab8c6b0d5dcce99b5520f23a7 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Tue, 22 Jun 2021 17:07:56 +0200 Subject: [PATCH 05/13] Add common interface for backends & move context to ImPlotContext --- backends/implot_gpu.h | 154 ++++++++++++++++++++++++++++++++++++ backends/implot_opengl3.cpp | 72 ++++++++++++----- implot.cpp | 18 +---- implot_internal.h | 18 ++--- implot_items.cpp | 26 +----- 5 files changed, 220 insertions(+), 68 deletions(-) create mode 100644 backends/implot_gpu.h diff --git a/backends/implot_gpu.h b/backends/implot_gpu.h new file mode 100644 index 0000000..e5d9477 --- /dev/null +++ b/backends/implot_gpu.h @@ -0,0 +1,154 @@ +// MIT License + +// Copyright (c) 2021 Evan Pezent + +// 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. + +// ImPlot v0.10 WIP + +#pragma once + +#include "../implot.h" + +namespace ImPlot { +namespace Backend { + +/** + * @brief Struct to hold backend-related context data + * + * A backend may store in this struct any data it needs, with no constraints. A + * pointer to this struct will be stored inside ImPlot's context and can be + * accessed at any time. This pointer will be set to the returned value of @ref + * CreateContext(). All resources held by this struct must be freed inside @ref + * DestroyContext(). + */ +struct ContextData; + +/** + * @brief Create backend context + * + * Creates and intializes the backend context. The returned pointer will be saved + * in ImPlot's context and can be accessed later. + */ +IMPLOT_API void* CreateContext(); + +/** + * @brief Destroy backend context + * + * Destroys and frees any memory or resources needed by the backend. After this + * call returns, no more calls to any backend function will be performed. + */ +IMPLOT_API void DestroyContext(); + +/** + * @brief Add a colormap + * + * Adds a colormap to be handled by the backend. + * + * @param keys Colors for this colormap, in RGBA format + * @param count Number of colors in this colormap + * @param qual Qualitative: whether the colormap is continuous (`false`) or + * not (`true`) + */ +IMPLOT_API void AddColormap(const ImU32* keys, int count, bool qual); + +/** + * @brief Set heatmap data + * + * Sets the data of the heatmap with the given plot ID. + * + * @param plotID ID of the heatmap to update. This ID is unique, but it is not + * continuous nor always positive. + * @param values Data of the heatmap to be set.`values[0]` corresponds with the + * top-left corner of the data. + * @param rows Number of rows of this heatmap + * @param cols Number of columns of this heatmap + */ +IMPLOT_API void SetHeatmapData(int plotID, const ImS8* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImU8*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const ImU8* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImS16*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const ImS16* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImU16*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const ImU16* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImS32*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const ImS32* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImU32*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const ImU32* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const float*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const float* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const double*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const double* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImS64*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const ImS64* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImU64*,int,int) */ +IMPLOT_API void SetHeatmapData(int plotID, const ImU64* values, int rows, int cols); + +/** + * @brief Render heatmap + * + * Renders the heatmap by submitting the appropriate commands to the current + * draw list. + * + * @param plotID ID of the heatmap to be rendered + * @param DrawList Draw list where to submit the render commands + * @param bounds_min Minimum bounds of the heatmap (without clipping) + * @param bounds_max Maximum bounds of the heatmap (without clipping) + * @param scale_min Minimum value of the heatmap + * @param scale_max Maximum value of the heatmap + * @param colormap Colormap to be used when rendering this heatmap + * + * @note There might be values greater than `scale_max` or lower than `scale_min`. + * The shader used for rendering should clamp this values appropriately. + */ +IMPLOT_API void RenderHeatmap( + int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, + float scale_min, float scale_max, ImPlotColormap colormap); + +/** @brief Bust plot cache. Called from @ref ImPlot::BustPlotCache() */ +IMPLOT_API void BustPlotCache(); + +/** @brief Bust item cache. Called from @ref ImPlot::BustItemCache() */ +IMPLOT_API void BustItemCache(); + + +#if !defined(IMPLOT_ENABLE_OPENGL3_ACCELERATION) + +// Dummy implementation for backend functions +inline void* CreateContext() { return nullptr; } +inline void DestroyContext() {} + +inline void AddColormap(const ImU32*, int, bool) {} + +inline void BustPlotCache() {} +inline void BustItemCache() {} + +#endif + +} +} diff --git a/backends/implot_opengl3.cpp b/backends/implot_opengl3.cpp index be70f1b..15ea30a 100644 --- a/backends/implot_opengl3.cpp +++ b/backends/implot_opengl3.cpp @@ -2,6 +2,7 @@ #include "../implot.h" #include "../implot_internal.h" +#include "implot_gpu.h" #if defined(IMGUI_IMPL_OPENGL_ES2) #include @@ -32,7 +33,7 @@ using namespace gl; #endif namespace ImPlot { -namespace Backends { +namespace Backend { struct HeatmapData { @@ -42,7 +43,7 @@ struct HeatmapData float MaxValue; }; -struct OpenGLContextData +struct ContextData { GLuint g_ShaderProgram = 0; ///< Shader ID for the heatmap shader GLuint g_AttribLocationHeatmapSampler = 0; ///< Attribute location for the heatmap texture @@ -62,10 +63,29 @@ struct OpenGLContextData ImVector temp3; }; -static OpenGLContextData Context; +void* CreateContext() +{ + return new ContextData; +} + +void DestroyContext() +{ + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + + for(const HeatmapData& data : Context.HeatmapDataList) + glDeleteTextures(1, &data.HeatmapTexID); + + for(GLuint texID : Context.ColormapIDs) + glDeleteTextures(1, &texID); + + Context.HeatmapDataList.clear(); + Context.PlotIDs.Clear(); +} static void CreateShader(const ImDrawList*, const ImDrawCmd*) { + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + GLuint CurrentShader; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&CurrentShader); @@ -73,14 +93,12 @@ static void CreateShader(const ImDrawList*, const ImDrawCmd*) GLuint g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(CurrentShader, "Position"); GLuint g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(CurrentShader, "UV"); - GLuint g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(CurrentShader, "Color"); const GLchar* VertexShaderCode_t = "#version 330 core\n" "precision mediump float;\n" "layout (location = %d) in vec2 Position;\n" "layout (location = %d) in vec2 UV;\n" - "layout (location = %d) in vec4 Color;\n" "\n" "uniform mat4 ProjMtx;\n" "out vec2 Frag_UV;\n" @@ -110,7 +128,7 @@ static void CreateShader(const ImDrawList*, const ImDrawCmd*) "}\n"; GLchar* VertexShaderCode = new GLchar[512]; - snprintf(VertexShaderCode, 512, VertexShaderCode_t, g_AttribLocationVtxPos, g_AttribLocationVtxUV, g_AttribLocationVtxColor); + snprintf(VertexShaderCode, 512, VertexShaderCode_t, g_AttribLocationVtxPos, g_AttribLocationVtxUV); GLuint g_VertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(g_VertexShader, 1, &VertexShaderCode, nullptr); @@ -145,6 +163,8 @@ static void CreateShader(const ImDrawList*, const ImDrawCmd*) static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd) { + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + int plotID = (int)(intptr_t)cmd->UserCallbackData; int plotIdx = Context.PlotIDs.GetInt(plotID, -1); HeatmapData& data = Context.HeatmapDataList[plotIdx]; @@ -175,6 +195,8 @@ static void UnbindTexture(const ImDrawList*, const ImDrawCmd*) static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei cols, GLenum type) { + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + int idx = Context.PlotIDs.GetInt(plotID, -1); GLuint texID = Context.HeatmapDataList[idx].HeatmapTexID; @@ -183,7 +205,7 @@ static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei c glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cols, rows, 0, GL_RED, type, data); } -void OpenGL3_AddColormap(const ImU32* keys, int count, bool qual) +void AddColormap(const ImU32* keys, int count, bool qual) { GLuint texID; glGenTextures(1, &texID); @@ -195,6 +217,7 @@ void OpenGL3_AddColormap(const ImU32* keys, int count, bool qual) glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, qual ? GL_NEAREST : GL_LINEAR); glBindTexture(GL_TEXTURE_1D, 0); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); // PETA AQUI (GImPlot es NULL) Context.ColormapIDs.push_back(texID); } @@ -210,16 +233,18 @@ static GLuint CreateHeatmapTexture() return textureID; } -void OpenGL3_SetHeatmapData(int plotID, const ImS8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_BYTE); } -void OpenGL3_SetHeatmapData(int plotID, const ImU8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_BYTE); } -void OpenGL3_SetHeatmapData(int plotID, const ImS16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_SHORT); } -void OpenGL3_SetHeatmapData(int plotID, const ImU16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_SHORT); } -void OpenGL3_SetHeatmapData(int plotID, const ImS32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_INT); } -void OpenGL3_SetHeatmapData(int plotID, const ImU32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_INT); } -void OpenGL3_SetHeatmapData(int plotID, const float* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_FLOAT); } +void SetHeatmapData(int plotID, const ImS8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_BYTE); } +void SetHeatmapData(int plotID, const ImU8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_BYTE); } +void SetHeatmapData(int plotID, const ImS16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_SHORT); } +void SetHeatmapData(int plotID, const ImU16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_SHORT); } +void SetHeatmapData(int plotID, const ImS32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_INT); } +void SetHeatmapData(int plotID, const ImU32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_INT); } +void SetHeatmapData(int plotID, const float* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_FLOAT); } -void OpenGL3_SetHeatmapData(int plotID, const double* values, int rows, int cols) +void SetHeatmapData(int plotID, const double* values, int rows, int cols) { + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + if(Context.temp1.Size < rows * cols) Context.temp1.resize(rows * cols); @@ -229,8 +254,10 @@ void OpenGL3_SetHeatmapData(int plotID, const double* values, int rows, int cols SetTextureData(plotID, Context.temp1.Data, rows, cols, GL_FLOAT); } -void OpenGL3_SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) +void SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) { + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + if(Context.temp2.Size < rows * cols) Context.temp2.resize(rows * cols); @@ -240,8 +267,10 @@ void OpenGL3_SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) SetTextureData(plotID, Context.temp2.Data, rows, cols, GL_INT); } -void OpenGL3_SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) +void SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) { + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + if(Context.temp3.Size < rows * cols) Context.temp3.resize(rows * cols); @@ -251,8 +280,9 @@ void OpenGL3_SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) SetTextureData(plotID, Context.temp3.Data, rows, cols, GL_UNSIGNED_INT); } -void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) +void RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) { + ContextData& Context = *((ContextData*)GImPlot->backendCtx); int idx = Context.PlotIDs.GetInt(plotID, -1); if(idx < 0) @@ -285,8 +315,10 @@ void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bound DrawList.AddCallback(ImDrawCallback_ResetRenderState, nullptr); } -void OpenGL3_BustPlotCache() +void BustPlotCache() { + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + for(const HeatmapData& data : Context.HeatmapDataList) glDeleteTextures(1, &data.HeatmapTexID); @@ -294,6 +326,8 @@ void OpenGL3_BustPlotCache() Context.PlotIDs.Clear(); } +void BustItemCache() {} + } } diff --git a/implot.cpp b/implot.cpp index e3374f7..862d945 100644 --- a/implot.cpp +++ b/implot.cpp @@ -88,16 +88,6 @@ You can read releases logs https://github.com/epezent/implot/releases for more d #define GetBufSize GetSize // A little bit ugly since 'GetBufSize' could technically be used elsewhere (but currently isn't). Could use a proxy define if needed. #endif -#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION -namespace ImPlot { -namespace Backends { - -void OpenGL3_BustPlotCache(); - -} -} -#endif - // Global plot context ImPlotContext* GImPlot = NULL; @@ -384,13 +374,15 @@ void SetImGuiContext(ImGuiContext* ctx) { ImPlotContext* CreateContext() { ImPlotContext* ctx = IM_NEW(ImPlotContext)(); - Initialize(ctx); if (GImPlot == NULL) SetCurrentContext(ctx); + ctx->backendCtx = Backend::CreateContext(); + Initialize(ctx); return ctx; } void DestroyContext(ImPlotContext* ctx) { + Backend::DestroyContext(); if (ctx == NULL) ctx = GImPlot; if (GImPlot == ctx) @@ -498,9 +490,7 @@ ImPlotPlot* GetCurrentPlot() { void BustPlotCache() { GImPlot->Plots.Clear(); -#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION - Backends::OpenGL3_BustPlotCache(); -#endif + Backend::BustPlotCache(); } void PushLinkedAxis(ImPlotAxis& axis) { diff --git a/implot_internal.h b/implot_internal.h index cc2f4b6..866148f 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -37,21 +37,12 @@ #include #include "imgui_internal.h" +#include "backends/implot_gpu.h" #ifndef IMPLOT_VERSION #error Must include implot.h before implot_internal.h #endif -#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION -namespace ImPlot { -namespace Backends { - -void OpenGL3_AddColormap(const ImU32* keys, int count, bool qual); - -} -} -#endif - //----------------------------------------------------------------------------- // [SECTION] Constants //----------------------------------------------------------------------------- @@ -386,9 +377,7 @@ struct ImPlotColormapData { int idx = Count++; Map.SetInt(id,idx); _AppendTable(idx); -#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION - ImPlot::Backends::OpenGL3_AddColormap(keys, count, qual); -#endif + ImPlot::Backend::AddColormap(keys, count, qual); return idx; } @@ -929,6 +918,9 @@ struct ImPlotContext { ImPlotNextItemData NextItemData; ImPlotInputMap InputMap; ImPlotPoint MousePos[IMPLOT_Y_AXES]; + + // Backend + void* backendCtx; }; //----------------------------------------------------------------------------- diff --git a/implot_items.cpp b/implot_items.cpp index 2483566..0cf8d28 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -24,6 +24,7 @@ #include "implot.h" #include "implot_internal.h" +#include "backends/implot_gpu.h" #ifdef _MSC_VER #define sprintf sprintf_s @@ -53,26 +54,6 @@ #endif -#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION -namespace ImPlot { -namespace Backends { - void OpenGL3_SetHeatmapData(int texID, const ImS8* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const ImU8* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const ImS16* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const ImU16* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const ImS32* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const ImU32* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const ImS64* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const ImU64* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const float* values, int rows, int cols); - void OpenGL3_SetHeatmapData(int texID, const double* values, int rows, int cols); - - void OpenGL3_RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap); -} -} -#endif - - namespace ImPlot { //----------------------------------------------------------------------------- @@ -158,6 +139,7 @@ void HideNextItem(bool hidden, ImGuiCond cond) { void BustItemCache() { ImPlotContext& gp = *GImPlot; + Backend::BustItemCache(); for (int p = 0; p < gp.Plots.GetBufSize(); ++p) { ImPlotPlot& plot = *gp.Plots.GetByIndex(p); plot.ColormapIdx = 0; @@ -1915,8 +1897,8 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value ImVec2 bmin = transformer(bounds_min); ImVec2 bmax = transformer(bounds_max); - Backends::OpenGL3_RenderHeatmap(gp.CurrentPlot->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap); - Backends::OpenGL3_SetHeatmapData(gp.CurrentPlot->ID, values, rows, cols); + Backend::RenderHeatmap(gp.CurrentPlot->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap); + Backend::SetHeatmapData(gp.CurrentPlot->ID, values, rows, cols); #else GetterHeatmap getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); switch (GetCurrentScale()) { From 50407e5279c51e885ae61511a56d08fee2ec90c5 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Wed, 23 Jun 2021 00:51:02 +0200 Subject: [PATCH 06/13] Fix integer heatmaps not working properly --- backends/implot_opengl3.cpp | 194 +++++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 83 deletions(-) diff --git a/backends/implot_opengl3.cpp b/backends/implot_opengl3.cpp index 15ea30a..e29ed65 100644 --- a/backends/implot_opengl3.cpp +++ b/backends/implot_opengl3.cpp @@ -35,8 +35,20 @@ using namespace gl; namespace ImPlot { namespace Backend { +struct Shader +{ + GLuint ID = 0; ///< Shader ID for the heatmap shader + + GLuint g_AttribLocationHeatmapSampler = 0; ///< Attribute location for the heatmap texture + GLuint g_AttribLocationColormapSampler = 0; ///< Attribute location for the colormap texture + GLuint g_AttribLocationProjection = 0; ///< Attribute location for the projection matrix uniform + GLuint g_AttribLocationMinValue = 0; ///< Attribute location for the minimum value uniform + GLuint g_AttribLocationMaxValue = 0; ///< Attribute location for the maximum value uniform +}; + struct HeatmapData { + Shader* ShaderProgram; GLuint HeatmapTexID; GLuint ColormapTexID; float MinValue; @@ -45,12 +57,8 @@ struct HeatmapData struct ContextData { - GLuint g_ShaderProgram = 0; ///< Shader ID for the heatmap shader - GLuint g_AttribLocationHeatmapSampler = 0; ///< Attribute location for the heatmap texture - GLuint g_AttribLocationColormapSampler = 0; ///< Attribute location for the colormap texture - GLuint g_AttribLocationProjection = 0; ///< Attribute location for the projection matrix uniform - GLuint g_AttribLocationMinValue = 0; ///< Attribute location for the minimum value uniform - GLuint g_AttribLocationMaxValue = 0; ///< Attribute location for the maximum value uniform + Shader ShaderInt; ///< Shader for integer heatmaps + Shader ShaderFloat; ///< Shader for floating-point heatmaps GLuint g_AttribLocationImGuiProjection = 0; ///< Attribute location for the projection matrix uniform (ImGui default shader) @@ -78,10 +86,78 @@ void DestroyContext() for(GLuint texID : Context.ColormapIDs) glDeleteTextures(1, &texID); + glDeleteProgram(Context.ShaderInt.ID); + glDeleteProgram(Context.ShaderFloat.ID); + Context.HeatmapDataList.clear(); Context.PlotIDs.Clear(); } +#define VERTEX_SHADER_CODE \ + "#version 330 core\n" \ + "precision mediump float;\n" \ + "layout (location = %d) in vec2 Position;\n" \ + "layout (location = %d) in vec2 UV;\n" \ + "\n" \ + "uniform mat4 ProjMtx;\n" \ + "out vec2 Frag_UV;\n" \ + "\n" \ + "void main()\n" \ + "{\n" \ + " Frag_UV = UV;\n" \ + " gl_Position = ProjMtx * vec4(Position.xy, 0.0f, 1.0f);\n" \ + "}\n" + +#define FRAGMENT_SHADER_CODE \ + "#version 330 core\n" \ + "precision mediump float;\n" \ + "\n" \ + "in vec2 Frag_UV;\n" \ + "out vec4 Out_Color;\n" \ + "\n" \ + "uniform sampler1D colormap;\n" \ + "uniform %csampler2D heatmap;\n" \ + "uniform float min_val;\n" \ + "uniform float max_val;\n" \ + "\n" \ + "void main()\n" \ + "{\n" \ + " float value = float(texture(heatmap, Frag_UV).r);\n" \ + " float offset = (value - min_val) / (max_val - min_val);\n" \ + " Out_Color = texture(colormap, clamp(offset, 0.0f, 1.0f));\n" \ + "}\n" + +static void CompileShader(Shader& ShaderProgram, GLchar* VertexShaderCode, GLchar* FragmentShaderCode) +{ + GLuint g_VertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(g_VertexShader, 1, &VertexShaderCode, nullptr); + glCompileShader(g_VertexShader); + + GLuint g_FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(g_FragmentShader, 1, &FragmentShaderCode, nullptr); + glCompileShader(g_FragmentShader); + + ShaderProgram.ID = glCreateProgram(); + glAttachShader(ShaderProgram.ID, g_VertexShader); + glAttachShader(ShaderProgram.ID, g_FragmentShader); + glLinkProgram(ShaderProgram.ID); + + glDetachShader(ShaderProgram.ID, g_VertexShader); + glDetachShader(ShaderProgram.ID, g_FragmentShader); + glDeleteShader(g_VertexShader); + glDeleteShader(g_FragmentShader); + + ShaderProgram.g_AttribLocationHeatmapSampler = glGetUniformLocation(ShaderProgram.ID, "heatmap"); + ShaderProgram.g_AttribLocationColormapSampler = glGetUniformLocation(ShaderProgram.ID, "colormap"); + ShaderProgram.g_AttribLocationProjection = glGetUniformLocation(ShaderProgram.ID, "ProjMtx"); + ShaderProgram.g_AttribLocationMinValue = glGetUniformLocation(ShaderProgram.ID, "min_val"); + ShaderProgram.g_AttribLocationMaxValue = glGetUniformLocation(ShaderProgram.ID, "max_val"); + + glUseProgram(ShaderProgram.ID); + glUniform1i(ShaderProgram.g_AttribLocationHeatmapSampler, 0); // Set texture slot of heatmap texture + glUniform1i(ShaderProgram.g_AttribLocationColormapSampler, 1); // Set texture slot of colormap texture +} + static void CreateShader(const ImDrawList*, const ImDrawCmd*) { ContextData& Context = *((ContextData*)GImPlot->backendCtx); @@ -91,74 +167,25 @@ static void CreateShader(const ImDrawList*, const ImDrawCmd*) Context.g_AttribLocationImGuiProjection = glGetUniformLocation(CurrentShader, "ProjMtx"); - GLuint g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(CurrentShader, "Position"); - GLuint g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(CurrentShader, "UV"); - - const GLchar* VertexShaderCode_t = - "#version 330 core\n" - "precision mediump float;\n" - "layout (location = %d) in vec2 Position;\n" - "layout (location = %d) in vec2 UV;\n" - "\n" - "uniform mat4 ProjMtx;\n" - "out vec2 Frag_UV;\n" - "\n" - "void main()\n" - "{\n" - " Frag_UV = UV;\n" - " gl_Position = ProjMtx * vec4(Position.xy, 0.0f, 1.0f);\n" - "}\n"; - - const GLchar* FragmentShaderCode = - "#version 330 core\n" - "precision mediump float;\n" - "\n" - "in vec2 Frag_UV;\n" - "out vec4 Out_Color;\n" - "\n" - "uniform sampler1D colormap;\n" - "uniform sampler2D heatmap;\n" - "uniform float min_val;\n" - "uniform float max_val;\n" - "\n" - "void main()\n" - "{\n" - " float offset = (texture(heatmap, Frag_UV).r - min_val) / (max_val - min_val);\n" - " Out_Color = texture(colormap, clamp(offset, 0.0f, 1.0f));\n" - "}\n"; + GLuint g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(CurrentShader, "Position"); + GLuint g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(CurrentShader, "UV"); GLchar* VertexShaderCode = new GLchar[512]; - snprintf(VertexShaderCode, 512, VertexShaderCode_t, g_AttribLocationVtxPos, g_AttribLocationVtxUV); + GLchar* FragmentShaderCode = new GLchar[512]; - GLuint g_VertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(g_VertexShader, 1, &VertexShaderCode, nullptr); - glCompileShader(g_VertexShader); + snprintf(VertexShaderCode, 512, VERTEX_SHADER_CODE, g_AttribLocationVtxPos, g_AttribLocationVtxUV); + snprintf(FragmentShaderCode, 512, FRAGMENT_SHADER_CODE, ' '); - GLuint g_FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(g_FragmentShader, 1, &FragmentShaderCode, nullptr); - glCompileShader(g_FragmentShader); + CompileShader(Context.ShaderFloat, VertexShaderCode, FragmentShaderCode); - Context.g_ShaderProgram = glCreateProgram(); - glAttachShader(Context.g_ShaderProgram, g_VertexShader); - glAttachShader(Context.g_ShaderProgram, g_FragmentShader); - glLinkProgram(Context.g_ShaderProgram); + snprintf(VertexShaderCode, 512, VERTEX_SHADER_CODE, g_AttribLocationVtxPos, g_AttribLocationVtxUV); + snprintf(FragmentShaderCode, 512, FRAGMENT_SHADER_CODE, 'i'); - glDetachShader(Context.g_ShaderProgram, g_VertexShader); - glDetachShader(Context.g_ShaderProgram, g_FragmentShader); - glDeleteShader(g_VertexShader); - glDeleteShader(g_FragmentShader); - - Context.g_AttribLocationHeatmapSampler = glGetUniformLocation(Context.g_ShaderProgram, "heatmap"); - Context.g_AttribLocationColormapSampler = glGetUniformLocation(Context.g_ShaderProgram, "colormap"); - Context.g_AttribLocationProjection = glGetUniformLocation(Context.g_ShaderProgram, "ProjMtx"); - Context.g_AttribLocationMinValue = glGetUniformLocation(Context.g_ShaderProgram, "min_val"); - Context.g_AttribLocationMaxValue = glGetUniformLocation(Context.g_ShaderProgram, "max_val"); - - glUseProgram(Context.g_ShaderProgram); - glUniform1i(Context.g_AttribLocationHeatmapSampler, 0); // Set texture slot of heatmap texture - glUniform1i(Context.g_AttribLocationColormapSampler, 1); // Set texture slot of colormap texture + CompileShader(Context.ShaderInt, VertexShaderCode, FragmentShaderCode); + glUseProgram(0); delete[] VertexShaderCode; + delete[] FragmentShaderCode; } static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd) @@ -177,14 +204,14 @@ static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd) glGetUniformfv(CurrentShader, Context.g_AttribLocationImGuiProjection, &OrthoProjection[0][0]); // Enable our shader - glUseProgram(Context.g_ShaderProgram); + glUseProgram(data.ShaderProgram->ID); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, data.HeatmapTexID); // Set texture ID of data glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_1D, data.ColormapTexID); // Set texture ID of colormap - glUniformMatrix4fv(Context.g_AttribLocationProjection, 1, GL_FALSE, &OrthoProjection[0][0]); - glUniform1f(Context.g_AttribLocationMinValue, data.MinValue); // Set minimum range - glUniform1f(Context.g_AttribLocationMaxValue, data.MaxValue); // Set maximum range + glUniformMatrix4fv(data.ShaderProgram->g_AttribLocationProjection, 1, GL_FALSE, &OrthoProjection[0][0]); + glUniform1f(data.ShaderProgram->g_AttribLocationMinValue, data.MinValue); // Set minimum range + glUniform1f(data.ShaderProgram->g_AttribLocationMaxValue, data.MaxValue); // Set maximum range } static void UnbindTexture(const ImDrawList*, const ImDrawCmd*) @@ -193,16 +220,17 @@ static void UnbindTexture(const ImDrawList*, const ImDrawCmd*) glBindTexture(GL_TEXTURE_2D, 0); } -static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei cols, GLenum type) +static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei cols, GLint internalFormat, GLenum format, GLenum type) { ContextData& Context = *((ContextData*)GImPlot->backendCtx); int idx = Context.PlotIDs.GetInt(plotID, -1); GLuint texID = Context.HeatmapDataList[idx].HeatmapTexID; + Context.HeatmapDataList[idx].ShaderProgram = (type == GL_FLOAT ? &Context.ShaderFloat : &Context.ShaderInt); // Set heatmap data glBindTexture(GL_TEXTURE_2D, texID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cols, rows, 0, GL_RED, type, data); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, cols, rows, 0, format, type, data); } void AddColormap(const ImU32* keys, int count, bool qual) @@ -233,13 +261,13 @@ static GLuint CreateHeatmapTexture() return textureID; } -void SetHeatmapData(int plotID, const ImS8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_BYTE); } -void SetHeatmapData(int plotID, const ImU8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_BYTE); } -void SetHeatmapData(int plotID, const ImS16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_SHORT); } -void SetHeatmapData(int plotID, const ImU16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_SHORT); } -void SetHeatmapData(int plotID, const ImS32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_INT); } -void SetHeatmapData(int plotID, const ImU32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_UNSIGNED_INT); } -void SetHeatmapData(int plotID, const float* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_FLOAT); } +void SetHeatmapData(int plotID, const ImS8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R8I, GL_RED_INTEGER, GL_BYTE); } +void SetHeatmapData(int plotID, const ImU8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE); } +void SetHeatmapData(int plotID, const ImS16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R16I, GL_RED_INTEGER, GL_SHORT); } +void SetHeatmapData(int plotID, const ImU16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT); } +void SetHeatmapData(int plotID, const ImS32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R32I, GL_RED_INTEGER, GL_INT); } +void SetHeatmapData(int plotID, const ImU32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT); } +void SetHeatmapData(int plotID, const float* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R32F, GL_RED, GL_FLOAT); } void SetHeatmapData(int plotID, const double* values, int rows, int cols) { @@ -251,7 +279,7 @@ void SetHeatmapData(int plotID, const double* values, int rows, int cols) for(int i = 0; i < rows*cols; i++) Context.temp1[i] = (float)values[i]; - SetTextureData(plotID, Context.temp1.Data, rows, cols, GL_FLOAT); + SetTextureData(plotID, Context.temp1.Data, rows, cols, GL_R32F, GL_RED, GL_FLOAT); } void SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) @@ -264,7 +292,7 @@ void SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) for(int i = 0; i < rows*cols; i++) Context.temp2[i] = (ImS32)values[i]; - SetTextureData(plotID, Context.temp2.Data, rows, cols, GL_INT); + SetTextureData(plotID, Context.temp2.Data, rows, cols, GL_R32I, GL_RED_INTEGER, GL_INT); } void SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) @@ -277,7 +305,7 @@ void SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) for(int i = 0; i < rows*cols; i++) Context.temp3[i] = (ImU32)values[i]; - SetTextureData(plotID, Context.temp3.Data, rows, cols, GL_UNSIGNED_INT); + SetTextureData(plotID, Context.temp3.Data, rows, cols, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT); } void RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) @@ -305,7 +333,7 @@ void RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, c data.MaxValue = scale_max; } - if(Context.g_ShaderProgram == 0) + if(Context.ShaderInt.ID == 0) DrawList.AddCallback(CreateShader, nullptr); DrawList.AddCallback(RenderCallback, (void*)(intptr_t)plotID); From 541521fe6984b0744eabcb56a7b15bd95f671a99 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Wed, 23 Jun 2021 11:43:12 +0200 Subject: [PATCH 07/13] Misc changes - Rename implot_gpu.h -> implot_backend.h - Rename IMPLOT_ENABLE_OPENGL3_ACCELERATION -> IMPLOT_BACKEND_ENABLE_OPENGL3 - Use gp.CurrentItem->ID over gp.CurrentPlot->ID - Add flags for different features supported by backends --- backends/{implot_gpu.h => implot_backend.h} | 22 +++++++++++++++++---- backends/implot_opengl3.cpp | 4 ++-- implot_internal.h | 2 +- implot_items.cpp | 10 +++++----- 4 files changed, 26 insertions(+), 12 deletions(-) rename backends/{implot_gpu.h => implot_backend.h} (95%) diff --git a/backends/implot_gpu.h b/backends/implot_backend.h similarity index 95% rename from backends/implot_gpu.h rename to backends/implot_backend.h index e5d9477..952d317 100644 --- a/backends/implot_gpu.h +++ b/backends/implot_backend.h @@ -26,6 +26,12 @@ #include "../implot.h" +#ifdef IMPLOT_BACKEND_ENABLE_OPENGL3 + #define IMPLOT_BACKEND_ENABLED + #define IMPLOT_BACKEND_HAS_HEATMAP + #define IMPLOT_BACKEND_HAS_COLORMAP +#endif + namespace ImPlot { namespace Backend { @@ -136,19 +142,27 @@ IMPLOT_API void BustPlotCache(); /** @brief Bust item cache. Called from @ref ImPlot::BustItemCache() */ IMPLOT_API void BustItemCache(); +} +} -#if !defined(IMPLOT_ENABLE_OPENGL3_ACCELERATION) +namespace ImPlot { +namespace Backend { + +#ifndef IMPLOT_BACKEND_ENABLED -// Dummy implementation for backend functions inline void* CreateContext() { return nullptr; } inline void DestroyContext() {} -inline void AddColormap(const ImU32*, int, bool) {} - inline void BustPlotCache() {} inline void BustItemCache() {} #endif +#ifndef IMPLOT_BACKEND_HAS_COLORMAP + +inline void AddColormap(const ImU32*, int, bool) {} + +#endif + } } diff --git a/backends/implot_opengl3.cpp b/backends/implot_opengl3.cpp index e29ed65..0db06f7 100644 --- a/backends/implot_opengl3.cpp +++ b/backends/implot_opengl3.cpp @@ -1,8 +1,8 @@ -#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION +#ifdef IMPLOT_BACKEND_ENABLE_OPENGL3 #include "../implot.h" #include "../implot_internal.h" -#include "implot_gpu.h" +#include "implot_backend.h" #if defined(IMGUI_IMPL_OPENGL_ES2) #include diff --git a/implot_internal.h b/implot_internal.h index 866148f..9dea114 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -37,7 +37,7 @@ #include #include "imgui_internal.h" -#include "backends/implot_gpu.h" +#include "backends/implot_backend.h" #ifndef IMPLOT_VERSION #error Must include implot.h before implot_internal.h diff --git a/implot_items.cpp b/implot_items.cpp index 0cf8d28..1c18f9d 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -24,7 +24,7 @@ #include "implot.h" #include "implot_internal.h" -#include "backends/implot_gpu.h" +#include "backends/implot_backend.h" #ifdef _MSC_VER #define sprintf sprintf_s @@ -1892,13 +1892,13 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value } const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; -#ifdef IMPLOT_ENABLE_OPENGL3_ACCELERATION - // NOTE: Order is important! +#ifdef IMPLOT_BACKEND_HAS_HEATMAP ImVec2 bmin = transformer(bounds_min); ImVec2 bmax = transformer(bounds_max); - Backend::RenderHeatmap(gp.CurrentPlot->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap); - Backend::SetHeatmapData(gp.CurrentPlot->ID, values, rows, cols); + // NOTE: Order is important! + Backend::RenderHeatmap(gp.CurrentItem->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap); + Backend::SetHeatmapData(gp.CurrentItem->ID, values, rows, cols); #else GetterHeatmap getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); switch (GetCurrentScale()) { From 27769a846ff8a012bb9af94753e87876a43833d2 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Wed, 23 Jun 2021 13:37:55 +0200 Subject: [PATCH 08/13] Cleanup & maintenance - Removed unnecessary attributes from shader - Renamed functions & structs - Reduced memory footprint of OpenGL backend context --- backends/implot_backend.h | 24 ++++-- backends/implot_opengl3.cpp | 161 +++++++++++++++++------------------- 2 files changed, 92 insertions(+), 93 deletions(-) diff --git a/backends/implot_backend.h b/backends/implot_backend.h index 952d317..c565aee 100644 --- a/backends/implot_backend.h +++ b/backends/implot_backend.h @@ -35,6 +35,10 @@ namespace ImPlot { namespace Backend { +//----------------------------------------------------------------------------- +// [SECTION] Misc backend functions +//----------------------------------------------------------------------------- + /** * @brief Struct to hold backend-related context data * @@ -62,6 +66,16 @@ IMPLOT_API void* CreateContext(); */ IMPLOT_API void DestroyContext(); +/** @brief Bust plot cache. Called from @ref ImPlot::BustPlotCache() */ +IMPLOT_API void BustPlotCache(); + +/** @brief Bust item cache. Called from @ref ImPlot::BustItemCache() */ +IMPLOT_API void BustItemCache(); + +//----------------------------------------------------------------------------- +// [SECTION] Colormap functions +//----------------------------------------------------------------------------- + /** * @brief Add a colormap * @@ -74,6 +88,10 @@ IMPLOT_API void DestroyContext(); */ IMPLOT_API void AddColormap(const ImU32* keys, int count, bool qual); +//----------------------------------------------------------------------------- +// [SECTION] Heatmap functions +//----------------------------------------------------------------------------- + /** * @brief Set heatmap data * @@ -136,12 +154,6 @@ IMPLOT_API void RenderHeatmap( int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap); -/** @brief Bust plot cache. Called from @ref ImPlot::BustPlotCache() */ -IMPLOT_API void BustPlotCache(); - -/** @brief Bust item cache. Called from @ref ImPlot::BustItemCache() */ -IMPLOT_API void BustItemCache(); - } } diff --git a/backends/implot_opengl3.cpp b/backends/implot_opengl3.cpp index 0db06f7..ec01181 100644 --- a/backends/implot_opengl3.cpp +++ b/backends/implot_opengl3.cpp @@ -35,40 +35,39 @@ using namespace gl; namespace ImPlot { namespace Backend { -struct Shader +struct HeatmapShader { - GLuint ID = 0; ///< Shader ID for the heatmap shader + GLuint ID = 0; ///< Shader ID for the heatmap shader - GLuint g_AttribLocationHeatmapSampler = 0; ///< Attribute location for the heatmap texture - GLuint g_AttribLocationColormapSampler = 0; ///< Attribute location for the colormap texture - GLuint g_AttribLocationProjection = 0; ///< Attribute location for the projection matrix uniform - GLuint g_AttribLocationMinValue = 0; ///< Attribute location for the minimum value uniform - GLuint g_AttribLocationMaxValue = 0; ///< Attribute location for the maximum value uniform + GLuint AttribLocationProjection = 0; ///< Attribute location for the projection matrix uniform + GLuint AttribLocationMinValue = 0; ///< Attribute location for the minimum value uniform + GLuint AttribLocationMaxValue = 0; ///< Attribute location for the maximum value uniform }; struct HeatmapData { - Shader* ShaderProgram; - GLuint HeatmapTexID; - GLuint ColormapTexID; - float MinValue; - float MaxValue; + HeatmapShader* ShaderProgram; ///< Shader to be used by this heatmap (either ShaderInt or ShaderFloat) + GLuint HeatmapTexID; ///< Texture ID of the heatmap 2D texture + GLuint ColormapTexID; ///< Texture ID of the colormap 1D texture + float MinValue; ///< Minimum value of the colormap + float MaxValue; ///< Maximum value of the colormap }; struct ContextData { - Shader ShaderInt; ///< Shader for integer heatmaps - Shader ShaderFloat; ///< Shader for floating-point heatmaps + HeatmapShader ShaderInt; ///< Shader for integer heatmaps + HeatmapShader ShaderFloat; ///< Shader for floating-point heatmaps - GLuint g_AttribLocationImGuiProjection = 0; ///< Attribute location for the projection matrix uniform (ImGui default shader) + GLuint AttribLocationImGuiProjection = 0; ///< Attribute location for the projection matrix uniform (ImGui default shader) + GLuint ImGuiShader = 0; ///< Shader ID of ImGui's default shader - ImVector HeatmapDataList; ///< Array of heatmap data - ImVector ColormapIDs; ///< Texture IDs of the colormap textures - ImGuiStorage PlotIDs; ///< PlotID <-> Heatmap array index table + ImVector HeatmapDataList; ///< Array of heatmap data + ImVector ColormapIDs; ///< Texture IDs of the colormap textures + ImGuiStorage PlotIDs; ///< PlotID <-> Heatmap array index table - ImVector temp1; - ImVector temp2; - ImVector temp3; + ImVector temp1; ///< Temporary data + ImVector temp2; ///< Temporary data + ImVector temp3; ///< Temporary data }; void* CreateContext() @@ -93,7 +92,7 @@ void DestroyContext() Context.PlotIDs.Clear(); } -#define VERTEX_SHADER_CODE \ +#define HEATMAP_VERTEX_SHADER_CODE \ "#version 330 core\n" \ "precision mediump float;\n" \ "layout (location = %d) in vec2 Position;\n" \ @@ -108,7 +107,7 @@ void DestroyContext() " gl_Position = ProjMtx * vec4(Position.xy, 0.0f, 1.0f);\n" \ "}\n" -#define FRAGMENT_SHADER_CODE \ +#define HEATMAP_FRAGMENT_SHADER_CODE \ "#version 330 core\n" \ "precision mediump float;\n" \ "\n" \ @@ -127,59 +126,55 @@ void DestroyContext() " Out_Color = texture(colormap, clamp(offset, 0.0f, 1.0f));\n" \ "}\n" -static void CompileShader(Shader& ShaderProgram, GLchar* VertexShaderCode, GLchar* FragmentShaderCode) +static void CompileShader(HeatmapShader& ShaderProgram, GLchar* VertexShaderCode, GLchar* FragmentShaderCode) { - GLuint g_VertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(g_VertexShader, 1, &VertexShaderCode, nullptr); - glCompileShader(g_VertexShader); + GLuint VertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(VertexShader, 1, &VertexShaderCode, nullptr); + glCompileShader(VertexShader); - GLuint g_FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(g_FragmentShader, 1, &FragmentShaderCode, nullptr); - glCompileShader(g_FragmentShader); + GLuint FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(FragmentShader, 1, &FragmentShaderCode, nullptr); + glCompileShader(FragmentShader); ShaderProgram.ID = glCreateProgram(); - glAttachShader(ShaderProgram.ID, g_VertexShader); - glAttachShader(ShaderProgram.ID, g_FragmentShader); + glAttachShader(ShaderProgram.ID, VertexShader); + glAttachShader(ShaderProgram.ID, FragmentShader); glLinkProgram(ShaderProgram.ID); - glDetachShader(ShaderProgram.ID, g_VertexShader); - glDetachShader(ShaderProgram.ID, g_FragmentShader); - glDeleteShader(g_VertexShader); - glDeleteShader(g_FragmentShader); + glDetachShader(ShaderProgram.ID, VertexShader); + glDetachShader(ShaderProgram.ID, FragmentShader); + glDeleteShader(VertexShader); + glDeleteShader(FragmentShader); - ShaderProgram.g_AttribLocationHeatmapSampler = glGetUniformLocation(ShaderProgram.ID, "heatmap"); - ShaderProgram.g_AttribLocationColormapSampler = glGetUniformLocation(ShaderProgram.ID, "colormap"); - ShaderProgram.g_AttribLocationProjection = glGetUniformLocation(ShaderProgram.ID, "ProjMtx"); - ShaderProgram.g_AttribLocationMinValue = glGetUniformLocation(ShaderProgram.ID, "min_val"); - ShaderProgram.g_AttribLocationMaxValue = glGetUniformLocation(ShaderProgram.ID, "max_val"); + ShaderProgram.AttribLocationProjection = glGetUniformLocation(ShaderProgram.ID, "ProjMtx"); + ShaderProgram.AttribLocationMinValue = glGetUniformLocation(ShaderProgram.ID, "min_val"); + ShaderProgram.AttribLocationMaxValue = glGetUniformLocation(ShaderProgram.ID, "max_val"); glUseProgram(ShaderProgram.ID); - glUniform1i(ShaderProgram.g_AttribLocationHeatmapSampler, 0); // Set texture slot of heatmap texture - glUniform1i(ShaderProgram.g_AttribLocationColormapSampler, 1); // Set texture slot of colormap texture + glUniform1i(glGetUniformLocation(ShaderProgram.ID, "heatmap"), 0); // Set texture slot of heatmap texture + glUniform1i(glGetUniformLocation(ShaderProgram.ID, "colormap"), 1); // Set texture slot of colormap texture } -static void CreateShader(const ImDrawList*, const ImDrawCmd*) +static void CreateHeatmapShader(const ImDrawList*, const ImDrawCmd*) { ContextData& Context = *((ContextData*)GImPlot->backendCtx); - GLuint CurrentShader; - glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&CurrentShader); + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&Context.ImGuiShader); - Context.g_AttribLocationImGuiProjection = glGetUniformLocation(CurrentShader, "ProjMtx"); - - GLuint g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(CurrentShader, "Position"); - GLuint g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(CurrentShader, "UV"); + Context.AttribLocationImGuiProjection = glGetUniformLocation(Context.ImGuiShader, "ProjMtx"); + GLuint AttribLocationVtxPos = (GLuint)glGetAttribLocation(Context.ImGuiShader, "Position"); + GLuint AttribLocationVtxUV = (GLuint)glGetAttribLocation(Context.ImGuiShader, "UV"); GLchar* VertexShaderCode = new GLchar[512]; GLchar* FragmentShaderCode = new GLchar[512]; - snprintf(VertexShaderCode, 512, VERTEX_SHADER_CODE, g_AttribLocationVtxPos, g_AttribLocationVtxUV); - snprintf(FragmentShaderCode, 512, FRAGMENT_SHADER_CODE, ' '); + snprintf(VertexShaderCode, 512, HEATMAP_VERTEX_SHADER_CODE, AttribLocationVtxPos, AttribLocationVtxUV); + snprintf(FragmentShaderCode, 512, HEATMAP_FRAGMENT_SHADER_CODE, ' '); CompileShader(Context.ShaderFloat, VertexShaderCode, FragmentShaderCode); - snprintf(VertexShaderCode, 512, VERTEX_SHADER_CODE, g_AttribLocationVtxPos, g_AttribLocationVtxUV); - snprintf(FragmentShaderCode, 512, FRAGMENT_SHADER_CODE, 'i'); + snprintf(VertexShaderCode, 512, HEATMAP_VERTEX_SHADER_CODE, AttribLocationVtxPos, AttribLocationVtxUV); + snprintf(FragmentShaderCode, 512, HEATMAP_FRAGMENT_SHADER_CODE, 'i'); CompileShader(Context.ShaderInt, VertexShaderCode, FragmentShaderCode); glUseProgram(0); @@ -196,12 +191,9 @@ static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd) int plotIdx = Context.PlotIDs.GetInt(plotID, -1); HeatmapData& data = Context.HeatmapDataList[plotIdx]; - GLuint CurrentShader; - glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&CurrentShader); - // Get projection matrix of current shader float OrthoProjection[4][4]; - glGetUniformfv(CurrentShader, Context.g_AttribLocationImGuiProjection, &OrthoProjection[0][0]); + glGetUniformfv(Context.ImGuiShader, Context.AttribLocationImGuiProjection, &OrthoProjection[0][0]); // Enable our shader glUseProgram(data.ShaderProgram->ID); @@ -209,15 +201,18 @@ static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, data.HeatmapTexID); // Set texture ID of data glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_1D, data.ColormapTexID); // Set texture ID of colormap - glUniformMatrix4fv(data.ShaderProgram->g_AttribLocationProjection, 1, GL_FALSE, &OrthoProjection[0][0]); - glUniform1f(data.ShaderProgram->g_AttribLocationMinValue, data.MinValue); // Set minimum range - glUniform1f(data.ShaderProgram->g_AttribLocationMaxValue, data.MaxValue); // Set maximum range + glUniformMatrix4fv(data.ShaderProgram->AttribLocationProjection, 1, GL_FALSE, &OrthoProjection[0][0]); + glUniform1f(data.ShaderProgram->AttribLocationMinValue, data.MinValue); // Set minimum range + glUniform1f(data.ShaderProgram->AttribLocationMaxValue, data.MaxValue); // Set maximum range } -static void UnbindTexture(const ImDrawList*, const ImDrawCmd*) +static void ResetState(const ImDrawList*, const ImDrawCmd*) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); + + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + glUseProgram(Context.ImGuiShader); } static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei cols, GLint internalFormat, GLenum format, GLenum type) @@ -235,9 +230,9 @@ static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei c void AddColormap(const ImU32* keys, int count, bool qual) { - GLuint texID; - glGenTextures(1, &texID); - glBindTexture(GL_TEXTURE_1D, texID); + GLuint textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_1D, textureID); glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, count, 0, GL_RGBA, GL_UNSIGNED_BYTE, keys); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -245,8 +240,8 @@ void AddColormap(const ImU32* keys, int count, bool qual) glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, qual ? GL_NEAREST : GL_LINEAR); glBindTexture(GL_TEXTURE_1D, 0); - ContextData& Context = *((ContextData*)GImPlot->backendCtx); // PETA AQUI (GImPlot es NULL) - Context.ColormapIDs.push_back(texID); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + Context.ColormapIDs.push_back(textureID); } static GLuint CreateHeatmapTexture() @@ -311,36 +306,28 @@ void SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) void RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) { ContextData& Context = *((ContextData*)GImPlot->backendCtx); - int idx = Context.PlotIDs.GetInt(plotID, -1); + int idx = Context.PlotIDs.GetInt(plotID, Context.HeatmapDataList.Size); - if(idx < 0) + if(idx == Context.HeatmapDataList.Size) { // New entry - HeatmapData data; - data.HeatmapTexID = CreateHeatmapTexture(); - data.ColormapTexID = Context.ColormapIDs[colormap]; - data.MinValue = scale_min; - data.MaxValue = scale_max; - Context.PlotIDs.SetInt(plotID, Context.HeatmapDataList.Size); - Context.HeatmapDataList.push_back(data); - } - else - { - HeatmapData& data = Context.HeatmapDataList[idx]; - data.ColormapTexID = Context.ColormapIDs[colormap]; - data.MinValue = scale_min; - data.MaxValue = scale_max; + Context.HeatmapDataList.push_back(HeatmapData()); + Context.HeatmapDataList[idx].HeatmapTexID = CreateHeatmapTexture(); } - if(Context.ShaderInt.ID == 0) - DrawList.AddCallback(CreateShader, nullptr); + HeatmapData& data = Context.HeatmapDataList[idx]; + data.ColormapTexID = Context.ColormapIDs[colormap]; + data.MinValue = scale_min; + data.MaxValue = scale_max; + + if(Context.ShaderInt.ID == 0 || Context.ShaderFloat.ID == 0) + DrawList.AddCallback(CreateHeatmapShader, nullptr); DrawList.AddCallback(RenderCallback, (void*)(intptr_t)plotID); DrawList.PrimReserve(6, 4); DrawList.PrimRectUV(bounds_min, bounds_max, ImVec2(0.0f, 1.0f), ImVec2(1.0f, 0.0f), 0); - DrawList.AddCallback(UnbindTexture, nullptr); - DrawList.AddCallback(ImDrawCallback_ResetRenderState, nullptr); + DrawList.AddCallback(ResetState, nullptr); } void BustPlotCache() From 449b30b38997feaa1b952a7675091d44c8247e6e Mon Sep 17 00:00:00 2001 From: marcizhu Date: Thu, 24 Jun 2021 10:39:21 +0200 Subject: [PATCH 09/13] Standandardize backends for future implementations --- backends/implot_backend.h | 149 +------------- ...ot_opengl3.cpp => implot_impl_opengl3.cpp} | 71 +++---- backends/implot_impl_opengl3.h | 194 ++++++++++++++++++ 3 files changed, 239 insertions(+), 175 deletions(-) rename backends/{implot_opengl3.cpp => implot_impl_opengl3.cpp} (82%) create mode 100644 backends/implot_impl_opengl3.h diff --git a/backends/implot_backend.h b/backends/implot_backend.h index c565aee..1ccfca4 100644 --- a/backends/implot_backend.h +++ b/backends/implot_backend.h @@ -24,156 +24,25 @@ #pragma once -#include "../implot.h" - -#ifdef IMPLOT_BACKEND_ENABLE_OPENGL3 - #define IMPLOT_BACKEND_ENABLED - #define IMPLOT_BACKEND_HAS_HEATMAP - #define IMPLOT_BACKEND_HAS_COLORMAP +#if defined(IMPLOT_BACKEND_ENABLE_OPENGL3) + #include "implot_impl_opengl3.h" +#elif defined(IMPLOT_BACKEND_ENABLE_METAL) + #include "implot_impl_metal.h" #endif namespace ImPlot { namespace Backend { -//----------------------------------------------------------------------------- -// [SECTION] Misc backend functions -//----------------------------------------------------------------------------- - -/** - * @brief Struct to hold backend-related context data - * - * A backend may store in this struct any data it needs, with no constraints. A - * pointer to this struct will be stored inside ImPlot's context and can be - * accessed at any time. This pointer will be set to the returned value of @ref - * CreateContext(). All resources held by this struct must be freed inside @ref - * DestroyContext(). - */ -struct ContextData; - -/** - * @brief Create backend context - * - * Creates and intializes the backend context. The returned pointer will be saved - * in ImPlot's context and can be accessed later. - */ -IMPLOT_API void* CreateContext(); - -/** - * @brief Destroy backend context - * - * Destroys and frees any memory or resources needed by the backend. After this - * call returns, no more calls to any backend function will be performed. - */ -IMPLOT_API void DestroyContext(); - -/** @brief Bust plot cache. Called from @ref ImPlot::BustPlotCache() */ -IMPLOT_API void BustPlotCache(); - -/** @brief Bust item cache. Called from @ref ImPlot::BustItemCache() */ -IMPLOT_API void BustItemCache(); - -//----------------------------------------------------------------------------- -// [SECTION] Colormap functions -//----------------------------------------------------------------------------- - -/** - * @brief Add a colormap - * - * Adds a colormap to be handled by the backend. - * - * @param keys Colors for this colormap, in RGBA format - * @param count Number of colors in this colormap - * @param qual Qualitative: whether the colormap is continuous (`false`) or - * not (`true`) - */ -IMPLOT_API void AddColormap(const ImU32* keys, int count, bool qual); - -//----------------------------------------------------------------------------- -// [SECTION] Heatmap functions -//----------------------------------------------------------------------------- - -/** - * @brief Set heatmap data - * - * Sets the data of the heatmap with the given plot ID. - * - * @param plotID ID of the heatmap to update. This ID is unique, but it is not - * continuous nor always positive. - * @param values Data of the heatmap to be set.`values[0]` corresponds with the - * top-left corner of the data. - * @param rows Number of rows of this heatmap - * @param cols Number of columns of this heatmap - */ -IMPLOT_API void SetHeatmapData(int plotID, const ImS8* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const ImU8*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const ImU8* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const ImS16*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const ImS16* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const ImU16*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const ImU16* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const ImS32*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const ImS32* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const ImU32*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const ImU32* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const float*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const float* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const double*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const double* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const ImS64*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const ImS64* values, int rows, int cols); - -/** @copydoc SetHeatmapData(int,const ImU64*,int,int) */ -IMPLOT_API void SetHeatmapData(int plotID, const ImU64* values, int rows, int cols); - -/** - * @brief Render heatmap - * - * Renders the heatmap by submitting the appropriate commands to the current - * draw list. - * - * @param plotID ID of the heatmap to be rendered - * @param DrawList Draw list where to submit the render commands - * @param bounds_min Minimum bounds of the heatmap (without clipping) - * @param bounds_max Maximum bounds of the heatmap (without clipping) - * @param scale_min Minimum value of the heatmap - * @param scale_max Maximum value of the heatmap - * @param colormap Colormap to be used when rendering this heatmap - * - * @note There might be values greater than `scale_max` or lower than `scale_min`. - * The shader used for rendering should clamp this values appropriately. - */ -IMPLOT_API void RenderHeatmap( - int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, - float scale_min, float scale_max, ImPlotColormap colormap); - -} -} - -namespace ImPlot { -namespace Backend { - #ifndef IMPLOT_BACKEND_ENABLED + inline void* CreateContext() { return nullptr; } + inline void DestroyContext() {} -inline void* CreateContext() { return nullptr; } -inline void DestroyContext() {} - -inline void BustPlotCache() {} -inline void BustItemCache() {} - + inline void BustPlotCache() {} + inline void BustItemCache() {} #endif #ifndef IMPLOT_BACKEND_HAS_COLORMAP - -inline void AddColormap(const ImU32*, int, bool) {} - + inline void AddColormap(const ImU32*, int, bool) {} #endif } diff --git a/backends/implot_opengl3.cpp b/backends/implot_impl_opengl3.cpp similarity index 82% rename from backends/implot_opengl3.cpp rename to backends/implot_impl_opengl3.cpp index ec01181..7809957 100644 --- a/backends/implot_opengl3.cpp +++ b/backends/implot_impl_opengl3.cpp @@ -3,6 +3,7 @@ #include "../implot.h" #include "../implot_internal.h" #include "implot_backend.h" +#include "implot_impl_opengl3.h" #if defined(IMGUI_IMPL_OPENGL_ES2) #include @@ -63,7 +64,7 @@ struct ContextData ImVector HeatmapDataList; ///< Array of heatmap data ImVector ColormapIDs; ///< Texture IDs of the colormap textures - ImGuiStorage PlotIDs; ///< PlotID <-> Heatmap array index table + ImGuiStorage ItemIDs; ///< PlotID <-> Heatmap array index table ImVector temp1; ///< Temporary data ImVector temp2; ///< Temporary data @@ -77,7 +78,7 @@ void* CreateContext() void DestroyContext() { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); for(const HeatmapData& data : Context.HeatmapDataList) glDeleteTextures(1, &data.HeatmapTexID); @@ -89,7 +90,7 @@ void DestroyContext() glDeleteProgram(Context.ShaderFloat.ID); Context.HeatmapDataList.clear(); - Context.PlotIDs.Clear(); + Context.ItemIDs.Clear(); } #define HEATMAP_VERTEX_SHADER_CODE \ @@ -157,7 +158,7 @@ static void CompileShader(HeatmapShader& ShaderProgram, GLchar* VertexShaderCode static void CreateHeatmapShader(const ImDrawList*, const ImDrawCmd*) { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&Context.ImGuiShader); @@ -185,10 +186,10 @@ static void CreateHeatmapShader(const ImDrawList*, const ImDrawCmd*) static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd) { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); - int plotID = (int)(intptr_t)cmd->UserCallbackData; - int plotIdx = Context.PlotIDs.GetInt(plotID, -1); + int itemID = (int)(intptr_t)cmd->UserCallbackData; + int plotIdx = Context.ItemIDs.GetInt(itemID, -1); HeatmapData& data = Context.HeatmapDataList[plotIdx]; // Get projection matrix of current shader @@ -211,15 +212,15 @@ static void ResetState(const ImDrawList*, const ImDrawCmd*) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); glUseProgram(Context.ImGuiShader); } -static void SetTextureData(int plotID, const void* data, GLsizei rows, GLsizei cols, GLint internalFormat, GLenum format, GLenum type) +static void SetTextureData(int itemID, const void* data, GLsizei rows, GLsizei cols, GLint internalFormat, GLenum format, GLenum type) { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); - int idx = Context.PlotIDs.GetInt(plotID, -1); + int idx = Context.ItemIDs.GetInt(itemID, -1); GLuint texID = Context.HeatmapDataList[idx].HeatmapTexID; Context.HeatmapDataList[idx].ShaderProgram = (type == GL_FLOAT ? &Context.ShaderFloat : &Context.ShaderInt); @@ -240,7 +241,7 @@ void AddColormap(const ImU32* keys, int count, bool qual) glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, qual ? GL_NEAREST : GL_LINEAR); glBindTexture(GL_TEXTURE_1D, 0); - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); Context.ColormapIDs.push_back(textureID); } @@ -256,17 +257,17 @@ static GLuint CreateHeatmapTexture() return textureID; } -void SetHeatmapData(int plotID, const ImS8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R8I, GL_RED_INTEGER, GL_BYTE); } -void SetHeatmapData(int plotID, const ImU8* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE); } -void SetHeatmapData(int plotID, const ImS16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R16I, GL_RED_INTEGER, GL_SHORT); } -void SetHeatmapData(int plotID, const ImU16* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT); } -void SetHeatmapData(int plotID, const ImS32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R32I, GL_RED_INTEGER, GL_INT); } -void SetHeatmapData(int plotID, const ImU32* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT); } -void SetHeatmapData(int plotID, const float* values, int rows, int cols) { SetTextureData(plotID, values, rows, cols, GL_R32F, GL_RED, GL_FLOAT); } +void SetHeatmapData(int itemID, const ImS8* values, int rows, int cols) { SetTextureData(itemID, values, rows, cols, GL_R8I, GL_RED_INTEGER, GL_BYTE); } +void SetHeatmapData(int itemID, const ImU8* values, int rows, int cols) { SetTextureData(itemID, values, rows, cols, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE); } +void SetHeatmapData(int itemID, const ImS16* values, int rows, int cols) { SetTextureData(itemID, values, rows, cols, GL_R16I, GL_RED_INTEGER, GL_SHORT); } +void SetHeatmapData(int itemID, const ImU16* values, int rows, int cols) { SetTextureData(itemID, values, rows, cols, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT); } +void SetHeatmapData(int itemID, const ImS32* values, int rows, int cols) { SetTextureData(itemID, values, rows, cols, GL_R32I, GL_RED_INTEGER, GL_INT); } +void SetHeatmapData(int itemID, const ImU32* values, int rows, int cols) { SetTextureData(itemID, values, rows, cols, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT); } +void SetHeatmapData(int itemID, const float* values, int rows, int cols) { SetTextureData(itemID, values, rows, cols, GL_R32F, GL_RED, GL_FLOAT); } -void SetHeatmapData(int plotID, const double* values, int rows, int cols) +void SetHeatmapData(int itemID, const double* values, int rows, int cols) { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); if(Context.temp1.Size < rows * cols) Context.temp1.resize(rows * cols); @@ -274,12 +275,12 @@ void SetHeatmapData(int plotID, const double* values, int rows, int cols) for(int i = 0; i < rows*cols; i++) Context.temp1[i] = (float)values[i]; - SetTextureData(plotID, Context.temp1.Data, rows, cols, GL_R32F, GL_RED, GL_FLOAT); + SetTextureData(itemID, Context.temp1.Data, rows, cols, GL_R32F, GL_RED, GL_FLOAT); } -void SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) +void SetHeatmapData(int itemID, const ImS64* values, int rows, int cols) { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); if(Context.temp2.Size < rows * cols) Context.temp2.resize(rows * cols); @@ -287,12 +288,12 @@ void SetHeatmapData(int plotID, const ImS64* values, int rows, int cols) for(int i = 0; i < rows*cols; i++) Context.temp2[i] = (ImS32)values[i]; - SetTextureData(plotID, Context.temp2.Data, rows, cols, GL_R32I, GL_RED_INTEGER, GL_INT); + SetTextureData(itemID, Context.temp2.Data, rows, cols, GL_R32I, GL_RED_INTEGER, GL_INT); } -void SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) +void SetHeatmapData(int itemID, const ImU64* values, int rows, int cols) { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); if(Context.temp3.Size < rows * cols) Context.temp3.resize(rows * cols); @@ -300,18 +301,18 @@ void SetHeatmapData(int plotID, const ImU64* values, int rows, int cols) for(int i = 0; i < rows*cols; i++) Context.temp3[i] = (ImU32)values[i]; - SetTextureData(plotID, Context.temp3.Data, rows, cols, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT); + SetTextureData(itemID, Context.temp3.Data, rows, cols, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT); } -void RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) +void RenderHeatmap(int itemID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); - int idx = Context.PlotIDs.GetInt(plotID, Context.HeatmapDataList.Size); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + int idx = Context.ItemIDs.GetInt(itemID, Context.HeatmapDataList.Size); if(idx == Context.HeatmapDataList.Size) { // New entry - Context.PlotIDs.SetInt(plotID, Context.HeatmapDataList.Size); + Context.ItemIDs.SetInt(itemID, Context.HeatmapDataList.Size); Context.HeatmapDataList.push_back(HeatmapData()); Context.HeatmapDataList[idx].HeatmapTexID = CreateHeatmapTexture(); } @@ -324,7 +325,7 @@ void RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, c if(Context.ShaderInt.ID == 0 || Context.ShaderFloat.ID == 0) DrawList.AddCallback(CreateHeatmapShader, nullptr); - DrawList.AddCallback(RenderCallback, (void*)(intptr_t)plotID); + DrawList.AddCallback(RenderCallback, (void*)(intptr_t)itemID); DrawList.PrimReserve(6, 4); DrawList.PrimRectUV(bounds_min, bounds_max, ImVec2(0.0f, 1.0f), ImVec2(1.0f, 0.0f), 0); DrawList.AddCallback(ResetState, nullptr); @@ -332,13 +333,13 @@ void RenderHeatmap(int plotID, ImDrawList& DrawList, const ImVec2& bounds_min, c void BustPlotCache() { - ContextData& Context = *((ContextData*)GImPlot->backendCtx); + ContextData& Context = *((ContextData*)GImPlot->backendCtx); for(const HeatmapData& data : Context.HeatmapDataList) glDeleteTextures(1, &data.HeatmapTexID); Context.HeatmapDataList.clear(); - Context.PlotIDs.Clear(); + Context.ItemIDs.Clear(); } void BustItemCache() {} diff --git a/backends/implot_impl_opengl3.h b/backends/implot_impl_opengl3.h new file mode 100644 index 0000000..844e659 --- /dev/null +++ b/backends/implot_impl_opengl3.h @@ -0,0 +1,194 @@ +// MIT License + +// Copyright (c) 2021 Evan Pezent + +// 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. + +// ImPlot v0.10 WIP + +#pragma once + +#define IMPLOT_BACKEND_ENABLED +#define IMPLOT_BACKEND_HAS_HEATMAP +#define IMPLOT_BACKEND_HAS_COLORMAP + +#if !defined(IMGUI_IMPL_OPENGL_ES2) \ + && !defined(IMGUI_IMPL_OPENGL_ES3) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) + +// Try to detect GLES on matching platforms +#if defined(__APPLE__) +#include "TargetConditionals.h" +#endif +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) +#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" +#elif defined(__EMSCRIPTEN__) +#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" + +// Otherwise try to detect supported Desktop OpenGL loaders.. +#elif defined(__has_include) +#if __has_include() + #define IMGUI_IMPL_OPENGL_LOADER_GLEW +#elif __has_include() + #define IMGUI_IMPL_OPENGL_LOADER_GLAD +#elif __has_include() + #define IMGUI_IMPL_OPENGL_LOADER_GLAD2 +#elif __has_include() + #define IMGUI_IMPL_OPENGL_LOADER_GL3W +#elif __has_include() + #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3 +#elif __has_include() + #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2 +#else + #error "Cannot detect OpenGL loader!" +#endif +#else + #define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository +#endif + +#endif + +namespace ImPlot { +namespace Backend { + +//----------------------------------------------------------------------------- +// [SECTION] Misc backend functions +//----------------------------------------------------------------------------- + +/** + * @brief Struct to hold backend-related context data + * + * A backend may store in this struct any data it needs, with no constraints. A + * pointer to this struct will be stored inside ImPlot's context and can be + * accessed at any time. This pointer will be set to the returned value of @ref + * CreateContext(). All resources held by this struct must be freed inside @ref + * DestroyContext(). + */ +struct ContextData; + +/** + * @brief Create backend context + * + * Creates and intializes the backend context. The returned pointer will be saved + * in ImPlot's context and can be accessed later. + */ +IMPLOT_API void* CreateContext(); + +/** + * @brief Destroy backend context + * + * Destroys and frees any memory or resources needed by the backend. After this + * call returns, no more calls to any backend function can be performed. + */ +IMPLOT_API void DestroyContext(); + +/** @brief Bust plot cache. Called from @ref ImPlot::BustPlotCache() */ +IMPLOT_API void BustPlotCache(); + +/** @brief Bust item cache. Called from @ref ImPlot::BustItemCache() */ +IMPLOT_API void BustItemCache(); + +//----------------------------------------------------------------------------- +// [SECTION] Colormap functions +//----------------------------------------------------------------------------- + +/** + * @brief Add a colormap + * + * Adds a colormap to be handled by the backend. + * + * @param keys Colors for this colormap, in RGBA format + * @param count Number of colors in this colormap + * @param qual Qualitative: whether the colormap is continuous (`false`) or + * not (`true`) + */ +IMPLOT_API void AddColormap(const ImU32* keys, int count, bool qual); + +//----------------------------------------------------------------------------- +// [SECTION] Heatmap functions +//----------------------------------------------------------------------------- + +/** + * @brief Set heatmap data + * + * Sets the data of the heatmap with the given plot ID. + * + * @param itemID ID of the heatmap to update. + * @param values Data of the heatmap to be set.`values[0]` corresponds with the + * top-left corner of the data. + * @param rows Number of rows of this heatmap + * @param cols Number of columns of this heatmap + */ +IMPLOT_API void SetHeatmapData(int itemID, const ImS8* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImU8*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const ImU8* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImS16*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const ImS16* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImU16*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const ImU16* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImS32*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const ImS32* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImU32*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const ImU32* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const float*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const float* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const double*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const double* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImS64*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const ImS64* values, int rows, int cols); + +/** @copydoc SetHeatmapData(int,const ImU64*,int,int) */ +IMPLOT_API void SetHeatmapData(int itemID, const ImU64* values, int rows, int cols); + +/** + * @brief Render heatmap + * + * Renders the heatmap using OpenGL acceleration + * + * @param itemID ID of the heatmap to be rendered + * @param DrawList Draw list where to submit the render commands + * @param bounds_min Minimum bounds of the heatmap (without clipping) + * @param bounds_max Maximum bounds of the heatmap (without clipping) + * @param scale_min Minimum value of the heatmap + * @param scale_max Maximum value of the heatmap + * @param colormap Colormap to be used when rendering this heatmap + * + * @note There might be values greater than `scale_max` or lower than `scale_min`. + * The shader used for rendering will clamp this values. + */ +IMPLOT_API void RenderHeatmap( + int itemID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, + float scale_min, float scale_max, ImPlotColormap colormap); + +} +} From 5f11a7875dea56b454a67bc35ea230e1031acb35 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Thu, 24 Jun 2021 17:32:54 +0200 Subject: [PATCH 10/13] Added logarithmic X & Y axes --- backends/implot_impl_opengl3.cpp | 86 +++++++++++++++++++++++--------- backends/implot_impl_opengl3.h | 14 ++++++ implot_items.cpp | 13 ++--- 3 files changed, 84 insertions(+), 29 deletions(-) diff --git a/backends/implot_impl_opengl3.cpp b/backends/implot_impl_opengl3.cpp index 7809957..9eb7e84 100644 --- a/backends/implot_impl_opengl3.cpp +++ b/backends/implot_impl_opengl3.cpp @@ -43,6 +43,9 @@ struct HeatmapShader GLuint AttribLocationProjection = 0; ///< Attribute location for the projection matrix uniform GLuint AttribLocationMinValue = 0; ///< Attribute location for the minimum value uniform GLuint AttribLocationMaxValue = 0; ///< Attribute location for the maximum value uniform + GLuint AttribLocationAxisLog = 0; ///< Attribute location for the logarithmic axes uniform + GLuint AttribLocationMinBounds = 0; ///< Attribute location for the minimum bounds uniform + GLuint AttribLocationMaxBounds = 0; ///< Attribute location for the maximum bounds uniform }; struct HeatmapData @@ -50,8 +53,12 @@ struct HeatmapData HeatmapShader* ShaderProgram; ///< Shader to be used by this heatmap (either ShaderInt or ShaderFloat) GLuint HeatmapTexID; ///< Texture ID of the heatmap 2D texture GLuint ColormapTexID; ///< Texture ID of the colormap 1D texture + ImVec2 MinBounds; ///< Minimum bounds of the heatmap + ImVec2 MaxBounds; ///< Maximum bounds of the heatmap float MinValue; ///< Minimum value of the colormap float MaxValue; ///< Maximum value of the colormap + bool AxisLogX; ///< Whether the X axis is logarithmic or not + bool AxisLogY; ///< Whether the Y axis is logarithmic or not }; struct ContextData @@ -108,23 +115,38 @@ void DestroyContext() " gl_Position = ProjMtx * vec4(Position.xy, 0.0f, 1.0f);\n" \ "}\n" -#define HEATMAP_FRAGMENT_SHADER_CODE \ - "#version 330 core\n" \ - "precision mediump float;\n" \ - "\n" \ - "in vec2 Frag_UV;\n" \ - "out vec4 Out_Color;\n" \ - "\n" \ - "uniform sampler1D colormap;\n" \ - "uniform %csampler2D heatmap;\n" \ - "uniform float min_val;\n" \ - "uniform float max_val;\n" \ - "\n" \ - "void main()\n" \ - "{\n" \ - " float value = float(texture(heatmap, Frag_UV).r);\n" \ - " float offset = (value - min_val) / (max_val - min_val);\n" \ - " Out_Color = texture(colormap, clamp(offset, 0.0f, 1.0f));\n" \ +#define HEATMAP_FRAGMENT_SHADER_CODE \ + "#version 330 core\n" \ + "precision mediump float;\n" \ + "\n" \ + "in vec2 Frag_UV;\n" \ + "out vec4 Out_Color;\n" \ + "\n" \ + "uniform sampler1D colormap;\n" \ + "uniform %csampler2D heatmap;\n" \ + "uniform float min_val;\n" \ + "uniform float max_val;\n" \ + "\n" \ + "uniform vec2 bounds_min;\n" \ + "uniform vec2 bounds_max;\n" \ + "uniform bvec2 ax_log;\n" \ + "\n" \ + "float log_den(float x, float min_rng, float max_rng)\n" \ + "{\n" \ + " float minrl = log(min_rng);\n" \ + " float maxrl = log(max_rng);\n" \ + "\n" \ + " return (exp((maxrl - minrl) * x + minrl) - min_rng) / (max_rng - min_rng);" \ + "}\n" \ + "\n" \ + "void main()\n" \ + "{\n" \ + " float uv_x = ax_log.x ? log_den(Frag_UV.x, bounds_min.x, bounds_max.x) : Frag_UV.x;\n" \ + " float uv_y = ax_log.y ? log_den(Frag_UV.y, bounds_min.y, bounds_max.y) : Frag_UV.y;\n" \ + "\n" \ + " float value = float(texture(heatmap, vec2(uv_x, uv_y)).r);\n" \ + " float offset = (value - min_val) / (max_val - min_val);\n" \ + " Out_Color = texture(colormap, clamp(offset, 0.0f, 1.0f));\n" \ "}\n" static void CompileShader(HeatmapShader& ShaderProgram, GLchar* VertexShaderCode, GLchar* FragmentShaderCode) @@ -150,6 +172,9 @@ static void CompileShader(HeatmapShader& ShaderProgram, GLchar* VertexShaderCode ShaderProgram.AttribLocationProjection = glGetUniformLocation(ShaderProgram.ID, "ProjMtx"); ShaderProgram.AttribLocationMinValue = glGetUniformLocation(ShaderProgram.ID, "min_val"); ShaderProgram.AttribLocationMaxValue = glGetUniformLocation(ShaderProgram.ID, "max_val"); + ShaderProgram.AttribLocationMinBounds = glGetUniformLocation(ShaderProgram.ID, "bounds_min"); + ShaderProgram.AttribLocationMaxBounds = glGetUniformLocation(ShaderProgram.ID, "bounds_max"); + ShaderProgram.AttribLocationAxisLog = glGetUniformLocation(ShaderProgram.ID, "ax_log"); glUseProgram(ShaderProgram.ID); glUniform1i(glGetUniformLocation(ShaderProgram.ID, "heatmap"), 0); // Set texture slot of heatmap texture @@ -166,16 +191,16 @@ static void CreateHeatmapShader(const ImDrawList*, const ImDrawCmd*) GLuint AttribLocationVtxPos = (GLuint)glGetAttribLocation(Context.ImGuiShader, "Position"); GLuint AttribLocationVtxUV = (GLuint)glGetAttribLocation(Context.ImGuiShader, "UV"); - GLchar* VertexShaderCode = new GLchar[512]; - GLchar* FragmentShaderCode = new GLchar[512]; + GLchar* VertexShaderCode = new GLchar[1000]; + GLchar* FragmentShaderCode = new GLchar[1000]; - snprintf(VertexShaderCode, 512, HEATMAP_VERTEX_SHADER_CODE, AttribLocationVtxPos, AttribLocationVtxUV); - snprintf(FragmentShaderCode, 512, HEATMAP_FRAGMENT_SHADER_CODE, ' '); + snprintf(VertexShaderCode, 1000, HEATMAP_VERTEX_SHADER_CODE, AttribLocationVtxPos, AttribLocationVtxUV); + snprintf(FragmentShaderCode, 1000, HEATMAP_FRAGMENT_SHADER_CODE, ' '); CompileShader(Context.ShaderFloat, VertexShaderCode, FragmentShaderCode); - snprintf(VertexShaderCode, 512, HEATMAP_VERTEX_SHADER_CODE, AttribLocationVtxPos, AttribLocationVtxUV); - snprintf(FragmentShaderCode, 512, HEATMAP_FRAGMENT_SHADER_CODE, 'i'); + snprintf(VertexShaderCode, 1000, HEATMAP_VERTEX_SHADER_CODE, AttribLocationVtxPos, AttribLocationVtxUV); + snprintf(FragmentShaderCode, 1000, HEATMAP_FRAGMENT_SHADER_CODE, 'i'); CompileShader(Context.ShaderInt, VertexShaderCode, FragmentShaderCode); glUseProgram(0); @@ -205,6 +230,9 @@ static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd) glUniformMatrix4fv(data.ShaderProgram->AttribLocationProjection, 1, GL_FALSE, &OrthoProjection[0][0]); glUniform1f(data.ShaderProgram->AttribLocationMinValue, data.MinValue); // Set minimum range glUniform1f(data.ShaderProgram->AttribLocationMaxValue, data.MaxValue); // Set maximum range + glUniform2i(data.ShaderProgram->AttribLocationAxisLog, data.AxisLogX, data.AxisLogY); // Logarithmic axis + glUniform2f(data.ShaderProgram->AttribLocationMinBounds, data.MinBounds.x, data.MinBounds.y); // Set minimum bounds + glUniform2f(data.ShaderProgram->AttribLocationMaxBounds, data.MaxBounds.x, data.MaxBounds.y); // Set maximum bounds } static void ResetState(const ImDrawList*, const ImDrawCmd*) @@ -304,6 +332,18 @@ void SetHeatmapData(int itemID, const ImU64* values, int rows, int cols) SetTextureData(itemID, Context.temp3.Data, rows, cols, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT); } +void SetAxisLog(int itemID, bool x_is_log, bool y_is_log, const ImVec2& bounds_min, const ImVec2& bounds_max) +{ + ContextData& Context = *((ContextData*)GImPlot->backendCtx); + int idx = Context.ItemIDs.GetInt(itemID, -1); + HeatmapData& data = Context.HeatmapDataList[idx]; + + data.AxisLogX = x_is_log; + data.AxisLogY =y_is_log; + data.MinBounds = bounds_min; + data.MaxBounds = bounds_max; +} + void RenderHeatmap(int itemID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) { ContextData& Context = *((ContextData*)GImPlot->backendCtx); diff --git a/backends/implot_impl_opengl3.h b/backends/implot_impl_opengl3.h index 844e659..8efbd52 100644 --- a/backends/implot_impl_opengl3.h +++ b/backends/implot_impl_opengl3.h @@ -190,5 +190,19 @@ IMPLOT_API void RenderHeatmap( int itemID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap); +/** + * @brief Set logarithmic axis + * + * Sets whether the X and Y axis are logarithmic or not, and their bounds. This + * function only has to be called if either the axis change state or if the bounds + * change. + * + * @param x_is_log Whether the X axis is logarithmic or not + * @param y_is_log Whether the Y axis is logarithmic or not + * @param bounds_min Minimum bounds (for X & Y) of the heatmap + * @param bounds_min Maximum bounds (for X & Y) of the heatmap + */ +void SetAxisLog(int itemID, bool x_is_log, bool y_is_log, const ImVec2& bounds_min, const ImVec2& bounds_max); + } } diff --git a/implot_items.cpp b/implot_items.cpp index 1c18f9d..7441066 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1896,17 +1896,18 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value ImVec2 bmin = transformer(bounds_min); ImVec2 bmax = transformer(bounds_max); + ImPlotScale scale = GetCurrentScale(); + // NOTE: Order is important! Backend::RenderHeatmap(gp.CurrentItem->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap); + Backend::SetAxisLog(gp.CurrentItem->ID, + scale == ImPlotScale_LogLin || scale == ImPlotScale_LogLog, + scale == ImPlotScale_LinLog || scale == ImPlotScale_LogLog, + ImVec2(bounds_min.x, bounds_min.y), ImVec2(bounds_max.x, bounds_max.y)); Backend::SetHeatmapData(gp.CurrentItem->ID, values, rows, cols); #else GetterHeatmap getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); - switch (GetCurrentScale()) { - case ImPlotScale_LinLin: RenderPrimitives(RectRenderer, TransformerLinLin>(getter, TransformerLinLin()), DrawList, gp.CurrentPlot->PlotRect); break; - case ImPlotScale_LogLin: RenderPrimitives(RectRenderer, TransformerLogLin>(getter, TransformerLogLin()), DrawList, gp.CurrentPlot->PlotRect); break; - case ImPlotScale_LinLog: RenderPrimitives(RectRenderer, TransformerLinLog>(getter, TransformerLinLog()), DrawList, gp.CurrentPlot->PlotRect); break; - case ImPlotScale_LogLog: RenderPrimitives(RectRenderer, TransformerLogLog>(getter, TransformerLogLog()), DrawList, gp.CurrentPlot->PlotRect); break; - } + RenderPrimitives(RectRenderer, Transformer>(getter, transformer), DrawList, gp.CurrentPlot->PlotRect); #endif if (fmt != NULL) { const double w = (bounds_max.x - bounds_min.x) / cols; From bef982f0b627d0641881a3c98f7c9062bd5962dd Mon Sep 17 00:00:00 2001 From: marcizhu Date: Fri, 25 Jun 2021 11:52:38 +0200 Subject: [PATCH 11/13] Fix vertically-flipped 2D histograms --- backends/implot_impl_opengl3.cpp | 12 ++++++------ backends/implot_impl_opengl3.h | 4 ++-- implot_items.cpp | 5 ++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/backends/implot_impl_opengl3.cpp b/backends/implot_impl_opengl3.cpp index 9eb7e84..d572ead 100644 --- a/backends/implot_impl_opengl3.cpp +++ b/backends/implot_impl_opengl3.cpp @@ -53,8 +53,8 @@ struct HeatmapData HeatmapShader* ShaderProgram; ///< Shader to be used by this heatmap (either ShaderInt or ShaderFloat) GLuint HeatmapTexID; ///< Texture ID of the heatmap 2D texture GLuint ColormapTexID; ///< Texture ID of the colormap 1D texture - ImVec2 MinBounds; ///< Minimum bounds of the heatmap - ImVec2 MaxBounds; ///< Maximum bounds of the heatmap + ImPlotPoint MinBounds; ///< Minimum bounds of the heatmap + ImPlotPoint MaxBounds; ///< Maximum bounds of the heatmap float MinValue; ///< Minimum value of the colormap float MaxValue; ///< Maximum value of the colormap bool AxisLogX; ///< Whether the X axis is logarithmic or not @@ -332,19 +332,19 @@ void SetHeatmapData(int itemID, const ImU64* values, int rows, int cols) SetTextureData(itemID, Context.temp3.Data, rows, cols, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT); } -void SetAxisLog(int itemID, bool x_is_log, bool y_is_log, const ImVec2& bounds_min, const ImVec2& bounds_max) +void SetAxisLog(int itemID, bool x_is_log, bool y_is_log, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { ContextData& Context = *((ContextData*)GImPlot->backendCtx); int idx = Context.ItemIDs.GetInt(itemID, -1); HeatmapData& data = Context.HeatmapDataList[idx]; data.AxisLogX = x_is_log; - data.AxisLogY =y_is_log; + data.AxisLogY = y_is_log; data.MinBounds = bounds_min; data.MaxBounds = bounds_max; } -void RenderHeatmap(int itemID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap) +void RenderHeatmap(int itemID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, float scale_min, float scale_max, ImPlotColormap colormap, bool reverse_y) { ContextData& Context = *((ContextData*)GImPlot->backendCtx); int idx = Context.ItemIDs.GetInt(itemID, Context.HeatmapDataList.Size); @@ -367,7 +367,7 @@ void RenderHeatmap(int itemID, ImDrawList& DrawList, const ImVec2& bounds_min, c DrawList.AddCallback(RenderCallback, (void*)(intptr_t)itemID); DrawList.PrimReserve(6, 4); - DrawList.PrimRectUV(bounds_min, bounds_max, ImVec2(0.0f, 1.0f), ImVec2(1.0f, 0.0f), 0); + DrawList.PrimRectUV(bounds_min, bounds_max, ImVec2(0.0f, reverse_y ? 1.0f : 0.0f), ImVec2(1.0f, reverse_y ? 0.0f : 1.0f), 0); DrawList.AddCallback(ResetState, nullptr); } diff --git a/backends/implot_impl_opengl3.h b/backends/implot_impl_opengl3.h index 8efbd52..34695cc 100644 --- a/backends/implot_impl_opengl3.h +++ b/backends/implot_impl_opengl3.h @@ -188,7 +188,7 @@ IMPLOT_API void SetHeatmapData(int itemID, const ImU64* values, int rows, int c */ IMPLOT_API void RenderHeatmap( int itemID, ImDrawList& DrawList, const ImVec2& bounds_min, const ImVec2& bounds_max, - float scale_min, float scale_max, ImPlotColormap colormap); + float scale_min, float scale_max, ImPlotColormap colormap, bool reverse_y); /** * @brief Set logarithmic axis @@ -202,7 +202,7 @@ IMPLOT_API void RenderHeatmap( * @param bounds_min Minimum bounds (for X & Y) of the heatmap * @param bounds_min Maximum bounds (for X & Y) of the heatmap */ -void SetAxisLog(int itemID, bool x_is_log, bool y_is_log, const ImVec2& bounds_min, const ImVec2& bounds_max); +void SetAxisLog(int itemID, bool x_is_log, bool y_is_log, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max); } } diff --git a/implot_items.cpp b/implot_items.cpp index 7441066..c6d081a 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1895,15 +1895,14 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value #ifdef IMPLOT_BACKEND_HAS_HEATMAP ImVec2 bmin = transformer(bounds_min); ImVec2 bmax = transformer(bounds_max); - ImPlotScale scale = GetCurrentScale(); // NOTE: Order is important! - Backend::RenderHeatmap(gp.CurrentItem->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap); + Backend::RenderHeatmap(gp.CurrentItem->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap, reverse_y); Backend::SetAxisLog(gp.CurrentItem->ID, scale == ImPlotScale_LogLin || scale == ImPlotScale_LogLog, scale == ImPlotScale_LinLog || scale == ImPlotScale_LogLog, - ImVec2(bounds_min.x, bounds_min.y), ImVec2(bounds_max.x, bounds_max.y)); + bounds_min, bounds_max); Backend::SetHeatmapData(gp.CurrentItem->ID, values, rows, cols); #else GetterHeatmap getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); From 72cf0243475ecd0a1580afa01b51a61e8918a262 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Sun, 27 Jun 2021 12:12:07 -0700 Subject: [PATCH 12/13] meh --- implot_items.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 7441066..928526b 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1899,11 +1899,11 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value ImPlotScale scale = GetCurrentScale(); // NOTE: Order is important! - Backend::RenderHeatmap(gp.CurrentItem->ID, DrawList, bmin, bmax, scale_min, scale_max, gp.Style.Colormap); + Backend::RenderHeatmap(gp.CurrentItem->ID, DrawList, bmin, bmax, (float)scale_min, (float)scale_max, gp.Style.Colormap); Backend::SetAxisLog(gp.CurrentItem->ID, scale == ImPlotScale_LogLin || scale == ImPlotScale_LogLog, scale == ImPlotScale_LinLog || scale == ImPlotScale_LogLog, - ImVec2(bounds_min.x, bounds_min.y), ImVec2(bounds_max.x, bounds_max.y)); + ImVec2((float)bounds_min.x, (float)bounds_min.y), ImVec2((float)bounds_max.x, (float)bounds_max.y)); Backend::SetHeatmapData(gp.CurrentItem->ID, values, rows, cols); #else GetterHeatmap getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); From 353e8ed0aa79a13a1500777bbbeac457fb5bc8a2 Mon Sep 17 00:00:00 2001 From: marcizhu Date: Fri, 2 Jul 2021 23:01:31 +0200 Subject: [PATCH 13/13] Fix incorrect colormap colors --- backends/implot_impl_opengl3.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/backends/implot_impl_opengl3.cpp b/backends/implot_impl_opengl3.cpp index d572ead..e70bb58 100644 --- a/backends/implot_impl_opengl3.cpp +++ b/backends/implot_impl_opengl3.cpp @@ -111,8 +111,8 @@ void DestroyContext() "\n" \ "void main()\n" \ "{\n" \ - " Frag_UV = UV;\n" \ - " gl_Position = ProjMtx * vec4(Position.xy, 0.0f, 1.0f);\n" \ + " Frag_UV = UV;\n" \ + " gl_Position = ProjMtx * vec4(Position.xy, 0.0f, 1.0f);\n" \ "}\n" #define HEATMAP_FRAGMENT_SHADER_CODE \ @@ -141,12 +141,14 @@ void DestroyContext() "\n" \ "void main()\n" \ "{\n" \ + " float min_tex_offs = 0.5 / float(textureSize(colormap, 0));\n" \ " float uv_x = ax_log.x ? log_den(Frag_UV.x, bounds_min.x, bounds_max.x) : Frag_UV.x;\n" \ " float uv_y = ax_log.y ? log_den(Frag_UV.y, bounds_min.y, bounds_max.y) : Frag_UV.y;\n" \ "\n" \ " float value = float(texture(heatmap, vec2(uv_x, uv_y)).r);\n" \ - " float offset = (value - min_val) / (max_val - min_val);\n" \ - " Out_Color = texture(colormap, clamp(offset, 0.0f, 1.0f));\n" \ + " float offset = (value - min_val) / (max_val - min_val);\n" \ + " offset = mix(min_tex_offs, 1.0 - min_tex_offs, clamp(offset, 0.0f, 1.0f));\n" \ + " Out_Color = texture(colormap, offset);\n" \ "}\n" static void CompileShader(HeatmapShader& ShaderProgram, GLchar* VertexShaderCode, GLchar* FragmentShaderCode) @@ -262,7 +264,7 @@ void AddColormap(const ImU32* keys, int count, bool qual) GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_1D, textureID); - glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, count, 0, GL_RGBA, GL_UNSIGNED_BYTE, keys); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, count, 0, GL_RGBA, GL_UNSIGNED_BYTE, keys); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, qual ? GL_NEAREST : GL_LINEAR);