486 lines
13 KiB
C++
486 lines
13 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "VisualLoggerDatabase.h"
|
|
#include "Engine/EngineTypes.h"
|
|
#include "Engine/World.h"
|
|
#include "EngineUtils.h"
|
|
#include "LogVisualizerSettings.h"
|
|
#include "LogVisualizerPublic.h"
|
|
#include "VisualLoggerRenderingActor.h"
|
|
|
|
TSharedPtr< struct FVisualLoggerDatabase > FVisualLoggerDatabase::StaticInstance = nullptr;
|
|
TSharedPtr< struct FVisualLoggerGraphsDatabase > FVisualLoggerGraphsDatabase::StaticInstance = nullptr;
|
|
|
|
void FVisualLoggerDBRow::AddItem(const FVisualLogDevice::FVisualLogEntryItem& NewItem)
|
|
{
|
|
const int32 ItemIndex = Items.Add(NewItem);
|
|
const int32 HiddenIndex = HiddenItems.Add(false);
|
|
checkf(ItemIndex == HiddenIndex, TEXT("Items and HiddenItems should have matching indices."));
|
|
|
|
DBEvents.OnNewItem.Broadcast(*this, ItemIndex);
|
|
}
|
|
|
|
void FVisualLoggerDBRow::MoveTo(int32 Index)
|
|
{
|
|
const int32 OldItemIndex = CurrentItemIndex;
|
|
CurrentItemIndex = Items.IsValidIndex(Index) ? Index : INDEX_NONE;
|
|
if (OldItemIndex != CurrentItemIndex)
|
|
{
|
|
DBEvents.OnItemSelectionChanged.Broadcast(*this, CurrentItemIndex);
|
|
}
|
|
}
|
|
|
|
const FVisualLogDevice::FVisualLogEntryItem& FVisualLoggerDBRow::GetCurrentItem() const
|
|
{
|
|
check(Items.IsValidIndex(CurrentItemIndex));
|
|
return Items[CurrentItemIndex];
|
|
}
|
|
|
|
void FVisualLoggerDBRow::SetItemVisibility(int32 ItemIndex, bool IsVisible)
|
|
{
|
|
const bool IsHidden = !IsVisible;
|
|
if (HiddenItems[ItemIndex] != IsHidden)
|
|
{
|
|
HiddenItems[ItemIndex] = IsHidden;
|
|
NumHiddenItems += IsHidden ? 1 : -1;
|
|
check(NumHiddenItems >= 0);
|
|
}
|
|
}
|
|
|
|
int32 FVisualLoggerDBRow::GetClosestItem(double Time) const
|
|
{
|
|
int32 BestItemIndex = INDEX_NONE;
|
|
double BestDistance = MAX_dbl;
|
|
for (int32 Index = 0; Index < Items.Num(); Index++)
|
|
{
|
|
auto& CurrentEntryItem = Items[Index];
|
|
|
|
if (IsItemVisible(Index) == false)
|
|
{
|
|
continue;
|
|
}
|
|
TArray<FVisualLoggerCategoryVerbosityPair> OutCategories;
|
|
const double CurrentDist = FMath::Abs(Time - CurrentEntryItem.Entry.TimeStamp);
|
|
if (CurrentDist < BestDistance)
|
|
{
|
|
BestDistance = CurrentDist;
|
|
BestItemIndex = Index;
|
|
}
|
|
}
|
|
|
|
const double CurrentDist = Items.IsValidIndex(CurrentItemIndex) && IsItemVisible(CurrentItemIndex) ? FMath::Abs(Time - Items[CurrentItemIndex].Entry.TimeStamp) : MAX_dbl;
|
|
if (BestItemIndex != INDEX_NONE && CurrentDist > BestDistance)
|
|
{
|
|
return BestItemIndex;
|
|
}
|
|
|
|
return CurrentItemIndex;
|
|
}
|
|
|
|
int32 FVisualLoggerDBRow::GetClosestItem(double Time, double ScrubTime) const
|
|
{
|
|
int32 BestItemIndex = INDEX_NONE;
|
|
double BestDistance = MAX_dbl;
|
|
for (int32 Index = 0; Index < Items.Num(); Index++)
|
|
{
|
|
auto& CurrentEntryItem = Items[Index];
|
|
|
|
if (CurrentEntryItem.Entry.TimeStamp > ScrubTime)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (IsItemVisible(Index) == false)
|
|
{
|
|
continue;
|
|
}
|
|
TArray<FVisualLoggerCategoryVerbosityPair> OutCategories;
|
|
const double CurrentDist = FMath::Abs(CurrentEntryItem.Entry.TimeStamp - Time);
|
|
if (CurrentDist < BestDistance && CurrentEntryItem.Entry.TimeStamp <= Time)
|
|
{
|
|
BestDistance = CurrentDist;
|
|
BestItemIndex = Index;
|
|
}
|
|
}
|
|
|
|
if (BestItemIndex != INDEX_NONE)
|
|
{
|
|
return BestItemIndex;
|
|
}
|
|
|
|
return CurrentItemIndex;
|
|
}
|
|
|
|
FVisualLoggerDatabase& FVisualLoggerDatabase::Get()
|
|
{
|
|
return *StaticInstance;
|
|
}
|
|
|
|
void FVisualLoggerDatabase::Initialize()
|
|
{
|
|
StaticInstance = MakeShareable(new FVisualLoggerDatabase);
|
|
FVisualLoggerGraphsDatabase::Initialize();
|
|
}
|
|
|
|
void FVisualLoggerDatabase::Shutdown()
|
|
{
|
|
FVisualLoggerGraphsDatabase::Shutdown();
|
|
StaticInstance.Reset();
|
|
}
|
|
|
|
void FVisualLoggerDatabase::Reset()
|
|
{
|
|
Rows.Reset();
|
|
RowNameToIndex.Reset();
|
|
SelectedRows.Reset();
|
|
HiddenRows.Reset();
|
|
FVisualLoggerGraphsDatabase::Get().Reset();
|
|
}
|
|
|
|
void FVisualLoggerDatabase::AddItem(const FVisualLogDevice::FVisualLogEntryItem& NewItem)
|
|
{
|
|
const bool bCreateNew = RowNameToIndex.Contains(NewItem.OwnerName) == false;
|
|
int32 RowIndex = INDEX_NONE;
|
|
FVisualLoggerDBRow* CurrentRow = nullptr;
|
|
|
|
if (bCreateNew)
|
|
{
|
|
RowIndex = Rows.Add(FVisualLoggerDBRow(DBEvents, NewItem.OwnerName, NewItem.OwnerDisplayName, NewItem.OwnerClassName));
|
|
RowNameToIndex.Add(NewItem.OwnerName, RowIndex);
|
|
CurrentRow = &Rows[RowIndex];
|
|
DBEvents.OnNewRow.Broadcast(*CurrentRow);
|
|
}
|
|
else
|
|
{
|
|
RowIndex = RowNameToIndex[NewItem.OwnerName];
|
|
if (ensure(Rows.IsValidIndex(RowIndex)))
|
|
{
|
|
CurrentRow = &Rows[RowIndex];
|
|
}
|
|
}
|
|
|
|
if (CurrentRow)
|
|
{
|
|
CurrentRow->AddItem(NewItem);
|
|
FVisualLoggerGraphsDatabase::Get().AddItem(NewItem);
|
|
}
|
|
}
|
|
|
|
bool FVisualLoggerDatabase::ContainsRowByName(FName InName)
|
|
{
|
|
return RowNameToIndex.Contains(InName);
|
|
}
|
|
|
|
FVisualLoggerDBRow& FVisualLoggerDatabase::GetRowByName(FName InName)
|
|
{
|
|
const int32* Idx = RowNameToIndex.Find(InName);
|
|
check(Idx);
|
|
|
|
return Rows[*Idx];
|
|
}
|
|
|
|
void FVisualLoggerDatabase::SelectRow(FName InName, bool bDeselectOtherNodes)
|
|
{
|
|
const bool bAlreadySelected = SelectedRows.Find(InName) != INDEX_NONE;
|
|
if (bAlreadySelected && (!bDeselectOtherNodes || SelectedRows.Num() == 1))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bDeselectOtherNodes)
|
|
{
|
|
for (auto CurrentName : SelectedRows)
|
|
{
|
|
if (CurrentName != InName)
|
|
{
|
|
FVisualLoggerDBRow& DBRow = GetRowByName(CurrentName);
|
|
DBRow.MoveTo(INDEX_NONE);
|
|
}
|
|
}
|
|
SelectedRows.Reset();
|
|
if (bAlreadySelected)
|
|
{
|
|
SelectedRows.AddUnique(InName);
|
|
DBEvents.OnRowSelectionChanged.Broadcast(SelectedRows);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!bAlreadySelected)
|
|
{
|
|
SelectedRows.AddUnique(InName);
|
|
DBEvents.OnRowSelectionChanged.Broadcast(SelectedRows);
|
|
}
|
|
}
|
|
|
|
void FVisualLoggerDatabase::DeselectRow(FName InName)
|
|
{
|
|
const bool bSelected = SelectedRows.Find(InName) != INDEX_NONE;
|
|
if (bSelected)
|
|
{
|
|
FVisualLoggerDBRow& DBRow = GetRowByName(InName);
|
|
DBRow.MoveTo(INDEX_NONE);
|
|
|
|
SelectedRows.RemoveSingleSwap(InName, EAllowShrinking::No);
|
|
DBEvents.OnRowSelectionChanged.Broadcast(SelectedRows);
|
|
}
|
|
}
|
|
|
|
const TArray<FName>& FVisualLoggerDatabase::GetSelectedRows() const
|
|
{
|
|
return SelectedRows;
|
|
}
|
|
|
|
void FVisualLoggerDatabase::SetRowVisibility(FName RowName, bool SetAsVisible)
|
|
{
|
|
const bool IsVisible = HiddenRows.Find(RowName) == INDEX_NONE;
|
|
if (IsVisible != SetAsVisible)
|
|
{
|
|
if (SetAsVisible)
|
|
{
|
|
HiddenRows.RemoveSingleSwap(RowName);
|
|
}
|
|
else
|
|
{
|
|
HiddenRows.AddUnique(RowName);
|
|
}
|
|
|
|
DBEvents.OnRowChangedVisibility.Broadcast(RowName);
|
|
}
|
|
}
|
|
|
|
void FVisualLoggerDatabase::RemoveRow(FName RowName)
|
|
{
|
|
if (SelectedRows.Find(RowName) != INDEX_NONE)
|
|
{
|
|
SelectedRows.RemoveSwap(RowName);
|
|
}
|
|
if (HiddenRows.Find(RowName) != INDEX_NONE)
|
|
{
|
|
HiddenRows.RemoveSwap(RowName);
|
|
}
|
|
if (RowNameToIndex.Contains(RowName))
|
|
{
|
|
const int32 RemovedIndex = RowNameToIndex.FindAndRemoveChecked(RowName);
|
|
Rows.RemoveAtSwap(RemovedIndex, EAllowShrinking::No);
|
|
if (Rows.IsValidIndex(RemovedIndex))
|
|
{
|
|
RowNameToIndex[Rows[RemovedIndex].GetOwnerName()] = RemovedIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
bool FVisualLoggerGraph::IsDataVisible(FName DataName) const
|
|
{
|
|
return HiddenGraphs.Contains(DataName) == false;
|
|
}
|
|
|
|
void FVisualLoggerGraph::SetDataVisibility(FName DataName, bool IsVisible)
|
|
{
|
|
if (IsVisible)
|
|
{
|
|
HiddenGraphs.Remove(DataName);
|
|
}
|
|
else
|
|
{
|
|
HiddenGraphs.AddUnique(DataName);
|
|
}
|
|
}
|
|
|
|
FVisualLoggerGraphData& FVisualLoggerGraph::FindOrAddDataByName(FName DataName)
|
|
{
|
|
if (DataNameToIndex.Contains(DataName))
|
|
{
|
|
return DataGraphs[DataNameToIndex[DataName]];
|
|
}
|
|
|
|
const int32 Index = DataGraphs.Add(FVisualLoggerGraphData(DataName));
|
|
DataNameToIndex.Add(DataName, Index);
|
|
FVisualLoggerDatabase::Get().GetEvents().OnGraphDataNameAddedEvent.Broadcast(OwnerName, GraphName, DataName);
|
|
|
|
return DataGraphs[Index];
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
FVisualLoggerGraphsDatabase& FVisualLoggerGraphsDatabase::Get()
|
|
{
|
|
return *StaticInstance;
|
|
}
|
|
|
|
void FVisualLoggerGraphsDatabase::Initialize()
|
|
{
|
|
StaticInstance = MakeShareable(new FVisualLoggerGraphsDatabase);
|
|
}
|
|
|
|
void FVisualLoggerGraphsDatabase::Shutdown()
|
|
{
|
|
StaticInstance.Reset();
|
|
}
|
|
|
|
void FVisualLoggerGraphsDatabase::Reset()
|
|
{
|
|
OwnerNameToGraphs.Reset();
|
|
HiddenGraphs.Reset();
|
|
}
|
|
|
|
void FVisualLoggerGraphsDatabase::AddItem(const FVisualLogDevice::FVisualLogEntryItem& NewItem)
|
|
{
|
|
FVisualLoggerGraphHelper* GraphHelperPtr = OwnerNameToGraphs.Find(NewItem.OwnerName);
|
|
if (!GraphHelperPtr)
|
|
{
|
|
GraphHelperPtr = &OwnerNameToGraphs.Add(NewItem.OwnerName);
|
|
}
|
|
FVisualLoggerGraphHelper& GraphHelper = *GraphHelperPtr;
|
|
|
|
for (const FVisualLogHistogramSample& HistogramSample : NewItem.Entry.HistogramSamples)
|
|
{
|
|
int32 GraphIndex = INDEX_NONE;
|
|
if (GraphHelper.GraphNameToIndex.Contains(HistogramSample.GraphName) == false)
|
|
{
|
|
FVisualLoggerGraph Graph(NewItem.OwnerName);
|
|
Graph.SetGraphName(HistogramSample.GraphName);
|
|
GraphIndex = GraphHelper.AllGraphs.Add(Graph);
|
|
GraphHelper.GraphNameToIndex.Add(HistogramSample.GraphName, GraphIndex);
|
|
|
|
FVisualLoggerDatabase::Get().GetEvents().OnGraphAddedEvent.Broadcast(NewItem.OwnerName, HistogramSample.GraphName);
|
|
}
|
|
else
|
|
{
|
|
GraphIndex = GraphHelper.GraphNameToIndex[HistogramSample.GraphName];
|
|
}
|
|
|
|
check(GraphIndex != INDEX_NONE);
|
|
FVisualLoggerGraphData& GraphData = GraphHelper.AllGraphs[GraphIndex].FindOrAddDataByName(HistogramSample.DataName);
|
|
GraphData.Samples.Add(HistogramSample.SampleValue);
|
|
GraphData.TimeStamps.Add(NewItem.Entry.TimeStamp);
|
|
}
|
|
}
|
|
|
|
bool FVisualLoggerGraphsDatabase::IsGraphVisible(FName OwnerName, FName GraphName)
|
|
{
|
|
return HiddenGraphs.Find(*(OwnerName.ToString() + TEXT("$") + GraphName.ToString())) == INDEX_NONE;
|
|
}
|
|
|
|
void FVisualLoggerGraphsDatabase::SetGraphVisibility(FName OwnerName, FName GraphName, bool SetAsVisible)
|
|
{
|
|
const FName FullName = *(OwnerName.ToString() + TEXT("$") + GraphName.ToString());
|
|
const bool IsVisible = HiddenGraphs.Find(FullName) == INDEX_NONE;
|
|
if (IsVisible != SetAsVisible)
|
|
{
|
|
if (SetAsVisible)
|
|
{
|
|
HiddenGraphs.RemoveSingleSwap(FullName);
|
|
}
|
|
else
|
|
{
|
|
HiddenGraphs.Add(FullName);
|
|
}
|
|
|
|
FVisualLoggerDatabase::Get().GetEvents().OnGraphChangedVisibilityEvent.Broadcast(GraphName);
|
|
}
|
|
}
|
|
|
|
bool FVisualLoggerGraphsDatabase::ContainsGraphByName(FName OwnerName, FName GraphName)
|
|
{
|
|
const FVisualLoggerGraphHelper& GraphHelper = OwnerNameToGraphs.FindOrAdd(OwnerName);
|
|
return GraphHelper.GraphNameToIndex.Contains(GraphName);
|
|
}
|
|
|
|
FVisualLoggerGraph& FVisualLoggerGraphsDatabase::GetGraphByName(FName OwnerName, FName GraphName)
|
|
{
|
|
FVisualLoggerGraphHelper& GraphHelper = OwnerNameToGraphs.FindOrAdd(OwnerName);
|
|
const bool bContainsRow = GraphHelper.GraphNameToIndex.Contains(GraphName);
|
|
check(bContainsRow);
|
|
|
|
const int32 GraphIndex = GraphHelper.GraphNameToIndex[GraphName];
|
|
return GraphHelper.AllGraphs[GraphIndex];
|
|
}
|
|
|
|
const TArray<FVisualLoggerGraph>& FVisualLoggerGraphsDatabase::GetGraphsByOwnerName(FName OwnerName)
|
|
{
|
|
const FVisualLoggerGraphHelper& GraphHelper = OwnerNameToGraphs.FindOrAdd(OwnerName);
|
|
return GraphHelper.AllGraphs;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FVisualLoggerEditorInterface */
|
|
/************************************************************************/
|
|
const FName& FVisualLoggerEditorInterface::GetRowClassName(FName RowName) const
|
|
{
|
|
return FVisualLoggerDatabase::Get().GetRowByName(RowName).GetOwnerClassName();
|
|
}
|
|
|
|
int32 FVisualLoggerEditorInterface::GetSelectedItemIndex(FName RowName) const
|
|
{
|
|
return FVisualLoggerDatabase::Get().GetRowByName(RowName).GetCurrentItemIndex();
|
|
}
|
|
|
|
const TArray<FVisualLogDevice::FVisualLogEntryItem>& FVisualLoggerEditorInterface::GetRowItems(FName RowName)
|
|
{
|
|
return FVisualLoggerDatabase::Get().GetRowByName(RowName).GetItems();
|
|
}
|
|
|
|
const FVisualLogDevice::FVisualLogEntryItem& FVisualLoggerEditorInterface::GetSelectedItem(FName RowName) const
|
|
{
|
|
return FVisualLoggerDatabase::Get().GetRowByName(RowName).GetCurrentItem();
|
|
}
|
|
|
|
const TArray<FName>& FVisualLoggerEditorInterface::GetSelectedRows() const
|
|
{
|
|
return FVisualLoggerDatabase::Get().GetSelectedRows();
|
|
}
|
|
|
|
bool FVisualLoggerEditorInterface::IsRowVisible(FName RowName) const
|
|
{
|
|
return FVisualLoggerDatabase::Get().IsRowVisible(RowName);
|
|
}
|
|
|
|
UWorld* FVisualLoggerEditorInterface::GetWorld() const
|
|
{
|
|
return FLogVisualizer::Get().GetWorld();
|
|
}
|
|
|
|
bool FVisualLoggerEditorInterface::IsItemVisible(FName RowName, int32 ItemIndex) const
|
|
{
|
|
return FVisualLoggerDatabase::Get().GetRowByName(RowName).IsItemVisible(ItemIndex);
|
|
}
|
|
|
|
AActor* FVisualLoggerEditorInterface::GetHelperActor(UWorld* InWorld) const
|
|
{
|
|
UWorld* World = InWorld ? InWorld : GetWorld();
|
|
if (World == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (TActorIterator<AVisualLoggerRenderingActor> It(World); It)
|
|
{
|
|
return *It;
|
|
}
|
|
|
|
FActorSpawnParameters SpawnInfo;
|
|
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
|
|
|
// The helper actor is created on demand and only once per world so we can allow it to spawn during construction script.
|
|
SpawnInfo.bAllowDuringConstructionScript = true;
|
|
|
|
#if WITH_EDITOR
|
|
// Nothing to set on this actor, se we just hide it from the outliner :
|
|
SpawnInfo.bHideFromSceneOutliner = true;
|
|
#endif //WITH_EDITOR
|
|
|
|
return World->SpawnActor<AVisualLoggerRenderingActor>(SpawnInfo);
|
|
}
|
|
|
|
bool FVisualLoggerEditorInterface::MatchCategoryFilters(const FString& String, ELogVerbosity::Type Verbosity)
|
|
{
|
|
return FVisualLoggerFilters::Get().ShouldDisplayCategoryByString(String, Verbosity);
|
|
}
|