Removes AssetMemoryAnalyzer that relies on the MemoryDrillerBus

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-12-01 19:08:12 -08:00
parent 95ed1015a5
commit c3f035c4e3
189 changed files with 41 additions and 82987 deletions
@@ -19,7 +19,6 @@
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/std/typetraits/is_base_of.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/IO/Streamer/FileRequest.h>
namespace AZ
@@ -581,7 +580,6 @@ namespace AZ
template<typename Bus>
using ConnectionPolicy = AssetConnectionPolicy<Bus>;
using EventProcessingPolicy = Debug::AssetTrackingEventProcessingPolicy<>;
//////////////////////////////////////////////////////////////////////////
virtual ~AssetEvents() {}
@@ -10,7 +10,6 @@
#include <AzCore/Asset/AssetManager_private.h>
#include <AzCore/Asset/AssetDataStream.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/IO/IStreamer.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Math/MathUtils.h>
@@ -164,8 +163,6 @@ namespace AZ::Data
AZ_PROFILE_SCOPE(AzCore, "AZ::Data::LoadAssetJob::Process: %s",
asset.GetHint().c_str());
AZ_ASSET_ATTACH_TO_SCOPE(this);
if (m_owner->ValidateAndRegisterAssetLoading(asset))
{
LoadAndSignal(asset);
@@ -200,7 +197,6 @@ namespace AZ::Data
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(cl_assetLoadDelay));
}
AZ_ASSET_NAMED_SCOPE(asset.GetHint().c_str());
bool loadedSuccessfully = false;
if (!cl_assetLoadError && m_requestState == AZ::IO::IStreamerTypes::RequestStatus::Completed)
@@ -982,7 +978,6 @@ namespace AZ::Data
}
AZ_PROFILE_SCOPE(AzCore, "GetAsset: %s", assetInfo.m_relativePath.c_str());
AZ_ASSET_NAMED_SCOPE("GetAsset: %s", assetInfo.m_relativePath.c_str());
AZStd::shared_ptr<AssetDataStream> dataStream;
AssetStreamInfo loadInfo;
@@ -16,7 +16,6 @@
#define AZCORE_COMPONENT_TICK_BUS_H
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/parallel/mutex.h> // For TickBus thread events.
#include <AzCore/Script/ScriptTimePoint.h>
@@ -112,10 +111,6 @@ namespace AZ
AZ_FORCE_INLINE bool operator()(TickEvents* left, TickEvents* right) const { return left->GetTickOrder() < right->GetTickOrder(); }
};
/**
* Enable tick bus to work with the AssetTracking
*/
using EventProcessingPolicy = Debug::AssetTrackingEventProcessingPolicy<>;
//////////////////////////////////////////////////////////////////////////
/**
@@ -217,10 +212,6 @@ namespace AZ
*/
typedef AZStd::mutex EventQueueMutexType;
/**
* Enable tick bus to work with the AssetTracking
*/
using EventProcessingPolicy = Debug::AssetTrackingEventProcessingPolicy<>;
//////////////////////////////////////////////////////////////////////////
/**
@@ -1,326 +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 "AssetTracking.h"
#include <AzCore/Debug/AssetTrackingTypes.h>
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Memory/HphaSchema.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/smart_ptr/make_shared.h>
namespace AZ::Debug
{
namespace
{
struct AssetTreeNode;
// Per-thread data that needs to be stored.
struct ThreadData
{
AZStd::vector<AssetTreeNodeBase*, AZStdAssetTrackingAllocator> m_currentAssetStack;
};
// Access thread data through a virtual function to ensure that the same thread-local data is being shared across DLLs.
// Otherwise, the thread_local variables are replicated across DLLs that link the AzCore library, and you'll get a
// different version in each module.
class ThreadDataProvider
{
public:
virtual ThreadData& GetThreadData() = 0;
};
}
class AssetTrackingImpl final :
public ThreadDataProvider
{
public:
AZ_TYPE_INFO(AssetTrackingImpl, "{01E2A099-3523-40BE-80E0-E0ADD861BEE1}");
AZ_CLASS_ALLOCATOR(AssetTrackingImpl, OSAllocator, 0);
AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
~AssetTrackingImpl();
void AssetBegin(const char* id, const char* file, int line);
void AssetAttach(void* otherAllocation, const char* file, int line);
void AssetEnd();
ThreadData& GetThreadData() override;
private:
static EnvironmentVariable<AssetTrackingImpl*>& GetEnvironmentVariable();
static AssetTrackingImpl* GetSharedInstance();
static ThreadData& GetSharedThreadData();
using PrimaryAssets = AZStd::unordered_map<AssetTrackingId, AssetPrimaryInfo, AZStd::hash<AssetTrackingId>, AZStd::equal_to<AssetTrackingId>, AZStdAssetTrackingAllocator>;
using ThreadData = ThreadData;
using mutex_type = AZStd::mutex;
using lock_type = AZStd::lock_guard<mutex_type>;
mutex_type m_mutex;
PrimaryAssets m_primaryAssets;
AssetTreeNodeBase* m_assetRoot = nullptr;
AssetAllocationTableBase* m_allocationTable = nullptr;
bool m_performingAnalysis = false;
friend class AssetTracking;
friend class AssetTracking::Scope;
};
///////////////////////////////////////////////////////////////////////////////
// AssetTrackingImpl methods
///////////////////////////////////////////////////////////////////////////////
AssetTrackingImpl::AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable) :
m_assetRoot(&assetTree->GetRoot()),
m_allocationTable(allocationTable)
{
AZ_Assert(!GetSharedInstance(), "Only one AssetTrackingImpl can exist!");
GetEnvironmentVariable().Set(this);
AllocatorManager::Instance().EnterProfilingMode();
}
AssetTrackingImpl::~AssetTrackingImpl()
{
AllocatorManager::Instance().ExitProfilingMode();
GetEnvironmentVariable().Reset();
}
void AssetTrackingImpl::AssetBegin(const char* id, const char* file, int line)
{
// In the future it may be desirable to organize assets based on where in code the asset was entered into.
// For now these are ignored.
AZ_UNUSED(file);
AZ_UNUSED(line);
using namespace Internal;
AssetTrackingId assetId(id);
auto& threadData = GetSharedThreadData();
AssetTreeNodeBase* parentAsset = threadData.m_currentAssetStack.empty() ? nullptr : threadData.m_currentAssetStack.back();
AssetTreeNodeBase* childAsset;
AssetPrimaryInfo* assetPrimaryInfo;
if (!parentAsset)
{
parentAsset = m_assetRoot;
}
{
lock_type lock(m_mutex);
// Locate or create the primary record for this asset
auto primaryItr = m_primaryAssets.find(assetId);
if (primaryItr != m_primaryAssets.end())
{
assetPrimaryInfo = &primaryItr->second;
}
else
{
auto insertResult = m_primaryAssets.emplace(assetId, AssetPrimaryInfo());
assetPrimaryInfo = &insertResult.first->second;
assetPrimaryInfo->m_id = &insertResult.first->first;
}
// Add this asset to the stack for this thread's context
childAsset = parentAsset->FindOrAddChild(assetId, assetPrimaryInfo);
}
threadData.m_currentAssetStack.push_back(childAsset);
}
void AssetTrackingImpl::AssetAttach(void* otherAllocation, const char* file, int line)
{
AZ_UNUSED(file);
AZ_UNUSED(line);
using namespace Internal;
AssetTreeNodeBase* assetInfo = m_allocationTable->FindAllocation(otherAllocation);
// We will push back a nullptr if there is no asset, this is necessary to balance the call to AssetEnd()
GetSharedThreadData().m_currentAssetStack.push_back(assetInfo);
}
void AssetTrackingImpl::AssetEnd()
{
AZ_Assert(!GetSharedThreadData().m_currentAssetStack.empty(), "AssetEnd() called without matching AssetBegin() or AssetAttach. Use the AZ_ASSET_NAMED_SCOPE and AZ_ASSET_ATTACH_TO_SCOPE macros to avoid this!");
GetSharedThreadData().m_currentAssetStack.pop_back();
}
AssetTrackingImpl* AssetTrackingImpl::GetSharedInstance()
{
auto environmentVariable = GetEnvironmentVariable();
if(environmentVariable)
{
return *environmentVariable;
}
return nullptr;
}
ThreadData& AssetTrackingImpl::GetSharedThreadData()
{
// Cast to the base type so our virtual call doesn't get optimized away. We require GetThreadData() to be executed in the same DLL every time.
return static_cast<ThreadDataProvider*>(GetSharedInstance())->GetThreadData();
}
AssetTrackingImpl::ThreadData& AssetTrackingImpl::GetThreadData()
{
static thread_local ThreadData* data = nullptr;
static thread_local typename AZStd::aligned_storage_t<sizeof(ThreadData), alignof(ThreadData)> storage;
if (!data)
{
data = new (&storage) ThreadData;
}
return *data;
}
EnvironmentVariable<AssetTrackingImpl*>& AssetTrackingImpl::GetEnvironmentVariable()
{
static EnvironmentVariable<AssetTrackingImpl*> assetTrackingImpl = Environment::CreateVariable<AssetTrackingImpl*>(AzTypeInfo<AssetTrackingImpl*>::Name());
return assetTrackingImpl;
}
///////////////////////////////////////////////////////////////////////////////
// AssetTracking::Scope functions
///////////////////////////////////////////////////////////////////////////////
AssetTracking::Scope AssetTracking::Scope::ScopeFromAssetId(const char* file, int line, const char* fmt, ...)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
static const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
impl->AssetBegin(buffer, file, line);
}
return Scope();
}
AssetTracking::Scope AssetTracking::Scope::ScopeFromAttachment(void* attachTo, const char* file, int line)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetAttach(attachTo, file, line);
}
return Scope();
}
AssetTracking::Scope::~Scope()
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetEnd();
}
}
AssetTracking::Scope::Scope()
{
}
///////////////////////////////////////////////////////////////////////////////
// AssetTracking functions
///////////////////////////////////////////////////////////////////////////////
void AssetTracking::EnterScopeByAssetId(const char* file, int line, const char* fmt, ...)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
static const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
impl->AssetBegin(buffer, file, line);
}
}
void AssetTracking::EnterScopeByAttachment(void* attachTo, const char* file, int line)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetAttach(attachTo, file, line);
}
}
void AssetTracking::ExitScope()
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetEnd();
}
}
const char* AssetTracking::GetDebugScope()
{
// Output debug information about the current asset scope in the current thread.
// Do not use in production code.
#ifndef RELEASE
static const int BUFFER_SIZE = 1024;
static char buffer[BUFFER_SIZE];
const auto& assetStack = AssetTrackingImpl::GetSharedInstance()->GetThreadData().m_currentAssetStack;
if (assetStack.empty())
{
azsnprintf(buffer, BUFFER_SIZE, "<none>");
}
else
{
char* pos = buffer;
for (auto itr = assetStack.rbegin(); itr != assetStack.rend(); ++itr)
{
pos += azsnprintf(pos, BUFFER_SIZE - (pos - buffer), "%s\n", (*itr)->GetAssetPrimaryInfo()->m_id->m_id.c_str());
if (pos >= buffer + BUFFER_SIZE)
{
break;
}
}
}
return buffer;
#else
return "";
#endif
}
AssetTracking::AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable)
{
m_impl.reset(aznew AssetTrackingImpl(assetTree, allocationTable));
}
AssetTracking::~AssetTracking()
{
}
AssetTreeNodeBase* AssetTracking::GetCurrentThreadAsset() const
{
const auto& assetStack = m_impl->GetThreadData().m_currentAssetStack;
AssetTreeNodeBase* result = assetStack.empty() ? nullptr : assetStack.back();
return result;
}
} // namespace AzFramework
@@ -1,131 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/EBus/Policies.h>
#ifndef AZ_TRACK_ASSET_SCOPES
// You may manually uncomment this to enable asset tracking.
//# define AZ_TRACK_ASSET_SCOPES
#endif
#if !defined(AZ_TRACK_ASSET_SCOPES)
// Default to enabling asset tracking when memory tracking is enabled
# define AZ_TRACK_ASSET_SCOPES
#endif
#ifdef AZ_TRACK_ASSET_SCOPES
#define AZ_ASSET_SCOPE_VARIABLE_NAME(line) AZ_JOIN(_az_assettracking_scope_, line)
///////////////////////////////////////////////////////////////////////////////
// Preferred macros to use at the top of a scope you want to to track asset memory for.
///////////////////////////////////////////////////////////////////////////////
// Creates a new scope with a name, usually the name of an asset being loaded. (This may be a format-string, e.g. "Foo: %s", bar.c_str())
# define AZ_ASSET_NAMED_SCOPE(...) AZ::Debug::AssetTracking::Scope AZ_ASSET_SCOPE_VARIABLE_NAME(__LINE__) (AZ::Debug::AssetTracking::Scope::ScopeFromAssetId(__FILE__, __LINE__, __VA_ARGS__))
// Attempts to enter an existing scope that already owns some other allocation.
# define AZ_ASSET_ATTACH_TO_SCOPE(other) AZ::Debug::AssetTracking::Scope AZ_ASSET_SCOPE_VARIABLE_NAME(__LINE__) (AZ::Debug::AssetTracking::Scope::ScopeFromAttachment((other), __FILE__, __LINE__))
///////////////////////////////////////////////////////////////////////////////
// Optional macros to manually enter and exit a scope.
// It is the responsibility of the user to make sure every call to AZ_ASSET_ENTER_SCOPE_* is matched by a corresponding call to AZ_ASSET_EXIT_SCOPE.
///////////////////////////////////////////////////////////////////////////////
# define AZ_ASSET_ENTER_SCOPE_BY_ASSET_ID(...) AZ::Debug::AssetTracking::EnterScopeByAssetId(__FILE__, __LINE__, __VA_ARGS__)
# define AZ_ASSET_ENTER_SCOPE_BY_ATTACHMENT(other) AZ::Debug::AssetTracking::EnterScopeByAttachment((other), __FILE__, __LINE__)
# define AZ_ASSET_EXIT_SCOPE AZ::Debug::AssetTracking::ExitScope()
#else
# define AZ_ASSET_NAMED_SCOPE(...) (void)0
# define AZ_ASSET_ATTACH_TO_SCOPE(other) (void)0
# define AZ_ASSET_ENTER_SCOPE_BY_ASSET_ID(...) (void)0
# define AZ_ASSET_ENTER_SCOPE_BY_ATTACHMENT(other) (void)0
# define AZ_ASSET_EXIT_SCOPE (void)0
#endif
namespace AZ
{
class ReflectContext;
namespace Debug
{
class AssetTrackingImpl;
class AssetTreeBase;
class AssetTreeNodeBase;
class AssetAllocationTableBase;
class AssetTracking
{
public:
AZ_TYPE_INFO(AssetTracking, "{D4335180-09A2-415A-8B50-9B734E7CE1E6}");
AZ_CLASS_ALLOCATOR(AssetTracking, OSAllocator, 0);
// Provide RAII method for entering and exiting scopes.
// Generally you will want to use the macros at the top of this file rather than instantiating this object directly.
class Scope
{
public:
static Scope ScopeFromAssetId(const char* file, int line, const char* fmt, ...);
static Scope ScopeFromAttachment(void* attachTo, const char* file, int line);
Scope(Scope&&) = default;
~Scope();
private:
Scope();
};
// Generally you will want to use the macros at the top of this file rather than calling these functions directly.
static void EnterScopeByAssetId(const char* file, int line, const char* fmt, ...);
static void EnterScopeByAttachment(void* attachTo, const char* file, int line);
static void ExitScope();
static const char* GetDebugScope();
AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
~AssetTracking();
AssetTreeNodeBase* GetCurrentThreadAsset() const;
private:
AZStd::unique_ptr<AssetTrackingImpl> m_impl;
};
// An EBus processing policy that attempts to attach to an existing scope before calling a handler.
//
// Use this on EBuses where you want the callees to track asset memory during their event handlers.
// This will work so long as the callees were themselves allocated inside an existing asset scope.
//
// May be added to an existing EBus with the following code:
// using EventProcessingPolicy = Debug::AssetTrackingEventProcessingPolicy;
//
template<typename Parent = EBusEventProcessingPolicy>
struct AssetTrackingEventProcessingPolicy
{
template<class Results, class Function, class Interface, class... InputArgs>
static void CallResult(Results& results, Function&& func, Interface&& iface, InputArgs&&... args)
{
AZ_ASSET_ATTACH_TO_SCOPE(iface);
Parent::CallResult(results, AZStd::forward<Function>(func), AZStd::forward<Interface>(iface), AZStd::forward<InputArgs>(args)...);
}
template<class Function, class Interface, class... InputArgs>
static void Call(Function&& func, Interface&& iface, InputArgs&&... args)
{
AZ_ASSET_ATTACH_TO_SCOPE(iface);
Parent::Call(AZStd::forward<Function>(func), AZStd::forward<Interface>(iface), AZStd::forward<InputArgs>(args)...);
}
};
}
} // namespace AzFramework
@@ -1,120 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/HphaSchema.h>
#include <AzCore/Memory/SimpleSchemaAllocator.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
namespace Debug
{
struct AssetTrackingId;
}
}
namespace AZStd
{
// Declare hash specializations for types that need them; implementations will have to come after the classes are fully defined
template<>
struct hash<AZ::Debug::AssetTrackingId>
{
size_t operator()(const AZ::Debug::AssetTrackingId& id) const;
};
}
namespace AZ
{
namespace Debug
{
class AssetTrackingImpl;
// Custom allocator for the Analyzer that doesn't go through profiling tools and cannot be overridden
class AssetTrackingAllocator : public AZ::SimpleSchemaAllocator<AZ::HphaSchema, AZ::HphaSchema::Descriptor, false>
{
public:
AZ_TYPE_INFO(AssetTrackingAllocator, "{F6C08E92-559C-4153-9620-6A8491F78F10}");
using Base = AZ::SimpleSchemaAllocator<AZ::HphaSchema, AZ::HphaSchema::Descriptor, false>;
using Descriptor = Base::Descriptor;
AssetTrackingAllocator()
: Base("AssetTrackingAllocator", "Allocator for the AssetTracking")
{
DisableOverriding();
}
};
using AZStdAssetTrackingAllocator = AZ::AZStdAlloc<AssetTrackingAllocator>;
using AssetTrackingString = AZStd::basic_string<char, AZStd::char_traits<char>, AZStdAssetTrackingAllocator>;
template<typename Key, typename MappedType>
using AssetTrackingMap = AZStd::unordered_map<Key, MappedType, AZStd::hash<Key>, AZStd::equal_to<Key>, AZStdAssetTrackingAllocator>;
// ID for an asset that is hashable.
// Currently only contains one string identifier, but we may want to store a more sophisticated ID in the future.
struct AssetTrackingId
{
AssetTrackingId(const char* id) : m_id(id)
{
}
bool operator==(const AssetTrackingId& other) const
{
return m_id == other.m_id;
}
AssetTrackingString m_id;
};
// Primary information about an asset.
// Currently just contains the ID of the asset, but in the future may carry additional information about that asset (such as where in code it was initialized).
struct AssetPrimaryInfo
{
const AssetTrackingId* m_id;
};
// Base class for a node in the asset tree. Implemented by the template AssetTreeNode<>.
class AssetTreeNodeBase
{
public:
virtual ~AssetTreeNodeBase() = default;
virtual const AssetPrimaryInfo* GetAssetPrimaryInfo() const = 0;
virtual AssetTreeNodeBase* FindOrAddChild(const AssetTrackingId& id, const AssetPrimaryInfo* info) = 0;
};
// Base class for an asset tree. Implemented by the template AssetTree<>.
class AssetTreeBase
{
public:
virtual ~AssetTreeBase() = default;
virtual AssetTreeNodeBase& GetRoot() = 0;
};
// Base class for an asset allocation table. Implemented by the template AssetAllocationTable<>.
class AssetAllocationTableBase
{
public:
virtual ~AssetAllocationTableBase() = default;
virtual AssetTreeNodeBase* FindAllocation(void* ptr) const = 0;
};
}
}
///////////////////////////////////////////////////////////////////////////////
// Hash functions for map support
///////////////////////////////////////////////////////////////////////////////
inline size_t AZStd::hash<AZ::Debug::AssetTrackingId>::operator()(const AZ::Debug::AssetTrackingId& info) const
{
return AZStd::hash<AZ::Debug::AssetTrackingString>()(info.m_id);
}
@@ -1,174 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Debug/AssetTrackingTypes.h>
#include <AzCore/std/containers/map.h>
namespace AZ
{
namespace Debug
{
// A node in the current asset state tree.
// Each thread maintains a stack of currently in-scope assets. As this stack changes the asset tree forms.
// The same asset may appear in multiple places in the tree, e.g. if asset A is a common asset loaded by both asset B and asset C, the tree may look like:
// Root -> B -> A
// \--> C -> A
template<typename AssetDataT>
class AssetTreeNode : public AssetTreeNodeBase
{
public:
AssetTreeNode(const AssetPrimaryInfo* primaryInfo = nullptr, AssetTreeNode* parent = nullptr) :
m_primaryinfo(primaryInfo),
m_parent(parent)
{
}
~AssetTreeNode() override = default;
const AssetPrimaryInfo* GetAssetPrimaryInfo() const override
{
return m_primaryinfo;
}
AssetTreeNodeBase* FindOrAddChild(const AssetTrackingId& id, const AssetPrimaryInfo* info) override
{
AssetTreeNodeBase* result = nullptr;
auto childItr = m_children.find(id);
if (childItr != m_children.end())
{
result = &childItr->second;
}
else
{
auto childResult = m_children.emplace(id, AssetTreeNode(info, this));
result = &childResult.first->second;
}
return result;
}
using AssetMap = AssetTrackingMap<AssetTrackingId, AssetTreeNode>;
const AssetPrimaryInfo* m_primaryinfo;
AssetTreeNode* m_parent;
AssetMap m_children;
AssetDataT m_data;
};
template<typename AssetDataT>
class AssetTree : public AssetTreeBase
{
public:
~AssetTree() override = default;
AssetTreeNodeBase& GetRoot() override
{
return m_rootAssets;
}
using NodeType = AssetTreeNode<AssetDataT>;
NodeType m_rootAssets;
};
template<typename AllocationDataT>
struct AllocationRecord
{
AssetTreeNodeBase* m_asset;
uint32_t m_size;
AllocationDataT m_data;
};
template<typename AllocationDataT>
class AllocationTable : public AssetAllocationTableBase
{
public:
using RecordType = AllocationRecord<AllocationDataT>;
using AllocationReverseMap = AZStd::map<void*, RecordType, AZStd::greater<void*>, AZStdAssetTrackingAllocator>;
using mutex_type = AZStd::mutex;
using lock_type = AZStd::lock_guard<mutex_type>;
AllocationTable(mutex_type& mutex) : m_mutex(mutex)
{
}
~AllocationTable() override = default;
AssetTreeNodeBase* FindAllocation(void* ptr) const override
{
// Note that ptr is not guaranteed to have an exact entry in the map. For instance, ptr may point to a member of the original object that was allocated, or
// ptr may be a different "this" pointer in the case of multiple inheritance.
//
// To solve this, we use lower_bound() and check to see if ptr falls in the range of the nearest allocation. Our map uses AZStd::greater instead of
// AZStd::less as its sorting function, and thus sorts largest-to-smallest instead of smallest-to-largest. This causes lower_bound() to return the first
// iterator that is not greater than otherAllocation, i.e. less than or equal to ptr.
lock_type lock(m_mutex);
auto itr = m_allocationTable.lower_bound(ptr);
AssetTreeNodeBase* result = nullptr;
if (itr != m_allocationTable.end())
{
// Check if otherAllocation is within the size range of the allocation we found
if (reinterpret_cast<uintptr_t>(ptr) <= reinterpret_cast<uintptr_t>(itr->first) + itr->second.m_size)
{
result = itr->second.m_asset;
}
}
return result;
}
void ReallocateAllocation(void* prevAddress, void* newAddress, size_t newByteSize)
{
lock_type lock(m_mutex);
auto itr = m_allocationTable.find(prevAddress);
if (itr != m_allocationTable.end())
{
RecordType newAllocation = itr->second;
newAllocation.m_size = (uint32_t)newByteSize;
m_allocationTable.erase(itr);
m_allocationTable.emplace(newAddress, AZStd::move(newAllocation));
}
}
void ResizeAllocation(void* address, size_t newSize)
{
// Resize an existing allocation if we can find it
lock_type lock(m_mutex);
auto itr = m_allocationTable.find(address);
if (itr != m_allocationTable.end())
{
itr->second.m_size = (uint32_t)newSize;
}
}
AllocationReverseMap& Get()
{
return m_allocationTable;
}
const AllocationReverseMap& Get() const
{
return m_allocationTable;
}
private:
AllocationReverseMap m_allocationTable;
mutex_type& m_mutex;
};
}
}
@@ -13,7 +13,6 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Debug/AssetTracking.h>
namespace AZ
{
@@ -92,10 +92,6 @@ set(FILES
Compression/Compression.h
Compression/zstd_compression.cpp
Compression/zstd_compression.h
Debug/AssetTracking.cpp
Debug/AssetTracking.h
Debug/AssetTrackingTypesImpl.h
Debug/AssetTrackingTypes.h
Debug/Budget.h
Debug/Budget.cpp
Debug/BudgetTracker.h
@@ -1,169 +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/AssetTrackingTypesImpl.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
using namespace AZ;
namespace UnitTest
{
namespace
{
struct TestData
{
};
using AssetTree = AZ::Debug::AssetTree<TestData>;
using AllocationTable = AZ::Debug::AllocationTable<TestData>;
struct AssetTrackingTestEnvironment
{
AssetTrackingTestEnvironment() : m_table(m_mutex)
{
}
AZStd::mutex m_mutex;
AssetTree m_tree;
AllocationTable m_table;
AZStd::unique_ptr<AZ::Debug::AssetTracking> m_assetTracking;
};
}
class AssetTrackingTests
: public ::testing::Test
{
public:
void SetUp() override
{
AZ::AllocatorInstance<AZ::Debug::AssetTrackingAllocator>::Create();
m_env.reset(new AssetTrackingTestEnvironment);
m_env->m_assetTracking.reset(aznew AZ::Debug::AssetTracking(&m_env->m_tree, &m_env->m_table));
}
void TearDown() override
{
m_env.reset();
AZ::AllocatorInstance<AZ::Debug::AssetTrackingAllocator>::Destroy();
}
void RunTests()
{
TestAssetScopes();
TestScopedAllocation();
TestScopeAttach();
}
private:
void TestAssetScopes()
{
const char* debugScopeText;
{
AZ_ASSET_NAMED_SCOPE("TestAssetScopes.1");
{
AZ_ASSET_NAMED_SCOPE("TestAssetScopes.2");
{
AZ_ASSET_NAMED_SCOPE("TestAssetScopes.3");
debugScopeText = AZ::Debug::AssetTracking::GetDebugScope();
EXPECT_STREQ(debugScopeText, "TestAssetScopes.3\nTestAssetScopes.2\nTestAssetScopes.1\n");
}
debugScopeText = AZ::Debug::AssetTracking::GetDebugScope();
EXPECT_STREQ(debugScopeText, "TestAssetScopes.2\nTestAssetScopes.1\n");
}
debugScopeText = AZ::Debug::AssetTracking::GetDebugScope();
EXPECT_STREQ(debugScopeText, "TestAssetScopes.1\n");
}
}
void TestScopedAllocation()
{
void* TEST_POINTER = (void*)0xDEADBEEFull;
static const size_t TEST_SIZE = 32;
{
AZ_ASSET_NAMED_SCOPE("TestScopedAllocation.1");
AZ::Debug::AssetTreeNodeBase* activeAsset = m_env->m_assetTracking->GetCurrentThreadAsset();
m_env->m_table.Get().emplace(TEST_POINTER, AllocationTable::RecordType{ activeAsset, (uint32_t)TEST_SIZE, TestData() });
}
auto& rootAsset = m_env->m_tree.m_rootAssets;
auto itr = rootAsset.m_children.find("TestScopedAllocation.1");
EXPECT_EQ(&rootAsset, &m_env->m_tree.GetRoot());
ASSERT_NE(itr, rootAsset.m_children.end());
EXPECT_EQ(itr->second.m_primaryinfo->m_id->m_id, "TestScopedAllocation.1");
EXPECT_EQ(&itr->second, m_env->m_table.FindAllocation(TEST_POINTER));
auto allocationRecord = m_env->m_table.Get().find(TEST_POINTER);
ASSERT_NE(allocationRecord, m_env->m_table.Get().end());
EXPECT_EQ(allocationRecord->second.m_asset, &itr->second);
EXPECT_EQ(allocationRecord->second.m_size, TEST_SIZE);
// Test realocation
void* TEST_REALLOC_POINTER = (void*)0xDEADC0DEull;
static const size_t TEST_REALLOC_SIZE = 128;
m_env->m_table.ReallocateAllocation(TEST_POINTER, TEST_REALLOC_POINTER, TEST_REALLOC_SIZE);
allocationRecord = m_env->m_table.Get().find(TEST_POINTER);
EXPECT_EQ(allocationRecord, m_env->m_table.Get().end());
allocationRecord = m_env->m_table.Get().find(TEST_REALLOC_POINTER);
ASSERT_NE(allocationRecord, m_env->m_table.Get().end());
EXPECT_EQ(allocationRecord->second.m_asset, &itr->second);
EXPECT_EQ(allocationRecord->second.m_size, TEST_REALLOC_SIZE);
// Test resize
static const size_t TEST_RESIZE_SIZE = 1024;
m_env->m_table.ResizeAllocation(TEST_REALLOC_POINTER, TEST_RESIZE_SIZE);
EXPECT_EQ(allocationRecord->second.m_size, TEST_RESIZE_SIZE);
}
void TestScopeAttach()
{
void* TEST_POINTER = (void*)0xDEADBEEFull;
static const size_t TEST_SIZE = 32;
AZ::Debug::AssetTreeNodeBase* originatingAsset;
{
AZ_ASSET_ATTACH_TO_SCOPE(TEST_POINTER);
EXPECT_EQ(m_env->m_assetTracking->GetCurrentThreadAsset(), nullptr);
}
{
AZ_ASSET_NAMED_SCOPE("TestScopeAttach.1");
{
AZ_ASSET_NAMED_SCOPE("TestScopeAttach.2");
{
originatingAsset = m_env->m_assetTracking->GetCurrentThreadAsset();
EXPECT_NE(originatingAsset, nullptr);
m_env->m_table.Get().emplace(TEST_POINTER, AllocationTable::RecordType{ originatingAsset, (uint32_t)TEST_SIZE, TestData() });
}
}
}
EXPECT_EQ(m_env->m_assetTracking->GetCurrentThreadAsset(), nullptr);
{
AZ_ASSET_ATTACH_TO_SCOPE(TEST_POINTER);
EXPECT_EQ(m_env->m_assetTracking->GetCurrentThreadAsset(), originatingAsset);
}
}
AZStd::unique_ptr<AssetTrackingTestEnvironment> m_env;
};
TEST_F(AssetTrackingTests, Test)
{
RunTests();
}
}
@@ -69,7 +69,6 @@ set(FILES
TickBusTest.cpp
UUIDTests.cpp
XML.cpp
Debug/AssetTracking.cpp
Debug/LocalFileEventLoggerTests.cpp
Debug/Trace.cpp
Debug/UnhandledExceptions.cpp
@@ -8,7 +8,6 @@
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/EntityUtils.h>
#include <AzCore/Component/TickBus.h>
@@ -243,8 +242,6 @@ namespace AzFramework
//=========================================================================
void EntityContext::ActivateEntity(AZ::EntityId entityId)
{
AZ_ASSET_ATTACH_TO_SCOPE(this);
// Verify that this context has the right to perform operations on the entity
bool validEntity = IsOwnedByThisContext(entityId);
AZ_Warning("GameEntityContext", validEntity, "Entity with id %llu does not belong to the game context.", entityId);
@@ -386,7 +386,6 @@ namespace AzFramework
void SliceEntityOwnershipService::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> readyAsset)
{
AZ_PROFILE_FUNCTION(AzFramework);
AZ_ASSET_ATTACH_TO_SCOPE(readyAsset.Get());
AZ_Assert(readyAsset.GetAs<AZ::SliceAsset>(), "Asset is not a slice!");
@@ -400,7 +399,6 @@ namespace AzFramework
// we intentionally capture readyAsset by value here, so that its refcount doesn't hit 0 by the time this call happens.
AZStd::function<void()> instantiateCallback = [this, readyAsset]()
{
AZ_ASSET_ATTACH_TO_SCOPE(readyAsset.Get());
const AZ::Data::AssetId readyAssetId = readyAsset.GetId();
for (auto iter = m_queuedSliceInstantiations.begin(); iter != m_queuedSliceInstantiations.end(); )
{