You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Legacy/CryCommon/CrySizer.h

587 lines
18 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Declaration and definition of the CrySizer class, which is used to
// calculate the memory usage by the subsystems and components, to help
// the artists keep the memory budged low.
#ifndef CRYINCLUDE_CRYCOMMON_CRYSIZER_H
#define CRYINCLUDE_CRYCOMMON_CRYSIZER_H
#pragma once
//////////////////////////////////////////////////////////////////////////
// common containers for overloads
#include <list>
#include "Cry_Math.h"
#include <StlUtils.h>
#include <Tarray.h>
#include <CryPodArray.h>
#include <Cry_Vector3.h>
#include <Cry_Quat.h>
#include <Cry_Color.h>
#include <smartptr.h>
// forward declarations for overloads
struct AABB;
struct SVF_P3F;
struct SVF_P3F_C4B_T2F;
struct SVF_P3F_C4B_T2S;
struct SVF_P3S_C4B_T2S;
struct SPipTangents;
#ifdef WIN64
#include <string.h> // workaround for Amd64 compiler
#endif
namespace AZ
{
class Vector3;
}
// flags applicable to the ICrySizer (retrieved via getFlags() method)
//
enum ICrySizerFlagsEnum
{
// if this flag is set, during getSize(), the subsystem must count all the objects
// it uses in the other subsystems also
CSF_RecurseSubsystems = 1 << 0,
CSF_Reserved1 = 1 << 1,
CSF_Reserved2 = 1 << 2
};
//////////////////////////////////////////////////////////////////////////
// Helper functions to calculate size of the std containers.
//////////////////////////////////////////////////////////////////////////
namespace stl
{
template <class Map>
inline size_t size_of_map(const Map& m)
{
if (!m.empty())
{
return m.size() * sizeof(typename Map::value_type) + m.size() * sizeof(MapLikeStruct);
}
return 0;
}
template <class Map>
inline size_t size_of_set(const Map& m)
{
if (!m.empty())
{
return m.size() * sizeof(typename Map::value_type) + m.size() * sizeof(MapLikeStruct);
}
return 0;
}
template <class List>
inline size_t size_of_list(const List& c)
{
if (!c.empty())
{
return c.size() * sizeof(typename List::value_type) + c.size() * sizeof(void*) * 2; // sizeof stored type + 2 pointers prev,next
}
return 0;
}
template <class Deque>
inline size_t size_of_deque(const Deque& c)
{
if (!c.empty())
{
return c.size() * sizeof(typename Deque::value_type);
}
return 0;
}
};
//////////////////////////////////////////////////////////////////////////
// interface ICrySizer
// USAGE
// An instance of this class is passed down to each and every component in the system.
// Every component it's passed to optionally pushes its name on top of the
// component name stack (thus ensuring that all the components calculated down
// the tree will be assigned the correct subsystem/component name)
// Every component must Add its size with one of the Add* functions, and Add the
// size of all its subcomponents recursively
// In order to push the component/system name on the name stack, the clients must
// use the SIZER_COMPONENT_NAME macro or CrySizerComponentNameHelper class:
//
// void X::getSize (ICrySizer* pSizer)
// {
// SIZER_COMPONENT_NAME(pSizer, X);
// if (!pSizer->Add (this))
// return;
// pSizer->Add (m_arrMySimpleArray);
// pSizer->Add (m_setMySimpleSet);
// m_pSubobject->getSize (pSizer);
// }
//
// The Add* functions return bool. If they return true, then the object has been added
// to the set for the first time, and you should go on recursively adding all its children.
// If it returns false, then you can spare time and rather not go on into recursion;
// however it doesn't reflect on the results: an object that's added more than once is
// counted only once.
//
// WARNING:
// If you have an array (pointer), you should Add its size with addArray
class ICrySizer
{
public:
virtual ~ICrySizer(){}
// this class is used to push/pop the name to/from the stack automatically
// (to exclude stack overruns or underruns at runtime)
friend class CrySizerComponentNameHelper;
virtual void Release() = 0;
// Return total calculated size.
virtual size_t GetTotalSize() = 0;
// Return total objects added.
virtual size_t GetObjectCount() = 0;
// Resets the counting.
virtual void Reset() = 0;
virtual void End() = 0;
// adds an object identified by the unique pointer (it needs not be
// the actual object position in the memory, though it would be nice,
// but it must be unique throughout the system and unchanging for this object)
// nCount parameter is only used for counting number of objects, it doesnt affect the size of the object.
// RETURNS: true if the object has actually been added (for the first time)
// and calculated
virtual bool AddObject (const void* pIdentifier, size_t nSizeBytes, int nCount = 1) = 0;
template<typename Type>
bool AddObjectSize(const Type* pObj)
{
return AddObject(pObj, sizeof *pObj);
}
////////////////////////////////////////////////////////////////////////////////////////
// temp dummy function while checking in the CrySizer changes, will be removed soon
template<typename Type>
void AddObject(const Type& rObj)
{
(void)rObj;
}
template<typename Type>
void AddObject(Type* pObj)
{
if (pObj)
{
//forward to reference object to allow function overload
this->AddObject(*pObj);
}
}
// overloads for smart_ptr and other common objects
template<typename T>
void AddObject(const _smart_ptr<T>& rObj) { this->AddObject(rObj.get()); }
template<typename T>
void AddObject(const AZStd::shared_ptr<T>& rObj) { this->AddObject(rObj.get()); }
template<typename T>
void AddObject(const std::shared_ptr<T>& rObj) { this->AddObject(rObj.get()); }
template<typename T>
void AddObject(const std::unique_ptr<T>& rObj) { this->AddObject(rObj.get()); }
template<typename T, typename U>
void AddObject(const std::pair<T, U>& rPair)
{
this->AddObject(rPair.first);
this->AddObject(rPair.second);
}
template<typename T, typename U>
void AddObject(const AZStd::pair<T, U>& rPair)
{
this->AddObject(rPair.first);
this->AddObject(rPair.second);
}
void AddObject(const string& rString) {this->AddObject(rString.c_str(), rString.capacity()); }
void AddObject(const CryStringT<wchar_t>& rString) {this->AddObject(rString.c_str(), rString.capacity()); }
void AddObject(const CryFixedStringT<32>&){}
void AddObject(const wchar_t&) {}
void AddObject(const char&) {}
void AddObject(const unsigned char&) {}
void AddObject(const signed char&) {}
void AddObject(const short&) {}
void AddObject(const unsigned short&) {}
void AddObject(const int&) {}
void AddObject(const unsigned int&) {}
void AddObject(const long&) {}
void AddObject(const unsigned long&) {}
void AddObject(const float&) {}
void AddObject(const bool&) {}
void AddObject(const unsigned long long&) {}
void AddObject(const long long&) {}
void AddObject(const double&) {}
void AddObject(const Vec2&) {}
void AddObject(const Vec3&) {}
void AddObject(const Vec4&) {}
void AddObject(const Ang3&) {}
void AddObject(const Matrix34&) {}
void AddObject(const Quat&) {}
void AddObject(const QuatT&) {}
void AddObject(const QuatTS&) {}
void AddObject(const ColorF&) {}
void AddObject(const AABB&) {}
void AddObject(const SVF_P3F&) {}
void AddObject(const SVF_P3F_C4B_T2F&) {}
void AddObject(const SVF_P3F_C4B_T2S&) {}
void AddObject(const SVF_P3S_C4B_T2S&) {}
void AddObject(const SPipTangents&) {}
void AddObject([[maybe_unused]] const AZ::Vector3& rObj) {}
void AddObject(void*) {}
// overloads for container, will automaticly traverse the content
template<typename T, typename Alloc>
void AddObject(const std::list<T, Alloc>& rList)
{
// dummy struct to get correct element size
struct Dummy
{
void* a;
void* b;
T t;
};
for (typename std::list<T, Alloc>::const_iterator it = rList.begin(); it != rList.end(); ++it)
{
if (this->AddObject(&(*it), sizeof(Dummy)))
{
this->AddObject(*it);
}
}
}
template<typename K, typename T, typename Comp, typename Equal, typename Alloc>
void AddObject([[maybe_unused]] const AZStd::unordered_map<K, T, Comp, Equal, Alloc>& rVector)
{
}
template<typename T, typename Alloc>
void AddObject(const std::vector<T, Alloc>& rVector)
{
if (rVector.empty())
{
this->AddObject(&rVector, rVector.capacity() * sizeof(T));
return;
}
if (!this->AddObject(&rVector[0], rVector.capacity() * sizeof(T)))
{
return;
}
for (typename std::vector<T, Alloc>::const_iterator it = rVector.begin(); it != rVector.end(); ++it)
{
this->AddObject(*it);
}
}
template<typename T, typename Alloc>
void AddObject(const std::deque<T, Alloc>& rVector)
{
for (typename std::deque<T, Alloc>::const_iterator it = rVector.begin(); it != rVector.end(); ++it)
{
if (this->AddObject(&(*it), sizeof(T)))
{
this->AddObject(*it);
}
}
}
template<typename T, typename I, typename S>
void AddObject(const DynArray<T, I, S>& rVector)
{
if (rVector.empty())
{
this->AddObject(rVector.begin(), rVector.get_alloc_size());
return;
}
if (!this->AddObject(rVector.begin(), rVector.get_alloc_size()))
{
return;
}
for (typename DynArray<T, I, S>::const_iterator it = rVector.begin(); it != rVector.end(); ++it)
{
this->AddObject(*it);
}
}
template<typename T>
void AddObject(const PodArray<T>& rVector)
{
if (!this->AddObject(rVector.begin(), rVector.capacity() * sizeof(T)))
{
return;
}
for (typename PodArray<T>::const_iterator it = rVector.begin(); it != rVector.end(); ++it)
{
this->AddObject(*it);
}
}
template<typename K, typename T, typename Comp, typename Alloc>
void AddObject(const std::map<K, T, Comp, Alloc>& rVector)
{
// dummy struct to get correct element size
struct Dummy
{
void* a;
void* b;
void* c;
void* d;
K k;
T t;
};
for (typename std::map<K, T, Comp, Alloc>::const_iterator it = rVector.begin(); it != rVector.end(); ++it)
{
if (this->AddObject(&(*it), sizeof(Dummy)))
{
this->AddObject(it->first);
this->AddObject(it->second);
}
}
}
template<typename T, typename Comp, typename Alloc>
void AddObject(const std::set<T, Comp, Alloc>& rVector)
{
// dummy struct to get correct element size
struct Dummy
{
void* a;
void* b;
void* c;
void* d;
T t;
};
for (typename std::set<T, Comp, Alloc>::const_iterator it = rVector.begin(); it != rVector.end(); ++it)
{
if (this->AddObject(&(*it), sizeof(Dummy)))
{
this->AddObject(*it);
}
}
}
template <typename TKey, typename TValue, typename TPredicate, typename TAlloc>
void AddObject (const std::multimap<TKey, TValue, TPredicate, TAlloc>& rContainer)
{
AddContainer(rContainer);
}
////////////////////////////////////////////////////////////////////////////////////////
template <typename T>
bool Add (const T* pId, size_t num)
{
return AddObject(pId, num * sizeof(T));
}
template <class T>
bool Add (const T& rObject)
{
return AddObject (&rObject, sizeof(T));
}
bool Add (const char* szText)
{
return AddObject(szText, strlen(szText) + 1);
}
template <class StringCls>
bool AddString (const StringCls& strText)
{
if (!strText.empty())
{
return AddObject (strText.c_str(), strText.size());
}
else
{
return false;
}
}
#ifdef _XSTRING_
template <class Elem, class Traits, class Allocator>
bool Add (const std::basic_string<Elem, Traits, Allocator>& strText)
{
AddString (strText);
return true;
}
#endif
#ifndef NOT_USE_CRY_STRING
bool Add (const string& strText)
{
AddString(strText);
return true;
}
#endif
// Template helper function to add generic stl container
template <typename Container>
bool AddContainer (const Container& rContainer)
{
if (rContainer.capacity())
{
return AddObject (&rContainer, rContainer.capacity() * sizeof(typename Container::value_type));
}
return false;
}
template <typename Container>
bool AddHashMap(const Container& rContainer)
{
if (!rContainer.empty())
{
return AddObject (&(*rContainer.begin()), rContainer.size() * sizeof(typename Container::value_type));
}
return false;
}
// Specialization of the AddContainer for the std::list
template <typename Type, typename TAlloc>
bool AddContainer (const std::list<Type, TAlloc>& rContainer)
{
if (!rContainer.empty())
{
return AddObject(&(*rContainer.begin()), stl::size_of_list(rContainer));
}
return false;
}
// Specialization of the AddContainer for the std::deque
template <typename Type, typename TAlloc>
bool AddContainer (const std::deque<Type, TAlloc>& rContainer)
{
if (!rContainer.empty())
{
return AddObject(&(*rContainer.begin()), stl::size_of_deque(rContainer));
}
return false;
}
// Specialization of the AddContainer for the std::map
template <typename TKey, typename TValue, typename TPredicate, typename TAlloc>
bool AddContainer (const std::map<TKey, TValue, TPredicate, TAlloc>& rContainer)
{
if (!rContainer.empty())
{
return AddObject(&(*rContainer.begin()), stl::size_of_map(rContainer));
}
return false;
}
// Specialization of the AddContainer for the std::multimap
template <typename TKey, typename TValue, typename TPredicate, typename TAlloc>
bool AddContainer (const std::multimap<TKey, TValue, TPredicate, TAlloc>& rContainer)
{
if (!rContainer.empty())
{
return AddObject(&(*rContainer.begin()), stl::size_of_map(rContainer));
}
return false;
}
// Specialization of the AddContainer for the std::set
template <typename TKey, typename TPredicate, typename TAlloc>
bool AddContainer (const std::set<TKey, TPredicate, TAlloc>& rContainer)
{
if (!rContainer.empty())
{
return AddObject(&(*rContainer.begin()), stl::size_of_set(rContainer));
}
return false;
}
// Specialization of the AddContainer for the AZStd::unordered_map
template <typename KEY, typename TYPE, class HASH, class EQUAL, class ALLOCATOR>
bool AddContainer(const AZStd::unordered_map<KEY, TYPE, HASH, EQUAL, ALLOCATOR>& rContainer)
{
if (!rContainer.empty())
{
return AddObject(&(*rContainer.begin()), rContainer.size() * sizeof(typename AZStd::unordered_map<KEY, TYPE, HASH, EQUAL, ALLOCATOR>::value_type));
}
else
{
return false;
}
}
void Test()
{
std::map<int, float> mymap;
AddContainer(mymap);
}
// returns the flags
unsigned GetFlags() const {return m_nFlags; }
protected:
// these functions must operate on the component name stack
// they are to be only accessible from within class CrySizerComponentNameHelper
// which should be used through macro SIZER_COMPONENT_NAME
virtual void Push (const char* szComponentName) = 0;
// pushes the name that is the name of the previous component . (dot) this name
virtual void PushSubcomponent (const char* szSubcomponentName) = 0;
virtual void Pop () = 0;
unsigned m_nFlags;
};
//////////////////////////////////////////////////////////////////////////
// This is on-stack class that is only used to push/pop component names
// to/from the sizer name stack.
//
// USAGE:
//
// Create an instance of this class at the start of a function, before
// calling Add* methods of the sizer interface. Everything added in the
// function and below will be considered this component, unless
// explicitly set otherwise.
//
class CrySizerComponentNameHelper
{
public:
// pushes the component name on top of the name stack of the given sizer
CrySizerComponentNameHelper (ICrySizer* pSizer, const char* szComponentName, bool bSubcomponent)
: m_pSizer(pSizer)
{
if (bSubcomponent)
{
pSizer->PushSubcomponent (szComponentName);
}
else
{
pSizer->Push (szComponentName);
}
}
// pops the component name off top of the name stack of the sizer
~CrySizerComponentNameHelper()
{
m_pSizer->Pop();
}
protected:
ICrySizer* m_pSizer;
};
// use this to push (and automatically pop) the sizer component name at the beginning of the
// getSize() function
#define SIZER_COMPONENT_NAME(pSizerPointer, szComponentName) PREFAST_SUPPRESS_WARNING(6246) CrySizerComponentNameHelper AZ_JOIN(sizerHelper, __LINE__)(pSizerPointer, szComponentName, false)
#define SIZER_SUBCOMPONENT_NAME(pSizerPointer, szComponentName) PREFAST_SUPPRESS_WARNING(6246) CrySizerComponentNameHelper AZ_JOIN(sizerHelper, __LINE__)(pSizerPointer, szComponentName, true)
#endif // CRYINCLUDE_CRYCOMMON_CRYSIZER_H