initial commit; cv 0.0.3
This commit is contained in:
194
Features/IUIFeature.hpp
Normal file
194
Features/IUIFeature.hpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <exception>
|
||||
|
||||
#include <DynamicOutput/DynamicOutput.hpp>
|
||||
#include <Unreal/UObject.hpp>
|
||||
|
||||
#include <Static/LogGate.hpp>
|
||||
|
||||
namespace PalUIExtension::Features
|
||||
{
|
||||
using namespace RC;
|
||||
using namespace RC::Unreal;
|
||||
|
||||
class IUIFeature
|
||||
{
|
||||
public:
|
||||
using StringType = std::basic_string<TCHAR>;
|
||||
|
||||
virtual ~IUIFeature() = default;
|
||||
|
||||
virtual auto Name() const -> const TCHAR* = 0;
|
||||
|
||||
virtual auto InstallHooks() -> void = 0;
|
||||
|
||||
virtual auto OnWorldTeardown() -> void {}
|
||||
|
||||
virtual auto SetEnabled(bool enabled) -> void {}
|
||||
|
||||
virtual auto GetEnabled() -> bool { return false; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ---- logging ---------------------------------------------------
|
||||
|
||||
template <typename... Args>
|
||||
auto SendLog(LogLevel::LogLevel Level, const TCHAR* Fmt, Args&&... Fargs) const -> void
|
||||
{
|
||||
Emit(Level, Fmt, std::forward<Args>(Fargs)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto SendLogOnce(const TCHAR* Key, LogLevel::LogLevel Level, const TCHAR* Fmt, Args&&... Fargs) const -> void
|
||||
{
|
||||
if (!Key) return;
|
||||
if (!m_log_once_keys.insert(StringType{ Key }).second) return;
|
||||
|
||||
Emit(Level, Fmt, std::forward<Args>(Fargs)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto SendLogThrottled(const TCHAR* Key, int64_t IntervalMs, LogLevel::LogLevel Level, const TCHAR* Fmt, Args&&... Fargs) const -> void
|
||||
{
|
||||
if (!Key) return;
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
const auto Now = Clock::now();
|
||||
|
||||
auto& Last = m_log_throttle[StringType{ Key }];
|
||||
if (Last.time_since_epoch().count() != 0 && std::chrono::duration_cast<std::chrono::milliseconds>(Now - Last).count() < IntervalMs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Last = Now;
|
||||
|
||||
Emit(Level, Fmt, std::forward<Args>(Fargs)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto SendVerbose(const TCHAR* Fmt, Args&&... Fargs) const -> void
|
||||
{
|
||||
if constexpr (Static::kVerboseLogging)
|
||||
{
|
||||
Emit(LogLevel::Verbose, Fmt, std::forward<Args>(Fargs)...);
|
||||
}
|
||||
}
|
||||
|
||||
auto ResetLogOnce() const -> void
|
||||
{
|
||||
m_log_once_keys.clear();
|
||||
m_log_throttle.clear();
|
||||
}
|
||||
|
||||
// ---- argument helpers -------------------------------------------
|
||||
static auto NameOf(UObject* Obj) -> StringType { return Obj ? Obj->GetName() : StringType{ STR("<null>") }; }
|
||||
|
||||
static auto FullNameOf(UObject* Obj) -> StringType { return Obj ? Obj->GetFullName() : StringType{ STR("<null>") }; }
|
||||
|
||||
static auto ClassNameOf(UObject* Obj) -> StringType
|
||||
{
|
||||
if (!Obj) return StringType{ STR("<null>") };
|
||||
UClass* Cls = Obj->GetClassPrivate();
|
||||
return Cls ? Cls->GetName() : StringType{ STR("<no class>") };
|
||||
}
|
||||
|
||||
static auto AddressOf(const void* P) -> uint64_t
|
||||
{
|
||||
return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(P));
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... Args>
|
||||
auto Emit(LogLevel::LogLevel Level, const TCHAR* Fmt, Args&&... Fargs) const -> void
|
||||
{
|
||||
if (!Fmt) return;
|
||||
|
||||
try
|
||||
{
|
||||
switch (Level)
|
||||
{
|
||||
case LogLevel::Error:
|
||||
Output::send<LogLevel::Error>(STR("PalUIExt [{}] "), Name());
|
||||
Output::send<LogLevel::Error>(Fmt, std::forward<Args>(Fargs)...);
|
||||
Output::send<LogLevel::Error>(STR("\n"));
|
||||
break;
|
||||
case LogLevel::Warning:
|
||||
Output::send<LogLevel::Warning>(STR("PalUIExt [{}] "), Name());
|
||||
Output::send<LogLevel::Warning>(Fmt, std::forward<Args>(Fargs)...);
|
||||
Output::send<LogLevel::Warning>(STR("\n"));
|
||||
break;
|
||||
case LogLevel::Verbose:
|
||||
Output::send<LogLevel::Verbose>(STR("PalUIExt [{}] "), Name());
|
||||
Output::send<LogLevel::Verbose>(Fmt, std::forward<Args>(Fargs)...);
|
||||
Output::send<LogLevel::Verbose>(STR("\n"));
|
||||
break;
|
||||
default:
|
||||
Output::send<LogLevel::Normal>(STR("PalUIExt [{}] "), Name());
|
||||
Output::send<LogLevel::Normal>(Fmt, std::forward<Args>(Fargs)...);
|
||||
Output::send<LogLevel::Normal>(STR("\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
try { Output::send<LogLevel::Error>(STR("PalUIExt: malformed log format string\n")); }
|
||||
catch (...) {}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::unordered_set<StringType> m_log_once_keys;
|
||||
mutable std::unordered_map<StringType, std::chrono::steady_clock::time_point> m_log_throttle;
|
||||
};
|
||||
|
||||
|
||||
class ScopedHotPathTimer
|
||||
{
|
||||
public:
|
||||
ScopedHotPathTimer(const IUIFeature& Feature, const TCHAR* Label,
|
||||
int64_t ReportAboveMicros = 250)
|
||||
: m_feature(Feature), m_label(Label), m_threshold(ReportAboveMicros)
|
||||
{
|
||||
if constexpr (Static::kProfileHotPath)
|
||||
{
|
||||
m_start = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedHotPathTimer()
|
||||
{
|
||||
if constexpr (Static::kProfileHotPath)
|
||||
{
|
||||
const auto Micros = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - m_start).count();
|
||||
|
||||
if (Micros >= m_threshold)
|
||||
{
|
||||
m_feature.SendLogThrottled(m_label, 1000, LogLevel::Warning,
|
||||
STR("slow path: {} took {}us"), m_label, Micros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScopedHotPathTimer(const ScopedHotPathTimer&) = delete;
|
||||
ScopedHotPathTimer& operator=(const ScopedHotPathTimer&) = delete;
|
||||
|
||||
private:
|
||||
const IUIFeature& m_feature;
|
||||
const TCHAR* m_label;
|
||||
int64_t m_threshold;
|
||||
std::chrono::steady_clock::time_point m_start{};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user