1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-11-26 20:28:50 -05:00

Add GPU-accelerated heatmaps (#254)

* Add GPU-accelerated heatmaps

* Add BustPlotCache() and other small changes

* Fixupside-down heatmaps

* Prevent new/delete on each call to PlotHeatmap() on certain cases

* Add common interface for backends & move context to ImPlotContext

* Fix integer heatmaps not working properly

* 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

* Cleanup & maintenance

- Removed unnecessary attributes from shader
- Renamed functions & structs
- Reduced memory footprint of OpenGL backend context

* Standandardize backends for future implementations

* Added logarithmic X & Y axes

* Fix vertically-flipped 2D histograms

* Fix incorrect colormap colors
This commit is contained in:
Marc 2021-07-03 06:29:37 +02:00 committed by GitHub
parent 65aa2c8264
commit 48501a3c10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 675 additions and 8 deletions

49
backends/implot_backend.h Normal file
View File

@ -0,0 +1,49 @@
// 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
#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 {
#ifndef IMPLOT_BACKEND_ENABLED
inline void* CreateContext() { return nullptr; }
inline void DestroyContext() {}
inline void BustPlotCache() {}
inline void BustItemCache() {}
#endif
#ifndef IMPLOT_BACKEND_HAS_COLORMAP
inline void AddColormap(const ImU32*, int, bool) {}
#endif
}
}

View File

@ -0,0 +1,392 @@
#ifdef IMPLOT_BACKEND_ENABLE_OPENGL3
#include "../implot.h"
#include "../implot_internal.h"
#include "implot_backend.h"
#include "implot_impl_opengl3.h"
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
// 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 <GL/gl3w.h> // Initialize with gl3wInit()
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h> // Initialize with glewInit()
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Initialize with gladLoadGL()
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
#include <glad/gl.h> // 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 <glbinding/Binding.h> // Initialize with glbinding::Binding::initialize()
#include <glbinding/gl/gl.h>
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 <glbinding/glbinding.h>// Initialize with glbinding::initialize()
#include <glbinding/gl/gl.h>
using namespace gl;
#else
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
namespace ImPlot {
namespace Backend {
struct HeatmapShader
{
GLuint ID = 0; ///< Shader ID for the heatmap shader
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
{
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
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
bool AxisLogY; ///< Whether the Y axis is logarithmic or not
};
struct ContextData
{
HeatmapShader ShaderInt; ///< Shader for integer heatmaps
HeatmapShader ShaderFloat; ///< Shader for floating-point heatmaps
GLuint AttribLocationImGuiProjection = 0; ///< Attribute location for the projection matrix uniform (ImGui default shader)
GLuint ImGuiShader = 0; ///< Shader ID of ImGui's default shader
ImVector<HeatmapData> HeatmapDataList; ///< Array of heatmap data
ImVector<GLuint> ColormapIDs; ///< Texture IDs of the colormap textures
ImGuiStorage ItemIDs; ///< PlotID <-> Heatmap array index table
ImVector<float> temp1; ///< Temporary data
ImVector<ImS32> temp2; ///< Temporary data
ImVector<ImU32> temp3; ///< Temporary data
};
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);
glDeleteProgram(Context.ShaderInt.ID);
glDeleteProgram(Context.ShaderFloat.ID);
Context.HeatmapDataList.clear();
Context.ItemIDs.Clear();
}
#define HEATMAP_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 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 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" \
" 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)
{
GLuint VertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(VertexShader, 1, &VertexShaderCode, nullptr);
glCompileShader(VertexShader);
GLuint FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(FragmentShader, 1, &FragmentShaderCode, nullptr);
glCompileShader(FragmentShader);
ShaderProgram.ID = glCreateProgram();
glAttachShader(ShaderProgram.ID, VertexShader);
glAttachShader(ShaderProgram.ID, FragmentShader);
glLinkProgram(ShaderProgram.ID);
glDetachShader(ShaderProgram.ID, VertexShader);
glDetachShader(ShaderProgram.ID, FragmentShader);
glDeleteShader(VertexShader);
glDeleteShader(FragmentShader);
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
glUniform1i(glGetUniformLocation(ShaderProgram.ID, "colormap"), 1); // Set texture slot of colormap texture
}
static void CreateHeatmapShader(const ImDrawList*, const ImDrawCmd*)
{
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&Context.ImGuiShader);
Context.AttribLocationImGuiProjection = glGetUniformLocation(Context.ImGuiShader, "ProjMtx");
GLuint AttribLocationVtxPos = (GLuint)glGetAttribLocation(Context.ImGuiShader, "Position");
GLuint AttribLocationVtxUV = (GLuint)glGetAttribLocation(Context.ImGuiShader, "UV");
GLchar* VertexShaderCode = new GLchar[1000];
GLchar* FragmentShaderCode = new GLchar[1000];
snprintf(VertexShaderCode, 1000, HEATMAP_VERTEX_SHADER_CODE, AttribLocationVtxPos, AttribLocationVtxUV);
snprintf(FragmentShaderCode, 1000, HEATMAP_FRAGMENT_SHADER_CODE, ' ');
CompileShader(Context.ShaderFloat, VertexShaderCode, FragmentShaderCode);
snprintf(VertexShaderCode, 1000, HEATMAP_VERTEX_SHADER_CODE, AttribLocationVtxPos, AttribLocationVtxUV);
snprintf(FragmentShaderCode, 1000, HEATMAP_FRAGMENT_SHADER_CODE, 'i');
CompileShader(Context.ShaderInt, VertexShaderCode, FragmentShaderCode);
glUseProgram(0);
delete[] VertexShaderCode;
delete[] FragmentShaderCode;
}
static void RenderCallback(const ImDrawList*, const ImDrawCmd* cmd)
{
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
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
float OrthoProjection[4][4];
glGetUniformfv(Context.ImGuiShader, Context.AttribLocationImGuiProjection, &OrthoProjection[0][0]);
// Enable our shader
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(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*)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
glUseProgram(Context.ImGuiShader);
}
static void SetTextureData(int itemID, const void* data, GLsizei rows, GLsizei cols, GLint internalFormat, GLenum format, GLenum type)
{
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
int idx = Context.ItemIDs.GetInt(itemID, -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, internalFormat, cols, rows, 0, format, type, data);
}
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_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);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, qual ? GL_NEAREST : GL_LINEAR);
glBindTexture(GL_TEXTURE_1D, 0);
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
Context.ColormapIDs.push_back(textureID);
}
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 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 itemID, const double* values, int rows, int cols)
{
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
if(Context.temp1.Size < rows * cols)
Context.temp1.resize(rows * cols);
for(int i = 0; i < rows*cols; i++)
Context.temp1[i] = (float)values[i];
SetTextureData(itemID, Context.temp1.Data, rows, cols, GL_R32F, GL_RED, GL_FLOAT);
}
void SetHeatmapData(int itemID, const ImS64* values, int rows, int cols)
{
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
if(Context.temp2.Size < rows * cols)
Context.temp2.resize(rows * cols);
for(int i = 0; i < rows*cols; i++)
Context.temp2[i] = (ImS32)values[i];
SetTextureData(itemID, Context.temp2.Data, rows, cols, GL_R32I, GL_RED_INTEGER, GL_INT);
}
void SetHeatmapData(int itemID, const ImU64* values, int rows, int cols)
{
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
if(Context.temp3.Size < rows * cols)
Context.temp3.resize(rows * cols);
for(int i = 0; i < rows*cols; i++)
Context.temp3[i] = (ImU32)values[i];
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 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.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, bool reverse_y)
{
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
int idx = Context.ItemIDs.GetInt(itemID, Context.HeatmapDataList.Size);
if(idx == Context.HeatmapDataList.Size)
{
// New entry
Context.ItemIDs.SetInt(itemID, Context.HeatmapDataList.Size);
Context.HeatmapDataList.push_back(HeatmapData());
Context.HeatmapDataList[idx].HeatmapTexID = CreateHeatmapTexture();
}
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)itemID);
DrawList.PrimReserve(6, 4);
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);
}
void BustPlotCache()
{
ContextData& Context = *((ContextData*)GImPlot->backendCtx);
for(const HeatmapData& data : Context.HeatmapDataList)
glDeleteTextures(1, &data.HeatmapTexID);
Context.HeatmapDataList.clear();
Context.ItemIDs.Clear();
}
void BustItemCache() {}
}
}
#endif

View File

@ -0,0 +1,208 @@
// 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(<GL/glew.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#elif __has_include(<glad/glad.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#elif __has_include(<glad/gl.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD2
#elif __has_include(<GL/gl3w.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#elif __has_include(<glbinding/glbinding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
#elif __has_include(<glbinding/Binding.h>)
#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, bool reverse_y);
/**
* @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 ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max);
}
}

View File

@ -374,13 +374,15 @@ void SetImGuiContext(ImGuiContext* ctx) {
ImPlotContext* CreateContext() { ImPlotContext* CreateContext() {
ImPlotContext* ctx = IM_NEW(ImPlotContext)(); ImPlotContext* ctx = IM_NEW(ImPlotContext)();
Initialize(ctx);
if (GImPlot == NULL) if (GImPlot == NULL)
SetCurrentContext(ctx); SetCurrentContext(ctx);
ctx->backendCtx = Backend::CreateContext();
Initialize(ctx);
return ctx; return ctx;
} }
void DestroyContext(ImPlotContext* ctx) { void DestroyContext(ImPlotContext* ctx) {
Backend::DestroyContext();
if (ctx == NULL) if (ctx == NULL)
ctx = GImPlot; ctx = GImPlot;
if (GImPlot == ctx) if (GImPlot == ctx)
@ -435,7 +437,6 @@ void Initialize(ImPlotContext* ctx) {
IMPLOT_APPEND_CMAP(PiYG, false); IMPLOT_APPEND_CMAP(PiYG, false);
IMPLOT_APPEND_CMAP(Spectral, false); IMPLOT_APPEND_CMAP(Spectral, false);
IMPLOT_APPEND_CMAP(Greys, false); IMPLOT_APPEND_CMAP(Greys, false);
} }
void Reset(ImPlotContext* ctx) { void Reset(ImPlotContext* ctx) {
@ -489,6 +490,7 @@ ImPlotPlot* GetCurrentPlot() {
void BustPlotCache() { void BustPlotCache() {
GImPlot->Plots.Clear(); GImPlot->Plots.Clear();
Backend::BustPlotCache();
} }
void PushLinkedAxis(ImPlotAxis& axis) { void PushLinkedAxis(ImPlotAxis& axis) {

View File

@ -37,6 +37,7 @@
#include <time.h> #include <time.h>
#include "imgui_internal.h" #include "imgui_internal.h"
#include "backends/implot_backend.h"
#ifndef IMPLOT_VERSION #ifndef IMPLOT_VERSION
#error Must include implot.h before implot_internal.h #error Must include implot.h before implot_internal.h
@ -376,6 +377,7 @@ struct ImPlotColormapData {
int idx = Count++; int idx = Count++;
Map.SetInt(id,idx); Map.SetInt(id,idx);
_AppendTable(idx); _AppendTable(idx);
ImPlot::Backend::AddColormap(keys, count, qual);
return idx; return idx;
} }
@ -916,6 +918,9 @@ struct ImPlotContext {
ImPlotNextItemData NextItemData; ImPlotNextItemData NextItemData;
ImPlotInputMap InputMap; ImPlotInputMap InputMap;
ImPlotPoint MousePos[IMPLOT_Y_AXES]; ImPlotPoint MousePos[IMPLOT_Y_AXES];
// Backend
void* backendCtx;
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -24,6 +24,7 @@
#include "implot.h" #include "implot.h"
#include "implot_internal.h" #include "implot_internal.h"
#include "backends/implot_backend.h"
#ifdef _MSC_VER #ifdef _MSC_VER
#define sprintf sprintf_s #define sprintf sprintf_s
@ -138,6 +139,7 @@ void HideNextItem(bool hidden, ImGuiCond cond) {
void BustItemCache() { void BustItemCache() {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
Backend::BustItemCache();
for (int p = 0; p < gp.Plots.GetBufSize(); ++p) { for (int p = 0; p < gp.Plots.GetBufSize(); ++p) {
ImPlotPlot& plot = *gp.Plots.GetByIndex(p); ImPlotPlot& plot = *gp.Plots.GetByIndex(p);
plot.ColormapIdx = 0; plot.ColormapIdx = 0;
@ -1890,13 +1892,22 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value
} }
const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double yref = reverse_y ? bounds_max.y : bounds_min.y;
const double ydir = reverse_y ? -1 : 1; const double ydir = reverse_y ? -1 : 1;
#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, reverse_y);
Backend::SetAxisLog(gp.CurrentItem->ID,
scale == ImPlotScale_LogLin || scale == ImPlotScale_LogLog,
scale == ImPlotScale_LinLog || scale == ImPlotScale_LogLog,
bounds_min, bounds_max);
Backend::SetHeatmapData(gp.CurrentItem->ID, values, rows, cols);
#else
GetterHeatmap<T> 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); GetterHeatmap<T> 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()) { RenderPrimitives(RectRenderer<GetterHeatmap<T>, Transformer>(getter, transformer), DrawList, gp.CurrentPlot->PlotRect);
case ImPlotScale_LinLin: RenderPrimitives(RectRenderer<GetterHeatmap<T>, TransformerLinLin>(getter, TransformerLinLin()), DrawList, gp.CurrentPlot->PlotRect); break; #endif
case ImPlotScale_LogLin: RenderPrimitives(RectRenderer<GetterHeatmap<T>, TransformerLogLin>(getter, TransformerLogLin()), DrawList, gp.CurrentPlot->PlotRect); break;;
case ImPlotScale_LinLog: RenderPrimitives(RectRenderer<GetterHeatmap<T>, TransformerLinLog>(getter, TransformerLinLog()), DrawList, gp.CurrentPlot->PlotRect); break;;
case ImPlotScale_LogLog: RenderPrimitives(RectRenderer<GetterHeatmap<T>, TransformerLogLog>(getter, TransformerLogLog()), DrawList, gp.CurrentPlot->PlotRect); break;;
}
if (fmt != NULL) { if (fmt != NULL) {
const double w = (bounds_max.x - bounds_min.x) / cols; const double w = (bounds_max.x - bounds_min.x) / cols;
const double h = (bounds_max.y - bounds_min.y) / rows; const double h = (bounds_max.y - bounds_min.y) / rows;