mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-04 06:12:04 +00:00
[client] add ImPlot and rework latency graphs
This commit is contained in:
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -5,7 +5,7 @@
|
||||
path = repos/PureSpice
|
||||
url = https://github.com/gnif/PureSpice
|
||||
[submodule "repos/cimgui"]
|
||||
path = repos/cimgui
|
||||
path = repos/gui/cimgui
|
||||
url = https://github.com/cimgui/cimgui.git
|
||||
[submodule "repos/wayland-protocols"]
|
||||
path = repos/wayland-protocols
|
||||
@@ -13,3 +13,6 @@
|
||||
[submodule "repos/nanosvg"]
|
||||
path = repos/nanosvg
|
||||
url = https://github.com/memononen/nanosvg.git
|
||||
[submodule "repos/cimplot"]
|
||||
path = repos/gui/cimplot
|
||||
url = https://github.com/cimgui/cimplot.git
|
||||
|
||||
@@ -181,18 +181,11 @@ if(ENABLE_AUDIO)
|
||||
list(APPEND SOURCES src/audio.c)
|
||||
endif()
|
||||
|
||||
# Force cimgui to build as a static library.
|
||||
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
|
||||
# Support Unicode codepoints outside the basic multilingual plane.
|
||||
set(IMGUI_WCHAR32 "yes")
|
||||
add_definitions("-DCIMGUI_USE_OPENGL2=1")
|
||||
add_definitions("-DCIMGUI_USE_OPENGL3=1")
|
||||
|
||||
add_subdirectory("${PROJECT_TOP}/resources" "${CMAKE_BINARY_DIR}/resources")
|
||||
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
|
||||
add_subdirectory("${PROJECT_TOP}/repos/LGMP/lgmp" "${CMAKE_BINARY_DIR}/LGMP" )
|
||||
add_subdirectory("${PROJECT_TOP}/repos/PureSpice" "${CMAKE_BINARY_DIR}/PureSpice")
|
||||
add_subdirectory("${PROJECT_TOP}/repos/cimgui" "${CMAKE_BINARY_DIR}/cimgui" EXCLUDE_FROM_ALL)
|
||||
add_subdirectory("${PROJECT_TOP}/repos/gui" "${CMAKE_BINARY_DIR}/gui" EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(displayservers)
|
||||
add_subdirectory(renderers)
|
||||
@@ -235,7 +228,7 @@ target_link_libraries(looking-glass-client
|
||||
renderers
|
||||
transports
|
||||
audiodevs
|
||||
cimgui
|
||||
lg_gui
|
||||
)
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
@@ -88,6 +88,7 @@ bool waylandPresentationInit(void)
|
||||
wlWm.photonTimings = ringbuffer_new(256, sizeof(float));
|
||||
wlWm.photonGraph = app_registerGraph("PHOTON", wlWm.photonTimings,
|
||||
0.0f, 30.0f, NULL);
|
||||
app_setGraphCompact(wlWm.photonGraph, true);
|
||||
wp_presentation_add_listener(wlWm.presentation, &presentationListener, NULL);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -145,6 +145,8 @@ GraphHandle app_registerGraph(const char * name, RingBuffer buffer,
|
||||
float min, float max, GraphFormatFn formatFn);
|
||||
void app_unregisterGraph(GraphHandle handle);
|
||||
void app_invalidateGraph(GraphHandle handle);
|
||||
void app_setGraphCompact(GraphHandle handle, bool compact);
|
||||
void app_setFrameImportTime(uint64_t time);
|
||||
|
||||
void app_overlayConfigRegister(const char * title,
|
||||
void (*callback)(void * udata, int * id), void * udata);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
typedef struct ImVec2 ImVec2;
|
||||
typedef struct ImVec2_c ImVec2;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
@@ -104,7 +104,6 @@ add_library(renderer_EGL STATIC
|
||||
filter_downscale.c
|
||||
${EGL_SHADER_OBJS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(renderer_EGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1 IMGUI_IMPL_OPENGL_ES3)
|
||||
@@ -113,7 +112,7 @@ target_link_libraries(renderer_EGL
|
||||
PkgConfig::RENDERER_EGL
|
||||
lg_common
|
||||
|
||||
cimgui
|
||||
lg_gui
|
||||
)
|
||||
if(RENDERER_EGL_OPT_FOUND)
|
||||
target_link_libraries(renderer_EGL
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include <GLES3/gl32.h>
|
||||
|
||||
#include "cimgui.h"
|
||||
#include "generator/output/cimgui_impl.h"
|
||||
#include "cimgui_impl.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
@@ -124,9 +124,6 @@ struct Inst
|
||||
int overlayHistoryCount[DESKTOP_DAMAGE_COUNT];
|
||||
unsigned int overlayHistoryIdx;
|
||||
|
||||
RingBuffer importTimings;
|
||||
GraphHandle importGraph;
|
||||
|
||||
bool showSpice;
|
||||
int spiceWidth, spiceHeight;
|
||||
|
||||
@@ -292,10 +289,6 @@ static bool egl_create(LG_Renderer ** renderer, const LG_RendererParams params,
|
||||
LG_LOCK_INIT(this->desktopDamageLock);
|
||||
this->desktopDamage[0].count = -1;
|
||||
|
||||
this->importTimings = ringbuffer_new(256, sizeof(float));
|
||||
this->importGraph = app_registerGraph("IMPORT", this->importTimings,
|
||||
0.0f, 5.0f, NULL);
|
||||
|
||||
*needsOpenGL = false;
|
||||
return true;
|
||||
}
|
||||
@@ -317,8 +310,6 @@ static void egl_deinitialize(LG_Renderer * renderer)
|
||||
egl_stateInvalidate();
|
||||
}
|
||||
|
||||
ringbuffer_free(&this->importTimings);
|
||||
|
||||
egl_desktopFree(&this->desktop);
|
||||
egl_cursorFree (&this->cursor);
|
||||
egl_damageFree (&this->damage);
|
||||
@@ -592,10 +583,8 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
|
||||
|
||||
static bool egl_onFontUpdate(LG_Renderer * renderer)
|
||||
{
|
||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
const bool result = ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
egl_stateInvalidate();
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool egl_onMouseShape(LG_Renderer * renderer, const LG_RendererCursor cursor,
|
||||
@@ -759,14 +748,14 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
egl_stateCheckShared();
|
||||
|
||||
uint64_t start = nanotime();
|
||||
const uint64_t start = nanotime();
|
||||
if (unlikely(!egl_desktopUpdate(
|
||||
this->desktop, frame, dmaFd, damageRects, damageRectsCount)))
|
||||
{
|
||||
DEBUG_INFO("Failed to to update the desktop");
|
||||
return false;
|
||||
}
|
||||
ringbuffer_push(this->importTimings, &(float){ (nanotime() - start) * 1e-6f });
|
||||
app_setFrameImportTime(nanotime() - start);
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
|
||||
|
||||
@@ -496,10 +496,6 @@ static void configUI(void * opaque, int * id)
|
||||
|
||||
bool doMove = false;
|
||||
|
||||
ImVec2 window, pos;
|
||||
igGetWindowPos(&window);
|
||||
igGetMousePos(&pos);
|
||||
|
||||
EGL_Filter ** filters = vector_data(&this->filters);
|
||||
size_t count = vector_size(&this->filters);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
|
||||
@@ -8,7 +8,6 @@ pkg_check_modules(RENDERER_OPENGL REQUIRED IMPORTED_TARGET
|
||||
|
||||
add_library(renderer_OpenGL STATIC
|
||||
opengl.c
|
||||
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl2.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(renderer_OpenGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
|
||||
@@ -17,7 +16,7 @@ target_link_libraries(renderer_OpenGL
|
||||
PkgConfig::RENDERER_OPENGL
|
||||
lg_common
|
||||
|
||||
cimgui
|
||||
lg_gui
|
||||
)
|
||||
|
||||
target_include_directories(renderer_OpenGL
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <GL/gl.h>
|
||||
|
||||
#include "cimgui.h"
|
||||
#include "generator/output/cimgui_impl.h"
|
||||
#include "cimgui_impl.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
@@ -318,8 +318,7 @@ void opengl_onResize(LG_Renderer * renderer, const int width, const int height,
|
||||
|
||||
static bool opengl_onFontUpdate(LG_Renderer * renderer)
|
||||
{
|
||||
ImGui_ImplOpenGL2_DestroyFontsTexture();
|
||||
return ImGui_ImplOpenGL2_CreateFontsTexture();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opengl_onMouseShape(LG_Renderer * renderer, const LG_RendererCursor cursor,
|
||||
|
||||
@@ -917,6 +917,16 @@ void app_invalidateGraph(GraphHandle handle)
|
||||
overlayGraph_invalidate(handle);
|
||||
}
|
||||
|
||||
void app_setGraphCompact(GraphHandle handle, bool compact)
|
||||
{
|
||||
overlayGraph_setCompact(handle, compact);
|
||||
}
|
||||
|
||||
void app_setFrameImportTime(uint64_t time)
|
||||
{
|
||||
g_state.frameImportTime = time;
|
||||
}
|
||||
|
||||
void app_registerOverlay(const struct LG_OverlayOps * ops, const void * params)
|
||||
{
|
||||
ASSERT_LG_OVERLAY_VALID(ops);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#endif
|
||||
#define STBTT_STATIC
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "repos/cimgui/imgui/imstb_truetype.h"
|
||||
#include "repos/gui/cimgui/imgui/imstb_truetype.h"
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -202,18 +202,73 @@ static bool tickTimerFn(void * unused)
|
||||
struct RenderTiming
|
||||
{
|
||||
uint64_t renderStart;
|
||||
uint64_t producerTime;
|
||||
uint64_t captureTime;
|
||||
uint64_t postProcessTime;
|
||||
uint64_t copyTime;
|
||||
uint64_t importTime;
|
||||
};
|
||||
|
||||
static struct RenderTiming frameTimingLoad(void)
|
||||
{
|
||||
struct RenderTiming timing = {};
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const unsigned sequence = atomic_load_explicit(
|
||||
&g_state.frameTimingSequence, memory_order_seq_cst);
|
||||
if (sequence & 1)
|
||||
continue;
|
||||
|
||||
timing.captureTime = atomic_load_explicit(
|
||||
&g_state.producerCaptureTime, memory_order_seq_cst);
|
||||
timing.postProcessTime = atomic_load_explicit(
|
||||
&g_state.producerPostProcessTime, memory_order_seq_cst);
|
||||
timing.copyTime = atomic_load_explicit(
|
||||
&g_state.producerCopyTime, memory_order_seq_cst);
|
||||
timing.importTime = atomic_load_explicit(
|
||||
&g_state.clientImportTime, memory_order_seq_cst);
|
||||
|
||||
if (sequence == atomic_load_explicit(
|
||||
&g_state.frameTimingSequence, memory_order_seq_cst))
|
||||
return timing;
|
||||
}
|
||||
}
|
||||
|
||||
static void frameTimingStore(const LG_TransportFrameTiming * timing,
|
||||
uint64_t importTime)
|
||||
{
|
||||
atomic_fetch_add_explicit(
|
||||
&g_state.frameTimingSequence, 1, memory_order_seq_cst);
|
||||
atomic_store_explicit(&g_state.producerCaptureTime,
|
||||
timing->captureTime, memory_order_seq_cst);
|
||||
atomic_store_explicit(&g_state.producerPostProcessTime,
|
||||
timing->postProcessTime, memory_order_seq_cst);
|
||||
atomic_store_explicit(&g_state.producerCopyTime,
|
||||
timing->copyTime, memory_order_seq_cst);
|
||||
atomic_store_explicit(&g_state.clientImportTime,
|
||||
importTime, memory_order_seq_cst);
|
||||
atomic_fetch_add_explicit(
|
||||
&g_state.frameTimingSequence, 1, memory_order_seq_cst);
|
||||
}
|
||||
|
||||
static void preSwapCallback(void * udata)
|
||||
{
|
||||
const struct RenderTiming * timing = (const struct RenderTiming *)udata;
|
||||
const uint64_t renderTime = nanotime() - timing->renderStart;
|
||||
ringbuffer_push(g_state.renderDuration, &(float) {renderTime * 1e-6f});
|
||||
|
||||
if (timing->producerTime)
|
||||
ringbuffer_push(g_state.endToEndDuration,
|
||||
&(float) {(timing->producerTime + renderTime) * 1e-6f});
|
||||
const uint64_t timestamp = nanotime();
|
||||
const uint64_t renderTime = timestamp - timing->renderStart;
|
||||
if (timing->captureTime || timing->postProcessTime || timing->copyTime ||
|
||||
timing->importTime)
|
||||
{
|
||||
const OverlayFrameTiming frameTiming = {
|
||||
.timestamp = timestamp,
|
||||
.capture = timing->captureTime * 1e-6f,
|
||||
.postProcess = timing->postProcessTime * 1e-6f,
|
||||
.copy = timing->copyTime * 1e-6f,
|
||||
.import = timing->importTime * 1e-6f,
|
||||
.render = renderTime * 1e-6f,
|
||||
};
|
||||
ringbuffer_push(g_state.frameLatency, &frameTiming);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
if (!l_testCapture.enabled || l_testCapture.complete)
|
||||
@@ -375,7 +430,7 @@ static int renderThread(void * unused)
|
||||
.x = g_state.windowScale,
|
||||
.y = g_state.windowScale,
|
||||
};
|
||||
g_state.io->FontGlobalScale = 1.0f / g_state.windowScale;
|
||||
igGetStyle()->FontScaleMain = 1.0f / g_state.windowScale;
|
||||
}
|
||||
|
||||
const bool fontDirty = atomic_exchange(&g_state.fontDirty, false);
|
||||
@@ -407,11 +462,9 @@ static int renderThread(void * unused)
|
||||
|
||||
const bool invalidate = atomic_exchange(&g_state.invalidateWindow, false);
|
||||
|
||||
const struct RenderTiming renderTiming = {
|
||||
.renderStart = nanotime(),
|
||||
.producerTime = newFrame ? atomic_load_explicit(
|
||||
&g_state.producerFrameTime, memory_order_acquire) : 0,
|
||||
};
|
||||
struct RenderTiming renderTiming =
|
||||
newFrame ? frameTimingLoad() : (struct RenderTiming) {};
|
||||
renderTiming.renderStart = nanotime();
|
||||
LG_LOCK(g_state.lgrLock);
|
||||
|
||||
renderQueue_process();
|
||||
@@ -783,6 +836,7 @@ int main_frameThread(void * unused)
|
||||
damageCount = 0;
|
||||
}
|
||||
|
||||
g_state.frameImportTime = 0;
|
||||
if (!RENDERER(onFrame, frame.framebuffer, frame.dmaFD,
|
||||
frame.damageRects, damageCount))
|
||||
{
|
||||
@@ -797,15 +851,7 @@ int main_frameThread(void * unused)
|
||||
g_state.transportOps->getFrameTiming(
|
||||
g_state.transport, &frame, &timing);
|
||||
|
||||
ringbuffer_push(g_state.captureDuration,
|
||||
&(float) {timing.captureTime * 1e-6f});
|
||||
ringbuffer_push(g_state.postProcessDuration,
|
||||
&(float) {timing.postProcessTime * 1e-6f});
|
||||
ringbuffer_push(g_state.copyDuration,
|
||||
&(float) {timing.copyTime * 1e-6f});
|
||||
atomic_store_explicit(&g_state.producerFrameTime,
|
||||
timing.captureTime + timing.postProcessTime + timing.copyTime,
|
||||
memory_order_release);
|
||||
frameTimingStore(&timing, g_state.frameImportTime);
|
||||
|
||||
overlaySplash_show(false);
|
||||
if ((frame.flags & LG_TRANSPORT_FRAME_REQUEST_ACTIVATION) &&
|
||||
@@ -824,13 +870,6 @@ int main_frameThread(void * unused)
|
||||
g_state.autoIdleInhibitState = blockScreensaver;
|
||||
}
|
||||
|
||||
const uint64_t now = nanotime();
|
||||
const uint64_t delta = now - g_state.lastFrameTime;
|
||||
g_state.lastFrameTime = now;
|
||||
if (g_state.lastFrameTimeValid)
|
||||
ringbuffer_push(g_state.uploadTimings, &(float) { delta * 1e-6f });
|
||||
g_state.lastFrameTimeValid = true;
|
||||
|
||||
atomic_fetch_add_explicit(&g_state.frameCount, 1, memory_order_relaxed);
|
||||
#ifdef ENABLE_TESTS
|
||||
atomic_store_explicit(&l_testFrameSerial, frame.serial,
|
||||
@@ -1309,7 +1348,7 @@ static int lg_run(void)
|
||||
|
||||
/* setup imgui */
|
||||
igCreateContext(NULL);
|
||||
g_state.io = igGetIO();
|
||||
g_state.io = igGetIO_Nil();
|
||||
g_state.style = igGetStyle();
|
||||
|
||||
g_state.style->Colors[ImGuiCol_ModalWindowDimBg] = (ImVec4) { 0.0f, 0.0f, 0.0f, 0.4f };
|
||||
@@ -1335,19 +1374,10 @@ static int lg_run(void)
|
||||
|
||||
// initialize metrics ringbuffers
|
||||
g_state.renderTimings = ringbuffer_new(256, sizeof(float));
|
||||
g_state.uploadTimings = ringbuffer_new(256, sizeof(float));
|
||||
g_state.renderDuration = ringbuffer_new(256, sizeof(float));
|
||||
g_state.captureDuration = ringbuffer_new(256, sizeof(float));
|
||||
g_state.postProcessDuration = ringbuffer_new(256, sizeof(float));
|
||||
g_state.copyDuration = ringbuffer_new(256, sizeof(float));
|
||||
g_state.endToEndDuration = ringbuffer_new(256, sizeof(float));
|
||||
overlayGraph_register("FRAME" , g_state.renderTimings , 0.0f, 50.0f, NULL);
|
||||
overlayGraph_register("UPLOAD" , g_state.uploadTimings , 0.0f, 50.0f, NULL);
|
||||
overlayGraph_register("RENDER" , g_state.renderDuration , 0.0f, 10.0f, NULL);
|
||||
overlayGraph_register("CAPTURE" , g_state.captureDuration , 0.0f, 20.0f, NULL);
|
||||
overlayGraph_register("POST PROCESS", g_state.postProcessDuration, 0.0f, 20.0f, NULL);
|
||||
overlayGraph_register("COPY" , g_state.copyDuration , 0.0f, 20.0f, NULL);
|
||||
overlayGraph_register("END TO END" , g_state.endToEndDuration , 0.0f, 50.0f, NULL);
|
||||
g_state.frameLatency = ringbuffer_new(4096, sizeof(OverlayFrameTiming));
|
||||
overlayGraph_setCompact(overlayGraph_register(
|
||||
"FRAME", g_state.renderTimings, 0.0f, 50.0f, NULL), true);
|
||||
overlayGraph_registerFrameTiming("FRAME LATENCY", g_state.frameLatency);
|
||||
|
||||
// unknown guest OS at this time
|
||||
g_state.guestOS = LG_TRANSPORT_OS_OTHER;
|
||||
@@ -1830,12 +1860,7 @@ static void lg_shutdown(void)
|
||||
|
||||
// free metrics ringbuffers
|
||||
ringbuffer_free(&g_state.renderTimings);
|
||||
ringbuffer_free(&g_state.uploadTimings);
|
||||
ringbuffer_free(&g_state.renderDuration);
|
||||
ringbuffer_free(&g_state.captureDuration);
|
||||
ringbuffer_free(&g_state.postProcessDuration);
|
||||
ringbuffer_free(&g_state.copyDuration);
|
||||
ringbuffer_free(&g_state.endToEndDuration);
|
||||
ringbuffer_free(&g_state.frameLatency);
|
||||
|
||||
free(g_state.fontName);
|
||||
igDestroyContext(NULL);
|
||||
|
||||
@@ -143,20 +143,18 @@ struct AppState
|
||||
bool formatValid;
|
||||
uint64_t frameTime;
|
||||
uint64_t overlayFrameTime;
|
||||
uint64_t lastFrameTime;
|
||||
bool lastFrameTimeValid;
|
||||
uint64_t lastRenderTime;
|
||||
bool lastRenderTimeValid;
|
||||
RingBuffer renderTimings;
|
||||
RingBuffer renderDuration;
|
||||
RingBuffer uploadTimings;
|
||||
RingBuffer captureDuration;
|
||||
RingBuffer postProcessDuration;
|
||||
RingBuffer copyDuration;
|
||||
RingBuffer endToEndDuration;
|
||||
RingBuffer frameLatency;
|
||||
uint64_t frameImportTime;
|
||||
|
||||
atomic_uint_least64_t pendingCount;
|
||||
atomic_uint_least64_t producerFrameTime;
|
||||
atomic_uint frameTimingSequence;
|
||||
atomic_uint_least64_t producerCaptureTime;
|
||||
atomic_uint_least64_t producerPostProcessTime;
|
||||
atomic_uint_least64_t producerCopyTime;
|
||||
atomic_uint_least64_t clientImportTime;
|
||||
atomic_uint_least64_t renderCount, frameCount;
|
||||
_Atomic(float) fps, ups;
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ static int alert_render(void * udata, bool interactive, struct Rect * windowRect
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoTitleBar
|
||||
);
|
||||
|
||||
igPushFont(g_state.fontLarge);
|
||||
igPushFont(g_state.fontLarge, 0.0f);
|
||||
igText("%s", l_alert.message);
|
||||
igPopFont();
|
||||
|
||||
|
||||
@@ -20,28 +20,47 @@
|
||||
|
||||
#include "interface/overlay.h"
|
||||
#include "cimgui.h"
|
||||
#include "cimplot.h"
|
||||
|
||||
#include "../main.h"
|
||||
#include "../overlays.h"
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/stringutils.h"
|
||||
#include "common/time.h"
|
||||
#include "overlay_utils.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
struct GraphState
|
||||
{
|
||||
bool show;
|
||||
struct ll * graphs;
|
||||
ImPlotContext * plotContext;
|
||||
ImPlotSpec * plotSpec;
|
||||
};
|
||||
|
||||
static struct GraphState gs = {0};
|
||||
|
||||
enum OverlayGraphType
|
||||
{
|
||||
OVERLAY_GRAPH_LINE,
|
||||
OVERLAY_GRAPH_FRAME_TIMING,
|
||||
};
|
||||
|
||||
#define TIMING_STATISTIC_COUNT 3
|
||||
|
||||
struct OverlayGraph
|
||||
{
|
||||
char * name;
|
||||
RingBuffer buffer;
|
||||
bool enabled;
|
||||
bool compact;
|
||||
enum OverlayGraphType type;
|
||||
float min;
|
||||
float max;
|
||||
float yScale[TIMING_STATISTIC_COUNT];
|
||||
GraphFormatFn formatFn;
|
||||
};
|
||||
|
||||
@@ -81,6 +100,23 @@ static void graphs_earlyInit(void)
|
||||
|
||||
static bool graphs_init(void ** udata, const void * params)
|
||||
{
|
||||
gs.plotContext = ImPlot_CreateContext();
|
||||
if (!gs.plotContext)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create ImPlot context");
|
||||
return false;
|
||||
}
|
||||
|
||||
ImPlot_SetImGuiContext(igGetCurrentContext());
|
||||
gs.plotSpec = ImPlotSpec_ImPlotSpec();
|
||||
if (!gs.plotSpec)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create ImPlot specification");
|
||||
ImPlot_DestroyContext(gs.plotContext);
|
||||
gs.plotContext = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
app_overlayConfigRegister("Performance Metrics", configCallback, NULL);
|
||||
app_registerKeybind(KEY_T, showTimingKeybind, NULL,
|
||||
"Show frame timing information");
|
||||
@@ -97,6 +133,11 @@ static void graphs_free(void * udata)
|
||||
}
|
||||
ll_free(gs.graphs);
|
||||
gs.graphs = NULL;
|
||||
|
||||
ImPlotSpec_destroy(gs.plotSpec);
|
||||
gs.plotSpec = NULL;
|
||||
ImPlot_DestroyContext(gs.plotContext);
|
||||
gs.plotContext = NULL;
|
||||
}
|
||||
|
||||
struct BufferMetrics
|
||||
@@ -119,6 +160,7 @@ static bool rbCalcMetrics(int index, void * value_, void * udata_)
|
||||
udata->min = *value;
|
||||
udata->max = *value;
|
||||
udata->sum = *value;
|
||||
udata->last = *value;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -133,6 +175,248 @@ static bool rbCalcMetrics(int index, void * value_, void * udata_)
|
||||
return true;
|
||||
}
|
||||
|
||||
#define TIMING_PLOT_BUCKETS 100
|
||||
#define TIMING_PLOT_WINDOW_NS 20000000000ULL
|
||||
#define TIMING_PLOT_BUCKET_NS \
|
||||
(TIMING_PLOT_WINDOW_NS / TIMING_PLOT_BUCKETS)
|
||||
#define TIMING_STAGE_COUNT 5
|
||||
|
||||
struct TimingPlotData
|
||||
{
|
||||
uint64_t windowStart;
|
||||
uint64_t windowEnd;
|
||||
float stageMin[TIMING_STAGE_COUNT][TIMING_PLOT_BUCKETS];
|
||||
float stageMax[TIMING_STAGE_COUNT][TIMING_PLOT_BUCKETS];
|
||||
float stageSum[TIMING_STAGE_COUNT][TIMING_PLOT_BUCKETS];
|
||||
unsigned count[TIMING_PLOT_BUCKETS];
|
||||
};
|
||||
|
||||
static float roundTimingScale(float value)
|
||||
{
|
||||
value = max(value, 1.0f);
|
||||
const float magnitude = powf(10.0f, floorf(log10f(value)));
|
||||
const float normalized = value / magnitude;
|
||||
const float rounded = normalized <= 1.0f ? 1.0f :
|
||||
normalized <= 1.5f ? 1.5f :
|
||||
normalized <= 2.0f ? 2.0f :
|
||||
normalized <= 3.0f ? 3.0f :
|
||||
normalized <= 5.0f ? 5.0f :
|
||||
normalized <= 7.5f ? 7.5f : 10.0f;
|
||||
return rounded * magnitude;
|
||||
}
|
||||
|
||||
static float graphScale(struct OverlayGraph * graph, int scaleId,
|
||||
float peak)
|
||||
{
|
||||
float scale = graph->yScale[scaleId];
|
||||
if (scale == 0.0f || peak > scale * 0.95f || peak < scale * 0.45f)
|
||||
scale = roundTimingScale(peak * 1.2f);
|
||||
|
||||
graph->yScale[scaleId] = scale;
|
||||
return scale;
|
||||
}
|
||||
|
||||
static float graphRowWeight(const struct OverlayGraph * graph)
|
||||
{
|
||||
if (graph->type == OVERLAY_GRAPH_FRAME_TIMING)
|
||||
return 3.0f;
|
||||
return graph->compact ? 0.5f : 1.0f;
|
||||
}
|
||||
|
||||
static bool accumulateTimingSample(int index, void * value_, void * udata_)
|
||||
{
|
||||
struct TimingPlotData * data = udata_;
|
||||
const OverlayFrameTiming * timing = value_;
|
||||
if (timing->timestamp < data->windowStart)
|
||||
return true;
|
||||
if (timing->timestamp >= data->windowEnd)
|
||||
return false;
|
||||
|
||||
const uint64_t offset = timing->timestamp - data->windowStart;
|
||||
const int bucket = min(offset / TIMING_PLOT_BUCKET_NS,
|
||||
TIMING_PLOT_BUCKETS - 1);
|
||||
const float values[TIMING_STAGE_COUNT] = {
|
||||
timing->capture,
|
||||
timing->postProcess,
|
||||
timing->copy,
|
||||
timing->import,
|
||||
timing->render,
|
||||
};
|
||||
for (int i = 0; i < TIMING_STAGE_COUNT; ++i)
|
||||
{
|
||||
if (!data->count[bucket])
|
||||
data->stageMin[i][bucket] = data->stageMax[i][bucket] = values[i];
|
||||
else
|
||||
{
|
||||
data->stageMin[i][bucket] = min(data->stageMin[i][bucket], values[i]);
|
||||
data->stageMax[i][bucket] = max(data->stageMax[i][bucket], values[i]);
|
||||
}
|
||||
data->stageSum[i][bucket] += values[i];
|
||||
}
|
||||
++data->count[bucket];
|
||||
return true;
|
||||
}
|
||||
|
||||
static ImPlotFlags graphFlags(bool interactive, bool legend)
|
||||
{
|
||||
ImPlotFlags flags = legend ? ImPlotFlags_None : ImPlotFlags_NoLegend;
|
||||
if (!interactive)
|
||||
flags |= ImPlotFlags_NoInputs | ImPlotFlags_NoMenus |
|
||||
ImPlotFlags_NoBoxSelect;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void renderLineGraph(struct OverlayGraph * graph, bool interactive,
|
||||
ImVec2 size)
|
||||
{
|
||||
struct BufferMetrics metrics = {};
|
||||
ringbuffer_forEach(graph->buffer, rbCalcMetrics, &metrics, false);
|
||||
|
||||
const int count = ringbuffer_getCount(graph->buffer);
|
||||
if (metrics.sum > 0.0f && count > 0)
|
||||
{
|
||||
metrics.avg = metrics.sum / count;
|
||||
metrics.freq = 1000.0f / metrics.avg;
|
||||
}
|
||||
|
||||
const char * description;
|
||||
if (graph->formatFn)
|
||||
description = graph->formatFn(graph->name,
|
||||
metrics.min, metrics.max, metrics.avg, metrics.freq, metrics.last);
|
||||
else
|
||||
{
|
||||
static char text[128];
|
||||
snprintf(text, sizeof(text),
|
||||
"%s: min %.2f, max %.2f, avg %.2f ms / %.2f Hz",
|
||||
graph->name, metrics.min, metrics.max, metrics.avg, metrics.freq);
|
||||
description = text;
|
||||
}
|
||||
|
||||
char title[256];
|
||||
snprintf(title, sizeof(title), "%s###graph_%p", description, (void *)graph);
|
||||
if (!ImPlot_BeginPlot(title, size, graphFlags(interactive, false)))
|
||||
return;
|
||||
|
||||
ImPlot_SetupAxes(NULL, "ms",
|
||||
ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_None);
|
||||
const float valueMax = graphScale(graph, 0,
|
||||
count > 0 ? metrics.max : graph->max);
|
||||
ImPlot_SetupAxesLimits(0.0, max(1, count - 1), graph->min, valueMax,
|
||||
ImPlotCond_Always);
|
||||
|
||||
gs.plotSpec->Offset = ringbuffer_getStart(graph->buffer);
|
||||
gs.plotSpec->Stride = sizeof(float);
|
||||
gs.plotSpec->Flags = ImPlotLineFlags_None;
|
||||
ImPlot_PlotLine_FloatPtrInt(graph->name,
|
||||
ringbuffer_getValues(graph->buffer), count, 1.0, 0.0, *gs.plotSpec);
|
||||
ImPlot_EndPlot();
|
||||
}
|
||||
|
||||
static void renderTimingStatistic(struct OverlayGraph * graph,
|
||||
const char * statistic, int statisticId,
|
||||
const float values[TIMING_STAGE_COUNT][TIMING_PLOT_BUCKETS],
|
||||
bool interactive, ImVec2 size)
|
||||
{
|
||||
float cumulative[TIMING_STAGE_COUNT][TIMING_PLOT_BUCKETS] = {};
|
||||
float zero[TIMING_PLOT_BUCKETS] = {};
|
||||
float xValues[TIMING_PLOT_BUCKETS];
|
||||
float peak = 0.0f;
|
||||
for (int bucket = 0; bucket < TIMING_PLOT_BUCKETS; ++bucket)
|
||||
{
|
||||
xValues[bucket] = bucket;
|
||||
for (int stage = 0; stage < TIMING_STAGE_COUNT; ++stage)
|
||||
cumulative[stage][bucket] = values[stage][bucket] +
|
||||
(stage ? cumulative[stage - 1][bucket] : 0.0f);
|
||||
peak = max(peak, cumulative[TIMING_STAGE_COUNT - 1][bucket]);
|
||||
}
|
||||
const float valueMax = graphScale(graph, statisticId, peak);
|
||||
|
||||
char title[160];
|
||||
snprintf(title, sizeof(title), "%s %s###graph_%p_%d",
|
||||
graph->name, statistic, (void *)graph, statisticId);
|
||||
if (!ImPlot_BeginPlot(title, size, graphFlags(interactive, true)))
|
||||
return;
|
||||
|
||||
ImPlot_SetupAxes(NULL, "ms",
|
||||
ImPlotAxisFlags_NoDecorations,
|
||||
ImPlotAxisFlags_None);
|
||||
ImPlot_SetupAxesLimits(-0.5, TIMING_PLOT_BUCKETS - 0.5, 0.0, valueMax,
|
||||
ImPlotCond_Always);
|
||||
ImPlot_SetupLegend(ImPlotLocation_South,
|
||||
ImPlotLegendFlags_Outside | ImPlotLegendFlags_Horizontal);
|
||||
|
||||
const char * labels[TIMING_STAGE_COUNT] = {
|
||||
"Capture",
|
||||
"Post",
|
||||
"Copy",
|
||||
"Import",
|
||||
"Render",
|
||||
};
|
||||
|
||||
gs.plotSpec->Offset = 0;
|
||||
gs.plotSpec->Stride = sizeof(float);
|
||||
for (int stage = 0; stage < TIMING_STAGE_COUNT; ++stage)
|
||||
{
|
||||
const ImVec4 color =
|
||||
ImPlot_GetColormapColor(stage, ImPlotColormap_Deep);
|
||||
gs.plotSpec->FillColor = color;
|
||||
gs.plotSpec->FillAlpha = 0.35f;
|
||||
gs.plotSpec->LineWeight = 1.0f;
|
||||
gs.plotSpec->Flags = ImPlotShadedFlags_None;
|
||||
ImPlot_PlotShaded_FloatPtrFloatPtrFloatPtr(labels[stage], xValues,
|
||||
stage ? cumulative[stage - 1] : zero, cumulative[stage],
|
||||
TIMING_PLOT_BUCKETS, *gs.plotSpec);
|
||||
|
||||
gs.plotSpec->LineColor = color;
|
||||
gs.plotSpec->LineWeight = 1.0f;
|
||||
gs.plotSpec->FillAlpha = 1.0f;
|
||||
gs.plotSpec->Flags = ImPlotLineFlags_None;
|
||||
ImPlot_PlotLine_FloatPtrInt(labels[stage], cumulative[stage],
|
||||
TIMING_PLOT_BUCKETS, 1.0, 0.0, *gs.plotSpec);
|
||||
}
|
||||
|
||||
gs.plotSpec->LineColor = (ImVec4) {0.0f, 0.0f, 0.0f, -1.0f};
|
||||
gs.plotSpec->FillColor = (ImVec4) {0.0f, 0.0f, 0.0f, -1.0f};
|
||||
gs.plotSpec->LineWeight = 1.0f;
|
||||
ImPlot_EndPlot();
|
||||
}
|
||||
|
||||
static void renderTimingGraph(struct OverlayGraph * graph, bool interactive,
|
||||
ImVec2 size)
|
||||
{
|
||||
struct TimingPlotData data = {};
|
||||
data.windowEnd = nanotime() / TIMING_PLOT_BUCKET_NS * TIMING_PLOT_BUCKET_NS;
|
||||
data.windowStart = data.windowEnd > TIMING_PLOT_WINDOW_NS ?
|
||||
data.windowEnd - TIMING_PLOT_WINDOW_NS : 0;
|
||||
ringbuffer_forEach(graph->buffer, accumulateTimingSample, &data, false);
|
||||
|
||||
float minimum[TIMING_STAGE_COUNT][TIMING_PLOT_BUCKETS] = {};
|
||||
float maximum[TIMING_STAGE_COUNT][TIMING_PLOT_BUCKETS] = {};
|
||||
float average[TIMING_STAGE_COUNT][TIMING_PLOT_BUCKETS] = {};
|
||||
for (int bucket = 0; bucket < TIMING_PLOT_BUCKETS; ++bucket)
|
||||
{
|
||||
if (!data.count[bucket])
|
||||
continue;
|
||||
|
||||
for (int stage = 0; stage < TIMING_STAGE_COUNT; ++stage)
|
||||
{
|
||||
minimum[stage][bucket] = data.stageMin[stage][bucket];
|
||||
maximum[stage][bucket] = data.stageMax[stage][bucket];
|
||||
average[stage][bucket] =
|
||||
data.stageSum[stage][bucket] / data.count[bucket];
|
||||
}
|
||||
}
|
||||
|
||||
const float spacing = igGetStyle()->ItemSpacing.y;
|
||||
const float height = (size.y - spacing * 2.0f) / 3.0f;
|
||||
renderTimingStatistic(graph, "MINIMUM", 0, minimum,
|
||||
interactive, (ImVec2) {size.x, height});
|
||||
renderTimingStatistic(graph, "MAXIMUM", 1, maximum,
|
||||
interactive, (ImVec2) {size.x, height});
|
||||
renderTimingStatistic(graph, "AVERAGE", 2, average,
|
||||
interactive, (ImVec2) {size.x, height});
|
||||
}
|
||||
|
||||
static int graphs_render(void * udata, bool interactive,
|
||||
struct Rect * windowRects, int maxRects)
|
||||
{
|
||||
@@ -143,11 +427,15 @@ static int graphs_render(void * udata, bool interactive,
|
||||
|
||||
GraphHandle graph;
|
||||
int graphCount = 0;
|
||||
float graphRows = 0.0f;
|
||||
ll_lock(gs.graphs);
|
||||
ll_forEachNL(gs.graphs, item, graph)
|
||||
{
|
||||
if (graph->enabled)
|
||||
{
|
||||
++graphCount;
|
||||
graphRows += graphRowWeight(graph);
|
||||
}
|
||||
}
|
||||
|
||||
if (!graphCount)
|
||||
@@ -162,7 +450,7 @@ static int graphs_render(void * udata, bool interactive,
|
||||
igSetNextWindowSize(
|
||||
(ImVec2){
|
||||
28.0f * fontSize,
|
||||
7.0f * fontSize * graphCount
|
||||
7.0f * fontSize * graphRows
|
||||
},
|
||||
ImGuiCond_FirstUseEver);
|
||||
|
||||
@@ -172,51 +460,27 @@ static int graphs_render(void * udata, bool interactive,
|
||||
|
||||
igBegin("Performance Metrics", NULL, flags);
|
||||
|
||||
ImVec2 winSize;
|
||||
igGetContentRegionAvail(&winSize);
|
||||
const float height = (winSize.y / graphCount)
|
||||
- igGetStyle()->ItemSpacing.y;
|
||||
ImVec2 winSize = igGetContentRegionAvail();
|
||||
const float spacing = igGetStyle()->ItemSpacing.y;
|
||||
const float rowHeight =
|
||||
(winSize.y - spacing * (graphCount - 1)) / graphRows;
|
||||
|
||||
for (int pass = 0; pass < 2; ++pass)
|
||||
{
|
||||
ll_forEachNL(gs.graphs, item, graph)
|
||||
{
|
||||
if (!graph->enabled)
|
||||
if (!graph->enabled || graph->compact != (pass == 1))
|
||||
continue;
|
||||
|
||||
struct BufferMetrics metrics = {};
|
||||
ringbuffer_forEach(graph->buffer, rbCalcMetrics, &metrics, false);
|
||||
|
||||
if (metrics.sum > 0.0f)
|
||||
{
|
||||
metrics.avg = metrics.sum / ringbuffer_getCount(graph->buffer);
|
||||
metrics.freq = 1000.0f / metrics.avg;
|
||||
}
|
||||
|
||||
const char * title;
|
||||
if (graph->formatFn)
|
||||
title = graph->formatFn(graph->name,
|
||||
metrics.min, metrics.max, metrics.avg, metrics.freq, metrics.last);
|
||||
else
|
||||
{
|
||||
static char _title[64];
|
||||
snprintf(_title, sizeof(_title),
|
||||
"%s: min:%4.2f max:%4.2f avg:%4.2f/%4.2fHz",
|
||||
graph->name, metrics.min, metrics.max, metrics.avg, metrics.freq);
|
||||
title = _title;
|
||||
}
|
||||
|
||||
igPushID_Ptr(graph);
|
||||
igPlotLines_FloatPtr(
|
||||
"##graph",
|
||||
(float *)ringbuffer_getValues(graph->buffer),
|
||||
ringbuffer_getLength(graph->buffer),
|
||||
ringbuffer_getStart (graph->buffer),
|
||||
title,
|
||||
graph->min,
|
||||
graph->max,
|
||||
(ImVec2){ winSize.x, height },
|
||||
sizeof(float));
|
||||
const float height = rowHeight * graphRowWeight(graph);
|
||||
if (graph->type == OVERLAY_GRAPH_FRAME_TIMING)
|
||||
renderTimingGraph(graph, interactive, (ImVec2) {winSize.x, height});
|
||||
else
|
||||
renderLineGraph(graph, interactive, (ImVec2) {winSize.x, height});
|
||||
igPopID();
|
||||
};
|
||||
}
|
||||
}
|
||||
ll_unlock(gs.graphs);
|
||||
|
||||
overlayGetImGuiRect(windowRects);
|
||||
@@ -259,13 +523,26 @@ GraphHandle overlayGraph_register(const char * name, RingBuffer buffer,
|
||||
|
||||
graph->buffer = buffer;
|
||||
graph->enabled = true;
|
||||
graph->compact = false;
|
||||
graph->type = OVERLAY_GRAPH_LINE;
|
||||
graph->min = min;
|
||||
graph->max = max;
|
||||
for (int i = 0; i < TIMING_STATISTIC_COUNT; ++i)
|
||||
graph->yScale[i] = 0.0f;
|
||||
graph->formatFn = formatFn;
|
||||
ll_push(gs.graphs, graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
GraphHandle overlayGraph_registerFrameTiming(const char * name,
|
||||
RingBuffer buffer)
|
||||
{
|
||||
GraphHandle graph = overlayGraph_register(name, buffer, 0.0f, 0.0f, NULL);
|
||||
if (graph)
|
||||
graph->type = OVERLAY_GRAPH_FRAME_TIMING;
|
||||
return graph;
|
||||
}
|
||||
|
||||
void overlayGraph_unregister(GraphHandle handle)
|
||||
{
|
||||
if (!gs.graphs || !handle)
|
||||
@@ -279,6 +556,12 @@ void overlayGraph_unregister(GraphHandle handle)
|
||||
app_invalidateWindow(false);
|
||||
}
|
||||
|
||||
void overlayGraph_setCompact(GraphHandle handle, bool compact)
|
||||
{
|
||||
if (handle)
|
||||
handle->compact = compact;
|
||||
}
|
||||
|
||||
void overlayGraph_iterate(void (*callback)(GraphHandle handle, const char * name,
|
||||
bool * enabled, void * udata), void * udata)
|
||||
{
|
||||
|
||||
@@ -125,7 +125,7 @@ static int msg_render(void * udata, bool interactive, struct Rect * windowRects,
|
||||
continue;
|
||||
}
|
||||
|
||||
igCalcTextSize(&textSize, line, NULL, false, 0.0);
|
||||
textSize = igCalcTextSize(line, NULL, false, 0.0);
|
||||
igSetCursorPosX((igGetWindowWidth() * 0.5f) - (textSize.x * 0.5f));
|
||||
igText("%s", stringlist_at(msg->lines, i));
|
||||
}
|
||||
@@ -135,7 +135,7 @@ static int msg_render(void * udata, bool interactive, struct Rect * windowRects,
|
||||
bool destroy = false;
|
||||
if (msg->confirm)
|
||||
{
|
||||
igCalcTextSize(&textSize, "Yes", NULL, false, 0.0);
|
||||
textSize = igCalcTextSize("Yes", NULL, false, 0.0);
|
||||
ImGuiStyle * style = igGetStyle();
|
||||
textSize.x += (style->FramePadding.x * 2.0f) * 8.0f;
|
||||
textSize.y += (style->FramePadding.y * 2.0f) * 1.5f;
|
||||
@@ -156,7 +156,7 @@ static int msg_render(void * udata, bool interactive, struct Rect * windowRects,
|
||||
}
|
||||
else
|
||||
{
|
||||
igCalcTextSize(&textSize, "OK", NULL, false, 0.0);
|
||||
textSize = igCalcTextSize("OK", NULL, false, 0.0);
|
||||
ImGuiStyle * style = igGetStyle();
|
||||
textSize.x += (style->FramePadding.x * 2.0f) * 8.0f;
|
||||
textSize.y += (style->FramePadding.y * 2.0f) * 1.5f;
|
||||
|
||||
@@ -122,7 +122,7 @@ static void renderText(ImDrawList * list, int x, int y, ImU32 color,
|
||||
if (textHeight == 0.0f)
|
||||
{
|
||||
const char * tmp = "W";
|
||||
igCalcTextSize(&size, tmp, tmp + 1, false, 0.0f);
|
||||
size = igCalcTextSize(tmp, tmp + 1, false, 0.0f);
|
||||
textHeight = size.y;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ static void renderText(ImDrawList * list, int x, int y, ImU32 color,
|
||||
const char * text = stringlist_at(lines, topAlign ? i : count - i - 1);
|
||||
const int len = strlen(text);
|
||||
|
||||
igCalcTextSize(&size, text, text + len, false, 0.0f);
|
||||
size = igCalcTextSize(text, text + len, false, 0.0f);
|
||||
ImDrawList_AddText_Vec2(
|
||||
list,
|
||||
(ImVec2){
|
||||
@@ -195,7 +195,7 @@ static int splash_render(void * udata, bool interactive, struct Rect * windowRec
|
||||
|
||||
ImDrawList_AddImage(
|
||||
list,
|
||||
(ImTextureID)l_logo.tex,
|
||||
(ImTextureRef) { ._TexID = (ImTextureID)(uintptr_t)l_logo.tex },
|
||||
(ImVec2){
|
||||
logoRect.x,
|
||||
logoRect.y
|
||||
|
||||
@@ -101,7 +101,7 @@ static int status_render(void * udata, bool interactive, struct Rect * windowRec
|
||||
|
||||
ImDrawList_AddImage(
|
||||
igGetBackgroundDrawList_Nil(),
|
||||
(ImTextureID)img->tex,
|
||||
(ImTextureRef) { ._TexID = (ImTextureID)(uintptr_t)img->tex },
|
||||
(ImVec2){
|
||||
xPos,
|
||||
marginY
|
||||
|
||||
@@ -41,11 +41,8 @@
|
||||
|
||||
void overlayGetImGuiRect(struct Rect * rect)
|
||||
{
|
||||
ImVec2 size;
|
||||
ImVec2 pos;
|
||||
|
||||
igGetWindowPos(&pos);
|
||||
igGetWindowSize(&size);
|
||||
ImVec2 pos = igGetWindowPos();
|
||||
ImVec2 size = igGetWindowSize();
|
||||
|
||||
rect->x = pos.x;
|
||||
rect->y = pos.y;
|
||||
@@ -60,9 +57,8 @@ ImVec2 * overlayGetScreenSize(void)
|
||||
|
||||
static void overlayAddUnderline(ImU32 color)
|
||||
{
|
||||
ImVec2 min, max;
|
||||
igGetItemRectMin(&min);
|
||||
igGetItemRectMax(&max);
|
||||
ImVec2 min = igGetItemRectMin();
|
||||
ImVec2 max = igGetItemRectMax();
|
||||
min.y = max.y;
|
||||
ImDrawList_AddLine(igGetWindowDrawList(), min, max, color, 1.0f);
|
||||
}
|
||||
|
||||
@@ -44,9 +44,23 @@ extern struct LG_OverlayOps LGOverlayStatus;
|
||||
|
||||
void overlayAlert_show(LG_MsgAlert type, const char * fmt, va_list args);
|
||||
|
||||
typedef struct OverlayFrameTiming
|
||||
{
|
||||
uint64_t timestamp;
|
||||
float capture;
|
||||
float postProcess;
|
||||
float copy;
|
||||
float import;
|
||||
float render;
|
||||
}
|
||||
OverlayFrameTiming;
|
||||
|
||||
GraphHandle overlayGraph_register(const char * name, RingBuffer buffer,
|
||||
float min, float max, GraphFormatFn formatFn);
|
||||
GraphHandle overlayGraph_registerFrameTiming(const char * name,
|
||||
RingBuffer buffer);
|
||||
void overlayGraph_unregister(GraphHandle handle);
|
||||
void overlayGraph_setCompact(GraphHandle handle, bool compact);
|
||||
void overlayGraph_iterate(void (*callback)(GraphHandle handle, const char * name,
|
||||
bool * enabled, void * udata), void * udata);
|
||||
void overlayGraph_invalidate(GraphHandle handle);
|
||||
|
||||
@@ -610,7 +610,7 @@ bool util_buildUIFontAtlas(
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = ImFontAtlas_Build(atlas);
|
||||
result = true;
|
||||
|
||||
done:
|
||||
LG_UNLOCK(UIFontLock);
|
||||
|
||||
@@ -5,7 +5,7 @@ add_executable(font-tests
|
||||
../src/font.c
|
||||
)
|
||||
target_compile_definitions(font-tests PRIVATE
|
||||
FONT_TEST_FILE="${PROJECT_TOP}/repos/cimgui/imgui/misc/fonts/DroidSans.ttf"
|
||||
FONT_TEST_FILE="${PROJECT_TOP}/repos/gui/cimgui/imgui/misc/fonts/DroidSans.ttf"
|
||||
)
|
||||
target_include_directories(font-tests PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
if (EXISTS "${PROJECT_TOP}/.git" AND (
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/cimgui/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/gui/cimgui/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/gui/cimplot/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/LGMP/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/PureSpice/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/cimgui/imgui/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/gui/cimgui/imgui/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/gui/cimplot/implot/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/wayland-protocols/.git") OR
|
||||
(NOT EXISTS "${PROJECT_TOP}/repos/nanosvg/.git")
|
||||
))
|
||||
|
||||
Submodule repos/cimgui deleted from d6b4ecda71
57
repos/gui/CMakeLists.txt
Normal file
57
repos/gui/CMakeLists.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(lg_gui LANGUAGES CXX)
|
||||
|
||||
add_library(lg_gui STATIC
|
||||
cimgui/cimgui.cpp
|
||||
cimgui/imgui/imgui.cpp
|
||||
cimgui/imgui/imgui_draw.cpp
|
||||
cimgui/imgui/imgui_tables.cpp
|
||||
cimgui/imgui/imgui_widgets.cpp
|
||||
cimplot/cimplot.cpp
|
||||
cimplot/implot/implot.cpp
|
||||
cimplot/implot/implot_items.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(lg_gui PUBLIC
|
||||
IMGUI_DISABLE_OBSOLETE_FUNCTIONS=1
|
||||
IMGUI_USE_WCHAR32
|
||||
IMGUI_USER_CONFIG="../cimconfig.h"
|
||||
)
|
||||
|
||||
if(ENABLE_OPENGL)
|
||||
target_sources(lg_gui PRIVATE
|
||||
cimgui/imgui/backends/imgui_impl_opengl2.cpp
|
||||
)
|
||||
target_compile_definitions(lg_gui PUBLIC CIMGUI_USE_OPENGL2=1)
|
||||
endif()
|
||||
|
||||
if(ENABLE_EGL)
|
||||
set(IMGUI_OPENGL3_BACKEND
|
||||
cimgui/imgui/backends/imgui_impl_opengl3.cpp
|
||||
)
|
||||
target_sources(lg_gui PRIVATE ${IMGUI_OPENGL3_BACKEND})
|
||||
set_source_files_properties(${IMGUI_OPENGL3_BACKEND} PROPERTIES
|
||||
COMPILE_DEFINITIONS IMGUI_IMPL_OPENGL_ES3
|
||||
)
|
||||
target_compile_definitions(lg_gui PUBLIC CIMGUI_USE_OPENGL3=1)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(lg_gui PUBLIC
|
||||
IMGUI_IMPL_API=extern\t"C"\t__declspec\(dllexport\)
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(lg_gui PUBLIC IMGUI_IMPL_API=extern\t"C"\t)
|
||||
endif()
|
||||
|
||||
target_include_directories(lg_gui PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cimgui"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cimgui/imgui"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cimplot"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cimplot/implot"
|
||||
)
|
||||
|
||||
set_target_properties(lg_gui PROPERTIES
|
||||
CXX_STANDARD 11
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
1
repos/gui/cimgui
Submodule
1
repos/gui/cimgui
Submodule
Submodule repos/gui/cimgui added at 4e8fcc1367
1
repos/gui/cimplot
Submodule
1
repos/gui/cimplot
Submodule
Submodule repos/gui/cimplot added at 8213880419
Reference in New Issue
Block a user