Add AZ_BUDGET_DEFINE/AZ_BUDGET_DECLARE and remove driller

NOTE: The memory driller is still intact for now to avoid needing to
modify allocators, but the frame/cpu portions of driller and the
standalone executable are now gone.

Signed-off-by: Jeremy Ong <jcong@amazon.com>
This commit is contained in:
Jeremy Ong
2021-08-21 02:27:56 -06:00
parent b31d673de0
commit 07a14bdce1
293 changed files with 459 additions and 44183 deletions
+4 -4
View File
@@ -1047,7 +1047,7 @@ static bool TryRenameFile(const QString& oldPath, const QString& newPath, int re
bool CCryEditDoc::SaveLevel(const QString& filename)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
AZ_PROFILE_FUNCTION(Editor);
QWaitCursor wait;
CAutoCheckOutDialogEnableForAll enableForAll;
@@ -1067,7 +1067,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
{
AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel BackupBeforeSave");
AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel BackupBeforeSave");
BackupBeforeSave();
}
@@ -1178,7 +1178,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
CPakFile pakFile;
{
AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel Open PakFile");
AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel Open PakFile");
if (!pakFile.Open(tempSaveFile.toUtf8().data(), false))
{
gEnv->pLog->LogWarning("Unable to open pack file %s for writing", tempSaveFile.toUtf8().data());
@@ -1209,7 +1209,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
AZ::IO::ByteContainerStream<AZStd::vector<char>> entitySaveStream(&entitySaveBuffer);
{
AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel Save Entities To Stream");
AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel Save Entities To Stream");
EBUS_EVENT_RESULT(
savedEntities, AzToolsFramework::EditorEntityContextRequestBus, SaveToStreamForEditor, entitySaveStream, layerEntities,
instancesInLayers);
-4
View File
@@ -8,8 +8,6 @@
#pragma once
#ifndef CRYINCLUDE_EDITOR_EDITORDEFS_H
#define CRYINCLUDE_EDITOR_EDITORDEFS_H
#include <AzCore/PlatformDef.h>
@@ -186,5 +184,3 @@
#endif
#endif
#endif // CRYINCLUDE_EDITOR_EDITORDEFS_H
-8
View File
@@ -394,8 +394,6 @@ void EditorViewportWidget::UpdateContent(int flags)
//////////////////////////////////////////////////////////////////////////
void EditorViewportWidget::Update()
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
if (Editor::EditorQtApplication::instance()->isMovingOrResizing())
{
return;
@@ -957,15 +955,11 @@ AzFramework::CameraState EditorViewportWidget::GetCameraState()
AZ::Vector3 EditorViewportWidget::PickTerrain(const AzFramework::ScreenPoint& point)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
return LYVec3ToAZVec3(ViewToWorld(AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(point), nullptr, true));
}
AZ::EntityId EditorViewportWidget::PickEntity(const AzFramework::ScreenPoint& point)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
PreWidgetRendering();
AZ::EntityId entityId;
@@ -992,8 +986,6 @@ float EditorViewportWidget::TerrainHeight(const AZ::Vector2& position)
void EditorViewportWidget::FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntitiesOut)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
visibleEntitiesOut.assign(m_entityVisibilityQuery.Begin(), m_entityVisibilityQuery.End());
}
+3 -4
View File
@@ -6,9 +6,6 @@
*
*/
#ifndef CRYINCLUDE_EDITOR_IEDITOR_H
#define CRYINCLUDE_EDITOR_IEDITOR_H
#pragma once
#ifdef PLUGIN_EXPORTS
@@ -25,6 +22,7 @@
#include <WinWidgetId.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Debug/Budget.h>
class QMenu;
@@ -738,4 +736,5 @@ struct IInitializeUIInfo
virtual void SetInfoText(const char* text) = 0;
};
#endif // CRYINCLUDE_EDITOR_IEDITOR_H
AZ_DECLARE_BUDGET(Editor);
-2
View File
@@ -405,8 +405,6 @@ void CEditorImpl::Update()
// Make sure this is not called recursively
m_bUpdates = false;
FUNCTION_PROFILER(GetSystem(), PROFILE_EDITOR);
//@FIXME: Restore this latter.
//if (GetGameEngine() && GetGameEngine()->IsLevelLoaded())
{
+2 -3
View File
@@ -38,7 +38,6 @@
// To use the Andrew's algorithm in order to make convex hull from the points, this header is needed.
#include "Util/GeometryUtil.h"
namespace {
QColor kLinkColorParent = QColor(0, 255, 255);
QColor kLinkColorChild = QColor(0, 0, 255);
@@ -1928,7 +1927,7 @@ bool CBaseObject::HitTestRectBounds(HitContext& hc, const AABB& box)
//////////////////////////////////////////////////////////////////////////
bool CBaseObject::HitTestRect(HitContext& hc)
{
AZ_PROFILE_FUNCTION(Entity);
AZ_PROFILE_FUNCTION(Editor);
AABB box;
@@ -1965,7 +1964,7 @@ bool CBaseObject::HitHelperTest(HitContext& hc)
//////////////////////////////////////////////////////////////////////////
bool CBaseObject::HitHelperAtTest(HitContext& hc, const Vec3& pos)
{
AZ_PROFILE_FUNCTION(Entity);
AZ_PROFILE_FUNCTION(Editor);
bool bResult = false;
@@ -1261,10 +1261,6 @@ void DisplayContext::DrawTextureLabel(const Vec3& pos, int nWidth, int nHeight,
//////////////////////////////////////////////////////////////////////////
void DisplayContext::Flush2D()
{
#ifndef PHYSICS_EDITOR
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
#endif
if (m_textureLabels.empty())
{
return;
+1 -1
View File
@@ -497,7 +497,7 @@ bool CEntityObject::HitTestRect(HitContext& hc)
//////////////////////////////////////////////////////////////////////////
int CEntityObject::MouseCreateCallback(CViewport* view, EMouseEvent event, QPoint& point, int flags)
{
AZ_PROFILE_FUNCTION(Editor);
AZ_PROFILE_FUNCTION(Entity);
if (event == eMouseMove || event == eMouseLDown)
{
-2
View File
@@ -18,8 +18,6 @@
//////////////////////////////////////////////////////////////////////////
void CGizmoManager::Display(DisplayContext& dc)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
AABB bbox;
std::vector<CGizmo*> todelete;
for (Gizmos::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
-1
View File
@@ -37,7 +37,6 @@ AZ_CVAR(
bool, ed_visibility_use, true, nullptr, AZ::ConsoleFunctorFlags::Null,
"Enable/disable using the new IVisibilitySystem for Entity visibility determination");
/*!
* Class Description used for object templates.
* This description filled from Xml template files.
@@ -19,7 +19,6 @@
#include "Objects/ObjectLoader.h"
#include "Objects/SelectionGroup.h"
//////////////////////////////////////////////////////////////////////////
// CUndoBaseObjectNew implementation.
//////////////////////////////////////////////////////////////////////////
-2
View File
@@ -148,8 +148,6 @@ void QTopRendererWnd::UpdateContent(int flags)
//////////////////////////////////////////////////////////////////////////
void QTopRendererWnd::Draw([[maybe_unused]] DisplayContext& dc)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
////////////////////////////////////////////////////////////////////////
// Perform the rendering for this window
////////////////////////////////////////////////////////////////////////
-4
View File
@@ -41,7 +41,6 @@ struct SPointSorter
//===================================================================
void ConvexHull2DGraham(std::vector<Vec3>& ptsOut, const std::vector<Vec3>& ptsIn)
{
FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
const unsigned nPtsIn = ptsIn.size();
if (nPtsIn < 3)
{
@@ -66,7 +65,6 @@ void ConvexHull2DGraham(std::vector<Vec3>& ptsOut, const std::vector<Vec3>& ptsI
std::swap(ptsSorted[0], ptsSorted[iBotRight]);
{
FRAME_PROFILER("SORT Graham", gEnv->pSystem, PROFILE_AI)
std::sort(ptsSorted.begin() + 1, ptsSorted.end(), SPointSorter(ptsSorted[0]));
}
ptsSorted.erase(std::unique(ptsSorted.begin(), ptsSorted.end(), ptEqual), ptsSorted.end());
@@ -196,7 +194,6 @@ inline bool PointSorterAndrew(const Vec3& lhs, const Vec3& rhs)
//===================================================================
SANDBOX_API void ConvexHull2DAndrew(std::vector<Vec3>& ptsOut, const std::vector<Vec3>& ptsIn)
{
FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
const int n = (int)ptsIn.size();
if (n < 3)
{
@@ -206,7 +203,6 @@ SANDBOX_API void ConvexHull2DAndrew(std::vector<Vec3>& ptsOut, const std::vector
std::vector<Vec3> P = ptsIn;
{
FRAME_PROFILER("SORT Andrew", gEnv->pSystem, PROFILE_AI)
std::sort(P.begin(), P.end(), PointSorterAndrew);
}
-4
View File
@@ -400,7 +400,6 @@ void QtViewport::UpdateContent(int flags)
//////////////////////////////////////////////////////////////////////////
void QtViewport::Update()
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
m_viewportUi.Update();
m_bAdvancedSelectMode = false;
@@ -1436,9 +1435,6 @@ bool QtViewport::MouseCallback(EMouseEvent event, const QPoint& point, Qt::Keybo
//////////////////////////////////////////////////////////////////////////
void QtViewport::ProcessRenderLisneters(DisplayContext& rstDisplayContext)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
size_t nCount(0);
size_t nTotal(0);
@@ -10,7 +10,7 @@
// Component includes
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Debug/FrameProfilerComponent.h>
#include <AzCore/Debug/BudgetsComponent.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/Jobs/JobManagerComponent.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
@@ -36,7 +36,7 @@ namespace AZ
JsonSystemComponent::CreateDescriptor(),
AssetManagerComponent::CreateDescriptor(),
UserSettingsComponent::CreateDescriptor(),
Debug::FrameProfilerComponent::CreateDescriptor(),
Debug::BudgetsComponent::CreateDescriptor(),
SliceComponent::CreateDescriptor(),
SliceSystemComponent::CreateDescriptor(),
SliceMetadataInfoComponent::CreateDescriptor(),
@@ -54,6 +54,7 @@ namespace AZ
{
return AZ::ComponentTypeList
{
azrtti_typeid<Debug::BudgetsComponent>(),
azrtti_typeid<TimeSystemComponent>(),
azrtti_typeid<LoggerSystemComponent>(),
azrtti_typeid<EventSchedulerSystemComponent>(),
@@ -51,7 +51,6 @@
#include <AzCore/Driller/Driller.h>
#include <AzCore/Memory/MemoryDriller.h>
#include <AzCore/Debug/TraceMessagesDriller.h>
#include <AzCore/Debug/ProfilerDriller.h>
#include <AzCore/Debug/EventTraceDriller.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Script/ScriptSystemBus.h>
@@ -918,8 +917,6 @@ namespace AZ
{
m_drillerManager->Register(aznew Debug::MemoryDriller);
}
// Profiler driller will consume resources only when started.
m_drillerManager->Register(aznew Debug::ProfilerDriller);
// Trace messages driller will consume resources only when started.
m_drillerManager->Register(aznew Debug::TraceMessagesDriller);
m_drillerManager->Register(aznew Debug::EventTraceDriller);
@@ -16,6 +16,7 @@
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/std/string/string.h>
@@ -438,3 +439,4 @@ namespace AZ
return component;
}
} // namespace AZ
@@ -0,0 +1,45 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "Budget.h"
#include <AzCore/Module/Environment.h>
#include <AzCore/Math/Crc.h>
AZ_DEFINE_BUDGET(AzCore);
AZ_DEFINE_BUDGET(Editor);
AZ_DEFINE_BUDGET(Entity);
AZ_DEFINE_BUDGET(Game);
AZ_DEFINE_BUDGET(System);
AZ_DEFINE_BUDGET(Audio);
AZ_DEFINE_BUDGET(Animation);
namespace AZ::Debug
{
// Global container for all registered budgets
class BudgetRegistry
{
public:
static BudgetRegistry& Instance()
{
}
private:
};
void Budget::ResetAll()
{
}
Budget::Budget(const char* name)
: m_name{ name }
, m_crc{ Crc32(name) }
{
// TODO: Register budget with singleton budget registry
}
} // namespace AZ::Debug
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Module/Environment.h>
#include <AzCore/std/parallel/scoped_lock.h>
#include <AzCore/std/parallel/atomic.h>
#pragma warning(push)
// This warning must be disabled because Budget::Get<T> may not have an implementation if this file is transitively included
// in a source file that doesn't actually have any budgets declared in scope (via AZ_DEFINE_BUDGET or AZ_DECLARE_BUDGET).
// In this situation, the warning about internal linkage without an implementation is benign because we know that this function
// cannot be invoked from that translation unit.
#pragma warning(disable: 5046)
namespace AZ::Debug
{
// A budget collates per-frame resource utilization and memory for a particular category
class Budget final
{
public:
// Invoked once at the start of the frame to reset per-frame counters
static void ResetAll();
// If you encounter a linker error complaining that this function is not defined, you have likely forgotten to either
// define or declare the budget used in a profile or memory marker. See AZ_DEFINE_BUDGET and AZ_DECLARE_BUDGET below
// for usage.
template<typename BudgetType>
static Budget* Get();
explicit Budget(const char* name);
const char* Name() const
{
return m_name;
}
uint32_t Crc() const
{
return m_crc;
}
private:
const char* m_name;
uint32_t m_crc;
};
} // namespace AZ::Debug
#pragma warning(pop)
#define AZ_BUDGET_NAME(name) AzBudget##name
// Usage example:
// In a single C++ source file:
// AZ_DEFINE_BUDGET(AzCore);
//
// Anywhere the budget is used, the budget must be declared (either in a header or in the source file itself)
// AZ_DECLARE_BUDGET(AzCore);
//
// The budget is usable in the same file it was defined without needing an additional declaration
// Implementation notes:
// Every budget definition is declared in static storage along with the environment variable and mutex. This imposes a slight
// memory overhead in the data segment only in instances where the same static module defining a budget is linked against multiple
// DLLs, however, this simplifies the implementation and works regardless of whether the budget is defined in a static or dynamic
// library. When loading the budget, a relaxed load is sufficient because Environment::CreateVariable internally locks and returns
// the element found if the environment variable was already created (skipping construction in the process). Thus, the budget pointer
// will reference the statically stored budget for the first thread that grabs the lock.
#define AZ_DEFINE_BUDGET(name) \
template<> \
::AZ::Debug::Budget* ::AZ::Debug::Budget::Get<class AZ_BUDGET_NAME(name)*>() \
{ \
static ::AZStd::mutex s_azBudgetMutex##name; \
static ::AZ::EnvironmentVariable<::AZ::Debug::Budget*> s_azBudgetEnv##name; \
static ::AZ::Debug::Budget s_azBudget##name{ #name }; \
static ::AZStd::atomic<::AZ::Debug::Budget*> budget; \
::AZ::Debug::Budget* out = budget.load(AZStd::memory_order_relaxed); \
if (out) \
{ \
return out; \
} \
else \
{ \
{ \
AZStd::scoped_lock lock{ s_azBudgetMutex##name }; \
if (!s_azBudgetEnv##name) \
{ \
s_azBudgetEnv##name = ::AZ::Environment::CreateVariable<::AZ::Debug::Budget*>("budgetEnv" #name, &s_azBudget##name); \
} \
} \
out = *s_azBudgetEnv##name; \
budget = out; \
return out; \
} \
}
// If using a budget defined in a different C++ source file, add AZ_DECLARE_BUDGET(yourBudget); somewhere in your source file at namespace
// scope Alternatively, AZ_DECLARE_BUDGET can be used in a header to declare the budget for use across any users of the header
#define AZ_DECLARE_BUDGET(name) extern template ::AZ::Debug::Budget* ::AZ::Debug::Budget::Get<class AZ_BUDGET_NAME(name)*>()
// Declare budgets that are core engine budgets, or may be shared/needed across multiple external gems
// You should NOT need to declare user-space or budgets with isolated usage here. Prefer declaring them local to the module(s) that use
// the budget and defining them within a single module to avoid needing to recompile the entire engine.
AZ_DECLARE_BUDGET(AzCore);
AZ_DECLARE_BUDGET(Editor);
AZ_DECLARE_BUDGET(Entity);
AZ_DECLARE_BUDGET(Game);
AZ_DECLARE_BUDGET(System);
AZ_DECLARE_BUDGET(Physics);
AZ_DECLARE_BUDGET(Animation);
@@ -0,0 +1,29 @@
#include <AzCore/Debug/BudgetsComponent.h>
namespace AZ::Debug
{
void BudgetsComponent::Reflect(AZ::ReflectContext*)
{
}
BudgetsComponent::BudgetsComponent()
{
}
BudgetsComponent::~BudgetsComponent()
{
}
void BudgetsComponent::Init()
{
}
void BudgetsComponent::Activate()
{
}
void BudgetsComponent::Deactivate()
{
}
} // namespace AZ::Debug
@@ -0,0 +1,31 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/Component.h>
namespace AZ::Debug
{
class BudgetsComponent : public Component
{
public:
AZ_COMPONENT(AZ::Debug::BudgetsComponent, "{52063706-5B36-4B24-A781-B49AFDBB5BC5}");
static void Reflect(AZ::ReflectContext* context);
BudgetsComponent();
~BudgetsComponent() override;
void Init() override;
void Activate() override;
void Deactivate() override;
private:
};
} // namespace AZ::Debug
@@ -1,63 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_FRAME_PROFILER_H
#define AZCORE_FRAME_PROFILER_H
#include <AzCore/Driller/DrillerBus.h>
#include <AzCore/std/containers/ring_buffer.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/std/parallel/config.h>
#include <AzCore/std/containers/unordered_map.h>
namespace AZ
{
namespace Debug
{
namespace FrameProfiler
{
/**
* This structure is used for frame data history, make sure it's memory efficient.
*/
struct FrameData
{
unsigned int m_frameId; ///< Id of the frame this data belongs to.
union
{
ProfilerRegister::TimeData m_timeData;
ProfilerRegister::ValuesData m_userValues;
};
};
struct RegisterData
{
//////////////////////////////////////////////////////////////////////////
// Profile register snapshot
/// data that doesn't change
const char* m_name; ///< Name of the profiler register.
const char* m_function; ///< Function name in the code.
int m_line; ///< Line number if the code.
AZ::u32 m_systemId; ///< Register system id.
ProfilerRegister::Type m_type;
RegisterData* m_lastParent; ///< Pointer to the last parent register data.
AZStd::ring_buffer<FrameData> m_frames; ///< History of all frame deltas (basically the data you want to display)
};
struct ThreadData
{
typedef AZStd::unordered_map<const ProfilerRegister*, RegisterData> RegistersMap;
AZStd::thread_id m_id; ///< Thread id (same as AZStd::thread::id)
RegistersMap m_registers; ///< Map with all the registers (with history)
};
typedef AZStd::fixed_vector<ThreadData, Profiler::m_maxNumberOfThreads> ThreadDataArray; ///< Array with samplers for all threads
} // namespace FrameProfiler
} // namespace Debug
} // namespace AZ
#endif // AZCORE_FRAME_PROFILER_H
#pragma once
@@ -1,38 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_FRAME_PROFILER_BUS_H
#define AZCORE_FRAME_PROFILER_BUS_H
#include <AzCore/EBus/EBus.h>
#include <AzCore/Debug/FrameProfiler.h>
namespace AZ
{
namespace Debug
{
class FrameProfilerComponent;
/**
* Interface class for frame profiler events.
*/
class FrameProfilerEvents
: public AZ::EBusTraits
{
public:
virtual ~FrameProfilerEvents() {}
/// Called when the frame profiler has computed a new frame (even is there is no new data).
virtual void OnFrameProfilerData(const FrameProfiler::ThreadDataArray& data) = 0;
};
typedef AZ::EBus<FrameProfilerEvents> FrameProfilerBus;
} // namespace Debug
} // namespace AZ
#endif // AZCORE_FRAME_PROFILER_BUS_H
#pragma once
@@ -1,250 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/FrameProfilerComponent.h>
#include <AzCore/Debug/FrameProfilerBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Debug/Profiler.h>
namespace AZ
{
namespace Debug
{
//=========================================================================
// FrameProfilerComponent
// [12/5/2012]
//=========================================================================
FrameProfilerComponent::FrameProfilerComponent()
: m_numFramesStored(2)
, m_frameId(0)
, m_pauseOnFrame(0)
, m_currentThreadData(NULL)
{
}
//=========================================================================
// ~FrameProfilerComponent
// [12/5/2012]
//=========================================================================
FrameProfilerComponent::~FrameProfilerComponent()
{
}
//=========================================================================
// Activate
// [12/5/2012]
//=========================================================================
void FrameProfilerComponent::Activate()
{
if (!Profiler::IsReady())
{
Profiler::Create();
}
Profiler::AddReference();
TickBus::Handler::BusConnect();
AZ_Assert(m_numFramesStored >= 1, "We must have at least one frame to store, otherwise this component is useless!");
}
//=========================================================================
// Deactivate
// [12/5/2012]
//=========================================================================
void FrameProfilerComponent::Deactivate()
{
TickBus::Handler::BusDisconnect();
Profiler::ReleaseReference();
}
//=========================================================================
// OnTick
// [12/5/2012]
//=========================================================================
void FrameProfilerComponent::OnTick(float deltaTime, ScriptTimePoint time)
{
(void)deltaTime;
(void)time;
++m_frameId;
AZ_Error("Profiler", m_frameId != m_pauseOnFrame, "Triggered user pause/error on this frame! Check FrameProfilerComponent pauseOnFrame value!");
if (!Profiler::IsReady())
{
return; // we can't sample registers without profiler
}
// collect data from the profiler
m_currentThreadData = NULL;
Profiler::Instance().ReadRegisterValues(AZStd::bind(&FrameProfilerComponent::ReadProfilerRegisters, this, AZStd::placeholders::_1, AZStd::placeholders::_2));
// process all the resulting data here, not while reading the registers
for (size_t iThread = 0; iThread < m_threads.size(); ++iThread)
{
FrameProfiler::ThreadData& td = m_threads[iThread];
FrameProfiler::ThreadData::RegistersMap::iterator it = td.m_registers.begin();
FrameProfiler::ThreadData::RegistersMap::iterator last = td.m_registers.end();
for (; it != last; ++it)
{
// fix up parents
FrameProfiler::RegisterData& rd = it->second;
if (rd.m_type == ProfilerRegister::PRT_TIME)
{
const FrameProfiler::FrameData& fd = rd.m_frames.back();
if (fd.m_timeData.m_lastParent != nullptr)
{
FrameProfiler::ThreadData::RegistersMap::iterator parentIt = td.m_registers.find(fd.m_timeData.m_lastParent);
AZ_Assert(parentIt != td.m_registers.end(), "We have a parent register that is not in our register map. This should not happen!");
rd.m_lastParent = &parentIt->second;
}
else
{
rd.m_lastParent = NULL;
}
}
}
}
// send an even to whomever cares
EBUS_EVENT(FrameProfilerBus, OnFrameProfilerData, m_threads);
}
int FrameProfilerComponent::GetTickOrder()
{
// Even it's not critical we should tick last to capture the current frame
// so TICK_LAST (since it's not the last int +1 is a valid assumption)
return TICK_LAST + 1;
}
//=========================================================================
// ReadRegisterCallback
// [12/5/2012]
//=========================================================================
bool FrameProfilerComponent::ReadProfilerRegisters(const ProfilerRegister& reg, const AZStd::thread_id& id)
{
if (m_currentThreadData == NULL || m_currentThreadData->m_id != id)
{
m_currentThreadData = NULL;
// find the thread and cache it, as we will received registers thread by thread... so we don't search.
for (size_t i = 0; i < m_threads.size(); ++i)
{
FrameProfiler::ThreadData* td = &m_threads[i];
if (td->m_id == id)
{
m_currentThreadData = td;
break;
}
}
if (m_currentThreadData == NULL)
{
m_threads.push_back();
m_currentThreadData = &m_threads.back();
m_currentThreadData->m_id = id;
}
}
const ProfilerRegister* profReg = &reg;
FrameProfiler::ThreadData::RegistersMap::pair_iter_bool pairIterBool = m_currentThreadData->m_registers.insert_key(profReg);
FrameProfiler::RegisterData& regData = pairIterBool.first->second;
// now update dynamic data with as little as possible computation (we must be fast)
FrameProfiler::FrameData fd; // we can actually move this computation (FrameData and push) for later but we will need to use more memory
fd.m_frameId = m_frameId;
if (pairIterBool.second)
{
// when insert copy the static data only once
regData.m_name = profReg->m_name;
regData.m_function = profReg->m_function;
regData.m_line = profReg->m_line;
regData.m_systemId = profReg->m_systemId;
regData.m_frames.set_capacity(m_numFramesStored);
regData.m_type = static_cast<ProfilerRegister::Type>(profReg->m_type);
}
switch (regData.m_type)
{
case ProfilerRegister::PRT_TIME:
{
fd.m_timeData.m_time = profReg->m_timeData.m_time;
fd.m_timeData.m_childrenTime = profReg->m_timeData.m_childrenTime;
fd.m_timeData.m_calls = profReg->m_timeData.m_calls;
fd.m_timeData.m_childrenCalls = profReg->m_timeData.m_childrenCalls;
fd.m_timeData.m_lastParent = profReg->m_timeData.m_lastParent;
} break;
case ProfilerRegister::PRT_VALUE:
{
fd.m_userValues.m_value1 = profReg->m_userValues.m_value1;
fd.m_userValues.m_value2 = profReg->m_userValues.m_value2;
fd.m_userValues.m_value3 = profReg->m_userValues.m_value3;
fd.m_userValues.m_value4 = profReg->m_userValues.m_value4;
fd.m_userValues.m_value5 = profReg->m_userValues.m_value5;
} break;
}
regData.m_frames.push_back(fd);
return true;
}
//=========================================================================
// GetProvidedServices
//=========================================================================
void FrameProfilerComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("FrameProfilerService", 0x05d1bb90));
}
//=========================================================================
// GetIncompatibleServices
//=========================================================================
void FrameProfilerComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("FrameProfilerService", 0x05d1bb90));
}
//=========================================================================
// GetDependentServices
//=========================================================================
void FrameProfilerComponent::GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(AZ_CRC("MemoryService", 0x5c4d473c));
}
//=========================================================================
// Reflect
//=========================================================================
void FrameProfilerComponent::Reflect(ReflectContext* context)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<FrameProfilerComponent, AZ::Component>()
->Version(1)
->Field("numFramesStored", &FrameProfilerComponent::m_numFramesStored)
->Field("pauseOnFrame", &FrameProfilerComponent::m_pauseOnFrame)
;
if (EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<FrameProfilerComponent>(
"Frame Profiler", "Performs per frame profiling (FPS counter, registers, etc.)")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Profiling")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->DataElement(AZ::Edit::UIHandlers::SpinBox, &FrameProfilerComponent::m_numFramesStored, "Number of Frames", "How many frames we will keep with the RUNTIME buffers.")
->Attribute(AZ::Edit::Attributes::Min, 1)
->DataElement(AZ::Edit::UIHandlers::SpinBox, &FrameProfilerComponent::m_pauseOnFrame, "Pause on frame", "Paused the engine (debug break) on a specific frame. 0 means no pause!")
;
}
}
}
} // namespace Debug
} // namespace AZ
@@ -1,75 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_FRAME_PROFILER_COMPONENT_H
#define AZCORE_FRAME_PROFILER_COMPONENT_H
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/FrameProfiler.h>
#include <AzCore/std/parallel/threadbus.h>
#include <AzCore/Math/Crc.h>
namespace AZ
{
namespace Debug
{
/**
* Frame profiler component provides a frame profiling information
* (from FPS counter to profiler registers manipulation and so on).
* It's a debug system so it should not be active in release
*/
class FrameProfilerComponent
: public Component
, public AZ::TickBus::Handler
{
public:
AZ_COMPONENT(AZ::Debug::FrameProfilerComponent, "{B81739EF-ED77-4F67-9D05-6ADF94F0431A}")
FrameProfilerComponent();
virtual ~FrameProfilerComponent();
private:
//////////////////////////////////////////////////////////////////////////
// Component base
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Tick bus
void OnTick(float deltaTime, ScriptTimePoint time) override;
int GetTickOrder() override;
//////////////////////////////////////////////////////////////////////////
/// \ref ComponentDescriptor::GetProvidedServices
static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
/// \ref ComponentDescriptor::GetIncompatibleServices
static void GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible);
/// \ref ComponentDescriptor::GetDependentServices
static void GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent);
/// \red ComponentDescriptor::Reflect
static void Reflect(ReflectContext* reflection);
/// callback for reading profiler registers
bool ReadProfilerRegisters(const ProfilerRegister& reg, const AZStd::thread_id& id);
// Keep in mind memory usage, increases quickly. Prefer remote tools (where the history is kept on the PC) instead of keeping long history
unsigned int m_numFramesStored; ///< Number of frames that we will store in history buffers. >= 1
unsigned int m_frameId; ///< Frame id (it's just counted from the start).
unsigned int m_pauseOnFrame; ///< Allows you to specify a frame the code will pause onto.
FrameProfiler::ThreadDataArray m_threads; ///< Array with samplers for all threads
FrameProfiler::ThreadData* m_currentThreadData; ///< Cached pointer to the last accessed thread data.
};
}
}
#endif // AZCORE_FRAME_PROFILER_COMPONENT_H
#pragma once
+2 -652
View File
@@ -7,664 +7,14 @@
*/
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Debug/ProfilerDrillerBus.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/parallel/shared_spin_mutex.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/functional.h>
#include <AzCore/Math/Crc.h>
namespace AZ
namespace AZ::Debug
{
uint32_t ProfileScope::GetSystemID(const char* system)
{
// TODO: stable ids for registered budgets
return AZ::Crc32(system);
}
namespace Debug
{
//////////////////////////////////////////////////////////////////////////
// Globals
AZStd::chrono::microseconds ProfilerRegister::TimeData::s_startStopOverheadPer1000Calls(0);
Profiler* Profiler::s_instance = nullptr;
u64 Profiler::s_id = 0;
int Profiler::s_useCount = 0;
//////////////////////////////////////////////////////////////////////////
/**
* Profile data stored per thread.
*/
struct ProfilerThreadData
{
static const int m_maxStackSize = 32;
typedef AZStd::list<ProfilerRegister, OSStdAllocator> ProfilerRegisterList;
typedef AZStd::fixed_vector<ProfilerSection*, m_maxStackSize> ProfilerSectionStack;
AZStd::thread::id m_id; ///< Thread id.
ProfilerRegisterList m_registers; ///< Thread profiler registers (for this thread).
mutable AZStd::shared_spin_mutex m_registersLock; ///< Lock for accessing thread profiler entries. Sadly the only reason for this to exists is so we can read safe the register counters.
ProfilerSectionStack m_stack; ///< Current active sections stack.
};
struct ProfilerSystemData
{
AZ::u32 m_id;
const char* m_name;
bool m_isActive;
};
/**
* Profiler class data (hidden in from the header file)
*/
struct ProfilerData
{
AZ_CLASS_ALLOCATOR(ProfilerData, OSAllocator, 0);
AZStd::fixed_vector<ProfilerThreadData, Profiler::m_maxNumberOfThreads> m_threads; ///< Array with thread with all belonging information.
AZStd::shared_spin_mutex m_threadDataMutex; ///< Spin read/write lock (shared_mutex) for access to the m_threads.
AZStd::fixed_vector<ProfilerSystemData, Profiler::m_maxNumberOfSystems> m_systems; ///< Array with systems (profiler/timer groups) that you can enable/disable.
};
//=========================================================================
// Profiler
// [12/3/2012]
//=========================================================================
Profiler::Profiler(const Descriptor& desc)
{
(void)desc;
m_data = aznew ProfilerData;
// we can periodically call this function (like the end of every frame to refresh the current estimation).
ProfilerRegister::TimerComputeStartStopOverhead();
// use a timestamp as and id.
s_id = AZStd::GetTimeUTCMilliSecond();
}
//=========================================================================
// ~Profiler
// [12/3/2012]
//=========================================================================
Profiler::~Profiler()
{
AZ_Assert(s_useCount == 0, "You deleted the profiler while it's still in use.");
s_id = 0;
delete m_data;
}
//=========================================================================
// Create
// [12/3/2012]
//=========================================================================
bool Profiler::Create(const Descriptor& desc)
{
AZ_Assert(s_instance == nullptr, "Profiler is already created!");
if (s_instance != nullptr)
{
return false;
}
s_instance = azcreate(Profiler, (desc), AZ::OSAllocator, "Profiler", 0);
return true;
}
//=========================================================================
// Destroy
// [12/3/2012]
//=========================================================================
void Profiler::Destroy()
{
AZ_Assert(s_instance != nullptr, "Profiler not created");
if (s_instance)
{
azdestroy(s_instance, AZ::OSAllocator);
s_instance = nullptr;
}
}
//=========================================================================
// AddReference
// [5/24/2013]
//=========================================================================
void Profiler::AddReference()
{
++s_useCount;
}
//=========================================================================
//
// [5/24/2013]
//=========================================================================
void Profiler::ReleaseReference()
{
AZ_Assert(s_useCount > 0, "Use count is already 0, you can't release it!");
--s_useCount;
if (s_useCount == 0)
{
Destroy();
}
}
//=========================================================================
// RegisterSystem
// [12/3/2012]
//=========================================================================
bool Profiler::RegisterSystem(AZ::u32 systemId, const char* name, bool isActive)
{
for (size_t i = 0; i < m_data->m_systems.size(); ++i)
{
if (m_data->m_systems[i].m_id == systemId)
{
return false;
}
}
ProfilerSystemData sd;
sd.m_id = systemId;
sd.m_isActive = isActive;
sd.m_name = name;
m_data->m_systems.push_back(sd);
return true;
}
//=========================================================================
// UnregisterSystem
// [12/3/2012]
//=========================================================================
bool Profiler::UnregisterSystem(AZ::u32 systemId)
{
size_t i = 0;
for (; i < m_data->m_systems.size(); ++i)
{
ProfilerSystemData& sd = m_data->m_systems[i];
if (sd.m_id == systemId)
{
if (sd.m_isActive)
{
DeactivateSystem(sd.m_name);
}
// Make sure we triggest driller message when the m_threadDataMutex is NOT locked
// as we lock them in reverse order when we update the profile driller.
AZ_Assert(false, "Currently this code is unused. If we do use it, we should make call EBUS even outside of this function. Where m_threadDataMutex is NOT locked!");
//EBUS_DBG_EVENT(ProfilerDrillerBus,OnUnregisterSystem,systemId);
break;
}
}
if (i < m_data->m_systems.size())
{
m_data->m_systems.erase(m_data->m_systems.begin() + i);
return true;
}
return false;
}
//=========================================================================
// ActivateSystem
// [12/3/2012]
//=========================================================================
bool Profiler::SetSystemState(AZ::u32 systemId, bool isActive)
{
for (size_t i = 0; i < m_data->m_systems.size(); ++i)
{
ProfilerSystemData& sd = m_data->m_systems[i];
if (sd.m_id == systemId)
{
if (sd.m_isActive != isActive)
{
sd.m_isActive = isActive;
size_t numThreads = m_data->m_threads.size();
for (size_t j = 0; j < numThreads; ++j)
{
ProfilerThreadData& data = m_data->m_threads[j];
ProfilerThreadData::ProfilerRegisterList::iterator it = data.m_registers.begin();
ProfilerThreadData::ProfilerRegisterList::iterator end = data.m_registers.end();
for (; it != end; ++it)
{
ProfilerRegister& reg = *it;
// This is a big question since this is the only writer
// and the timers are readers and value should be read atomically (1 byte)
// we should be safe without a synchronization here (as data should be written as we exit)
// at worst we can put a volatile in front of the bool. Either way this should not cause
// crashes or anything
if (reg.m_systemId == systemId)
{
reg.m_isActive = isActive ? 1 : 0;
}
}
}
}
return true;
}
}
return false;
}
//=========================================================================
// ActivateSystem
// [5/24/2013]
//=========================================================================
void Profiler::ActivateSystem(const char* systemName)
{
AZ::u32 systemId = AZ::Crc32(systemName);
bool isNewSystem = false;
{
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(m_data->m_threadDataMutex);
if (!SetSystemState(systemId, true))
{
// if the system is new add it
RegisterSystem(systemId, systemName, true);
isNewSystem = true;
}
}
if (isNewSystem)
{
// Make sure we triggest driller message when the m_threadDataMutex is NOT locked
// as we lock them in reverse order when we update the profile driller.
EBUS_DBG_EVENT(ProfilerDrillerBus, OnRegisterSystem, systemId, systemName);
}
}
//=========================================================================
// DeactivateSystem
// [5/24/2013]
//=========================================================================
void Profiler::DeactivateSystem(const char* systemName)
{
AZ::u32 systemId = AZ::Crc32(systemName);
bool isNewSystem = false;
{
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(m_data->m_threadDataMutex);
if (!SetSystemState(systemId, false))
{
// if the system is new add it
RegisterSystem(systemId, systemName, false);
isNewSystem = true;
}
}
if (isNewSystem)
{
// Make sure we triggest driller message when the m_threadDataMutex is NOT locked
// as we lock them in reverse order when we update the profile driller.
EBUS_DBG_EVENT(ProfilerDrillerBus, OnRegisterSystem, systemId, systemName);
}
}
//=========================================================================
// IsSystemActive
// [5/24/2013]
//=========================================================================
bool Profiler::IsSystemActive(const char* systemName) const
{
return IsSystemActive(AZ::Crc32(systemName));
}
//=========================================================================
// IsSystemActive
// [12/3/2012]
//=========================================================================
bool Profiler::IsSystemActive(AZ::u32 systemId) const
{
for (size_t i = 0; i < m_data->m_systems.size(); ++i)
{
if (m_data->m_systems[i].m_id == systemId)
{
return m_data->m_systems[i].m_isActive != 0;
}
}
return false;
}
//=========================================================================
// GetNumberOfSystems
// [12/3/2012]
//=========================================================================
int Profiler::GetNumberOfSystems() const
{
return static_cast<int>(m_data->m_systems.size());
}
//=========================================================================
// GetSystemName
// [12/3/2012]
//=========================================================================
const char* Profiler::GetSystemName(int index) const
{
return m_data->m_systems[index].m_name;
}
//=========================================================================
// GetSystemName
// [12/3/2012]
//=========================================================================
const char* Profiler::GetSystemName(AZ::u32 systemId) const
{
for (size_t i = 0; i < m_data->m_systems.size(); ++i)
{
if (m_data->m_systems[i].m_id == systemId)
{
return m_data->m_systems[i].m_name;
}
}
return NULL;
}
//=========================================================================
// RemoveThreadData
// [12/3/2012]
//=========================================================================
void Profiler::RemoveThreadData(AZStd::thread_id id)
{
// this is very tricky we must be sure that thread is no longer operational
// otherwise we will crash badly. We can do only because nobody should
// reference this registers but the thread local data.
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(m_data->m_threadDataMutex);
size_t numThreads = m_data->m_threads.size();
ProfilerThreadData* threadData = NULL;
for (size_t i = 0; i < numThreads; ++i)
{
ProfilerThreadData& data = m_data->m_threads[i];
if (data.m_id == id)
{
threadData = &data;
break;
}
}
if (threadData)
{
// delete all registers, we can remove the thread data too, but we will need to switch the structure to list
// so far this is super minimal overhead, registers are more.
threadData->m_registers.clear();
}
}
//=========================================================================
// ReadRegisterValues
// [12/3/2012]
//=========================================================================
void Profiler::ReadRegisterValues(const ReadProfileRegisterCB& callback, AZ::u32 systemFilter, const AZStd::thread_id* threadFilter) const
{
AZStd::shared_lock<AZStd::shared_spin_mutex> readLock(s_instance->m_data->m_threadDataMutex);
size_t numThreads = Profiler::s_instance->m_data->m_threads.size();
for (size_t i = 0; i < numThreads; ++i)
{
const ProfilerThreadData& data = s_instance->m_data->m_threads[i];
if (threadFilter && *threadFilter != data.m_id)
{
continue;
}
{
AZStd::shared_lock<AZStd::shared_spin_mutex> registersLock(data.m_registersLock);
ProfilerThreadData::ProfilerRegisterList::const_iterator it = data.m_registers.begin();
ProfilerThreadData::ProfilerRegisterList::const_iterator end = data.m_registers.end();
for (; it != end; ++it)
{
const ProfilerRegister& reg = *it;
if (!reg.m_isActive || (systemFilter != 0 && systemFilter != reg.m_systemId))
{
continue;
}
if (!callback(reg, data.m_id))
{
return;
}
}
}
}
}
//=========================================================================
// ResetRegisters
// [12/4/2012]
//=========================================================================
void Profiler::ResetRegisters()
{
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(s_instance->m_data->m_threadDataMutex);
size_t numThreads = Profiler::s_instance->m_data->m_threads.size();
for (size_t i = 0; i < numThreads; ++i)
{
ProfilerThreadData& data = s_instance->m_data->m_threads[i];
{
AZStd::unique_lock<AZStd::shared_spin_mutex> registersLock(data.m_registersLock);
ProfilerThreadData::ProfilerRegisterList::iterator it = data.m_registers.begin();
ProfilerThreadData::ProfilerRegisterList::iterator end = data.m_registers.end();
for (; it != end; ++it)
{
ProfilerRegister& reg = *it;
reg.Reset();
}
}
}
// Reset registers event
}
//=========================================================================
// CreateRegister
// [6/28/2013]
//=========================================================================
ProfilerRegister*
ProfilerRegister::CreateRegister(const char* systemName, const char* name, const char* function, int line, ProfilerRegister::Type type)
{
static AZ_THREAD_LOCAL ProfilerThreadData* threadData = nullptr;
static AZ_THREAD_LOCAL u64 profilerId = 0;
if (profilerId != Profiler::s_id)
{
threadData = nullptr; // profiler has changed
profilerId = Profiler::s_id;
}
AZ::u32 systemId = AZ::Crc32(systemName);
ProfilerRegister* reg;
{
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(Profiler::s_instance->m_data->m_threadDataMutex);
// make sure we have the system registered. This function will just return false if the system exists.
if (systemName)
{
Profiler::s_instance->RegisterSystem(systemId, systemName, true);
}
if (threadData == nullptr) // if this is a new thread add the data
{
AZStd::thread::id threadId = AZStd::this_thread::get_id();
Profiler::s_instance->m_data->m_threads.push_back();
threadData = &Profiler::s_instance->m_data->m_threads.back();
threadData->m_id = threadId;
}
threadData->m_registers.push_back();
reg = &threadData->m_registers.back();
reg->m_name = name;
reg->m_function = function;
reg->m_line = line;
reg->m_systemId = systemId;
reg->m_isActive = Profiler::s_instance->IsSystemActive(systemId) ? 1 : 0;
reg->m_type = type;
reg->m_threadData = threadData;
reg->Reset();
}
// Make sure we triggest driller message when the m_threadDataMutex is NOT locked
// as we lock them in reverse order when we update the profile driller.
EBUS_DBG_EVENT(ProfilerDrillerBus, OnNewRegister, *reg, threadData->m_id);
return reg;
}
//=========================================================================
// TimerCreateAndStart
// [11/30/2012]
//=========================================================================
ProfilerRegister*
ProfilerRegister::TimerCreateAndStart(const char* systemName, const char* name, ProfilerSection * section, const char* function, int line)
{
AZStd::chrono::system_clock::time_point start = AZStd::chrono::system_clock::now();
ProfilerRegister* reg = CreateRegister(systemName, name, function, line, ProfilerRegister::PRT_TIME);
AZStd::chrono::system_clock::time_point end = AZStd::chrono::system_clock::now();
// adjust the parent timer with the overhead we incur during timer operations. (TODO with TLS this is so fast that we might not need to do it)
if (!reg->m_threadData->m_stack.empty()) // if we are not he last element
{
AZStd::chrono::microseconds elapsed = end - start;
reg->m_threadData->m_stack.back()->m_childTime += elapsed; // no need to check if we go in the future as this will happen on Stop
}
if (reg->m_isActive)
{
section->m_register = reg;
section->m_start = end;
reg->m_threadData->m_stack.push_back(section);
}
else
{
section->m_register = nullptr;
}
return reg;
}
//=========================================================================
// ValueCreate
// [6/28/2013]
//=========================================================================
ProfilerRegister*
ProfilerRegister::ValueCreate(const char* systemName, const char* name, const char* function, int line)
{
return CreateRegister(systemName, name, function, line, ProfilerRegister::PRT_VALUE);
}
//=========================================================================
// TimerStart
// [11/29/2012]
//=========================================================================
void ProfilerRegister::TimerStart(ProfilerSection* section)
{
ProfilerRegister* reg = this;
if (reg->m_isActive)
{
section->m_register = reg;
reg->m_threadData->m_stack.push_back(section);
section->m_start = AZStd::chrono::system_clock::now();
}
else
{
section->m_register = nullptr;
}
}
//=========================================================================
// TimerStop
// [11/29/2012]
//=========================================================================
void ProfilerRegister::TimerStop()
{
AZStd::chrono::system_clock::time_point end = AZStd::chrono::system_clock::now();
ProfilerSection* section = m_threadData->m_stack.back();
AZStd::chrono::microseconds elapsedTime = end - section->m_start;
{
m_threadData->m_registersLock.lock(); // lock for write
++m_timeData.m_calls;
m_timeData.m_time += elapsedTime.count();
m_timeData.m_childrenTime += section->m_childTime.count();
m_timeData.m_childrenCalls += section->m_childCalls;
m_threadData->m_registersLock.unlock(); // unlock
}
m_threadData->m_stack.pop_back();
// adjust the parent timer with the overhead we incur during timer operations.
if (!m_threadData->m_stack.empty())
{
ProfilerSection* parent = m_threadData->m_stack.back();
m_timeData.m_lastParent = parent->m_register;
parent->m_childTime += elapsedTime /*+ s_startStopOverhead*/; // add the overhead since most of it is in Stop()
++parent->m_childCalls;
}
}
//=========================================================================
// Reset
// [12/4/2012]
//=========================================================================
void ProfilerRegister::Reset()
{
switch (m_type)
{
case PRT_TIME:
{
m_timeData.m_time = 0;
m_timeData.m_childrenTime = 0;
m_timeData.m_calls = 0;
m_timeData.m_childrenCalls = 0;
m_timeData.m_lastParent = nullptr;
} break;
case PRT_VALUE:
{
m_userValues.m_value1 = 0;
m_userValues.m_value2 = 0;
m_userValues.m_value3 = 0;
m_userValues.m_value4 = 0;
m_userValues.m_value5 = 0;
} break;
}
}
//=========================================================================
// ComputeStartStopOverhead
// [12/3/2012]
//=========================================================================
void ProfilerRegister::TimerComputeStartStopOverhead()
{
// compute default thread start stop overhead
ProfilerThreadData sampleThreadData;
sampleThreadData.m_id = AZStd::this_thread::get_id();
sampleThreadData.m_registers.push_back();
ProfilerRegister& sampleRegister = sampleThreadData.m_registers.back();
sampleRegister.m_isActive = true;
sampleRegister.m_name = nullptr;
sampleRegister.m_systemId = 0;
sampleRegister.m_threadData = &sampleThreadData;
const int numSamples = 1000;
for (int iRepetition = 0; iRepetition < 1000; ++iRepetition) // just for test
{
ProfilerSection section;
sampleRegister.TimerStart(&section);
AZStd::chrono::system_clock::time_point start = AZStd::chrono::system_clock::now();
for (int i = 0; i < numSamples; ++i)
{
static AZ::Debug::ProfilerRegister* sampleRegisterPtr = &sampleRegister; // the creation is timed differently
ProfilerSection subSection;
if (sampleRegisterPtr != NULL)
{
sampleRegister.TimerStart(&subSection);
}
}
AZStd::chrono::microseconds elapsed = (AZStd::chrono::system_clock::now() - start);
if (TimeData::s_startStopOverheadPer1000Calls.count() == 0) // if first time set otherwise smooth average
{
TimeData::s_startStopOverheadPer1000Calls = elapsed;
}
else
{
float fNew = static_cast<float>(elapsed.count());
float fCurrent = static_cast<float>(TimeData::s_startStopOverheadPer1000Calls.count());
int deltaValue = static_cast<int>((fNew - fCurrent) * 0.1f);
if (deltaValue < 0)
{
TimeData::s_startStopOverheadPer1000Calls -= AZStd::chrono::microseconds(-deltaValue);
}
else
{
TimeData::s_startStopOverheadPer1000Calls += AZStd::chrono::microseconds(deltaValue);
}
}
}
//AZ_TracePrintf("Profiler","Overhead %d microseconds per 1000 profile calls!\n",TimeData::s_startStopOverheadPer1000Calls.count());
}
}
} // namespace AZ
} // namespace AZ::Debug
+39 -322
View File
@@ -7,42 +7,50 @@
*/
#pragma once
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/function/function_fwd.h>
#include <AzCore/Debug/Budget.h>
#ifdef USE_PIX
#include <AzCore/PlatformIncl.h>
#include <WinPixEventRuntime/pix3.h>
#endif
#if defined(AZ_PROFILER_MACRO_DISABLE) // by default we never disable the profiler registers as their overhead should be minimal, you can still do that for your code though.
# define AZ_PROFILE_SCOPE(...)
# define AZ_PROFILE_FUNCTION(...)
# define AZ_PROFILE_BEGIN(...)
# define AZ_PROFILE_END(...)
#if defined(AZ_PROFILER_MACRO_DISABLE) // by default we never disable the profiler registers as their overhead should be minimal, you can
// still do that for your code though.
#define AZ_PROFILE_SCOPE(...)
#define AZ_PROFILE_FUNCTION(...)
#define AZ_PROFILE_BEGIN(...)
#define AZ_PROFILE_END(...)
#else
/**
* Macro to declare a profile section for the current scope { }.
* format is: AZ_PROFILE_SCOPE(categoryName, const char* formatStr, ...)
*/
# define AZ_PROFILE_SCOPE(category, ...) ::AZ::ProfileScope AZ_JOIN(azProfileScope, __LINE__){ #category, __VA_ARGS__ }
# define AZ_PROFILE_FUNCTION(category) AZ_PROFILE_SCOPE(category, AZ_FUNCTION_SIGNATURE)
#define AZ_PROFILE_SCOPE(budget, ...) \
::AZ::Debug::ProfileScope AZ_JOIN(azProfileScope, __LINE__) \
{ \
*::AZ::Debug::Budget::Get<class AZ_BUDGET_NAME(budget)*>(), __VA_ARGS__ \
}
#define AZ_PROFILE_FUNCTION(category) AZ_PROFILE_SCOPE(category, AZ_FUNCTION_SIGNATURE)
// Prefer using the scoped macros which automatically end the event (AZ_PROFILE_SCOPE/AZ_PROFILE_FUNCTION)
# define AZ_PROFILE_BEGIN(category, ...) ::AZ::ProfileScope::BeginRegion(#category, __VA_ARGS__)
# define AZ_PROFILE_END() ::AZ::ProfileScope::EndRegion()
#define AZ_PROFILE_BEGIN(budget, ...) \
::AZ::Debug::ProfileScope::BeginRegion(*::AZ::Debug::Budget::Get<class AZ_BUDGET_NAME(budget)*>(), __VA_ARGS__)
#define AZ_PROFILE_END() ::AZ::Debug::ProfileScope::EndRegion()
#endif // AZ_PROFILER_MACRO_DISABLE
#ifndef AZ_PROFILE_INTERVAL_START
# define AZ_PROFILE_INTERVAL_START(...)
# define AZ_PROFILE_INTERVAL_START_COLORED(...)
# define AZ_PROFILE_INTERVAL_END(...)
# define AZ_PROFILE_INTERVAL_SCOPED(...)
#define AZ_PROFILE_INTERVAL_START(...)
#define AZ_PROFILE_INTERVAL_START_COLORED(...)
#define AZ_PROFILE_INTERVAL_END(...)
#define AZ_PROFILE_INTERVAL_SCOPED(...)
#endif
#ifndef AZ_PROFILE_DATAPOINT
# define AZ_PROFILE_DATAPOINT(...)
# define AZ_PROFILE_DATAPOINT_PERCENT(...)
#define AZ_PROFILE_DATAPOINT(...)
#define AZ_PROFILE_DATAPOINT_PERCENT(...)
#endif
namespace AZStd
@@ -50,7 +58,7 @@ namespace AZStd
struct thread_id; // forward declare. This is the same type as AZStd::thread::id
}
namespace AZ
namespace AZ::Debug
{
class ProfileScope
{
@@ -58,28 +66,33 @@ namespace AZ
static uint32_t GetSystemID(const char* system);
template<typename... T>
static void BeginRegion([[maybe_unused]] const char* system, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args)
static void BeginRegion(
[[maybe_unused]] const Budget& budget, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args)
{
#if !defined(_RELEASE)
// TODO: Verification that the supplied system name corresponds to a known budget
#if defined(USE_PIX)
PIXBeginEvent(PIX_COLOR_INDEX(GetSystemID(system) & 0xff), eventName, args...);
PIXBeginEvent(PIX_COLOR_INDEX(budget.Crc() & 0xff), eventName, args...);
#endif
// TODO: injecting instrumentation for other profilers
// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism
// will be introduced in a future PR
#endif
// TODO: injecting instrumentation for other profilers
// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism
// will be introduced in a future PR
}
static void EndRegion()
{
#if !defined(_RELEASE)
#if defined(USE_PIX)
PIXEndEvent();
#endif
#endif
}
template<typename... T>
ProfileScope(const char* system, char const* eventName, T const&... args)
ProfileScope(const Budget& budget, char const* eventName, T const&... args)
{
BeginRegion(system, eventName, args...);
BeginRegion(budget, eventName, args...);
}
~ProfileScope()
@@ -87,303 +100,7 @@ namespace AZ
EndRegion();
}
};
namespace Debug
{
class ProfilerSection;
class ProfilerRegister;
struct ProfilerThreadData;
struct ProfilerData;
/**
*
*/
class Profiler
{
friend class ProfilerRegister;
friend struct ProfilerData;
public:
/// Max number of threads supported by the profiler.
static const int m_maxNumberOfThreads = 32;
/// Max number of systems supported by the profiler. (We can switch this container if needed)
static const int m_maxNumberOfSystems = 64;
~Profiler();
struct Descriptor
{
};
static bool Create(const Descriptor& desc = Descriptor());
static void Destroy();
static bool IsReady() { return s_instance != NULL; }
static Profiler& Instance() { return *s_instance; }
static u64 GetId() { return s_id; }
/// Increment the use count.
static void AddReference();
/// Release the use count if 0 a Destroy will be called automatically.
static void ReleaseReference();
void ActivateSystem(const char* systemName);
void DeactivateSystem(const char* systemName);
bool IsSystemActive(const char* systemName) const;
bool IsSystemActive(AZ::u32 systemId) const;
int GetNumberOfSystems() const;
const char* GetSystemName(int index) const;
const char* GetSystemName(AZ::u32 systemId) const;
/** Callback to read a single register. Make sure you read the data as fast as possible. Don't compute inside the callback
* it will lock and hold all the registers that we process. The best is just to read the value and push it into a
* history buffer.
*/
typedef AZStd::function<bool (const ProfilerRegister&, const AZStd::thread_id&)> ReadProfileRegisterCB;
/**
* Read register values, make sure the code here is fast and efficient as we are holding a lock.
* provide a callback that will be called for each register.
* You can choose to filter your values by thread or a system. Use this filter only to narrow you samples. Don't
* use it for multiple calls to sort your counters, use the history data.
* It addition keep in mind that you can run this function is parallel, as we only read the values.
*/
void ReadRegisterValues(const ReadProfileRegisterCB& callback, AZ::u32 systemFilter = 0, const AZStd::thread_id* threadFilter = NULL) const;
/**
* This is slow operation that will cause contention try to avoid using it. A better way will be each frame instead of reset to read and store
* register values (this is good for history too) and make the difference that way.
*/
void ResetRegisters();
/// You can remove thread data ONLY IF YOU ARE SURE THIS THREAD IS NO LONGER ACTIVE! This will work only is specific cases.
void RemoveThreadData(AZStd::thread_id id);
private:
/// Register a new system in the profiler. Make sure the proper locks are LOCKED when calling this function (m_threadDataMutex)
bool RegisterSystem(AZ::u32 systemId, const char* name, bool isActive);
/// Unregister a system. Make sure the proper locks are LOCKED when calling this function (m_threadDataMutex)
bool UnregisterSystem(AZ::u32 systemId);
/// Sets the system active/inactive state. Make sure the proper locks are LOCKED when calling this function (m_threadDataMutex)
bool SetSystemState(AZ::u32 systemId, bool isActive);
Profiler(const Descriptor& desc);
Profiler& operator=(const Profiler&);
ProfilerData* m_data; ///< Hidden data to reduce the number of header files included;
static Profiler* s_instance; ///< The only instance of the profiler.
static u64 s_id; ///< Profiler unique (over time) id (don't use the pointer as it might be reused).
static int s_useCount;
};
/**
* A profiler "virtual" register that contains data about a certain place in the code.
*/
class ProfilerRegister
{
friend class Profiler;
public:
ProfilerRegister()
{}
enum Type
{
PRT_TIME = 0, ///< Time (members m_time,m_childrenTime,m_calls, m_childrenCalls and m_lastParant are used) register.
PRT_VALUE, ///< Value register
};
/// Time register data.
struct TimeData
{
AZ::u64 m_time; ///< Total inclusive time current and children in microseconds.
AZ::u64 m_childrenTime; ///< Time taken by child profilers in microseconds.
AZ::s64 m_calls; ///< Number of calls for this register.
AZ::s64 m_childrenCalls;///< Number of children calls.
ProfilerRegister* m_lastParent; ///< Pointer to the last parent register.
static AZStd::chrono::microseconds s_startStopOverheadPer1000Calls; ///< Static constant representing a standard start stop overhead per 1000 calls. You can use this to adjust timings.
};
/// Value register data.
struct ValuesData
{
AZ::s64 m_value1;
AZ::s64 m_value2;
AZ::s64 m_value3;
AZ::s64 m_value4;
AZ::s64 m_value5;
};
static ProfilerRegister* TimerCreateAndStart(const char* systemName, const char* name, ProfilerSection* section, const char* function, int line);
static ProfilerRegister* ValueCreate(const char* systemName, const char* name, const char* function, int line);
void TimerStart(ProfilerSection* section);
void ValueSet(const AZ::s64& v1);
void ValueSet(const AZ::s64& v1, const AZ::s64& v2);
void ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3);
void ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4);
void ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4, const AZ::s64& v5);
void ValueAdd(const AZ::s64& v1);
void ValueAdd(const AZ::s64& v1, const AZ::s64& v2);
void ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3);
void ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4);
void ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4, const AZ::s64& v5);
// Dynamic register data
union
{
TimeData m_timeData;
ValuesData m_userValues;
};
// Static value data.
const char* m_name; ///< Name of the profiler register.
const char* m_function; ///< Function name in the code.
int m_line; ///< Line number if the code.
AZ::u32 m_systemId; ///< ID of the system this profiler belongs to.
unsigned char m_type : 7; ///< Register type
unsigned char m_isActive : 1; ///< Flag if the profiler is active.
private:
friend class ProfilerSection;
static ProfilerRegister* CreateRegister(const char* systemName, const char* name, const char* function, int line, ProfilerRegister::Type type);
/// Compute static start/stop overhead approximation. You can call this periodically (or not) to update the overhead.
static void TimerComputeStartStopOverhead();
void TimerStop();
void Reset();
ProfilerRegister* GetValueRegisterForThisThread();
ProfilerThreadData* m_threadData; ///< Pointer to this entry thread data.
};
/**
* Scoped stop register count on destruction.
*/
class ProfilerSection
{
friend class ProfilerRegister;
public:
ProfilerSection()
: m_register(nullptr)
, m_profilerId(AZ::Debug::Profiler::GetId())
, m_childTime(0)
, m_childCalls(0)
{}
~ProfilerSection()
{
// If we have a valid register and the profiler did not change while we were active stop the register.
if (m_register && m_profilerId == AZ::Debug::Profiler::GetId())
{
m_register->TimerStop();
}
}
void Stop()
{
// If we have a valid register and the profiler did not change while we were active stop the register.
if (m_register && m_profilerId == AZ::Debug::Profiler::GetId())
{
m_register->TimerStop();
}
m_register = nullptr;
}
private:
ProfilerRegister* m_register; ///< Pointer to the owning profiler register.
u64 m_profilerId; ///< Id of the profiler when we started this section.
AZStd::chrono::system_clock::time_point m_start; ///< Start mark.
AZStd::chrono::microseconds m_childTime; ///< Time spent in child profilers.
int m_childCalls; ///< Number of children calls.
};
AZ_FORCE_INLINE ProfilerRegister* ProfilerRegister::GetValueRegisterForThisThread()
{
return this;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1, const AZ::s64& v2)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
reg->m_userValues.m_value2 = v2;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
reg->m_userValues.m_value2 = v2;
reg->m_userValues.m_value3 = v3;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
reg->m_userValues.m_value2 = v2;
reg->m_userValues.m_value3 = v3;
reg->m_userValues.m_value4 = v4;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4, const AZ::s64& v5)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
reg->m_userValues.m_value2 = v2;
reg->m_userValues.m_value3 = v3;
reg->m_userValues.m_value4 = v4;
reg->m_userValues.m_value5 = v5;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1, const AZ::s64& v2)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
reg->m_userValues.m_value2 += v2;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
reg->m_userValues.m_value2 += v2;
reg->m_userValues.m_value3 += v3;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
reg->m_userValues.m_value2 += v2;
reg->m_userValues.m_value3 += v3;
reg->m_userValues.m_value4 += v4;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4, const AZ::s64& v5)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
reg->m_userValues.m_value2 += v2;
reg->m_userValues.m_value3 += v3;
reg->m_userValues.m_value4 += v4;
reg->m_userValues.m_value5 += v5;
}
} // namespace Debug
namespace Internal
{
struct RegisterData
{
AZ::Debug::ProfilerRegister* m_register; ///< Pointer to the register data.
AZ::u64 m_profilerId; ///< Profiler ID which create the \ref register data.
};
}
} // namespace AZ
} // namespace AZ::Debug
#ifdef USE_PIX
// The pix3 header unfortunately brings in other Windows macros we need to undef
@@ -1,310 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/ProfilerDriller.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/bind/bind.h>
namespace AZ
{
namespace Debug
{
//=========================================================================
// ProfilerDriller
// [7/9/2013]
//=========================================================================
ProfilerDriller::ProfilerDriller()
{
AZStd::ThreadDrillerEventBus::Handler::BusConnect();
}
//=========================================================================
// ~ProfilerDriller
// [7/9/2013]
//=========================================================================
ProfilerDriller::~ProfilerDriller()
{
AZStd::ThreadDrillerEventBus::Handler::BusDisconnect();
}
//=========================================================================
// Start
// [5/24/2013]
//=========================================================================
void ProfilerDriller::Start(const Param* params, int numParams)
{
for (int i = 0; i < m_numberOfSystemFilters; ++i)
{
m_systemFilters[i].desc = "SystemID of the system which counters we are interested in";
m_systemFilters[i].type = Param::PT_INT;
m_systemFilters[i].value = 0;
}
// Copy valid filters.
m_numberOfValidFilters = 0;
if (params)
{
for (int i = 0; i < numParams; ++i)
{
if (params[i].type == Param::PT_INT && params[i].value != 0)
{
m_systemFilters[m_numberOfValidFilters++].value = params[i].value;
}
}
}
// output current threads
for (ThreadArrayType::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
{
OutputThreadEnter(*it);
}
ProfilerDrillerBus::Handler::BusConnect();
if (!Profiler::IsReady())
{
Profiler::Create();
}
Profiler::AddReference();
}
//=========================================================================
// Stop
// [5/24/2013]
//=========================================================================
void ProfilerDriller::Stop()
{
Profiler::ReleaseReference();
ProfilerDrillerBus::Handler::BusDisconnect();
}
//=========================================================================
// OnError
// [2/8/2013]
//=========================================================================
void ProfilerDriller::Update()
{
// \note We could add thread_id in addition to the System ID, but I can't foresee many cases where we would like to profile only a specific thread.
if (m_numberOfValidFilters)
{
for (int iFilter = 0; iFilter < m_numberOfValidFilters; ++iFilter)
{
AZ::u32 systemFilter = *reinterpret_cast<AZ::u32*>(&m_systemFilters[iFilter].value);
Profiler::Instance().ReadRegisterValues(AZStd::bind(&ProfilerDriller::ReadProfilerRegisters, this, AZStd::placeholders::_1, AZStd::placeholders::_2), systemFilter);
}
}
else
{
Profiler::Instance().ReadRegisterValues(AZStd::bind(&ProfilerDriller::ReadProfilerRegisters, this, AZStd::placeholders::_1, AZStd::placeholders::_2), 0);
}
}
//=========================================================================
// ReadProfilerRegisters
// [2/11/2013]
//=========================================================================
bool ProfilerDriller::ReadProfilerRegisters(const ProfilerRegister& reg, const AZStd::thread_id& id)
{
(void)id;
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("UpdateRegister", 0x6c00b890));
m_output->Write(AZ_CRC("Id", 0xbf396750), &reg);
// Send only the data which is changing
switch (reg.m_type)
{
case ProfilerRegister::PRT_TIME:
{
m_output->Write(AZ_CRC("Time", 0x6f949845), reg.m_timeData.m_time);
m_output->Write(AZ_CRC("ChildrenTime", 0x46162d3f), reg.m_timeData.m_childrenTime);
m_output->Write(AZ_CRC("Calls", 0xdaa35c8f), reg.m_timeData.m_calls);
m_output->Write(AZ_CRC("ChildrenCalls", 0x6a5a4618), reg.m_timeData.m_childrenCalls);
m_output->Write(AZ_CRC("ParentId", 0x856a684c), reg.m_timeData.m_lastParent);
} break;
case ProfilerRegister::PRT_VALUE:
{
m_output->Write(AZ_CRC("Value1", 0xa2756c5a), reg.m_userValues.m_value1);
m_output->Write(AZ_CRC("Value2", 0x3b7c3de0), reg.m_userValues.m_value2);
m_output->Write(AZ_CRC("Value3", 0x4c7b0d76), reg.m_userValues.m_value3);
m_output->Write(AZ_CRC("Value4", 0xd21f98d5), reg.m_userValues.m_value4);
m_output->Write(AZ_CRC("Value5", 0xa518a843), reg.m_userValues.m_value5);
} break;
}
m_output->EndTag(AZ_CRC("UpdateRegister", 0x6c00b890));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
return true;
}
//=========================================================================
// OnThreadEnter
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnThreadEnter(const AZStd::thread_id& id, const AZStd::thread_desc* desc)
{
m_threads.push_back();
ThreadInfo& info = m_threads.back();
info.m_id = (size_t)id.m_id;
if (desc)
{
info.m_name = desc->m_name;
info.m_cpuId = desc->m_cpuId;
info.m_priority = desc->m_priority;
info.m_stackSize = desc->m_stackSize;
}
else
{
info.m_name = nullptr;
info.m_cpuId = -1;
info.m_priority = -100000;
info.m_stackSize = 0;
}
if (m_output)
{
OutputThreadEnter(info);
}
}
//=========================================================================
// OnThreadExit
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnThreadExit(const AZStd::thread_id& id)
{
ThreadArrayType::iterator it = m_threads.begin();
while (it != m_threads.end())
{
if (it->m_id == (size_t)id.m_id)
{
break;
}
++it;
}
if (it != m_threads.end())
{
if (m_output)
{
OutputThreadExit(*it);
}
m_threads.erase(it);
}
}
//=========================================================================
// OutputThreadEnter
// [7/9/2013]
//=========================================================================
void ProfilerDriller::OutputThreadEnter(const ThreadInfo& threadInfo)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("ThreadEnter", 0x60e4acfb));
m_output->Write(AZ_CRC("Id", 0xbf396750), threadInfo.m_id);
if (threadInfo.m_name)
{
m_output->Write(AZ_CRC("Name", 0x5e237e06), threadInfo.m_name);
}
m_output->Write(AZ_CRC("CpuId", 0xdf558508), threadInfo.m_cpuId);
m_output->Write(AZ_CRC("Priority", 0x62a6dc27), threadInfo.m_priority);
m_output->Write(AZ_CRC("StackSize", 0x9cfaf35b), threadInfo.m_stackSize);
m_output->EndTag(AZ_CRC("ThreadEnter", 0x60e4acfb));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
//=========================================================================
// OutputThreadExit
// [7/9/2013]
//=========================================================================
void ProfilerDriller::OutputThreadExit(const ThreadInfo& threadInfo)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("OnThreadExit", 0x16042db9));
m_output->Write(AZ_CRC("Id", 0xbf396750), threadInfo.m_id);
m_output->EndTag(AZ_CRC("OnThreadExit", 0x16042db9));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
//=========================================================================
// OnRegisterSystem
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnRegisterSystem(AZ::u32 id, const char* name)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("RegisterSystem", 0x957739ef));
m_output->Write(AZ_CRC("Id", 0xbf396750), id);
m_output->Write(AZ_CRC("Name", 0x5e237e06), name);
m_output->EndTag(AZ_CRC("RegisterSystem", 0x957739ef));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
//=========================================================================
// OnUnregisterSystem
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnUnregisterSystem(AZ::u32 id)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("UnregisterSystem", 0xa20538e4));
m_output->Write(AZ_CRC("Id", 0xbf396750), id);
m_output->EndTag(AZ_CRC("UnregisterSystem", 0xa20538e4));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
//=========================================================================
// OnNewRegister
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnNewRegister(const ProfilerRegister& reg, const AZStd::thread_id& threadId)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("NewRegister", 0xf0f2f287));
m_output->Write(AZ_CRC("Id", 0xbf396750), &reg);
m_output->Write(AZ_CRC("ThreadId", 0xd0fd9043), threadId.m_id);
if (reg.m_name)
{
m_output->Write(AZ_CRC("Name", 0x5e237e06), reg.m_name);
}
if (reg.m_function)
{
m_output->Write(AZ_CRC("Function", 0xcaae163d), reg.m_function);
}
m_output->Write(AZ_CRC("Line", 0xd114b4f6), reg.m_line);
m_output->Write(AZ_CRC("SystemId", 0x0dfecf6f), reg.m_systemId);
m_output->Write(AZ_CRC("Type", 0x8cde5729), reg.m_type);
switch (reg.m_type)
{
case ProfilerRegister::PRT_TIME:
{
m_output->Write(AZ_CRC("Time", 0x6f949845), reg.m_timeData.m_time);
m_output->Write(AZ_CRC("ChildrenTime", 0x46162d3f), reg.m_timeData.m_childrenTime);
m_output->Write(AZ_CRC("Calls", 0xdaa35c8f), reg.m_timeData.m_calls);
m_output->Write(AZ_CRC("ChildrenCalls", 0x6a5a4618), reg.m_timeData.m_childrenCalls);
m_output->Write(AZ_CRC("ParentId", 0x856a684c), reg.m_timeData.m_lastParent);
} break;
case ProfilerRegister::PRT_VALUE:
{
m_output->Write(AZ_CRC("Value1", 0xa2756c5a), reg.m_userValues.m_value1);
m_output->Write(AZ_CRC("Value2", 0x3b7c3de0), reg.m_userValues.m_value2);
m_output->Write(AZ_CRC("Value3", 0x4c7b0d76), reg.m_userValues.m_value3);
m_output->Write(AZ_CRC("Value4", 0xd21f98d5), reg.m_userValues.m_value4);
m_output->Write(AZ_CRC("Value5", 0xa518a843), reg.m_userValues.m_value5);
} break;
}
m_output->EndTag(AZ_CRC("NewRegister", 0xf0f2f287));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
} // namespace Debug
} // namespace AZ
@@ -1,102 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_PROFILER_DRILLER_H
#define AZCORE_PROFILER_DRILLER_H 1
#include <AzCore/Driller/Driller.h>
#include <AzCore/Debug/ProfilerDrillerBus.h>
#include <AzCore/std/parallel/threadbus.h>
namespace AZStd
{
struct thread_id;
struct thread_desc;
}
namespace AZ
{
namespace Debug
{
struct ProfilerSystemData;
class ProfilerRegister;
/**
* ProfilerDriller or we can just make a Profiler driller and read the registers ourself.
*/
class ProfilerDriller
: public Driller
, public ProfilerDrillerBus::Handler
, public AZStd::ThreadDrillerEventBus::Handler
{
struct ThreadInfo
{
AZ::u64 m_id;
AZ::u32 m_stackSize;
AZ::s32 m_priority;
AZ::s32 m_cpuId;
const char* m_name;
};
typedef vector<ThreadInfo>::type ThreadArrayType;
public:
AZ_CLASS_ALLOCATOR(ProfilerDriller, OSAllocator, 0)
ProfilerDriller();
virtual ~ProfilerDriller();
protected:
//////////////////////////////////////////////////////////////////////////
// Driller
virtual const char* GroupName() const { return "SystemDrillers"; }
virtual const char* GetName() const { return "ProfilerDriller"; }
virtual const char* GetDescription() const { return "Collects data from all available profile registers."; }
virtual int GetNumParams() const { return m_numberOfSystemFilters; }
virtual const Param* GetParam(int index) const { AZ_Assert(index >= 0 && index < m_numberOfSystemFilters, "Invalid index"); return &m_systemFilters[index]; }
virtual void Start(const Param* params = NULL, int numParams = 0);
virtual void Stop();
virtual void Update();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Thread driller event bus
/// Called when we enter a thread, optional thread_desc is provided when the use provides one.
virtual void OnThreadEnter(const AZStd::thread_id& id, const AZStd::thread_desc* desc);
/// Called when we exit a thread.
virtual void OnThreadExit(const AZStd::thread_id& id);
/// Output thread enter to stream.
void OutputThreadEnter(const ThreadInfo& threadInfo);
/// Output thread exit to stream.
void OutputThreadExit(const ThreadInfo& threadInfo);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Profiler Driller bus
virtual void OnRegisterSystem(AZ::u32 id, const char* name);
virtual void OnUnregisterSystem(AZ::u32 id);
virtual void OnNewRegister(const ProfilerRegister& reg, const AZStd::thread_id& threadId);
//////////////////////////////////////////////////////////////////////////
/// Read profile registers callback.
bool ReadProfilerRegisters(const ProfilerRegister& reg, const AZStd::thread_id& id);
static const int m_numberOfSystemFilters = 16;
int m_numberOfValidFilters = 0 ; ///< Number of valid filter set when the driller was created.
Param m_systemFilters[m_numberOfSystemFilters]; ///< If != 0, it's a ID of specific System we would like to drill.
ThreadArrayType m_threads;
};
}
} // namespace AZ
#endif // AZCORE_PROFILER_DRILLER_H
#pragma once
@@ -1,45 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_PROFILER_DRILLER_BUS_H
#define AZCORE_PROFILER_DRILLER_BUS_H
#include <AzCore/Driller/DrillerBus.h>
namespace AZStd
{
struct thread_id;
}
namespace AZ
{
namespace Debug
{
class ProfilerRegister;
/**
* ProfilerDrillerInterface driller profiler event interface, that records events from the profiler system.
*/
class ProfilerDrillerInterface
: public DrillerEBusTraits
{
public:
virtual ~ProfilerDrillerInterface() {}
virtual void OnRegisterSystem(AZ::u32 id, const char* name) = 0;
virtual void OnUnregisterSystem(AZ::u32 id) = 0;
virtual void OnNewRegister(const ProfilerRegister& reg, const AZStd::thread_id& threadId) = 0;
};
typedef AZ::EBus<ProfilerDrillerInterface> ProfilerDrillerBus;
}
}
#endif // AZCORE_PROFILER_DRILLER_BUS_H
#pragma once
@@ -426,7 +426,6 @@ namespace AZ
void FileIOStream::Seek(OffsetType bytes, SeekMode mode)
{
AZ_PROFILE_SCOPE(AzCore, "FileIO Seek: %s", m_filename.c_str());
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
AZ_Assert(IsOpen(), "Cannot seek on a FileIOStream that is not open.");
@@ -454,7 +453,6 @@ namespace AZ
SizeType FileIOStream::Read(SizeType bytes, void* oBuffer)
{
AZ_PROFILE_SCOPE(AzCore, "FileIO Read: %s", m_filename.c_str());
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
AZ_Assert(IsOpen(), "Cannot read from a FileIOStream that is not open.");
@@ -93,20 +93,17 @@ set(FILES
Debug/AssetTracking.h
Debug/AssetTrackingTypesImpl.h
Debug/AssetTrackingTypes.h
Debug/Budget.h
Debug/Budget.cpp
Debug/BudgetsComponent.h
Debug/BudgetsComponent.cpp
Debug/LocalFileEventLogger.h
Debug/LocalFileEventLogger.cpp
Debug/FrameProfiler.h
Debug/FrameProfilerBus.h
Debug/FrameProfilerComponent.cpp
Debug/FrameProfilerComponent.h
Debug/IEventLogger.h
Debug/MemoryProfiler.h
Debug/Profiler.cpp
Debug/Profiler.h
Debug/ProfilerBus.h
Debug/ProfilerDriller.cpp
Debug/ProfilerDriller.h
Debug/ProfilerDrillerBus.h
Debug/StackTracer.h
Debug/EventTrace.h
Debug/EventTrace.cpp
@@ -572,8 +569,6 @@ set(FILES
Statistics/StatisticalProfilerProxySystemComponent.cpp
Statistics/StatisticalProfilerProxySystemComponent.h
Statistics/StatisticsManager.h
Statistics/TimeDataStatisticsManager.cpp
Statistics/TimeDataStatisticsManager.h
StringFunc/StringFunc.cpp
StringFunc/StringFunc.h
UserSettings/UserSettings.cpp
@@ -22,8 +22,6 @@
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Debug/FrameProfilerBus.h>
#include <AzCore/Debug/FrameProfilerComponent.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/UnitTest/TestTypes.h>
@@ -1,208 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UnitTest/UnitTest.h>
#include <AzTest/AzTest.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Statistics/TimeDataStatisticsManager.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/EntityUtils.h>
#include <AzCore/Debug/FrameProfilerBus.h>
#include <AzCore/Debug/FrameProfilerComponent.h>
using namespace AZ;
using namespace Debug;
namespace UnitTest
{
/**
* Validate functionality of the convenience class TimeDataStatisticsManager.
* It is a specialized version of RunningStatisticsManager that works with Timer type
* of registers that can be captured with the FrameProfilerBus::OnFrameProfilerData()
*/
class TimeDataStatisticsManagerTest
: public AllocatorsFixture
, public FrameProfilerBus::Handler
{
static constexpr const char* PARENT_TIMER_STAT = "ParentStat";
static constexpr const char* CHILD_TIMER_STAT0 = "ChildStat0";
static constexpr const char* CHILD_TIMER_STAT1 = "ChildStat1";
public:
TimeDataStatisticsManagerTest()
: AllocatorsFixture()
{
}
void SetUp() override
{
AllocatorsFixture::SetUp();
m_statsManager = AZStd::make_unique<Statistics::TimeDataStatisticsManager>();
}
void TearDown() override
{
m_statsManager = nullptr;
AllocatorsFixture::TearDown();
}
//////////////////////////////////////////////////////////////////////////
// FrameProfilerBus
virtual void OnFrameProfilerData(const FrameProfiler::ThreadDataArray& data)
{
for (size_t iThread = 0; iThread < data.size(); ++iThread)
{
const FrameProfiler::ThreadData& td = data[iThread];
FrameProfiler::ThreadData::RegistersMap::const_iterator regIt = td.m_registers.begin();
for (; regIt != td.m_registers.end(); ++regIt)
{
const FrameProfiler::RegisterData& rd = regIt->second;
u32 unitTestCrc = AZ_CRC("UnitTest", 0x8089cea8);
if (unitTestCrc != rd.m_systemId)
{
continue; //Not for us.
}
ASSERT_EQ(ProfilerRegister::PRT_TIME, rd.m_type);
const FrameProfiler::FrameData& fd = rd.m_frames.back();
m_statsManager->PushTimeDataSample(rd.m_name, fd.m_timeData);
}
}
}
//////////////////////////////////////////////////////////////////////////
int ChildFunction0(int numIterations, int sleepTimeMilliseconds)
{
AZ_PROFILE_SCOPE(UnitTest, CHILD_TIMER_STAT0);
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(sleepTimeMilliseconds));
int result = 5;
for (int i = 0; i < numIterations; ++i)
{
result += i % 3;
}
return result;
}
int ChildFunction1(int numIterations, int sleepTimeMilliseconds)
{
AZ_PROFILE_SCOPE(UnitTest, CHILD_TIMER_STAT1);
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(sleepTimeMilliseconds));
int result = 5;
for (int i = 0; i < numIterations; ++i)
{
result += i % 3;
}
return result;
}
int ParentFunction(int numIterations, int sleepTimeMilliseconds)
{
AZ_PROFILE_SCOPE(UnitTest, PARENT_TIMER_STAT);
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(sleepTimeMilliseconds));
int result = 0;
result += ChildFunction0(numIterations, sleepTimeMilliseconds);
result += ChildFunction1(numIterations, sleepTimeMilliseconds);
return result;
}
void run()
{
Debug::FrameProfilerBus::Handler::BusConnect();
ComponentApplication app;
ComponentApplication::Descriptor desc;
desc.m_useExistingAllocator = true;
desc.m_enableDrilling = false; // we already created a memory driller for the test (AllocatorsFixture)
ComponentApplication::StartupParameters startupParams;
startupParams.m_allocator = &AllocatorInstance<SystemAllocator>::Get();
Entity* systemEntity = app.Create(desc, startupParams);
systemEntity->CreateComponent<FrameProfilerComponent>();
systemEntity->Init();
systemEntity->Activate(); // start frame component
const int sleepTimeAllFuncsMillis = 1;
const int numIterations = 10;
for (int iterationCounter = 0; iterationCounter < numIterations; ++iterationCounter)
{
ParentFunction(numIterations, sleepTimeAllFuncsMillis);
//Collect all samples.
app.Tick();
}
//Verify we have three running stats.
{
AZStd::vector<Statistics::NamedRunningStatistic*> allStats;
m_statsManager->GetAllStatistics(allStats);
EXPECT_EQ(allStats.size(), 3);
}
AZStd::string parentStatName(PARENT_TIMER_STAT);
AZStd::string child0StatName(CHILD_TIMER_STAT0);
AZStd::string child1StatName(CHILD_TIMER_STAT1);
ASSERT_TRUE(m_statsManager->GetStatistic(parentStatName) != nullptr);
ASSERT_TRUE(m_statsManager->GetStatistic(child0StatName) != nullptr);
ASSERT_TRUE(m_statsManager->GetStatistic(child1StatName) != nullptr);
EXPECT_EQ(m_statsManager->GetStatistic(parentStatName)->GetNumSamples(), numIterations);
EXPECT_EQ(m_statsManager->GetStatistic(child0StatName)->GetNumSamples(), numIterations);
EXPECT_EQ(m_statsManager->GetStatistic(child1StatName)->GetNumSamples(), numIterations);
const double minimumExpectDurationOfChildFunctionMicros = 1;
const double minimumExpectDurationOfParentFunctionMicros = 1;
EXPECT_GE(m_statsManager->GetStatistic(parentStatName)->GetMinimum(), minimumExpectDurationOfParentFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(parentStatName)->GetAverage(), minimumExpectDurationOfParentFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(parentStatName)->GetMaximum(), minimumExpectDurationOfParentFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child0StatName)->GetMinimum(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child0StatName)->GetAverage(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child0StatName)->GetMaximum(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child1StatName)->GetMinimum(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child1StatName)->GetAverage(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child1StatName)->GetMaximum(), minimumExpectDurationOfChildFunctionMicros);
//Let's validate TimeDataStatisticsManager::RemoveStatistics()
m_statsManager->RemoveStatistic(child1StatName);
ASSERT_TRUE(m_statsManager->GetStatistic(parentStatName) != nullptr);
ASSERT_TRUE(m_statsManager->GetStatistic(child0StatName) != nullptr);
EXPECT_EQ(m_statsManager->GetStatistic(child1StatName), nullptr);
//Let's store the sample count for both parentStatName and child0StatName.
const AZ::u64 numSamplesParent = m_statsManager->GetStatistic(parentStatName)->GetNumSamples();
const AZ::u64 numSamplesChild0 = m_statsManager->GetStatistic(child0StatName)->GetNumSamples();
//Let's call child1 function again and call app.Tick(). child1StatName should be readded to m_statsManager.
ChildFunction1(numIterations, sleepTimeAllFuncsMillis);
app.Tick();
ASSERT_TRUE(m_statsManager->GetStatistic(child1StatName) != nullptr);
EXPECT_EQ(m_statsManager->GetStatistic(parentStatName)->GetNumSamples(), numSamplesParent);
EXPECT_EQ(m_statsManager->GetStatistic(child0StatName)->GetNumSamples(), numSamplesChild0);
EXPECT_EQ(m_statsManager->GetStatistic(child1StatName)->GetNumSamples(), 1);
Debug::FrameProfilerBus::Handler::BusDisconnect();
app.Destroy();
}
AZStd::unique_ptr<Statistics::TimeDataStatisticsManager> m_statsManager;
};//class TimeDataStatisticsManagerTest
// TODO:BUDGETS disabled until profiler budgets system comes online
// TEST_F(TimeDataStatisticsManagerTest, Test)
// {
// run();
// }
//End of all Tests of TimeDataStatisticsManagerTest
}//namespace UnitTest
@@ -66,7 +66,6 @@ set(FILES
SystemFile.cpp
TaskTests.cpp
TickBusTest.cpp
TimeDataStatistics.cpp
UUIDTests.cpp
XML.cpp
Debug/AssetTracking.cpp
@@ -25,7 +25,6 @@
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/Serialization/DataPatch.h>
#include <AzCore/Debug/FrameProfilerComponent.h>
#include <AzCore/NativeUI/NativeUISystemComponent.h>
#include <AzCore/Module/ModuleManagerBus.h>
#include <AzCore/Interface/Interface.h>
@@ -296,7 +295,6 @@ namespace AzFramework
azrtti_typeid<AZ::JobManagerComponent>(),
azrtti_typeid<AZ::AssetManagerComponent>(),
azrtti_typeid<AZ::UserSettingsComponent>(),
azrtti_typeid<AZ::Debug::FrameProfilerComponent>(),
azrtti_typeid<AZ::SliceComponent>(),
azrtti_typeid<AZ::SliceSystemComponent>(),
@@ -191,3 +191,5 @@ namespace AzFramework
};
} // namespace AzFramework
AZ_DECLARE_BUDGET(AzFramework);
@@ -27,6 +27,8 @@
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
#include <AzFramework/Visibility/OctreeSystemComponent.h>
AZ_DEFINE_BUDGET(AzFramework);
namespace AzFramework
{
AzFrameworkModule::AzFrameworkModule()
@@ -10,6 +10,7 @@
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzFramework/Entity/EntityOwnershipServiceBus.h>
@@ -18,6 +19,8 @@ namespace AZ
class Entity;
}
AZ_DECLARE_BUDGET(AzFramework);
namespace AzFramework
{
// Types
@@ -33,6 +33,8 @@ extern "C" {
# include <Lua/lauxlib.h>
}
AZ_DEFINE_BUDGET(Script);
namespace ScriptComponentCpp
{
template<typename T>
@@ -10,6 +10,7 @@
#define AZFRAMEWORK_TARGETMANAGEMENTAPI_H
#include <AzCore/base.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/deque.h>
@@ -21,6 +22,8 @@
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Memory/OSAllocator.h>
AZ_DECLARE_BUDGET(AzFramework);
namespace AZ
{
class ReflectContext;
@@ -12,6 +12,8 @@
#include <AzFramework/Visibility/BoundsBus.h>
#include <cstring>
AZ_DECLARE_BUDGET(AzFramework);
namespace AzFramework
{
EntityVisibilityBoundsUnionSystem::EntityVisibilityBoundsUnionSystem()
@@ -5,14 +5,10 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZTOOLSFRAMEWORK_TOOLSAPPLICATIONAPI_H
#define AZTOOLSFRAMEWORK_TOOLSAPPLICATIONAPI_H
#include <AzCore/base.h>
#pragma once
#include <AzCore/base.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Uuid.h>
@@ -1089,4 +1085,5 @@ namespace AzToolsFramework
}
} // namespace AzToolsFramework
#endif // AZTOOLSFRAMEWORK_TOOLSAPPLICATIONAPI_H
AZ_DECLARE_BUDGET(AzToolsFramework);
@@ -52,6 +52,8 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h>
AZ_DEFINE_BUDGET(AzToolsFramework);
namespace AzToolsFramework
{
AzToolsFrameworkModule::AzToolsFrameworkModule()
@@ -38,7 +38,6 @@
#include <AzFramework/Driller/RemoteDrillerInterface.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Debug/ProfilerDriller.h>
#ifdef AZ_PLATFORM_WINDOWS
#include "shlobj.h"
@@ -5,7 +5,10 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/base.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/RTTI/RTTI.h>
@@ -14,7 +17,7 @@
#include <AzCore/Component/ComponentBus.h>
#include "PropertyEditorAPI_Internals.h"
#pragma once
AZ_DECLARE_BUDGET(AzToolsFramework);
class QWidget;
class QCheckBox;
@@ -9,8 +9,11 @@
#pragma once
#include <AzCore/Component/Entity.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/Interface/Interface.h>
AZ_DECLARE_BUDGET(AzToolsFramework);
namespace AzToolsFramework
{
namespace UndoSystem
@@ -6,6 +6,7 @@
*
*/
#include <AzCore/Debug/Budget.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/std/hash.h>
@@ -14,6 +15,8 @@
#include <GridMate/GridMateEventsBus.h>
#include <GridMate/Version.h>
AZ_DEFINE_BUDGET(GridMate);
namespace GridMate
{
class GridMateImpl
@@ -5,11 +5,9 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef GM_REPLICADEFS_H
#define GM_REPLICADEFS_H
/// \file ReplicaChunk.h
#pragma once
#include <AzCore/Debug/Budget.h>
#include <AzCore/Math/Crc.h>
namespace GridMate
@@ -80,4 +78,5 @@ namespace GridMate
static const ZoneMask ZoneMask_All = (ZoneMask) - 1;
} // namespace Gridmate
#endif // GM_REPLICADEFS_H
AZ_DECLARE_BUDGET(GridMate);
@@ -6,8 +6,6 @@
*
*/
#include <AzCore/Debug/Profiler.h>
#include <GridMate/Replica/SystemReplicas.h>
#include <GridMate/Replica/Tasks/ReplicaMarshalTasks.h>
#include <GridMate/Replica/ReplicaDrillerEvents.h>
-67
View File
@@ -1,67 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Debug/Profiler.h>
#define SUBSYSTEM_DEFINES \
X(PROFILE_ANY, "Any") \
X(PROFILE_RENDERER, "Renderer") \
X(PROFILE_3DENGINE, "3DEngine") \
X(PROFILE_PARTICLE, "Particle") \
X(PROFILE_AI, "AI") \
X(PROFILE_ANIMATION, "Animation") \
X(PROFILE_MOVIE, "Movie") \
X(PROFILE_ENTITY, "Entity") \
X(PROFILE_UI, "UI") \
X(PROFILE_NETWORK, "Network") \
X(PROFILE_PHYSICS, "Physics") \
X(PROFILE_SCRIPT, "Script") \
X(PROFILE_SCRIPT_CFUNC, "Script C Functions") \
X(PROFILE_AUDIO, "Audio") \
X(PROFILE_EDITOR, "Editor") \
X(PROFILE_SYSTEM, "System") \
X(PROFILE_ACTION, "Action") \
X(PROFILE_GAME, "Game") \
X(PROFILE_INPUT, "Input") \
X(PROFILE_SYNC, "Sync") \
X(PROFILE_NETWORK_TRAFFIC, "Network Traffic") \
X(PROFILE_DEVICE, "Device")
#define X(Subsystem, SubsystemName) Subsystem,
enum EProfiledSubsystem
{
SUBSYSTEM_DEFINES
PROFILE_LAST_SUBSYSTEM
};
#undef X
#include <AzCore/Debug/EventTrace.h>
#define FUNCTION_PROFILER_LEGACYONLY(pISystem, subsystem)
#define FUNCTION_PROFILER(pISystem, subsystem)
#define FUNCTION_PROFILER_FAST(pISystem, subsystem, bProfileEnabled)
#define FUNCTION_PROFILER_ALWAYS(pISystem, subsystem)
#define FRAME_PROFILER_LEGACYONLY(szProfilerName, pISystem, subsystem)
#define FRAME_PROFILER(szProfilerName, pISystem, subsystem)
#define FRAME_PROFILER_FAST(szProfilerName, pISystem, subsystem, bProfileEnabled)
#define FUNCTION_PROFILER_SYS(subsystem)
#define STALL_PROFILER(cause)
-7
View File
@@ -1654,11 +1654,4 @@ inline void CryLogAlways(const char* format, ...)
}
#endif // EXCLUDE_NORMAL_LOG
//////////////////////////////////////////////////////////////////////////
// Additional headers.
//////////////////////////////////////////////////////////////////////////
#include <FrameProfiler.h>
#endif // CRYINCLUDE_CRYCOMMON_ISYSTEM_H
@@ -87,7 +87,6 @@ set(FILES
CrySystemBus.h
CryTypeInfo.h
CryVersion.h
FrameProfiler.h
HeapAllocator.h
LegacyAllocator.cpp
LegacyAllocator.h
@@ -1899,7 +1899,6 @@ static void LogDecompTimer(__int64 nTotalTicks, __int64 nDecompTicks, __int64 nA
AZStd::string CLocalizedStringsManager::SLocalizedStringEntry::GetTranslatedText(const SLanguage* pLanguage) const
{
FUNCTION_PROFILER_FAST(GetISystem(), PROFILE_SYSTEM, g_bProfilerEnabled);
if ((flags & IS_COMPRESSED) != 0)
{
#if defined(LOG_DECOMP_TIMES)
-3
View File
@@ -396,7 +396,6 @@ void CLog::LogV(const ELogType type, [[maybe_unused]]int flags, const char* szFo
}
}
FUNCTION_PROFILER(GetISystem(), PROFILE_SYSTEM);
LOADING_TIME_PROFILE_SECTION(GetISystem());
bool bfile = false, bconsole = false;
@@ -1445,8 +1444,6 @@ void CLog::RemoveCallback(ILogCallback* pCallback)
//////////////////////////////////////////////////////////////////////////
void CLog::Update()
{
FUNCTION_PROFILER_FAST(m_pSystem, PROFILE_SYSTEM, g_bProfilerEnabled);
if (CryGetCurrentThreadId() == m_nMainThreadId)
{
if (!m_threadSafeMsgQueue.empty())
+1 -15
View File
@@ -659,8 +659,6 @@ ISystem* CSystem::GetCrySystem()
//////////////////////////////////////////////////////////////////////////
void CSystem::SleepIfNeeded()
{
FUNCTION_PROFILER_FAST(this, PROFILE_SYSTEM, g_bProfilerEnabled);
ITimer* const pTimer = gEnv->pTimer;
static bool firstCall = true;
@@ -738,7 +736,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
_mm_setcsr(_mm_getcsr() & ~0x280 | (g_cvars.sys_float_exceptions > 0 ? 0 : 0x280));
#endif //WIN32
FUNCTION_PROFILER_LEGACYONLY(GetISystem(), PROFILE_SYSTEM);
AZ_TRACE_METHOD();
m_nUpdateCounter++;
@@ -832,8 +829,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
//limit frame rate if vsync is turned off
//for consoles this is done inside renderthread to be vsync dependent
{
FRAME_PROFILER_LEGACYONLY("FRAME_CAP", gEnv->pSystem, PROFILE_SYSTEM);
AZ_TRACE_METHOD_NAME("FrameLimiter");
static ICVar* pSysMaxFPS = NULL;
static ICVar* pVSync = NULL;
@@ -882,7 +877,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
//update console system
if (m_env.pConsole)
{
FRAME_PROFILER("SysUpdate:Console", this, PROFILE_SYSTEM);
m_env.pConsole->Update();
}
@@ -898,7 +892,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
// Run movie system pre-update
if (!bNoUpdate)
{
FRAME_PROFILER("SysUpdate:UpdateMovieSystem", this, PROFILE_SYSTEM);
UpdateMovieSystem(updateFlags, fMovieFrameTime, true);
}
@@ -914,7 +907,6 @@ bool CSystem::UpdatePostTickBus(int updateFlags, int /*nPauseMode*/)
if (!m_bNoUpdate)
{
const float fMovieFrameTime = m_Time.GetFrameTime(ITimer::ETIMER_UI);
FRAME_PROFILER("SysUpdate:UpdateMovieSystem", this, PROFILE_SYSTEM);
UpdateMovieSystem(updateFlags, fMovieFrameTime, false);
}
@@ -922,7 +914,6 @@ bool CSystem::UpdatePostTickBus(int updateFlags, int /*nPauseMode*/)
// Update sound system
if (!m_bNoUpdate)
{
FRAME_PROFILER("SysUpdate:UpdateAudioSystems", this, PROFILE_SYSTEM);
UpdateAudioSystems();
}
@@ -949,10 +940,7 @@ bool CSystem::UpdatePostTickBus(int updateFlags, int /*nPauseMode*/)
m_updateTimes.push_back(std::make_pair(cur_time, updateTime));
}
{
FRAME_PROFILER("SysUpdate - SystemEventDispatcher::Update", this, PROFILE_SYSTEM);
m_pSystemEventDispatcher->Update();
}
m_pSystemEventDispatcher->Update();
if (!gEnv->IsEditing() && m_eRuntimeState == ESYSTEM_EVENT_LEVEL_GAMEPLAY_START)
{
@@ -973,8 +961,6 @@ bool CSystem::UpdateLoadtime()
void CSystem::UpdateAudioSystems()
{
AZ_TRACE_METHOD();
FRAME_PROFILER_LEGACYONLY("SysUpdate:Audio", this, PROFILE_SYSTEM);
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::ExternalUpdate);
}
@@ -172,8 +172,6 @@ CViewSystem::~CViewSystem()
//------------------------------------------------------------------------
void CViewSystem::Update(float frameTime)
{
FUNCTION_PROFILER(GetISystem(), PROFILE_ACTION);
if (gEnv->IsDedicated())
{
return;
-31
View File
@@ -38,34 +38,3 @@ ly_add_target(
PRIVATE
STANDALONETOOLS_ENABLE_LUA_IDE
)
ly_add_target(
NAME Profiler APPLICATION
NAMESPACE AZ
AUTOMOC
AUTOUIC
AUTORCC
FILES_CMAKE
standalone_tools_files.cmake
profiler_files.cmake
Platform/${PAL_PLATFORM_NAME}/profiler_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
Source/Editor
Source/Driller
Source/Driller/Carrier
Source/Driller/Profiler
Source/Driller/IO
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
AZ::AzCore
AZ::AzFramework
AZ::AzToolsFramework
AZ::GridMate
${additional_dependencies}
COMPILE_DEFINITIONS
PRIVATE
STANDALONETOOLS_ENABLE_PROFILER
)
@@ -1,84 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "AnnotationHeaderView.hxx"
#include "AnnotationsDataView.hxx"
#include "Annotations.hxx"
#include <QtWidgets/QGridLayout>
namespace Driller
{
static const int k_contractedSize = 20;
static const int k_textWidth = 153;
AnnotationHeaderView::AnnotationHeaderView(AnnotationsProvider* ptrAnnotations, QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
, m_ptrAnnotations(ptrAnnotations)
{
setupUi(this);
m_State.m_EndFrame = -1;
m_State.m_FramesInView = 10;
m_State.m_FrameOffset = 0;
annotationDataView->RegisterAnnotationHeaderView(this, m_ptrAnnotations);
annotationDataView->setAutoFillBackground(true);
connect(configureAnnotations, SIGNAL(pressed()), this, SIGNAL(OnOptionsClick()));
connect(annotationDataView, SIGNAL(InformOfMouseOverAnnotation(const Annotation&)), this, SIGNAL(InformOfMouseOverAnnotation(const Annotation&)));
connect(annotationDataView, SIGNAL(InformOfClickAnnotation(const Annotation&)), this, SIGNAL(InformOfClickAnnotation(const Annotation&)));
connect(m_ptrAnnotations, SIGNAL(AnnotationDataInvalidated()), this, SLOT(RefreshView()));
annotationDataView->update();
QSize size = annotationDataView->size();
int nextHeight = size.height();
(void)nextHeight;
}
QSize AnnotationHeaderView::sizeHint() const
{
return QSize(0, k_contractedSize);
}
AnnotationHeaderView::~AnnotationHeaderView()
{
}
void AnnotationHeaderView::RefreshView()
{
annotationDataView->update();
}
void AnnotationHeaderView::SetEndFrame(FrameNumberType frameNum)
{
QSize size = annotationDataView->size();
int nextHeight = size.height();
(void)nextHeight;
m_State.m_EndFrame = frameNum;
annotationDataView->update();
}
void AnnotationHeaderView::SetSliderOffset(FrameNumberType frameNum)
{
m_State.m_FrameOffset = frameNum;
annotationDataView->update();
}
void AnnotationHeaderView::SetDataPointsInView(int count)
{
m_State.m_FramesInView = count;
annotationDataView->update();
}
}
#include <Source/Driller/Annotations/moc_AnnotationHeaderView.cpp>
@@ -1,74 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef ANNOTATION_HEADER_VIEW_HXX
#define ANNOTATION_HEADER_VIEW_HXX
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <QtWidgets/QWidget>
#include <Source/Driller/Annotations/ui_AnnotationHeaderView.h>
#include <Source/Driller/DrillerNetworkMessages.h>
#include <Source/Driller/DrillerDataTypes.h>
#endif
namespace Driller
{
class AnnotationsProvider;
class AnnotationsDataView;
class Annotation;
/** The annotation header view runs along the top of the channels, and shows annotation blips that can be hovered over.
* this gives nice hit boxes for clicking that are not a sliver thick.
*/
class AnnotationHeaderView : public QWidget, private Ui::AnnotationHeaderView
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(AnnotationHeaderView,AZ::SystemAllocator,0);
AnnotationHeaderView(AnnotationsProvider* ptrAnnotations, QWidget* parent = NULL, Qt::WindowFlags flags = Qt::WindowFlags());
virtual ~AnnotationHeaderView(void);
struct HeaderViewState
{
int m_EndFrame;
int m_FramesInView;
int m_FrameOffset;
};
const HeaderViewState& GetState() { return m_State; }
void SetDataPointsInView( int count );
void SetEndFrame( FrameNumberType frame );
void SetSliderOffset( FrameNumberType frame );
signals:
void OnOptionsClick();
void InformOfMouseOverAnnotation(const Annotation& annotation);
void InformOfClickAnnotation(const Annotation& annotation);
public slots:
void RefreshView();
private:
HeaderViewState m_State;
AnnotationsProvider *m_ptrAnnotations;
virtual QSize sizeHint() const;
};
}
#endif // ANNOTATION_HEADER_VIEW_HXX
@@ -1,178 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AnnotationHeaderView</class>
<widget class="QWidget" name="AnnotationHeaderView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1023</width>
<height>38</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="annotationBackground">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QPushButton" name="configureAnnotations">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Configure which messages are annotated on the display</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Configure Annotations</string>
</property>
<property name="icon">
<iconset resource="../../../../../Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Resources/sharedResources.qrc">
<normaloff>:/driller/settings_icon</normaloff>:/driller/settings_icon</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Driller::AnnotationsDataView" name="annotationDataView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Driller::AnnotationsDataView</class>
<extends>QWidget</extends>
<header>Source/Driller/Annotations/AnnotationsDataView.hxx</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../../../Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Resources/sharedResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -1,405 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "Annotations.hxx"
#include <Source/Driller/Workspaces/Workspace.h>
#include <AzCore/std/sort.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <QColor>
#include <QRgb>
namespace Driller
{
// stores the settings that are saved into the file and transported from user to user to accompany drill files.
// as always, this is just a dumb container and does not need encapsulation
class AnnotationWorkspaceSettings
: public AZ::UserSettings
{
public:
AZ_RTTI(AnnotationWorkspaceSettings, "{431EFFCF-C3C5-4BB3-8246-E452E11D4FF8}", AZ::UserSettings);
AZ_CLASS_ALLOCATOR(AnnotationWorkspaceSettings, AZ::SystemAllocator, 0);
ChannelContainer m_ActiveAnnotationChannels;
ChannelCRCContainer m_ActiveAnnotationChannelCRCs;
static void Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AnnotationWorkspaceSettings>()
->Version(2)
->Field("m_ActiveAnnotationChannels", &AnnotationWorkspaceSettings::m_ActiveAnnotationChannels)
->Field("m_ActiveAnnotationChannelCRCs", &AnnotationWorkspaceSettings::m_ActiveAnnotationChannelCRCs);
}
}
};
// stores the data that goes with the user preferences, even without a workspace file
// mainly gui stuff...
// as always, this is just a dumb container and does not need encapsulation
class AnnotationUserSettings
: public AZ::UserSettings
{
public:
AZ_RTTI(AnnotationUserSettings, "{D3584846-0574-4B63-9693-4F3265CDE16D}", AZ::UserSettings);
AZ_CLASS_ALLOCATOR(AnnotationUserSettings, AZ::SystemAllocator, 0);
ChannelContainer m_KnownAnnotationChannels; // keeps track of all annotation channels ever seen
AZStd::unordered_map<AZ::u32, AZ::u32> m_customizedColors;
AZ::u32 GetRGBAColorForChannel(AZ::u32 channelNameCRC)
{
auto found = m_customizedColors.find(channelNameCRC);
if (found != m_customizedColors.end())
{
return found->second;
}
AZ::u32 num_different_colors = 7;
QColor col;
float sat = .9f;
float val = .9f;
col.setHsvF((float)(channelNameCRC % num_different_colors) / (float(num_different_colors)), sat, val);
QRgb rgbColor = col.rgba();
return rgbColor;
}
void SetRGBAColorForChannel(AZ::u32 channelNameCRC, AZ::u32 rgbaColor)
{
m_customizedColors[channelNameCRC] = rgbaColor;
}
void ResetColorForChannel(AZ::u32 channelNameCRC)
{
m_customizedColors.erase(channelNameCRC);
}
static void Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AnnotationUserSettings>()
->Version(1)
->Field("m_KnownAnnotationChannels", &AnnotationUserSettings::m_KnownAnnotationChannels)
->Field("m_customizedColors", &AnnotationUserSettings::m_customizedColors);
}
}
};
void AnnotationsProvider::Reflect(AZ::ReflectContext* context)
{
AnnotationWorkspaceSettings::Reflect(context);
AnnotationUserSettings::Reflect(context);
}
Annotation::Annotation()
{
m_ChannelCRC = 0;
m_eventIndex = 0;
m_frameIndex = 0;
}
Annotation::Annotation(AZ::s64 eventID, FrameNumberType frame, const char* text, const char* channel)
{
m_eventIndex = eventID;
m_frameIndex = frame;
m_text = text;
m_channel = channel;
m_ChannelCRC = AZ::Crc32(m_channel.c_str());
}
Annotation::Annotation(const Annotation& other)
{
*this = other;
}
Annotation::Annotation(Annotation&& other)
{
*this = AZStd::move(other);
}
Annotation& Annotation::operator=(Annotation&& other)
{
if (this != &other)
{
m_eventIndex = other.m_eventIndex;
m_frameIndex = other.m_frameIndex;
m_text = AZStd::move(other.m_text);
m_channel = AZStd::move(other.m_channel);
m_ChannelCRC = other.m_ChannelCRC;
}
return *this;
}
Annotation& Annotation::operator=(const Annotation& other)
{
if (this != &other)
{
m_eventIndex = other.m_eventIndex;
m_frameIndex = other.m_frameIndex;
m_text = other.m_text;
m_channel = other.m_channel;
m_ChannelCRC = other.m_ChannelCRC;
}
return *this;
}
AnnotationsProvider::AnnotationsProvider(QObject* pParent)
: QObject(pParent)
{
m_bVectorDirty = false;
/// load user settings and workspace settings from usersettings component to persist state
m_ptrUserSettings = AZ::UserSettings::CreateFind<AnnotationUserSettings>(AZ_CRC("ANNOT_USERSETTINGS", 0x3ddaa4f1), AZ::UserSettings::CT_GLOBAL);
m_ptrWorkspaceSettings = AZ::UserSettings::CreateFind<AnnotationWorkspaceSettings>(AZ_CRC("ANNOT_WORKSPACESETTINGS", 0xf7ca8dd3), AZ::UserSettings::CT_GLOBAL);
}
AnnotationsProvider::~AnnotationsProvider()
{
AZ::UserSettings::Release(m_ptrUserSettings);
AZ::UserSettings::Release(m_ptrWorkspaceSettings);
}
void AnnotationsProvider::LoadSettingsFromWorkspace(WorkspaceSettingsProvider* ptrProvider)
{
AnnotationWorkspaceSettings* rawPtr = ptrProvider->FindSetting<AnnotationWorkspaceSettings>(AZ_CRC("ANNOTATIONWORKSPACE", 0x28319f66));
if (rawPtr)
{
m_ptrWorkspaceSettings->m_ActiveAnnotationChannels = rawPtr->m_ActiveAnnotationChannels;
m_ptrWorkspaceSettings->m_ActiveAnnotationChannelCRCs = rawPtr->m_ActiveAnnotationChannelCRCs;
// aggregate missing channels:
for (auto it = m_ptrWorkspaceSettings->m_ActiveAnnotationChannels.begin(); it != m_ptrWorkspaceSettings->m_ActiveAnnotationChannels.end(); ++it)
{
NotifyOfChannelExistence(it->c_str());
}
}
}
void AnnotationsProvider::SaveSettingsToWorkspace(WorkspaceSettingsProvider* ptrProvider)
{
AnnotationWorkspaceSettings* rawPtr = ptrProvider->CreateSetting<AnnotationWorkspaceSettings>(AZ_CRC("ANNOTATIONWORKSPACE", 0x28319f66));
rawPtr->m_ActiveAnnotationChannels = m_ptrWorkspaceSettings->m_ActiveAnnotationChannels;
rawPtr->m_ActiveAnnotationChannelCRCs = m_ptrWorkspaceSettings->m_ActiveAnnotationChannelCRCs;
}
// returns the end of the vector (ie, the invalid iterator)
AnnotationsProvider::ConstAnnotationIterator AnnotationsProvider::GetEnd() const
{
AZ_Assert(!m_bVectorDirty, "You may not interrogate the annotations provider before it is finalized");
return m_currentAnnotations.end();
}
// returns iterator to the first annotation thats on the frame given (or the end iterator)
AnnotationsProvider::ConstAnnotationIterator AnnotationsProvider::GetFirstAnnotationForFrame(FrameNumberType frameIndex) const
{
AZ_Assert(!m_bVectorDirty, "You may not interrogate the annotations provider before it is finalized");
// do we have annotations for that framE?
FrameIndexToCurrentMap::const_iterator it = m_frameToIndex.find(frameIndex);
if (it == m_frameToIndex.end())
{
return GetEnd();
}
return m_currentAnnotations.begin() + it->second;
}
// returns iterator to the first annotation that is foer that event # (or the end iterator)
AnnotationsProvider::ConstAnnotationIterator AnnotationsProvider::GetAnnotationForEvent(EventNumberType eventIndex) const
{
AZ_Assert(!m_bVectorDirty, "You may not interrogate the annotations provider before it is finalized");
// do we have annotations for that event index?
EventIndexToCurrentMap::const_iterator it = m_eventToIndex.find(eventIndex);
if (it == m_eventToIndex.end())
{
return GetEnd();
}
return m_currentAnnotations.begin() + it->second;
}
// note:: claims ownership of the data in annotation.
void AnnotationsProvider::AddAnnotation(Annotation&& target)
{
if (m_eventToIndex.find(target.GetEventIndex()) != m_eventToIndex.end())
{
return;
}
// can we do this quickly and easily?
if (
(!m_bVectorDirty) && // if we're not already going to need to sort, and...
(
(m_currentAnnotations.empty()) || // we either have no annotations or...
(m_currentAnnotations.back().GetEventIndex() < target.GetEventIndex()) // the fresh annotation belongs at the end anyway and we will not need to re-sort.
)
)
{
// we're adding annotations onto the end, so we will not have to sort, we can just accumulate the data.
m_eventToIndex[target.GetEventIndex()] = m_currentAnnotations.size();
// if its the first annotation for this frame, we can also add it:
if (m_frameToIndex.find(target.GetFrameIndex()) == m_frameToIndex.end())
{
m_frameToIndex[target.GetFrameIndex()] = m_currentAnnotations.size();
}
m_currentAnnotations.push_back(target);
}
else
{
// we're adding annotations out of order, we have to re-sort:
m_currentAnnotations.push_back(target);
m_bVectorDirty = true;
}
}
// called by the main controller to sort and build the map cache.
void AnnotationsProvider::Finalize()
{
/*
// temp: add some fake annots
AddAnnotation(Annotation(5, 10, "Test annotation", "tracker"));
AddAnnotation(Annotation(15, 110, "Test annotation2", "tracker1"));
AddAnnotation(Annotation(25, 210, "Test annotatio3n", "tracker2"));
AddAnnotation(Annotation(35, 310, "Test annotation4", "tracker3"));
*/
if (!m_bVectorDirty)
{
emit AnnotationDataInvalidated();
return;
}
m_frameToIndex.clear();
m_eventToIndex.clear();
// sort them so they are in order from beginning to end:
AZStd::sort(
m_currentAnnotations.begin(),
m_currentAnnotations.end(),
[](const Annotation& a, const Annotation& b) -> bool
{
return a.GetEventIndex() < b.GetEventIndex();
}
);
// now build the lookup tables:
FrameNumberType lastFrameIndex = -1;
for (AZStd::size_t idx = 0, endIdx = m_currentAnnotations.size(); idx < endIdx; ++idx)
{
const Annotation& current = m_currentAnnotations[idx];
m_eventToIndex[current.GetEventIndex()] = idx;
if (lastFrameIndex != current.GetFrameIndex())
{
m_frameToIndex[current.GetFrameIndex()] = idx;
lastFrameIndex = current.GetFrameIndex();
}
}
emit AnnotationDataInvalidated();
m_bVectorDirty = false;
}
const ChannelContainer& AnnotationsProvider::GetAllKnownChannels() const
{
return m_ptrUserSettings->m_KnownAnnotationChannels;
}
void AnnotationsProvider::GetCurrentlyEnabledChannelCRCs(ChannelCRCContainer& target) const
{
target.insert(m_ptrWorkspaceSettings->m_ActiveAnnotationChannelCRCs.begin(), m_ptrWorkspaceSettings->m_ActiveAnnotationChannelCRCs.end());
}
// let us know that a channel exists:
void AnnotationsProvider::NotifyOfChannelExistence(const char* name)
{
if (m_ptrUserSettings->m_KnownAnnotationChannels.insert(name).second)
{
emit KnownAnnotationsChanged();
}
}
void AnnotationsProvider::SetChannelEnabled(const char* channelName, bool enabled)
{
if (enabled)
{
if (m_ptrWorkspaceSettings->m_ActiveAnnotationChannels.insert(channelName).second)
{
m_ptrWorkspaceSettings->m_ActiveAnnotationChannelCRCs.insert(AZ::Crc32(channelName));
NotifyOfChannelExistence(channelName);
emit SelectedAnnotationsChanged();
}
}
else
{
AZ::u32 channelCRC = AZ::Crc32(channelName);
if (IsChannelEnabled(channelCRC))
{
m_ptrWorkspaceSettings->m_ActiveAnnotationChannels.erase(channelName);
m_ptrWorkspaceSettings->m_ActiveAnnotationChannelCRCs.erase(channelCRC);
emit SelectedAnnotationsChanged();
}
}
}
QColor AnnotationsProvider::GetColorForChannel(AZ::u32 channelNameCRC) const
{
QRgb rgbaValue = m_ptrUserSettings->GetRGBAColorForChannel(channelNameCRC);
return QColor(rgbaValue);
}
void AnnotationsProvider::SetColorForChannel(AZ::u32 channelNameCRC, QColor newColor)
{
QRgb rgbaValue = newColor.rgba();
m_ptrUserSettings->SetRGBAColorForChannel(channelNameCRC, rgbaValue);
if (IsChannelEnabled(channelNameCRC))
{
// update displays
emit SelectedAnnotationsChanged();
}
}
void AnnotationsProvider::ResetColorForChannel(AZ::u32 channelNameCRC)
{
m_ptrUserSettings->ResetColorForChannel(channelNameCRC);
}
bool AnnotationsProvider::IsChannelEnabled(AZ::u32 channelNameCRC) const
{
return (m_ptrWorkspaceSettings->m_ActiveAnnotationChannelCRCs.find(channelNameCRC) != m_ptrWorkspaceSettings->m_ActiveAnnotationChannelCRCs.end());
}
void AnnotationsProvider::Clear()
{
m_frameToIndex.clear();
m_eventToIndex.clear();
m_currentAnnotations.clear();
m_bVectorDirty = false;
}
}
#include <Source/Driller/Annotations/moc_Annotations.cpp>
@@ -1,135 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef DRILLER_ANNOTATIONS_H
#define DRILLER_ANNOTATIONS_H
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <AzCore/Math/Crc.h>
#include <QObject>
#include <Source/Driller/DrillerDataTypes.h>
#endif
#pragma once
namespace AZ { class ReflectContext; }
namespace Driller
{
// ------------------------------------------------------------------------------------------------------------
// Annotation
// represents one annotation returned or fed into the annotations interface.
class Annotation
{
public:
AZ_CLASS_ALLOCATOR(Annotation, AZ::SystemAllocator, 0);
Annotation();
Annotation(AZ::s64 eventID, FrameNumberType frame, const char* text, const char* channel);
Annotation(const Annotation& other);
Annotation(Annotation&& other);
Annotation& operator=(Annotation&& other);
Annotation& operator=(const Annotation& other);
AZ::s64 GetEventIndex() const { return m_eventIndex; }
FrameNumberType GetFrameIndex() const { return m_frameIndex; }
const AZStd::string& GetText() const { return m_text; }
const AZStd::string& GetChannel() const { return m_channel; }
const AZ::u32 GetChannelCRC() const { return m_ChannelCRC; }
private:
AZ::s64 m_eventIndex;
FrameNumberType m_frameIndex;
AZStd::string m_text;
AZStd::string m_channel;
AZ::u32 m_ChannelCRC;
};
// ------------------------------------------------------------------------------------------------------------
// AnnotationsProviderInterface
// a class which provides annotation information to the parts of the system that care about annotations.
// other parts of the system that want to know what annotations occur where will be given a pointer to this guy
// and they will ask him what they need to know.
// PLEASE NOTE: This is essentially a live cache of what's currently in the view range and is for rendering only.
// its essentially destroyed and recreated every frame.
class AnnotationWorkspaceSettings;
class AnnotationUserSettings;
class WorkspaceSettingsProvider;
// contains a set of channel names
typedef AZStd::unordered_set<AZStd::string> ChannelContainer;
typedef AZStd::unordered_set<AZ::u32> ChannelCRCContainer;
class AnnotationsProvider
: public QObject
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(AnnotationsProvider, AZ::SystemAllocator, 0);
typedef AZStd::vector<Annotation> AnnotationContainer;
typedef AnnotationContainer::const_iterator ConstAnnotationIterator;
// graph renderers need to know what color to draw annotations, and what annotations exist given a particular event or frame:
ConstAnnotationIterator GetEnd() const; // returns the end of the vector (ie, the invalid iterator)
ConstAnnotationIterator GetFirstAnnotationForFrame(FrameNumberType frameIndex) const; // returns iterator to the first annotation thats on the frame given (or the end iterator)
ConstAnnotationIterator GetAnnotationForEvent(EventNumberType eventIndex) const; // returns iterator to the first annotation that is for that event # (or the end iterator)
// claims ownership of the given annotation, via r-value ref. This is used during populate.
void AddAnnotation(Annotation&& target);
// configure a channel
void ConfigureChannel(const char* channel, QColor color);
AnnotationsProvider(QObject* pParent = NULL);
~AnnotationsProvider();
// called by the main controller to sort and build the map cache.
void Finalize();
void Clear();
static void Reflect(AZ::ReflectContext* context);
// --- channel management ---
const ChannelContainer& GetAllKnownChannels() const;
void GetCurrentlyEnabledChannelCRCs(ChannelCRCContainer& target) const;
void NotifyOfChannelExistence(const char* name);
void SetChannelEnabled(const char* channelName, bool enabled);
bool IsChannelEnabled(AZ::u32 channelNameCRC) const;
QColor GetColorForChannel(AZ::u32 channelNameCRC) const; // each channel has a color, this is configured externally.
void SetColorForChannel(AZ::u32 channelNameCRC, QColor newColor); // each channel has a color, this is configured externally.
void ResetColorForChannel(AZ::u32 channelNameCRC); // each channel has a color, this is configured externally.
void LoadSettingsFromWorkspace(WorkspaceSettingsProvider* ptrProvider);
void SaveSettingsToWorkspace(WorkspaceSettingsProvider* ptrProvider);
signals:
void KnownAnnotationsChanged();
void SelectedAnnotationsChanged();
void AnnotationDataInvalidated();
protected:
AnnotationContainer m_currentAnnotations;
typedef AZStd::unordered_map<EventNumberType, AZStd::size_t> EventIndexToCurrentMap; // maps from event index to index in our vector.
typedef AZStd::unordered_map<FrameNumberType, AZStd::size_t> FrameIndexToCurrentMap; // maps from frame index to index in our vector.
EventIndexToCurrentMap m_eventToIndex;
FrameIndexToCurrentMap m_frameToIndex;
// housekeeping
bool m_bVectorDirty;
AZStd::intrusive_ptr<AnnotationWorkspaceSettings> m_ptrWorkspaceSettings; // loaded from workspace and user settings. // crc(somethingelse)
AZStd::intrusive_ptr<AnnotationUserSettings> m_ptrUserSettings; // loaded only from user settings / CRC(whatever)
};
}
#endif//DRILLER_ANNOTATIONS_H
@@ -1,211 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "AnnotationsDataView.hxx"
#include "AnnotationHeaderView.hxx"
#include "Annotations.hxx"
#include <QPen>
#include <QPainter>
#include <QPainterPath>
#include <QMouseEvent>
namespace Driller
{
static const float adv_arrow_width = 8.0f;
AnnotationsDataView::AnnotationsDataView(QWidget* parent)
: QWidget(parent)
, m_ptrHeaderView(nullptr)
, m_ptrAnnotations(nullptr)
{
setAttribute(Qt::WA_OpaquePaintEvent, true);
setMouseTracking(true);
}
AnnotationsDataView::~AnnotationsDataView()
{
}
void AnnotationsDataView::RegisterAnnotationHeaderView(AnnotationHeaderView* header, AnnotationsProvider* annotations)
{
m_ptrHeaderView = header;
m_ptrAnnotations = annotations;
}
int AnnotationsDataView::PositionToFrame(const QPoint& pt)
{
QRect wrect = rect();
int frame = m_ptrHeaderView->GetState().m_FrameOffset + m_ptrHeaderView->GetState().m_FramesInView - 1;
frame = frame <= m_ptrHeaderView->GetState().m_EndFrame ? frame : m_ptrHeaderView->GetState().m_EndFrame;
int rOffset = wrect.width() - pt.x();
int rCell = (int)((float)rOffset / GetBarWidth());
int retFrame = frame - rCell;
//AZ_TracePrintf("Driller","Click Frame Raw Input = %d\n", retFrame);
return retFrame;
}
float AnnotationsDataView::GetBarWidth()
{
return ((float)(rect().width()) / (float)(m_ptrHeaderView->GetState().m_FramesInView));
}
void AnnotationsDataView::paintEvent(QPaintEvent* event)
{
(void)event;
m_ClickableAreas.clear();
QPen pen;
pen.setWidth(1);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
pen.setBrush(brush);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::TextAntialiasing, true);
painter.setPen(pen);
painter.fillRect(rect(), Qt::black);
int frame = m_ptrHeaderView->GetState().m_FrameOffset + m_ptrHeaderView->GetState().m_FramesInView - 1;
frame = frame <= m_ptrHeaderView->GetState().m_EndFrame ? frame : m_ptrHeaderView->GetState().m_EndFrame;
QRect wrect = rect();
float barWidth = GetBarWidth();
int barWidthHalf = (int)(barWidth / 2.0f);
QPen fatPen(QColor(255, 255, 255, 255));
fatPen.setWidth(2);
fatPen.setCapStyle(Qt::FlatCap);
if (m_ptrHeaderView->GetState().m_EndFrame)
{
float rightEdgeOfBar = (float)wrect.right();
float leftEdgeOfBar = rightEdgeOfBar - barWidth;
while (frame >= 0 && rightEdgeOfBar >= wrect.left())
{
int actualLeftEdge = (int)floorf(leftEdgeOfBar);
float center = (float)(actualLeftEdge + barWidthHalf) + 0.5f;
// annotations?
AnnotationsProvider::ConstAnnotationIterator it = m_ptrAnnotations->GetFirstAnnotationForFrame(frame);
AnnotationsProvider::ConstAnnotationIterator endIt = m_ptrAnnotations->GetEnd();
while ((it != endIt) && (it->GetFrameIndex() == frame))
{
QPainterPath newPath;
QPolygonF newPolygon;
newPolygon << QPointF(center - adv_arrow_width, 1.0f) << QPointF(center, wrect.height() - 1.0f) << QPointF(center + adv_arrow_width, 1.0f);
newPath.addPolygon(newPolygon);
newPath.closeSubpath();
if (m_eventsToHighlight.find(it->GetEventIndex()) != m_eventsToHighlight.end())
{
painter.setPen(fatPen);
painter.setBrush(m_ptrAnnotations->GetColorForChannel(it->GetChannelCRC()));
}
else
{
painter.setPen(QColor(0, 0, 0, 0));
painter.setBrush(m_ptrAnnotations->GetColorForChannel(it->GetChannelCRC()));
}
painter.drawPath(newPath);
m_ClickableAreas[it->GetEventIndex()] = newPath;
++it;
}
--frame;
rightEdgeOfBar -= barWidth;
leftEdgeOfBar -= barWidth;
}
}
}
void AnnotationsDataView::mouseMoveEvent(QMouseEvent* event)
{
AZStd::unordered_set<AZ::s64> newEventsToHighlight;
for (auto it = m_ClickableAreas.begin(); it != m_ClickableAreas.end(); ++it)
{
if (it->second.contains(event->pos()))
{
auto annot = m_ptrAnnotations->GetAnnotationForEvent(it->first);
if (annot != m_ptrAnnotations->GetEnd())
{
newEventsToHighlight.insert(annot->GetEventIndex());
emit InformOfMouseOverAnnotation(*annot);
}
}
}
bool doUpdate = false;
// did our highlight change?
for (auto it = newEventsToHighlight.begin(); it != newEventsToHighlight.end(); ++it)
{
if (m_eventsToHighlight.find(*it) == m_eventsToHighlight.end())
{
doUpdate = true;
break;
}
}
// did our highlight change?
if (!doUpdate)
{
for (auto it = m_eventsToHighlight.begin(); it != m_eventsToHighlight.end(); ++it)
{
if (newEventsToHighlight.find(*it) == newEventsToHighlight.end())
{
doUpdate = true;
break;
}
}
}
if (doUpdate)
{
newEventsToHighlight.swap(m_eventsToHighlight);
update();
}
// find the first annotation within a margin:
event->ignore();
}
void AnnotationsDataView::mousePressEvent(QMouseEvent* event)
{
for (auto it = m_ClickableAreas.begin(); it != m_ClickableAreas.end(); ++it)
{
if (it->second.contains(event->pos()))
{
auto annot = m_ptrAnnotations->GetAnnotationForEvent(it->first);
if (annot != m_ptrAnnotations->GetEnd())
{
emit InformOfClickAnnotation(*annot);
}
}
}
event->ignore();
}
void AnnotationsDataView::mouseReleaseEvent(QMouseEvent* event)
{
event->ignore();
}
}
#include <Source/Driller/Annotations/moc_AnnotationsDataView.cpp>
@@ -1,61 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef ANNOTATIONS_DATA_VIEW
#define ANNOTATIONS_DATA_VIEW
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
#include <QWidget>
#endif
namespace Driller
{
class AnnotationsProvider;
class Annotation;
class AnnotationHeaderView;
/** Annotations Data View just shows the annotations that are available in a horizontal strip with indicators for easy clickability.
*/
class AnnotationsDataView : public QWidget
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(AnnotationsDataView, AZ::SystemAllocator, 0);
AnnotationsDataView(QWidget* parent = nullptr);
virtual ~AnnotationsDataView();
void RegisterAnnotationHeaderView(AnnotationHeaderView* header, AnnotationsProvider* annotations);
int PositionToFrame( const QPoint &pt );
float GetBarWidth();
virtual void paintEvent( QPaintEvent *event );
virtual void mouseMoveEvent( QMouseEvent *event );
virtual void mousePressEvent( QMouseEvent *event );
virtual void mouseReleaseEvent( QMouseEvent *event );
signals:
void InformOfMouseOverAnnotation(const Annotation& annotation);
void InformOfClickAnnotation(const Annotation& annotation);
private:
typedef AZStd::unordered_map<AZ::s64, QPainterPath> EventIndexToClickablePath;
EventIndexToClickablePath m_ClickableAreas;
AZStd::unordered_set<AZ::s64> m_eventsToHighlight;
AnnotationsProvider *m_ptrAnnotations;
AnnotationHeaderView *m_ptrHeaderView;
};
}
#endif // ANNOTATIONS_DATA_VIEW
@@ -1,250 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "AnnotationsDataView_Events.hxx"
#include "AnnotationsHeaderView_Events.hxx"
#include "Annotations.hxx"
#include <Source/Driller/Axis.hxx>
#include <QPainter>
#include <QPainterPath>
#include <QPen>
#include <QMouseEvent>
namespace Driller
{
static const float adv_events_arrow_width = 8.0f;
AnnotationsDataView_Events::AnnotationsDataView_Events(AnnotationHeaderView_Events* header, AnnotationsProvider* annotations)
: QWidget(header)
, m_ptrHeaderView(header)
, m_ptrAnnotations(annotations)
, m_ptrAxis(NULL)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setFixedHeight(18);
setAutoFillBackground(false);
setAttribute(Qt::WA_OpaquePaintEvent, true);
setMouseTracking(true);
m_CurrentFrameNumber = 0;
}
void AnnotationsDataView_Events::AttachToAxis(Charts::Axis* pAxis)
{
if (m_ptrAxis)
{
disconnect(m_ptrAxis, SIGNAL(destroyed(QObject*)), this, SLOT(OnAxisDestroyed()));
disconnect(m_ptrAxis, SIGNAL(Invalidated()), this, SLOT(OnAxisInvalidated()));
}
m_ptrAxis = pAxis;
if (pAxis)
{
connect(m_ptrAxis, SIGNAL(destroyed(QObject*)), this, SLOT(OnAxisDestroyed()));
connect(m_ptrAxis, SIGNAL(Invalidated()), this, SLOT(OnAxisInvalidated()));
}
}
void AnnotationsDataView_Events::OnAxisDestroyed()
{
m_ptrAxis = NULL;
update();
}
void AnnotationsDataView_Events::OnAxisInvalidated()
{
update();
}
AnnotationsDataView_Events::~AnnotationsDataView_Events()
{
}
void AnnotationsDataView_Events::paintEvent(QPaintEvent* event)
{
(void)event;
m_ClickableAreas.clear();
// scan for annotations
// fill with black
QPainter painter(this);
painter.fillRect(rect(), Qt::black);
if (!m_ptrAxis)
{
return;
}
if (!m_ptrAxis->GetValid())
{
return;
}
QRectF drawRange = rect();
// adjust for inset:
drawRange.adjust(2.0f, 0.0f, -4.0f, 0.0f);
float leftEdge = (float)drawRange.left();
float drawRangeWidth = (float)drawRange.width();
AZ::s64 eventIndexStart = (AZ::s64)m_ptrAxis->GetWindowMin();
AZ::s64 eventIndexEnd = (AZ::s64)m_ptrAxis->GetWindowMax() + 1;
float eventIndexRange = ((float)m_ptrAxis->GetWindowMax() - (float)m_ptrAxis->GetWindowMin()); // this is the domain range
if (eventIndexRange <= 0.0f)
{
return;
}
float oneEventWidthInPixels = drawRangeWidth / eventIndexRange;
float halfEventWidth = oneEventWidthInPixels * 0.5f;
// find the first event within that range:
QPen fatPen(QColor(255, 255, 255, 255));
fatPen.setWidth(2);
fatPen.setCapStyle(Qt::FlatCap);
AnnotationsProvider::ConstAnnotationIterator it = m_ptrAnnotations->GetFirstAnnotationForFrame(m_CurrentFrameNumber);
AnnotationsProvider::ConstAnnotationIterator endIt = m_ptrAnnotations->GetEnd();
// now keep going until we hit the end of the range:
while (it != endIt)
{
if (it->GetEventIndex() >= eventIndexEnd)
{
break;
}
if (it->GetEventIndex() < eventIndexStart)
{
++it;
continue; // we're within the zoom
}
// transform that event ID into the window domain:
float eventRatio = ((float)it->GetEventIndex() - m_ptrAxis->GetWindowMin()) / eventIndexRange;
float center = floorf(leftEdge + (drawRangeWidth * eventRatio));
center += (float)drawRange.left();
center += halfEventWidth;
QPainterPath newPath;
QPolygonF newPolygon;
newPolygon << QPointF(center - adv_events_arrow_width, 1.0f) << QPointF(center, drawRange.height() - 1.0f) << QPointF(center + adv_events_arrow_width, 1.0f);
newPath.addPolygon(newPolygon);
newPath.closeSubpath();
if (m_eventsToHighlight.find(it->GetEventIndex()) != m_eventsToHighlight.end())
{
painter.setPen(fatPen);
painter.setBrush(m_ptrAnnotations->GetColorForChannel(it->GetChannelCRC()));
}
else
{
painter.setPen(QColor(0, 0, 0, 0));
painter.setBrush(m_ptrAnnotations->GetColorForChannel(it->GetChannelCRC()));
}
painter.drawPath(newPath);
m_ClickableAreas[it->GetEventIndex()] = newPath;
++it;
}
}
void AnnotationsDataView_Events::mouseMoveEvent(QMouseEvent* event)
{
AZStd::unordered_set<AZ::s64> newEventsToHighlight;
for (auto it = m_ClickableAreas.begin(); it != m_ClickableAreas.end(); ++it)
{
if (it->second.contains(event->pos()))
{
auto annot = m_ptrAnnotations->GetAnnotationForEvent(it->first);
if (annot != m_ptrAnnotations->GetEnd())
{
newEventsToHighlight.insert(annot->GetEventIndex());
emit InformOfMouseOverAnnotation(*annot);
}
}
}
bool doUpdate = false;
// did our highlight change?
for (auto it = newEventsToHighlight.begin(); it != newEventsToHighlight.end(); ++it)
{
if (m_eventsToHighlight.find(*it) == m_eventsToHighlight.end())
{
doUpdate = true;
break;
}
}
// did our highlight change?
if (!doUpdate)
{
for (auto it = m_eventsToHighlight.begin(); it != m_eventsToHighlight.end(); ++it)
{
if (newEventsToHighlight.find(*it) == newEventsToHighlight.end())
{
doUpdate = true;
break;
}
}
}
if (doUpdate)
{
newEventsToHighlight.swap(m_eventsToHighlight);
update();
}
// find the first annotation within a margin:
event->ignore();
}
void AnnotationsDataView_Events::mousePressEvent(QMouseEvent* event)
{
for (auto it = m_ClickableAreas.begin(); it != m_ClickableAreas.end(); ++it)
{
if (it->second.contains(event->pos()))
{
auto annot = m_ptrAnnotations->GetAnnotationForEvent(it->first);
if (annot != m_ptrAnnotations->GetEnd())
{
emit InformOfClickAnnotation(*annot);
}
}
}
event->ignore();
}
void AnnotationsDataView_Events::mouseReleaseEvent(QMouseEvent* event)
{
event->ignore();
}
void AnnotationsDataView_Events::OnScrubberFrameUpdate(FrameNumberType newFramenumber)
{
if (newFramenumber != m_CurrentFrameNumber)
{
m_CurrentFrameNumber = newFramenumber;
// we don't update here because we wait for the new range to be set
//update();
}
}
}
#include <Source/Driller/Annotations/moc_AnnotationsDataView_Events.cpp>
@@ -1,77 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef ANNOTATIONS_DATA_VIEW_EVENTS_HXX
#define ANNOTATIONS_DATA_VIEW_EVENTS_HXX
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
#include <QtWidgets/QWidget>
#include "Source/Driller/DrillerDataTypes.h"
#endif
namespace Charts
{
class Axis;
}
namespace Driller
{
class AnnotationsProvider;
class Annotation;
class AnnotationHeaderView_Events;
/** Annotations Data View just shows the annotations that are available in a horizontal strip with indicators for easy clickability.
* This flavor of the view is supposed to operate on individual events instead of individual frames and is supposed to sit above the event driller track
* But it can actually work on any track thats willing to provide it with an axis.
*/
class AnnotationsDataView_Events : public QWidget
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(AnnotationsDataView_Events, AZ::SystemAllocator, 0);
AnnotationsDataView_Events( AnnotationHeaderView_Events* header, AnnotationsProvider *annotations );
virtual ~AnnotationsDataView_Events();
void AttachToAxis(Charts::Axis *pAxis);
virtual void paintEvent( QPaintEvent *event );
virtual void mouseMoveEvent( QMouseEvent *event );
virtual void mousePressEvent( QMouseEvent *event );
virtual void mouseReleaseEvent( QMouseEvent *event );
signals:
void InformOfMouseOverAnnotation(const Annotation& annotation);
void InformOfClickAnnotation(const Annotation& annotation);
private:
typedef AZStd::unordered_map<AZ::s64, QPainterPath> EventIndexToClickablePath;
EventIndexToClickablePath m_ClickableAreas;
AZStd::unordered_set<AZ::s64> m_eventsToHighlight;
QPainter *m_Painter;
Charts::Axis *m_ptrAxis;
AnnotationsProvider *m_ptrAnnotations;
AnnotationHeaderView_Events *m_ptrHeaderView;
FrameNumberType m_CurrentFrameNumber;
const Annotation* GetNearestAnnotationToMousePoint(QPoint pos) const;
public slots:
void OnAxisInvalidated();
void OnAxisDestroyed();
void OnScrubberFrameUpdate(FrameNumberType newFrameNumber);
};
}
#endif // ANNOTATIONS_DATA_VIEW
@@ -1,74 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "AnnotationsHeaderView_Events.hxx"
#include "AnnotationsDataView_Events.hxx"
#include "Annotations.hxx"
#include <QtWidgets/QGridLayout>
namespace Driller
{
static const int k_eventContractedSize = 18;
AnnotationHeaderView_Events::AnnotationHeaderView_Events(QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
, m_ptrAnnotations(NULL)
{
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
this->setFixedHeight(k_eventContractedSize);
this->setAutoFillBackground(true);
QHBoxLayout* mainLayout = new QHBoxLayout(this);
this->setLayout(mainLayout);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
}
void AnnotationHeaderView_Events::OnScrubberFrameUpdate(FrameNumberType newFrame)
{
if (m_ptrDataView)
{
m_ptrDataView->OnScrubberFrameUpdate(newFrame);
}
}
QSize AnnotationHeaderView_Events::sizeHint() const
{
return QSize(0, k_eventContractedSize);
}
void AnnotationHeaderView_Events::ControllerSizeChanged(QSize newSize)
{
(void)newSize;
}
AnnotationHeaderView_Events::~AnnotationHeaderView_Events()
{
}
void AnnotationHeaderView_Events::AttachToAxis(AnnotationsProvider* ptrAnnotations, Charts::Axis* target)
{
m_ptrAnnotations = ptrAnnotations;
m_ptrDataView = aznew AnnotationsDataView_Events(this, ptrAnnotations);
connect(m_ptrDataView, SIGNAL(InformOfMouseOverAnnotation(const Annotation&)), this, SIGNAL(InformOfMouseOverAnnotation(const Annotation&)));
connect(m_ptrDataView, SIGNAL(InformOfClickAnnotation(const Annotation&)), this, SIGNAL(InformOfClickAnnotation(const Annotation&)));
connect(m_ptrAnnotations, SIGNAL(AnnotationDataInvalidated()), this, SLOT(RefreshView()));
layout()->addWidget(m_ptrDataView);
m_ptrDataView->AttachToAxis(target);
}
void AnnotationHeaderView_Events::RefreshView()
{
m_ptrDataView->update();
}
}
#include <Source/Driller/Annotations/moc_AnnotationsHeaderView_Events.cpp>
@@ -1,65 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef ANNOTATION_HEADER_VIEW_EVENTS_HXX
#define ANNOTATION_HEADER_VIEW_EVENTS_HXX
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <QtWidgets/QWidget>
#include <Source/Driller/DrillerNetworkMessages.h>
#include "Source/Driller/DrillerDataTypes.h"
#endif
namespace Charts
{
class Axis;
}
namespace Driller
{
class AnnotationsProvider;
class AnnotationsDataView_Events;
class Annotation;
/** This version of the annotations header view sits above the per-frame events widget (near the bottom of hte main view).
* Its job is to show annotations that happen within a single frame (on an event-by-event basis!)
* It can actually work on any track thats willing to provide it with an axis.
*/
class AnnotationHeaderView_Events : public QWidget
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(AnnotationHeaderView_Events,AZ::SystemAllocator,0);
AnnotationHeaderView_Events(QWidget* parent = NULL, Qt::WindowFlags flags = Qt::WindowFlags());
virtual ~AnnotationHeaderView_Events(void);
void AttachToAxis(AnnotationsProvider* ptrAnnotations, Charts::Axis *target);
signals:
void InformOfMouseOverAnnotation(const Annotation& annotation);
void InformOfClickAnnotation(const Annotation& annotation);
public slots:
void RefreshView();
void ControllerSizeChanged(QSize newSize);
void OnScrubberFrameUpdate(FrameNumberType newFrame);
private:
AnnotationsProvider *m_ptrAnnotations;
AnnotationsDataView_Events *m_ptrDataView;
virtual QSize sizeHint() const;
};
}
#endif // ANNOTATION_HEADER_VIEW_HXX
@@ -1,151 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>configureAnnotationsDialog</class>
<widget class="QDialog" name="configureAnnotationsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>446</width>
<height>168</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Annotations</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Display Annotations From the Selected Features</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="searchField">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="placeholderText">
<string>Search Annotations</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTableView" name="statusTable">
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>60</number>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>30</number>
</attribute>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,248 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <QtCore/QAbstractTableModel>
#include "ConfigureAnnotationsWindow.hxx"
#include <Source/Driller/Annotations/ui_ConfigureAnnotationsDialog.h>
#include <Source/Driller/Annotations/Annotations.hxx>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
#include <AzToolsFramework/UI/UICore/ColorPickerDelegate.hxx>
#include <QPainter>
#include <QSortFilterProxyModel>
#include <QCloseEvent>
namespace Driller
{
//////////////////////////////
// ConfigureAnnotationsModel
//////////////////////////////
int ConfigureAnnotationsModel::rowCount(const QModelIndex& index) const
{
if (index == QModelIndex())
{
return (int)m_cache.size();
}
return 0;
}
int ConfigureAnnotationsModel::columnCount(const QModelIndex& index) const
{
(void)index;
return 1;
}
Qt::ItemFlags ConfigureAnnotationsModel::flags(const QModelIndex& index) const
{
if (index == QModelIndex())
{
return Qt::ItemFlags();
}
if (index.column() == 0)
{
return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
bool ConfigureAnnotationsModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (role == Qt::CheckStateRole)
{
Qt::CheckState newState = (Qt::CheckState)value.toInt();
if (index.column() == 0)
{
if (index.row() < (int)m_cache.size())
{
Qt::CheckState oldEnabled = m_ptrProvider->IsChannelEnabled(AZ::Crc32(m_cache[index.row()].toUtf8().data())) ? Qt::Checked : Qt::Unchecked;
if (newState != oldEnabled)
{
m_ptrProvider->SetChannelEnabled(m_cache[index.row()].toUtf8().data(), newState == Qt::Checked ? true : false);
return true;
}
}
}
}
else if (role == AzToolsFramework::ColorPickerDelegate::COLOR_PICKER_ROLE)
{
QColor newColor = qvariant_cast<QColor>(value);
AZ::u32 crcOfChannel = AZ::Crc32(m_cache[index.row()].toUtf8().data());
m_ptrProvider->SetColorForChannel(crcOfChannel, newColor);
m_cachedColorIcons[index.row()] = CreatePixmapForColor(newColor);
}
return false;
}
QVariant ConfigureAnnotationsModel::data(const QModelIndex& index, int role) const
{
if (index == QModelIndex())
{
return QVariant();
}
if (index.column() == 0)
{
switch (role)
{
case Qt::CheckStateRole:
{
AZ::u32 crcvalue = AZ::Crc32(m_cache[index.row()].toUtf8().data());
return QVariant(m_ptrProvider->IsChannelEnabled(crcvalue) ? QVariant(Qt::Checked) : QVariant(Qt::Unchecked));
}
case Qt::DecorationRole:
{
return m_cachedColorIcons[index.row()];
}
case Qt::DisplayRole:
{
return m_cache[index.row()];
}
case AzToolsFramework::ColorPickerDelegate::COLOR_PICKER_ROLE:
{
AZ::u32 crcvalue = AZ::Crc32(m_cache[index.row()].toUtf8().data());
QColor channelColor = m_ptrProvider->GetColorForChannel(crcvalue);
return QVariant(channelColor);
}
}
}
return QVariant();
}
QVariant ConfigureAnnotationsModel::headerData (int section, Qt::Orientation orientation, int role) const
{
(void)orientation;
if (role == Qt::DisplayRole)
{
switch (section)
{
case 0:
return QVariant(tr("Annotation Type"));
break;
}
return QVariant();
}
else if (role == Qt::TextAlignmentRole)
{
if (section == 0)
{
return QVariant(Qt::AlignVCenter | Qt::AlignLeft);
}
}
return QVariant();
}
QPixmap ConfigureAnnotationsModel::CreatePixmapForColor(QColor color)
{
QPixmap pixmap(16, 16);
{
QPainter painter(&pixmap);
painter.fillRect(0, 0, 16, 16, Qt::black);
painter.fillRect(1, 1, 15, 15, color);
}
return pixmap;
}
void ConfigureAnnotationsModel::Recache()
{
beginResetModel();
m_cache.clear();
m_cachedColorIcons.clear();
AZStd::for_each(m_ptrProvider->GetAllKnownChannels().begin(), m_ptrProvider->GetAllKnownChannels().end(),
[this](const AZStd::string& value)
{
m_cache.push_back(QString::fromUtf8(value.c_str()));
QColor channelColor = m_ptrProvider->GetColorForChannel(AZ::Crc32(value.c_str()));
m_cachedColorIcons.push_back(CreatePixmapForColor(channelColor));
});
endResetModel();
}
ConfigureAnnotationsModel::ConfigureAnnotationsModel(AnnotationsProvider* ptrProvider, QObject* pParent)
: QAbstractTableModel(pParent)
{
m_ptrProvider = ptrProvider;
connect(ptrProvider, &AnnotationsProvider::KnownAnnotationsChanged, this, &ConfigureAnnotationsModel::Recache);
Recache();
}
ConfigureAnnotationsModel::~ConfigureAnnotationsModel()
{
}
///////////////////////////////
// ConfigureAnnotationsWindow
///////////////////////////////
ConfigureAnnotationsWindow::ConfigureAnnotationsWindow(QWidget* pParent /* = NULL */)
: QDialog(pParent)
, m_proxyModel(nullptr)
{
m_ptrLoadedUI = azcreate(Ui::configureAnnotationsDialog, ());
m_ptrLoadedUI->setupUi(this);
}
ConfigureAnnotationsWindow::~ConfigureAnnotationsWindow()
{
AZStd::intrusive_ptr<AzToolsFramework::QWidgetSavedState> pState = AZ::UserSettings::CreateFind<AzToolsFramework::QWidgetSavedState>(AZ_CRC("CONFIGURE ANNOTATIONS WINDOW", 0x581c6568), AZ::UserSettings::CT_GLOBAL);
if (pState)
{
pState->CaptureGeometry(this);
}
azdestroy(m_ptrLoadedUI);
}
void ConfigureAnnotationsWindow::Initialize(AnnotationsProvider* ptrProvider)
{
m_ptrProvider = ptrProvider;
m_ptrModel = aznew ConfigureAnnotationsModel(ptrProvider, this);
m_proxyModel = new QSortFilterProxyModel(this);
m_proxyModel->setDynamicSortFilter(false);
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_proxyModel->setSourceModel(m_ptrModel);
m_ptrLoadedUI->statusTable->setSelectionBehavior(QAbstractItemView::SelectRows);
m_ptrLoadedUI->statusTable->setModel(m_proxyModel);
m_ptrLoadedUI->statusTable->setItemDelegate(aznew AzToolsFramework::ColorPickerDelegate(this));
m_ptrLoadedUI->statusTable->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
connect(m_ptrLoadedUI->searchField, SIGNAL(textChanged(const QString&)), this, SLOT(OnFilterChanged(const QString&)));
AZStd::intrusive_ptr<AzToolsFramework::QWidgetSavedState> windowState = AZ::UserSettings::Find<AzToolsFramework::QWidgetSavedState>(AZ_CRC("CONFIGURE ANNOTATIONS WINDOW", 0x581c6568), AZ::UserSettings::CT_GLOBAL);
if (windowState)
{
windowState->RestoreGeometry(this);
}
}
void ConfigureAnnotationsWindow::OnFilterChanged(const QString& filter)
{
m_proxyModel->setFilterFixedString(filter);
}
void ConfigureAnnotationsWindow::closeEvent (QCloseEvent* e)
{
e->accept();
deleteLater();
}
}
#include <Source/Driller/Annotations/moc_ConfigureAnnotationsWindow.cpp>
@@ -1,91 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CONFIGURE_ANNOTATIONS_WINDOW_H
#define CONFIGURE_ANNOTATIONS_WINDOW_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include <QAbstractTableModel>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#endif
class QSortFilterProxyModel;
namespace Ui
{
class configureAnnotationsDialog;
}
namespace Driller
{
class AnnotationsProvider;
class ConfigureAnnotationsModel;
class ConfigureAnnotationsWindow : public QDialog
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ConfigureAnnotationsWindow, AZ::SystemAllocator, 0);
ConfigureAnnotationsWindow(QWidget *pParent = NULL);
virtual ~ConfigureAnnotationsWindow();
void Initialize(AnnotationsProvider* ptrProvider);
public slots:
void OnFilterChanged(const QString&);
protected:
Ui::configureAnnotationsDialog* m_ptrLoadedUI;
QSortFilterProxyModel* m_proxyModel;
ConfigureAnnotationsModel* m_ptrModel;
AnnotationsProvider* m_ptrProvider;
virtual void closeEvent ( QCloseEvent * e );
};
class ConfigureAnnotationsModel : public QAbstractTableModel
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(ConfigureAnnotationsModel, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////////
// QAbstractTableModel
int rowCount(const QModelIndex& index = QModelIndex()) const override;
int columnCount(const QModelIndex& index = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex& index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QVariant headerData ( int section, Qt::Orientation orientation, int role ) const override;
////////////////////////////////////////////////////////////////////////////////////////////////
ConfigureAnnotationsModel(AnnotationsProvider* ptrProvider, QObject *pParent = NULL);
virtual ~ConfigureAnnotationsModel();
private:
AnnotationsProvider* m_ptrProvider;
AZStd::vector<QString> m_cache;
AZStd::vector<QPixmap> m_cachedColorIcons;
QPixmap CreatePixmapForColor(QColor color);
private slots:
void Recache();
};
}
#endif //CONFIGURE_ANNOTATIONS_WINDOW_H
@@ -1,636 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Math/MathUtils.h>
#include <Source/Driller/Axis.hxx>
#include <Source/Driller/AreaChart.hxx>
#include <Source/Driller/moc_AreaChart.cpp>
#include <QPainter>
#include <QPen>
#include <QMouseEvent>
namespace AreaChart
{
///////////////
// LineSeries
///////////////
LineSeries::LineSeries(AreaChart* owner, size_t seriesId, const QString& name, const QColor& color, size_t seriesSize)
: m_owner(owner)
, m_seriesId(seriesId)
, m_name(name)
, m_color(color)
, m_highlighted(false)
, m_enabled(true)
, m_hasData(false)
{
if (seriesSize > 0)
{
m_linePoints.reserve(seriesSize);
}
}
LineSeries::~LineSeries()
{
}
size_t LineSeries::GetSeriesId() const
{
return m_seriesId;
}
void LineSeries::AddPoint(const LinePoint& linePoint)
{
// Handle simple case first
if (m_linePoints.empty() || m_linePoints.back().m_position < linePoint.m_position)
{
m_hasData |= linePoint.m_value > 0;
m_linePoints.push_back(linePoint);
}
else
{
// TODO: Handle the case of out of order insertion
AZ_Error("LineSeries",false,"Trying to add series point out of order. Unsupported behavior");
}
}
void LineSeries::Reset()
{
m_linePoints.clear();
}
bool LineSeries::IsHighlighted() const
{
return m_highlighted;
}
bool LineSeries::IsEnabled() const
{
return m_enabled && m_hasData;
}
const QColor& LineSeries::GetColor() const
{
return m_color;
}
void LineSeries::ResetPainterPath()
{
// Kind of silly that QPainterPath doesn't have a clear.
m_painterPath = QPainterPath();
}
QPainterPath& LineSeries::GetPainterPath()
{
return m_painterPath;
}
const QPainterPath& LineSeries::GetPainterPath() const
{
return m_painterPath;
}
//////////////
// AreaChart
//////////////
const size_t AreaChart::k_invalidSeriesId = static_cast<size_t>(-1);
AreaChart::AreaChart(QWidget* parent)
: QWidget(parent)
, m_inspectionSeries(k_invalidSeriesId)
, m_sizingDirty(true)
, m_regenGraph(true)
, m_axisMin(0)
, m_horizontalAxis(nullptr)
, m_verticalAxis(nullptr)
, m_insetTop(16)
, m_insetBottom(24)
, m_insetLeft(56)
, m_insetRight(16)
, m_widgetBackground(32,32,32,255)
, m_graphBackground(Qt::black)
{
setStyleSheet(QString("QToolTip { border: 1px solid white; padding: 1px; background: black; color: white; }"));
m_axisMax = m_axisMin;
}
AreaChart::~AreaChart()
{
delete m_horizontalAxis;
delete m_verticalAxis;
}
bool AreaChart::IsMouseInspectionEnabled() const
{
return hasMouseTracking();
}
void AreaChart::EnableMouseInspection(bool enabled)
{
setMouseTracking(enabled);
}
void AreaChart::SetMinimumValueRange(unsigned int value)
{
m_axisMin = value;
m_axisMax = m_axisMin;
for (auto& sizingPair : m_maxSizing)
{
if (sizingPair.second > m_axisMax)
{
m_axisMax = sizingPair.second;
}
}
m_regenGraph = true;
update();
}
void AreaChart::ResetChart()
{
m_axisMax = m_axisMin;
m_maxSizing.clear();
m_lineSeries.clear();
m_markers.clear();
m_sizingDirty = true;
m_regenGraph = true;
update();
}
void AreaChart::ConfigureVerticalAxis(QString label, unsigned int minimumHeight)
{
SetMinimumValueRange(minimumHeight);
if (m_verticalAxis == nullptr)
{
m_verticalAxis = aznew Charts::Axis();
}
if (m_verticalAxis)
{
m_verticalAxis->SetLabel(label);
m_verticalAxis->SetAxisRange(0.0f, static_cast<float>(m_axisMax));
}
}
void AreaChart::ConfigureHorizontalAxis(QString label, int minimum, int maximum)
{
if (m_horizontalAxis == nullptr)
{
m_horizontalAxis = aznew Charts::Axis();
}
if (m_horizontalAxis)
{
m_horizontalAxis->SetLabel(label);
m_horizontalAxis->SetAxisRange(static_cast<float>(minimum), static_cast<float>(maximum));
}
}
void AreaChart::ResetSeries(AZ::u32 seriesId)
{
if (!IsValidSeriesId(seriesId))
{
return;
}
m_lineSeries[seriesId].Reset();
}
size_t AreaChart::CreateSeries(const QString& name, const QColor& color, size_t size)
{
size_t seriesKey = m_lineSeries.size();
AZ_Error("AreaChart", seriesKey != k_invalidSeriesId,"Trying to use invalid key for series Id. Too many Area Series created.");
if (size <= 0)
{
size = m_maxSizing.size();
}
m_lineSeries.emplace_back(this, seriesKey, name, color, size);
return seriesKey;
}
void AreaChart::AddPoint(size_t seriesId, int position, unsigned int value)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
LinePoint linePoint(position,value);
AddPoint(seriesId,linePoint);
}
void AreaChart::AddPoint(size_t seriesId, const LinePoint& linePoint)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (!IsValidSeriesId(seriesId))
{
AZ_Error("AreaChart", false, "Invalid SeriesId given.");
return;
}
LineSeries& lineSeries = m_lineSeries[seriesId];
lineSeries.AddPoint(linePoint);
auto sizingIter = m_maxSizing.find(linePoint.m_position);
if (sizingIter != m_maxSizing.end())
{
sizingIter->second += linePoint.m_value;
if (sizingIter->second > m_axisMax)
{
m_axisMax = sizingIter->second;
}
}
else
{
m_maxSizing[linePoint.m_position] = linePoint.m_value;
if (linePoint.m_value > m_axisMax)
{
m_axisMax = linePoint.m_value;
}
}
m_regenGraph = true;
update();
}
void AreaChart::SetSeriesHighlight(size_t seriesId, bool highlighted)
{
if (IsValidSeriesId(seriesId))
{
LineSeries& lineSeries = m_lineSeries[seriesId];
lineSeries.m_highlighted = highlighted;
update();
}
}
void AreaChart::SetSeriesEnabled(size_t seriesId, bool enabled)
{
if (IsValidSeriesId(seriesId))
{
LineSeries& lineSeries = m_lineSeries[seriesId];
lineSeries.m_enabled = enabled;
// Need to regen our graph data here, since we've removed one from the listing
m_regenGraph = true;
update();
}
}
void AreaChart::AddMarker(Charts::AxisType axis, int position, const QColor& color)
{
m_markers.emplace_back(axis, position, color);
}
void AreaChart::mouseMoveEvent(QMouseEvent* mouseEvent)
{
if (IsMouseInspectionEnabled())
{
QPoint mousePos = mouseEvent->pos();
size_t hoveredArea = k_invalidSeriesId;
if (m_graphRect.contains(mousePos) && m_hitAreas.size() > 0)
{
int offset = mousePos.x() - m_graphRect.left();
size_t counter = static_cast<size_t>(static_cast<float>(offset) / (static_cast<float>(m_graphRect.width())/m_hitAreas.size()));
bool escape = false;
// Need to handle the areas right at the edge of the polygons
for (int i = -1; i <= 1; ++i)
{
if ((counter + i) >= m_hitAreas.size())
{
continue;
}
const AZStd::vector<HitArea>& hitAreas = m_hitAreas[counter + i];
for (const HitArea& hitArea : hitAreas)
{
QPolygon polygon = hitArea.m_polygon;
if (hitArea.m_polygon.containsPoint(mousePos,Qt::OddEvenFill))
{
hoveredArea = hitArea.m_seriesId;
escape = true;
break;
}
}
if (escape)
{
break;
}
}
}
if (hoveredArea != m_inspectionSeries)
{
m_inspectionSeries = hoveredArea;
update();
// Signal out which series we are inspecting
emit InspectedSeries(m_inspectionSeries);
}
}
}
void AreaChart::leaveEvent(QEvent* event)
{
(void)event;
if (m_inspectionSeries != k_invalidSeriesId)
{
m_inspectionSeries = k_invalidSeriesId;
update();
// Signal out which series we are inspecting
emit InspectedSeries(m_inspectionSeries);
}
if (m_clicked)
{
m_clicked = false;
}
}
void AreaChart::mousePressEvent(QMouseEvent* mouseEvent)
{
if (IsMouseInspectionEnabled())
{
m_clicked = true;
m_mouseDownPoint = mouseEvent->pos();
}
}
void AreaChart::mouseReleaseEvent(QMouseEvent* mouseEvent)
{
if (IsMouseInspectionEnabled() && m_clicked)
{
QPoint upPoint = mouseEvent->pos();
// Want it to be roughly the same spot.
if ((m_mouseDownPoint - upPoint).manhattanLength() < 20)
{
int closestValue = 0;
if (m_horizontalAxis && m_graphRect.width() > 0)
{
float ratio = static_cast<float>(upPoint.x() - m_graphRect.left()) / static_cast<float>(m_graphRect.width());
closestValue = static_cast<int>(m_horizontalAxis->GetRangeMin()) + static_cast<int>((m_horizontalAxis->GetRange() * ratio) + 0.5f);
}
emit SelectedSeries(m_inspectionSeries, closestValue);
}
}
}
void AreaChart::resizeEvent(QResizeEvent* event)
{
(void)event;
m_sizingDirty = true;
update();
}
void AreaChart::paintEvent(QPaintEvent* event)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
(void)event;
if (m_sizingDirty)
{
m_sizingDirty = false;
m_regenGraph = true;
QPoint topLeft(rect().left() + m_insetLeft, rect().top() + m_insetTop);
QPoint bottomRight(rect().right() - m_insetRight, rect().bottom() - m_insetBottom);
m_graphRect = QRect(topLeft,bottomRight);
}
if (m_regenGraph)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
m_regenGraph = false;
if (m_verticalAxis)
{
m_verticalAxis->SetAxisRange(0.0f, static_cast<float>(m_axisMax));
}
m_hitAreas.clear();
m_hitAreas.reserve(m_maxSizing.size());
m_hitAreas.resize(m_maxSizing.size());
// Running tally of samples that we need to keep track of to manipulate our way through
AZStd::vector<unsigned int> runningTotal(m_maxSizing.size(), 0);
for (LineSeries& lineSeries : m_lineSeries)
{
unsigned int counter = 0;
// Would have to special case out the single data point sample
if (lineSeries.IsEnabled() && lineSeries.m_linePoints.size() > 1)
{
unsigned int currentValue = lineSeries.m_linePoints[counter].m_value;
unsigned int bottomLeft = runningTotal[counter];
unsigned int topLeft = bottomLeft + currentValue;
runningTotal[counter] = topLeft;
++counter;
lineSeries.ResetPainterPath();
QPainterPath& painterPath = lineSeries.GetPainterPath();
AZ_Assert(runningTotal.size() == lineSeries.m_linePoints.size(), "Mismatched/missing sample values given to AreaChart");
for (; counter < lineSeries.m_linePoints.size(); ++counter)
{
currentValue = lineSeries.m_linePoints[counter].m_value;
unsigned int bottomRight = runningTotal[counter];
unsigned int topRight = bottomRight + currentValue;
runningTotal[counter] = topRight;
QPolygon polygon;
polygon.append(ConvertToGraphPoint(counter - 1, bottomLeft));
polygon.append(ConvertToGraphPoint(counter - 1, topLeft));
polygon.append(ConvertToGraphPoint(counter, topRight));
polygon.append(ConvertToGraphPoint(counter, bottomRight));
painterPath.addPolygon(polygon);
m_hitAreas[counter].emplace_back(polygon, lineSeries.GetSeriesId());
bottomLeft = bottomRight;
topLeft = topRight;
}
}
}
}
{
QPen pen;
QBrush brush;
QPainter p(this);
p.fillRect(rect(),m_widgetBackground);
p.fillRect(m_graphRect, m_graphBackground);
QRect widgetBounds = rect();
if (m_horizontalAxis)
{
m_horizontalAxis->PaintAxis(Charts::AxisType::Horizontal, &p, widgetBounds, m_graphRect, nullptr);
}
if (m_verticalAxis)
{
m_verticalAxis->PaintAxis(Charts::AxisType::Vertical, &p, widgetBounds, m_graphRect, nullptr);
}
p.setClipRect(m_graphRect.left(), m_graphRect.top() - 1, m_graphRect.width() + 2, m_graphRect.height() + 2);
brush.setStyle(Qt::SolidPattern);
pen.setStyle(Qt::SolidLine);
pen.setWidth(2);
for (LineSeries& lineSeries : m_lineSeries)
{
if (!lineSeries.IsEnabled())
{
continue;
}
brush.setColor(lineSeries.GetColor());
p.fillPath(lineSeries.GetPainterPath(), brush);
if (lineSeries.IsHighlighted()
|| lineSeries.GetSeriesId() == m_inspectionSeries)
{
// Then highlight it
pen.setColor(Qt::white);
p.setPen(pen);
p.drawPath(lineSeries.GetPainterPath());
}
}
brush.setStyle(Qt::SolidPattern);
pen.setStyle(Qt::SolidLine);
pen.setColor( m_graphBackground );
pen.setWidth(2);
p.setPen(pen);
for (GraphMarker& marker : m_markers)
{
brush.setColor(marker.m_color);
switch (marker.m_axis)
{
case Charts::AxisType::Horizontal:
{
static const int k_barWidth = 4;
static const int k_halfWidth = k_barWidth / 2;
if (!AZ::IsClose(m_horizontalAxis->GetRange(),0.0f,0.01f) )
{
float minRange = m_horizontalAxis->GetRangeMin();
float ratio = (marker.m_position - minRange) / m_horizontalAxis->GetRange();
ratio = AZStd::GetMin(1.0f, ratio);
QPoint startPoint;
startPoint.setX(m_graphRect.left() + static_cast<int>(m_graphRect.width() * ratio) - k_halfWidth);
startPoint.setY(m_graphRect.top());
p.fillRect(startPoint.x(), startPoint.y(), k_barWidth, m_graphRect.height(), brush);
p.drawRect(startPoint.x(), startPoint.y() + 1, k_barWidth, m_graphRect.height() - 1);
}
break;
}
case Charts::AxisType::Vertical:
{
QPoint startPoint = ConvertToGraphPoint(0, static_cast<unsigned int>(marker.m_position));
startPoint.setX(m_graphRect.right());
p.fillRect(startPoint.x(), startPoint.y(), m_graphRect.width(), 2, brush);
p.drawRect(startPoint.x(), startPoint.y(), m_graphRect.width(), 2);
break;
}
default:
AZ_Error("Standalone Tools", false, "Unknown axis type given to marker.");
};
}
}
}
Charts::Axis* AreaChart::GetAxis(Charts::AxisType axisType)
{
switch (axisType)
{
case Charts::AxisType::Horizontal:
return m_horizontalAxis;
case Charts::AxisType::Vertical:
return m_verticalAxis;
default:
AZ_Error("AreaChart", false, "Unknown AxisType.");
return nullptr;
}
}
bool AreaChart::IsValidSeriesId(size_t seriesId) const
{
return seriesId < m_lineSeries.size();
}
QPoint AreaChart::ConvertToGraphPoint(int index, unsigned int value)
{
QPoint graphPoint(m_graphRect.bottomLeft());
int maxSizes = static_cast<int>(m_maxSizing.size());
if (m_horizontalAxis)
{
maxSizes = AZStd::GetMax(maxSizes, static_cast<int>(m_horizontalAxis->GetRange()));
}
if (maxSizes >= 2)
{
// -1, since the index is 0 based.
graphPoint.setX(m_graphRect.left() + static_cast<int>(m_graphRect.width() * (static_cast<float>(index) / static_cast<float>(maxSizes - 1))));
}
if (m_axisMax > 0)
{
graphPoint.setY(m_graphRect.bottom() - static_cast<int>(m_graphRect.height() * (static_cast<float>(value) / static_cast<float>(m_axisMax))));
}
return graphPoint;
}
}
@@ -1,212 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#ifndef PROFILER_AREACHART_H
#define PROFILER_AREACHART_H
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <QWidget>
#include <QPainterPath>
#include <Source/Driller/ChartTypes.hxx>
#endif
namespace Charts
{
class Axis;
}
namespace AreaChart
{
class AreaChart;
struct LinePoint
{
public:
LinePoint(int position, unsigned int value)
: m_position(position)
, m_value(value)
{
}
int m_position;
unsigned int m_value;
};
class LineSeries
{
private:
friend class AreaChart;
typedef AZStd::vector< LinePoint > LinePoints;
public:
LineSeries(AreaChart* owner, size_t seriesId, const QString& name, const QColor& color, size_t seriesSize = 0);
~LineSeries();
size_t GetSeriesId() const;
void AddPoint(const LinePoint& linePoint);
void Reset();
bool IsHighlighted() const;
bool IsEnabled() const;
const QColor& GetColor() const;
void ResetPainterPath();
QPainterPath& GetPainterPath();
const QPainterPath& GetPainterPath() const;
private:
AreaChart* m_owner;
LinePoints m_linePoints;
size_t m_seriesId;
QString m_name;
QColor m_color;
QPainterPath m_painterPath;
bool m_highlighted;
bool m_enabled;
bool m_hasData;
};
class AreaChart
: public QWidget
{
Q_OBJECT
Q_PROPERTY(int insetTop MEMBER m_insetTop)
Q_PROPERTY(int insetBottom MEMBER m_insetBottom)
Q_PROPERTY(int insetLeft MEMBER m_insetLeft)
Q_PROPERTY(int insetRight MEMBER m_insetRight)
Q_PROPERTY(QColor widgetBackground MEMBER m_widgetBackground)
Q_PROPERTY(QColor graphBackground MEMBER m_graphBackground)
struct HitArea
{
HitArea(const QPolygon& polygon, size_t seriesId)
: m_polygon(polygon)
, m_seriesId(seriesId)
{
}
QPolygon m_polygon;
size_t m_seriesId;
};
struct GraphMarker
{
GraphMarker(Charts::AxisType axis, int position, const QColor& color)
: m_axis(axis)
, m_position(position)
, m_color(color)
{
}
Charts::AxisType m_axis;
int m_position;
QColor m_color;
};
public:
static const size_t k_invalidSeriesId;
AreaChart(QWidget* parent = nullptr);
~AreaChart();
bool IsMouseInspectionEnabled() const;
void EnableMouseInspection(bool enabled);
void ResetChart();
void ConfigureVerticalAxis(QString label, unsigned int minimumHeight = -1);
void ConfigureHorizontalAxis(QString label, int minimum, int maximum);
size_t CreateSeries(const QString& name, const QColor& color, size_t size = 0);
void ResetSeries(AZ::u32);
// Methods of adding points
void AddPoint(size_t seriesId, int position, unsigned int value);
void AddPoint(size_t seriesId, const LinePoint& linePoint);
// Methods of manipulating series
void SetSeriesHighlight(size_t seriesId, bool highlighted);
void SetSeriesEnabled(size_t seriesId, bool enabled);
// Methods of adding markers
void AddMarker(Charts::AxisType axis, int position, const QColor& color);
public slots:
signals:
void InspectedSeries(size_t seriesId);
void SelectedSeries(size_t seriesId, int position);
protected:
// Mouse Inspection
void mouseMoveEvent(QMouseEvent* mouseEvent) override;
void leaveEvent(QEvent* mouseEvent) override;
// Mouse clicks
void mousePressEvent(QMouseEvent* mouseEvent) override;
void mouseReleaseEvent(QMouseEvent* mouseEvent) override;
void resizeEvent(QResizeEvent* resizeEvent) override;
void paintEvent(QPaintEvent* paintEvent) override;
private:
void SetMinimumValueRange(unsigned int value);
Charts::Axis* GetAxis(Charts::AxisType axisType);
bool IsValidSeriesId(size_t seriesId) const;
QPoint ConvertToGraphPoint(int index, unsigned int value);
AZStd::vector< GraphMarker > m_markers;
AZStd::vector< LineSeries > m_lineSeries;
AZStd::unordered_map<int, unsigned int> m_maxSizing;
size_t m_inspectionSeries;
size_t m_mouseOverArea;
AZStd::vector< AZStd::vector<HitArea> > m_hitAreas;
bool m_clicked;
QPoint m_mouseDownPoint;
QRect m_graphRect;
bool m_sizingDirty;
bool m_regenGraph;
unsigned int m_axisMin;
unsigned int m_axisMax;
Charts::Axis* m_horizontalAxis;
Charts::Axis* m_verticalAxis;
// Styling
int m_insetTop;
int m_insetBottom;
int m_insetLeft;
int m_insetRight;
QColor m_widgetBackground;
QColor m_graphBackground;
};
}
#endif
@@ -1,648 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/Profiler.h>
#include <QPainter>
#include "Axis.hxx"
namespace Charts
{
//////////////////////////////////////////////////////////////////////////
Axis::Axis(QObject* pParent)
: QObject(pParent)
, m_lockRange(true)
, m_lockZoom(true)
, m_lockRight(false)
, m_autoWindow(true)
, m_rangeMin(0)
, m_rangeMax(0)
, m_windowMax(0)
, m_windowMin(0)
, m_rangeMaxInitialized(false)
, m_rangeMinInitialized(false)
{}
Axis::~Axis()
{
}
bool Axis::GetValid() const
{
return ((m_rangeMinInitialized) && (m_rangeMaxInitialized) && (m_rangeMin < m_rangeMax) && (m_windowMin < m_windowMax));
}
void Axis::Clear()
{
bool wasValid = GetValid();
m_rangeMinInitialized = false;
m_rangeMaxInitialized = false;
m_rangeMax = m_rangeMin = m_windowMax = m_windowMin = 0.0f;
if (wasValid)
{
emit Invalidated();
}
}
void Axis::SetAxisRange(float minimum, float maximum)
{
float oldRangeMin = m_rangeMin;
float oldRangeMax = m_rangeMax;
float oldWindowMin = m_windowMin;
float oldWindowMax = m_windowMax;
bool wasValid = GetValid();
if (m_lockRight)
{
m_windowMin += maximum - m_rangeMax;
m_windowMax = m_rangeMax;
}
m_rangeMin = minimum;
m_rangeMax = maximum;
if (m_autoWindow && !m_lockRight)
{
m_windowMin = m_rangeMin;
m_windowMax = m_rangeMax;
}
m_rangeMinInitialized = true;
m_rangeMaxInitialized = true;
if ((oldRangeMax != m_rangeMax) ||
(oldRangeMin != m_rangeMin) ||
(oldWindowMin != m_windowMin) ||
(oldWindowMax != m_windowMax) ||
(wasValid != GetValid()))
{
emit Invalidated();
}
}
void Axis::AddAxisRange(float value)
{
bool updateValues = false;
float minRange = m_rangeMin;
float maxRange = m_rangeMax;
if ((value < m_rangeMin) || !m_rangeMinInitialized)
{
updateValues = true;
minRange = value;
}
if ((m_rangeMax < value) || !m_rangeMaxInitialized)
{
updateValues = true;
maxRange = value;
}
if (updateValues)
{
SetAxisRange(minRange, maxRange);
}
}
void Axis::SetRangeMax(float rangemax)
{
SetAxisRange(m_rangeMin, rangemax);
}
void Axis::SetRangeMin(float rangemin)
{
SetAxisRange(rangemin, m_rangeMax);
}
void Axis::UpdateWindowRange(float delta)
{
if (delta != 0.0f)
{
m_windowMax += delta;
m_windowMin += delta;
if (m_windowMax > m_rangeMax)
{
delta = m_windowMax - m_rangeMax;
m_windowMax -= delta;
m_windowMin -= delta;
}
if (m_windowMin < m_rangeMin)
{
delta = m_rangeMin - m_windowMin;
m_windowMax += delta;
m_windowMin += delta;
}
emit Invalidated();
}
}
void Axis::SetViewFull()
{
float oldWindowMin = m_windowMin;
float oldWindowMax = m_windowMax;
m_autoWindow = true;
m_windowMin = m_rangeMin;
m_windowMax = m_rangeMax;
if (
(oldWindowMin != m_windowMin) ||
(oldWindowMax != m_windowMax)
)
{
emit Invalidated();
}
}
void Axis::SetLabel(QString newLabel)
{
if (m_label != newLabel)
{
m_label = newLabel;
emit Invalidated();
}
}
bool Axis::GetLockedRight() const
{
return m_lockRight;
}
bool Axis::GetLockedRange() const
{
return m_lockRange;
}
bool Axis::GetLockedZoom() const
{
return m_lockZoom;
}
void Axis::SetLockedRange(bool newValue)
{
m_lockRange = newValue;
}
void Axis::SetLockedZoom(bool newValue)
{
m_lockZoom = newValue;
}
void Axis::SetLockedRight(bool newValue)
{
m_lockRight = newValue;
}
void Axis::SetAutoWindow(bool autoWindow)
{
m_autoWindow = autoWindow;
}
bool Axis::GetAutoWindow() const
{
return m_autoWindow;
}
QString Axis::GetLabel() const
{
return m_label;
}
float Axis::GetWindowMin() const
{
return m_windowMin;
}
float Axis::GetWindowMax() const
{
return m_windowMax;
}
float Axis::GetRangeMin() const
{
return m_rangeMin;
}
float Axis::GetRangeMax() const
{
return m_rangeMax;
}
void Axis::SetWindowMin(float newValue)
{
if (m_windowMin != newValue)
{
m_windowMin = newValue;
emit Invalidated();
}
}
void Axis::SetWindowMax(float newValue)
{
if (m_windowMax != newValue)
{
m_windowMax = newValue;
emit Invalidated();
}
}
float Axis::GetWindowRange() const
{
if (!GetValid())
{
return 1.0f;
}
return m_windowMax - m_windowMin;
}
float Axis::GetRange() const
{
if (!GetValid())
{
return 1.0f;
}
return m_rangeMax - m_rangeMin;
}
// focuspoint is a number from 0 to 1 that indicates how far along that axis the focal point is
// for example on a horizontal axis, 0 is the start, or GetwindowMin(), and 1 is the end, GetWindowMaX()
void Axis::Drag(float delta)
{
if (!GetValid())
{
return;
}
if (GetLockedRange())
{
return;
}
UpdateWindowRange(delta);
}
void Axis::ZoomToRange(float windowMin, float windowMax, bool clamp)
{
if (!GetValid())
{
return;
}
float oldmin = m_windowMin;
float oldmax = m_windowMax;
m_windowMin = windowMin;
m_windowMax = windowMax;
if (clamp)
{
if (m_windowMin < m_rangeMin)
{
m_windowMin = m_rangeMin;
}
if (m_windowMax > m_rangeMax)
{
m_windowMax = m_rangeMax;
}
}
if ((m_windowMin != oldmin) || (m_windowMax != oldmax))
{
emit Invalidated();
}
}
void Axis::Zoom(float ratio, float steps, float zoomLimit)
{
if (!GetValid())
{
return;
}
if (GetLockedRight())
{
ratio = 1.0f;
}
if (!GetLockedZoom())
{
SetAutoWindow(false);
float testMin = GetWindowMin();
float testMax = GetWindowMax();
testMin -= float(GetWindowRange()) * 0.05f * ratio * -steps;
testMax += float(GetWindowRange()) * 0.05f * (1.0f - ratio) * -steps;
if ((testMax - testMin) > 0.0f)
{
if (testMax > GetRangeMax())
{
float offset = GetRangeMax() - testMax;
testMax += offset;
testMin += offset;
}
if (testMin < GetRangeMin())
{
float offset = testMin - GetRangeMin();
testMax -= offset;
testMin -= offset;
}
if (testMax - testMin >= zoomLimit)
{
SetWindowMin(testMin);
SetWindowMax(testMax);
}
if ((testMax - testMin) > (GetRangeMax() - GetRangeMin()))
{
SetViewFull();
}
}
}
}
// given a width of a view in pixels, subdivide it and return a vector of domain units that
// satisfy a human outlook on domain values:
float Axis::ComputeAxisDivisions(float pixelWidth, AZStd::vector<float>& domainPointsOut, float minPixels, float maxPixels, bool allowFractions)
{
if (!GetValid())
{
return 1.0f;
}
float divisor = 1.0f;
float windowRange = GetWindowRange();
// compute the divisor
float currentDivisionWidthInPixels = pixelWidth / (windowRange / divisor);
while (currentDivisionWidthInPixels > maxPixels)
{
// drop it down by 0.5 then by 0.1
divisor /= 2.0f;
currentDivisionWidthInPixels = pixelWidth / (windowRange / divisor);
if (currentDivisionWidthInPixels <= maxPixels)
{
break;
}
divisor *= 0.2f; // 0.5f * 0.2f = 0.1
currentDivisionWidthInPixels = pixelWidth / (windowRange / divisor);
}
// the min pixels is an absolute requirement to prevent text from overlapping
// which is why we apply that constraint LAST.
// so for example, if window shows 100 units, and divisor is 2.0, then that would yield 50 pieces.
// 50 pieces over the span of 1000 pixels is 1000 / 50, 20 pixels each;
// 100 pieces would be (num pieces / pixel space to draw in) pixels
while (currentDivisionWidthInPixels < minPixels)
{
// the division width is too small, there are too many pieces. Make fewer pieces by choosing a larger
// divisor
// drop it down by 0.5 then by 0.1
divisor *= 5.0f;
currentDivisionWidthInPixels = pixelWidth / (windowRange / divisor);
if (currentDivisionWidthInPixels >= minPixels)
{
break;
}
divisor *= 2.0f;
currentDivisionWidthInPixels = pixelWidth / (windowRange / divisor);
}
if ((divisor < 1.0f) && (!allowFractions))
{
divisor = 1.0f;
}
// now lay out the domain starting with the first unit of that number:
float startingUnit = floorf(m_windowMin / divisor) * divisor;
// to retain precision we are going to try to do the math around the origin.
startingUnit -= m_windowMin;
AZStd::size_t maximumAllowedUnitsBeforeSomethingTerribleHasOccurred = (AZStd::size_t)(pixelWidth / 4.0f);
// so for example if we've decided that the divisor is 10 units, and the window min is 12.5, then it will go to 1.25, chop off the .25, and go back to 10
while (startingUnit <= windowRange)
{
if (startingUnit >= 0.0f)
{
domainPointsOut.push_back(startingUnit + m_windowMin);
}
startingUnit += divisor;
if (domainPointsOut.size() > maximumAllowedUnitsBeforeSomethingTerribleHasOccurred)
{
break; // you can put a break point here, but it usually means that we've lost enough precision. Change your axis range or numbering scheme!
}
}
return divisor;
}
void Axis::PaintAxis(AxisType axisType, QPainter* painter, const QRect& widgetBounds, const QRect& graphBounds, QAbstractAxisFormatter* formatter)
{
switch (axisType)
{
case AxisType::Horizontal:
PaintAsHorizontalAxis(painter, widgetBounds, graphBounds, formatter);
break;
case AxisType::Vertical:
PaintAsVerticalAxis(painter, widgetBounds, graphBounds, formatter);
break;
default:
break;
}
}
void Axis::PaintAsVerticalAxis(QPainter* painter, const QRect& widgetBounds, const QRect& graphBounds, QAbstractAxisFormatter* formatter)
{
(void)widgetBounds;
const QColor axisBrush = QColor(255, 255, 0, 255);
const QColor axisPen = QColor(0, 255, 255, 255);
const QColor dottedColor(64, 64, 64, 255);
const QColor solidColor(0, 255, 255, 255);
QBrush brush;
QPen pen;
brush.setColor(axisBrush);
pen.setColor(axisPen);
painter->setPen(pen);
QFont currentFont = painter->font();
currentFont.setPointSize(currentFont.pointSize() - 1);
painter->setFont(currentFont);
int w = painter->fontMetrics().horizontalAdvance(GetLabel());
int h = painter->fontMetrics().height();
int centerHeight = graphBounds.top() + (graphBounds.height() / 2);
DrawRotatedText(GetLabel(), painter, 270, h, centerHeight + w / 2, 1.25f);
currentFont.setPointSize(currentFont.pointSize() + 1);
painter->setFont(currentFont);
QPoint startPoint = graphBounds.topLeft();
QPoint endPoint = graphBounds.bottomLeft();
int height = endPoint.y() - startPoint.y();
int fontH = painter->fontMetrics().height();
if (height == 0)
{
height = 1;
}
AZStd::vector<float> divisions;
divisions.reserve(10);
float divisionSize = ComputeAxisDivisions(static_cast<float>(height), divisions, fontH * 2.0f, fontH * 2.0f);
QPen dottedPen;
dottedPen.setStyle(Qt::DotLine);
dottedPen.setColor(dottedColor);
QBrush solidBrush;
QPen solidPen;
solidPen.setStyle(Qt::SolidLine);
solidPen.setColor(solidColor);
solidPen.setWidth(1);
float fullRange = fabs(GetWindowRange());
for (float division : divisions)
{
float ratio = static_cast<float>(division - GetWindowMin()) / fullRange;
QPoint lineStart;
lineStart.setX(endPoint.x());
lineStart.setY(endPoint.y() - static_cast<int>(height*ratio));
QPoint lineEnd(static_cast<int>(lineStart.x() + graphBounds.width()), lineStart.y());
painter->setPen(dottedPen);
painter->drawLine(lineStart, lineEnd);
QString text;
if (formatter)
{
text = formatter->convertAxisValueToText(Charts::AxisType::Vertical, division, divisions.front(), divisions.back(), divisionSize);
}
else
{
text = QString("%1").arg((AZ::s64)division);
}
int textW = painter->fontMetrics().horizontalAdvance(text);
painter->setPen(solidPen);
painter->drawText(lineStart.x() - textW - 2, (int)lineStart.y() + fontH / 2, text);
}
}
void Axis::PaintAsHorizontalAxis(QPainter* painter, const QRect& widgetBounds, const QRect& graphBounds, QAbstractAxisFormatter* formatter)
{
const QColor axisBrush = QColor(255, 255, 0, 255);
const QColor axisPen = QColor(0, 255, 255, 255);
const QColor dottedColor(64, 64, 64,255);
const QColor solidColor(0, 255, 255, 255);
QBrush brush;
QPen pen;
brush.setColor(axisBrush);
pen.setColor(axisPen);
painter->setPen(pen);
QFont currentFont = painter->font();
currentFont.setPointSize(currentFont.pointSize() - 1);
painter->setFont(currentFont);
painter->drawText(0, 0, widgetBounds.width(), widgetBounds.height(), Qt::AlignHCenter | Qt::AlignBottom, GetLabel());
currentFont.setPointSize(currentFont.pointSize() + 1);
painter->setFont(currentFont);
QPoint startPoint = graphBounds.bottomLeft();
QPoint endPoint = graphBounds.bottomRight();
int width = endPoint.x() - startPoint.x();
if (width == 0)
{
width = 1;
}
float textSpaceRequired = (float)painter->fontMetrics().horizontalAdvance("9,999,999.99");
int fontH = painter->fontMetrics().height();
AZStd::vector<float> divisions;
divisions.reserve(10);
float divisionSize = ComputeAxisDivisions(static_cast<float>(width), divisions, textSpaceRequired, textSpaceRequired);
QPen dottedPen;
dottedPen.setStyle(Qt::DotLine);
dottedPen.setColor(dottedColor);
QBrush solidBrush;
QPen solidPen;
solidPen.setStyle(Qt::SolidLine);
solidPen.setColor(solidColor);
solidPen.setWidth(1);
float fullRange = fabs(GetWindowRange());
for (float division : divisions)
{
float ratio = float(division - GetWindowMin()) / fullRange;
QPoint lineStart;
lineStart.setX(startPoint.x() + static_cast<int>(width*ratio));
lineStart.setY(startPoint.y());
QPoint lineEnd(lineStart.x(), static_cast<int>(startPoint.y() - graphBounds.height()));
painter->setPen(dottedPen);
painter->drawLine(lineStart, lineEnd);
QString text;
if (formatter)
{
text = formatter->convertAxisValueToText(Charts::AxisType::Horizontal, division, divisions.front(), divisions.back(), divisionSize);
}
else
{
text = QString("%1").arg((AZ::s64)division);
}
int textW = painter->fontMetrics().horizontalAdvance(text);
painter->setPen(solidPen);
painter->drawText(lineStart.x() - textW / 2, startPoint.y() + fontH, text);
}
}
void Axis::DrawRotatedText(QString text, QPainter *painter, float degrees, int x, int y, float scale)
{
painter->save();
painter->translate(x, y);
painter->scale(scale, scale);
painter->rotate(degrees);
painter->drawText(0, 0, text);
painter->restore();
}
}
#include <Source/Driller/moc_Axis.cpp>
@@ -1,98 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CHART_AXIS_H
#define CHART_AXIS_H
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <QObject>
#include <Source/Driller/ChartTypes.hxx>
#endif
class QPainter;
#pragma once
namespace Charts
{
// the Axis represents one axis on a chart. its float-based
// it contains information about window range and domain range.
class Axis : public QObject
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(Axis,AZ::SystemAllocator,0);
Axis(QObject* pParent = NULL);
~Axis(); // not virtual
public:
bool GetValid() const;
bool GetAutoWindow() const;
QString GetLabel() const;
float GetWindowMin() const;
float GetWindowMax() const;
float GetRangeMin() const;
float GetRangeMax() const;
float GetWindowRange() const;
float GetRange() const;
bool GetLockedZoom() const;
bool GetLockedRange() const;
bool GetLockedRight() const;
void Zoom(float focusPoint, float steps, float zoomLimit );
void ZoomToRange(float windowMin, float windowMax, bool clamp);
void PaintAxis(AxisType axisType, QPainter* painter, const QRect& widgetBounds, const QRect& graphBounds, QAbstractAxisFormatter* formatter);
float ComputeAxisDivisions(float pixelWidth, AZStd::vector<float>& domainPointsOut, float minPixels, float maxPixels, bool allowFractions = true);
signals:
void Invalidated(); // something has happened which should cause anyone using this axis to update themselves.
public slots:
void SetAxisRange( float minimum, float maximum );
void AddAxisRange( float value );
void SetViewFull();
void SetLabel(QString newLabel);
void SetLockedRight(bool lockRight);
void SetLockedRange(bool locked); // cannot pan
void SetLockedZoom(bool locked); // cannot zoom.
void Clear();
void SetAutoWindow( bool autoWindow );
void SetWindowMin(float newValue);
void SetWindowMax(float newValue);
void UpdateWindowRange(float delta);
void SetRangeMax(float rangemax);
void SetRangeMin(float rangemin);
void Drag(float delta);
private:
void PaintAsVerticalAxis(QPainter* painter, const QRect& widgetBounds, const QRect& graphBounds, QAbstractAxisFormatter* formatter);
void PaintAsHorizontalAxis(QPainter* painter, const QRect& widgetBounds, const QRect& graphBounds, QAbstractAxisFormatter* formatter);
void DrawRotatedText(QString text, QPainter *painter, float degrees, int x, int y, float scale);
QString m_label;
float m_rangeMin;
float m_rangeMax;
float m_windowMin;
float m_windowMax;
bool m_lockZoom; // zoom is always to the range
bool m_lockRange; // you may only pan
bool m_lockRight;
bool m_autoWindow;
bool m_rangeMinInitialized;
bool m_rangeMaxInitialized; // these are false until you init them, and the axis is invalid until such a time.
};
}
#endif
@@ -1,48 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef DRILLER_CSVEXPORTSETTINGS_H
#define DRILLER_CSVEXPORTSETTINGS_H
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#pragma once
namespace Driller
{
class CSVExportSettings
{
public:
AZ_CLASS_ALLOCATOR(CSVExportSettings, AZ::SystemAllocator, 0);
CSVExportSettings()
: m_shouldExportColumnDescriptor(true)
{
}
virtual ~CSVExportSettings()
{
}
void SetShouldExportColumnDescriptors(bool shouldExport)
{
m_shouldExportColumnDescriptor = shouldExport;
}
bool ShouldExportColumnDescriptors() const
{
return m_shouldExportColumnDescriptor;
}
private:
bool m_shouldExportColumnDescriptor;
};
}
#endif
@@ -1,392 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/Trace.h>
#include <AzCore/std/string/conversions.h>
#include "CarrierDataAggregator.hxx"
#include <Source/Driller/Carrier/moc_CarrierDataAggregator.cpp>
#include "CarrierDataEvents.h"
#include "CarrierDataView.hxx"
namespace Driller
{
/////////////////////////////
// CarrierCSVExportSettings
/////////////////////////////
CarrierExportSettings::CarrierExportSettings()
{
m_columnDescriptors =
{
{ExportField::Data_Sent, "Data Sent(Bytes)"},
{ExportField::Data_Received, "Data Received(Bytes)"},
{ExportField::Data_Resent, "Data Resent(Bytes)"},
{ExportField::Data_Acked, "Data Acked(Bytes)"},
{ExportField::Packets_Sent, "Packets Sent"},
{ExportField::Packets_Received, "Packets Received"},
{ExportField::Packets_Lost, "Packets Lost"},
{ExportField::Packets_Acked, "Packets Acked"},
{ExportField::Packet_RTT, "Packet Round Trip Time"},
{ExportField::Packet_Loss, "Packet Loss(%)"},
{ExportField::Effective_Data_Sent, "Effective Data Sent(Bytes)"},
{ExportField::Effective_Data_Received, "Effective Data Received(Bytes)"},
{ExportField::Effective_Data_Resent, "Effective Data Resent(Bytes)"},
{ExportField::Effective_Data_Acked, "Effective Data Acked(Bytes)"},
{ExportField::Effective_Packets_Sent, "Effective Packets Sent"},
{ExportField::Effective_Packets_Received, "Effective Packets Received"},
{ExportField::Effective_Packets_Lost, "Effective Packets Lost"},
{ExportField::Effective_Packets_Acked, "Effective Packets Acked"},
{ExportField::Effective_Packet_RTT, "Effective Packet Round Trip Time"},
{ExportField::Effective_Packet_Loss, "Effective Packet Loss(%)"}
};
m_exportOrdering =
{
ExportField::Data_Sent,
ExportField::Effective_Data_Sent,
ExportField::Data_Received,
ExportField::Effective_Data_Received,
ExportField::Data_Resent,
ExportField::Effective_Data_Resent,
ExportField::Data_Acked,
ExportField::Effective_Data_Acked,
ExportField::Packets_Sent,
ExportField::Effective_Packets_Sent,
ExportField::Packets_Received,
ExportField::Effective_Packets_Received,
ExportField::Packets_Lost,
ExportField::Effective_Packets_Lost,
ExportField::Packets_Acked,
ExportField::Effective_Packets_Acked,
ExportField::Packet_RTT,
ExportField::Effective_Packet_RTT,
ExportField::Packet_Loss,
ExportField::Effective_Packet_Loss
};
for (const AZStd::pair< ExportField, AZStd::string >& item : m_columnDescriptors)
{
m_stringToExportEnum[item.second] = item.first;
}
}
void CarrierExportSettings::GetExportItems(QStringList& items) const
{
for (const AZStd::pair< CarrierExportField, AZStd::string>& item : m_columnDescriptors)
{
items.push_back(QString(item.second.c_str()));
}
}
void CarrierExportSettings::GetActiveExportItems(QStringList& items) const
{
for (CarrierExportField currentField : m_exportOrdering)
{
if (currentField != CarrierExportField::UNKNOWN)
{
items.push_back(QString(FindColumnDescriptor(currentField).c_str()));
}
}
}
void CarrierExportSettings::UpdateExportOrdering(const QStringList& activeItems)
{
m_exportOrdering.clear();
for (const QString& activeItem : activeItems)
{
ExportField field = FindExportFieldFromDescriptor(activeItem.toStdString().c_str());
AZ_Assert(field != ExportField::UNKNOWN, "Unknown descriptor %s", activeItem.toStdString().c_str());
if (field != ExportField::UNKNOWN)
{
m_exportOrdering.push_back(field);
}
}
}
const AZStd::vector< CarrierExportField >& CarrierExportSettings::GetExportOrder() const
{
return m_exportOrdering;
}
const AZStd::string& CarrierExportSettings::FindColumnDescriptor(ExportField exportField) const
{
static const AZStd::string emptyDescriptor;
AZStd::unordered_map<ExportField, AZStd::string>::const_iterator descriptorIter = m_columnDescriptors.find(exportField);
if (descriptorIter == m_columnDescriptors.end())
{
AZ_Assert(false, "Unknown column descriptor in Carrier CSV Export");
return emptyDescriptor;
}
else
{
return descriptorIter->second;
}
}
CarrierExportField CarrierExportSettings::FindExportFieldFromDescriptor(const char* columnDescriptor) const
{
AZStd::unordered_map<AZStd::string, ExportField>::const_iterator exportIter = m_stringToExportEnum.find(columnDescriptor);
ExportField retVal = ExportField::UNKNOWN;
if (exportIter != m_stringToExportEnum.end())
{
retVal = exportIter->second;
}
return retVal;
}
//////////////////////////
// CarrierDataAggregator
//////////////////////////
float CarrierDataAggregator::GetTValueAtFrame(FrameNumberType frame, AZ::s64 maxValue)
{
float valueAtFrame = 0.0f;
size_t numEventsAtFrame = NumOfEventsAtFrame(frame);
for (EventNumberType i = m_frameToEventIndex[frame]; i < static_cast<EventNumberType>(m_frameToEventIndex[frame] + numEventsAtFrame); i++)
{
CarrierDataEvent* event = static_cast<CarrierDataEvent*>(m_events[i]);
// Consider the aggregation a sum of all data send and received (i.e. total bandwidth used).
valueAtFrame += event->mLastSecond.mDataSend + event->mLastSecond.mDataReceived;
}
if (valueAtFrame >= maxValue)
{
return 1.0f;
}
if (valueAtFrame == 0.0f)
{
return -1.0f;
}
float tValue = (valueAtFrame / (maxValue * 2)) - 1.0f;
return tValue;
}
CarrierDataAggregator::CarrierDataAggregator(int identity)
: Aggregator(identity)
, m_parser(this)
{
}
CustomizeCSVExportWidget* CarrierDataAggregator::CreateCSVExportCustomizationWidget()
{
return aznew GenericCustomizeCSVExportWidget(m_csvExportSettings);
}
float CarrierDataAggregator::ValueAtFrame(FrameNumberType frame)
{
return GetTValueAtFrame(frame, (1024 * 20));
}
QColor CarrierDataAggregator::GetColor() const
{
return QColor(255, 0, 0);
}
QString CarrierDataAggregator::GetName() const
{
return QString("Carrier");
}
QString CarrierDataAggregator::GetChannelName() const
{
return ChannelName();
}
QString CarrierDataAggregator::GetDescription() const
{
return QString("GridMate Carrier Data");
}
QString CarrierDataAggregator::GetToolTip() const
{
return QString("Information about overall bandwidth usage");
}
AZ::Uuid CarrierDataAggregator::GetID() const
{
return AZ::Uuid("{927B208C-28E8-4BE7-BF4E-629D98F7097F}");
}
QWidget* CarrierDataAggregator::DrillDownRequest(FrameNumberType frame)
{
(void)frame;
// Always provide a full view of the driller data.
FrameNumberType lastFrame = static_cast<FrameNumberType>(m_frameToEventIndex.size() - 1);
// This pointer is cleaned up by qt when the window is closed.
return aznew CarrierDataView(0, lastFrame, this);
}
void CarrierDataAggregator::OptionsRequest()
{
}
void CarrierDataAggregator::ExportColumnDescriptorToCSV(AZ::IO::SystemFile& file, CSVExportSettings* exportSettings)
{
CarrierExportSettings* carrierExportSettings = static_cast<CarrierExportSettings*>(exportSettings);
const AZStd::vector< CarrierExportField >& exportOrdering = carrierExportSettings->GetExportOrder();
bool addComma = false;
for (CarrierExportField currentField : exportOrdering)
{
if (addComma)
{
file.Write(",", 1);
}
const AZStd::string& columnDescriptor = carrierExportSettings->FindColumnDescriptor(currentField);
file.Write(columnDescriptor.c_str(), columnDescriptor.size());
addComma = true;
}
file.Write("\n", 1);
}
void CarrierDataAggregator::ExportEventToCSV(AZ::IO::SystemFile& file, const DrillerEvent* drillerEvent, CSVExportSettings* exportSettings)
{
const CarrierDataEvent* carrierEvent = static_cast<const CarrierDataEvent*>(drillerEvent);
CarrierExportSettings* carrierExportSettings = static_cast<CarrierExportSettings*>(exportSettings);
const AZStd::vector< CarrierExportField >& exportOrdering = carrierExportSettings->GetExportOrder();
bool addComma = false;
AZStd::string number;
for (CarrierExportField currentField : exportOrdering)
{
if (addComma)
{
file.Write(",", 1);
}
switch (currentField)
{
case CarrierExportField::Data_Sent:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mDataSend);
break;
}
case CarrierExportField::Data_Received:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mDataReceived);
break;
}
case CarrierExportField::Data_Resent:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mDataResent);
break;
}
case CarrierExportField::Data_Acked:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mDataAcked);
break;
}
case CarrierExportField::Packets_Sent:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mPacketSend);
break;
}
case CarrierExportField::Packets_Received:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mPacketReceived);
break;
}
case CarrierExportField::Packets_Lost:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mPacketLost);
break;
}
case CarrierExportField::Packets_Acked:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mPacketAcked);
break;
}
case CarrierExportField::Packet_RTT:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mRTT);
break;
}
case CarrierExportField::Packet_Loss:
{
AZStd::to_string(number, carrierEvent->mLastSecond.mPacketLoss);
break;
}
case CarrierExportField::Effective_Data_Sent:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mDataSend);
break;
}
case CarrierExportField::Effective_Data_Received:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mDataReceived);
break;
}
case CarrierExportField::Effective_Data_Resent:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mDataResent);
break;
}
case CarrierExportField::Effective_Data_Acked:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mDataAcked);
break;
}
case CarrierExportField::Effective_Packets_Sent:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mPacketSend);
break;
}
case CarrierExportField::Effective_Packets_Received:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mPacketReceived);
break;
}
case CarrierExportField::Effective_Packets_Lost:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mPacketLost);
break;
}
case CarrierExportField::Effective_Packets_Acked:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mPacketAcked);
break;
}
case CarrierExportField::Effective_Packet_RTT:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mRTT);
break;
}
case CarrierExportField::Effective_Packet_Loss:
{
AZStd::to_string(number, carrierEvent->mEffectiveLastSecond.mPacketLoss);
break;
}
default:
AZ_Assert(false, "Unknown CarrierExportField");
break;
}
file.Write(number.c_str(), number.size());
addComma = true;
}
file.Write("\n", 1);
}
}
@@ -1,154 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef DRILLER_CARRIER_DATAAGGREGATOR_H
#define DRILLER_CARRIER_DATAAGGREGATOR_H
#if !defined(Q_MOC_RUN)
#include "Source/Driller/DrillerAggregator.hxx"
#include "CarrierDataParser.h"
#include "Source/Driller/GenericCustomizeCSVExportWidget.hxx"
#endif
namespace Driller
{
struct CarrierData;
}
namespace Driller
{
class CarrierDataView;
class CarrierExportSettings
: public GenericCSVExportSettings
{
public:
enum class ExportField
{
Data_Sent,
Data_Received,
Data_Resent,
Data_Acked,
Packets_Sent,
Packets_Received,
Packets_Lost,
Packets_Acked,
Packet_RTT,
Packet_Loss,
Effective_Data_Sent,
Effective_Data_Received,
Effective_Data_Resent,
Effective_Data_Acked,
Effective_Packets_Sent,
Effective_Packets_Received,
Effective_Packets_Lost,
Effective_Packets_Acked,
Effective_Packet_RTT,
Effective_Packet_Loss,
UNKNOWN
};
private:
AZStd::unordered_map<ExportField,AZStd::string> m_columnDescriptors;
AZStd::unordered_map<AZStd::string, ExportField> m_stringToExportEnum;
AZStd::vector< ExportField > m_exportOrdering;
public:
AZ_CLASS_ALLOCATOR(CarrierExportSettings, AZ::SystemAllocator, 0);
CarrierExportSettings();
void GetExportItems(QStringList& items) const override;
void GetActiveExportItems(QStringList& items) const override;
const AZStd::vector< ExportField >& GetExportOrder() const;
const AZStd::string& FindColumnDescriptor(ExportField exportField) const;
protected:
void UpdateExportOrdering(const QStringList& activeItems) override;
private:
ExportField FindExportFieldFromDescriptor(const char* descriptor) const;
};
typedef CarrierExportSettings::ExportField CarrierExportField;
class CarrierDataAggregator : public Aggregator
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(CarrierDataAggregator, AZ::SystemAllocator, 0);
CarrierDataAggregator(int identity = 0);
static AZ::u32 DrillerId() { return AZ_CRC("CarrierDriller"); }
// Driller::Aggregator.
AZ::u32 GetDrillerId() const override
{
return DrillerId();
}
static const char* ChannelName() { return "GridMate"; }
AZ::Crc32 GetChannelId() const override
{
return AZ::Crc32(ChannelName());
}
AZ::Debug::DrillerHandlerParser* GetDrillerDataParser() override
{
return &m_parser;
}
bool CanExportToCSV() const override
{
return true;
}
CustomizeCSVExportWidget* CreateCSVExportCustomizationWidget() override;
// Driller::Aggregator.
void ApplySettingsFromWorkspace(WorkspaceSettingsProvider*) override {}
void ActivateWorkspaceSettings(WorkspaceSettingsProvider*) override {}
void SaveSettingsToWorkspace(WorkspaceSettingsProvider*) override {}
public slots:
// Driller::Aggregator.
float ValueAtFrame(FrameNumberType frame) override;
QColor GetColor() const override;
QString GetName() const override;
QString GetChannelName() const override;
QString GetDescription() const override;
QString GetToolTip() const override;
AZ::Uuid GetID() const override;
QWidget* DrillDownRequest(FrameNumberType frame) override;
void OptionsRequest() override;
protected:
void ExportColumnDescriptorToCSV(AZ::IO::SystemFile& file, CSVExportSettings* exportSettings) override;
void ExportEventToCSV(AZ::IO::SystemFile& file, const DrillerEvent* drillerEvent, CSVExportSettings* exportSettings) override;
private:
// Aggregate all data events in a particular frame and return a normalized
// value in the range [-1,1] based on the min/max range [0,maxValue].
float GetTValueAtFrame(FrameNumberType frame, AZ::s64 maxValue);
CarrierExportSettings m_csvExportSettings;
// A parser that will parse carrier driller XML data and add events back
// into this aggregator.
CarrierDataParser m_parser;
};
}
#endif
@@ -1,62 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef DRILLER_CARRIER_DATAEVENTS_H
#define DRILLER_CARRIER_DATAEVENTS_H
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/string/string.h>
#include "Source/Driller/DrillerEvent.h"
namespace Driller
{
namespace Carrier
{
enum CarrierEventType
{
CET_INFO = 1
};
}
struct CarrierData
{
AZ::s32 mDataSend; //< Data sent (bytes).
AZ::s32 mDataReceived; //< Data received (bytes).
AZ::s32 mDataResent; //< Data resent (bytes).
AZ::s32 mDataAcked; //< Data acknowledged (bytes).
AZ::s32 mPacketSend; //< Number of packets sent.
AZ::s32 mPacketReceived; //< Number of packets received.
AZ::s32 mPacketLost; //< Number of packets lost.
AZ::s32 mPacketAcked; //< Number of packets acknowledged.
float mRTT; //< Round-trip time.
float mPacketLoss; //< Packet loss percentage.
};
class CarrierDataEvent
: public DrillerEvent
{
public:
AZ_CLASS_ALLOCATOR(CarrierDataEvent, AZ::SystemAllocator, 0);
CarrierDataEvent()
: DrillerEvent(Carrier::CET_INFO)
, mID("<none>")
{
};
virtual void StepForward(Aggregator* data) { (void)data; };
virtual void StepBackward(Aggregator* data) { (void)data; };
AZStd::string mID;
CarrierData mLastSecond;
CarrierData mEffectiveLastSecond;
};
}
#endif
@@ -1,131 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CarrierDataEvents.h"
#include "CarrierDataAggregator.hxx"
#include "CarrierDataParser.h"
namespace Driller
{
CarrierDataParser::CarrierDataParser(CarrierDataAggregator* m_aggregator)
: DrillerHandlerParser(false)
, m_currentType(CDT_NONE)
, m_aggregator(m_aggregator)
{
}
CarrierDataParser::~CarrierDataParser()
{
}
AZ::Debug::DrillerHandlerParser* CarrierDataParser::OnEnterTag(AZ::u32 tagName)
{
// Start of a new event.
if (tagName == AZ_CRC("Statistics"))
{
m_currentType = CDT_STATISTICS;
m_aggregator->AddEvent(aznew CarrierDataEvent);
return this;
}
else if (tagName == AZ_CRC("LastSecond"))
{
m_currentType = CDT_LAST_SECOND;
return this;
}
else if (tagName == AZ_CRC("EffectiveLastSecond"))
{
m_currentType = CDT_EFFECTIVE_LAST_SECOND;
return this;
}
else
{
return nullptr;
}
}
void CarrierDataParser::OnExitTag(AZ::Debug::DrillerHandlerParser* handler, AZ::u32 tagName)
{
(void)handler;
if (tagName == AZ_CRC("LastSecond") || tagName == AZ_CRC("EffectiveLastSecond"))
{
m_currentType = CDT_NONE;
}
}
void CarrierDataParser::OnData(const AZ::Debug::DrillerSAXParser::Data& dataNode)
{
// Ignore unsupported tags.
if (m_currentType == CDT_NONE)
{
return;
}
CarrierDataEvent* event = static_cast<CarrierDataEvent*>(m_aggregator->GetEvents().back());
if (m_currentType == CDT_STATISTICS)
{
dataNode.Read(event->mID);
return;
}
CarrierData* eventData = nullptr;
switch (m_currentType)
{
case CDT_LAST_SECOND:
eventData = &event->mLastSecond;
break;
case CDT_EFFECTIVE_LAST_SECOND:
eventData = &event->mEffectiveLastSecond;
break;
default:
return;
}
if (dataNode.m_name == AZ_CRC("DataSend"))
{
dataNode.Read(eventData->mDataSend);
}
else if (dataNode.m_name == AZ_CRC("DataReceived"))
{
dataNode.Read(eventData->mDataReceived);
}
else if (dataNode.m_name == AZ_CRC("DataResent"))
{
dataNode.Read(eventData->mDataResent);
}
else if (dataNode.m_name == AZ_CRC("DataAcked"))
{
dataNode.Read(eventData->mDataAcked);
}
else if (dataNode.m_name == AZ_CRC("PacketSend"))
{
dataNode.Read(eventData->mPacketSend);
}
else if (dataNode.m_name == AZ_CRC("PacketReceived"))
{
dataNode.Read(eventData->mPacketReceived);
}
else if (dataNode.m_name == AZ_CRC("PacketLost"))
{
dataNode.Read(eventData->mPacketLost);
}
else if (dataNode.m_name == AZ_CRC("PacketAcked"))
{
dataNode.Read(eventData->mPacketAcked);
}
else if (dataNode.m_name == AZ_CRC("PacketLoss"))
{
dataNode.Read(eventData->mPacketLoss);
}
else if (dataNode.m_name == AZ_CRC("rtt"))
{
dataNode.Read(eventData->mRTT);
}
}
}
@@ -1,50 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef DRILLER_CARRIER_DATAPARSER_H
#define DRILLER_CARRIER_DATAPARSER_H
#include <AzCore/Driller/Stream.h>
#include "CarrierDataEvents.h"
namespace Driller
{
class CarrierDataAggregator;
enum CarrierDataType
{
CDT_NONE,
CDT_STATISTICS,
CDT_LAST_SECOND,
CDT_EFFECTIVE_LAST_SECOND
};
class CarrierDataParser
: public AZ::Debug::DrillerHandlerParser
{
public:
// The parser will add events to a CarrierDataAggregator instance.
CarrierDataParser(CarrierDataAggregator* m_aggregator);
virtual ~CarrierDataParser();
// AZ::Debug::DrillerHandlerParser: Callbacks for entering, leaving and finding data
// while parsing the driller XML data.
AZ::Debug::DrillerHandlerParser* OnEnterTag(AZ::u32 tagName) override;
void OnExitTag(AZ::Debug::DrillerHandlerParser* handler, AZ::u32 tagName) override;
void OnData(const AZ::Debug::DrillerSAXParser::Data& dataNode) override;
private:
// The current tag type while parsing the driller XML data.
CarrierDataType m_currentType;
// The aggregator where events are added as a result of parsing the XML data.
CarrierDataAggregator* m_aggregator;
};
}
#endif
@@ -1,268 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/std/containers/set.h>
#include "CarrierDataEvents.h"
#include "CarrierDataAggregator.hxx"
#include "CarrierDataView.hxx"
#include "CarrierOperationTelemetryEvent.h"
#include "Source/Driller/DrillerOperationTelemetryEvent.h"
#include <Source/Driller/Carrier/moc_CarrierDataView.cpp>
#include <Source/Driller/Carrier/ui_CarrierDataView.h>
#include <QString>
namespace Driller
{
static AZ::s64 GetLargestDataValue(const CarrierDataView::DataPointList& dataPointList)
{
AZ::s64 maxDataValue = 0;
for (auto dataPoint = dataPointList.begin(); dataPoint != dataPointList.end(); dataPoint++)
{
if (dataPoint->second > maxDataValue)
{
maxDataValue = static_cast<AZ::s64>(dataPoint->second);
}
}
return maxDataValue;
}
static void BuildEventList(const CarrierDataAggregator* aggr,
FrameNumberType startFrame,
FrameNumberType endFrame,
AZStd::vector<EventNumberType>& eventIdxList)
{
eventIdxList.clear();
for (FrameNumberType frame = startFrame; frame <= endFrame; frame++)
{
size_t numEvents = aggr->NumOfEventsAtFrame(frame);
if (numEvents == 0)
{
continue;
}
EventNumberType firstEventIdx = aggr->GetFirstIndexAtFrame(frame);
for (EventNumberType eventIdx = firstEventIdx; eventIdx < static_cast<EventNumberType>(firstEventIdx + numEvents); eventIdx++)
{
eventIdxList.push_back(eventIdx);
}
}
}
static void GetEventIds(const CarrierDataAggregator* aggr,
FrameNumberType startFrame,
FrameNumberType endFrameIdx,
AZStd::set<AZStd::string>& ids)
{
ids.clear();
for (FrameNumberType frameId = startFrame; frameId <= endFrameIdx; frameId++)
{
size_t numEvents = aggr->NumOfEventsAtFrame(frameId);
EventNumberType firstEventIndex = aggr->GetFirstIndexAtFrame(frameId);
for (EventNumberType eventIndex = firstEventIndex; eventIndex < static_cast<EventNumberType>(firstEventIndex + numEvents); eventIndex++)
{
CarrierDataEvent* event = static_cast<CarrierDataEvent*>(aggr->GetEvents()[eventIndex]);
ids.insert(event->mID);
}
}
}
CarrierDataView::CarrierDataView(FrameNumberType startFrame, FrameNumberType endFrame, const CarrierDataAggregator* aggregator)
: QDialog()
, mStartFrame(0)
, mEndFrame(0)
, m_lifespanTelemetry("CarrierDataView")
{
// Create a window defined in CarrierDataView.ui.
m_gui = azcreate(Ui::CarrierDataView, ());
m_gui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
mStartFrame = startFrame;
mEndFrame = endFrame;
mAggregator = aggregator;
// Prepare the dialog.
show();
raise();
activateWindow();
setFocus();
setWindowTitle(aggregator->GetDialogTitle());
// Find all unique ids and add them to the drop down box.
AZStd::set<AZStd::string> ids;
GetEventIds(aggregator, startFrame, endFrame, ids);
for (auto id = ids.begin(); id != ids.end(); id++)
{
m_gui->Filter->addItem((*id).c_str());
}
QObject::connect(m_gui->Filter, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentFilterChanged()));
// Update the charts based on the current filter.
OnCurrentFilterChanged();
}
CarrierDataView::~CarrierDataView()
{
azdestroy(m_gui);
}
void CarrierDataView::OnCurrentFilterChanged()
{
AZStd::string currentId = AZStd::string(m_gui->Filter->currentText().toUtf8().constData());
SetupAllCharts(currentId, mAggregator, mStartFrame, mEndFrame);
CarrierOperationTelemetryEvent filterChanged;
filterChanged.SetAttribute("IPFilterChanged", "");
filterChanged.Log();
}
void CarrierDataView::SetupAllCharts(AZStd::string id,
const CarrierDataAggregator* aggr,
FrameNumberType startFrame,
FrameNumberType endFrame)
{
AZStd::vector<EventNumberType> eventIdxList;
BuildEventList(aggr, startFrame, endFrame, eventIdxList);
DataPointList send;
DataPointList recv;
DataPointList effectiveSend;
DataPointList effectiveRecv;
DataPointList pktSend;
DataPointList pktRecv;
DataPointList rtt;
DataPointList loss;
EventNumberType eventIndex = 0;
for (auto eventItr = eventIdxList.begin(); eventItr != eventIdxList.end(); eventItr++)
{
EventNumberType realEventIndex = *eventItr;
CarrierDataEvent* event = static_cast<CarrierDataEvent*>(aggr->GetEvents()[realEventIndex]);
if (event->mID.compare(id) != 0)
{
continue;
}
// Total bytes sent and received.
send.push_back(DataPoint(eventIndex, static_cast<float>(event->mLastSecond.mDataSend)));
recv.push_back(DataPoint(eventIndex, static_cast<float>(event->mLastSecond.mDataReceived)));
// Effective bytes sent and received (
effectiveSend.push_back(DataPoint(eventIndex, static_cast<float>(event->mEffectiveLastSecond.mDataSend)));
effectiveRecv.push_back(DataPoint(eventIndex, static_cast<float>(event->mEffectiveLastSecond.mDataReceived)));
// Total packets send and received.
pktSend.push_back(DataPoint(eventIndex, static_cast<float>(event->mLastSecond.mPacketSend)));
pktRecv.push_back(DataPoint(eventIndex, static_cast<float>(event->mLastSecond.mPacketReceived)));
// RTT
rtt.push_back(DataPoint(eventIndex, event->mLastSecond.mRTT));
// Loss
loss.push_back(DataPoint(eventIndex, event->mLastSecond.mPacketLoss));
eventIndex++;
}
SetupDualBytesChart(m_gui->sendRecvDataStrip, send, recv);
SetupDualBytesChart(m_gui->effectiveSendRecvDataStrip, effectiveSend, effectiveRecv);
SetupDualPacketChart(m_gui->packetSendRecvDataStrip, pktSend, pktRecv);
SetupTimeChart(m_gui->rttDataStrip, rtt);
}
void CarrierDataView::SetupDualBytesChart(StripChart::DataStrip* chart, const DataPointList& bytes0, const DataPointList& bytes1)
{
chart->Reset();
AZ::s64 maxSend = GetLargestDataValue(bytes0);
AZ::s64 maxRecv = GetLargestDataValue(bytes1);
chart->AddAxis("Seconds", 0.0f, static_cast<float>(bytes0.size() > bytes1.size() ? bytes0.size() : bytes1.size()), false);
chart->AddAxis("Bytes/second", 0.0f, (maxSend > maxRecv ? maxSend : maxRecv) * 1.2f, false);
int sendChannel = chart->AddChannel("Bytes0");
int recvChannel = chart->AddChannel("Bytes1");
chart->SetChannelColor(sendChannel, QColor(0, 255, 0));
chart->SetChannelStyle(sendChannel, StripChart::Channel::STYLE_CONNECTED_LINE);
chart->SetChannelColor(recvChannel, QColor(255, 0, 0));
chart->SetChannelStyle(recvChannel, StripChart::Channel::STYLE_CONNECTED_LINE);
for (auto dataPoint = bytes0.begin(); dataPoint != bytes0.end(); dataPoint++)
{
chart->AddData(sendChannel, 0, static_cast<float>(dataPoint->first), dataPoint->second);
}
for (auto dataPoint = bytes1.begin(); dataPoint != bytes1.end(); dataPoint++)
{
chart->AddData(recvChannel, 0, static_cast<float>(dataPoint->first), dataPoint->second);
}
}
void CarrierDataView::SetupDualPacketChart(StripChart::DataStrip* chart, const DataPointList& packets0, const DataPointList& packets1)
{
chart->Reset();
AZ::s64 maxSend = GetLargestDataValue(packets0);
AZ::s64 maxRecv = GetLargestDataValue(packets1);
chart->AddAxis("Seconds", 0.0f, static_cast<float>(packets0.size() > packets1.size() ? packets0.size() : packets1.size()), false);
chart->AddAxis("Packets/second", 0.0f, (maxSend > maxRecv ? maxSend : maxRecv) * 1.2f, false);
int sendChannel = chart->AddChannel("Bytes0");
int recvChannel = chart->AddChannel("Bytes1");
chart->SetChannelColor(sendChannel, QColor(0, 255, 0));
chart->SetChannelStyle(sendChannel, StripChart::Channel::STYLE_CONNECTED_LINE);
chart->SetChannelColor(recvChannel, QColor(255, 0, 0));
chart->SetChannelStyle(recvChannel, StripChart::Channel::STYLE_CONNECTED_LINE);
for (auto dataPoint = packets0.begin(); dataPoint != packets0.end(); dataPoint++)
{
chart->AddData(sendChannel, 0, static_cast<float>(dataPoint->first), dataPoint->second);
}
for (auto dataPoint = packets1.begin(); dataPoint != packets1.end(); dataPoint++)
{
chart->AddData(recvChannel, 0, static_cast<float>(dataPoint->first), dataPoint->second);
}
}
void CarrierDataView::SetupTimeChart(StripChart::DataStrip* chart, const DataPointList& time)
{
chart->Reset();
AZ::s64 maxRTT = GetLargestDataValue(time);
chart->AddAxis("Seconds", 0.0f, static_cast<float>(time.size()), false);
chart->AddAxis("Milliseconds", 0.0f, maxRTT * 1.2f);
int channel = chart->AddChannel("RTT");
chart->SetChannelColor(channel, QColor(255, 0, 255));
chart->SetChannelStyle(channel, StripChart::Channel::STYLE_CONNECTED_LINE);
for (auto dataPoint = time.begin(); dataPoint != time.end(); dataPoint++)
{
chart->AddData(channel, 0, static_cast<float>(dataPoint->first), dataPoint->second);
}
}
void CarrierDataView::SetupPercentageChart(StripChart::DataStrip* chart, const DataPointList& percentage)
{
chart->Reset();
chart->AddAxis("Seconds", 0.0f, static_cast<float>(percentage.size()), false);
chart->AddAxis("Percentage", 0.0f, 100.0f);
int channel = chart->AddChannel("Loss");
chart->SetChannelColor(channel, QColor(255, 255, 255));
chart->SetChannelStyle(channel, StripChart::Channel::STYLE_CONNECTED_LINE);
for (auto dataPoint = percentage.begin(); dataPoint != percentage.end(); dataPoint++)
{
chart->AddData(channel, 0, static_cast<float>(dataPoint->first), dataPoint->second);
}
}
}
@@ -1,70 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CARRIER_DATAVIEW_H
#define CARRIER_DATAVIEW_H
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QDialog>
#include "Source/Driller/StripChart.hxx"
#include "Source/Driller/DrillerOperationTelemetryEvent.h"
#include "Source/Driller/DrillerDataTypes.h"
#endif
namespace Ui
{
class CarrierDataView;
}
namespace Driller
{
class CarrierDataAggregator;
class CarrierDataView
: public QDialog
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(CarrierDataView, AZ::SystemAllocator, 0);
CarrierDataView(FrameNumberType startFrame, FrameNumberType endFrame, const CarrierDataAggregator* aggr);
virtual ~CarrierDataView();
// A data point is made up of a x and y value (first and second respectively)
// which can be plotted onto a XY chart.
typedef AZStd::pair<AZ::s64, float> DataPoint;
typedef AZStd::vector<DataPoint> DataPointList;
public slots:
void OnCurrentFilterChanged();
private:
void SetupAllCharts(AZStd::string id,
const CarrierDataAggregator* aggr,
FrameNumberType startFrame,
FrameNumberType endFrame);
void SetupDualBytesChart(StripChart::DataStrip* chart, const DataPointList& bytes0, const DataPointList& bytes1);
void SetupDualPacketChart(StripChart::DataStrip* chart, const DataPointList& packets1, const DataPointList& packets2);
void SetupTimeChart(StripChart::DataStrip* chart, const DataPointList& time);
void SetupPercentageChart(StripChart::DataStrip* chart, const DataPointList& percentage);
const CarrierDataAggregator* mAggregator;
FrameNumberType mStartFrame, mEndFrame;
DrillerWindowLifepsanTelemetry m_lifespanTelemetry;
// A QT widget defined in CarrierDataView.ui and compiled into ui_CarrierDataView.hpp.
Ui::CarrierDataView* m_gui;
};
}
#endif
@@ -1,327 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CarrierDataView</class>
<widget class="QDialog" name="CarrierDataView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1000</width>
<height>825</height>
</rect>
</property>
<property name="windowTitle">
<string>Carrier Data View</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="frame">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<widget class="QComboBox" name="Filter"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="StripChart::DataStrip" name="sendRecvDataStrip" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>128</height>
</size>
</property>
<widget class="QLabel" name="SendLabel">
<property name="geometry">
<rect>
<x>70</x>
<y>20</y>
<width>111</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color : rgb(0, 255, 0); }</string>
</property>
<property name="text">
<string>Total Send</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="RecvLabel">
<property name="geometry">
<rect>
<x>70</x>
<y>40</y>
<width>111</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color : rgb(255, 0, 0); }</string>
</property>
<property name="text">
<string>Total Received</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
</item>
<item>
<widget class="StripChart::DataStrip" name="effectiveSendRecvDataStrip" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>128</height>
</size>
</property>
<widget class="QLabel" name="SendLabel_2">
<property name="geometry">
<rect>
<x>70</x>
<y>20</y>
<width>141</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color : rgb(0, 255, 0); }</string>
</property>
<property name="text">
<string>User Data Send</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="RecvLabel_2">
<property name="geometry">
<rect>
<x>70</x>
<y>40</y>
<width>141</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color : rgb(255, 0, 0); }</string>
</property>
<property name="text">
<string>User Data Received</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
</item>
<item>
<widget class="StripChart::DataStrip" name="packetSendRecvDataStrip" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>128</height>
</size>
</property>
<widget class="QLabel" name="SendLabel_3">
<property name="geometry">
<rect>
<x>70</x>
<y>20</y>
<width>141</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color : rgb(0, 255, 0); }</string>
</property>
<property name="text">
<string>Packets Send</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="RecvLabel_3">
<property name="geometry">
<rect>
<x>70</x>
<y>40</y>
<width>141</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color : rgb(255, 0, 0); }</string>
</property>
<property name="text">
<string>Packets Received</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
</item>
<item>
<widget class="StripChart::DataStrip" name="rttDataStrip" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>128</height>
</size>
</property>
<widget class="QLabel" name="SendLabel_4">
<property name="geometry">
<rect>
<x>70</x>
<y>20</y>
<width>181</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color : rgb(255, 0, 255); }</string>
</property>
<property name="text">
<string>Return Trip Time (Latency)</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>StripChart::DataStrip</class>
<extends>QWidget</extends>
<header>../StripChart.hxx</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -1,26 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef DRILLER_CARRIER_CARRIEROPERATIONTELEMETRYEVENT_H
#define DRILLER_CARRIER_CARRIEROPERATIONTELEMETRYEVENT_H
#include "Source/Telemetry/TelemetryEvent.h"
namespace Driller
{
class CarrierOperationTelemetryEvent
: public Telemetry::TelemetryEvent
{
public:
CarrierOperationTelemetryEvent()
: Telemetry::TelemetryEvent("CarrierDataViewOperation")
{
}
};
}
#endif
@@ -1,27 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "Source/Driller/ChannelConfigurationDialog.hxx"
#include <Source/Driller/moc_ChannelConfigurationDialog.cpp>
namespace Driller
{
ChannelConfigurationDialog::ChannelConfigurationDialog(QWidget* parent)
: QDialog(parent)
{
setAttribute(Qt::WA_DeleteOnClose, true);
setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint);
QMargins margins(0, 0, 0, 0);
setContentsMargins(margins);
}
ChannelConfigurationDialog::~ChannelConfigurationDialog()
{
emit DialogClosed(this);
}
}
@@ -1,38 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#ifndef PROFILER_CHANNELCONFIGURATIONDIALOG_H
#define PROFILER_CHANNELCONFIGURATIONDIALOG_H
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <QDialog>
#endif
namespace Driller
{
class ChannelConfigurationDialog
: public QDialog
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(ChannelConfigurationDialog,AZ::SystemAllocator,0);
ChannelConfigurationDialog(QWidget* parent = nullptr);
~ChannelConfigurationDialog();
signals:
void DialogClosed(QDialog* dialog);
};
}
#endif
@@ -1,21 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Source/Driller/ChannelConfigurationWidget.hxx>
#include <Source/Driller/moc_ChannelConfigurationWidget.cpp>
namespace Driller
{
///////////////////////////////
// ChannelConfigurationWidget
///////////////////////////////
ChannelConfigurationWidget::ChannelConfigurationWidget(QWidget* parent)
: QWidget(parent)
{
}
}
@@ -1,37 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#ifndef PROFILER_CHANNELCONFIGURATIONWIDGET_H
#define PROFILER_CHANNELCONFIGURATIONWIDGET_H
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <QtWidgets/QWidget>
#endif
namespace Driller
{
class ChannelConfigurationWidget
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ChannelConfigurationWidget, AZ::SystemAllocator,0);
ChannelConfigurationWidget(QWidget* parent = nullptr);
~ChannelConfigurationWidget() = default;
public slots:
signals:
void ConfigurationChanged();
};
}
#endif
@@ -1,484 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ChannelControl.hxx"
#include <Source/Driller/moc_ChannelControl.cpp>
#include "Annotations/Annotations.hxx"
#include "ChannelDataView.hxx"
#include "ChannelProfilerWidget.hxx"
#include "DrillerAggregator.hxx"
#include "DrillerMainWindowMessages.h"
#include "Source/Driller/ChannelConfigurationDialog.hxx"
#include "Source/Driller/ChannelConfigurationWidget.hxx"
#include "Source/Driller/CollapsiblePanel.hxx"
#include "Source/Driller/CustomizeCSVExportWidget.hxx"
#include "Source/Driller/DrillerOperationTelemetryEvent.h"
namespace Driller
{
ChannelControl::ChannelControl(const char* channelName, AnnotationsProvider* ptrAnnotations, QWidget* parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
, m_isSetup(false)
, m_captureMode(CaptureMode::Unknown)
, m_channelId(channelName)
, m_configurationDialog(nullptr)
{
setupUi(this);
m_State.m_EndFrame = -1;
m_State.m_FramesInView = 10; // magic numbers (this matches the default drop-down menu option)
m_State.m_ContractedHeight = false;
m_State.m_ScrubberFrame = 0;
m_State.m_FrameOffset = 0;
m_State.m_LoopBegin = 0;
m_State.m_LoopEnd = 0;
this->channelName->setText(channelName ? channelName : "Channel Name Here");
channelDataView->RegisterToChannel(this, ptrAnnotations);
channelDataView->setAutoFillBackground(true);
configChannel->setVisible(false);
connect(channelDataView, SIGNAL(InformOfMouseClick(Qt::MouseButton, FrameNumberType, FrameNumberType, int)), SIGNAL(InformOfMouseClick(Qt::MouseButton, FrameNumberType, FrameNumberType, int)));
connect(channelDataView, SIGNAL(InformOfMouseMove(FrameNumberType, FrameNumberType, int)), SIGNAL(InformOfMouseMove(FrameNumberType, FrameNumberType, int)));
connect(channelDataView, SIGNAL(InformOfMouseRelease(Qt::MouseButton, FrameNumberType, FrameNumberType, int)), SIGNAL(InformOfMouseRelease(Qt::MouseButton, FrameNumberType, FrameNumberType, int)));
connect(channelDataView, SIGNAL(InformOfMouseWheel(FrameNumberType, int, FrameNumberType, int)), SIGNAL(InformOfMouseWheel(FrameNumberType, int, FrameNumberType, int)));
connect(configChannel, SIGNAL(clicked()), SLOT(OnConfigureChannel()));
ConfigureUI();
}
ChannelControl::~ChannelControl()
{
if (m_configurationDialog)
{
m_configurationDialog->close();
}
m_configurationDialog = nullptr;
QList<QWidget*>::iterator iter = m_OpenDrills.begin();
while (iter != m_OpenDrills.end())
{
delete *iter;
++iter;
}
m_OpenDrills.clear();
}
bool ChannelControl::IsInCaptureMode(CaptureMode captureMode) const
{
return m_captureMode == captureMode;
}
void ChannelControl::SetCaptureMode(CaptureMode captureMode)
{
if (captureMode != m_captureMode)
{
m_captureMode = captureMode;
ConfigureUI();
emit OnCaptureModeChanged(m_captureMode);
}
}
void ChannelControl::OnContractedToggled(bool toggleState)
{
(void)toggleState;
// We're going to want to contract this eventually. So keeping the function
// to avoid unwiring all of the other methods, while we transition
/*
m_Contracted->setChecked(toggleState);
if (toggleState) // MINIMIZED
{
m_State.m_ContractedHeight = true;
m_Info->hide();
setFixedHeight(k_contractedSize);
QIcon eyecon(":/general/expand");
m_Contracted->setIcon(eyecon);
}
else // NORMAL
{
m_State.m_ContractedHeight = false;
m_Info->show();
this->setFixedHeight(k_expandedSize);
QIcon eyecon(":/general/contract");
m_Contracted->setIcon(eyecon);
}
emit ExpandedContracted();
*/
}
void ChannelControl::OnSuccessfulDrillDown(QWidget* drillerWidget)
{
if (drillerWidget)
{
connect(drillerWidget, SIGNAL(destroyed(QObject*)), this, SLOT(OnDrillDestroyed(QObject*)));
m_OpenDrills.push_back(drillerWidget);
}
}
void ChannelControl::OnDrillDestroyed(QObject* drill)
{
QWidget* qw = qobject_cast<QWidget*>(drill);
m_OpenDrills.removeOne(qw);
}
void ChannelControl::OnShowCommand()
{
//todo: phughes consider auto-hide toggle to control this behavior
//QList<QPoint>::iterator ptiter = m_OpenDrillsPositions.begin();
//QList<QWidget*>::iterator iter = m_OpenDrills.begin();
//while (iter != m_OpenDrills.end())
//{
// (*iter)->show();
// (*iter)->move(*ptiter);
// ++iter;
// ++ptiter;
//}
}
void ChannelControl::OnHideCommand()
{
//todo: phughes consider auto-hide toggle to control this behavior
//m_OpenDrillsPositions.clear();
//QList<QWidget*>::iterator iter = m_OpenDrills.begin();
//while (iter != m_OpenDrills.end())
//{
// if (*iter)
// {
// m_OpenDrillsPositions.push_back((*iter)->pos());
// (*iter)->hide();
// }
// ++iter;
//}
}
bool ChannelControl::IsSetup() const
{
return m_isSetup;
}
void ChannelControl::SignalSetup()
{
m_isSetup = true;
ConfigureUI();
}
bool ChannelControl::IsActive() const
{
bool isActive = false;
for (ChannelProfilerWidget* profiler : m_profilerWidgets)
{
if (profiler->IsActive())
{
isActive = true;
break;
}
}
return isActive;
}
const AZStd::list< ChannelProfilerWidget* >& ChannelControl::GetProfilers() const
{
return m_profilerWidgets;
}
ChannelProfilerWidget* ChannelControl::GetMainProfiler() const
{
ChannelProfilerWidget* retVal = nullptr;
for (ChannelProfilerWidget* profiler : m_profilerWidgets)
{
if (profiler->IsActive())
{
retVal = profiler;
break;
}
}
return retVal;
}
ChannelProfilerWidget* ChannelControl::AddAggregator(Aggregator* aggregator)
{
ChannelProfilerWidget* retVal = nullptr;
for (ChannelProfilerWidget* profiler : m_profilerWidgets)
{
if (profiler->GetID() == aggregator->GetID())
{
AZ_Warning("ChannelControl", false, "Trying to register two aggregators with the same ID");
retVal = profiler;
break;
}
}
if (retVal == nullptr)
{
retVal = aznew ChannelProfilerWidget(this, aggregator);
if (retVal != nullptr)
{
ConnectProfilerWidget(retVal);
profilerLayout->addWidget(retVal);
m_profilerWidgets.push_back(retVal);
aggregator->AnnotateChannelView(channelDataView);
if (aggregator->HasConfigurations())
{
configChannel->setVisible(true);
}
connect(aggregator, SIGNAL(NormalizedRangeChanged()), SLOT(OnNormalizedRangeChanged()));
}
}
return retVal;
}
bool ChannelControl::RemoveAggregator(Aggregator* aggregator)
{
bool removed = false;
for (auto profilerIter = m_profilerWidgets.begin();
profilerIter != m_profilerWidgets.end();
++profilerIter)
{
if ((*profilerIter)->GetID() == aggregator->GetID())
{
removed = true;
m_profilerWidgets.erase(profilerIter);
break;
}
}
AZ_Warning("ChannelControl", removed, "Trying to remove aggregator from the wrong Channel Control");
return removed;
}
void ChannelControl::SetAllProfilersEnabled(bool enabled)
{
for (ChannelProfilerWidget* profiler : m_profilerWidgets)
{
profiler->SetIsActive(enabled);
}
}
AZ::Crc32 ChannelControl::GetChannelId() const
{
return m_channelId;
}
void ChannelControl::SetEndFrame(FrameNumberType endFrame)
{
channelDataView->DirtyGraphData();
m_State.m_EndFrame = endFrame;
channelDataView->update();
}
void ChannelControl::SetSliderOffset(FrameNumberType frameOffset)
{
channelDataView->DirtyGraphData();
m_State.m_FrameOffset = frameOffset;
//AZ_TracePrintf("Driller"," SetSliderOffset %d = %d - %d\n", m_State.m_FrameOffset, m_State.m_EndFrame, frame);
channelDataView->update();
}
void ChannelControl::SetLoopBegin(FrameNumberType frameNum)
{
if (m_State.m_LoopBegin != frameNum)
{
m_State.m_LoopBegin = frameNum;
FrameNumberType lastFrame = m_State.m_FrameOffset + m_State.m_FramesInView - 1;
if (frameNum < m_State.m_FrameOffset)
{
emit RequestScrollToFrame(frameNum);
}
else if (frameNum > lastFrame)
{
emit RequestScrollToFrame(m_State.m_FrameOffset + (frameNum - lastFrame));
}
channelDataView->update();
}
}
void ChannelControl::SetLoopEnd(FrameNumberType frameNum)
{
if (m_State.m_LoopEnd != frameNum)
{
m_State.m_LoopEnd = frameNum;
FrameNumberType lastFrame = m_State.m_FrameOffset + m_State.m_FramesInView - 1;
if (frameNum < m_State.m_FrameOffset)
{
emit RequestScrollToFrame(frameNum);
}
else if (frameNum > lastFrame)
{
emit RequestScrollToFrame(m_State.m_FrameOffset + (frameNum - lastFrame));
}
channelDataView->update();
}
}
void ChannelControl::SetScrubberFrame(FrameNumberType frameNum)
{
if (m_State.m_ScrubberFrame != frameNum)
{
m_State.m_ScrubberFrame = frameNum;
FrameNumberType lastFrame = m_State.m_FrameOffset + m_State.m_FramesInView - 1;
if (frameNum < m_State.m_FrameOffset)
{
emit RequestScrollToFrame(frameNum);
}
else if (frameNum > lastFrame)
{
emit RequestScrollToFrame(m_State.m_FrameOffset + (frameNum - lastFrame));
}
channelDataView->update();
}
}
void ChannelControl::SetDataPointsInView(FrameNumberType count)
{
m_State.m_FramesInView = count;
channelDataView->DirtyGraphData();
channelDataView->update();
}
void ChannelControl::OnRefreshView()
{
channelDataView->update();
}
void ChannelControl::OnActivationChanged(ChannelProfilerWidget* profilerWidget, bool activated)
{
if (activated)
{
profilerWidget->GetAggregator()->AnnotateChannelView(channelDataView);
}
else
{
profilerWidget->GetAggregator()->RemoveChannelAnnotation(channelDataView);
}
channelDataView->DirtyGraphData();
channelDataView->update();
}
void ChannelControl::OnConfigureChannel()
{
if (m_configurationDialog == nullptr)
{
m_configurationDialog = aznew ChannelConfigurationDialog();
QVBoxLayout* layout = new QVBoxLayout();
QMargins contentMargins(3,5,3,5);
layout->setContentsMargins(contentMargins);
layout->setSpacing(5);
for (ChannelProfilerWidget* profilerWidget : m_profilerWidgets)
{
if (profilerWidget->IsActive())
{
ChannelConfigurationWidget* configurationWidget = profilerWidget->CreateConfigurationWidget();
if (configurationWidget)
{
connect(configurationWidget, SIGNAL(ConfigurationChanged()), SLOT(OnConfigurationChanged()));
layout->addWidget(configurationWidget);
}
}
}
m_configurationDialog->setLayout(layout);
m_configurationDialog->show();
m_configurationDialog->setFocus();
connect(m_configurationDialog, SIGNAL(DialogClosed(QDialog*)), SLOT(OnDialogClosed(QDialog*)));
m_configurationDialog->setWindowTitle(QString("%1's Channel Configurations.").arg(channelName->text()));
}
if (m_configurationDialog->isMinimized())
{
m_configurationDialog->showNormal();
}
m_configurationDialog->raise();
m_configurationDialog->activateWindow();
}
void ChannelControl::OnDialogClosed(QDialog* dialog)
{
if (m_configurationDialog == dialog)
{
m_configurationDialog = nullptr;
}
}
void ChannelControl::OnConfigurationChanged()
{
if (IsInCaptureMode(CaptureMode::Inspecting))
{
for (ChannelProfilerWidget* profilerWidget : m_profilerWidgets)
{
profilerWidget->GetAggregator()->OnConfigurationChanged();
}
// Force the channel data view to regrab all of the aggregator data.
channelDataView->RefreshGraphData();
}
}
void ChannelControl::OnNormalizedRangeChanged()
{
// Only want to allow this while we are inspecting.
// Otherwise just too much noise with the incoming data.
if (IsInCaptureMode(CaptureMode::Inspecting))
{
channelDataView->RefreshGraphData();
}
}
void ChannelControl::ConnectProfilerWidget(ChannelProfilerWidget* profilerWidget)
{
connect(this, SIGNAL(OnCaptureModeChanged(CaptureMode)), profilerWidget, SLOT(SetCaptureMode(CaptureMode)));
connect(profilerWidget, SIGNAL(OnActivationChanged(ChannelProfilerWidget*, bool)), this, SLOT(OnActivationChanged(ChannelProfilerWidget*, bool)));
profilerWidget->SetCaptureMode(m_captureMode);
}
void ChannelControl::ConfigureUI()
{
if (IsSetup())
{
configChannel->setEnabled(!IsInCaptureMode(CaptureMode::Capturing));
}
}
}
@@ -1,133 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CHANNEL_CONTROL_H
#define CHANNEL_CONTROL_H
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <QtWidgets/QWidget>
#include "DrillerNetworkMessages.h"
#include "Source/Driller/DrillerDataTypes.h"
// Generated Files
#include <Source/Driller/ui_ChannelControl.h>
#endif
namespace Driller
{
class AnnotationsProvider;
class ChannelConfigurationDialog;
/*
Channel Control intermediates between one data Aggregator, the application's main window, and the renderer.
This maintains state used by the renderer and passes changes both up and down via signal/slot.
*/
class ChannelControl
: public QWidget
, private Ui::ChannelControl
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(ChannelControl,AZ::SystemAllocator,0);
ChannelControl( const char* channelName, AnnotationsProvider* ptrAnnotations, QWidget* parent = NULL, Qt::WindowFlags flags = Qt::WindowFlags());
virtual ~ChannelControl(void);
struct
{
FrameNumberType m_EndFrame;
FrameNumberType m_FramesInView;
int m_ContractedHeight;
FrameNumberType m_ScrubberFrame;
FrameNumberType m_FrameOffset;
FrameNumberType m_LoopBegin;
FrameNumberType m_LoopEnd;
} m_State;
bool m_isLive;
QList<QWidget*> m_OpenDrills;
QList<QPoint> m_OpenDrillsPositions;
bool IsSetup() const;
void SignalSetup();
bool IsActive() const;
const AZStd::list< ChannelProfilerWidget* >& GetProfilers() const;
// Temporary solution, since eventually we'll want to display all of the information
// we have at once. For now, since we can only have one. Get a "main" window.
ChannelProfilerWidget* GetMainProfiler() const;
ChannelProfilerWidget* AddAggregator(Aggregator* aggregator);
bool RemoveAggregator(Aggregator* aggregator);
void SetAllProfilersEnabled(bool enabled);
AZ::Crc32 GetChannelId() const;
void SetDataPointsInView( FrameNumberType count );
void SetEndFrame( FrameNumberType frame );
void SetSliderOffset( FrameNumberType frame );
void SetLoopBegin( FrameNumberType frame );
void SetLoopEnd( FrameNumberType frame );
bool IsInCaptureMode(CaptureMode captureMode) const;
public slots:
void SetCaptureMode(CaptureMode captureMode);
void OnContractedToggled( bool toggleState );
void OnSuccessfulDrillDown(QWidget* drillerWidget);
void SetScrubberFrame( FrameNumberType frame );
void OnDrillDestroyed(QObject *drill);
void OnShowCommand();
void OnHideCommand();
void OnRefreshView();
void OnActivationChanged(ChannelProfilerWidget*,bool activated);
void OnConfigureChannel();
void OnDialogClosed(QDialog* dialog);
void OnConfigurationChanged();
void OnNormalizedRangeChanged();
signals:
void OnCaptureModeChanged(CaptureMode captureMode);
void RequestScrollToFrame( FrameNumberType frame );
void InformOfMouseClick(Qt::MouseButton button, FrameNumberType frame, FrameNumberType range, int modifiers );
void InformOfMouseMove( FrameNumberType frame, FrameNumberType range, int modifiers );
void InformOfMouseRelease(Qt::MouseButton button, FrameNumberType frame, FrameNumberType range, int modifiers );
void InformOfMouseWheel( FrameNumberType frame, int wheelAmount, FrameNumberType range, int modifiers );
QWidget* DrillDownRequest(FrameNumberType atFrame);
void OptionsRequest();
void ExpandedContracted();
void AddConfigurationWidgets(QLayout* configurationLayout);
QString GetInspectionFileName() const;
private:
void ConnectProfilerWidget(ChannelProfilerWidget* profilerWidget);
void ConfigureUI();
bool m_isSetup;
CaptureMode m_captureMode;
AZ::Crc32 m_channelId;
AZStd::list< ChannelProfilerWidget* > m_profilerWidgets;
ChannelConfigurationDialog* m_configurationDialog;
};
}
#endif // CHANNEL_CONTROL_H
@@ -1,218 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChannelControl</class>
<widget class="QWidget" name="ChannelControl">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1023</width>
<height>74</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="infoArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="channelName">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configChannel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Resources/sharedResources.qrc">
<normaloff>:/driller/settings_icon</normaloff>:/driller/settings_icon</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="profilerFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="profilerLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Driller::ChannelDataView" name="channelDataView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Driller::ChannelDataView</class>
<extends>QWidget</extends>
<header>Source/Driller/ChannelDataView.hxx</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../../Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Resources/sharedResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -1,875 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Math/MathUtils.h>
#include "ChannelDataView.hxx"
#include <Source/Driller/moc_ChannelDataView.cpp>
#include "ChannelControl.hxx"
#include "ChannelProfilerWidget.hxx"
#include "DrillerAggregator.hxx"
#include "Annotations/Annotations.hxx"
#include <QPainter>
#include <QPen>
#include <QToolTip>
#include <QDesktopWidget>
#include <QScreen>
namespace Driller
{
static const int k_contractedSize = 28;
static const int k_expandedSize = 64;
static const int k_textWidth = 128;
static const int k_barHeight = 5;
////////////////////////
// AggregatorDataPoint
////////////////////////
AggregatorDataPoint::AggregatorDataPoint()
: m_isActive(true)
, m_isShowingOverlay(false)
, m_shouldOutline(false)
{
}
AggregatorDataPoint::AggregatorDataPoint(QRect rectangle, ChannelProfilerWidget* profilerWidget)
: m_visualBlock(rectangle)
, m_isActive(true)
, m_isShowingOverlay(false)
, m_shouldOutline(false)
{
m_combinedProfilers.insert(profilerWidget);
}
void AggregatorDataPoint::Draw(QPainter& painter, int leftEdge, float barWidth)
{
QColor drawColor;
QColor outlineColor;
m_isActive = false;
for (ChannelProfilerWidget* currentProfiler : m_combinedProfilers)
{
if (currentProfiler->IsActive())
{
if (m_isActive)
{
drawColor = QColor(255, 255, 255);
outlineColor = QColor(255, 255, 0);
break;
}
else
{
m_isActive = true;
drawColor = currentProfiler->GetAggregator()->GetColor();
// right now this should never be used. If it eventually is used, we'll probably
// want to add a flow to the aggregators to pass along this color. For now
// some silly wrapping to do something noticeable.
outlineColor = QColor((drawColor.red() + 100) % 255, (drawColor.green() + 100) % 255, (drawColor.blue() + 100) % 255);
}
}
}
if (m_isActive)
{
const int upAmount = static_cast<int>(ceilf(k_barHeight * 0.5f));
int centerHeight = m_visualBlock.center().y();
int topEdge = static_cast<int>(centerHeight - upAmount);
if (m_shouldOutline)
{
static const int k_outlineSize = 1;
painter.fillRect(
leftEdge,
topEdge,
static_cast<int>(barWidth),
k_barHeight,
outlineColor);
painter.fillRect(
leftEdge + k_outlineSize,
(int)topEdge + k_outlineSize,
(int)barWidth - (2 * k_outlineSize), // overdraw on dense data > 1 event per pixel, optimize later
k_barHeight - (2 * k_outlineSize),
drawColor);
}
else
{
painter.fillRect(
leftEdge,
topEdge,
static_cast<int>(barWidth),
k_barHeight,
drawColor);
}
}
}
bool AggregatorDataPoint::IntersectsDataPoint(const AggregatorDataPoint& dataPoint)
{
return m_visualBlock.intersects(dataPoint.m_visualBlock);
}
bool AggregatorDataPoint::ContainsPoint(const QPoint& point)
{
// Don't want to collide if we aren't visible.
if (m_isActive)
{
// Our Visual block's horizontal information is only valid when it is created.
// After that we lazily update information, so the only valid information that the block maintins is the vertical.
// So we're going to rely on the DataView to manage the horizontal collision, and we'll manage the vertical.
return (m_visualBlock.bottom() > point.y() && m_visualBlock.top() <= point.y());
}
else
{
return false;
}
}
void AggregatorDataPoint::AddAggregatorDataPoint(const AggregatorDataPoint& dataPoint)
{
m_drawColor = QColor(255, 255, 255);
// dataPointBottomRight
QPoint dpBR = dataPoint.m_visualBlock.bottomRight();
// dataPointTopLeft
QPoint dpTL = dataPoint.m_visualBlock.topLeft();
// visualBlockBottomRight
QPoint vbBR = m_visualBlock.bottomRight();
// visualBlockTopLeft
QPoint vbTL = m_visualBlock.topLeft();
QPoint bottomRight(AZStd::GetMax(dpBR.x(), vbBR.x()), AZStd::GetMax(dpBR.y(), vbBR.y()));
QPoint topLeft(AZStd::GetMin(dpTL.x(), vbTL.x()), AZStd::GetMin(dpTL.y(), vbTL.y()));
m_visualBlock = QRect(topLeft, bottomRight);
m_combinedProfilers.insert(dataPoint.m_combinedProfilers.begin(), dataPoint.m_combinedProfilers.end());
}
bool AggregatorDataPoint::SetOverlayEnabled(bool enabled)
{
if (m_isShowingOverlay != enabled)
{
m_isShowingOverlay = enabled;
if (m_isShowingOverlay)
{
int activeProfilers = 0;
QString toolTip = "Multiple Profiler(s)";
for (ChannelProfilerWidget* channelProfiler : m_combinedProfilers)
{
if (channelProfiler->IsActive())
{
++activeProfilers;
toolTip.append(QString("<BR> - <I>%1</I>").arg(channelProfiler->GetName()));
}
}
if (activeProfilers >= 2)
{
m_shouldOutline = true;
// Since QToolTip::HideText doesn't seem to actually do anything, I'm trying to show
// a second tool tip while the first one is being displayed. However,
// if I use the same string for the tool tip, it doesn't actually update the position.
// But since the cursor has moved away from the original postion, it begins the countdown
// timer to hide the tool tip. Meaning, the new tool tip I was trying to generate hides.
// Going to alternatingly append a space to the end to force it to be different to
// avoid this nonsense.
{
static bool k_dumbToolTipHack = false;
if (k_dumbToolTipHack)
{
toolTip.append(" ");
}
k_dumbToolTipHack = !k_dumbToolTipHack;
}
QToolTip::showText(QCursor::pos(), toolTip);
}
else
{
m_shouldOutline = false;
}
}
else
{
m_shouldOutline = false;
QToolTip::hideText();
}
return true;
}
return false;
}
/////////////////
// BudgetMarker
/////////////////
BudgetMarker::BudgetMarker(float value, QColor& drawColor)
: m_value(value)
, m_drawColor(drawColor)
{
}
BudgetMarker::~BudgetMarker()
{
}
float BudgetMarker::GetValue() const
{
return m_value;
}
const QColor& BudgetMarker::GetColor() const
{
return m_drawColor;
}
////////////////////
// ChannelDataView
////////////////////
ChannelDataView::ChannelDataView(QWidget* parent)
: QWidget(parent)
, m_Channel(nullptr)
, m_ptrAnnotations(nullptr)
, m_minFrame(-1)
, m_maxFrame(-1)
, m_highlightedFrame(-1)
, m_lastFrame(0)
, m_xOffset(0)
, m_initializeDrag(false)
, m_dragInitialized(false)
, m_shouldIgnorePoint(false)
, m_mouseGrabbed(false)
, m_dirtyGraph(true)
{
setAutoFillBackground(true);
setAttribute(Qt::WA_OpaquePaintEvent, true);
setMouseTracking(true);
}
ChannelDataView::~ChannelDataView()
{
}
void ChannelDataView::RegisterToChannel(ChannelControl* channel, AnnotationsProvider* annotations)
{
m_Channel = channel;
m_ptrAnnotations = annotations;
}
int ChannelDataView::FrameToPosition(FrameNumberType frameNumber)
{
int localOffset = 0;
FrameNumberType frameDifference = 0;
if (frameNumber < m_Channel->m_State.m_FrameOffset)
{
frameDifference = frameNumber - m_Channel->m_State.m_FrameOffset;
}
else if (frameNumber >= (m_Channel->m_State.m_FrameOffset + (m_Channel->m_State.m_FramesInView - 1)))
{
frameDifference = frameNumber - (m_Channel->m_State.m_FrameOffset + (m_Channel->m_State.m_FramesInView - 1));
localOffset = rect().width();
}
localOffset += static_cast<int>(GetBarWidth() * frameDifference);
return mapToGlobal(QPoint(localOffset, 0)).x();
}
FrameNumberType ChannelDataView::PositionToFrame(const QPoint& pt)
{
QRect wrect = rect();
FrameNumberType frame = m_Channel->m_State.m_FrameOffset + m_Channel->m_State.m_FramesInView - 1;
frame = frame <= m_Channel->m_State.m_EndFrame ? frame : m_Channel->m_State.m_EndFrame;
float pct = (float)pt.x() / (float)wrect.width();
FrameNumberType rCell = m_Channel->m_State.m_FramesInView - 1 - (int)((float)m_Channel->m_State.m_FramesInView * pct);
FrameNumberType retFrame = frame - rCell;
return retFrame;
}
FrameNumberType ChannelDataView::FramesPerPixel()
{
FrameNumberType range = PositionToFrame(QPoint(0, 0)) - PositionToFrame(QPoint(1, 0));
return(range > 0 ? range : 1);
}
float ChannelDataView::GetBarWidth()
{
return ((float)(rect().width()) / (float)(m_Channel->m_State.m_FramesInView));
}
void ChannelDataView::paintEvent(QPaintEvent* event)
{
(void)event;
if (m_dirtyGraph)
{
m_dirtyGraph = false;
RecalculateGraphedPoints();
}
QPen pen;
pen.setWidth(1);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
pen.setBrush(brush);
QPainter painter(this);
painter.setPen(pen);
painter.fillRect(rect(), Qt::black);
FrameNumberType frame = m_Channel->m_State.m_FrameOffset + m_Channel->m_State.m_FramesInView - 1;
frame = frame <= m_Channel->m_State.m_EndFrame ? frame : m_Channel->m_State.m_EndFrame;
QRect wrect = rect();
float barWidth = GetBarWidth();
int barWidthHalf = (int)(barWidth / 2.0f);
float drawBarWidth = ((barWidth - 1.0f) < 1.0f ? 1.0f : (barWidth - 1.0f));
if (m_Channel->IsActive())
{
// PLAYBACK LOOP MARKERS
if (m_Channel->m_State.m_LoopBegin >= frame - m_Channel->m_State.m_FramesInView)
{
float l = rect().right() - barWidth / 2.0f - 1 - barWidth * (frame - m_Channel->m_State.m_LoopBegin);
painter.fillRect((int)l, 0, 2, rect().height(), QColor(255, 255, 0, 255));
}
if (m_Channel->m_State.m_LoopEnd >= frame - m_Channel->m_State.m_FramesInView)
{
float l = rect().right() - barWidth / 2.0f - 1 - barWidth * (frame - m_Channel->m_State.m_LoopEnd);
painter.fillRect((int)l, 0, 2, rect().height(), QColor(255, 255, 0, 255));
}
}
brush.setStyle(Qt::Dense2Pattern);
brush.setColor(Qt::red);
int wrectHeight = wrect.height() / (m_Channel->m_State.m_ContractedHeight ? 2 : 1);
if (m_Channel->IsActive() && m_Channel->m_State.m_EndFrame)
{
// SCRUBBER
if (m_Channel->m_State.m_ScrubberFrame >= frame - m_Channel->m_State.m_FramesInView)
{
float l = rect().right()-GetBarWidth()/2.0f - 1 - GetBarWidth() * (frame - m_Channel->m_State.m_ScrubberFrame);
painter.fillRect( (int)l, 0, 2, rect().height(), brush );
}
float rightEdgeOfBar = (float)wrect.right();
float leftEdgeOfBar = rightEdgeOfBar - barWidth;
AZStd::list< ChannelProfilerWidget* > activeProfilers;
for (ChannelProfilerWidget* profiler : m_Channel->GetProfilers())
{
if (profiler->IsActive())
{
activeProfilers.push_back(profiler);
}
}
while (frame >= 0 && rightEdgeOfBar >= wrect.left())
{
int actualLeftEdge = (int)floorf(leftEdgeOfBar);
// Graph out precomputed points.
DataPointList& dataPointList = m_graphedPoints[frame];
for (AggregatorDataPoint& dataPoint : dataPointList)
{
dataPoint.Draw(painter, actualLeftEdge, drawBarWidth);
}
// annotations?
AnnotationsProvider::ConstAnnotationIterator it = m_ptrAnnotations->GetFirstAnnotationForFrame(frame);
AnnotationsProvider::ConstAnnotationIterator endIt = m_ptrAnnotations->GetEnd();
if (it != endIt)
{
painter.fillRect(actualLeftEdge + barWidthHalf, 0, 1, wrectHeight, m_ptrAnnotations->GetColorForChannel(it->GetChannelCRC()));
//++it;
}
--frame;
rightEdgeOfBar -= barWidth;
leftEdgeOfBar -= barWidth;
}
// Styling the budget markers.
// Mildly ugly, but not a lot of pixels to work with
// to make it look better.
pen = QPen(Qt::black);
pen.setWidth(1);
painter.setPen(pen);
brush.setStyle(Qt::BrushStyle::SolidPattern);
// Budget Markers
for (BudgetMarkerMap::value_type& mapPair : m_budgetMarkers)
{
BudgetMarker& budgetMarker = mapPair.second;
QColor drawColor = budgetMarker.GetColor();
QRect sizeRect = rect();
int x = rect().left();
float normalizedValue = ((budgetMarker.GetValue() + 1.0f) / 2.0f);
int y = static_cast<int>(rect().bottom() - (rect().height() * normalizedValue));
int width = rect().width();
int height = 4;
brush.setColor(drawColor);
// Want to make sure we always draw the entire box.
if (y > rect().bottom() - height)
{
y = rect().bottom() - height;
}
painter.fillRect(
x,
y,
width,
height,
brush);
painter.drawRect(
x,
y,
width,
height);
}
}
}
void ChannelDataView::mouseMoveEvent(QMouseEvent* event)
{
if (m_Channel->IsInCaptureMode(CaptureMode::Capturing))
{
return;
}
if (m_Channel->IsActive())
{
if (m_mouseGrabbed)
{
if (m_shouldIgnorePoint && event->globalPos() == m_centerPoint)
{
m_shouldIgnorePoint = false;
return;
}
QPoint mousePoint = event->globalPos();
int mouseDelta = (mousePoint.x() - m_centerPoint.x());
if (m_initializeDrag)
{
m_initializeDrag = false;
m_dragInitialized = true;
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
QRect screenGeometry = QApplication::primaryScreen()->geometry();
m_centerPoint = screenGeometry.center();
mouseDelta = (mousePoint.x() - mapToGlobal(m_simulatedPoint).x());
}
m_simulatedPoint.setX(m_simulatedPoint.x() + mouseDelta);
QPoint framePoint = m_simulatedPoint;
framePoint.setX(framePoint.x() + m_xOffset);
FrameNumberType frame = PositionToFrame(framePoint);
FrameNumberType framesPerPixel = FramesPerPixel();
// raw frame, sanitized by the controller
emit InformOfMouseMove(frame, framesPerPixel, event->modifiers());
event->ignore();
QRect boundingRect = rect();
m_shouldIgnorePoint = true;
QCursor::setPos(m_centerPoint);
m_xOffset += mouseDelta;
if (!boundingRect.contains(m_simulatedPoint))
{
m_simulatedPoint.setX(AZ::GetClamp(m_simulatedPoint.x(), boundingRect.left(), boundingRect.right()));
}
else
{
m_xOffset = 0;
}
if (m_lastFrame != frame)
{
m_lastFrame = frame;
if (framesPerPixel == 1)
{
int barWidth = static_cast<int>(ceilf(GetBarWidth()));
m_xOffset %= barWidth;
}
else
{
m_xOffset = 0;
}
}
// Can't scroll beyond the minimum frame.
if (m_lastFrame == 0 && m_xOffset < 0)
{
m_xOffset = 0;
}
// Can't scroll beyond the maximum frame.
else if (m_lastFrame == m_Channel->m_State.m_EndFrame && m_xOffset > 0)
{
m_xOffset = 0;
}
}
// If we aren't dragging, we need to deal with highlighting
else
{
QPoint pos = event->localPos().toPoint();
// This guy expects relative position to parent. Not local space.
FrameNumberType frameNumber = PositionToFrame(event->pos());
FramePointMapping::iterator dataPointIter = m_graphedPoints.find(frameNumber);
if (dataPointIter != m_graphedPoints.end())
{
if (frameNumber != m_highlightedFrame)
{
RemoveHighlight();
}
bool needsUpdate = false;
DataPointList& dataPointList = dataPointIter->second;
AggregatorDataPoint* overlayPoint = nullptr;
for (AggregatorDataPoint& dataPoint : dataPointList)
{
if (dataPoint.ContainsPoint(pos))
{
m_highlightedFrame = frameNumber;
overlayPoint = &dataPoint;
}
else if (dataPoint.SetOverlayEnabled(false))
{
needsUpdate = true;
}
}
if (overlayPoint)
{
if (overlayPoint->SetOverlayEnabled(true))
{
needsUpdate = true;
}
}
else
{
m_highlightedFrame = -1;
}
if (needsUpdate)
{
update();
}
}
else
{
RemoveHighlight();
}
}
}
}
void ChannelDataView::mousePressEvent(QMouseEvent* event)
{
if (!m_Channel->IsInCaptureMode(CaptureMode::Capturing) && m_Channel->IsActive())
{
emit InformOfMouseClick(event->button(), PositionToFrame(event->pos()), FramesPerPixel(), event->modifiers());
event->ignore();
// Only want to perform dragging actions on left clicks.
if (event->button() == Qt::LeftButton)
{
grabMouse();
m_initializeDrag = true;
m_simulatedPoint = event->pos();
m_mouseGrabbed = true;
m_xOffset = 0;
m_lastFrame = m_Channel->m_State.m_ScrubberFrame;
}
}
}
void ChannelDataView::mouseReleaseEvent(QMouseEvent* event)
{
if (!m_Channel->IsInCaptureMode(CaptureMode::Capturing) && m_Channel->IsActive())
{
if (m_mouseGrabbed)
{
FrameNumberType frame = PositionToFrame(m_simulatedPoint);
emit InformOfMouseRelease(event->button(), frame, FramesPerPixel(), event->modifiers());
event->ignore();
m_mouseGrabbed = false;
if (m_dragInitialized)
{
m_shouldIgnorePoint = true;
QCursor::setPos(mapToGlobal(m_simulatedPoint));
QApplication::restoreOverrideCursor();
}
releaseMouse();
}
}
}
void ChannelDataView::wheelEvent(QWheelEvent* event)
{
if (event->angleDelta().y() == 0)
{
event->accept();
return;
}
emit InformOfMouseWheel(PositionToFrame(event->position().toPoint()), event->angleDelta().y(), FramesPerPixel(), event->modifiers());
event->ignore();
}
void ChannelDataView::leaveEvent(QEvent* event)
{
(void)event;
RemoveHighlight();
}
void ChannelDataView::DirtyGraphData()
{
m_dirtyGraph = true;
}
void ChannelDataView::RefreshGraphData()
{
for (ChannelProfilerWidget* profilerWidget : m_Channel->GetProfilers())
{
profilerWidget->GetAggregator()->AnnotateChannelView(this);
}
m_graphedPoints.clear();
DirtyGraphData();
update();
}
BudgetMarkerTicket ChannelDataView::AddBudgetMarker(float value, QColor color)
{
++m_budgetMarkerCounter;
if (m_budgetMarkerCounter == 0 || m_budgetMarkers.find(m_budgetMarkerCounter) != m_budgetMarkers.end())
{
BudgetMarkerTicket startTicket = m_budgetMarkerCounter;
do
{
++m_budgetMarkerCounter;
} while ((m_budgetMarkerCounter == 0 || m_budgetMarkers.find(m_budgetMarkerCounter) != m_budgetMarkers.end()) && m_budgetMarkerCounter != startTicket);
AZ_Assert(m_budgetMarkers.find(m_budgetMarkerCounter) == m_budgetMarkers.end(),"Ran out of tickets inside of budget marker creation.");
}
m_budgetMarkers.insert(BudgetMarkerMap::value_type(m_budgetMarkerCounter, BudgetMarker(value, color)));
return m_budgetMarkerCounter;
}
void ChannelDataView::RemoveBudgetMarker(BudgetMarkerTicket ticket)
{
m_budgetMarkers.erase(ticket);
}
void ChannelDataView::resizeEvent(QResizeEvent* newSize)
{
QWidget::resizeEvent(newSize);
// Graph data relies on the size of the graph, so we need to update it whenever we resize.
DirtyGraphData();
}
void ChannelDataView::RecalculateGraphedPoints()
{
FrameNumberType frame = m_Channel->m_State.m_FrameOffset + m_Channel->m_State.m_FramesInView - 1;
frame = AZ::GetMin(frame, m_Channel->m_State.m_EndFrame);
QRect wrect = rect();
int wrectHeight = wrect.height() / (m_Channel->m_State.m_ContractedHeight ? 2 : 1);
float barWidth = GetBarWidth();
if (m_Channel->m_State.m_EndFrame)
{
float vRange = (float)(wrectHeight - k_barHeight);
float half = vRange / 2.0f;
float rectBarWidth = (barWidth < 1.0f ? 1.0f : barWidth);
float rightEdgeOfBar = (float)wrect.right();
float leftEdgeOfBar = rightEdgeOfBar - barWidth;
FrameNumberType newMax = frame;
while (frame >= 0 && rightEdgeOfBar >= wrect.left())
{
// If the frame is a new frame we need to parse it's data.
if (m_graphedPoints.find(frame) == m_graphedPoints.end())
{
int actualLeftEdge = (int)floorf(leftEdgeOfBar);
int actualWidth = (int)floorf(rectBarWidth - 1.0f);
if (actualWidth < 1)
{
actualWidth = 1;
}
AZStd::list<AggregatorDataPoint> dataPoints;
for (ChannelProfilerWidget* currentProfiler : m_Channel->GetProfilers())
{
float vaf = currentProfiler->GetAggregator()->ValueAtFrame(frame);
int topOfBar = (int)(half - (vaf * half));
QRect drawRect(actualLeftEdge, topOfBar, actualWidth, k_barHeight);
dataPoints.emplace_back(drawRect, currentProfiler);
}
while (!dataPoints.empty())
{
AggregatorDataPoint currentPoint = dataPoints.front();
dataPoints.pop_front();
bool intersected = false;
AZStd::list<AggregatorDataPoint>::iterator aggregatorIter = dataPoints.begin();
do
{
intersected = false;
aggregatorIter = dataPoints.begin();
while (aggregatorIter != dataPoints.end())
{
if (currentPoint.IntersectsDataPoint((*aggregatorIter)))
{
intersected = true;
currentPoint.AddAggregatorDataPoint((*aggregatorIter));
aggregatorIter = dataPoints.erase(aggregatorIter);
}
else
{
++aggregatorIter;
}
}
} while (intersected);
m_graphedPoints[frame].push_back(currentPoint);
}
}
--frame;
rightEdgeOfBar -= barWidth;
leftEdgeOfBar -= barWidth;
}
FrameNumberType newMin = AZ::GetMax(0, frame + 1);
if (m_minFrame >= 0)
{
while (m_minFrame < newMin)
{
m_graphedPoints.erase(m_minFrame);
++m_minFrame;
}
}
m_minFrame = newMin;
if (m_maxFrame >= 0)
{
while (m_maxFrame >= 0 && m_maxFrame > newMax)
{
m_graphedPoints.erase(m_maxFrame);
--m_maxFrame;
}
}
m_maxFrame = newMax;
}
else
{
m_minFrame = -1;
m_maxFrame = -1;
m_graphedPoints.clear();
}
}
void ChannelDataView::RemoveHighlight()
{
if (m_highlightedFrame >= 0)
{
FramePointMapping::iterator dataPointIter = m_graphedPoints.find(m_highlightedFrame);
if (dataPointIter != m_graphedPoints.end())
{
bool needsUpdate = false;
DataPointList& dataPointList = dataPointIter->second;
for (AggregatorDataPoint& dataPoint : dataPointList)
{
needsUpdate = dataPoint.SetOverlayEnabled(false) || needsUpdate;
}
if (needsUpdate)
{
update();
}
}
m_highlightedFrame = -1;
}
}
}
@@ -1,162 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CHANNEL_DATA_VIEW_H
#define CHANNEL_DATA_VIEW_H
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/containers/unordered_map.h>
#include <QtWidgets/QWidget>
#include "Source/Driller/DrillerDataTypes.h"
#endif
namespace Driller
{
class AnnotationsProvider;
class ChannelControl;
class ChannelDataView;
class ChannelProfilerWidget;
class Aggregator;
class AggregatorDataPoint
{
public:
AggregatorDataPoint();
AggregatorDataPoint(QRect rectangle,ChannelProfilerWidget* profiler);
void Draw(QPainter& painter, int leftEdge, float barWidth);
bool ContainsPoint(const QPoint& point);
bool IntersectsDataPoint(const AggregatorDataPoint& dataPoint);
void AddAggregatorDataPoint(const AggregatorDataPoint& dataPoint);
bool SetOverlayEnabled(bool enabled);
private:
bool m_isActive;
bool m_isShowingOverlay;
bool m_shouldOutline;
QColor m_drawColor;
QRect m_visualBlock;
AZStd::unordered_set< ChannelProfilerWidget* > m_combinedProfilers;
};
class BudgetMarker
{
public:
BudgetMarker(float value, QColor& drawColor);
~BudgetMarker();
float GetValue() const;
const QColor& GetColor() const;
private:
float m_value;
QColor m_drawColor;
};
typedef unsigned int BudgetMarkerTicket;
/*
Channel Data View handles all rendering of the scrolling data graph.
To do this it caches pointer access to its owning Channel
and pulls state information directly from there.
Mouse events are passed upwards to its owning Channel via signal/slot.
*/
class ChannelDataView : public QWidget
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(ChannelDataView, AZ::SystemAllocator, 0);
ChannelDataView( QWidget* parent = nullptr );
virtual ~ChannelDataView();
void RegisterToChannel(ChannelControl* channel, AnnotationsProvider* annotations);
QPainter *m_Painter;
ChannelControl *m_Channel;
AnnotationsProvider *m_ptrAnnotations;
int FrameToPosition(FrameNumberType frameNumber);
FrameNumberType PositionToFrame( const QPoint &pt );
FrameNumberType FramesPerPixel();
float GetBarWidth();
virtual void paintEvent( QPaintEvent *event );
virtual void mouseMoveEvent( QMouseEvent *event );
virtual void mousePressEvent( QMouseEvent *event );
virtual void mouseReleaseEvent( QMouseEvent *event );
virtual void wheelEvent(QWheelEvent *event);
virtual void leaveEvent( QEvent* event );
void DirtyGraphData();
void RefreshGraphData();
BudgetMarkerTicket AddBudgetMarker(float value, QColor color);
void RemoveBudgetMarker(BudgetMarkerTicket ticket);
signals:
void InformOfMouseClick(Qt::MouseButton button, FrameNumberType frame, FrameNumberType range, int modifiers );
void InformOfMouseMove( FrameNumberType frame, FrameNumberType range, int modifiers );
void InformOfMouseRelease(Qt::MouseButton button, FrameNumberType frame, FrameNumberType range, int modifiers );
void InformOfMouseWheel( FrameNumberType frame, int wheelAmount, FrameNumberType range, int modifiers );
protected:
void resizeEvent(QResizeEvent* event) override;
private:
void RecalculateGraphedPoints();
void RemoveHighlight();
typedef AZStd::list< AggregatorDataPoint > DataPointList;
typedef AZStd::unordered_map<FrameNumberType, DataPointList > FramePointMapping;
typedef AZStd::unordered_map<BudgetMarkerTicket, BudgetMarker > BudgetMarkerMap;
BudgetMarkerTicket m_budgetMarkerCounter;
BudgetMarkerMap m_budgetMarkers;
FramePointMapping m_graphedPoints;
AZStd::list< ChannelProfilerWidget* > m_profilerWidgets;
FrameNumberType m_minFrame;
FrameNumberType m_maxFrame;
FrameNumberType m_highlightedFrame;
FrameNumberType m_lastFrame;
int m_xOffset;
bool m_initializeDrag;
bool m_dragInitialized;
bool m_shouldIgnorePoint;
QPoint m_simulatedPoint;
QPoint m_centerPoint;
bool m_mouseGrabbed;
bool m_dirtyGraph;
};
}
#endif // CHANNEL_DATA_VIEW_H
@@ -1,292 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "Source/Driller/ChannelProfilerWidget.hxx"
#include <Source/Driller/moc_ChannelProfilerWidget.cpp>
#include "Source/Driller/DrillerAggregator.hxx"
#include "Source/Driller/ChannelControl.hxx"
#include "Source/Driller/CollapsiblePanel.hxx"
#include "Source/Driller/CustomizeCSVExportWidget.hxx"
#include "Source/Driller/DrillerOperationTelemetryEvent.h"
#include <QPainter>
#include <QColor>
#include <QFileDialog>
namespace Driller
{
void ColorizeIcon(QIcon& icon, const char* iconPath, Aggregator* aggregator)
{
QImage alphaImage(iconPath);
alphaImage = alphaImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
QImage colorizedImage(alphaImage.width(), alphaImage.height(), QImage::Format_ARGB32_Premultiplied);
QColor color = aggregator->GetColor();
color.setAlphaF(1.0f);
QPainter painter;
painter.begin(&colorizedImage);
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.fillRect(colorizedImage.rect(), color);
painter.end();
colorizedImage.setAlphaChannel(alphaImage);
icon.addPixmap(QPixmap::fromImage(colorizedImage));
}
ChannelProfilerWidget::ChannelProfilerWidget(ChannelControl* channelControl, Aggregator* aggregator)
: QWidget(channelControl)
, m_channelControl(channelControl)
, m_drilledWidget(nullptr)
, m_aggregator(aggregator)
, m_captureMode(CaptureMode::Unknown)
, m_isActive(true)
, m_activeIcon()
, m_inactiveIcon()
{
setupUi(this);
connect(this, SIGNAL(DrillDownRequest(FrameNumberType)), m_aggregator, SLOT(DrillDownRequest(FrameNumberType)));
connect(this, SIGNAL(ExportToCSVRequest(const char*, CSVExportSettings*)), m_aggregator, SLOT(ExportToCSVRequest(const char*, CSVExportSettings*)));
connect(m_aggregator, SIGNAL(GetInspectionFileName()), m_channelControl, SIGNAL(GetInspectionFileName()));
connect(profilerName, SIGNAL(clicked()), this, SLOT(OnActivationToggled()));
connect(enableChannel, SIGNAL(clicked()), this, SLOT(OnActivationToggled()));
//connect(channelOptions,SIGNAL(clicked()),this, SLOT(OnConfigureChannel()));
connect(drillDown, SIGNAL(clicked()), this, SLOT(OnDrillDown()));
connect(exportData, SIGNAL(clicked()), this, SLOT(OnExportToCSV()));
ColorizeIcon(m_activeIcon, ":/driller/active_color_swatch", aggregator);
ColorizeIcon(m_inactiveIcon, ":/driller/inactive_color_swatch", aggregator);
profilerName->setToolTip(aggregator->GetToolTip());
profilerName->setText(GetName());
UpdateActivationIcon();
ConfigureUI();
}
ChannelProfilerWidget::~ChannelProfilerWidget()
{
}
Aggregator* ChannelProfilerWidget::GetAggregator() const
{
return m_aggregator;
}
bool ChannelProfilerWidget::IsActive() const
{
return m_isActive;
}
void ChannelProfilerWidget::SetIsActive(bool isActive)
{
if (m_isActive != isActive)
{
m_isActive = isActive;
UpdateActivationIcon();
if (m_aggregator && m_captureMode == Driller::CaptureMode::Configuration)
{
m_aggregator->EnableCapture(isActive);
}
emit OnActivationChanged(this, m_isActive);
}
}
QString ChannelProfilerWidget::GetName() const
{
return (m_aggregator ? m_aggregator->GetName() : "Unknown Profiler");
}
AZ::Uuid ChannelProfilerWidget::GetID()
{
return (m_aggregator ? m_aggregator->GetID() : AZ::Uuid::CreateNull());
}
ChannelConfigurationWidget* ChannelProfilerWidget::CreateConfigurationWidget()
{
ChannelConfigurationWidget* configurationWidget = nullptr;
if (m_aggregator)
{
configurationWidget = m_aggregator->CreateConfigurationWidget();
}
return configurationWidget;
}
void ChannelProfilerWidget::OnActivationToggled()
{
SetIsActive(!IsActive());
}
void ChannelProfilerWidget::SetCaptureMode(CaptureMode captureMode)
{
if (m_captureMode != captureMode)
{
m_captureMode = captureMode;
ConfigureUI();
}
}
void ChannelProfilerWidget::OnDrillDown()
{
if (m_channelControl && IsInCaptureMode(CaptureMode::Inspecting))
{
char traceStr[AZ::Uuid::MaxStringBuffer];
m_aggregator->GetID().ToString(traceStr, AZ_ARRAY_SIZE(traceStr));
AZ_TracePrintf("Driller", "Drill Down ID = %s\n", traceStr);
if (m_drilledWidget == nullptr)
{
m_drilledWidget = emit DrillDownRequest(m_channelControl->m_State.m_ScrubberFrame);
if (m_drilledWidget)
{
connect(m_drilledWidget, SIGNAL(destroyed(QObject*)), this, SLOT(OnDrillDestroyed(QObject*)));
emit OnSuccessfulDrillDown(m_drilledWidget);
}
}
else
{
if (m_drilledWidget->isMinimized())
{
m_drilledWidget->showNormal();
}
m_drilledWidget->raise();
m_drilledWidget->activateWindow();
}
}
}
void ChannelProfilerWidget::OnDrillDestroyed(QObject* widget)
{
if (widget == m_drilledWidget)
{
m_drilledWidget = nullptr;
}
}
void ChannelProfilerWidget::OnExportToCSV()
{
DrillerOperationTelemetryEvent exportToCSVEvent;
exportToCSVEvent.SetAttribute("ExportToCSV", m_aggregator->GetName().toStdString().c_str());
exportToCSVEvent.Log();
char traceStr[AZ::Uuid::MaxStringBuffer];
m_aggregator->GetID().ToString(traceStr, AZ_ARRAY_SIZE(traceStr));
AZ_TracePrintf("Driller", "Export Request for ID = %s\n", traceStr);
QFileDialog fileDialog;
CustomizeCSVExportWidget* customizeWidget = m_aggregator->CreateCSVExportCustomizationWidget();
// Always use the Qt dialog for consistency
fileDialog.setOption(QFileDialog::DontUseNativeDialog, true);
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
fileDialog.setWindowTitle(QString("Export %1 To CSV").arg(m_aggregator->GetName()));
fileDialog.setNameFilter("CSV (*.csv)");
fileDialog.setDefaultSuffix("csv");
if (customizeWidget)
{
CollapsiblePanel* collapsiblePanel = aznew CollapsiblePanel(&fileDialog);
collapsiblePanel->SetTitle("Customize");
collapsiblePanel->SetContent(customizeWidget);
// I don't know why the decided to use a GridLayout here instead of nested layouts. But whatever.
QGridLayout* gridLayout = static_cast<QGridLayout*>(fileDialog.layout());
int numRows = gridLayout->rowCount();
gridLayout->addWidget(collapsiblePanel, numRows, 0, 1, gridLayout->columnCount());
}
QString fileName;
if (fileDialog.exec())
{
QStringList fileList = fileDialog.selectedFiles();
for (const QString& file : fileList)
{
if (!file.isEmpty())
{
fileName = file;
break;
}
}
}
if (!fileName.isEmpty())
{
CSVExportSettings* exportSettings = nullptr;
if (customizeWidget)
{
customizeWidget->FinalizeSettings();
exportSettings = customizeWidget->GetExportSettings();
}
emit ExportToCSVRequest(fileName.toStdString().c_str(), exportSettings);
}
}
bool ChannelProfilerWidget::AllowCSVExport() const
{
return (m_aggregator ? m_aggregator->CanExportToCSV() : false);
}
void ChannelProfilerWidget::UpdateActivationIcon()
{
if (IsActive())
{
enableChannel->setIcon(m_activeIcon);
}
else
{
enableChannel->setIcon(m_inactiveIcon);
}
}
bool ChannelProfilerWidget::IsInCaptureMode(CaptureMode captureMode) const
{
return m_captureMode == captureMode;
}
void ChannelProfilerWidget::ConfigureUI()
{
switch (m_captureMode)
{
case CaptureMode::Configuration:
enableChannel->setEnabled(true);
drillDown->setVisible(false);
exportData->setVisible(false);
break;
case CaptureMode::Capturing:
enableChannel->setEnabled(false);
drillDown->setVisible(false);
exportData->setVisible(false);
break;
case CaptureMode::Inspecting:
enableChannel->setEnabled(true);
drillDown->setVisible(true);
exportData->setVisible(AllowCSVExport());
exportData->setEnabled(AllowCSVExport());
break;
default:
break;
}
}
}
@@ -1,92 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#ifndef DRILLER_CHANNELPROFILERWIDGET_H
#define DRILLER_CHANNELPROFILERWIDGET_H
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <QtWidgets/QWidget>
#include "Source/Driller/DrillerDataTypes.h"
// Generated Files
#include <Source/Driller/ui_ChannelProfilerWidget.h>
#endif
namespace Driller
{
class Aggregator;
class ChannelControl;
class CSVExportSettings;
class ChannelConfigurationWidget;
class ChannelProfilerWidget
: public QWidget
, private Ui::ChannelProfilerWidget
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(ChannelProfilerWidget, AZ::SystemAllocator,0);
ChannelProfilerWidget(ChannelControl* channelControl, Aggregator* aggregator);
virtual ~ChannelProfilerWidget();
Aggregator* GetAggregator() const;
bool IsActive() const;
void SetIsActive(bool isActive);
QString GetName() const;
AZ::Uuid GetID();
ChannelConfigurationWidget* CreateConfigurationWidget();
public slots:
void OnActivationToggled();
void SetCaptureMode(CaptureMode mode);
void OnDrillDown();
void OnDrillDestroyed(QObject* drill);
void OnExportToCSV();
signals:
void OnActivationChanged(ChannelProfilerWidget*,bool activated);
QWidget* DrillDownRequest(FrameNumberType atFrame);
void ExportToCSVRequest(const char* filename, CSVExportSettings* customizeWidget);
void OnSuccessfulDrillDown(QWidget* widget);
private:
bool AllowCSVExport() const;
void UpdateActivationIcon();
bool IsInCaptureMode(CaptureMode captureMode) const;
void ConfigureUI();
ChannelControl* m_channelControl;
QWidget* m_drilledWidget;
Aggregator* m_aggregator;
CaptureMode m_captureMode;
bool m_isActive;
QImage m_inactiveImage;
QImage m_activeImage;
QIcon m_activeIcon;
QIcon m_inactiveIcon;
};
}
#endif
@@ -1,150 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChannelProfilerWidget</class>
<widget class="QWidget" name="ChannelProfilerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>374</width>
<height>28</height>
</rect>
</property>
<property name="windowTitle">
<string>ChannelProfilerWidget</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="enableChannel">
<property name="toolTip">
<string>Toggle this Profiler On/Off</string>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>12</width>
<height>12</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="AzToolsFramework::ClickableLabel" name="profilerName">
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="exportData">
<property name="toolTip">
<string>Save to CSV</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Resources/sharedResources.qrc">
<normaloff>:/driller/export_button_enabled</normaloff>
<disabledoff>:/driller/export_button_disabled</disabledoff>
<disabledon>:/driller/export_button_disabled</disabledon>:/driller/export_button_enabled</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="drillDown">
<property name="toolTip">
<string>Detailed Profiling Information</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Resources/sharedResources.qrc">
<normaloff>:/driller/drill_down_enabled</normaloff>
<disabledoff>:/driller/drill_down_disabled</disabledoff>
<disabledon>:/driller/drill_down_disabled</disabledon>:/driller/drill_down_enabled</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzToolsFramework::ClickableLabel</class>
<extends>QLabel</extends>
<header>AzToolsFramework/UI/UICore/ClickableLabel.hxx</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../../../Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Resources/sharedResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -1,52 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ChartNumberFormats.h"
namespace DrillerCharts
{
QString FriendlyFormat(AZ::s64 n)
{
QString str;
str = QString("%L1").arg(n);
//if (n < 0)
//{
// str = "-";
//}
//AZ::s64 b = n / 1000000000l;
//n %= 1000000000l;
//AZ::s64 m = n / 1000000l;
//n %= 1000000l;
//AZ::s64 k = n / 1000l;
//n %= 1000l;
//if (b)
//{
// str += QString("%0b").arg(abs(b));
//}
//if (m)
//{
// str += QString("%0m").arg(abs(m));
//}
//if (k)
//{
// str += QString("%0k").arg(abs(k));
//}
//if (n)
//{
// str += QString("%0").arg(abs(n));
//}
return str;
}
}
@@ -1,22 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CHARTNUMBERFORMATS_H
#define CHARTNUMBERFORMATS_H
#include <AzCore/base.h>
#include <QString>
#pragma once
namespace DrillerCharts
{
QString FriendlyFormat(AZ::s64 n);
}
#endif //CHARTNUMBERFORMATS_H

Some files were not shown because too many files have changed in this diff Show More