Merge branch 'development' into cmake/linux_fix_warn_unused
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> # Conflicts: # Code/Legacy/CrySystem/Log.cpp # Code/Tools/Standalone/Source/Driller/Annotations/AnnotationHeaderView.cpp # Code/Tools/Standalone/Source/Driller/AreaChart.cpp # Code/Tools/Standalone/Source/Driller/AreaChart.hxx # Code/Tools/Standalone/Source/Driller/ChannelDataView.cpp # Code/Tools/Standalone/Source/Driller/DrillerCaptureWindow.cpp # Code/Tools/Standalone/Source/Driller/Profiler/ProfilerDataView.cpp
This commit is contained in:
@@ -10,7 +10,6 @@
|
||||
|
||||
// Component includes
|
||||
#include <AzCore/Asset/AssetManagerComponent.h>
|
||||
#include <AzCore/Debug/FrameProfilerComponent.h>
|
||||
#include <AzCore/IO/Streamer/StreamerComponent.h>
|
||||
#include <AzCore/Jobs/JobManagerComponent.h>
|
||||
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
|
||||
@@ -36,7 +35,6 @@ namespace AZ
|
||||
JsonSystemComponent::CreateDescriptor(),
|
||||
AssetManagerComponent::CreateDescriptor(),
|
||||
UserSettingsComponent::CreateDescriptor(),
|
||||
Debug::FrameProfilerComponent::CreateDescriptor(),
|
||||
SliceComponent::CreateDescriptor(),
|
||||
SliceSystemComponent::CreateDescriptor(),
|
||||
SliceMetadataInfoComponent::CreateDescriptor(),
|
||||
|
||||
@@ -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>
|
||||
@@ -546,6 +545,10 @@ namespace AZ
|
||||
m_entityActivatedEvent.DisconnectAllHandlers();
|
||||
m_entityDeactivatedEvent.DisconnectAllHandlers();
|
||||
|
||||
#if !defined(_RELEASE)
|
||||
m_budgetTracker.Reset();
|
||||
#endif
|
||||
|
||||
DestroyAllocator();
|
||||
}
|
||||
|
||||
@@ -594,6 +597,10 @@ namespace AZ
|
||||
CreateOSAllocator();
|
||||
CreateSystemAllocator();
|
||||
|
||||
#if !defined(_RELEASE)
|
||||
m_budgetTracker.Init();
|
||||
#endif
|
||||
|
||||
// This can be moved to the ComponentApplication constructor if need be
|
||||
// This is reading the *.setreg files using SystemFile and merging the settings
|
||||
// to the settings registry.
|
||||
@@ -625,8 +632,6 @@ namespace AZ
|
||||
m_eventLogger->Start(outputPath.Native(), baseFileName);
|
||||
}
|
||||
|
||||
CreateDrillers();
|
||||
|
||||
Sfmt::Create();
|
||||
|
||||
CreateReflectionManager();
|
||||
@@ -746,12 +751,6 @@ namespace AZ
|
||||
ComponentApplicationBus::Handler::BusDisconnect();
|
||||
TickRequestBus::Handler::BusDisconnect();
|
||||
|
||||
if (m_drillerManager)
|
||||
{
|
||||
Debug::DrillerManager::Destroy(m_drillerManager);
|
||||
m_drillerManager = nullptr;
|
||||
}
|
||||
|
||||
m_eventLogger->Stop();
|
||||
|
||||
// Clear the descriptor to deallocate all strings (owned by ModuleDescriptor)
|
||||
@@ -899,33 +898,6 @@ namespace AZ
|
||||
allocatorManager.FinalizeConfiguration();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// CreateDrillers
|
||||
// [2/20/2013]
|
||||
//=========================================================================
|
||||
void ComponentApplication::CreateDrillers()
|
||||
{
|
||||
// Create driller manager and register drillers if requested
|
||||
if (m_descriptor.m_enableDrilling)
|
||||
{
|
||||
m_drillerManager = Debug::DrillerManager::Create();
|
||||
// Memory driller is responsible for tracking allocations.
|
||||
// Tracking type and overhead is determined by app configuration.
|
||||
|
||||
// Only one MemoryDriller is supported at a time
|
||||
// Only create the memory driller if there is no handlers connected to the MemoryDrillerBus
|
||||
if (!Debug::MemoryDrillerBus::HasHandlers())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentApplication::MergeSettingsToRegistry(SettingsRegistryInterface& registry)
|
||||
{
|
||||
SettingsRegistryInterface::Specializations specializations;
|
||||
@@ -1416,10 +1388,6 @@ namespace AZ
|
||||
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
|
||||
}
|
||||
}
|
||||
if (m_drillerManager)
|
||||
{
|
||||
m_drillerManager->FrameUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Memory/AllocationRecords.h>
|
||||
#include <AzCore/Debug/BudgetTracker.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
#include <AzCore/Module/ModuleManager.h>
|
||||
@@ -225,11 +226,6 @@ namespace AZ
|
||||
/// Returns the path to the folder the executable is in.
|
||||
const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); }
|
||||
|
||||
|
||||
/// Returns pointer to the driller manager if it's enabled, otherwise NULL.
|
||||
Debug::DrillerManager* GetDrillerManager() override { return m_drillerManager; }
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// TickRequestBus
|
||||
float GetTickDeltaTime() override;
|
||||
@@ -324,9 +320,6 @@ namespace AZ
|
||||
/// Create the system allocator using the data in the m_descriptor
|
||||
void CreateSystemAllocator();
|
||||
|
||||
/// Create the drillers
|
||||
void CreateDrillers();
|
||||
|
||||
virtual void MergeSettingsToRegistry(SettingsRegistryInterface& registry);
|
||||
|
||||
//! Sets the specializations that will be used when loading the Settings Registry. Extend this in derived
|
||||
@@ -402,6 +395,10 @@ namespace AZ
|
||||
// from the m_console member when it goes out of scope
|
||||
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle m_settingsRegistryConsoleFunctors;
|
||||
|
||||
#if !defined(_RELEASE)
|
||||
Debug::BudgetTracker m_budgetTracker;
|
||||
#endif
|
||||
|
||||
// this is used when no argV/ArgC is supplied.
|
||||
// in order to have the same memory semantics (writable, non-const)
|
||||
// we create a buffer that can be written to (up to AZ_MAX_PATH_LEN) and then
|
||||
@@ -409,8 +406,6 @@ namespace AZ
|
||||
char m_commandLineBuffer[AZ_MAX_PATH_LEN];
|
||||
char* m_commandLineBufferAddress{ m_commandLineBuffer };
|
||||
|
||||
Debug::DrillerManager* m_drillerManager{ nullptr };
|
||||
|
||||
StartupParameters m_startupParameters;
|
||||
|
||||
char** m_argV{ nullptr };
|
||||
|
||||
@@ -187,11 +187,6 @@ namespace AZ
|
||||
//! @return a pointer to the name of the path that contains the application's executable.
|
||||
virtual const char* GetExecutableFolder() const = 0;
|
||||
|
||||
//! Returns a pointer to the driller manager, if driller is enabled.
|
||||
//! The driller manager manages all active driller sessions and driller factories.
|
||||
//! @return A pointer to the driller manager. If driller is not enabled, this function returns null.
|
||||
virtual Debug::DrillerManager* GetDrillerManager() = 0;
|
||||
|
||||
//! ResolveModulePath is called whenever LoadDynamicModule wants to resolve a module in order to actually load it.
|
||||
//! You can override this if you need to load modules from a different path or hijack module loading in some other way.
|
||||
//! If you do, ensure that you use platform-specific conventions to do so, as this is called by multiple platforms.
|
||||
|
||||
@@ -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,74 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
AZ_DEFINE_BUDGET(Animation);
|
||||
AZ_DEFINE_BUDGET(Audio);
|
||||
AZ_DEFINE_BUDGET(AzCore);
|
||||
AZ_DEFINE_BUDGET(Editor);
|
||||
AZ_DEFINE_BUDGET(Entity);
|
||||
AZ_DEFINE_BUDGET(Game);
|
||||
AZ_DEFINE_BUDGET(System);
|
||||
AZ_DEFINE_BUDGET(Physics);
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
struct BudgetImpl
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR(BudgetImpl, AZ::SystemAllocator, 0);
|
||||
// TODO: Budget implementation for tracking budget wall time per-core, memory, etc.
|
||||
};
|
||||
|
||||
Budget::Budget(const char* name)
|
||||
: m_name{ name }
|
||||
, m_crc{ Crc32(name) }
|
||||
{
|
||||
}
|
||||
|
||||
Budget::Budget(const char* name, uint32_t crc)
|
||||
: m_name{ name }
|
||||
, m_crc{ crc }
|
||||
{
|
||||
m_impl = aznew BudgetImpl;
|
||||
}
|
||||
|
||||
Budget::~Budget()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
delete m_impl;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:Budgets Methods below are stubbed pending future work to both update budget data and visualize it
|
||||
|
||||
void Budget::PerFrameReset()
|
||||
{
|
||||
}
|
||||
|
||||
void Budget::BeginProfileRegion()
|
||||
{
|
||||
}
|
||||
|
||||
void Budget::EndProfileRegion()
|
||||
{
|
||||
}
|
||||
|
||||
void Budget::TrackAllocation(uint64_t)
|
||||
{
|
||||
}
|
||||
|
||||
void Budget::UntrackAllocation(uint64_t)
|
||||
{
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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/BudgetTracker.h>
|
||||
#include <AzCore/Math/Crc.h>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
// A budget collates per-frame resource utilization and memory for a particular category
|
||||
class Budget final
|
||||
{
|
||||
public:
|
||||
explicit Budget(const char* name);
|
||||
Budget(const char* name, uint32_t crc);
|
||||
~Budget();
|
||||
|
||||
void PerFrameReset();
|
||||
void BeginProfileRegion();
|
||||
void EndProfileRegion();
|
||||
void TrackAllocation(uint64_t bytes);
|
||||
void UntrackAllocation(uint64_t bytes);
|
||||
|
||||
const char* Name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
uint32_t Crc() const
|
||||
{
|
||||
return m_crc;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
const uint32_t m_crc;
|
||||
struct BudgetImpl* m_impl = nullptr;
|
||||
};
|
||||
} // namespace AZ::Debug
|
||||
|
||||
// The budget is usable in the same file it was defined without needing an additional declaration.
|
||||
// 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.
|
||||
#define AZ_BUDGET_GETTER(name) GetAzBudget##name
|
||||
|
||||
#if defined(_RELEASE)
|
||||
#define AZ_DEFINE_BUDGET(name) \
|
||||
::AZ::Debug::Budget* AZ_BUDGET_GETTER(name)() \
|
||||
{ \
|
||||
return nullptr; \
|
||||
}
|
||||
#else
|
||||
// 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);
|
||||
#define AZ_DEFINE_BUDGET(name) \
|
||||
::AZ::Debug::Budget* AZ_BUDGET_GETTER(name)() \
|
||||
{ \
|
||||
constexpr static uint32_t crc = AZ_CRC_CE(#name); \
|
||||
static ::AZ::Debug::Budget* budget = ::AZ::Debug::BudgetTracker::GetBudgetFromEnvironment(#name, crc); \
|
||||
return budget; \
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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) ::AZ::Debug::Budget* AZ_BUDGET_GETTER(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(Animation);
|
||||
AZ_DECLARE_BUDGET(Audio);
|
||||
AZ_DECLARE_BUDGET(AzCore);
|
||||
AZ_DECLARE_BUDGET(Editor);
|
||||
AZ_DECLARE_BUDGET(Entity);
|
||||
AZ_DECLARE_BUDGET(Game);
|
||||
AZ_DECLARE_BUDGET(System);
|
||||
AZ_DECLARE_BUDGET(Physics);
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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/BudgetTracker.h>
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Debug/Budget.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
constexpr static const char* BudgetTrackerEnvName = "budgetTrackerEnv";
|
||||
|
||||
struct BudgetTracker::BudgetTrackerImpl
|
||||
{
|
||||
AZStd::unordered_map<const char*, Budget> m_budgets;
|
||||
};
|
||||
|
||||
Budget* BudgetTracker::GetBudgetFromEnvironment(const char* budgetName, uint32_t crc)
|
||||
{
|
||||
BudgetTracker* tracker = Interface<BudgetTracker>::Get();
|
||||
if (tracker)
|
||||
{
|
||||
return &tracker->GetBudget(budgetName, crc);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BudgetTracker::~BudgetTracker()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool BudgetTracker::Init()
|
||||
{
|
||||
if (Interface<BudgetTracker>::Get())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Interface<BudgetTracker>::Register(this);
|
||||
m_impl = new BudgetTrackerImpl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BudgetTracker::Reset()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
Interface<BudgetTracker>::Unregister(this);
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Budget& BudgetTracker::GetBudget(const char* budgetName, uint32_t crc)
|
||||
{
|
||||
AZStd::scoped_lock lock{ m_mutex };
|
||||
|
||||
auto it = m_impl->m_budgets.try_emplace(budgetName, budgetName, crc).first;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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/RTTI/RTTI.h>
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
class Budget;
|
||||
|
||||
class BudgetTracker
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(BudgetTracker, "{E14A746D-BFFE-4C02-90FB-4699B79864A5}");
|
||||
static Budget* GetBudgetFromEnvironment(const char* budgetName, uint32_t crc);
|
||||
|
||||
~BudgetTracker();
|
||||
|
||||
// Returns false if the budget tracker was already present in the environment (initialized already elsewhere)
|
||||
bool Init();
|
||||
void Reset();
|
||||
|
||||
Budget& GetBudget(const char* budgetName, uint32_t crc);
|
||||
|
||||
private:
|
||||
struct BudgetTrackerImpl;
|
||||
|
||||
AZStd::mutex m_mutex;
|
||||
|
||||
// The BudgetTracker is likely included in proportionally high number of files throughout the
|
||||
// engine, so indirection is used here to avoid imposing excessive recompilation in periods
|
||||
// while the budget system is iterated on.
|
||||
BudgetTrackerImpl* m_impl = nullptr;
|
||||
};
|
||||
} // 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 = ®
|
||||
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
|
||||
@@ -7,664 +7,4 @@
|
||||
*/
|
||||
|
||||
#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
|
||||
{
|
||||
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(§ion);
|
||||
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
|
||||
|
||||
@@ -7,42 +7,49 @@
|
||||
*/
|
||||
#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_BUDGET_GETTER(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_BUDGET_GETTER(budget)(), __VA_ARGS__)
|
||||
#define AZ_PROFILE_END(budget) ::AZ::Debug::ProfileScope::EndRegion(AZ_BUDGET_GETTER(budget)())
|
||||
|
||||
#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,340 +57,25 @@ namespace AZStd
|
||||
struct thread_id; // forward declare. This is the same type as AZStd::thread::id
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
class ProfileScope
|
||||
{
|
||||
public:
|
||||
static uint32_t GetSystemID(const char* system);
|
||||
template<typename... T>
|
||||
static void BeginRegion([[maybe_unused]] Budget* budget, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args);
|
||||
|
||||
static void EndRegion([[maybe_unused]] Budget* budget);
|
||||
|
||||
template<typename... T>
|
||||
static void BeginRegion([[maybe_unused]] const char* system, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args)
|
||||
{
|
||||
// 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...);
|
||||
#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
|
||||
}
|
||||
ProfileScope(Budget* budget, char const* eventName, T const&... args);
|
||||
|
||||
static void EndRegion()
|
||||
{
|
||||
#if defined(USE_PIX)
|
||||
PIXEndEvent();
|
||||
#endif
|
||||
}
|
||||
~ProfileScope();
|
||||
|
||||
template<typename... T>
|
||||
ProfileScope(const char* system, char const* eventName, T const&... args)
|
||||
{
|
||||
BeginRegion(system, eventName, args...);
|
||||
}
|
||||
|
||||
~ProfileScope()
|
||||
{
|
||||
EndRegion();
|
||||
}
|
||||
private:
|
||||
Budget* m_budget;
|
||||
};
|
||||
|
||||
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
|
||||
@@ -391,3 +83,5 @@ namespace AZ
|
||||
#undef LoadImage
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
|
||||
#include <AzCore/Debug/Profiler.inl>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
template<typename... T>
|
||||
void ProfileScope::BeginRegion(
|
||||
[[maybe_unused]] Budget* budget, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args)
|
||||
{
|
||||
if (!budget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#if !defined(_RELEASE)
|
||||
// TODO: Verification that the supplied system name corresponds to a known budget
|
||||
#if defined(USE_PIX)
|
||||
PIXBeginEvent(PIX_COLOR_INDEX(budget->Crc() & 0xff), eventName, args...);
|
||||
#endif
|
||||
budget->BeginProfileRegion();
|
||||
// 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
|
||||
}
|
||||
|
||||
inline void ProfileScope::EndRegion([[maybe_unused]] Budget* budget)
|
||||
{
|
||||
if (!budget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#if !defined(_RELEASE)
|
||||
budget->EndProfileRegion();
|
||||
#if defined(USE_PIX)
|
||||
PIXEndEvent();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
ProfileScope::ProfileScope(Budget* budget, char const* eventName, T const&... args)
|
||||
: m_budget{ budget }
|
||||
{
|
||||
BeginRegion(budget, eventName, args...);
|
||||
}
|
||||
|
||||
inline ProfileScope::~ProfileScope()
|
||||
{
|
||||
EndRegion(m_budget);
|
||||
}
|
||||
|
||||
} // namespace AZ::Debug
|
||||
@@ -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), ®);
|
||||
// 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), ®);
|
||||
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.");
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace AZ
|
||||
{
|
||||
inline namespace PlatformDefaults
|
||||
{
|
||||
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformAndroid, PlatformIOS, PlatformMac, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
|
||||
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformLinux, PlatformAndroid, PlatformIOS, PlatformMac, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
|
||||
|
||||
const char* PlatformIdToPalFolder(AZ::PlatformId platform)
|
||||
{
|
||||
@@ -27,6 +27,8 @@ namespace AZ
|
||||
{
|
||||
case AZ::PC:
|
||||
return "PC";
|
||||
case AZ::LINUX_ID:
|
||||
return "Linux";
|
||||
case AZ::ANDROID_ID:
|
||||
return "Android";
|
||||
case AZ::IOS:
|
||||
@@ -56,10 +58,14 @@ namespace AZ
|
||||
|
||||
const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform)
|
||||
{
|
||||
if (osPlatform == PlatformCodeNameWindows || osPlatform == PlatformCodeNameLinux)
|
||||
if (osPlatform == PlatformCodeNameWindows)
|
||||
{
|
||||
return PlatformPC;
|
||||
}
|
||||
if (osPlatform == PlatformCodeNameLinux)
|
||||
{
|
||||
return PlatformLinux;
|
||||
}
|
||||
else if (osPlatform == PlatformCodeNameMac)
|
||||
{
|
||||
return PlatformMac;
|
||||
@@ -201,6 +207,8 @@ namespace AZ
|
||||
{
|
||||
case PlatformId::PC:
|
||||
platformCodes.emplace_back(PlatformCodeNameWindows);
|
||||
break;
|
||||
case PlatformId::LINUX_ID:
|
||||
platformCodes.emplace_back(PlatformCodeNameLinux);
|
||||
break;
|
||||
case PlatformId::ANDROID_ID:
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace AZ
|
||||
inline namespace PlatformDefaults
|
||||
{
|
||||
constexpr char PlatformPC[] = "pc";
|
||||
constexpr char PlatformLinux[] = "linux";
|
||||
constexpr char PlatformAndroid[] = "android";
|
||||
constexpr char PlatformIOS[] = "ios";
|
||||
constexpr char PlatformMac[] = "mac";
|
||||
@@ -50,6 +51,7 @@ namespace AZ
|
||||
AZ_ENUM_WITH_UNDERLYING_TYPE(PlatformId, int,
|
||||
(Invalid, -1),
|
||||
PC,
|
||||
LINUX_ID,
|
||||
ANDROID_ID,
|
||||
IOS,
|
||||
MAC_ID,
|
||||
@@ -63,12 +65,13 @@ namespace AZ
|
||||
// Add new platforms above this
|
||||
NumPlatformIds
|
||||
);
|
||||
constexpr int NumClientPlatforms = 7;
|
||||
constexpr int NumClientPlatforms = 8;
|
||||
constexpr int NumPlatforms = NumClientPlatforms + 1; // 1 "Server" platform currently
|
||||
enum class PlatformFlags : AZ::u32
|
||||
{
|
||||
Platform_NONE = 0x00,
|
||||
Platform_PC = 1 << PlatformId::PC,
|
||||
Platform_LINUX = 1 << PlatformId::LINUX_ID,
|
||||
Platform_ANDROID = 1 << PlatformId::ANDROID_ID,
|
||||
Platform_IOS = 1 << PlatformId::IOS,
|
||||
Platform_MAC = 1 << PlatformId::MAC_ID,
|
||||
@@ -83,7 +86,7 @@ namespace AZ
|
||||
// A special platform that will always correspond to all non-server platforms, even if new ones are added
|
||||
Platform_ALL_CLIENT = 1ULL << 31,
|
||||
|
||||
AllNamedPlatforms = Platform_PC | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
|
||||
AllNamedPlatforms = Platform_PC | Platform_LINUX | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
|
||||
};
|
||||
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags);
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace UnitTest
|
||||
MOCK_CONST_METHOD0(GetAppRoot, const char* ());
|
||||
MOCK_CONST_METHOD0(GetEngineRoot, const char* ());
|
||||
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
|
||||
MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ());
|
||||
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));
|
||||
};
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/UnitTest/UnitTest.h>
|
||||
|
||||
#include <AzCore/Debug/BudgetTracker.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Driller/Driller.h>
|
||||
#include <AzCore/Memory/MemoryDriller.h>
|
||||
|
||||
@@ -93,20 +93,18 @@ set(FILES
|
||||
Debug/AssetTracking.h
|
||||
Debug/AssetTrackingTypesImpl.h
|
||||
Debug/AssetTrackingTypes.h
|
||||
Debug/Budget.h
|
||||
Debug/Budget.cpp
|
||||
Debug/BudgetTracker.h
|
||||
Debug/BudgetTracker.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.inl
|
||||
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 +570,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,6 +22,8 @@ namespace AZStd
|
||||
using std::exp2;
|
||||
using std::floor;
|
||||
using std::fmod;
|
||||
using std::llround;
|
||||
using std::lround;
|
||||
using std::pow;
|
||||
using std::round;
|
||||
using std::sin;
|
||||
|
||||
Reference in New Issue
Block a user