Profiler: Runtime region name support (#2924)

* Profiler: add support for runtime region names
* Profiler: fix group name collisions
* Profiler: use set of GroupRegionNames
* Profiler: add comments + named constant

Signed-off-by: Jacob Hilliard <jhlliar@amazon.com>
This commit is contained in:
Jacob Hilliard
2021-08-16 10:13:24 -07:00
committed by GitHub
parent 4f2d4d00ec
commit e2b4d8e502
3 changed files with 55 additions and 0 deletions
@@ -30,6 +30,12 @@ namespace AZ
const char* const m_groupName = nullptr;
const char* const m_regionName = nullptr;
struct Hash
{
AZStd::size_t operator()(const GroupRegionName& name) const;
};
bool operator==(const GroupRegionName& other) const;
};
CachedTimeRegion() = default;
@@ -95,6 +101,9 @@ namespace AZ
virtual void SetProfilerEnabled(bool enabled) = 0;
virtual bool IsProfilerEnabled() const = 0 ;
//! Used by AZ_ATOM_PROFILE_DYNAMIC to create GroupRegionNames with known lifetimes.
virtual const CachedTimeRegion::GroupRegionName& InsertDynamicName(const char* groupName, const AZStd::string& regionName) = 0;
};
} // namespace RPI
@@ -120,3 +129,10 @@ namespace AZ
#define AZ_ATOM_PROFILE_FUNCTION(groupName, regionName) \
AZ_TRACE_METHOD(); \
AZ_ATOM_PROFILE_TIME_GROUP_REGION(groupName, regionName) \
//! Macro that allows for region names to be submitted at runtime. Use sparingly - this acquires a lock and allocates new objects within a map.
#define AZ_ATOM_PROFILE_DYNAMIC(groupName, regionName) \
static_assert(AZStd::is_convertible_v<decltype(groupName), const char*>, "Runtime group names are not allowed, use a static string literal instead."); \
const AZ::RHI::CachedTimeRegion::GroupRegionName& AZ_JOIN(groupRegionName, __LINE__) = \
AZ::RHI::CpuProfiler::Get()->InsertDynamicName(groupName, regionName); \
AZ::RHI::TimeRegion AZ_JOIN(timeRegion, __LINE__)(&AZ_JOIN(groupRegionName, __LINE__));
@@ -13,6 +13,7 @@
#include <AzCore/Component/TickBus.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/shared_mutex.h>
#include <AzCore/std/smart_ptr/intrusive_refcount.h>
@@ -114,9 +115,11 @@ namespace AZ
bool IsContinuousCaptureInProgress() const final override;
void SetProfilerEnabled(bool enabled) final override;
bool IsProfilerEnabled() const final override;
const CachedTimeRegion::GroupRegionName& InsertDynamicName(const char* groupName, const AZStd::string& regionName) final override;
private:
static constexpr AZStd::size_t MaxFramesToSave = 2 * 60 * 120; // 2 minutes of 120fps
static constexpr AZStd::size_t MaxRegionStringPoolSize = 16384; // Max amount of unique strings to save in the pool before throwing warnings.
// Lazily create and register the local thread data
void RegisterThreadStorage();
@@ -129,6 +132,15 @@ namespace AZ
AZStd::vector<RHI::Ptr<CpuTimingLocalStorage>, AZ::OSStdAllocator> m_registeredThreads;
AZStd::mutex m_threadRegisterMutex;
// Pool for GroupRegionNames that are generated at runtime through AZ_ATOM_PROFILE_DYNAMIC. Each unique
// combination of group name and region name submitted will be stored in this pool to emulate static lifetime.
AZStd::unordered_set<CachedTimeRegion::GroupRegionName, CachedTimeRegion::GroupRegionName::Hash> m_dynamicGroupRegionNamePool;
// String pool for storing region names submitted at runtime. Each call to AZ_ATOM_PROFILE_DYNAMIC will either construct
// a string in this pool or use an already-existing entry.
AZStd::unordered_set<AZStd::string> m_regionNameStringPool;
AZStd::mutex m_dynamicNameMutex;
// Thread local storage, gets lazily allocated when a thread is created
static thread_local CpuTimingLocalStorage* ms_threadLocalStorage;
@@ -74,6 +74,20 @@ namespace AZ
{
}
AZStd::size_t CachedTimeRegion::GroupRegionName::Hash::operator()(const CachedTimeRegion::GroupRegionName& name) const
{
AZStd::size_t seed = 0;
AZStd::hash_combine(seed, name.m_groupName);
AZStd::hash_combine(seed, name.m_regionName);
return seed;
}
bool CachedTimeRegion::GroupRegionName::operator==(const GroupRegionName& other) const
{
return (m_groupName == other.m_groupName) && (m_regionName == other.m_regionName);
}
// --- CpuProfilerImpl ---
void CpuProfilerImpl::Init()
@@ -218,6 +232,19 @@ namespace AZ
return m_enabled;
}
const CachedTimeRegion::GroupRegionName& CpuProfilerImpl::InsertDynamicName(const char* groupName, const AZStd::string& regionName)
{
AZStd::scoped_lock lock(m_dynamicNameMutex);
AZ_Warning("CpuProfiler", m_regionNameStringPool.size() < MaxRegionStringPoolSize,
"Stored dynamic region names are accumulating. Consider removing a AZ_ATOM_PROFILE_DYNAMIC invocation.");
auto [regionNameItr, wasRegionInserted] = m_regionNameStringPool.insert(regionName);
CachedTimeRegion::GroupRegionName newGroupRegionName(groupName, regionNameItr->c_str());
auto [groupRegionNameItr, wasGroupRegionInserted] = m_dynamicGroupRegionNamePool.insert(newGroupRegionName);
return *groupRegionNameItr;
}
void CpuProfilerImpl::OnSystemTick()
{
if (!m_enabled)