Removes AssetMemoryAnalyzer that relies on the MemoryDrillerBus
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -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(); )
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user