Refactor budget definitions as named functions instead of template specializations

Signed-off-by: Jeremy Ong <jcong@amazon.com>
monroegm-disable-blank-issue-2
Jeremy Ong 4 years ago
parent 5e04c3737f
commit c37c0cab08

@ -31,6 +31,7 @@
#include <QMessageBox>
#include <QPushButton>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/smart_ptr/make_shared.h>

@ -13,6 +13,7 @@
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Serialization/SerializeContext.h>
ImporterRootDisplay::ImporterRootDisplay(AZ::SerializeContext* serializeContext, QWidget* parent)

@ -545,6 +545,8 @@ namespace AZ
m_entityActivatedEvent.DisconnectAllHandlers();
m_entityDeactivatedEvent.DisconnectAllHandlers();
m_budgetTracker.Reset();
DestroyAllocator();
}

@ -11,8 +11,8 @@
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/BudgetTracker.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>
@ -395,6 +395,8 @@ namespace AZ
// from the m_console member when it goes out of scope
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle m_settingsRegistryConsoleFunctors;
Debug::BudgetTracker m_budgetTracker;
// 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
@ -402,8 +404,6 @@ namespace AZ
char m_commandLineBuffer[AZ_MAX_PATH_LEN];
char* m_commandLineBufferAddress{ m_commandLineBuffer };
AZ::Debug::BudgetTracker m_budgetTracker;
StartupParameters m_startupParameters;
char** m_argV{ nullptr };

@ -12,13 +12,14 @@
#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(Audio);
AZ_DEFINE_BUDGET(Animation);
AZ_DEFINE_BUDGET(Physics);
namespace AZ::Debug
{
@ -28,9 +29,9 @@ namespace AZ::Debug
// TODO: Budget implementation for tracking budget wall time per-core, memory, etc.
};
Budget::Budget(const char* name)
Budget::Budget(const char* name, uint32_t crc)
: m_name{ name }
, m_crc{ Crc32(name) }
, m_crc{ crc }
{
m_impl = aznew BudgetImpl;
}

@ -7,15 +7,9 @@
*/
#pragma once
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/Debug/BudgetTracker.h>
#pragma warning(push)
// This warning must be disabled because Budget::Get<T> may not have an implementation if this file is transitively included
// in a source file that doesn't actually have any budgets declared in scope (via AZ_DEFINE_BUDGET or AZ_DECLARE_BUDGET).
// In this situation, the warning about internal linkage without an implementation is benign because we know that this function
// cannot be invoked from that translation unit.
#pragma warning(disable: 5046)
#include <AzCore/Math/Crc.h>
#include <AzCore/std/parallel/atomic.h>
namespace AZ::Debug
{
@ -23,13 +17,7 @@ namespace AZ::Debug
class Budget final
{
public:
// If you encounter a linker error complaining that this function is not defined, you have likely forgotten to either
// define or declare the budget used in a profile or memory marker. See AZ_DEFINE_BUDGET and AZ_DECLARE_BUDGET below
// for usage.
template<typename BudgetType>
static Budget* Get();
explicit Budget(const char* name);
Budget(const char* name, uint32_t crc);
~Budget();
void PerFrameReset();
@ -54,11 +42,12 @@ namespace AZ::Debug
struct BudgetImpl* m_impl = nullptr;
};
} // namespace AZ::Debug
#pragma warning(pop)
// Budgets are registered and retrieved using the proxy type specialization of Budget::Get<T>. The type itself has no declaration/definition
// other than this forward type pointer declaration
#define AZ_BUDGET_PROXY_TYPE(name) class AzBudget##name*
// 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
// Usage example:
// In a single C++ source file:
@ -66,12 +55,10 @@ namespace AZ::Debug
//
// Anywhere the budget is used, the budget must be declared (either in a header or in the source file itself)
// AZ_DECLARE_BUDGET(AzCore);
//
// The budget is usable in the same file it was defined without needing an additional declaration
#define AZ_DEFINE_BUDGET(name) \
template<> \
::AZ::Debug::Budget* ::AZ::Debug::Budget::Get<AZ_BUDGET_PROXY_TYPE(name)>() \
::AZ::Debug::Budget* AZ_BUDGET_GETTER(name)() \
{ \
constexpr static uint32_t crc = AZ_CRC_CE(#name); \
static ::AZStd::atomic<::AZ::Debug::Budget*> budget; \
::AZ::Debug::Budget* out = budget.load(AZStd::memory_order_acquire); \
if (out) \
@ -80,22 +67,23 @@ namespace AZ::Debug
} \
else \
{ \
budget.store(&::AZ::Debug::BudgetTracker::GetBudgetFromEnvironment(#name), AZStd::memory_order_release); \
budget.store(::AZ::Debug::BudgetTracker::GetBudgetFromEnvironment(#name, crc), AZStd::memory_order_release); \
return budget; \
} \
}
// If using a budget defined in a different C++ source file, add AZ_DECLARE_BUDGET(yourBudget); somewhere in your source file at namespace
// scope Alternatively, AZ_DECLARE_BUDGET can be used in a header to declare the budget for use across any users of the header
#define AZ_DECLARE_BUDGET(name) extern template ::AZ::Debug::Budget* ::AZ::Debug::Budget::Get<AZ_BUDGET_PROXY_TYPE(name)>()
#define AZ_DECLARE_BUDGET(name) extern ::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);
AZ_DECLARE_BUDGET(Animation);

@ -10,6 +10,7 @@
#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>
@ -20,40 +21,54 @@ namespace AZ::Debug
struct BudgetTrackerImpl
{
AZ_CLASS_ALLOCATOR(BudgetTrackerImpl, AZ::SystemAllocator, 0);
AZStd::unordered_map<const char*, Budget> m_budgets;
};
Budget& BudgetTracker::GetBudgetFromEnvironment(const char* budgetName)
Budget* BudgetTracker::GetBudgetFromEnvironment(const char* budgetName, uint32_t crc)
{
return (*Environment::FindVariable<BudgetTracker*>(BudgetTrackerEnvName))->GetBudget(budgetName);
BudgetTracker* tracker = Interface<BudgetTracker>::Get();
if (tracker)
{
return &tracker->GetBudget(budgetName, crc);
}
return nullptr;
}
BudgetTracker::~BudgetTracker()
{
if (m_impl)
Reset();
}
bool BudgetTracker::Init()
{
if (Interface<BudgetTracker>::Get())
{
delete m_impl;
return false;
}
Interface<BudgetTracker>::Register(this);
m_impl = new BudgetTrackerImpl;
return true;
}
void BudgetTracker::Init()
void BudgetTracker::Reset()
{
AZ_Assert(!m_impl, "BudgetTracker::Init called more than once");
m_impl = aznew BudgetTrackerImpl;
m_envVar = Environment::CreateVariable<BudgetTracker*>(BudgetTrackerEnvName, this);
if (m_impl)
{
Interface<BudgetTracker>::Unregister(this);
delete m_impl;
m_impl = nullptr;
}
}
Budget& BudgetTracker::GetBudget(const char* budgetName)
Budget& BudgetTracker::GetBudget(const char* budgetName, uint32_t crc)
{
AZStd::scoped_lock lock{ m_mutex };
auto it = m_impl->m_budgets.find(budgetName);
if (it == m_impl->m_budgets.end())
{
it = m_impl->m_budgets.emplace(budgetName, budgetName).first;
it = m_impl->m_budgets.try_emplace(budgetName, budgetName, crc).first;
}
return it->second;

@ -9,6 +9,7 @@
#pragma once
#include <AzCore/Module/Environment.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/parallel/mutex.h>
namespace AZ::Debug
@ -18,17 +19,19 @@ namespace AZ::Debug
class BudgetTracker
{
public:
static Budget& GetBudgetFromEnvironment(const char* budgetName);
AZ_RTTI(BudgetTracker, "{E14A746D-BFFE-4C02-90FB-4699B79864A5}");
static Budget* GetBudgetFromEnvironment(const char* budgetName, uint32_t crc);
~BudgetTracker();
void Init();
// Returns false if the budget tracker was already present in the environment (initialized already elsewhere)
bool Init();
void Reset();
Budget& GetBudget(const char* budgetName);
Budget& GetBudget(const char* budgetName, uint32_t crc);
private:
AZStd::mutex m_mutex;
AZ::EnvironmentVariable<BudgetTracker*> m_envVar;
// 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

@ -29,15 +29,14 @@
#define AZ_PROFILE_SCOPE(budget, ...) \
::AZ::Debug::ProfileScope AZ_JOIN(azProfileScope, __LINE__) \
{ \
*::AZ::Debug::Budget::Get<AZ_BUDGET_PROXY_TYPE(budget)>(), __VA_ARGS__ \
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(budget, ...) \
::AZ::Debug::ProfileScope::BeginRegion(*::AZ::Debug::Budget::Get<AZ_BUDGET_PROXY_TYPE(budget)>(), __VA_ARGS__)
#define AZ_PROFILE_END(budget) ::AZ::Debug::ProfileScope::EndRegion(*::AZ::Debug::Budget::Get<AZ_BUDGET_PROXY_TYPE(budget)>())
#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
@ -64,44 +63,17 @@ namespace AZ::Debug
{
public:
template<typename... T>
static void BeginRegion([[maybe_unused]] Budget& budget, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args)
{
#if !defined(_RELEASE)
// TODO: Verification that the supplied system name corresponds to a known budget
#if defined(USE_PIX)
PIXBeginEvent(PIX_COLOR_INDEX(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
}
static void EndRegion([[maybe_unused]] Budget& budget)
{
#if !defined(_RELEASE)
budget.EndProfileRegion();
#if defined(USE_PIX)
PIXEndEvent();
#endif
#endif
}
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>
ProfileScope(Budget& budget, char const* eventName, T const&... args)
: m_budget{ budget }
{
BeginRegion(budget, eventName, args...);
}
ProfileScope(Budget* budget, char const* eventName, T const&... args);
~ProfileScope()
{
EndRegion(m_budget);
}
~ProfileScope();
private:
Budget& m_budget;
Budget* m_budget;
};
} // namespace AZ::Debug
@ -111,3 +83,5 @@ namespace AZ::Debug
#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

@ -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>

@ -102,6 +102,7 @@ set(FILES
Debug/IEventLogger.h
Debug/MemoryProfiler.h
Debug/Profiler.cpp
Debug/Profiler.inl
Debug/Profiler.h
Debug/ProfilerBus.h
Debug/StackTracer.h

@ -16,11 +16,14 @@
#ifndef AZFRAMEWORK_ENTITYCONTEXTBUS_H
#define AZFRAMEWORK_ENTITYCONTEXTBUS_H
#include <AzCore/Debug/Budget.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/ComponentBus.h>
AZ_DECLARE_BUDGET(AzFramework);
namespace AZ
{
class Entity;

@ -17,8 +17,6 @@
#include <AzCore/Component/ComponentBus.h>
#include "PropertyEditorAPI_Internals.h"
AZ_DECLARE_BUDGET(AzToolsFramework);
class QWidget;
class QCheckBox;
class QLabel;

@ -25,6 +25,8 @@ class QColor;
class QString;
class QPoint;
AZ_DECLARE_BUDGET(AzToolsFramework);
namespace AzToolsFramework
{
namespace Components

@ -8,7 +8,6 @@
set(FILES
test_Main.cpp
TestProfiler.cpp
Tests.h
Session.cpp
Serialize.cpp
@ -16,7 +15,6 @@ set(FILES
ReplicaSmall.cpp
ReplicaMedium.cpp
ReplicaBehavior.cpp
Replica.cpp
StreamSecureSocketDriverTests.cpp
StreamSocketDriverTests.cpp
CarrierStreamSocketDriverTests.cpp

@ -22,6 +22,8 @@
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/Debug/IEventLogger.h>
#include <AzCore/Interface/Interface.h>

@ -9,6 +9,7 @@
#include "CrySystem_precompiled.h"
#include "SystemEventDispatcher.h"
#include <AzCore/Debug/EventTrace.h>
CSystemEventDispatcher::CSystemEventDispatcher()
: m_listeners(0)

@ -13,6 +13,7 @@
#include <AzCore/Math/Color.h>
#endif // !AUDIO_RELEASE
#include <AzCore/Debug/Profiler.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <SoundCVars.h>

@ -17,8 +17,6 @@
#include <AzCore/std/bind/bind.h>
#include <AzCore/StringFunc/StringFunc.h>
AZ_DEFINE_BUDGET(Audio);
namespace Audio
{
extern CAudioLogger g_audioLogger;

@ -8,10 +8,13 @@
#pragma once
#include <AzCore/Debug/Budget.h>
#include <AzCore/RTTI/RTTI.h>
#include <NvCloth/Types.h>
AZ_DECLARE_BUDGET(Cloth);
namespace NvCloth
{
//! Interface that provides a set of functions to

@ -21,8 +21,6 @@
#define ENABLE_PHYSX_TIMESTEP_WARNING
#endif
AZ_DEFINE_BUDGET(Physics);
namespace PhysX
{
AZ_CLASS_ALLOCATOR_IMPL(PhysXSystem, AZ::SystemAllocator, 0);

@ -22,6 +22,8 @@
#if defined(SCRIPTEVENTS_EDITOR)
AZ_DECLARE_BUDGET(AzToolsFramework);
namespace ScriptEventsEditor
{
////////////////////////////

Loading…
Cancel
Save