updates to imgui integration (added thread metrics, moved things to a behavior, fixed bug i dont remember existing where imgui doesn't render for new scenes because it doesnt rebind to the new gui mode
This commit is contained in:
parent
f0d552c47b
commit
a145bae065
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"engine": {
|
"engine": {
|
||||||
"scenes": {
|
"scenes": {
|
||||||
"start": "StartMenu",
|
"start": "SourceEngine",
|
||||||
"matrix": { "reverseInfinite": true },
|
"matrix": { "reverseInfinite": true },
|
||||||
"meshes": { "interleaved": false },
|
"meshes": { "interleaved": false },
|
||||||
"lights": { "enabled": false,
|
"lights": { "enabled": false,
|
||||||
@ -299,7 +299,7 @@
|
|||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
},
|
||||||
"imgui": {
|
"imgui": {
|
||||||
"enabled": false
|
"enabled": true
|
||||||
},
|
},
|
||||||
"fsr": {
|
"fsr": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
"name": "Gui: Menu",
|
"name": "Gui: Menu",
|
||||||
"type": "Gui",
|
"type": "Gui",
|
||||||
"behaviors": [
|
"behaviors": [
|
||||||
"GuiBehavior"
|
"GuiBehavior",
|
||||||
|
"ImguiBehavior"
|
||||||
],
|
],
|
||||||
"ignore": false,
|
"ignore": false,
|
||||||
"transform": {
|
"transform": {
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
"behaviors": [
|
"behaviors": [
|
||||||
"SceneBehavior",
|
"SceneBehavior",
|
||||||
"ExtSceneBehavior",
|
"ExtSceneBehavior",
|
||||||
|
"ImguiBehavior",
|
||||||
"BgmEmitterBehavior"
|
"BgmEmitterBehavior"
|
||||||
],
|
],
|
||||||
"assets": [
|
"assets": [
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <uf/config.h>
|
#include <uf/config.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#if UF_USE_IMGUI
|
#if UF_USE_IMGUI
|
||||||
|
|
||||||
namespace ext {
|
namespace ext {
|
||||||
namespace imgui {
|
namespace imgui {
|
||||||
|
extern UF_API bool focused;
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
void tick();
|
void tick();
|
||||||
void render();
|
void render();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
extern UF_API bool focused;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,9 @@ namespace uf {
|
|||||||
uf::stl::string UF_API replace( const uf::stl::string&, const uf::stl::string&, const uf::stl::string& );
|
uf::stl::string UF_API replace( const uf::stl::string&, const uf::stl::string&, const uf::stl::string& );
|
||||||
uf::stl::string UF_API lowercase( const uf::stl::string& );
|
uf::stl::string UF_API lowercase( const uf::stl::string& );
|
||||||
uf::stl::string UF_API uppercase( const uf::stl::string& );
|
uf::stl::string UF_API uppercase( const uf::stl::string& );
|
||||||
|
uf::stl::string UF_API ltrim( const uf::stl::string& );
|
||||||
|
uf::stl::string UF_API rtrim( const uf::stl::string& );
|
||||||
|
uf::stl::string UF_API trim( const uf::stl::string& );
|
||||||
uf::stl::vector<uf::stl::string> UF_API split( const uf::stl::string&, const uf::stl::string& );
|
uf::stl::vector<uf::stl::string> UF_API split( const uf::stl::string&, const uf::stl::string& );
|
||||||
uf::stl::string UF_API si( double value, const uf::stl::string& unit, size_t precision = 3 );
|
uf::stl::string UF_API si( double value, const uf::stl::string& unit, size_t precision = 3 );
|
||||||
bool UF_API contains( const uf::stl::string&, const uf::stl::string& );
|
bool UF_API contains( const uf::stl::string&, const uf::stl::string& );
|
||||||
|
|||||||
58
engine/src/ext/imgui/behavior.cpp
Normal file
58
engine/src/ext/imgui/behavior.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include "behavior.h"
|
||||||
|
|
||||||
|
#include <uf/utils/hook/hook.h>
|
||||||
|
#include <uf/utils/io/fmt.h>
|
||||||
|
#include <uf/utils/io/payloads.h>
|
||||||
|
#include <uf/utils/io/console.h>
|
||||||
|
#include <uf/utils/window/payloads.h>
|
||||||
|
#include <uf/utils/thread/thread.h>
|
||||||
|
|
||||||
|
#if UF_USE_IMGUI
|
||||||
|
#include <uf/ext/imgui/imgui.h>
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
#include <imgui/imgui_stdlib.h>
|
||||||
|
|
||||||
|
#include "consoleWindow.inl"
|
||||||
|
#include "threadMetrics.inl"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
UF_BEHAVIOR_REGISTER_CPP(ext::ImguiBehavior)
|
||||||
|
UF_BEHAVIOR_TRAITS_CPP(ext::ImguiBehavior, ticks = false, renders = false, thread = "")
|
||||||
|
#define this (&self)
|
||||||
|
void ext::ImguiBehavior::initialize( uf::Object& self ) {
|
||||||
|
auto& metadata = this->getComponent<ext::ImguiBehavior::Metadata>();
|
||||||
|
auto& metadataJson = this->getComponent<uf::Serializer>();
|
||||||
|
|
||||||
|
#if UF_USE_IMGUI
|
||||||
|
this->addHook( "gui:IMGUI.tick", [&](){
|
||||||
|
tick( self );
|
||||||
|
} );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
::consoleWindow.position.x = uf::renderer::settings::width - ::consoleWindow.size.x;
|
||||||
|
|
||||||
|
UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson);
|
||||||
|
}
|
||||||
|
void ext::ImguiBehavior::tick( uf::Object& self ) {
|
||||||
|
#if UF_USE_IMGUI
|
||||||
|
// console window
|
||||||
|
{
|
||||||
|
bool opened;
|
||||||
|
::consoleWindow.Draw("Console", opened);
|
||||||
|
}
|
||||||
|
// thread metrics window
|
||||||
|
{
|
||||||
|
bool opened = true;
|
||||||
|
::threadMetrics.Draw("Thread Metrics", opened);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void ext::ImguiBehavior::render( uf::Object& self ){
|
||||||
|
|
||||||
|
}
|
||||||
|
void ext::ImguiBehavior::destroy( uf::Object& self ){}
|
||||||
|
void ext::ImguiBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ){
|
||||||
|
}
|
||||||
|
void ext::ImguiBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){
|
||||||
|
}
|
||||||
|
#undef this
|
||||||
20
engine/src/ext/imgui/behavior.h
Normal file
20
engine/src/ext/imgui/behavior.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <uf/config.h>
|
||||||
|
#include <uf/ext/ext.h>
|
||||||
|
#include <uf/engine/entity/entity.h>
|
||||||
|
#include <uf/engine/scene/scene.h>
|
||||||
|
#include <uf/utils/math/vector.h>
|
||||||
|
#include <uf/utils/renderer/renderer.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace ext {
|
||||||
|
namespace ImguiBehavior {
|
||||||
|
UF_BEHAVIOR_DEFINE_TYPE();
|
||||||
|
EXT_BEHAVIOR_DEFINE_TRAITS();
|
||||||
|
EXT_BEHAVIOR_DEFINE_FUNCTIONS();
|
||||||
|
UF_BEHAVIOR_DEFINE_METADATA(
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
194
engine/src/ext/imgui/consoleWindow.inl
Normal file
194
engine/src/ext/imgui/consoleWindow.inl
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
namespace {
|
||||||
|
struct ConsoleWindow {
|
||||||
|
ImGuiTextFilter filter;
|
||||||
|
int historyPos = -1;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool automatic = true;
|
||||||
|
bool bottom = false;
|
||||||
|
} scroll;
|
||||||
|
|
||||||
|
uf::stl::string commandBuf;
|
||||||
|
|
||||||
|
pod::Vector2ui size{800, 600};
|
||||||
|
pod::Vector2ui position{64, 32};
|
||||||
|
|
||||||
|
void Draw( const uf::stl::string& title, bool& open ) {
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(size.x, size.y), ImGuiCond_FirstUseEver);
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(position.x, position.y), ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
|
if (!ImGui::Begin(title.c_str(), &open)) {
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ImGui::BeginPopupContextItem() ) {
|
||||||
|
if ( ImGui::MenuItem("Close Console") ) open = false;
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawToolbar();
|
||||||
|
ImGui::Separator();
|
||||||
|
DrawLog();
|
||||||
|
ImGui::Separator();
|
||||||
|
DrawInput();
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DrawToolbar() {
|
||||||
|
if ( ImGui::SmallButton("Clear") ) uf::console::clear();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
bool copyToClipboard = ImGui::SmallButton("Copy");
|
||||||
|
|
||||||
|
if ( ImGui::Button("Options") ) {
|
||||||
|
ImGui::OpenPopup("Options");
|
||||||
|
}
|
||||||
|
if ( ImGui::BeginPopup("Options") ) {
|
||||||
|
ImGui::Checkbox("Auto-scroll", &scroll.automatic);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);
|
||||||
|
|
||||||
|
if ( copyToClipboard ) {
|
||||||
|
ImGui::LogToClipboard();
|
||||||
|
for ( auto& item : uf::console::log ) {
|
||||||
|
if (filter.PassFilter(item.c_str())) ImGui::LogText("%s\n", item.c_str());
|
||||||
|
}
|
||||||
|
ImGui::LogFinish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawLog() {
|
||||||
|
const float footerReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
||||||
|
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footerReserve), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
|
|
||||||
|
if ( ImGui::BeginPopupContextWindow() ) {
|
||||||
|
if ( ImGui::Selectable("Clear") ) uf::console::clear();
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1));
|
||||||
|
|
||||||
|
for ( auto& item : uf::console::log ) {
|
||||||
|
if ( !filter.PassFilter(item.c_str()) ) continue;
|
||||||
|
|
||||||
|
ImVec4 color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
bool hasColor = false;
|
||||||
|
|
||||||
|
if ( uf::string::matched(item, "^\\[ERROR\\]") ) {
|
||||||
|
color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); hasColor = true;
|
||||||
|
} else if ( uf::string::matched(item, "^\\> ")) {
|
||||||
|
color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); hasColor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hasColor ) ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
|
ImGui::TextUnformatted(item.c_str());
|
||||||
|
if ( hasColor ) ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( scroll.bottom || (scroll.automatic && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) ) {
|
||||||
|
ImGui::SetScrollHereY(1.0f);
|
||||||
|
}
|
||||||
|
scroll.bottom = false;
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawInput() {
|
||||||
|
bool reclaimFocus = false;
|
||||||
|
ImGuiInputTextFlags inputTextFlags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
|
||||||
|
|
||||||
|
if ( ImGui::InputText("Input", &commandBuf, inputTextFlags, &TextEditCallbackStub, (void*) this) ) {
|
||||||
|
uf::string::trim( commandBuf );
|
||||||
|
if ( !commandBuf.empty() ) {
|
||||||
|
uf::console::execute(commandBuf);
|
||||||
|
commandBuf.clear();
|
||||||
|
}
|
||||||
|
historyPos = -1;
|
||||||
|
scroll.bottom = true;
|
||||||
|
reclaimFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
if ( reclaimFocus ) ImGui::SetKeyboardFocusHere(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TextEditCallbackStub( ImGuiInputTextCallbackData* data ) {
|
||||||
|
ConsoleWindow* console = (ConsoleWindow*) data->UserData;
|
||||||
|
return console->TextEditCallback(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextEditCallback( ImGuiInputTextCallbackData* data ) {
|
||||||
|
switch ( data->EventFlag ) {
|
||||||
|
case ImGuiInputTextFlags_CallbackCompletion: {
|
||||||
|
const char* end = data->Buf + data->CursorPos;
|
||||||
|
const char* start = end;
|
||||||
|
while ( start > data->Buf ) {
|
||||||
|
const char c = start[-1];
|
||||||
|
if (c == ' ' || c == '\t' || c == ',' || c == ';') break;
|
||||||
|
start--;
|
||||||
|
}
|
||||||
|
|
||||||
|
uf::stl::string input{ start, end };
|
||||||
|
uf::stl::vector<uf::stl::string> candidates;
|
||||||
|
for ( auto& pair : uf::console::commands ) {
|
||||||
|
if (pair.first.find(input) == 0) candidates.emplace_back(pair.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( candidates.empty() ) {
|
||||||
|
UF_MSG_ERROR("No match for `{}`", input);
|
||||||
|
} else if ( candidates.size() == 1 ) {
|
||||||
|
data->DeleteChars((int)(start - data->Buf), (int)(end - start));
|
||||||
|
data->InsertChars(data->CursorPos, candidates[0].c_str());
|
||||||
|
data->InsertChars(data->CursorPos, " ");
|
||||||
|
} else {
|
||||||
|
int matchLen = (int)(end - start);
|
||||||
|
while ( true ) {
|
||||||
|
if ( matchLen >= candidates[0].size() ) break;
|
||||||
|
char c = candidates[0][matchLen];
|
||||||
|
bool allMatch = true;
|
||||||
|
for ( size_t i = 1; i < candidates.size(); i++ ) {
|
||||||
|
if ( matchLen >= candidates[i].size() || candidates[i][matchLen] != c ) {
|
||||||
|
allMatch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !allMatch ) break;
|
||||||
|
matchLen++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( matchLen > (int) (end - start) ) {
|
||||||
|
data->DeleteChars((int)(start - data->Buf), (int)(end - start));
|
||||||
|
data->InsertChars(data->CursorPos, candidates[0].substr(0, matchLen).c_str());
|
||||||
|
} else {
|
||||||
|
UF_MSG_DEBUG("Matches: {}", uf::string::join(candidates, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ImGuiInputTextFlags_CallbackHistory: {
|
||||||
|
const int prevPos = historyPos;
|
||||||
|
if ( data->EventKey == ImGuiKey_UpArrow ) {
|
||||||
|
if ( historyPos == -1 ) historyPos = (int)uf::console::history.size() - 1;
|
||||||
|
else if ( historyPos > 0 ) historyPos--;
|
||||||
|
} else if ( data->EventKey == ImGuiKey_DownArrow ) {
|
||||||
|
if ( historyPos != -1 && ++historyPos >= (int)uf::console::history.size() ) historyPos = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( prevPos != historyPos ) {
|
||||||
|
const char* historyStr = (historyPos >= 0) ? uf::console::history[historyPos].c_str() : "";
|
||||||
|
data->DeleteChars(0, data->BufTextLen);
|
||||||
|
data->InsertChars(0, historyStr);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} consoleWindow;
|
||||||
|
}
|
||||||
@ -2,8 +2,10 @@
|
|||||||
#include <uf/ext/imgui/imgui.h>
|
#include <uf/ext/imgui/imgui.h>
|
||||||
#include <uf/utils/math/vector.h>
|
#include <uf/utils/math/vector.h>
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
|
#include <imgui/imgui_stdlib.h>
|
||||||
#include <uf/spec/renderer/universal.h>
|
#include <uf/spec/renderer/universal.h>
|
||||||
#include <uf/utils/math/physics.h>
|
#include <uf/utils/math/physics.h>
|
||||||
|
#include <uf/utils/window/payloads.h>
|
||||||
|
|
||||||
#if UF_USE_VULKAN
|
#if UF_USE_VULKAN
|
||||||
#include <imgui/backends/imgui_impl_vulkan.h>
|
#include <imgui/backends/imgui_impl_vulkan.h>
|
||||||
@ -14,235 +16,35 @@
|
|||||||
#if UF_ENV_WINDOWS
|
#if UF_ENV_WINDOWS
|
||||||
#include <imgui/backends/imgui_impl_win32.h>
|
#include <imgui/backends/imgui_impl_win32.h>
|
||||||
#endif
|
#endif
|
||||||
|
#if UF_ENV_LINUX
|
||||||
#include <imgui/imgui_stdlib.h>
|
// to-do: linux
|
||||||
|
#endif
|
||||||
#include <uf/utils/io/fmt.h>
|
|
||||||
#include <uf/utils/io/payloads.h>
|
|
||||||
#include <uf/utils/io/console.h>
|
|
||||||
#include <uf/utils/window/payloads.h>
|
|
||||||
|
|
||||||
bool ext::imgui::focused = false;
|
bool ext::imgui::focused = false;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#if UF_USE_VULKAN
|
#if UF_USE_VULKAN
|
||||||
VkDescriptorPool descriptorPool{};
|
uf::renderer::RenderMode* boundRenderMode = NULL;
|
||||||
|
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct ConsoleWindow {
|
|
||||||
ImGuiTextFilter filter;
|
|
||||||
int history = 0;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool automatic = true;
|
|
||||||
bool bottom = false;
|
|
||||||
} scroll;
|
|
||||||
|
|
||||||
uf::stl::unordered_map<uf::stl::string, std::function<uf::stl::string(const uf::stl::string&)>> commands;
|
|
||||||
|
|
||||||
pod::Vector2ui size{800, 600};
|
|
||||||
pod::Vector2ui position{64, 32};
|
|
||||||
|
|
||||||
void Draw( const uf::stl::string& title, bool& open ) {
|
|
||||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y), ImGuiCond_FirstUseEver);
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(position.x, position.y), ImGuiCond_FirstUseEver);
|
|
||||||
|
|
||||||
if (!ImGui::Begin(title.c_str(), &open)) {
|
|
||||||
ImGui::End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ImGui::BeginPopupContextItem() ) {
|
|
||||||
if ( ImGui::MenuItem("Close Console") ) open = false;
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ImGui::SmallButton("Clear") ) uf::console::clear();
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
bool copyToClipboard = ImGui::SmallButton("Copy");
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
if ( ImGui::BeginPopup("Options") ) {
|
|
||||||
ImGui::Checkbox("Auto-scroll", &this->scroll.bottom);
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ImGui::Button("Options") ) {
|
|
||||||
ImGui::OpenPopup("Options");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
this->filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
const float footerReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
|
||||||
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footerReserve), false, ImGuiWindowFlags_HorizontalScrollbar);
|
|
||||||
if ( ImGui::BeginPopupContextWindow() ) {
|
|
||||||
if ( ImGui::Selectable("Clear") ) uf::console::clear();
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1));
|
|
||||||
|
|
||||||
if ( copyToClipboard ) ImGui::LogToClipboard();
|
|
||||||
for ( auto& item : uf::console::log ) {
|
|
||||||
if ( !this->filter.PassFilter(item.c_str()) ) continue;
|
|
||||||
|
|
||||||
ImVec4 color = ImVec4(0, 0, 0, 0);
|
|
||||||
|
|
||||||
if ( uf::string::matched(item, "^\\[ERROR\\]") ) color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
|
|
||||||
else if ( uf::string::matched(item, "^\\> ") ) color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f);
|
|
||||||
|
|
||||||
if ( color.w > 0.0f ) ImGui::PushStyleColor(ImGuiCol_Text, color);
|
|
||||||
ImGui::TextUnformatted( item.c_str() );
|
|
||||||
if ( color.w > 0.0f ) ImGui::PopStyleColor();
|
|
||||||
}
|
|
||||||
if ( copyToClipboard ) ImGui::LogFinish();
|
|
||||||
|
|
||||||
if ( this->scroll.bottom || (this->scroll.automatic && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) ) {
|
|
||||||
ImGui::SetScrollHereY(1.0f);
|
|
||||||
}
|
|
||||||
this->scroll.bottom = false;
|
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
|
||||||
ImGui::EndChild();
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
bool reclaimFocus = false;
|
|
||||||
ImGuiInputTextFlags inputTextFlags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
|
|
||||||
|
|
||||||
uf::stl::string command;
|
|
||||||
if ( ImGui::InputText("Input", &command, inputTextFlags, &TextEditCallbackStub, (void *)this) ) {
|
|
||||||
this->history = -1;
|
|
||||||
this->scroll.bottom = true;
|
|
||||||
reclaimFocus = true;
|
|
||||||
|
|
||||||
|
|
||||||
// to-do: add a way to either asynchronously invoke commands or not
|
|
||||||
uf::console::execute( command );
|
|
||||||
/*
|
|
||||||
uf::thread::queue( uf::thread::asyncThreadName, [=](){
|
|
||||||
uf::console::execute( command );
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
// this blocks
|
|
||||||
uf::thread::queue( uf::thread::fetchWorker(), [=](){
|
|
||||||
uf::console::execute( command );
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
// this still blocks
|
|
||||||
auto tasks = uf::thread::schedule(true);
|
|
||||||
tasks.queue([=](){
|
|
||||||
uf::console::execute( command );
|
|
||||||
});
|
|
||||||
uf::thread::execute( tasks );
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SetItemDefaultFocus();
|
|
||||||
if ( reclaimFocus ) ImGui::SetKeyboardFocusHere(-1);
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int TextEditCallbackStub( ImGuiInputTextCallbackData *data ) {
|
|
||||||
ConsoleWindow *console = (ConsoleWindow*) data->UserData;
|
|
||||||
return console->TextEditCallback(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextEditCallback( ImGuiInputTextCallbackData* data ) {
|
|
||||||
switch ( data->EventFlag ) {
|
|
||||||
case ImGuiInputTextFlags_CallbackCompletion: {
|
|
||||||
const char* end = data->Buf + data->CursorPos;
|
|
||||||
const char* start = end;
|
|
||||||
while ( start > data->Buf ) {
|
|
||||||
const char c = start[-1];
|
|
||||||
if (c == ' ' || c == '\t' || c == ',' || c == ';')
|
|
||||||
break;
|
|
||||||
start--;
|
|
||||||
}
|
|
||||||
|
|
||||||
uf::stl::string input{ start, end };
|
|
||||||
uf::stl::vector<uf::stl::string> candidates;
|
|
||||||
for ( auto& pair : uf::console::commands ) {
|
|
||||||
if ( pair.first.find(input) == 0 ) candidates.emplace_back( input );
|
|
||||||
}
|
|
||||||
if ( candidates.empty() ) {
|
|
||||||
UF_MSG_ERROR("No match for `{}`", input);
|
|
||||||
} else if ( candidates.size() == 1 ) {
|
|
||||||
data->DeleteChars((int)(start - data->Buf), (int)(end - start));
|
|
||||||
data->InsertChars(data->CursorPos, candidates[0].c_str());
|
|
||||||
data->InsertChars(data->CursorPos, " ");
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
int match = (int) (end - start);
|
|
||||||
while ( true ) {
|
|
||||||
int c = 0;
|
|
||||||
bool allCandidatesMatches = true;
|
|
||||||
for ( auto i = 0; i < candidates.size() && allCandidatesMatches; ++i ) {
|
|
||||||
if ( i == 0 ) c = uf::string::uppercase( candidates[i].at(match) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case ImGuiInputTextFlags_CallbackHistory: {
|
|
||||||
const int previousPosition = this->history;
|
|
||||||
if ( data->EventKey == ImGuiKey_UpArrow ) {
|
|
||||||
if (this->history == -1) this->history = uf::console::history.size() - 1;
|
|
||||||
else if (this->history > 0) this->history--;
|
|
||||||
} else if ( data->EventKey == ImGuiKey_DownArrow ) {
|
|
||||||
if ( this->history != -1 && ++this->history >= uf::console::history.size() ) this->history = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( previousPosition != this->history ) {
|
|
||||||
const char* history = (this->history >= 0) ? uf::console::history[this->history].c_str() : "";
|
|
||||||
data->DeleteChars(0, data->BufTextLen);
|
|
||||||
data->InsertChars(0, history);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ConsoleWindow console;
|
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
}
|
|
||||||
void ext::imgui::initialize() {
|
|
||||||
#if UF_USE_VULKAN
|
|
||||||
if ( !uf::renderer::hasRenderMode("Gui", true) ) return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
IMGUI_CHECKVERSION();
|
|
||||||
ImGui::CreateContext();
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; //
|
|
||||||
// io.ConfigFlags |= ImGuiConfigFlags_NoMouse; //
|
|
||||||
io.DisplaySize = ImVec2(uf::renderer::settings::width,uf::renderer::settings::height);
|
|
||||||
io.MouseDrawCursor = false;
|
|
||||||
io.IniFilename = NULL;
|
|
||||||
|
|
||||||
uf::hooks.addHook( "window:Mouse.CursorVisibility", [&]( pod::payloads::windowMouseCursorVisibility& payload ){
|
|
||||||
io.MouseDrawCursor = payload.mouse.visible;
|
|
||||||
});
|
|
||||||
|
|
||||||
::console.position.x = uf::renderer::settings::width - ::console.size.x;
|
|
||||||
|
|
||||||
|
void initPlatform() {
|
||||||
#if UF_ENV_WINDOWS
|
#if UF_ENV_WINDOWS
|
||||||
ImGui_ImplWin32_Init(uf::renderer::device.window->getHandle());
|
ImGui_ImplWin32_Init(uf::renderer::device.window->getHandle());
|
||||||
#endif
|
#endif
|
||||||
|
#if UF_ENV_LINUX
|
||||||
|
// to-do: linux
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void initRenderer() {
|
||||||
#if UF_USE_VULKAN
|
#if UF_USE_VULKAN
|
||||||
auto& renderMode = uf::renderer::getRenderMode("Gui", true);
|
auto& renderMode = uf::renderer::getRenderMode("Gui", true);
|
||||||
{
|
::boundRenderMode = &renderMode;
|
||||||
VkDescriptorPoolSize poolSizes[] =
|
|
||||||
{
|
// create descriptor pool
|
||||||
|
VkDescriptorPoolSize poolSizes[] = {
|
||||||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
||||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
||||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
||||||
@ -261,99 +63,155 @@ void ext::imgui::initialize() {
|
|||||||
poolInfo.maxSets = 1000 * IM_ARRAYSIZE(poolSizes);
|
poolInfo.maxSets = 1000 * IM_ARRAYSIZE(poolSizes);
|
||||||
poolInfo.poolSizeCount = (uint32_t)IM_ARRAYSIZE(poolSizes);
|
poolInfo.poolSizeCount = (uint32_t)IM_ARRAYSIZE(poolSizes);
|
||||||
poolInfo.pPoolSizes = poolSizes;
|
poolInfo.pPoolSizes = poolSizes;
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorPool(uf::renderer::device, &poolInfo, NULL, &::descriptorPool));
|
VK_CHECK_RESULT(vkCreateDescriptorPool(uf::renderer::device, &poolInfo, NULL, &descriptorPool));
|
||||||
}
|
|
||||||
{
|
// initialize ImGui vulkan backend
|
||||||
ImGui_ImplVulkan_InitInfo imguiInitInfo = {};
|
ImGui_ImplVulkan_InitInfo imguiInitInfo = {};
|
||||||
imguiInitInfo.Instance = uf::renderer::device.instance;
|
imguiInitInfo.Instance = uf::renderer::device.instance;
|
||||||
imguiInitInfo.PhysicalDevice = uf::renderer::device.physicalDevice;
|
imguiInitInfo.PhysicalDevice = uf::renderer::device.physicalDevice;
|
||||||
imguiInitInfo.Device = uf::renderer::device.logicalDevice;
|
imguiInitInfo.Device = uf::renderer::device.logicalDevice;
|
||||||
imguiInitInfo.QueueFamily = uf::renderer::device.queueFamilyIndices.graphics;
|
imguiInitInfo.QueueFamily = uf::renderer::device.queueFamilyIndices.graphics;
|
||||||
imguiInitInfo.Queue = uf::renderer::device.getQueue( uf::renderer::QueueEnum::GRAPHICS );
|
imguiInitInfo.Queue = uf::renderer::device.getQueue(uf::renderer::QueueEnum::GRAPHICS);
|
||||||
imguiInitInfo.PipelineCache = uf::renderer::device.pipelineCache;
|
imguiInitInfo.PipelineCache = uf::renderer::device.pipelineCache;
|
||||||
imguiInitInfo.DescriptorPool = ::descriptorPool;
|
imguiInitInfo.DescriptorPool = descriptorPool;
|
||||||
imguiInitInfo.Subpass = 0;
|
imguiInitInfo.Subpass = 0;
|
||||||
imguiInitInfo.MinImageCount = uf::renderer::swapchain.buffers;
|
imguiInitInfo.MinImageCount = uf::renderer::swapchain.buffers;
|
||||||
imguiInitInfo.ImageCount = uf::renderer::swapchain.buffers;
|
imguiInitInfo.ImageCount = uf::renderer::swapchain.buffers;
|
||||||
imguiInitInfo.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
imguiInitInfo.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
imguiInitInfo.Allocator = NULL;
|
imguiInitInfo.Allocator = nullptr;
|
||||||
imguiInitInfo.CheckVkResultFn = NULL;
|
imguiInitInfo.CheckVkResultFn = nullptr;
|
||||||
|
|
||||||
ImGui_ImplVulkan_Init(&imguiInitInfo, renderMode.renderTarget.renderPass);
|
ImGui_ImplVulkan_Init(&imguiInitInfo, renderMode.renderTarget.renderPass);
|
||||||
}
|
|
||||||
{
|
// upload fonts
|
||||||
// Use any command queue
|
|
||||||
auto commandBuffer = uf::renderer::device.fetchCommandBuffer(uf::renderer::QueueEnum::GRAPHICS);
|
auto commandBuffer = uf::renderer::device.fetchCommandBuffer(uf::renderer::QueueEnum::GRAPHICS);
|
||||||
ImGui_ImplVulkan_CreateFontsTexture(commandBuffer.handle);
|
ImGui_ImplVulkan_CreateFontsTexture(commandBuffer.handle);
|
||||||
uf::renderer::device.flushCommandBuffer(commandBuffer);
|
uf::renderer::device.flushCommandBuffer(commandBuffer);
|
||||||
ImGui_ImplVulkan_DestroyFontUploadObjects();
|
ImGui_ImplVulkan_DestroyFontUploadObjects();
|
||||||
}
|
|
||||||
|
|
||||||
{
|
// bind render callback
|
||||||
renderMode.bindCallback( 0, [&]( VkCommandBuffer commandBuffer, size_t _ ){
|
renderMode.bindCallback(0, [&]( VkCommandBuffer cb, size_t _ ) {
|
||||||
ImDrawData* drawData = ImGui::GetDrawData();
|
ImDrawData* drawData = ImGui::GetDrawData();
|
||||||
ImGui_ImplVulkan_RenderDrawData(drawData, commandBuffer);
|
if (drawData) ImGui_ImplVulkan_RenderDrawData(drawData, cb);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
#elif UF_USE_OPENGL
|
#elif UF_USE_OPENGL
|
||||||
ImGui_ImplOpenGL2_Init();
|
ImGui_ImplOpenGL2_Init();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void newFramePlatform() {
|
||||||
|
#if UF_ENV_WINDOWS
|
||||||
|
ImGui_ImplWin32_NewFrame();
|
||||||
|
#endif
|
||||||
|
#if UF_ENV_LINUX
|
||||||
|
// to-do: linux
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void newFrameRenderer() {
|
||||||
|
#if UF_USE_VULKAN
|
||||||
|
auto& renderMode = uf::renderer::getRenderMode("Gui", true);
|
||||||
|
renderMode.rerecord = true;
|
||||||
|
ImGui_ImplVulkan_NewFrame();
|
||||||
|
#elif UF_USE_OPENGL
|
||||||
|
ImGui_ImplOpenGL2_NewFrame();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownPlatform() {
|
||||||
|
#if UF_ENV_WINDOWS
|
||||||
|
ImGui_ImplWin32_Shutdown();
|
||||||
|
#endif
|
||||||
|
#if UF_ENV_LINUX
|
||||||
|
// to-do: linux
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownRenderer() {
|
||||||
|
#if UF_USE_VULKAN
|
||||||
|
ImGui_ImplVulkan_Shutdown();
|
||||||
|
if ( descriptorPool != VK_NULL_HANDLE ) {
|
||||||
|
vkDestroyDescriptorPool(uf::renderer::device, descriptorPool, nullptr);
|
||||||
|
descriptorPool = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
#elif UF_USE_OPENGL
|
||||||
|
ImGui_ImplOpenGL2_Shutdown();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ext::imgui::initialize() {
|
||||||
|
#if UF_USE_VULKAN
|
||||||
|
if ( !uf::renderer::hasRenderMode("Gui", true) ) return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
::initialized = true;
|
IMGUI_CHECKVERSION();
|
||||||
|
ImGui::CreateContext();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
|
||||||
|
io.DisplaySize = ImVec2((float) uf::renderer::settings::width, (float) uf::renderer::settings::height);
|
||||||
|
io.MouseDrawCursor = false;
|
||||||
|
io.IniFilename = nullptr;
|
||||||
|
|
||||||
|
uf::hooks.addHook("window:Mouse.CursorVisibility", [&](pod::payloads::windowMouseCursorVisibility& payload) {
|
||||||
|
ImGui::GetIO().MouseDrawCursor = payload.mouse.visible;
|
||||||
|
});
|
||||||
|
|
||||||
|
::initPlatform();
|
||||||
|
::initRenderer();
|
||||||
|
|
||||||
|
::initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ext::imgui::tick() {
|
void ext::imgui::tick() {
|
||||||
if ( !::initialized ) ext::imgui::initialize();
|
if ( !::initialized ) ext::imgui::initialize();
|
||||||
if ( !::initialized ) return;
|
if ( !::initialized ) return;
|
||||||
|
// no GUI render mode found
|
||||||
|
if ( !uf::renderer::hasRenderMode("Gui", true) ) return;
|
||||||
|
auto& renderMode = uf::renderer::getRenderMode("Gui", true);
|
||||||
|
|
||||||
|
// check if rendermode changed
|
||||||
|
if ( ::boundRenderMode != &renderMode ) {
|
||||||
|
::shutdownRenderer();
|
||||||
|
::initRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
io.DeltaTime = uf::physics::time::delta;
|
io.DeltaTime = uf::physics::time::delta;
|
||||||
io.DisplaySize = ImVec2(uf::renderer::settings::width,uf::renderer::settings::height);
|
io.DisplaySize = ImVec2((float)uf::renderer::settings::width, (float)uf::renderer::settings::height);
|
||||||
|
|
||||||
ext::imgui::focused = io.WantCaptureKeyboard || io.WantCaptureMouse;
|
ext::imgui::focused = io.WantCaptureKeyboard || io.WantCaptureMouse;
|
||||||
|
|
||||||
#if UF_USE_VULKAN
|
::newFrameRenderer();
|
||||||
auto& renderMode = uf::renderer::getRenderMode("Gui", true);
|
::newFramePlatform();
|
||||||
renderMode.rerecord = true;
|
|
||||||
|
|
||||||
ImGui_ImplVulkan_NewFrame();
|
|
||||||
#elif UF_USE_OPENGL
|
|
||||||
ImGui_ImplOpenGL2_NewFrame();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if UF_ENV_WINDOWS
|
|
||||||
ImGui_ImplWin32_NewFrame();
|
|
||||||
#endif
|
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
auto& scene = uf::scene::getCurrentScene();
|
uf::hooks.call("gui:IMGUI.tick");
|
||||||
if ( scene.globalFindByName("Gui: Menu") ) {
|
|
||||||
bool opened;
|
|
||||||
console.Draw("Console", opened);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ext::imgui::render() {
|
void ext::imgui::render() {
|
||||||
if ( !::initialized ) return;
|
if (!::initialized) return;
|
||||||
|
|
||||||
#if UF_USE_OPENGL
|
#if UF_USE_OPENGL
|
||||||
auto renderMode = uf::renderer::getCurrentRenderMode();
|
auto renderMode = uf::renderer::getCurrentRenderMode();
|
||||||
if ( !renderMode || renderMode->getName() != "Gui" ) return;
|
if ( !renderMode || renderMode->getName() != "Gui" ) return;
|
||||||
auto data = ImGui::GetDrawData();
|
|
||||||
if ( !data ) return;
|
|
||||||
ImGui_ImplOpenGL2_RenderDrawData(data);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
void ext::imgui::terminate() {
|
|
||||||
if ( !::initialized ) return;
|
|
||||||
#if UF_USE_VULKAN
|
|
||||||
ImGui_ImplVulkan_Shutdown();
|
|
||||||
#elif UF_USE_OPENGL
|
|
||||||
ImGui_ImplOpenGL2_Shutdown();
|
|
||||||
#endif
|
|
||||||
#if UF_ENV_WINDOWS
|
|
||||||
ImGui_ImplWin32_Shutdown();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ImGui::DestroyContext();
|
ImDrawData* data = ImGui::GetDrawData();
|
||||||
|
if (data) ImGui_ImplOpenGL2_RenderDrawData(data);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
void ext::imgui::terminate() {
|
||||||
|
if (!::initialized) return;
|
||||||
|
|
||||||
|
::shutdownRenderer();
|
||||||
|
::shutdownPlatform();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
|
::initialized = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
109
engine/src/ext/imgui/threadMetrics.inl
Normal file
109
engine/src/ext/imgui/threadMetrics.inl
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
namespace {
|
||||||
|
struct ThreadMetricsWindow {
|
||||||
|
static const int HISTORY_SIZE = 120;
|
||||||
|
|
||||||
|
struct ThreadHistory {
|
||||||
|
float activeTime[HISTORY_SIZE] = { 0.0f };
|
||||||
|
float idleTime[HISTORY_SIZE] = { 0.0f };
|
||||||
|
float totalTime[HISTORY_SIZE] = { 0.0f };
|
||||||
|
int offset = 0;
|
||||||
|
bool filled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
uf::stl::unordered_map<uf::stl::string, ThreadHistory> histories;
|
||||||
|
|
||||||
|
pod::Vector2ui size{400, 500};
|
||||||
|
pod::Vector2ui position{32, 32};
|
||||||
|
|
||||||
|
void Draw(const uf::stl::string& title, bool& open) {
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(size.x, size.y), ImGuiCond_FirstUseEver);
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(position.x, position.y), ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
|
if (!ImGui::Begin(title.c_str(), &open)) {
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UF_THREAD_METRICS
|
||||||
|
auto metrics = uf::thread::collectStats();
|
||||||
|
|
||||||
|
if (ImGui::BeginTable("ThreadMetricsTable", 2, ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) {
|
||||||
|
ImGui::TableSetupColumn("Thread Info", ImGuiTableColumnFlags_WidthFixed, 150.0f);
|
||||||
|
ImGui::TableSetupColumn("History", ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
|
for (auto& [name, stats] : metrics) {
|
||||||
|
float active = std::get<0>(stats);
|
||||||
|
float idle = std::get<1>(stats);
|
||||||
|
float total = std::get<2>(stats);
|
||||||
|
uint32_t tasks = std::get<3>(stats);
|
||||||
|
|
||||||
|
auto& history = histories[name];
|
||||||
|
|
||||||
|
history.activeTime[history.offset] = active;
|
||||||
|
history.idleTime[history.offset] = idle;
|
||||||
|
history.totalTime[history.offset] = total;
|
||||||
|
history.offset++;
|
||||||
|
if (history.offset >= HISTORY_SIZE) {
|
||||||
|
history.offset = 0;
|
||||||
|
history.filled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = history.filled ? HISTORY_SIZE : (history.offset == 0 ? 1 : history.offset);
|
||||||
|
float avgActive = 0.0f, avgIdle = 0.0f, avgTotal = 0.0f;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
avgActive += history.activeTime[i];
|
||||||
|
avgIdle += history.idleTime[i];
|
||||||
|
avgTotal += history.totalTime[i];
|
||||||
|
}
|
||||||
|
avgActive /= count;
|
||||||
|
avgIdle /= count;
|
||||||
|
avgTotal /= count;
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
|
ImGui::PushID(name.c_str());
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::TextUnformatted(name.c_str());
|
||||||
|
ImGui::Text("Tasks: %u", tasks);
|
||||||
|
ImGui::Text("Time: %.2f ms", avgTotal);
|
||||||
|
|
||||||
|
float utilization = (avgTotal > 0.0f) ? (avgActive / avgTotal) : 0.0f;
|
||||||
|
char utilText[32];
|
||||||
|
snprintf(utilText, sizeof(utilText), "Load: %.0f%%", utilization * 100.0f);
|
||||||
|
|
||||||
|
if (utilization > 0.8f) ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.8f, 0.2f, 0.2f, 1.0f));
|
||||||
|
ImGui::ProgressBar(utilization, ImVec2(-FLT_MIN, 0), utilText);
|
||||||
|
if (utilization > 0.8f) ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(1);
|
||||||
|
|
||||||
|
float graphWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
float max_val = 16.6f;
|
||||||
|
|
||||||
|
char overlayActive[32];
|
||||||
|
snprintf(overlayActive, sizeof(overlayActive), "Act: %.2fms", avgActive);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.2f, 0.7f, 0.2f, 1.0f));
|
||||||
|
ImGui::PlotHistogram("##Act", history.activeTime, HISTORY_SIZE, history.offset, overlayActive, 0.0f, max_val, ImVec2(graphWidth, 30));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
char overlayIdle[32];
|
||||||
|
snprintf(overlayIdle, sizeof(overlayIdle), "Idl: %.2fms", avgIdle);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.5f, 0.5f, 0.5f, 0.5f));
|
||||||
|
ImGui::PlotHistogram("##Idl", history.idleTime, HISTORY_SIZE, history.offset, overlayIdle, 0.0f, max_val, ImVec2(graphWidth, 20));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ImGui::TextColored(ImVec4(1,0,0,1), "UF_THREAD_METRICS is disabled!");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
} threadMetrics;
|
||||||
|
}
|
||||||
@ -81,6 +81,25 @@ uf::stl::vector<const char*> uf::string::cStrings( const uf::stl::vector<uf::stl
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uf::stl::string uf::string::ltrim( const uf::stl::string& str ) {
|
||||||
|
auto s = str;
|
||||||
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uf::stl::string uf::string::rtrim( const uf::stl::string& str ) {
|
||||||
|
auto s = str;
|
||||||
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}).base(), s.end());
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
uf::stl::string uf::string::trim( const uf::stl::string& str ) {
|
||||||
|
return uf::string::rtrim( uf::string::ltrim( str ) );
|
||||||
|
}
|
||||||
|
|
||||||
uf::stl::string uf::string::replace( const uf::stl::string& string, const uf::stl::string& search, const uf::stl::string& replace ) {
|
uf::stl::string uf::string::replace( const uf::stl::string& string, const uf::stl::string& search, const uf::stl::string& replace ) {
|
||||||
uf::stl::string result = string;
|
uf::stl::string result = string;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user